import { query, queryOne, run } from "@/lib/db"; import { getToken } from "next-auth/jwt"; export const dynamic = "force-dynamic"; // Disable caching // Helper function to ensure the hunt_timer table exists async function ensureTableExists() { try { // Directly try to create the table (IF NOT EXISTS prevents errors if it already exists) await run(` CREATE TABLE IF NOT EXISTS hunt_timer ( id INTEGER PRIMARY KEY AUTOINCREMENT, active INTEGER NOT NULL DEFAULT 0, start_time TEXT NOT NULL, end_time TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); // Verify the table exists by inserting a dummy record and then deleting it // This is a more reliable way to test if the table is usable const tempTimestamp = new Date().toISOString(); const tempId = await run( "INSERT INTO hunt_timer (active, start_time) VALUES (0, ?)", [tempTimestamp] ); if (tempId) { // Delete the test record await run("DELETE FROM hunt_timer WHERE id = ?", [tempId]); } console.log("hunt_timer table verified successfully"); return true; } catch (error) { console.error("Error ensuring hunt_timer table:", error); // Try with an alternative approach if the first method failed try { console.log("Attempting alternative table creation method..."); // Execute the CREATE TABLE statement directly await run(` DROP TABLE IF EXISTS hunt_timer; CREATE TABLE hunt_timer ( id INTEGER PRIMARY KEY AUTOINCREMENT, active INTEGER NOT NULL DEFAULT 0, start_time TEXT NOT NULL, end_time TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); console.log("hunt_timer table created with alternative method"); return true; } catch (createError) { console.error("Failed to create hunt_timer table:", createError); return false; } } } // Helper function to ensure settings table exists and manage hunt active status async function ensureSettingsTableExists() { try { // Create settings table if it doesn't exist await run(` CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT NOT NULL ) `); // Check if hunt_active setting exists const huntActiveSetting = await queryOne( "SELECT value FROM settings WHERE key = 'hunt_active'" ); // If not, create it with default value "false" if (!huntActiveSetting) { await run("INSERT OR IGNORE INTO settings (key, value) VALUES (?, ?)", [ "hunt_active", "false", ]); } return true; } catch (error) { console.error("Error ensuring settings table:", error); return false; } } // Helper function to update hunt active status - ensure it updates all tables async function updateHuntActiveStatus(isActive, startTime = null) { try { await ensureSettingsTableExists(); const now = startTime || new Date().toISOString(); // Update settings table - this is used by many endpoints to check status await run("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)", [ "hunt_active", isActive ? "true" : "false", ]); // Update hunt_status table for consistency try { await run(` CREATE TABLE IF NOT EXISTS hunt_status ( id INTEGER PRIMARY KEY CHECK (id = 1), is_active BOOLEAN DEFAULT 0, start_time TEXT, end_time TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); // Check if we need to insert the initial record const status = await queryOne("SELECT * FROM hunt_status LIMIT 1"); if (!status) { await run( "INSERT INTO hunt_status (id, is_active, start_time) VALUES (1, ?, ?)", [isActive ? 1 : 0, isActive ? now : null] ); } else { // Update existing record - but preserve start_time when stopping if (isActive) { await run( "UPDATE hunt_status SET is_active = ?, start_time = ? WHERE id = 1", [1, now] ); } else { await run("UPDATE hunt_status SET is_active = ? WHERE id = 1", [0]); } } } catch (err) { console.error("Error updating hunt_status table:", err); } // Also try to update the traditional hunts table for backwards compatibility try { const now = new Date().toISOString(); await run(` CREATE TABLE IF NOT EXISTS hunts ( id INTEGER PRIMARY KEY AUTOINCREMENT, active BOOLEAN DEFAULT 0, start_time DATETIME DEFAULT NULL, end_time DATETIME DEFAULT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); // Check if we need to insert a new record or update existing const hunt = await queryOne( "SELECT * FROM hunts ORDER BY id DESC LIMIT 1" ); if (!hunt) { await run("INSERT INTO hunts (active, start_time) VALUES (?, ?)", [ isActive ? 1 : 0, isActive ? now : null, ]); } else { // Update most recent hunt record await run("UPDATE hunts SET active = ?, start_time = ? WHERE id = ?", [ isActive ? 1 : 0, isActive ? now : null, hunt.id, ]); } } catch (err) { console.error("Error updating hunts table:", err); } console.log("Hunt active status updated across ALL tables:", isActive); return true; } catch (error) { console.error("Failed to update hunt active status:", error); return false; } } // GET endpoint to get current timer status export async function GET(request) { try { // Use proper next-auth token validation const token = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET || "your-fallback-secret-should-be-at-least-32-chars", }); if (!token) { return Response.json({ error: "Unauthorized" }, { status: 401 }); } // Ensure both tables exist const tableExists = await ensureTableExists(); await ensureSettingsTableExists(); if (!tableExists) { return Response.json( { error: "Database table not found" }, { status: 500 } ); } // Get the current hunt timer status const timerStatus = await queryOne( "SELECT active, start_time, end_time FROM hunt_timer ORDER BY id DESC LIMIT 1" ); // Get the hunt active status from settings const huntActiveSetting = await queryOne( "SELECT value FROM settings WHERE key = 'hunt_active'" ); const huntActive = huntActiveSetting ? huntActiveSetting.value === "true" : false; // Use the actual timer status as the source of truth if it exists const isTimerActive = timerStatus ? !!timerStatus.active : false; // Determine the actual active state by checking both sources // This ensures we don't miss an active timer due to inconsistent settings const isActuallyActive = huntActive || isTimerActive; if (!timerStatus) { return Response.json({ active: isActuallyActive, timerActive: false, startTime: null, endTime: null, serverTime: new Date().toISOString(), timestamp: Date.now(), }); } // Add cache-busting timestamp to ensure fresh data const responseData = { // Return consistent activity status from both sources active: isActuallyActive, timerActive: isTimerActive, startTime: timerStatus.start_time, endTime: timerStatus.end_time, serverTime: new Date().toISOString(), timestamp: Date.now(), }; return Response.json(responseData, { headers: { "Cache-Control": "no-store, no-cache, must-revalidate", Pragma: "no-cache", Expires: "0", }, }); } catch (error) { console.error("Error getting timer status:", error); return Response.json( { error: "Failed to get timer status" }, { status: 500 } ); } } // POST endpoint to start/stop timer export async function POST(request) { try { // Use next-auth's getToken to properly decode the token const token = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET || "your-fallback-secret-should-be-at-least-32-chars", }); if (!token) { return Response.json({ error: "Unauthorized" }, { status: 401 }); } // Check if user is admin (more lenient check) if (token.role !== "admin") { return Response.json( { error: "Only admin can start the timer" }, { status: 403 } ); } // Create required tables if they don't exist const tableExists = await ensureTableExists(); await ensureSettingsTableExists(); if (!tableExists) { return Response.json( { error: "Failed to create hunt_timer database table" }, { status: 500 } ); } const body = await request.json(); const { action, endTime } = body; console.log("Timer action:", action, "End time:", endTime); if (action === "start") { try { // Get current time in ISO format const startTime = new Date().toISOString(); // First, deactivate any existing timers await run("UPDATE hunt_timer SET active = 0 WHERE active = 1"); // Insert new timer record await run( "INSERT INTO hunt_timer (active, start_time, end_time) VALUES (?, ?, ?)", [1, startTime, endTime || null] ); // Update hunt active status to true await updateHuntActiveStatus(true, startTime); return Response.json({ success: true, message: "Timer started successfully", startTime, endTime: endTime || null, }); } catch (dbError) { console.error("Database error starting timer:", dbError); return Response.json( { error: "Database error: " + dbError.message }, { status: 500 } ); } } else if (action === "stop") { try { // Get current time for the end timestamp const endTime = new Date().toISOString(); // Update the most recent timer to inactive await run( "UPDATE hunt_timer SET active = 0, end_time = ? WHERE active = 1", [endTime] ); // Update hunt active status to false await updateHuntActiveStatus(false); return Response.json({ success: true, message: "Timer stopped successfully", endTime: endTime, }); } catch (dbError) { console.error("Database error stopping timer:", dbError); return Response.json( { error: "Database error: " + dbError.message }, { status: 500 } ); } } else { return Response.json( { error: "Invalid action. Use 'start' or 'stop'." }, { status: 400 } ); } } catch (error) { console.error("Error managing timer:", error); return Response.json( { error: "Failed to manage timer: " + error.message, }, { status: 500 } ); } }