|
@@ -0,0 +1,66 @@
|
|
|
|
|
+export default {
|
|
|
|
|
+ async fetch(request, env) {
|
|
|
|
|
+ const url = new URL(request.url);
|
|
|
|
|
+ let pathname = url.pathname.replace(/\/{2,}/g, "/");
|
|
|
|
|
+
|
|
|
|
|
+ // Serve admin UI at root /
|
|
|
|
|
+ if (pathname === "/") {
|
|
|
|
|
+ return serveFromKV(env, "admin", "index.html");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Normalize /site -> /site/
|
|
|
|
|
+ if (!pathname.endsWith("/") && pathname.split("/").length === 2) {
|
|
|
|
|
+ const redirectUrl = new URL(url);
|
|
|
|
|
+ redirectUrl.pathname = pathname + "/";
|
|
|
|
|
+ return Response.redirect(redirectUrl.toString(), 301);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Split path
|
|
|
|
|
+ const parts = pathname.replace(/^\/+/, "").split("/");
|
|
|
|
|
+
|
|
|
|
|
+ if (!parts[0]) {
|
|
|
|
|
+ return new Response("Not Found", { status: 404 });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const site = parts[0];
|
|
|
|
|
+ const filePath =
|
|
|
|
|
+ parts.length === 1 || parts[1] === ""
|
|
|
|
|
+ ? "index.html"
|
|
|
|
|
+ : parts.slice(1).join("/");
|
|
|
|
|
+
|
|
|
|
|
+ return serveFromKV(env, site, filePath);
|
|
|
|
|
+ },
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+async function serveFromKV(env, site, filePath) {
|
|
|
|
|
+ const key = `site:${site}:${filePath}`;
|
|
|
|
|
+
|
|
|
|
|
+ const content = await env.PAGES_KV.get(key, {
|
|
|
|
|
+ type: "arrayBuffer",
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ if (!content) {
|
|
|
|
|
+ return new Response("Not Found", { status: 404 });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return new Response(content, {
|
|
|
|
|
+ headers: {
|
|
|
|
|
+ "Content-Type": contentType(filePath),
|
|
|
|
|
+ "Cache-Control": "public, max-age=300",
|
|
|
|
|
+ },
|
|
|
|
|
+ });
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function contentType(path) {
|
|
|
|
|
+ if (path.endsWith(".html")) return "text/html; charset=utf-8";
|
|
|
|
|
+ if (path.endsWith(".css")) return "text/css; charset=utf-8";
|
|
|
|
|
+ if (path.endsWith(".js")) return "application/javascript; charset=utf-8";
|
|
|
|
|
+ if (path.endsWith(".json")) return "application/json; charset=utf-8";
|
|
|
|
|
+ if (path.endsWith(".png")) return "image/png";
|
|
|
|
|
+ if (path.endsWith(".jpg") || path.endsWith(".jpeg")) return "image/jpeg";
|
|
|
|
|
+ if (path.endsWith(".svg")) return "image/svg+xml";
|
|
|
|
|
+ if (path.endsWith(".ico")) return "image/x-icon";
|
|
|
|
|
+ if (path.endsWith(".woff")) return "font/woff";
|
|
|
|
|
+ if (path.endsWith(".woff2")) return "font/woff2";
|
|
|
|
|
+ return "application/octet-stream";
|
|
|
|
|
+}
|