Files
Artista-Filmcamp/src/lib/admin-auth.ts
T
Nick SzaunigandClaude Opus 5.5 771eeee887
Docker-Images bauen und veroeffentlichen / build-and-push (push) Successful in 4m25s
fix: Admin-Routen nicht vorab rendern (Docker-Build ohne Secrets)
next build versuchte /admin statisch zu rendern. currentAdmin() rief dabei
getAuth() vor headers() auf und scheiterte ohne BETTER_AUTH_SECRET im
Build-Container. Jetzt werden zuerst die Request-Header gelesen, wodurch
alle Admin-Routen dynamisch sind und Auth erst zur Laufzeit geladen wird.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-27 12:42:25 +02:00

48 lines
2.0 KiB
TypeScript

/**
* Serverseitige Zugriffskontrolle fuer den Adminbereich. Wird von JEDER
* Admin-Seite, Server-Action und jedem Admin-Endpunkt aufgerufen — der
* Proxy (src/proxy.ts) leitet nur vorab um und ist keine Sicherheitsgrenze.
*/
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import type { Actor } from "./audit";
import { getAuth } from "./auth";
export type AdminUser = { id: string; email: string; name: string; twoFactorEnabled: boolean };
async function currentAdmin(): Promise<AdminUser | null> {
// Zuerst die Request-Header lesen: das macht jede Admin-Route dynamisch.
// Sonst versucht "next build" sie vorab zu rendern und scheitert ohne
// Secrets im Container (getAuth() braucht BETTER_AUTH_SECRET).
const requestHeaders = await headers();
const session = await getAuth().api.getSession({ headers: requestHeaders });
if (!session) return null;
const user = session.user as typeof session.user & { twoFactorEnabled?: boolean | null };
return { id: user.id, email: user.email, name: user.name, twoFactorEnabled: Boolean(user.twoFactorEnabled) };
}
/** Fuer Seiten: leitet zur Anmeldung bzw. zur 2FA-Einrichtung um. */
export async function requireAdminPage(options: { allowWithout2fa?: boolean } = {}): Promise<AdminUser> {
const admin = await currentAdmin();
if (!admin) redirect("/admin/anmelden");
if (!admin.twoFactorEnabled && !options.allowWithout2fa) redirect("/admin/sicherheit");
return admin;
}
/** Fuer Server-Actions und Endpunkte: wirft statt umzuleiten. */
export async function requireAdminAction(): Promise<AdminUser> {
const admin = await currentAdmin();
if (!admin || !admin.twoFactorEnabled) throw new Error("Nicht berechtigt.");
return admin;
}
/** Fuer Route-Handler: null statt Ausnahme. */
export async function adminOrNull(): Promise<AdminUser | null> {
const admin = await currentAdmin();
return admin?.twoFactorEnabled ? admin : null;
}
export function adminActor(admin: AdminUser): Actor {
return { type: "ADMIN", id: admin.id };
}