'use server' import { revalidatePath } from 'next/cache' import prisma from '@/lib/prisma' import { auth } from '@/auth' import bcrypt from 'bcryptjs' import { z } from 'zod' import { SubscriptionTier, SubscriptionStatus } from '@prisma/client' // Schema pour la création d'utilisateur const CreateUserSchema = z.object({ name: z.string().min(2, "Name must be at least 2 characters"), email: z.string().email("Invalid email address"), password: z.string().min(6, "Password must be at least 6 characters"), role: z.enum(["USER", "ADMIN"]).default("USER"), }) async function checkAdmin() { const session = await auth() if (!session?.user?.id || (session.user as any).role !== 'ADMIN') { throw new Error('Unauthorized: Admin access required') } return session } const userListSelect = { id: true, name: true, email: true, role: true, createdAt: true, subscription: { select: { tier: true, status: true, currentPeriodEnd: true, }, }, } as const export async function getUsers() { await checkAdmin() try { return await prisma.user.findMany({ orderBy: { createdAt: 'desc' }, select: userListSelect, }) } catch (error) { // Prod DB parfois en retard sur les migrations (ex. table Subscription absente). console.error('getUsers with subscription failed, fallback without:', error) const users = await prisma.user.findMany({ orderBy: { createdAt: 'desc' }, select: { id: true, name: true, email: true, role: true, createdAt: true, }, }) return users.map((u) => ({ ...u, subscription: null })) } } export async function createUser(formData: FormData) { await checkAdmin() const rawData = { name: formData.get('name'), email: formData.get('email'), password: formData.get('password'), role: formData.get('role'), } const validatedFields = CreateUserSchema.safeParse(rawData) if (!validatedFields.success) { return { error: validatedFields.error.flatten().fieldErrors, } } const { name, email, password, role } = validatedFields.data const hashedPassword = await bcrypt.hash(password, 10) try { await prisma.user.create({ data: { name, email: email.toLowerCase(), password: hashedPassword, role, }, }) revalidatePath('/admin') return { success: true } } catch (error: any) { if (error.code === 'P2002') { return { error: { email: ['Email already exists'] } } } return { error: { _form: ['Failed to create user'] } } } } export async function deleteUser(userId: string) { const session = await checkAdmin() if (session.user?.id === userId) { throw new Error("You cannot delete your own account") } try { await prisma.user.delete({ where: { id: userId }, }) revalidatePath('/admin') return { success: true } } catch (error) { throw new Error('Failed to delete user') } } export async function updateUserRole(userId: string, newRole: string) { const session = await checkAdmin() if (session.user?.id === userId) { throw new Error("You cannot change your own role") } try { await prisma.user.update({ where: { id: userId }, data: { role: newRole }, }) revalidatePath('/admin') return { success: true } } catch (error) { throw new Error('Failed to update role') } } export async function updateUserSubscription(userId: string, tier: string) { const session = await checkAdmin() const validTiers: string[] = ['BASIC', 'PRO', 'BUSINESS', 'ENTERPRISE'] if (!validTiers.includes(tier)) { throw new Error('Invalid tier') } try { const existing = await prisma.subscription.findUnique({ where: { userId } }) const oldTier = existing?.tier ?? 'BASIC' const now = new Date() const periodEnd = new Date(now) periodEnd.setFullYear(periodEnd.getFullYear() + 1) await prisma.subscription.upsert({ where: { userId }, update: { tier: tier as SubscriptionTier, status: 'ACTIVE', currentPeriodStart: now, currentPeriodEnd: periodEnd, }, create: { userId, tier: tier as SubscriptionTier, status: 'ACTIVE' as SubscriptionStatus, currentPeriodStart: now, currentPeriodEnd: periodEnd, }, }) const { logAuditEventAsync } = await import('@/lib/audit-log') await logAuditEventAsync({ userId: session.user?.id, action: 'SUBSCRIPTION_OVERRIDE', resource: userId, metadata: { oldTier, newTier: tier, targetUserId: userId }, }) revalidatePath('/admin') return { success: true } } catch (error) { throw new Error('Failed to update subscription') } }