// Educational server-to-server endpoint. Public users need real sessions, quotas and rate limits. import { timingSafeEqual } from "node:crypto"; export const runtime = "nodejs"; export const maxDuration = 60; async function readBoundedJson(request: Request) { if (!request.body) throw new Error("Missing body"); const reader = request.body.getReader(); const decoder = new TextDecoder("utf-8", { fatal: true }); let bytes = 0; let text = ""; try { while (true) { const { done, value } = await reader.read(); if (done) break; bytes += value.byteLength; if (bytes > 4096) { await reader.cancel(); throw new Error("Body limit"); } text += decoder.decode(value, { stream: true }); } text += decoder.decode(); return JSON.parse(text); } finally { reader.releaseLock(); } } export async function POST(request: Request) { const key = process.env.PAASTA_API_KEY?.trim(); const model = process.env.PAASTA_MODEL_ID?.trim(); const token = process.env.CHAT_DEMO_TOKEN; if (!key || !model || !token || token.length < 32) { return Response.json({ error: "Server configuration is incomplete" }, { status: 503 }); } const actual = Buffer.from(request.headers.get("authorization") || ""); const expected = Buffer.from("Bearer " + token); if (actual.length !== expected.length || !timingSafeEqual(actual, expected)) { return Response.json({ error: "Unauthorized" }, { status: 401 }); } if (!/^application\/json(?:;|$)/i.test(request.headers.get("content-type") || "")) { return Response.json({ error: "JSON required" }, { status: 415 }); } let message: unknown; try { const input = await readBoundedJson(request); message = input?.message; } catch { return Response.json({ error: "Invalid or oversized JSON" }, { status: 400 }); } if (typeof message !== "string" || !message.trim() || message.length > 1000) { return Response.json({ error: "Message must contain 1–1000 characters" }, { status: 400 }); } const abort = new AbortController(); const onAbort = () => abort.abort(); let timedOut = false; const timer = setTimeout(() => { timedOut = true; abort.abort(); }, 45000); request.signal.addEventListener("abort", onAbort, { once: true }); if (request.signal.aborted) onAbort(); const clean = () => { clearTimeout(timer); request.signal.removeEventListener("abort", onAbort); }; try { const upstream = await fetch("https://console.paasta.cloud/api/ai/v1/chat/completions", { method: "POST", redirect: "error", headers: { Authorization: "Bearer " + key, "Content-Type": "application/json" }, body: JSON.stringify({ model, messages: [{ role: "user", content: message }], stream: true, max_tokens: 512, }), signal: abort.signal, }); if (!upstream.ok || !upstream.body || !/text\/event-stream/i.test(upstream.headers.get("content-type") || "")) { await upstream.body?.cancel(); clean(); return Response.json({ error: "Upstream streaming request failed", upstreamStatus: upstream.status }, { status: 502 }); } const reader = upstream.body.getReader(); const body = new ReadableStream({ async pull(output) { try { const item = await reader.read(); if (item.done) { clean(); output.close(); } else output.enqueue(item.value); } catch { clean(); output.error(new Error("Stream interrupted")); } }, async cancel() { abort.abort(); clean(); await reader.cancel().catch(() => {}); }, }); return new Response(body, { headers: { "Content-Type": "text/event-stream; charset=utf-8", "Cache-Control": "no-store, no-transform", "X-Accel-Buffering": "no", "X-Content-Type-Options": "nosniff", }, }); } catch { clean(); return Response.json({ error: timedOut ? "Upstream timeout" : "Upstream unavailable" }, { status: timedOut ? 504 : 502 }); } }