TypeScript 98.3%
CSS 0.9%
Shell 0.7%
1import { NextResponse } from "next/server";2import { z } from "zod";3import { apiError, parseBody } from "@/lib/api.ts";4import { hashPassword } from "@/lib/auth/password.ts";5import { assertSameOrigin, destroyAllSessions, logAuthEvent, requireRole } from "@/lib/auth/session.ts";6import { all, get, run } from "@/lib/db/index.ts";7import { randomBytes } from "node:crypto";89export async function GET() {10 try {11 await requireRole("admin");12 const users = all(13 `SELECT u.id, u.username, u.display_name, u.email, u.role, u.disabled, u.must_change_password,14 u.is_initial_admin, u.created_at, u.last_login_at,15 (SELECT GROUP_CONCAT(course_code) FROM enrollments e WHERE e.user_id = u.id) as courses,16 (SELECT COUNT(*) FROM sessions s WHERE s.user_id = u.id) as sessions17 FROM users u ORDER BY u.id`18 );19 const events = all("SELECT * FROM auth_events ORDER BY id DESC LIMIT 100");20 return NextResponse.json({ users, events });21 } catch (e) {22 return apiError(e);23 }24}2526const schema = z.object({27 userId: z.number().int().positive(),28 role: z.enum(["student", "instructor", "admin"]).optional(),29 disabled: z.boolean().optional(),30 resetPassword: z.boolean().optional(),31 courses: z.array(z.enum(["IMM1003", "IMM1033"])).optional(),32 revokeSessions: z.boolean().optional(),33});3435export async function PATCH(req: Request) {36 try {37 await assertSameOrigin();38 const admin = await requireRole("admin");39 const body = await parseBody(req, schema);40 const target = get<{ id: number; username: string; role: string }>("SELECT id, username, role FROM users WHERE id = ?", body.userId);41 if (!target) return NextResponse.json({ error: "Utilisateur introuvable." }, { status: 404 });42 if (target.id === admin.id && (body.disabled || (body.role && body.role !== "admin"))) {43 return NextResponse.json({ error: "Impossible de rétrograder ou désactiver votre propre compte." }, { status: 400 });44 }4546 let tempPassword: string | null = null;47 if (body.role) run("UPDATE users SET role = ? WHERE id = ?", body.role, target.id);48 if (body.disabled !== undefined) {49 run("UPDATE users SET disabled = ? WHERE id = ?", body.disabled ? 1 : 0, target.id);50 if (body.disabled) destroyAllSessions(target.id);51 }52 if (body.resetPassword) {53 tempPassword = "uqo-" + randomBytes(5).toString("hex");54 run("UPDATE users SET password_hash = ?, must_change_password = 1 WHERE id = ?", hashPassword(tempPassword), target.id);55 destroyAllSessions(target.id);56 }57 if (body.revokeSessions) destroyAllSessions(target.id);58 if (body.courses) {59 run("DELETE FROM enrollments WHERE user_id = ?", target.id);60 for (const c of body.courses) run("INSERT OR IGNORE INTO enrollments (user_id, course_code) VALUES (?, ?)", target.id, c);61 }62 logAuthEvent("admin-user-update", {63 userId: target.id, username: target.username,64 detail: JSON.stringify({ by: admin.username, role: body.role, disabled: body.disabled, reset: !!body.resetPassword }),65 });66 return NextResponse.json({ ok: true, tempPassword });67 } catch (e) {68 return apiError(e);69 }70}71