import { NextRequest, NextResponse } from 'next/server' import { hashPin, generateSessionHash, generateCode, createCodePair } from 'lib/codes' import { createRoom } from 'lib/roomService' import { setAnonymousSession } from 'lib/session' import { getCodeByCode, getCodeById, insertCode, updateCode, updateRoom, updateCodesRoomId } from 'lib/persistence' import { logEvent } from 'lib/logger' export async function POST(request: NextRequest) { try { const body = await request.json() const { code, pin, userAgent, codes } = body console.log('[codes/setup] Incoming request body:', { code, hasPin: Boolean(pin), userAgent }) if (!code) { console.warn('[codes/setup] Missing code in request body') return NextResponse.json( { error: 'Chyb¡ k¢d m¡stnosti' }, { status: 400 } ) } let codeData = await getCodeByCode(code) // If code is missing or already used (used === 1), create a fresh pair and use that instead. if (!codeData || codeData?.used === 1) { console.warn('[codes/setup] Code missing or already used - creating new pair for user', { requested: code.toUpperCase(), existing: !!codeData, used: codeData?.used ?? null }) try { const { code1, code2 } = await createCodePair() const createdPrimary = await insertCode({ code: code1, linked_to: null, used: null, used_count: 0 }) if (!createdPrimary) { throw new Error('Failed to create primary code') } const createdSecondary = await insertCode({ code: code2, linked_to: createdPrimary.id, used: null, used_count: 0 }) if (!createdSecondary) { // Best-effort cleanup marker await updateCode(createdPrimary.id, {} as any) throw new Error('Failed to create secondary code') } const linkResult = await updateCode(createdPrimary.id, { linked_to: createdSecondary.id }) if (!linkResult) { throw new Error('Failed to link primary and secondary codes') } codeData = { ...createdPrimary, linked_to: createdSecondary.id } } catch (createErr) { console.error('[codes/setup] Failed to create code pair fallback:', createErr) return NextResponse.json( { error: 'Nepodaýilo se vytvoýit k¢d' }, { status: 500 } ) } } // If multiple codes were supplied, create/ensure them and bind to one room if (Array.isArray(codes) && codes.length > 0) { const normalized = Array.from(new Set(codes.map((c: any) => String(c).toUpperCase().trim()).filter(Boolean))) if (normalized.length < 2) { console.warn('[codes/setup] multi-code flow requires at least 2 codes') } else { // Ensure each code exists (create if missing) const rows: Array = [] for (const candidate of normalized) { let cd = await getCodeByCode(candidate) if (!cd) { cd = await insertCode({ code: candidate, linked_to: null, used: null, used_count: 0 }) if (!cd) { console.error('[codes/setup] failed to create code', candidate) return NextResponse.json({ error: 'Nepodařilo se vytvořit kódy' }, { status: 500 }) } } rows.push(cd) } // Create room linked to first two codes and attach remaining codes const roomId = await createRoom(rows[0].id, rows[1].id) const remaining = rows.slice(2).map((r) => r.id) if (remaining.length > 0) { await updateCodesRoomId(remaining, roomId) } await logEvent({ module: 'codes', operation: 'setup_multi', data: { codes: normalized, roomId } }) return NextResponse.json({ success: true, roomId, codes: normalized }) } } // Ensure we have a linked code. If missing, create a linked secondary code. if (!codeData || !codeData.linked_to) { console.warn('[codes/setup] code.linked_to missing, creating secondary code', { codeId: codeData?.id || null }) let createdSecondary = null for (let i = 0; i < 5; i++) { const candidate = generateCode() const sec = await insertCode({ code: candidate, linked_to: codeData!.id, used: null, used_count: 0 }) if (sec) { createdSecondary = sec break } } if (!createdSecondary) { console.error('[codes/setup] Failed to create a linked secondary code') return NextResponse.json({ error: 'Nepodaýilo se vytvoýit p rovì k¢d' }, { status: 500 }) } const linkResult = await updateCode(codeData!.id, { linked_to: createdSecondary.id }) if (!linkResult) { console.error('[codes/setup] Failed to link primary to secondary') return NextResponse.json({ error: 'Nepodaýilo se aktualizovat p rov‚ k¢dy' }, { status: 500 }) } codeData!.linked_to = createdSecondary.id } const linkedCode = await getCodeById(codeData!.linked_to!) if (!linkedCode) { return NextResponse.json( { error: 'P rovì k¢d nenalezen' }, { status: 400 } ) } const roomId = await createRoom(codeData!.id, linkedCode.id) const sessionHash = generateSessionHash(code, userAgent || '') let pinHash: string | undefined if (pin) { pinHash = await hashPin(pin) } const now = new Date().toISOString() // Mark code as used with PIN or session - include userId if authenticated const updatePayload: Record = { used: 1, room_id: roomId, pin_hash: pinHash || null, session_hash: !pin ? sessionHash : null, date_first: now, date_last: now, used_count: 1 } // Do not set user_id server-side here to avoid relying on cookies/session in this route const updated = await updateCode(codeData!.id, updatePayload as any) const updateErr = !updated ? { message: 'Update failed' } : null if (updateErr) { // Attempt cleanup: disable created room to avoid dangling room try { await updateRoom(roomId, { status: 0 }) } catch (cleanupErr) { console.error('Cleanup failed after codes update error:', cleanupErr) } console.error('Failed to update code after creating room:', updateErr) return NextResponse.json( { error: 'Nepodaýilo se oznaŸit k¢d jako pou§itì (RLS nebo db chyba)', detail: updateErr.message || updateErr }, { status: 500 } ) } // Set anonymous session if no PIN if (!pin) { await setAnonymousSession({ codeId: codeData!.id, sessionHash, roomId, expiresAt: Date.now() + (10 * 365 * 24 * 60 * 60 * 1000) // 10 let pro bez PIN }) } else { // Set session for PIN-protected code await setAnonymousSession({ codeId: codeData!.id, sessionHash: Buffer.from(`${codeData!.id}:${Date.now()}`).toString('base64'), roomId, expiresAt: Date.now() + (30 * 24 * 60 * 60 * 1000) // 30 dn¡ pro s PIN }) } await logEvent({ module: 'codes', operation: 'setup', data: { codeId: codeData!.id, roomId, pinSet: Boolean(pin) }, ip: (request as any).headers?.get?.('x-forwarded-for') || null, userAgent: (request as any).headers?.get?.('user-agent') || null }) return NextResponse.json({ success: true, roomId, codeId: codeData!.id }) } catch (error) { console.error('Code setup error:', error) return NextResponse.json( { error: 'Vnitýn¡ chyba serveru' }, { status: 500 } ) } }