import express from "express"; import path from "node:path"; import { fileURLToPath } from "node:url"; import { fetchPageText } from "./fetch-page.js"; import { runPrefill } from "./prefill.js"; import { parseAlog } from "./alog.js"; import { listAlogLibrary, readAlogFromLibrary } from "./alog-library.js"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const ROOT = path.join(__dirname, ".."); const PORT = Number(process.env.PORT) || 8090; const app = express(); app.use(express.json({ limit: "2mb" })); app.use(express.static(path.join(ROOT, "public"))); app.use("/shared", express.static(path.join(ROOT, "shared"))); app.post("/api/prefill", async (req, res) => { const url = typeof req.body?.url === "string" ? req.body.url.trim() : ""; if (!url) return res.status(400).json({ ok: false, code: "bad_url", error: "Missing url." }); let page; try { page = await fetchPageText(url); } catch (err) { const code = err.code ?? "fetch_failed"; const status = code === "fetch_timeout" ? 504 : code === "bad_url" ? 400 : 502; return res.status(status).json({ ok: false, code, error: err.message }); } try { const result = await runPrefill(page); res.json({ ok: true, ...result }); } catch (err) { console.error("prefill failed:", err); const code = err.code ?? "prefill_failed"; const status = code === "no_model" ? 503 : code === "unparseable_model_output" ? 422 : 500; res.status(status).json({ ok: false, code, error: err.message }); } }); app.post("/api/alog", (req, res) => { const content = req.body?.content; if (typeof content !== "string" || content.trim() === "") { return res.status(400).json({ ok: false, code: "bad_request", error: "Missing .alog file content." }); } try { const result = parseAlog(content, req.body?.filename ?? "upload.alog"); res.json({ ok: true, ...result }); } catch (err) { console.error("alog parse failed:", err); res.status(422).json({ ok: false, code: "unparseable_alog", error: err.message }); } }); app.get("/api/alog/library", async (_req, res) => { try { res.json({ ok: true, files: await listAlogLibrary() }); } catch (err) { res.status(500).json({ ok: false, code: "library_failed", error: err.message }); } }); app.get("/api/alog/library/:filename", async (req, res) => { try { const result = await readAlogFromLibrary(req.params.filename); res.json({ ok: true, ...result }); } catch (err) { const status = err.code === "not_found" ? 404 : err.code === "bad_filename" ? 400 : 422; res.status(status).json({ ok: false, code: err.code ?? "library_read_failed", error: err.message }); } }); app.listen(PORT, () => { console.log(`Roast planner webapp listening on http://localhost:${PORT}`); });