import { NextRequest, NextResponse } from "next/server"; import { COOKIE_NAME, currentUser, signSession } from "@/lib/session"; import { allow, clientIp } from "@/lib/ratelimit"; import { demoUserFor, ensureDemo } from "@/lib/demo"; import { switches } from "@/lib/switches"; export const dynamic = "force-dynamic"; // One-click entry into the shared demo facility. Redirects use a raw relative Location so the // proxy in front of the app can't rewrite the host. export async function GET(req: NextRequest) { if (!(await switches()).demoOpen) return NextResponse.json({ error: "The demo is switched off." }, { status: 404 }); if (req.headers.get("sec-fetch-site") === "cross-site") return new NextResponse(null, { status: 303, headers: { Location: "/demo" } }); const as = req.nextUrl.searchParams.get("as") === "issuer" ? "issuer" : "admin"; // A link can't be used to swap a signed-in coordinator's real session for the demo (login CSRF). const cur = await currentUser(); if (cur && !cur.isDemo) return new NextResponse(null, { status: 303, headers: { Location: "/demo?signedin=1" } }); if (!allow("demo:" + clientIp(req.headers), 30, 10 * 60 * 1000)) return NextResponse.json({ error: "Too many requests — try again shortly." }, { status: 429 }); const f = await ensureDemo(); const u = demoUserFor(f, as); const res = new NextResponse(null, { status: 303, headers: { Location: "/app" } }); res.cookies.set(COOKIE_NAME, signSession(u.id, u.passwordHash, 60 * 60 * 4), { httpOnly: true, sameSite: "lax", secure: process.env.NODE_ENV === "production", path: "/", maxAge: 60 * 60 * 4 }); return res; }