import { createHash } from "node:crypto"; import { NextResponse, type NextRequest } from "next/server"; import { Pool } from "pg"; // Session-Refresh + Zugriffsschutz für die internen Bereiche. // Next 16 führt Proxy standardmäßig im Node.js-Runtime aus, direkter // pg-Zugriff ist hier also möglich (kein Edge-Workaround nötig). // // Der Cookie-Name ist hier bewusst dupliziert statt aus lib/auth/session // importiert: dieses Modul zieht next/headers (cookies()) nach, das // außerhalb von Server Components/Route Handlern/Server Actions nicht // nutzbar ist – Proxy läuft in einem eigenen Ausführungskontext. const SESSION_COOKIE = "session"; declare global { var __pgPoolProxy: Pool | undefined; } const pool = globalThis.__pgPoolProxy ?? new Pool({ connectionString: process.env.DATABASE_URL }); if (process.env.NODE_ENV !== "production") globalThis.__pgPoolProxy = pool; const REFRESH_THRESHOLD_MS = 7 * 24 * 60 * 60 * 1000; const SESSION_LIFETIME_MS = 30 * 24 * 60 * 60 * 1000; function hashToken(token: string): string { return createHash("sha256").update(token).digest("hex"); } export async function proxy(request: NextRequest) { const token = request.cookies.get(SESSION_COOKIE)?.value; function redirectToLogin() { // Absolute URL aus dem Host-Header des Browsers bauen: request.nextUrl // trägt bei "-H 0.0.0.0" die Server-Bindung statt des aufgerufenen // Hosts, und relative Location-Header lehnt die Middleware ab. const host = request.headers.get("host") ?? request.nextUrl.host; const proto = request.headers.get("x-forwarded-proto") ?? request.nextUrl.protocol.replace(":", ""); return NextResponse.redirect(`${proto}://${host}/login`, 303); } if (!token) return redirectToLogin(); const tokenHash = hashToken(token); const { rows } = await pool.query<{ expires_at: Date }>( "select expires_at from sessions where token_hash = $1 and expires_at > now()", [tokenHash] ); const session = rows[0]; if (!session) return redirectToLogin(); const response = NextResponse.next(); // Sliding-Refresh: bei weniger als 7 Tagen Restlaufzeit Sitzung und // Cookie verlängern, statt bei jedem Request zu schreiben. const msRemaining = session.expires_at.getTime() - Date.now(); if (msRemaining < REFRESH_THRESHOLD_MS) { const newExpiry = new Date(Date.now() + SESSION_LIFETIME_MS); await pool.query("update sessions set expires_at = $1, last_seen_at = now() where token_hash = $2", [ newExpiry, tokenHash, ]); response.cookies.set(SESSION_COOKIE, token, { httpOnly: true, secure: (process.env.APP_URL ?? "").startsWith("https://"), sameSite: "lax", path: "/", expires: newExpiry, }); } return response; } export const config = { matcher: ["/intern/:path*", "/admin/:path*", "/konto/:path*"], };