diff --git a/app/(main)/team/[teamId]/page.js b/app/(main)/team/[teamId]/page.js index 194efa1..5066cdc 100644 --- a/app/(main)/team/[teamId]/page.js +++ b/app/(main)/team/[teamId]/page.js @@ -5,7 +5,7 @@ import { useRouter } from "next/navigation"; import Link from "next/link"; export default function TeamPage({ params }) { - const { status } = useSession(); + const { status, data: session } = useSession(); const [team, setTeam] = useState(null); const [teamMembers, setTeamMembers] = useState([]); const [loading, setLoading] = useState(true); @@ -20,6 +20,7 @@ export default function TeamPage({ params }) { throw new Error("Invalid team ID"); } + // Fix the API endpoint path - ensure it matches your route structure const response = await fetch(`/api/teams/${teamId}`, { method: "GET", headers: { @@ -28,9 +29,17 @@ export default function TeamPage({ params }) { credentials: "include", }); + // Add better error handling for debugging if (!response.ok) { - const errorData = await response.json(); - throw new Error(errorData.error || "Failed to fetch team data"); + const text = await response.text(); + let errorMessage; + 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)}`; + } + throw new Error(errorMessage); } const data = await response.json(); @@ -100,6 +109,26 @@ export default function TeamPage({ params }) { +
+ + +
+
🏠
@@ -214,6 +243,30 @@ export default function TeamPage({ params }) { color: black; } + .team-footer { + display: flex; + justify-content: space-around; + padding: 15px 0; + background-color: #c8b6a6; + } + + .leave-button, + .start-hunt-button { + background-color: #e9c46a; + color: black; + padding: 10px 20px; + border: none; + border-radius: 5px; + font-weight: bold; + cursor: pointer; + } + + .leave-button:disabled, + .start-hunt-button:disabled { + background-color: #ccc; + cursor: not-allowed; + } + .navbar { display: flex; justify-content: space-around; diff --git a/app/api/auth/[...nextauth]/route.js b/app/api/auth/[...nextauth]/route.js index 1d02ad8..26ea97b 100644 --- a/app/api/auth/[...nextauth]/route.js +++ b/app/api/auth/[...nextauth]/route.js @@ -1,33 +1,49 @@ +export const runtime = "nodejs"; import NextAuth from "next-auth"; -import CredentialsProvider from "next-auth/providers/credentials"; import { validateCredentials } from "@/actions/auth"; +// Ensure NEXTAUTH_SECRET is defined +if (!process.env.NEXTAUTH_SECRET) { + console.error("Warning: NEXTAUTH_SECRET is not defined"); +} + +// Ensure NEXTAUTH_URL is defined +if (!process.env.NEXTAUTH_URL) { + console.error("Warning: NEXTAUTH_URL is not defined"); +} + 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" }, }, async authorize(credentials) { - if (!credentials?.username || !credentials?.password) return null; - const user = await validateCredentials( - credentials.username, - credentials.password - ); - return user - ? { - id: user.id, - username: user.username, - role: user.role, - teamId: user.teamId, - } - : null; + try { + if (!credentials?.username || !credentials?.password) return null; + const user = await validateCredentials( + credentials.username, + credentials.password + ); + return user + ? { + id: user.id, + username: user.username, + role: user.role, + teamId: user.teamId, + } + : null; + } catch (error) { + console.error("NextAuth authorize error:", error); + return null; + } }, - }), + }, ], callbacks: { async jwt({ token, user }) { @@ -50,10 +66,11 @@ export const authOptions = { return session; }, }, - pages: { signIn: "/login" }, + // 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 }; diff --git a/app/api/hunt/route.js b/app/api/hunt/route.js new file mode 100644 index 0000000..3b18de2 --- /dev/null +++ b/app/api/hunt/route.js @@ -0,0 +1,25 @@ +import { getServerSession } from "next-auth/next"; +import { authOptions } from "../auth/[...nextauth]/route"; +import { run } from "@/lib/db"; + +export async function POST(request) { + const session = await getServerSession(authOptions); + if (!session || session.user.role !== "admin") { + return new Response(JSON.stringify({ error: "Unauthorized" }), { + status: 401, + }); + } + + 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()" + + return new Response(JSON.stringify({ success: true }), { status: 200 }); + } catch (err) { + console.error("Hunt start error:", err); // Add logging for better debugging + return new Response(JSON.stringify({ error: err.message }), { + status: 500, + }); + } +} diff --git a/app/api/teams/[teamId]/route.js b/app/api/teams/[teamId]/route.js index cab71cc..c891b6b 100644 --- a/app/api/teams/[teamId]/route.js +++ b/app/api/teams/[teamId]/route.js @@ -1,50 +1,43 @@ -import { queryOne, query } from "../../../../lib/db"; +import { query, queryOne } from "@/lib/db"; import { getServerSession } from "next-auth/next"; import { authOptions } from "../../auth/[...nextauth]/route"; -export async function GET({ params }) { +export async function GET(request, context) { const session = await getServerSession(authOptions); if (!session) { - return new Response(JSON.stringify({ error: "Unauthorized" }), { - status: 401, - }); + return Response.json({ error: "Unauthorized" }, { status: 401 }); } try { - const teamId = parseInt(params.teamId, 10); - if (isNaN(teamId)) { - return new Response(JSON.stringify({ error: "Invalid team ID" }), { - status: 400, - }); + const teamId = context.params.teamId; + + // Validate teamId + if (!teamId || isNaN(parseInt(teamId, 10))) { + return Response.json({ error: "Invalid team ID" }, { status: 400 }); } - const team = await queryOne( - "SELECT id, name, code FROM teams WHERE id = ?", - [teamId] - ); + const team = await queryOne("SELECT * FROM teams WHERE id = ?", [teamId]); if (!team) { - return new Response(JSON.stringify({ error: "Team not found" }), { - status: 404, - }); + return Response.json({ error: "Team not found" }, { status: 404 }); } const members = await query( - "SELECT id, username FROM users WHERE team_id = ?", + "SELECT id, username FROM users WHERE team_id = ?", [teamId] ); - return new Response(JSON.stringify({ team, members }), { - status: 200, - headers: { - "Content-Type": "application/json", - }, - }); + return Response.json({ + success: true, + team, + members + }, { status: 200 }); + } catch (error) { console.error("Team fetch error:", error); - return new Response( - JSON.stringify({ error: "Failed to fetch team details" }), + return Response.json( + { error: "Failed to fetch team data" }, { status: 500 } ); } diff --git a/database.sqlite b/database.sqlite index 19ecf54..05bc2d1 100644 Binary files a/database.sqlite and b/database.sqlite differ diff --git a/init-schema.sql b/init-schema.sql index e433ac8..f2a7231 100644 --- a/init-schema.sql +++ b/init-schema.sql @@ -44,6 +44,22 @@ CREATE TABLE game_settings ( updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); +-- Execute this in your database: +CREATE TABLE IF NOT EXISTS hunts ( + id INT AUTO_INCREMENT PRIMARY KEY, + active BOOLEAN DEFAULT FALSE, + 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 initial admin user INSERT INTO users (username, password, role) VALUES ('admin', '$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy', 'admin'); diff --git a/package-lock.json b/package-lock.json index 53313e9..3390362 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,9 +46,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", - "integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", + "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" diff --git a/yarn.lock b/yarn.lock index ef65a7d..7f51566 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8,9 +8,9 @@ integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw== "@babel/runtime@^7.20.13": - version "7.26.9" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz" - integrity sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg== + version "7.26.10" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz" + integrity sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw== dependencies: regenerator-runtime "^0.14.0"