+
| Name |
E-Mail |
Rolle |
- Projekte |
+ Mandant |
+ Letzte Anmeldung |
+ Status |
Aktionen |
- {(user?.role === 'SERVER_ADMIN' ? users.filter(u => u.role === 'SERVER_ADMIN') : users).map(u => (
-
+ {users.map(u => {
+ const inactive = u.isActive === false
+ const tenantNames = (u.memberships || []).map(m => m.tenant?.name).filter(Boolean)
+ return (
+
|
|
-
-
- {u.email}
- {u.emailVerified === false && (
- unverifiziert
- )}
-
- |
+ {u.email} |
r.value === u.role)?.label || u.role}
|
- {u._count?.projects || 0} |
+
+ {tenantNames.length > 0 ? tenantNames.join(', ') : —}
+ |
+
+ {u.lastLoginAt ? (
+ {formatDateTime(u.lastLoginAt)}
+ ) : (
+ Nie eingeloggt
+ )}
+ |
+
+ {inactive ? (
+ Gesperrt
+ ) : u.emailVerified === false ? (
+ Unverifiziert
+ ) : (
+ Aktiv
+ )}
+ |
{u.emailVerified === false && (
@@ -783,19 +820,25 @@ export default function AdminPage() {
onClick={() => handleResetUserPassword(u)}>
-
|
- ))}
+ )})}
diff --git a/src/app/api/admin/users/[id]/route.ts b/src/app/api/admin/users/[id]/route.ts
index c5cf44d..59af631 100644
--- a/src/app/api/admin/users/[id]/route.ts
+++ b/src/app/api/admin/users/[id]/route.ts
@@ -9,6 +9,7 @@ const updateUserSchema = z.object({
password: z.string().min(6).optional(),
role: z.enum(['SERVER_ADMIN', 'TENANT_ADMIN', 'OPERATOR', 'VIEWER']).optional(),
emailVerified: z.boolean().optional(),
+ isActive: z.boolean().optional(),
})
export async function PATCH(
@@ -46,6 +47,13 @@ export async function PATCH(
if (data.role) updateData.role = data.role
if (data.password) updateData.password = await hashPassword(data.password)
if (data.emailVerified !== undefined) updateData.emailVerified = data.emailVerified
+ if (data.isActive !== undefined) {
+ // Niemand darf sich selbst sperren
+ if (data.isActive === false && session.id === params.id) {
+ return NextResponse.json({ error: 'Eigenen Account kann man nicht sperren' }, { status: 400 })
+ }
+ updateData.isActive = data.isActive
+ }
const user = await (prisma as any).user.update({
where: { id: params.id },
@@ -56,6 +64,8 @@ export async function PATCH(
name: true,
role: true,
emailVerified: true,
+ isActive: true,
+ lastLoginAt: true,
createdAt: true,
updatedAt: true,
},
diff --git a/src/app/api/admin/users/route.ts b/src/app/api/admin/users/route.ts
index d8c6148..bbcaa74 100644
--- a/src/app/api/admin/users/route.ts
+++ b/src/app/api/admin/users/route.ts
@@ -31,6 +31,8 @@ export async function GET() {
name: true,
role: true,
emailVerified: true,
+ isActive: true,
+ lastLoginAt: true,
createdAt: true,
updatedAt: true,
memberships: {
diff --git a/src/lib/auth.ts b/src/lib/auth.ts
index 98cd3a2..ef7cd6e 100644
--- a/src/lib/auth.ts
+++ b/src/lib/auth.ts
@@ -59,6 +59,7 @@ export async function login(
password: true,
role: true,
emailVerified: true,
+ isActive: true,
},
}) as any)
@@ -71,6 +72,12 @@ export async function login(
return { success: false, error: 'E-Mail oder Passwort falsch' }
}
+ // Gesperrte Benutzer dürfen sich nicht anmelden (isActive === false).
+ // Tolerant: nur blockieren, wenn explizit false (alte Datensätze ohne Wert bleiben aktiv).
+ if (user.isActive === false) {
+ return { success: false, error: 'Ihr Konto wurde deaktiviert. Bitte kontaktieren Sie den Administrator.' }
+ }
+
// Track email verification status (allow login regardless)
const emailVerified = (user as any).emailVerified !== false