From 788920b790ffaedf63d1b8711de63d8d6f313b9f Mon Sep 17 00:00:00 2001 From: Scrum4Me Agent <30029041+madhura68@users.noreply.github.com> Date: Tue, 5 May 2026 14:47:11 +0200 Subject: [PATCH] feat(ST-xmwvqru1): admin jobs-actions (cancelJob, deleteJob) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - lib/session.ts: isAdmin: boolean toegevoegd - lib/auth-guard.ts: requireAdmin() toegevoegd - actions/admin/jobs.ts: cancelJobAction (CUID-validatie, eindstatus-check → CANCELLED), deleteJobAction (hard delete) — beide 'use server', revalidatePath('/admin/jobs') --- actions/admin/jobs.ts | 41 +++++++++++++++++++++++++++++++++++++++++ lib/auth-guard.ts | 8 ++++++++ lib/session.ts | 1 + 3 files changed, 50 insertions(+) create mode 100644 actions/admin/jobs.ts diff --git a/actions/admin/jobs.ts b/actions/admin/jobs.ts new file mode 100644 index 0000000..9c9ac14 --- /dev/null +++ b/actions/admin/jobs.ts @@ -0,0 +1,41 @@ +'use server' + +import { revalidatePath } from 'next/cache' +import { z } from 'zod' +import { prisma } from '@/lib/prisma' +import { requireAdmin } from '@/lib/auth-guard' + +const cuidSchema = z.string().cuid() + +export async function cancelJobAction(jobId: string) { + await requireAdmin() + + const parsed = cuidSchema.safeParse(jobId) + if (!parsed.success) throw new Error('Ongeldig job-id') + + const job = await prisma.claudeJob.findUnique({ + where: { id: parsed.data }, + select: { status: true }, + }) + + if (!job) throw new Error('Job niet gevonden') + if (job.status === 'DONE' || job.status === 'FAILED' || job.status === 'CANCELLED') { + throw new Error('Job is al in eindstatus') + } + + await prisma.claudeJob.update({ + where: { id: parsed.data }, + data: { status: 'CANCELLED', finished_at: new Date() }, + }) + revalidatePath('/admin/jobs') +} + +export async function deleteJobAction(jobId: string) { + await requireAdmin() + + const parsed = cuidSchema.safeParse(jobId) + if (!parsed.success) throw new Error('Ongeldig job-id') + + await prisma.claudeJob.delete({ where: { id: parsed.data } }) + revalidatePath('/admin/jobs') +} diff --git a/lib/auth-guard.ts b/lib/auth-guard.ts index 8b6baf5..e82a568 100644 --- a/lib/auth-guard.ts +++ b/lib/auth-guard.ts @@ -22,3 +22,11 @@ export async function requireSession() { return session } + +export async function requireAdmin() { + const session = await getSession() + if (!session.userId || !session.isAdmin) { + redirect('/dashboard') + } + return session +} diff --git a/lib/session.ts b/lib/session.ts index bf1f9a9..5d7c587 100644 --- a/lib/session.ts +++ b/lib/session.ts @@ -3,6 +3,7 @@ import { SessionOptions } from 'iron-session' export interface SessionData { userId: string isDemo: boolean + isAdmin: boolean // ST-1002 (M10) — gezet door /api/auth/pair/claim na een succesvolle QR-pairing. // Beide velden zijn optioneel zodat bestaande wachtwoord-sessies onveranderd blijven. paired?: boolean