| import { NextRequest, NextResponse } from 'next/server' |
| import { codeEntrySchema } from 'lib/validations/code' |
| import { getAnonymousSession, setAnonymousSession } from 'lib/session' |
| import { generateSessionHash } from 'lib/codes' |
| import { getCodeByCode, updateCode } from 'lib/persistence' |
| import { assertNotBlocked, noteFailure, resetFailures } from 'lib/rateLimiter' |
| import { logEvent } from 'lib/logger' |
|
|
| export async function POST(request: NextRequest) { |
| try { |
| const body = await request.json() |
| const validation = codeEntrySchema.safeParse(body) |
|
|
| if (!validation.success) { |
| return NextResponse.json( |
| { error: 'Neplatnì form t k¢du' }, |
| { status: 400 } |
| ) |
| } |
|
|
| const { code } = validation.data |
| const headers = (request as any).headers |
| const ip = headers?.get?.('x-forwarded-for') || 'unknown' |
| const ua = headers?.get?.('user-agent') || 'unknown' |
| const deviceKey = `${ip}:${ua}` |
|
|
| try { |
| assertNotBlocked(ip) |
| assertNotBlocked(deviceKey) |
| } catch (_e) { |
| return NextResponse.json({ error: 'Pý¡liç mnoho pokus… – zkuste to pozdØji' }, { status: 429 }) |
| } |
|
|
| |
| const codeData = await getCodeByCode(code) |
|
|
| if (!codeData) { |
| noteFailure(ip, { limit: 3, windowMs: 5 * 60 * 1000, blockMs: 5 * 60 * 1000, escalateAfterBlocks: 3, escalateBlockMs: 24 * 60 * 60 * 1000 }) |
| noteFailure(deviceKey, { limit: 3, windowMs: 5 * 60 * 1000, blockMs: 5 * 60 * 1000, escalateAfterBlocks: 3, escalateBlockMs: 24 * 60 * 60 * 1000 }) |
| return NextResponse.json( |
| { error: 'K¢d nenalezen' }, |
| { status: 404 } |
| ) |
| } |
|
|
| |
| if (codeData.used !== 1) { |
| await logEvent({ |
| module: 'codes', |
| operation: 'validate_unused', |
| data: { codeId: codeData.id }, |
| ip, |
| userAgent: ua |
| }) |
| return NextResponse.json({ |
| redirect: `/setup?code=${code.toUpperCase()}`, |
| needsSetup: true |
| }) |
| } |
|
|
| |
| if (codeData.pin_hash) { |
| |
| const session = await getAnonymousSession() |
| if (session && session.codeId === codeData.id) { |
| |
| return NextResponse.json({ |
| redirect: `/room/${codeData.room_id}?code=${code.toUpperCase()}`, |
| roomId: codeData.room_id |
| }) |
| } else { |
| |
| return NextResponse.json({ |
| redirect: `/enter-pin?code=${code.toUpperCase()}`, |
| needsPin: true |
| }) |
| } |
| } else { |
| |
| if (!codeData.room_id) { |
| return NextResponse.json( |
| { error: 'M¡stnost pro tento k¢d nen¡ dostupn ' }, |
| { status: 500 } |
| ) |
| } |
|
|
| const session = await getAnonymousSession() |
| const hasValidSession = session && session.codeId === codeData.id && session.sessionHash === codeData.session_hash |
|
|
| if (!hasValidSession) { |
| const userAgent = request.headers.get('user-agent') || '' |
| const sessionHash = codeData.session_hash || generateSessionHash(code, userAgent) |
|
|
| await setAnonymousSession({ |
| codeId: codeData.id, |
| sessionHash, |
| roomId: codeData.room_id, |
| expiresAt: Date.now() + (10 * 365 * 24 * 60 * 60 * 1000) |
| }) |
|
|
| await updateCode(codeData.id, { session_hash: sessionHash }) |
| } |
|
|
| await logEvent({ |
| module: 'codes', |
| operation: 'validate_success', |
| data: { codeId: codeData.id, roomId: codeData.room_id }, |
| ip, |
| userAgent: ua |
| }) |
| resetFailures(ip) |
| resetFailures(deviceKey) |
| return NextResponse.json({ |
| redirect: `/room/${codeData.room_id}?code=${code.toUpperCase()}`, |
| roomId: codeData.room_id |
| }) |
| } |
| } catch (error) { |
| console.error('Code validation error:', error) |
| return NextResponse.json( |
| { error: 'Vnitýn¡ chyba serveru' }, |
| { status: 500 } |
| ) |
| } |
| } |
|
|