"Mein Konto": Profil, Passwort setzen oder wechseln, Passkeys anlegen, umbenennen und entfernen, aktive Sitzungen einsehen und ueberall abmelden. Der letzte Passkey laesst sich nicht entfernen, solange kein Passwort gesetzt ist - sonst waere das Konto ausgesperrt. Admin-Einstellungen: Betriebsart umschalten, Selbstregistrierung freigeben, Konten anlegen, bearbeiten, deaktivieren und loeschen. Die Instanzwerte aus der .env werden angezeigt, damit sichtbar ist, warum Passkeys gegebenenfalls nicht verfuegbar sind. Schutzregeln: - Der Wechsel in den Einzelbetrieb wird abgelehnt, solange mehr als ein Konto existiert. - Der letzte aktive Administrator kann weder herabgestuft noch deaktiviert oder geloescht werden. - Das eigene Konto laesst sich nicht loeschen oder deaktivieren. - Deaktivieren oder ein neues Passwort beenden sofort alle Sitzungen des Kontos. Administratoren verwalten Konten, keine Inhalte: fremde Projekte sehen auch sie nicht. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
66 lines
1.7 KiB
TypeScript
66 lines
1.7 KiB
TypeScript
import type { Metadata } from "next";
|
|
import { AccountManager, type AccountData } from "@/components/account/AccountManager";
|
|
import { requireUserPage } from "@/lib/auth/guard";
|
|
import { WEBAUTHN_AVAILABLE } from "@/lib/config";
|
|
import { prisma } from "@/lib/db";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
export async function generateMetadata(): Promise<Metadata> {
|
|
return { title: "Mein Konto" };
|
|
}
|
|
|
|
export default async function AccountPage() {
|
|
const user = await requireUserPage();
|
|
|
|
const [passkeys, sessions] = await Promise.all([
|
|
prisma.credential.findMany({
|
|
where: { userId: user.id },
|
|
select: {
|
|
id: true,
|
|
name: true,
|
|
deviceType: true,
|
|
backedUp: true,
|
|
createdAt: true,
|
|
lastUsedAt: true,
|
|
},
|
|
orderBy: { createdAt: "asc" },
|
|
}),
|
|
prisma.session.findMany({
|
|
where: { userId: user.id, expiresAt: { gt: new Date() } },
|
|
select: {
|
|
id: true,
|
|
userAgent: true,
|
|
ip: true,
|
|
createdAt: true,
|
|
lastSeenAt: true,
|
|
},
|
|
orderBy: { lastSeenAt: "desc" },
|
|
}),
|
|
]);
|
|
|
|
const data: AccountData = {
|
|
username: user.username,
|
|
displayName: user.displayName,
|
|
email: user.email,
|
|
role: user.role,
|
|
hasPassword: user.hasPassword,
|
|
passkeys,
|
|
sessions,
|
|
passkeysAvailable: WEBAUTHN_AVAILABLE,
|
|
};
|
|
|
|
return (
|
|
<div className="mx-auto max-w-3xl space-y-6 pt-4">
|
|
<header className="fade-up">
|
|
<h1 className="text-2xl font-semibold tracking-tight text-white">Mein Konto</h1>
|
|
<p className="mt-1 text-sm text-slate-400">
|
|
Profil, Anmeldemittel und aktive Sitzungen.
|
|
</p>
|
|
</header>
|
|
|
|
<AccountManager data={data} />
|
|
</div>
|
|
);
|
|
}
|