Spaces:
Running
Running
| #!/usr/bin/env bun | |
| // Simple static file server for Svelte build output | |
| import { join } from "path"; | |
| const PORT = process.env.PORT || 8000; | |
| const BUILD_DIR = "./build"; | |
| // MIME types for common web assets | |
| const MIME_TYPES = { | |
| ".html": "text/html", | |
| ".css": "text/css", | |
| ".js": "application/javascript", | |
| ".json": "application/json", | |
| ".png": "image/png", | |
| ".jpg": "image/jpeg", | |
| ".jpeg": "image/jpeg", | |
| ".gif": "image/gif", | |
| ".svg": "image/svg+xml", | |
| ".ico": "image/x-icon", | |
| ".woff": "font/woff", | |
| ".woff2": "font/woff2", | |
| ".ttf": "font/ttf", | |
| ".eot": "application/vnd.ms-fontobject", | |
| ".webp": "image/webp", | |
| ".avif": "image/avif", | |
| ".mp4": "video/mp4", | |
| ".webm": "video/webm" | |
| }; | |
| function getMimeType(filename) { | |
| const ext = filename.substring(filename.lastIndexOf(".")); | |
| return MIME_TYPES[ext] || "application/octet-stream"; | |
| } | |
| const server = Bun.serve({ | |
| port: PORT, | |
| hostname: "0.0.0.0", | |
| async fetch(req) { | |
| const url = new URL(req.url); | |
| let pathname = url.pathname; | |
| // Remove leading slash and default to index.html for root | |
| if (pathname === "/") { | |
| pathname = "index.html"; | |
| } else { | |
| pathname = pathname.substring(1); // Remove leading slash | |
| } | |
| try { | |
| // Try to serve the requested file | |
| const filePath = join(BUILD_DIR, pathname); | |
| const file = Bun.file(filePath); | |
| if (await file.exists()) { | |
| const mimeType = getMimeType(pathname); | |
| const headers = { | |
| "Content-Type": mimeType | |
| }; | |
| // Set cache headers | |
| if (pathname.includes("/_app/immutable/")) { | |
| // Long-term cache for immutable assets | |
| headers["Cache-Control"] = "public, max-age=31536000, immutable"; | |
| } else if (pathname.endsWith(".html")) { | |
| // No cache for HTML files | |
| headers["Cache-Control"] = "public, max-age=0, must-revalidate"; | |
| } else { | |
| // Short cache for other assets | |
| headers["Cache-Control"] = "public, max-age=3600"; | |
| } | |
| return new Response(file, { headers }); | |
| } | |
| // If file not found and no extension, serve index.html for SPA routing | |
| if (!pathname.includes(".")) { | |
| const indexFile = Bun.file(join(BUILD_DIR, "index.html")); | |
| if (await indexFile.exists()) { | |
| return new Response(indexFile, { | |
| headers: { | |
| "Content-Type": "text/html", | |
| "Cache-Control": "public, max-age=0, must-revalidate" | |
| } | |
| }); | |
| } | |
| } | |
| return new Response("Not Found", { | |
| status: 404, | |
| headers: { "Content-Type": "text/plain" } | |
| }); | |
| } catch (error) { | |
| console.error("Server error:", error); | |
| return new Response("Internal Server Error", { | |
| status: 500, | |
| headers: { "Content-Type": "text/plain" } | |
| }); | |
| } | |
| } | |
| }); | |
| console.log(`π Static server running on http://localhost:${server.port}`); | |
| console.log(`π Serving files from: ${BUILD_DIR}`); | |