diff --git a/src/app/api/v1/boss-dashboard/route.ts b/src/app/api/v1/boss-dashboard/route.ts new file mode 100644 index 0000000..81122ee --- /dev/null +++ b/src/app/api/v1/boss-dashboard/route.ts @@ -0,0 +1,61 @@ +import { NextRequest, NextResponse } from "next/server"; +import { getBossDashboardData, type BossWindow } from "@/lib/server/boss-dashboard-service"; + +/** + * GET /api/v1/boss-dashboard?window=today|week|month + * + * Read model for the boss's phone dashboard at `/m`. Returns a single + * window's worth of aggregates (~3-5 KB). Auth via the PB browser + * session — same pattern as /api/v1/dashboard. + */ +export const runtime = "nodejs"; +export const dynamic = "force-dynamic"; + +const VALID_WINDOWS: BossWindow[] = ["today", "week", "month"]; + +async function assertBrowserSession(req: NextRequest) { + const auth = req.headers.get("authorization"); + if (!auth) { + throw Object.assign(new Error("Not authenticated."), { status: 401 }); + } + const pbUrl = + process.env.NEXT_PUBLIC_POCKETBASE_URL || "http://127.0.0.1:8090"; + const token = auth.startsWith("Bearer ") ? auth.slice(7) : auth; + const res = await fetch( + `${pbUrl.replace(/\/$/, "")}/api/collections/users/auth-refresh`, + { + method: "POST", + headers: { Authorization: token }, + signal: AbortSignal.timeout(5000), + }, + ); + if (!res.ok) { + throw Object.assign(new Error("Invalid or expired session."), { + status: 401, + }); + } + return res.json(); +} + +export async function GET(req: NextRequest) { + try { + const user = await assertBrowserSession(req); + + const url = new URL(req.url); + const raw = url.searchParams.get("window") ?? "today"; + if (!VALID_WINDOWS.includes(raw as BossWindow)) { + return NextResponse.json( + { error: `Invalid window: ${raw}` }, + { status: 400 }, + ); + } + + const data = await getBossDashboardData({ window: raw as BossWindow }); + return NextResponse.json({ ...data, viewerName: user?.record?.name ?? null }); + } catch (error) { + const message = + error instanceof Error ? error.message : "Failed to load dashboard"; + const status = (error as Error & { status?: number }).status || 500; + return NextResponse.json({ error: message }, { status }); + } +}