Download src/app/api/codes/setup/route.ts from HonzaH/Letschat: direct link, hf CLI and curl.
- Browser
- Download file 7.78 kB
-
https://huggingface.co/spaces/HonzaH/Letschat/resolve/main/src/app/api/codes/setup/route.ts
- Command line
-
hf download hf://spaces/HonzaH/Letschat/src/app/api/codes/setup/route.ts
-
curl -L -o route.ts https://huggingface.co/spaces/HonzaH/Letschat/resolve/main/src/app/api/codes/setup/route.ts
7.78 kB
| 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<any> = [] | |
| 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<string, unknown> = { | |
| 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 } | |
| ) | |
| } | |
| } | |