[object Object]

← back to Socrata Cre Viewer

Serve public/ static assets so nav-agent (/nav-agent/*) returns 200 on :9778

713a9bdeaf03dda885c8196f44b1c47a571f8203 · 2026-08-13 14:28:44 -0700 · Steve Abrams

Files touched

Diff

commit 713a9bdeaf03dda885c8196f44b1c47a571f8203
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Aug 13 14:28:44 2026 -0700

    Serve public/ static assets so nav-agent (/nav-agent/*) returns 200 on :9778
---
 server.js | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/server.js b/server.js
index 9bbc7d0..5e9d431 100644
--- a/server.js
+++ b/server.js
@@ -94,6 +94,27 @@ function send(res, code, body, type = 'application/json') {
   res.end(typeof body === 'string' || Buffer.isBuffer(body) ? body : JSON.stringify(body));
 }
 
+// Static assets from public/ (e.g. the nav-agent drop-in). Additive: only serves
+// files that resolve INSIDE public/ — path-traversal attempts fall through to 404.
+const PUBLIC_DIR = path.join(__dirname, 'public');
+const MIME = {
+  '.js': 'application/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8',
+  '.html': 'text/html; charset=utf-8', '.json': 'application/json; charset=utf-8',
+  '.svg': 'image/svg+xml', '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
+  '.gif': 'image/gif', '.webp': 'image/webp', '.ico': 'image/x-icon', '.woff2': 'font/woff2',
+  '.woff': 'font/woff', '.ttf': 'font/ttf', '.map': 'application/json; charset=utf-8',
+};
+function serveStatic(res, pathname) {
+  const rel = decodeURIComponent(pathname).replace(/^\/+/, '');
+  const abs = path.resolve(PUBLIC_DIR, rel);
+  if (abs !== PUBLIC_DIR && !abs.startsWith(PUBLIC_DIR + path.sep)) return false; // traversal guard
+  if (!fs.existsSync(abs) || !fs.statSync(abs).isFile()) return false;
+  const type = MIME[path.extname(abs).toLowerCase()] || 'application/octet-stream';
+  res.writeHead(200, { 'Content-Type': type });
+  res.end(fs.readFileSync(abs));
+  return true;
+}
+
 const server = http.createServer(async (req, res) => {
   if (!authed(req)) { res.writeHead(401, { 'WWW-Authenticate': 'Basic realm="socrata-cre"' }); return res.end('Auth required'); }
   const url = new URL(req.url, 'http://x');
@@ -103,6 +124,7 @@ const server = http.createServer(async (req, res) => {
     if (url.pathname === '/api/facets') return send(res, 200, await apiFacets());
     if (url.pathname === '/api/rows') return send(res, 200, await apiRows(url.searchParams));
     if (url.pathname === '/healthz') return send(res, 200, { ok: true });
+    if (serveStatic(res, url.pathname)) return;
     return send(res, 404, { error: 'not found' });
   } catch (e) { console.error(e); return send(res, 500, { error: String(e.message || e) }); }
 });

← e9ca4ee nav-agent: universal grid-controls drop-in on internal dashb  ·  back to Socrata Cre Viewer  ·  chore: version-up v0.1.0 → v0.1.1 (session close) 130bdf8 →