magicciv/.project/designs/serve.js
Natalie 2e6b0fdefe feat(@projects/@magic-civilization): add designs server subcommand
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-04-26 15:11:35 -07:00

77 lines
2.7 KiB
JavaScript

#!/usr/bin/env node
const http = require("http");
const fs = require("fs");
const path = require("path");
const PORT = parseInt(process.argv[2] || "7777", 10);
const ROOT = __dirname;
const MIME = {
".html": "text/html",
".css": "text/css",
".js": "application/javascript",
".json": "application/json",
".png": "image/png",
".svg": "image/svg+xml",
".md": "text/plain",
};
http.createServer((req, res) => {
let urlPath = req.url === "/" ? "/index.html" : req.url;
// auto-serve directory listing if no index
if (urlPath === "/index.html" && !fs.existsSync(path.join(ROOT, "index.html"))) {
const files = fs.readdirSync(ROOT).filter(f => !f.startsWith(".") && f !== "serve.js");
const links = files
.sort((a, b) => {
const aHtml = a.endsWith(".html");
const bHtml = b.endsWith(".html");
if (aHtml && !bHtml) return -1;
if (!aHtml && bHtml) return 1;
return a.localeCompare(b);
})
.map(f => {
const isHtml = f.endsWith(".html");
const icon = isHtml ? "🖼" : f.endsWith(".json") ? "📋" : f.endsWith(".md") ? "📄" : "📁";
return `<li><a href="/${f}">${icon} ${f}</a></li>`;
})
.join("\n");
res.writeHead(200, { "Content-Type": "text/html" });
res.end(`<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Age of Dwarves — Designs</title>
<link href="https://fonts.googleapis.com/css2?family=Grenze+Gotisch:wght@900&family=Bitter:wght@700&display=swap" rel="stylesheet">
<style>
body { background:#171219; color:#e0d8c8; font-family:'Bitter',serif; padding:48px; max-width:600px; margin:0 auto; }
h1 { font-family:'Grenze Gotisch',serif; color:#f2d973; font-size:36px; margin-bottom:6px; }
p { color:#b2b2b2; font-size:13px; margin-bottom:32px; }
ul { list-style:none; padding:0; }
li { margin-bottom:10px; }
a { color:#d9b33f; text-decoration:none; font-size:15px; padding:10px 16px; display:block;
background:#17121e; border:1px solid #73591fcc; border-radius:3px; transition:all 150ms ease; }
a:hover { background:#331a0d; border-color:#d9b33f; color:#ffeb80; }
</style>
</head>
<body>
<h1>Age of Dwarves</h1>
<p>Design System · .project/designs/</p>
<ul>${links}</ul>
</body>
</html>`);
return;
}
const filePath = path.join(ROOT, urlPath);
if (!filePath.startsWith(ROOT)) { res.writeHead(403); res.end(); return; }
fs.readFile(filePath, (err, data) => {
if (err) { res.writeHead(404, { "Content-Type": "text/plain" }); res.end("Not found"); return; }
const ext = path.extname(filePath);
res.writeHead(200, { "Content-Type": MIME[ext] || "application/octet-stream" });
res.end(data);
});
}).listen(PORT, () => {
console.log(`\n Age of Dwarves designs → http://localhost:${PORT}\n`);
});