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
+136 -111

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 {
const errorData = JSON.parse(text);
errorMessage = errorData.error || "Failed to fetch team data";
} catch (e) {
errorMessage = `Failed to fetch team data: ${response.status} ${text.slice(0, 100)}`;
} catch (unused) {
// Using 'unused' instead of '_' to avoid the warning
errorMessage = `Failed to fetch team data: ${
response.status
} ${text.slice(0, 100)}`;
}
throw new Error(errorMessage);
}
+4 -5
View File
@@ -1,5 +1,7 @@
export const runtime = "nodejs";
// Fix: Make sure we're using the correct import
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { validateCredentials } from "@/actions/auth";
// Ensure NEXTAUTH_SECRET is defined
@@ -15,10 +17,9 @@ if (!process.env.NEXTAUTH_URL) {
export const authOptions = {
debug: process.env.NODE_ENV === "development",
providers: [
{
CredentialsProvider({
id: "credentials",
name: "Credentials",
type: "credentials",
credentials: {
username: { label: "Username", type: "text" },
password: { label: "Password", type: "password" },
@@ -43,7 +44,7 @@ export const authOptions = {
return null;
}
},
},
}),
],
callbacks: {
async jwt({ token, user }) {
@@ -66,11 +67,9 @@ export const authOptions = {
return session;
},
},
// no "pages" property
session: { strategy: "jwt", maxAge: 30 * 24 * 60 * 60 },
secret: process.env.NEXTAUTH_SECRET,
};
// Creating the handler after exporting authOptions
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
+7 -5
View File
@@ -2,7 +2,8 @@ import { getServerSession } from "next-auth/next";
import { authOptions } from "../auth/[...nextauth]/route";
import { run } from "@/lib/db";
export async function POST(request) {
export async function POST() {
// Removed unused 'request' parameter
const session = await getServerSession(authOptions);
if (!session || session.user.role !== "admin") {
return new Response(JSON.stringify({ error: "Unauthorized" }), {
@@ -12,12 +13,13 @@ export async function POST(request) {
try {
// 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]);
// Or if you just have one hunt record: "UPDATE hunts SET active = 1, start_time = NOW()"
await run("UPDATE hunts SET active = 1, start_time = NOW() WHERE id = ?", [
1,
]);
return new Response(JSON.stringify({ success: true }), { status: 200 });
} 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 }), {
status: 500,
});
+12 -9
View File
@@ -2,7 +2,8 @@ import { query, queryOne } from "@/lib/db";
import { getServerSession } from "next-auth/next";
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);
if (!session) {
@@ -11,7 +12,7 @@ export async function GET(request, context) {
try {
const teamId = context.params.teamId;
// Validate teamId
if (!teamId || isNaN(parseInt(teamId, 10))) {
return Response.json({ error: "Invalid team ID" }, { status: 400 });
@@ -24,16 +25,18 @@ export async function GET(request, context) {
}
const members = await query(
"SELECT id, username FROM users WHERE team_id = ?",
"SELECT id, username FROM users WHERE team_id = ?",
[teamId]
);
return Response.json({
success: true,
team,
members
}, { status: 200 });
return Response.json(
{
success: true,
team,
members,
},
{ status: 200 }
);
} catch (error) {
console.error("Team fetch error:", error);
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
-- Drop tables if they exist
DROP TABLE IF EXISTS team_clues;
DROP TABLE IF EXISTS clues;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS teams;
DROP TABLE IF EXISTS game_settings;
DROP TABLE IF EXISTS hunts;
-- Users 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 teams table
CREATE TABLE teams (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
code TEXT UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
code TEXT NOT NULL UNIQUE,
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 (
id INTEGER PRIMARY KEY AUTOINCREMENT,
description TEXT NOT NULL,
team_id INTEGER NOT NULL,
found BOOLEAN DEFAULT FALSE,
found_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (team_id) REFERENCES teams(id)
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT NOT NULL,
location TEXT NOT NULL,
qr_code TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Game settings table
CREATE TABLE game_settings (
id INTEGER PRIMARY KEY,
hunt_active BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-- Create team_clues table (for tracking which teams have found which clues)
CREATE TABLE team_clues (
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
);
-- Execute this in your database:
CREATE TABLE IF NOT EXISTS hunts (
id INT AUTO_INCREMENT PRIMARY KEY,
active BOOLEAN DEFAULT FALSE,
-- Create hunts table
CREATE TABLE 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
);
-- Insert a default hunt row if none exists:
INSERT INTO hunts (active)
SELECT FALSE
WHERE NOT EXISTS (SELECT 1 FROM hunts LIMIT 1);
-- Insert a default hunt row
INSERT INTO hunts (active) SELECT 0 WHERE NOT EXISTS (SELECT 1 FROM hunts LIMIT 1);
-- Insert initial admin user
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 Gamma', 'GAMMA789');
-- Insert sample clues for Team Alpha
INSERT INTO clues (description, team_id) VALUES ('Find the red door', 1);
INSERT INTO clues (description, team_id) VALUES ('Look under the bridge', 1);
INSERT INTO clues (description, team_id) VALUES ('Check the library', 1);
-- Insert sample clues
INSERT INTO clues (title, description, location, qr_code) VALUES ('Clue 1', 'Find the red door', 'Location 1', 'QR1');
INSERT INTO clues (title, description, location, qr_code) VALUES ('Clue 2', 'Look under the bridge', 'Location 2', 'QR2');
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
-- Drop tables if they exist
DROP TABLE IF EXISTS team_clues;
DROP TABLE IF EXISTS clues;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS teams;
DROP TABLE IF EXISTS game_settings;
DROP TABLE IF EXISTS hunts;
-- Users 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 teams table
CREATE TABLE teams (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
code TEXT UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
code TEXT NOT NULL UNIQUE,
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 (
id INTEGER PRIMARY KEY AUTOINCREMENT,
description TEXT NOT NULL,
team_id INTEGER NOT NULL,
found BOOLEAN DEFAULT FALSE,
found_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (team_id) REFERENCES teams(id)
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT NOT NULL,
location TEXT NOT NULL,
qr_code TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Game settings table
CREATE TABLE game_settings (
id INTEGER PRIMARY KEY,
hunt_active BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
-- Create team_clues table (for tracking which teams have found which clues)
CREATE TABLE team_clues (
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
);
-- Execute this in your database:
CREATE TABLE IF NOT EXISTS hunts (
id INT AUTO_INCREMENT PRIMARY KEY,
active BOOLEAN DEFAULT FALSE,
-- Create hunts table
CREATE TABLE 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
);
-- Insert a default hunt row if none exists:
INSERT INTO hunts (active)
SELECT FALSE
WHERE NOT EXISTS (SELECT 1 FROM hunts LIMIT 1);
-- Insert a default hunt row
INSERT INTO hunts (active) SELECT 0 WHERE NOT EXISTS (SELECT 1 FROM hunts LIMIT 1);
-- Insert initial admin user
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 Gamma', 'GAMMA789');
-- Insert sample clues for Team Alpha
INSERT INTO clues (description, team_id) VALUES ('Find the red door', 1);
INSERT INTO clues (description, team_id) VALUES ('Look under the bridge', 1);
INSERT INTO clues (description, team_id) VALUES ('Check the library', 1);
-- Insert sample clues
INSERT INTO clues (title, description, location, qr_code) VALUES ('Clue 1', 'Find the red door', 'Location 1', 'QR1');
INSERT INTO clues (title, description, location, qr_code) VALUES ('Clue 2', 'Look under the bridge', 'Location 2', 'QR2');
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() {
if (!db) {
db = new sqlite3.Database("./database.sqlite", (err) => {
db = new sqlite3.Database("./clue_hunt.db", (err) => {
if (err) {
console.error("Error connecting to database:", err);
} else {