feat(admin): Kontoverwaltung und Admin-Einstellungen
"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>
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user