TypeScript 50.6%
JavaScript 46.1%
SQL 2.1%
Shell 0.6%
CSS 0.5%
1import { NextRequest, NextResponse } from "next/server";2import { z } from "zod";3import { eq, desc, sql, ilike } from "drizzle-orm";4import { db } from "@/db/client";5import { users, userRoles, listings } from "@/db/schema";6import { assertSameOrigin, requireRole } from "@/lib/auth/session";7import { requireFreshMfa } from "@/lib/admin/mfa";8import { audit } from "@/lib/auth/audit";9import { handle, parseBody, apiError } from "@/lib/api";10export const dynamic = "force-dynamic";11export async function GET(req: NextRequest) { try { const u = await requireRole("ADMIN"); requireFreshMfa(u); const q = req.nextUrl.searchParams.get("q") ?? ""; const rows = await db().select({ id: users.id, email: users.email, displayName: users.displayName, status: users.status, createdAt: users.createdAt, roles: sql<string[]>`coalesce((select array_agg(role) from ${userRoles} r where r.user_id = ${users.id}), '{}')`, nListings: sql<number>`(select count(*) from ${listings} l where l.owner_user_id = ${users.id})::int` }).from(users).where(q ? ilike(users.email, `%${q}%`) : sql`true`).orderBy(desc(users.createdAt)).limit(100); return NextResponse.json({ items: rows }); } catch (e) { return handle(e); } }12const Body = z.object({ id: z.string().uuid(), action: z.enum(["suspend", "activate", "grant", "revoke"]), role: z.enum(["ADMIN", "MODERATOR", "AGENT_VERIFIED"]).optional(), note: z.string().max(300).optional() });13export async function POST(req: NextRequest) {14 try { await assertSameOrigin(); const u = await requireRole("ADMIN"); requireFreshMfa(u); const b = await parseBody(req, Body); const d = db();15 if (b.id === u.id && (b.action === "suspend" || (b.action === "revoke" && b.role === "ADMIN"))) return apiError(409, "self", "Impossible sur votre propre compte");16 if (b.action === "suspend") await d.update(users).set({ status: "SUSPENDED", updatedAt: new Date() }).where(eq(users.id, b.id));17 if (b.action === "activate") await d.update(users).set({ status: "ACTIVE", updatedAt: new Date() }).where(eq(users.id, b.id));18 if (b.action === "grant" && b.role) await d.insert(userRoles).values({ userId: b.id, role: b.role, grantedBy: u.id, note: b.note ?? null }).onConflictDoNothing();19 if (b.action === "revoke" && b.role) await d.delete(userRoles).where(sql`${userRoles.userId} = ${b.id} and ${userRoles.role} = ${b.role}`);20 await audit(`admin.user.${b.action}`, { actorId: u.id, objectType: "user", objectId: b.id, meta: { role: b.role ?? null } });21 return NextResponse.json({ ok: true }); } catch (e) { return handle(e); }22}23