Repository Public MIT
1import "server-only";2import { cookies, headers } from "next/headers";3import { redirect } from "next/navigation";4import { SESSION_COOKIE, SESSION_TTL_HOURS, isPlatformAdmin, principalFromSession } from "@/server/auth";5import { clientIp, makeCtx, type Ctx, type Principal } from "@/server/context";6import { env } from "@/server/env";78/** Request context for server components and server actions (cookie session or anonymous). */9export async function getCtx(): Promise<Ctx> {10 const h = await headers();11 const c = await cookies();12 const ctx = makeCtx({ ip: clientIp(h), userAgent: h.get("user-agent") ?? "" });13 ctx.principal = await principalFromSession(ctx, c.get(SESSION_COOKIE)?.value);14 return ctx;15}1617/** Anonymous context: public pages never vary with the viewer's session. */18export async function publicCtx(): Promise<Ctx> {19 const h = await headers();20 return makeCtx({ ip: clientIp(h), userAgent: h.get("user-agent") ?? "" });21}2223export type SessionPrincipal = Extract<Principal, { kind: "session" }>;2425export async function requireSession(opts: { allowPasswordChange?: boolean } = {}): Promise<{ ctx: Ctx; principal: SessionPrincipal; isAdmin: boolean }> {26 const ctx = await getCtx();27 if (ctx.principal.kind !== "session") redirect("/login");28 if (ctx.principal.user.must_change_password && !opts.allowPasswordChange) redirect("/dashboard/account?first=1");29 return { ctx, principal: ctx.principal, isAdmin: isPlatformAdmin(ctx.principal.user) };30}3132export async function setSessionCookie(token: string) {33 (await cookies()).set(SESSION_COOKIE, token, { httpOnly: true, secure: env.isProd, sameSite: "lax", path: "/", maxAge: SESSION_TTL_HOURS * 3600 });34}35export async function clearSessionCookie() {36 (await cookies()).delete(SESSION_COOKIE);37}38