- /setup fuehrt beim ersten Aufruf durch das Anlegen des Administratorkontos und die Wahl der Betriebsart. Der Endpunkt funktioniert nur, solange es keinen Benutzer gibt; danach kann sich niemand mehr nachtraeglich ein Adminkonto erschleichen. Vorhandene Projekte ohne Besitzer werden dabei uebernommen und im Formular vorher angekuendigt. - /login mit Passwort oder Passkey. Der Passkey-Knopf entfaellt, wenn APP_URL keinen sicheren Kontext ergibt - mit Hinweis, woran es liegt. Das Ziel nach der Anmeldung wird auf relative Pfade begrenzt, sonst waere der weiter-Parameter ein offener Redirect. - /register nur im Mehrbenutzerbetrieb und nur, wenn ein Administrator die Selbstregistrierung freigegeben hat. - Kopfzeile mit Benutzermenue: eigenes Konto, Admin-Einstellungen, Abmelden. Ohne Anmeldung bleibt sie auf das Logo reduziert. - Layout liest Titel und Namen ueber generateMetadata, damit APP_NAME zur Laufzeit greift statt beim Build eingefroren zu werden. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
45 lines
1.3 KiB
TypeScript
45 lines
1.3 KiB
TypeScript
import type { Metadata } from "next";
|
||
import { redirect } from "next/navigation";
|
||
import { AuthShell } from "@/components/auth/AuthShell";
|
||
import { LoginForm } from "@/components/auth/LoginForm";
|
||
import { getSessionUser } from "@/lib/auth/session";
|
||
import { getInstanceState } from "@/lib/auth/settings";
|
||
import { APP_NAME, WEBAUTHN_AVAILABLE } from "@/lib/config";
|
||
|
||
export const dynamic = "force-dynamic";
|
||
|
||
export async function generateMetadata(): Promise<Metadata> {
|
||
return { title: "Anmelden" };
|
||
}
|
||
|
||
type PageProps = { searchParams: Promise<{ weiter?: string }> };
|
||
|
||
export default async function LoginPage({ searchParams }: PageProps) {
|
||
const [user, state, params] = await Promise.all([
|
||
getSessionUser(),
|
||
getInstanceState(),
|
||
searchParams,
|
||
]);
|
||
|
||
if (state.needsSetup) redirect("/setup");
|
||
if (user) redirect("/");
|
||
|
||
// Nur relative Pfade zulassen – sonst waere das ein offener Redirect.
|
||
const raw = params.weiter ?? "/";
|
||
const next = raw.startsWith("/") && !raw.startsWith("//") ? raw : "/";
|
||
|
||
return (
|
||
<AuthShell
|
||
appName={APP_NAME}
|
||
title="Willkommen zurueck"
|
||
subtitle="Melde dich an, um deine Projekte zu sehen."
|
||
>
|
||
<LoginForm
|
||
passkeysAvailable={WEBAUTHN_AVAILABLE}
|
||
registrationOpen={state.registrationOpen}
|
||
next={next}
|
||
/>
|
||
</AuthShell>
|
||
);
|
||
}
|