import { createServer } from "node:http"; import { readFile } from "node:fs/promises"; import { extname, join, normalize } from "node:path"; import { fileURLToPath } from "node:url"; const root = fileURLToPath(new URL(".", import.meta.url)); const port = Number(process.env.PORT || 4173); const mime = { ".html": "text/html; charset=utf-8", ".css": "text/css; charset=utf-8", ".js": "text/javascript; charset=utf-8", ".json": "application/json; charset=utf-8", }; function readBody(request) { return new Promise((resolve, reject) => { let body = ""; request.on("data", (chunk) => { body += chunk; if (body.length > 2_000_000) { reject(new Error("Request body is too large")); request.destroy(); } }); request.on("end", () => resolve(body)); request.on("error", reject); }); } function sendJson(response, status, payload) { response.writeHead(status, { "Content-Type": "application/json; charset=utf-8" }); response.end(JSON.stringify(payload)); } async function handleChat(request, response) { try { const payload = JSON.parse(await readBody(request)); const apiBase = String(payload.apiBase || "").replace(/\/+$/, ""); const apiKey = String(payload.apiKey || ""); const model = String(payload.model || ""); if (!apiBase || !apiKey || !model) { sendJson(response, 400, { error: "API base, API key, and model are required." }); return; } const upstreamBody = { model, messages: payload.messages || [], temperature: payload.temperature, max_tokens: payload.max_tokens, }; if (payload.response_format) { upstreamBody.response_format = payload.response_format; } const upstream = await fetch(`${apiBase}/chat/completions`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}`, }, body: JSON.stringify(upstreamBody), }); const data = await upstream.json().catch(() => ({})); if (!upstream.ok) { sendJson(response, upstream.status, { error: data?.error?.message || "Model request failed." }); return; } sendJson(response, 200, { content: data?.choices?.[0]?.message?.content || "" }); } catch (error) { const cause = error.cause?.message ? ` (${error.cause.message})` : ""; sendJson(response, 500, { error: `${error.message}${cause}` }); } } async function handleStatic(request, response) { const url = new URL(request.url || "/", `http://${request.headers.host}`); const cleanPath = normalize(url.pathname === "/" ? "/index.html" : url.pathname).replace(/^(\.\.[/\\])+/, ""); const filePath = join(root, cleanPath); if (!filePath.startsWith(root)) { response.writeHead(403); response.end("Forbidden"); return; } try { const content = await readFile(filePath); response.writeHead(200, { "Content-Type": mime[extname(filePath)] || "application/octet-stream" }); response.end(content); } catch { response.writeHead(404); response.end("Not found"); } } const server = createServer((request, response) => { if (request.method === "POST" && request.url === "/api/chat") { handleChat(request, response); return; } if (request.method === "GET" || request.method === "HEAD") { handleStatic(request, response); return; } response.writeHead(405); response.end("Method not allowed"); }); server.listen(port, () => { console.log(`Identity Prompt Lab: http://localhost:${port}`); });