Spaces:
Sleeping
Sleeping
| import { NextRequest } from "next/server"; | |
| import { promises as fs } from "fs"; | |
| import os from "os"; | |
| import path from "path"; | |
| export const runtime = "nodejs"; | |
| async function resolveLogFilePath() { | |
| if (process.env.CHAT_LOG_PATH) return process.env.CHAT_LOG_PATH; | |
| const candidates = [ | |
| process.env.DATA_DIR, | |
| process.env.HF_HOME, | |
| process.env.HOME, | |
| "/tmp", | |
| os.tmpdir(), | |
| process.cwd(), | |
| ].filter(Boolean) as string[]; | |
| for (const dir of candidates) { | |
| const candidate = path.join(dir, "chat-logs.jsonl"); | |
| try { | |
| await fs.access(candidate); | |
| return candidate; | |
| } catch { | |
| // try next | |
| } | |
| } | |
| // Default location if not present yet | |
| return path.join(os.tmpdir(), "chat-logs.jsonl"); | |
| } | |
| const ADMIN_TOKEN = process.env.ADMIN_TOKEN || ""; | |
| function isAuthorized(req: NextRequest) { | |
| if (!ADMIN_TOKEN) return true; | |
| const tokenFromHeader = req.headers.get("x-admin-token") || ""; | |
| const auth = req.headers.get("authorization") || ""; | |
| const tokenFromBearer = auth.toLowerCase().startsWith("bearer ") ? auth.slice(7) : ""; | |
| const tokenFromQuery = req.nextUrl.searchParams.get("token") || ""; | |
| return tokenFromHeader === ADMIN_TOKEN || tokenFromBearer === ADMIN_TOKEN || tokenFromQuery === ADMIN_TOKEN; | |
| } | |
| function clampInt(v: number, min: number, max: number) { | |
| return Math.max(min, Math.min(max, v)); | |
| } | |
| export async function GET(req: NextRequest) { | |
| if (!isAuthorized(req)) { | |
| return new Response(JSON.stringify({ error: "Unauthorized" }), { | |
| status: 401, | |
| headers: { "Content-Type": "application/json" }, | |
| }); | |
| } | |
| const tailParam = req.nextUrl.searchParams.get("tail"); | |
| const rawParam = req.nextUrl.searchParams.get("raw"); | |
| const tail = clampInt(Number(tailParam ?? "100"), 1, 2000); | |
| const raw = rawParam === "1" || rawParam === "true"; | |
| try { | |
| const logFile = await resolveLogFilePath(); | |
| const file = await fs.readFile(logFile, "utf8"); | |
| const lines = file.split(/\r?\n/).filter(Boolean); | |
| const sliced = lines.slice(Math.max(0, lines.length - tail)); | |
| if (raw) { | |
| return new Response(sliced.join("\n"), { | |
| headers: { "Content-Type": "text/plain; charset=utf-8" }, | |
| }); | |
| } | |
| const parsed = sliced | |
| .map((l) => { | |
| try { | |
| return JSON.parse(l); | |
| } catch { | |
| return null; | |
| } | |
| }) | |
| .filter(Boolean); | |
| return new Response(JSON.stringify({ count: parsed.length, logs: parsed }), { | |
| headers: { "Content-Type": "application/json" }, | |
| }); | |
| } catch (error) { | |
| const message = error instanceof Error ? error.message : String(error); | |
| const status = message.includes("ENOENT") ? 404 : 500; | |
| return new Response(JSON.stringify({ error: "Failed to read logs", message }), { | |
| status, | |
| headers: { "Content-Type": "application/json" }, | |
| }); | |
| } | |
| } | |