// Route handler: POST /api/rooms - creer une room // GET /api/rooms?code=XXXX - recuperer l'etat d'une room import { NextRequest } from "next/server"; import { prisma } from "../../../lib/prisma"; // Types export type Player = { id: string; name: string; score: number; currentArticle: string; hasWon: boolean; isHost: boolean; lastSeen: number; // timestamp ms }; export type Room = { code: string; players: Player[]; phase: "waiting" | "countdown" | "playing" | "results"; round: number; totalRounds: number; maxPlayers: number; startArticle: string; targetArticle: string; roundWinner: string | null; countdownStart: number | null; roundStart: number | null; createdAt: number; }; // Helpers function generateCode(): string { const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ"; let code = ""; for (let i = 0; i < 4; i++) { code += chars[Math.floor(Math.random() * chars.length)]; } return code; } function generatePlayerId(): string { return crypto.randomUUID(); } function dbToRoom(row: { code: string; players: unknown; phase: string; round: number; totalRounds: number; maxPlayers: number; startArticle: string; targetArticle: string; roundWinner: string | null; countdownStart: bigint | null; roundStart: bigint | null; createdAt: bigint; }): Room { return { code: row.code, players: row.players as Player[], phase: row.phase as Room["phase"], round: row.round, totalRounds: row.totalRounds, maxPlayers: row.maxPlayers, startArticle: row.startArticle, targetArticle: row.targetArticle, roundWinner: row.roundWinner, countdownStart: row.countdownStart !== null ? Number(row.countdownStart) : null, roundStart: row.roundStart !== null ? Number(row.roundStart) : null, createdAt: Number(row.createdAt), }; } // Nettoie les rooms inactives depuis plus de 2h async function pruneOldRooms() { const cutoff = BigInt(Date.now() - 2 * 60 * 60 * 1000); await prisma.room.deleteMany({ where: { createdAt: { lt: cutoff } } }); } // POST /api/rooms // Body: { playerName: string } // Response: { room: Room, playerId: string } export async function POST(request: NextRequest) { const body = await request.json(); const { playerName, maxPlayers, totalRounds } = body as { playerName: string; maxPlayers?: number; totalRounds?: number; }; if ( !playerName || typeof playerName !== "string" || playerName.trim() === "" ) { return Response.json({ error: "Pseudo invalide" }, { status: 400 }); } const clampedMax = Math.min( Math.max(typeof maxPlayers === "number" ? Math.floor(maxPlayers) : 16, 2), 16, ); const clampedRounds = Math.min( Math.max(typeof totalRounds === "number" ? Math.floor(totalRounds) : 3, 1), 10, ); await pruneOldRooms(); // Generer un code unique let code = generateCode(); let attempts = 0; while (attempts < 20) { const existing = await prisma.room.findUnique({ where: { code } }); if (!existing) break; code = generateCode(); attempts++; } const playerId = generatePlayerId(); const now = BigInt(Date.now()); const players: Player[] = [ { id: playerId, name: playerName.trim().slice(0, 20), score: 0, currentArticle: "", hasWon: false, isHost: true, lastSeen: Date.now(), }, ]; const row = await prisma.room.create({ data: { code, players: players as object[], phase: "waiting", round: 0, totalRounds: clampedRounds, maxPlayers: clampedMax, startArticle: "", targetArticle: "", roundWinner: null, countdownStart: null, roundStart: null, createdAt: now, }, }); const room = dbToRoom(row); return Response.json({ room, playerId }); } // GET /api/rooms?code=XXXX export async function GET(request: NextRequest) { const { searchParams } = new URL(request.url); const code = searchParams.get("code"); if (!code) { return Response.json({ error: "Code manquant" }, { status: 400 }); } const row = await prisma.room.findUnique({ where: { code: code.toUpperCase() } }); if (!row) { return Response.json({ error: "Room introuvable" }, { status: 404 }); } return Response.json({ room: dbToRoom(row) }); }