46 lines
1.5 KiB
TypeScript
46 lines
1.5 KiB
TypeScript
import { readFile } from "node:fs/promises";
|
|
import path from "node:path";
|
|
import { PUBLIC_DIR, REPO_DIR, safeJoin } from "./paths";
|
|
|
|
/* Read-only file serving from the portfolio checkout, so the preview iframe
|
|
* can load the site's real stylesheets and the post's images. */
|
|
|
|
const TYPES: Record<string, string> = {
|
|
".css": "text/css; charset=utf-8",
|
|
".png": "image/png",
|
|
".jpg": "image/jpeg",
|
|
".jpeg": "image/jpeg",
|
|
".gif": "image/gif",
|
|
".webp": "image/webp",
|
|
".svg": "image/svg+xml",
|
|
".ico": "image/x-icon",
|
|
".mp4": "video/mp4",
|
|
};
|
|
|
|
/** Stylesheets the preview may load, keyed by their /site/... path. */
|
|
const SITE_CSS = new Set(["app/globals.css", "styles/markdown-container.css", "styles/blog.css"]);
|
|
|
|
async function send(file: string): Promise<Response> {
|
|
const type = TYPES[path.extname(file).toLowerCase()];
|
|
if (!type) return new Response("not found", { status: 404 });
|
|
try {
|
|
const body = await readFile(file);
|
|
return new Response(new Uint8Array(body), {
|
|
headers: { "content-type": type, "cache-control": "no-cache" },
|
|
});
|
|
} catch {
|
|
return new Response("not found", { status: 404 });
|
|
}
|
|
}
|
|
|
|
/** /site/<css allowlist> -> repo file; /site/<anything else> -> repo public/. */
|
|
export function serveSitePath(segments: string[]): Promise<Response> {
|
|
const rel = segments.join("/");
|
|
if (SITE_CSS.has(rel)) return send(path.join(REPO_DIR, rel));
|
|
return send(safeJoin(PUBLIC_DIR, ...segments));
|
|
}
|
|
|
|
export function servePublic(segments: string[]): Promise<Response> {
|
|
return send(safeJoin(PUBLIC_DIR, ...segments));
|
|
}
|