import type { APIRoute } from "astro"; import { readdir, readFile, stat } from "node:fs/promises"; import { join } from "node:path"; const DATA_ROOT = join(process.env.REPO_ROOT ?? "/Users/ace/palacering", "palaces", "manglasabang", "palaceappsdata", "palacetravel"); export const GET: APIRoute = async ({ params }) => { const path = params.path; if (!path) return new Response("Not found", { status: 404 }); const segments = path.split("/"); // Prevent directory traversal if (segments.some(s => s === ".." || s === ".")) { return new Response("Forbidden", { status: 403 }); } const fullPath = join(DATA_ROOT, ...segments); // If path ends with .json, serve the file directly if (fullPath.endsWith(".json")) { try { const content = await readFile(fullPath, "utf-8"); return new Response(content, { headers: { "Content-Type": "application/json" }, }); } catch { return new Response("Not found", { status: 404 }); } } // If path points to a directory (like "places" or "days"), read all .json files and merge try { const files = await readdir(fullPath); const jsonFiles = files.filter(f => f.endsWith(".json")); // For "places" directory: return a map of filename -> contents if (segments[segments.length - 1] === "places") { const result: Record = {}; for (const file of jsonFiles) { if (file === "safety.json") continue; // safety is fetched separately const content = await readFile(join(fullPath, file), "utf-8"); result[file.replace(".json", "")] = JSON.parse(content); } return new Response(JSON.stringify(result), { headers: { "Content-Type": "application/json" }, }); } // For "days" directory: return array of day objects, sorted by filename if (segments[segments.length - 1] === "days") { const days = []; for (const file of jsonFiles.sort()) { const content = await readFile(join(fullPath, file), "utf-8"); days.push(JSON.parse(content)); } return new Response(JSON.stringify(days), { headers: { "Content-Type": "application/json" }, }); } // For "meals" directory: return array of meal objects with id from filename if (segments[segments.length - 1] === "meals") { const meals = []; for (const file of jsonFiles.sort()) { const content = await readFile(join(fullPath, file), "utf-8"); const meal = JSON.parse(content); meal.id = file.replace(".json", ""); meals.push(meal); } return new Response(JSON.stringify(meals), { headers: { "Content-Type": "application/json" }, }); } // For "actuals" directory: return ActualDay[] from day subdirectories if (segments[segments.length - 1] === "actuals") { const entries = await readdir(fullPath, { withFileTypes: true }); const dayDirs = entries .filter(e => e.isDirectory() && e.name.startsWith("day-")) .sort((a, b) => a.name.localeCompare(b.name)); const result = []; for (const dayDir of dayDirs) { const dayPath = join(fullPath, dayDir.name); const meta = JSON.parse(await readFile(join(dayPath, "meta.json"), "utf-8")); const entryFiles = (await readdir(dayPath)) .filter(f => f.startsWith("entry-") && f.endsWith(".json")) .sort(); const dayEntries = []; for (const f of entryFiles) { dayEntries.push(JSON.parse(await readFile(join(dayPath, f), "utf-8"))); } result.push({ label: meta.label, entries: dayEntries }); } return new Response(JSON.stringify(result), { headers: { "Content-Type": "application/json" }, }); } return new Response("Not found", { status: 404 }); } catch { return new Response("Not found", { status: 404 }); } };