- build-mocks.cjs:从真实对话导出演示 mock 数据 - check-mocks.cjs:mock 数据完整性校验 - rebuild-cat-html.cjs / serve.cjs:分类页重建与本地预览服务
34 lines
1.6 KiB
JavaScript
34 lines
1.6 KiB
JavaScript
#!/usr/bin/env node
|
||
/* 极简静态文件服务器(官网预览用,替代沙箱内不可用的 python3 -m http.server) */
|
||
const http = require('http');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
|
||
const ROOT = path.join(__dirname, '..', 'exp');
|
||
const PORT = Number(process.argv[2]) || 8898;
|
||
|
||
const MIME = {
|
||
html: 'text/html; charset=utf-8', js: 'text/javascript', mjs: 'text/javascript',
|
||
css: 'text/css', json: 'application/json; charset=utf-8', png: 'image/png',
|
||
jpg: 'image/jpeg', jpeg: 'image/jpeg', gif: 'image/gif', svg: 'image/svg+xml',
|
||
webp: 'image/webp', woff: 'font/woff', woff2: 'font/woff2', ttf: 'font/ttf',
|
||
pdf: 'application/pdf', docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||
txt: 'text/plain; charset=utf-8', md: 'text/plain; charset=utf-8', ico: 'image/x-icon',
|
||
mp4: 'video/mp4', webm: 'video/webm',
|
||
};
|
||
|
||
http.createServer((req, res) => {
|
||
try {
|
||
let p = decodeURIComponent(new URL(req.url, 'http://x').pathname);
|
||
if (p.endsWith('/')) p += 'index.html';
|
||
const file = path.normalize(path.join(ROOT, p));
|
||
if (!file.startsWith(ROOT)) { res.writeHead(403); return res.end(); }
|
||
fs.readFile(file, (err, data) => {
|
||
if (err) { res.writeHead(404); return res.end('not found'); }
|
||
const ext = path.extname(file).slice(1).toLowerCase();
|
||
res.writeHead(200, { 'Content-Type': MIME[ext] || 'application/octet-stream' });
|
||
res.end(data);
|
||
});
|
||
} catch (e) { res.writeHead(500); res.end(String(e)); }
|
||
}).listen(PORT, '127.0.0.1', () => console.log(`preview: http://127.0.0.1:${PORT}/ (root: ${ROOT})`));
|