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 }; } // POST endpoint to fix database schema issues export async function POST(request) { try { // Verify admin access const { authorized, error, status } = await verifyAdmin(request); if (!authorized) { return Response.json({ error }, { status }); } const result = { success: true, operations: [], errors: [], }; // Get current table structure const tableInfo = await query("PRAGMA table_info(clues)"); console.log("Current clues table schema:", tableInfo); // Check if qr_code column exists and its constraints const qrCodeColumn = tableInfo.find((col) => col.name === "qr_code"); if (!qrCodeColumn) { // QR code column doesn't exist, add it with default empty string try { await run("ALTER TABLE clues ADD COLUMN qr_code TEXT DEFAULT ''"); result.operations.push( "Added qr_code column with TEXT type and empty default" ); } catch (err) { console.error("Error adding qr_code column:", err); result.errors.push(`Failed to add qr_code column: ${err.message}`); } } else if (qrCodeColumn.notnull === 1 && !qrCodeColumn.dflt_value) { // Column exists but has NOT NULL constraint with no default // We need to recreate the table to modify constraints try { // Start transaction await run("BEGIN TRANSACTION"); // Create new table with correct schema await run(` CREATE TABLE clues_new ( 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 ) `); // Copy data await run(` INSERT INTO clues_new (id, title, description, location, qr_code, created_at) SELECT id, title, description, location, COALESCE(qr_code, 'QR' || id) as qr_code, created_at FROM clues `); // Drop old table await run("DROP TABLE clues"); // Rename new table to original name await run("ALTER TABLE clues_new RENAME TO clues"); // Commit transaction await run("COMMIT"); result.operations.push("Recreated clues table with proper schema"); } catch (err) { // Rollback on error try { await run("ROLLBACK"); } catch (rollbackErr) { console.error("Error during rollback:", rollbackErr); } console.error("Error fixing table schema:", err); result.errors.push(`Failed to fix table schema: ${err.message}`); } } // Ensure all clues have proper QR codes try { // Update any NULL QR codes to match their ID await run( "UPDATE clues SET qr_code = 'QR' || id WHERE qr_code IS NULL OR qr_code = ''" ); result.operations.push("Updated any missing QR codes to match their IDs"); } catch (err) { console.error("Error updating QR codes:", err); result.errors.push(`Failed to update QR codes: ${err.message}`); } // Ensure all clues have proper sequential QR codes try { // Get all clues ordered by ID const clues = 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 < clues.length; i++) { const clue = clues[i]; const sequentialQrCode = `QR${i + 1}`; await run("UPDATE clues SET qr_code = ? WHERE id = ?", [ sequentialQrCode, clue.id, ]); } result.operations.push( "Updated all QR codes to be sequential (QR1, QR2, etc.)" ); } catch (err) { console.error("Error updating sequential QR codes:", err); result.errors.push( `Failed to update sequential QR codes: ${err.message}` ); } // Add foreign key support for team_clues table try { await run(` CREATE TABLE IF NOT EXISTS team_clues_new ( team_id INTEGER NOT NULL, clue_id INTEGER NOT NULL, found_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (team_id, clue_id), FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE CASCADE, FOREIGN KEY (clue_id) REFERENCES clues(id) ON DELETE CASCADE ) `); // Copy existing data if the table already exists try { await run(` INSERT OR IGNORE INTO team_clues_new (team_id, clue_id, found_at) SELECT team_id, clue_id, found_at FROM team_clues `); } catch (copyErr) { // If the old table doesn't exist, this is fine console.log("Note: No existing team_clues data to copy"); } // Drop the old table if it exists try { await run("DROP TABLE IF EXISTS team_clues"); } catch (dropErr) { console.error("Error dropping old team_clues table:", dropErr); } // Rename new table await run("ALTER TABLE team_clues_new RENAME TO team_clues"); result.operations.push("Updated team_clues table with ON DELETE CASCADE"); } catch (err) { console.error("Error fixing team_clues table:", err); result.errors.push(`Failed to fix team_clues table: ${err.message}`); } // Return result summary return Response.json({ ...result, message: result.errors.length === 0 ? "Database schema fixed successfully" : "Database fix completed with some errors", }); } catch (error) { console.error("Database fix error:", error); return Response.json( { error: "Failed to fix database: " + error.message }, { status: 500 } ); } }