import { onRequest } from "firebase-functions/https"; import { logger } from "firebase-functions"; import { runDailyArchive } from "../scheduled/dailyArchive"; import { forceSyncDay } from "../services/gameSyncService"; import { runChatToolsSheet, renderSheetMarkdown } from "../services/chatToolsSheetService"; import { runChatProbe, runChatProbeBatch, renderProbeMarkdown } from "../services/chatProbeService"; import { sendError } from "../middleware/errors"; import { daysAgoKst, parseDateString } from "../types/dateString"; /** * 임시 디버그 핸들러. 인증 없음 — 운영 안정화 후 제거할 것. * * - GET/POST `/debug/forceSync` — 어제(KST) 기준으로 라이브 데이터 sync 실행. * - GET/POST `/debug/forceSync?date=YYYY-MM-DD` — 지정한 날짜를 강제 sync. * - GET/POST `/debug/dailyArchive` — 어제(KST) 기준으로 dailyArchive 본체 실행. * - GET/POST `/debug/dailyArchive?date=YYYY-MM-DD` — 지정한 날짜를 archive 대상으로 실행. * - GET `/debug/chatToolsSheet?uid=UID` — 그 유저 기준으로 짹 도구 전체를 실호출한 * 결과 시트(JSON)를 반환. `&format=md`면 Markdown, `&player=`·`&date=`로 케이스 추가. * - GET `/debug/chatProbe?uid=UID&q=문장` — 자연어 문장 1개를 실제 모델+도구 루프로 * 돌리고, 모델이 고른 도구·인자(toolCalls)와 최종 응답을 반환. `&model=`로 테스트 모델 지정. * - GET `/debug/chatProbeSheet?uid=UID` — 기본 자연어 문장 세트를 배치로 프로브한 * 결과 시트(토큰·속도·캐시 포함). `&format=md`면 Markdown, `&model=<모델ID>`로 A/B * (미지정 시 기본 lite. 타임아웃은 모든 모델 공평하게 24초). * ⚠️ 무인증 + 임의 uid의 개인 데이터(출석·예측)를 노출하고 실제 Vertex 토큰을 * 소모한다 — 검수용 임시 엔드포인트다. */ export const debug = onRequest({ timeoutSeconds: 300 }, async (req, res) => { const segs = req.path.replace(/^\/+|\/+$/g, "").split("/"); const tail = segs.slice(-1)[0]; try { if (tail === "forceSync") { const dateParam = (typeof req.query.date === "string" && req.query.date) || (typeof req.body?.date === "string" && req.body.date) || undefined; const dateString = dateParam ? parseDateString(dateParam) : daysAgoKst(1); const ymd = dateString.replace(/-/g, ""); logger.info(`debug.forceSync triggered manually (ymd=${ymd})`); const result = await forceSyncDay(ymd); res.status(200).json({ ymd, ...result }); return; } if (tail === "dailyArchive") { const dateParam = (typeof req.query.date === "string" && req.query.date) || (typeof req.body?.date === "string" && req.body.date) || undefined; const overrideDate = dateParam ? parseDateString(dateParam) : undefined; logger.info( `debug.dailyArchive triggered manually (date=${overrideDate ?? "yesterday"})` ); const result = await runDailyArchive(overrideDate); res.status(200).json(result); return; } if (tail === "chatToolsSheet" || tail === "chat-tools-sheet") { const uid = (typeof req.query.uid === "string" && req.query.uid) || (typeof req.body?.uid === "string" && req.body.uid) || ""; if (!uid) { res.status(400).json({ error: "uid is required (e.g. ?uid=)" }); return; } const player = typeof req.query.player === "string" ? req.query.player : undefined; const date = typeof req.query.date === "string" ? req.query.date : undefined; const format = typeof req.query.format === "string" ? req.query.format : "json"; logger.info(`debug.chatToolsSheet triggered (uid=${uid})`); const sheet = await runChatToolsSheet(uid, { player, date }); if (format === "md" || format === "markdown") { res.status(200).type("text/markdown; charset=utf-8").send(renderSheetMarkdown(sheet)); return; } res.status(200).json(sheet); return; } if (tail === "chatProbe") { const uid = (typeof req.query.uid === "string" && req.query.uid) || (typeof req.body?.uid === "string" && req.body.uid) || ""; const q = (typeof req.query.q === "string" && req.query.q) || (typeof req.body?.q === "string" && req.body.q) || ""; if (!uid || !q) { res.status(400).json({ error: "uid and q are required (e.g. ?uid=&q=<문장>)" }); return; } const model = typeof req.query.model === "string" ? req.query.model : undefined; logger.info(`debug.chatProbe triggered (uid=${uid}, model=${model ?? "default"})`); const result = await runChatProbe(uid, q, model); res.status(200).json(result); return; } if (tail === "chatProbeSheet") { const uid = (typeof req.query.uid === "string" && req.query.uid) || (typeof req.body?.uid === "string" && req.body.uid) || ""; if (!uid) { res.status(400).json({ error: "uid is required (e.g. ?uid=)" }); return; } const format = typeof req.query.format === "string" ? req.query.format : "json"; const model = typeof req.query.model === "string" ? req.query.model : undefined; logger.info(`debug.chatProbeSheet triggered (uid=${uid}, model=${model ?? "default"})`); const sheet = await runChatProbeBatch(uid, undefined, model); if (format === "md" || format === "markdown") { res.status(200).type("text/markdown; charset=utf-8").send(renderProbeMarkdown(sheet)); return; } res.status(200).json(sheet); return; } res.status(404).json({ error: `Unknown: ${req.method} ${req.path}` }); } catch (err) { sendError(res, err); } });