Letschat / src /app /api /codes /setup /route.ts
HonzaH's picture
Upload 194 files
dac0489 verified
Raw
History Blame Contribute Delete
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 }
)
}
}