ThreadCount Community edition

Uniform stock management for healthcare linen rooms. Licensed under the GNU AGPL v3.
This commit is contained in:
ThreadCount
2026-09-13 08:45:19 +10:00
commit 1bc2de655a
505 changed files with 56223 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
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;
}