import { getToken } from "next-auth/jwt"; import { query, queryOne, run } from "@/lib/db"; export const dynamic = "force-dynamic"; // Disable caching // Ensure the user is an admin async function verifyAdmin(request) { const token = await getToken({ req: request, secret: process.env.NEXTAUTH_SECRET || "your-fallback-secret-should-be-at-least-32-chars", }); if (!token) { return { authorized: false, error: "Unauthorized", status: 401 }; } if (token.role !== "admin") { return { authorized: false, error: "Admin access required", status: 403 }; } return { authorized: true, token }; } // GET - Fetch all clues for admin export async function GET(request) { try { // Verify admin access const { authorized, error, status, token } = await verifyAdmin(request); if (!authorized) { return Response.json({ error }, { status }); } // Ensure clues table exists with simplified structure (no coordinates) await run(` CREATE TABLE IF NOT EXISTS clues ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, description TEXT NOT NULL, location TEXT, qr_code TEXT DEFAULT '', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); // Fetch all clues with sequential ordering - IMPORTANT: explicitly include qr_code field const clues = await query(` SELECT id, title, description, location, qr_code, created_at FROM clues ORDER BY id ASC `); console.log(`Admin API: Returning ${clues.length} clues`); return Response.json(clues, { headers: { "Cache-Control": "no-store, no-cache, must-revalidate", Pragma: "no-cache", Expires: "0", }, }); } catch (error) { console.error("Admin clue fetch error:", error); return Response.json( { error: "Failed to fetch clues: " + error.message }, { status: 500 } ); } } // POST - Add a new clue export async function POST(request) { try { // Verify admin access const { authorized, error, status } = await verifyAdmin(request); if (!authorized) { return Response.json({ error }, { status }); } // Parse the request body - now accepting qr_code let bodyData; try { bodyData = await request.json(); } catch (parseError) { console.error("Error parsing JSON request body:", parseError); return Response.json( { error: "Invalid JSON in request body" }, { status: 400 } ); } const { title, description, location = "", qr_code = "" } = bodyData; // Add more detailed validation if (!title) { return Response.json({ error: "Title is required" }, { status: 400 }); } if (!description) { return Response.json( { error: "Description is required" }, { status: 400 } ); } // Validate QR code format if provided if (qr_code && !qr_code.match(/^QR\d+$/i)) { return Response.json( { error: "QR code must follow the format QR[number], e.g. QR1, QR2" }, { status: 400 } ); } // Make sure the clues table exists before inserting try { await run(` CREATE TABLE IF NOT EXISTS clues ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, description TEXT NOT NULL, location TEXT, qr_code TEXT DEFAULT '', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); // Check if we need to add the qr_code column (for existing tables that don't have it) try { // First check if the column exists const tableInfo = await query(`PRAGMA table_info(clues)`); const qrCodeColumnExists = tableInfo.some( (col) => col.name === "qr_code" ); if (!qrCodeColumnExists) { // If the column doesn't exist, add it with a default value await run(`ALTER TABLE clues ADD COLUMN qr_code TEXT DEFAULT ''`); console.log("Added qr_code column to clues table"); } } catch (alterTableError) { console.error( "Error checking/modifying table structure:", alterTableError ); // Continue anyway - the insertion will either work or fail } } catch (tableError) { console.error("Error ensuring table exists:", tableError); return Response.json( { error: "Database error: Could not ensure clues table exists" }, { status: 500 } ); } // Insert the new clue with QR code if provided let result; try { if (qr_code) { // If QR code is provided, use it result = await run( `INSERT INTO clues (title, description, location, qr_code) VALUES (?, ?, ?, ?)`, [title, description, location, qr_code] ); } else { // Otherwise, just insert without QR code (will be set later) result = await run( `INSERT INTO clues (title, description, location) VALUES (?, ?, ?)`, [title, description, location] ); } } catch (insertError) { console.error("Database insert error:", insertError); return Response.json( { error: `Database error: ${insertError.message}` }, { status: 500 } ); } // Get the inserted clue ID and update QR codes let newClueId; try { const newClue = await queryOne("SELECT last_insert_rowid() as id"); newClueId = newClue?.id; // If no QR code was provided, update all QR codes to ensure sequential order if (!qr_code && newClueId) { // Update all clues to have sequential QR codes const allClues = await query("SELECT id FROM clues ORDER BY id ASC"); // Update each clue to have QR[index+1] format (sequential without gaps) for (let i = 0; i < allClues.length; i++) { const clue = allClues[i]; const sequentialQrCode = `QR${i + 1}`; await run("UPDATE clues SET qr_code = ? WHERE id = ?", [ sequentialQrCode, clue.id, ]); } } } catch (updateError) { console.error("Error updating QR codes:", updateError); // Continue anyway, the clue was inserted successfully } return Response.json( { success: true, message: "Clue added successfully", clueId: newClueId || null, }, { status: 201 } ); } catch (error) { console.error("Admin clue creation error:", error); return Response.json( { error: "Failed to add clue: " + error.message }, { status: 500 } ); } }