feat(ST-111ci8t4): admin user-actions (delete, updateRoles, setMustResetPassword)
- lib/session.ts: isAdmin: boolean toegevoegd aan SessionData
- lib/auth-guard.ts: requireAdmin() toegevoegd (redirect /dashboard bij !isAdmin)
- actions/admin/users.ts: deleteUserAction (zelfbescherming), updateUserRolesAction
(Zod z.nativeEnum, eigen ADMIN-rol-beveiliging, transactie), setMustResetPasswordAction
— alle drie 'use server', revalidatePath('/admin/users')
This commit is contained in:
parent
71281038ff
commit
5fd56e3f67
3 changed files with 52 additions and 0 deletions
43
actions/admin/users.ts
Normal file
43
actions/admin/users.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
'use server'
|
||||||
|
|
||||||
|
import { revalidatePath } from 'next/cache'
|
||||||
|
import { z } from 'zod'
|
||||||
|
import { Role } from '@prisma/client'
|
||||||
|
import { prisma } from '@/lib/prisma'
|
||||||
|
import { requireAdmin } from '@/lib/auth-guard'
|
||||||
|
|
||||||
|
export async function deleteUserAction(userId: string) {
|
||||||
|
const session = await requireAdmin()
|
||||||
|
if (userId === session.userId) {
|
||||||
|
throw new Error('Zelfverwijdering niet toegestaan')
|
||||||
|
}
|
||||||
|
await prisma.user.delete({ where: { id: userId } })
|
||||||
|
revalidatePath('/admin/users')
|
||||||
|
}
|
||||||
|
|
||||||
|
const rolesSchema = z.array(z.nativeEnum(Role))
|
||||||
|
|
||||||
|
export async function updateUserRolesAction(userId: string, roles: Role[]) {
|
||||||
|
const session = await requireAdmin()
|
||||||
|
|
||||||
|
const parsed = rolesSchema.safeParse(roles)
|
||||||
|
if (!parsed.success) {
|
||||||
|
throw new Error('Ongeldige rol-waarden')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userId === session.userId && !parsed.data.includes(Role.ADMIN)) {
|
||||||
|
throw new Error('Kan eigen ADMIN-rol niet verwijderen')
|
||||||
|
}
|
||||||
|
|
||||||
|
await prisma.$transaction([
|
||||||
|
prisma.userRole.deleteMany({ where: { user_id: userId } }),
|
||||||
|
...parsed.data.map((role) => prisma.userRole.create({ data: { user_id: userId, role } })),
|
||||||
|
])
|
||||||
|
revalidatePath('/admin/users')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function setMustResetPasswordAction(userId: string, value: boolean) {
|
||||||
|
await requireAdmin()
|
||||||
|
await prisma.user.update({ where: { id: userId }, data: { must_reset_password: value } })
|
||||||
|
revalidatePath('/admin/users')
|
||||||
|
}
|
||||||
|
|
@ -22,3 +22,11 @@ export async function requireSession() {
|
||||||
|
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function requireAdmin() {
|
||||||
|
const session = await getSession()
|
||||||
|
if (!session.userId || !session.isAdmin) {
|
||||||
|
redirect('/dashboard')
|
||||||
|
}
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { SessionOptions } from 'iron-session'
|
||||||
export interface SessionData {
|
export interface SessionData {
|
||||||
userId: string
|
userId: string
|
||||||
isDemo: boolean
|
isDemo: boolean
|
||||||
|
isAdmin: boolean
|
||||||
// ST-1002 (M10) — gezet door /api/auth/pair/claim na een succesvolle QR-pairing.
|
// ST-1002 (M10) — gezet door /api/auth/pair/claim na een succesvolle QR-pairing.
|
||||||
// Beide velden zijn optioneel zodat bestaande wachtwoord-sessies onveranderd blijven.
|
// Beide velden zijn optioneel zodat bestaande wachtwoord-sessies onveranderd blijven.
|
||||||
paired?: boolean
|
paired?: boolean
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue