export const prerender = false; import type { APIRoute } from "astro"; import { readdir, readFile, stat, writeFile, mkdir, rm } from "node:fs/promises"; import { resolve, relative, extname, dirname, basename, join } from "node:path"; const REPO_ROOT = process.env.REPO_ROOT || resolve("/Users/ace/palacering"); const NOTEBOOK_ROOT = resolve(REPO_ROOT, "palaces", "manglasabang", "notebook"); function json(data: object, status = 200) { return new Response(JSON.stringify(data), { status, headers: { "Content-Type": "application/json" }, }); } function safePath(reqPath: string) { const target = resolve(NOTEBOOK_ROOT, reqPath); const rel = relative(NOTEBOOK_ROOT, target); if (rel.startsWith("..") || rel.startsWith("/")) return null; return target; } export const GET: APIRoute = async ({ url }) => { const reqPath = url.searchParams.get("path") || ""; const target = safePath(reqPath); if (!target) return json({ error: "Invalid path" }, 403); try { const s = await stat(target); if (s.isDirectory()) { const items = await readdir(target, { withFileTypes: true }); const entries = items .filter((d) => !d.name.startsWith(".") && !d.name.endsWith(".checkpoint.md")) .map((d) => ({ name: d.name, type: d.isDirectory() ? "dir" : "file", })) .sort((a, b) => { if (a.type !== b.type) return a.type === "dir" ? -1 : 1; return a.name.localeCompare(b.name); }); return json({ type: "dir", path: reqPath, entries }); } const content = await readFile(target, "utf-8"); const ext = extname(target).toLowerCase(); const format = ext === ".md" ? "md" : ext === ".html" || ext === ".htm" ? "html" : "txt"; let checkpoint: string | null = null; if (ext === ".md") { const name = basename(target); const cpPath = join(dirname(target), name.replace(/\.md$/, ".checkpoint.md")); try { checkpoint = await readFile(cpPath, "utf-8"); } catch {} } return json({ type: "file", name: reqPath.split("/").pop(), content, format, checkpoint }); } catch (e: any) { if (e.code === "ENOENT") return json({ error: "Not found" }, 404); return json({ error: e.message }, 500); } }; export const POST: APIRoute = async ({ request }) => { const body = await request.json(); const { action, path: reqPath, content } = body; if (!reqPath) return json({ error: "Path required" }, 400); const target = safePath(reqPath); if (!target) return json({ error: "Invalid path" }, 403); try { if (action === "mkdir") { await mkdir(target, { recursive: true }); return json({ ok: true }); } if (action === "create") { // Don't overwrite existing files try { await stat(target); return json({ error: "File already exists" }, 409); } catch {} await mkdir(dirname(target), { recursive: true }); await writeFile(target, content || "", "utf-8"); return json({ ok: true }); } if (action === "save") { await writeFile(target, content ?? "", "utf-8"); return json({ ok: true }); } if (action === "delete") { const rel = relative(NOTEBOOK_ROOT, target); if (!rel || rel === ".") return json({ error: "Cannot delete root" }, 400); await rm(target, { recursive: true }); return json({ ok: true }); } if (action === "approve") { const ext = extname(target).toLowerCase(); if (ext !== ".md") return json({ error: "Checkpoints only for .md files" }, 400); const current = await readFile(target, "utf-8"); const cpPath = join(dirname(target), basename(target).replace(/\.md$/, ".checkpoint.md")); await writeFile(cpPath, current, "utf-8"); return json({ ok: true }); } if (action === "discard") { const ext = extname(target).toLowerCase(); if (ext !== ".md") return json({ error: "Checkpoints only for .md files" }, 400); const cpPath = join(dirname(target), basename(target).replace(/\.md$/, ".checkpoint.md")); try { const checkpoint = await readFile(cpPath, "utf-8"); await writeFile(target, checkpoint, "utf-8"); return json({ ok: true, content: checkpoint }); } catch { return json({ error: "No checkpoint found" }, 404); } } return json({ error: "Unknown action" }, 400); } catch (e: any) { return json({ error: e.message }, 500); } };