/** * Dev identity — temporary stand-in for real PB auth. * * The project is currently in "no auth" mode: PB's `users` collection * is empty by design, so `assertBrowserSession` (used elsewhere in the * API) always fails. Features that need caller identity for * attribution (claim `claimed_by`, future audit trails, "claimed by * me" detection) read identity from a cookie called * `wfdash_dev_email` instead. Default is a placeholder email that * matches the display name we expect in the UI badges. * * Identity resolution (server-side, in order): * 1. `X-User-Email` request header (explicit per-request override) * 2. `wfdash_dev_email` cookie (the dev user switcher in the UI) * 3. `DEV_USER_EMAIL` env var (CI / scripted requests) * 4. `DEFAULT_DEV_EMAIL` constant (fallback) * * Migration path: when real PB auth lands, replace `getServerIdentity` * with `assertBrowserSession` and have the login flow write the real * email to this cookie. The rest of the call sites stay the same. */ import type { NextRequest } from "next/server"; export const DEV_IDENTITY_COOKIE = "wfdash_dev_email"; export const DEV_IDENTITY_HEADER = "x-user-email"; export const DEFAULT_DEV_EMAIL = "david.els1@outlook.com"; function decode(value: string | undefined | null): string { if (!value) return ""; try { return decodeURIComponent(value); } catch { return value; } } function normalize(raw: string | undefined | null): string { const decoded = decode(raw).trim(); return decoded; } /** * Server-side: read the caller's dev identity from the request. * Always returns a non-empty string. */ export function getServerIdentity(req: NextRequest): string { const header = req.headers.get(DEV_IDENTITY_HEADER); const headerVal = normalize(header); if (headerVal) return headerVal; const cookieVal = normalize(req.cookies.get(DEV_IDENTITY_COOKIE)?.value); if (cookieVal) return cookieVal; return normalize(process.env.DEV_USER_EMAIL) || DEFAULT_DEV_EMAIL; } /** * Client-side: read the current dev identity from `document.cookie`. * Falls back to the default on SSR / when the cookie isn't set. */ export function getClientIdentity(): string { if (typeof document === "undefined") return DEFAULT_DEV_EMAIL; const escaped = DEV_IDENTITY_COOKIE.replace( /[.*+?^${}()|[\]\\]/g, "\\$&", ); const m = document.cookie.match( new RegExp(`(?:^|;\\s*)${escaped}=([^;]+)`), ); if (!m) return DEFAULT_DEV_EMAIL; const decoded = decode(m[1]).trim(); return decoded || DEFAULT_DEV_EMAIL; } /** * Client-side: persist a new dev identity to the cookie. * Path is `/` so the API routes can read it, max-age is 1 year. */ export function setClientIdentity(email: string): void { if (typeof document === "undefined") return; const value = encodeURIComponent(email.trim() || DEFAULT_DEV_EMAIL); const oneYear = 60 * 60 * 24 * 365; document.cookie = `${DEV_IDENTITY_COOKIE}=${value}; path=/; max-age=${oneYear}; SameSite=Lax`; }