TypeScript 98.3%
CSS 0.9%
Shell 0.7%
1// Connexion en un clic au compte de démonstration (accès aux deux cours).2// Compte étudiant PARTAGÉ, prévu pour l'essai de la plateforme — désactivable3// via DEMO_ACCOUNT_ENABLED=false.4import { NextResponse } from "next/server";5import { randomBytes } from "node:crypto";6import { apiError } from "@/lib/api.ts";7import { hashPassword } from "@/lib/auth/password.ts";8import { rateLimit } from "@/lib/auth/rate-limit.ts";9import { SESSION_COOKIE, assertSameOrigin, createSession, logAuthEvent } from "@/lib/auth/session.ts";10import { get, run } from "@/lib/db/index.ts";1112const DEMO_USERNAME = "demo.etudiant";1314export async function POST(req: Request) {15 try {16 await assertSameOrigin();17 if (process.env.DEMO_ACCOUNT_ENABLED === "false") {18 return NextResponse.json({ error: "Le compte de démonstration est désactivé." }, { status: 403 });19 }20 const ip = req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || "local";21 const rl = rateLimit(`demo:${ip}`, 10, 60 * 60 * 1000);22 if (!rl.ok) return NextResponse.json({ error: "Trop de connexions démo. Réessayez plus tard." }, { status: 429 });2324 let user = get<{ id: number; disabled: number }>("SELECT id, disabled FROM users WHERE username = ?", DEMO_USERNAME);25 if (!user) {26 // Mot de passe aléatoire jamais communiqué : ce compte ne se connecte que par ce bouton.27 const r = run(28 "INSERT INTO users (username, display_name, password_hash, role) VALUES (?, 'Étudiant·e démo', ?, 'student')",29 DEMO_USERNAME, hashPassword(randomBytes(24).toString("hex"))30 );31 user = { id: Number(r.lastInsertRowid), disabled: 0 };32 }33 if (user.disabled) return NextResponse.json({ error: "Le compte de démonstration est désactivé." }, { status: 403 });34 run("INSERT OR IGNORE INTO enrollments (user_id, course_code) VALUES (?, 'IMM1003'), (?, 'IMM1033')", user.id, user.id);3536 const { token, expiresAt } = createSession(user.id, ip, req.headers.get("user-agent") ?? undefined);37 logAuthEvent("demo-login", { userId: user.id, username: DEMO_USERNAME, ip });38 const res = NextResponse.json({ ok: true });39 res.cookies.set(SESSION_COOKIE, token, {40 httpOnly: true,41 sameSite: "lax",42 secure: process.env.NODE_ENV === "production" && (process.env.APP_URL ?? "").startsWith("https"),43 expires: expiresAt,44 path: "/",45 });46 return res;47 } catch (e) {48 return apiError(e);49 }50}51