import type { RowDataPacket } from 'mysql2' import { getSession } from '@/lib/session' import { db, DB } from '@/lib/db' import { getClassCss } from '@/lib/character-info' import { verifyTurnstile } from '@/lib/turnstile' import { QUEST_OPTIONS, questStatuses, cooldownRemainingMs, recordSearch, type QuestCategory } from '@/lib/quest-tracker' export async function POST(request: Request) { const session = await getSession() if (!session.accountId || !session.username) { return Response.json({ success: false, error: 'notAuthenticated' }, { status: 401 }) } let body: Record = {} try { body = await request.json() } catch { return Response.json({ success: false, error: 'invalidRequest' }, { status: 400 }) } const character = String(body.character ?? '').trim() const optionKey = String(body.option ?? '').trim() if (!character || !optionKey) return Response.json({ success: false, error: 'missingFields' }) const ip = (request.headers.get('x-forwarded-for') || '').split(',')[0].trim() || undefined if (!(await verifyTurnstile(String(body.turnstile ?? ''), ip))) { return Response.json({ success: false, error: 'captcha' }) } // Personaje: debe ser de la cuenta y estar desconectado. let guid = 0 let level = 0 let classCss = '' try { const [rows] = await db(DB.characters).query( 'SELECT guid, level, class, online FROM characters WHERE name = ? AND account = ? LIMIT 1', [character, session.accountId], ) const c = rows[0] if (!c) return Response.json({ success: false, error: 'characterNotOwned' }) if (c.online === 1) return Response.json({ success: false, error: 'characterOnline' }) guid = Number(c.guid) level = Number(c.level) classCss = getClassCss(Number(c.class)) } catch { return Response.json({ success: false, error: 'genericError' }) } // Resuelve categoría + misiones a comprobar. let category: QuestCategory let quests: number[] let label: string if (optionKey === 'specific') { const qid = Number(body.questId) if (!Number.isInteger(qid) || qid <= 0) return Response.json({ success: false, error: 'invalidQuestId' }) if (level < 10) return Response.json({ success: false, error: 'levelTooLow' }) category = 'specific' quests = [qid] label = `Misión #${qid}` } else { const opt = QUEST_OPTIONS.find((o) => o.key === optionKey) if (!opt) return Response.json({ success: false, error: 'invalidOption' }) if (opt.classCss && opt.classCss !== classCss) return Response.json({ success: false, error: 'wrongClass' }) if (level < opt.minLevel) return Response.json({ success: false, error: 'levelTooLow' }) if (opt.quests.length === 0) return Response.json({ success: false, error: 'noData' }) category = opt.category quests = opt.quests label = opt.label } const remaining = await cooldownRemainingMs(character, category) if (remaining > 0) { return Response.json({ success: false, error: 'cooldown', minutes: Math.ceil(remaining / 60000) }) } const statuses = await questStatuses(guid, quests) await recordSearch(character, category) return Response.json({ success: true, label, statuses }) }