// /app/api/matches/[id]/mapvote/reset/route.ts import { NextRequest, NextResponse } from 'next/server' import { getServerSession } from 'next-auth' import { authOptions } from '@/app/lib/auth' import { prisma } from '@/app/lib/prisma' import { sendServerSSEMessage } from '@/app/lib/sse-server-client' import { MAP_OPTIONS } from '@/app/lib/mapOptions' import { createHash } from 'crypto' export const runtime = 'nodejs' export const dynamic = 'force-dynamic' function shapeState(vote: any) { const ACTION_MAP = { BAN: 'ban', PICK: 'pick', DECIDER: 'decider' } as const const steps = [...vote.steps] .sort((a, b) => a.order - b.order) .map((s: any) => ({ order : s.order, action : ACTION_MAP[s.action as keyof typeof ACTION_MAP], teamId : s.teamId, map : s.map, chosenAt: s.chosenAt ? s.chosenAt.toISOString() : null, chosenBy: s.chosenBy ?? null, })) return { bestOf : vote.bestOf, mapPool : vote.mapPool as string[], currentIndex: vote.currentIdx, locked : vote.locked as boolean, opensAt : vote.opensAt ? new Date(vote.opensAt).toISOString() : null, steps, adminEdit: vote.adminEditingBy ? { enabled: true, by: vote.adminEditingBy as string, since: vote.adminEditingSince ? new Date(vote.adminEditingSince).toISOString() : null, } : { enabled: false, by: null, since: null }, } } function buildMapVisuals(matchId: string, mapPool: string[]) { const visuals: Record = {} for (const key of mapPool) { const opt = MAP_OPTIONS.find(o => o.key === key) const label = opt?.label ?? key const imgs = opt?.images ?? [] let bg = `/assets/img/maps/${key}/1.jpg` if (imgs.length > 0) { const h = createHash('sha256').update(`${matchId}:${key}`).digest('hex') const n = parseInt(h.slice(0, 8), 16) const idx = n % imgs.length bg = imgs[idx] } visuals[key] = { label, bg } } return visuals } export async function POST(req: NextRequest, { params }: { params: { matchId: string } }) { const session = await getServerSession(authOptions(req)) const me = session?.user as { steamId: string; isAdmin?: boolean } | undefined if (!me?.steamId) return NextResponse.json({ message: 'Nicht eingeloggt' }, { status: 401 }) const matchId = params.matchId if (!matchId) return NextResponse.json({ message: 'Missing id' }, { status: 400 }) try { const match = await prisma.match.findUnique({ where: { id: matchId }, include: { mapVote: { include: { steps: true } } }, }) if (!match?.mapVote) { return NextResponse.json({ message: 'Map-Vote nicht gefunden' }, { status: 404 }) } // optional: nur Admins erlauben – falls gewünscht: // if (!me.isAdmin) return NextResponse.json({ message: 'Nur Admins dürfen resetten' }, { status: 403 }) const vote = match.mapVote // Reset in einer Transaktion await prisma.$transaction(async (tx) => { // alle Steps zurücksetzen await Promise.all( vote.steps.map(s => tx.mapVoteStep.update({ where: { id: s.id }, data : { map: null, chosenAt: null, chosenBy: null }, }) ) ) // Vote Zustand zurücksetzen await tx.mapVote.update({ where: { id: vote.id }, data : { currentIdx: 0, locked: false, adminEditingBy: null, adminEditingSince: null, }, }) }) const updated = await prisma.mapVote.findUnique({ where: { id: vote.id }, include: { steps: true }, }) if (!updated) { return NextResponse.json({ message: 'Reset fehlgeschlagen' }, { status: 500 }) } // Events: zuerst map-vote-updated (UI neu laden) await sendServerSSEMessage({ type: 'map-vote-updated', matchId }) // dann map-vote-reset (globale Overlays schließen, lokale Flags löschen) await sendServerSSEMessage({ type: 'map-vote-reset', matchId }) const mapVisuals = buildMapVisuals(match.id, updated.mapPool) return NextResponse.json( { ...shapeState(updated), mapVisuals }, { headers: { 'Cache-Control': 'no-store' } }, ) } catch (e) { console.error('[map-vote-reset][POST] error', e) return NextResponse.json({ message: 'Reset fehlgeschlagen' }, { status: 500 }) } }