diff --git a/build.mjs b/build.mjs index 26ae01c..aed3239 100644 --- a/build.mjs +++ b/build.mjs @@ -1,4 +1,4 @@ -import { rm, mkdir, readdir } from "node:fs/promises"; +import { rm, mkdir, readdir, stat, cp } from "node:fs/promises"; import { join } from "node:path"; const DIST = "dist"; @@ -7,12 +7,21 @@ const ASSETS = join(DIST, "assets"); async function build() { console.log("🚀 Starting ZeroCMS Production Build (via Bun)..."); - // 1. Clean and Create Directories + // 1. Compile WASM First + console.log("🦀 Compiling Rust WASM module..."); + const proc = Bun.spawn(["bash", "scripts/build_tagger.sh"]); + const exitCode = await proc.exited; + if (exitCode !== 0) { + console.error("❌ Failed to compile WASM module"); + process.exit(1); + } + + // 2. Clean and Create Directories await rm(DIST, { recursive: true, force: true }); await mkdir(ASSETS, { recursive: true }); await mkdir(join(DIST, "lib"), { recursive: true }); - // 2. Bundle Dashboard App + // 3. Bundle Dashboard App console.log("📦 Bundling app... "); const result = await Bun.build({ entrypoints: ["app.js"], @@ -35,7 +44,12 @@ async function build() { for (const lib of libs) { const src = join("lib", lib); const dest = join(DIST, "lib", lib); - await Bun.write(dest, Bun.file(src)); + const stats = await stat(src); + if (stats.isDirectory()) { + await cp(src, dest, { recursive: true }); + } else { + await Bun.write(dest, Bun.file(src)); + } } // Extract the generated hashed file name from Bun's output diff --git a/serve.js b/serve.js index 0fe3dcc..c18e35e 100644 --- a/serve.js +++ b/serve.js @@ -292,16 +292,32 @@ const server = http.createServer((req, res) => { return; } - let filePath = '.' + req.url.split('?')[0]; + const basePath = process.cwd(); + let requestPath = req.url.split('?')[0]; + try { + requestPath = decodeURIComponent(requestPath); + } catch (e) { + res.writeHead(400, { 'Content-Type': 'text/plain' }); + res.end('Bad Request'); + return; + } + + let filePath = path.join(basePath, requestPath); + + // Security: Prevent path traversal + if (!filePath.startsWith(basePath) || (filePath !== basePath && !filePath.startsWith(basePath + path.sep))) { + res.writeHead(403, { 'Content-Type': 'text/plain' }); + res.end('Forbidden'); + return; + } // Map root to the main index.html (One-Pager) - if (filePath === './') { - filePath = './index.html'; + if (filePath === basePath || filePath === basePath + path.sep) { + filePath = path.join(basePath, 'index.html'); } - + // High-Performance Library Serving if (req.url.startsWith('/lib/')) { - filePath = '.' + req.url; res.setHeader('Cache-Control', 'public, max-age=31536000, immutable'); }