Refactor database schema; add hunts and team_clues tables, update clues structure, and improve error handling in routes

This commit is contained in:
bobbert committed 2025-03-12 16:16:42 +00:00
1 parent 4f7fdc6ad2
commit 77e83dc21c
13 files changed
+133 -108

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitToolBoxProjectSettings">
<option name="commitMessageIssueKeyValidationOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
<option name="commitMessageValidationEnabledOverride">
<BoolValueOverride>
<option name="enabled" value="true" />
</BoolValueOverride>
</option>
</component>
</project>
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/fill-database.sql" dialect="SQLite" />
<file url="file://$PROJECT_DIR$/init-schema.sql" dialect="SQLite" />
</component>
</project>
+5 -2
View File
@@ -36,8 +36,11 @@ export default function TeamPage({ params }) {
try { try {
const errorData = JSON.parse(text); const errorData = JSON.parse(text);
errorMessage = errorData.error || "Failed to fetch team data"; errorMessage = errorData.error || "Failed to fetch team data";
} catch (e) { } catch (unused) {
errorMessage = `Failed to fetch team data: ${response.status} ${text.slice(0, 100)}`; // Using 'unused' instead of '_' to avoid the warning
errorMessage = `Failed to fetch team data: ${
response.status
} ${text.slice(0, 100)}`;
} }
throw new Error(errorMessage); throw new Error(errorMessage);
} }
+4 -5
View File
@@ -1,5 +1,7 @@
export const runtime = "nodejs"; export const runtime = "nodejs";
// Fix: Make sure we're using the correct import
import NextAuth from "next-auth"; import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { validateCredentials } from "@/actions/auth"; import { validateCredentials } from "@/actions/auth";
// Ensure NEXTAUTH_SECRET is defined // Ensure NEXTAUTH_SECRET is defined
@@ -15,10 +17,9 @@ if (!process.env.NEXTAUTH_URL) {
export const authOptions = { export const authOptions = {
debug: process.env.NODE_ENV === "development", debug: process.env.NODE_ENV === "development",
providers: [ providers: [
{ CredentialsProvider({
id: "credentials", id: "credentials",
name: "Credentials", name: "Credentials",
type: "credentials",
credentials: { credentials: {
username: { label: "Username", type: "text" }, username: { label: "Username", type: "text" },
password: { label: "Password", type: "password" }, password: { label: "Password", type: "password" },
@@ -43,7 +44,7 @@ export const authOptions = {
return null; return null;
} }
}, },
}, }),
], ],
callbacks: { callbacks: {
async jwt({ token, user }) { async jwt({ token, user }) {
@@ -66,11 +67,9 @@ export const authOptions = {
return session; return session;
}, },
}, },
// no "pages" property
session: { strategy: "jwt", maxAge: 30 * 24 * 60 * 60 }, session: { strategy: "jwt", maxAge: 30 * 24 * 60 * 60 },
secret: process.env.NEXTAUTH_SECRET, secret: process.env.NEXTAUTH_SECRET,
}; };
// Creating the handler after exporting authOptions
const handler = NextAuth(authOptions); const handler = NextAuth(authOptions);
export { handler as GET, handler as POST }; export { handler as GET, handler as POST };
+6 -4
View File
@@ -2,7 +2,8 @@ import { getServerSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]/route"; import { authOptions } from "../auth/[...nextauth]/route";
import { run } from "@/lib/db"; import { run } from "@/lib/db";
export async function POST(request) { export async function POST() {
// Removed unused 'request' parameter
const session = await getServerSession(authOptions); const session = await getServerSession(authOptions);
if (!session || session.user.role !== "admin") { if (!session || session.user.role !== "admin") {
return new Response(JSON.stringify({ error: "Unauthorized" }), { return new Response(JSON.stringify({ error: "Unauthorized" }), {
@@ -12,12 +13,13 @@ export async function POST(request) {
try { try {
// Add a WHERE clause or provide an ID if you're updating a specific hunt record // Add a WHERE clause or provide an ID if you're updating a specific hunt record
await run("UPDATE hunts SET active = 1, start_time = NOW() WHERE id = ?", [1]); await run("UPDATE hunts SET active = 1, start_time = NOW() WHERE id = ?", [
// Or if you just have one hunt record: "UPDATE hunts SET active = 1, start_time = NOW()" 1,
]);
return new Response(JSON.stringify({ success: true }), { status: 200 }); return new Response(JSON.stringify({ success: true }), { status: 200 });
} catch (err) { } catch (err) {
console.error("Hunt start error:", err); // Add logging for better debugging console.error("Hunt start error:", err);
return new Response(JSON.stringify({ error: err.message }), { return new Response(JSON.stringify({ error: err.message }), {
status: 500, status: 500,
}); });
+10 -7
View File
@@ -2,7 +2,8 @@ import { query, queryOne } from "@/lib/db";
import { getServerSession } from "next-auth/next"; import { getServerSession } from "next-auth/next";
import { authOptions } from "../../auth/[...nextauth]/route"; import { authOptions } from "../../auth/[...nextauth]/route";
export async function GET(request, context) { export async function GET(_, context) {
// Changed 'request' to '_' to indicate unused parameter
const session = await getServerSession(authOptions); const session = await getServerSession(authOptions);
if (!session) { if (!session) {
@@ -28,12 +29,14 @@ export async function GET(request, context) {
[teamId] [teamId]
); );
return Response.json({ return Response.json(
success: true, {
team, success: true,
members team,
}, { status: 200 }); members,
},
{ status: 200 }
);
} catch (error) { } catch (error) {
console.error("Team fetch error:", error); console.error("Team fetch error:", error);
return Response.json( return Response.json(
BIN
View File
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1 +1 @@
<?xml version="1.0" encoding="UTF-8"?><sqlb_project><db path="clue_hunt.db" readonly="0" foreign_keys="1" case_sensitive_like="0" temp_store="0" wal_autocheckpoint="1000" synchronous="2"/><attached/><window><main_tabs open="structure browser pragmas query" current="0"/></window><tab_structure><column_width id="0" width="300"/><column_width id="1" width="0"/><column_width id="2" width="100"/><column_width id="3" width="1504"/><column_width id="4" width="0"/><expanded_item id="0" parent="1"/><expanded_item id="1" parent="1"/><expanded_item id="2" parent="1"/><expanded_item id="3" parent="1"/></tab_structure><tab_browse><table title="users" custom_title="0" dock_id="1" table="4,5:mainusers"/><dock_state state="000000ff00000000fd0000000100000002000005a80000031dfc0100000001fb000000160064006f0063006b00420072006f00770073006500310100000000000005a80000011e00ffffff000002690000000000000004000000040000000800000008fc00000000"/><default_encoding codec=""/><browse_table_settings><table schema="main" name="clues" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="212"/><column index="2" value="315"/><column index="3" value="133"/><column index="4" value="164"/><column index="5" value="296"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="sqlite_sequence" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="47"/><column index="2" value="35"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="teams" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="35"/><column index="2" value="87"/><column index="3" value="71"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="users" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="35"/><column index="2" value="65"/><column index="3" value="127"/><column index="4" value="47"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table></browse_table_settings></tab_browse><tab_sql><sql name="initialise-databse.sql*" filename="C:/Users/mudab/WebstormProjects/pe-two/clue-hunt-app/initialise-databse.sql">-- Reference to file &quot;C:/Users/mudab/WebstormProjects/pe-two/clue-hunt-app/initialise-databse.sql&quot; (not supported by this version) --</sql><current_tab id="0"/></tab_sql></sqlb_project> <?xml version="1.0" encoding="UTF-8"?><sqlb_project><db path="clue_hunt.db" readonly="0" foreign_keys="1" case_sensitive_like="0" temp_store="0" wal_autocheckpoint="1000" synchronous="2"/><attached/><window><main_tabs open="structure browser pragmas query" current="3"/></window><tab_structure><column_width id="0" width="300"/><column_width id="1" width="0"/><column_width id="2" width="100"/><column_width id="3" width="1830"/><column_width id="4" width="0"/><expanded_item id="0" parent="1"/><expanded_item id="1" parent="1"/><expanded_item id="2" parent="1"/><expanded_item id="3" parent="1"/></tab_structure><tab_browse><table title="clues" custom_title="0" dock_id="1" table="4,5:mainclues"/><dock_state state="000000ff00000000fd0000000100000002000005a800000326fc0100000001fb000000160064006f0063006b00420072006f00770073006500310100000000000005a80000011e00ffffff000005a80000000000000004000000040000000800000008fc00000000"/><default_encoding codec=""/><browse_table_settings><table schema="main" name="clues" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="35"/><column index="2" value="55"/><column index="3" value="175"/><column index="4" value="87"/><column index="5" value="54"/><column index="6" value="159"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="hunts" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="35"/><column index="2" value="43"/><column index="3" value="69"/><column index="4" value="62"/><column index="5" value="159"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table><table schema="main" name="sqlite_sequence" show_row_id="0" encoding="" plot_x_axis="" unlock_view_pk="_rowid_" freeze_columns="0"><sort/><column_widths><column index="1" value="47"/><column index="2" value="35"/></column_widths><filter_values/><conditional_formats/><row_id_formats/><display_formats/><hidden_columns/><plot_y_axes/><global_filter/></table></browse_table_settings></tab_browse><tab_sql><sql name="initialise-databse.sql" filename="C:/Users/mudab/WebstormProjects/pe-two/clue-hunt-app/initialise-databse.sql">-- Reference to file &quot;C:/Users/mudab/WebstormProjects/pe-two/clue-hunt-app/initialise-databse.sql&quot; (not supported by this version) --</sql><current_tab id="0"/></tab_sql></sqlb_project>
BIN
View File
Binary file not shown.
+42 -44
View File
@@ -1,64 +1,62 @@
-- Schema for clue_hunt.db -- Schema for clue_hunt.db
-- Drop tables if they exist -- Drop tables if they exist
DROP TABLE IF EXISTS team_clues;
DROP TABLE IF EXISTS clues; DROP TABLE IF EXISTS clues;
DROP TABLE IF EXISTS users; DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS teams; DROP TABLE IF EXISTS teams;
DROP TABLE IF EXISTS game_settings; DROP TABLE IF EXISTS game_settings;
DROP TABLE IF EXISTS hunts;
-- Users table -- Create teams table
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'user',
team_id INTEGER, -- Ensure this column exists
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (team_id) REFERENCES teams(id)
);
-- Teams table
CREATE TABLE teams ( CREATE TABLE teams (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, name TEXT NOT NULL,
code TEXT UNIQUE NOT NULL, code TEXT NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); );
-- Clues table -- Create users table
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
role TEXT DEFAULT 'user',
team_id INTEGER,
FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE SET NULL
);
-- Create clues table
CREATE TABLE clues ( CREATE TABLE clues (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
description TEXT NOT NULL, title TEXT NOT NULL,
team_id INTEGER NOT NULL, description TEXT NOT NULL,
found BOOLEAN DEFAULT FALSE, location TEXT NOT NULL,
found_at TIMESTAMP, qr_code TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
FOREIGN KEY (team_id) REFERENCES teams(id)
); );
-- Game settings table -- Create team_clues table (for tracking which teams have found which clues)
CREATE TABLE game_settings ( CREATE TABLE team_clues (
id INTEGER PRIMARY KEY, team_id INTEGER NOT NULL,
hunt_active BOOLEAN DEFAULT FALSE, clue_id INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, found_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_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
); );
-- Execute this in your database: -- Create hunts table
CREATE TABLE IF NOT EXISTS hunts ( CREATE TABLE hunts (
id INT AUTO_INCREMENT PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
active BOOLEAN DEFAULT FALSE, active BOOLEAN DEFAULT 0,
start_time DATETIME DEFAULT NULL, start_time DATETIME DEFAULT NULL,
end_time DATETIME DEFAULT NULL, end_time DATETIME DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); );
-- Insert a default hunt row if none exists: -- Insert a default hunt row
INSERT INTO hunts (active) INSERT INTO hunts (active) SELECT 0 WHERE NOT EXISTS (SELECT 1 FROM hunts LIMIT 1);
SELECT FALSE
WHERE NOT EXISTS (SELECT 1 FROM hunts LIMIT 1);
-- Insert initial admin user -- Insert initial admin user
INSERT INTO users (username, password, role) INSERT INTO users (username, password, role)
@@ -70,8 +68,8 @@ INSERT INTO teams (name, code) VALUES ('Team Alpha', 'ALPHA123');
INSERT INTO teams (name, code) VALUES ('Team Beta', 'BETA456'); INSERT INTO teams (name, code) VALUES ('Team Beta', 'BETA456');
INSERT INTO teams (name, code) VALUES ('Team Gamma', 'GAMMA789'); INSERT INTO teams (name, code) VALUES ('Team Gamma', 'GAMMA789');
-- Insert sample clues for Team Alpha -- Insert sample clues
INSERT INTO clues (description, team_id) VALUES ('Find the red door', 1); INSERT INTO clues (title, description, location, qr_code) VALUES ('Clue 1', 'Find the red door', 'Location 1', 'QR1');
INSERT INTO clues (description, team_id) VALUES ('Look under the bridge', 1); INSERT INTO clues (title, description, location, qr_code) VALUES ('Clue 2', 'Look under the bridge', 'Location 2', 'QR2');
INSERT INTO clues (description, team_id) VALUES ('Check the library', 1); INSERT INTO clues (title, description, location, qr_code) VALUES ('Clue 3', 'Check the library', 'Location 3', 'QR3');
+42 -44
View File
@@ -1,64 +1,62 @@
-- Schema for clue_hunt.db -- Schema for clue_hunt.db
-- Drop tables if they exist -- Drop tables if they exist
DROP TABLE IF EXISTS team_clues;
DROP TABLE IF EXISTS clues; DROP TABLE IF EXISTS clues;
DROP TABLE IF EXISTS users; DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS teams; DROP TABLE IF EXISTS teams;
DROP TABLE IF EXISTS game_settings; DROP TABLE IF EXISTS game_settings;
DROP TABLE IF EXISTS hunts;
-- Users table -- Create teams table
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL,
role TEXT NOT NULL DEFAULT 'user',
team_id INTEGER, -- Ensure this column exists
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (team_id) REFERENCES teams(id)
);
-- Teams table
CREATE TABLE teams ( CREATE TABLE teams (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, name TEXT NOT NULL,
code TEXT UNIQUE NOT NULL, code TEXT NOT NULL UNIQUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); );
-- Clues table -- Create users table
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
role TEXT DEFAULT 'user',
team_id INTEGER,
FOREIGN KEY (team_id) REFERENCES teams(id) ON DELETE SET NULL
);
-- Create clues table
CREATE TABLE clues ( CREATE TABLE clues (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
description TEXT NOT NULL, title TEXT NOT NULL,
team_id INTEGER NOT NULL, description TEXT NOT NULL,
found BOOLEAN DEFAULT FALSE, location TEXT NOT NULL,
found_at TIMESTAMP, qr_code TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
FOREIGN KEY (team_id) REFERENCES teams(id)
); );
-- Game settings table -- Create team_clues table (for tracking which teams have found which clues)
CREATE TABLE game_settings ( CREATE TABLE team_clues (
id INTEGER PRIMARY KEY, team_id INTEGER NOT NULL,
hunt_active BOOLEAN DEFAULT FALSE, clue_id INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, found_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_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
); );
-- Execute this in your database: -- Create hunts table
CREATE TABLE IF NOT EXISTS hunts ( CREATE TABLE hunts (
id INT AUTO_INCREMENT PRIMARY KEY, id INTEGER PRIMARY KEY AUTOINCREMENT,
active BOOLEAN DEFAULT FALSE, active BOOLEAN DEFAULT 0,
start_time DATETIME DEFAULT NULL, start_time DATETIME DEFAULT NULL,
end_time DATETIME DEFAULT NULL, end_time DATETIME DEFAULT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
); );
-- Insert a default hunt row if none exists: -- Insert a default hunt row
INSERT INTO hunts (active) INSERT INTO hunts (active) SELECT 0 WHERE NOT EXISTS (SELECT 1 FROM hunts LIMIT 1);
SELECT FALSE
WHERE NOT EXISTS (SELECT 1 FROM hunts LIMIT 1);
-- Insert initial admin user -- Insert initial admin user
INSERT INTO users (username, password, role) INSERT INTO users (username, password, role)
@@ -70,8 +68,8 @@ INSERT INTO teams (name, code) VALUES ('Team Alpha', 'ALPHA123');
INSERT INTO teams (name, code) VALUES ('Team Beta', 'BETA456'); INSERT INTO teams (name, code) VALUES ('Team Beta', 'BETA456');
INSERT INTO teams (name, code) VALUES ('Team Gamma', 'GAMMA789'); INSERT INTO teams (name, code) VALUES ('Team Gamma', 'GAMMA789');
-- Insert sample clues for Team Alpha -- Insert sample clues
INSERT INTO clues (description, team_id) VALUES ('Find the red door', 1); INSERT INTO clues (title, description, location, qr_code) VALUES ('Clue 1', 'Find the red door', 'Location 1', 'QR1');
INSERT INTO clues (description, team_id) VALUES ('Look under the bridge', 1); INSERT INTO clues (title, description, location, qr_code) VALUES ('Clue 2', 'Look under the bridge', 'Location 2', 'QR2');
INSERT INTO clues (description, team_id) VALUES ('Check the library', 1); INSERT INTO clues (title, description, location, qr_code) VALUES ('Clue 3', 'Check the library', 'Location 3', 'QR3');
+1 -1
View File
@@ -4,7 +4,7 @@ let db = null;
export function getDb() { export function getDb() {
if (!db) { if (!db) {
db = new sqlite3.Database("./database.sqlite", (err) => { db = new sqlite3.Database("./clue_hunt.db", (err) => {
if (err) { if (err) {
console.error("Error connecting to database:", err); console.error("Error connecting to database:", err);
} else { } else {