Files
ark-stronghold-protocol/server/http/common.js
T
sganggs b48a67caec feedback5 R1: pure move — the top-level code of server/index.js goes to server/http/ (config, common, files, static, media, buildTag, websocket, boot) verbatim; index.js imports it and re-exports the same 15 names, startServer and main are untouched
Only changes besides the move: `export` on helpers another module now needs (sendError, sendJson, splitUrl, GzipCache,
isNotModified, serveFile, serveMedia, noopLog, makeLogger), ROOT resolves two levels up from server/http/config.js
(same path), and short headers on the new files.
2026-10-05 16:02:33 +08:00

38 lines
2.2 KiB
JavaScript

// server/http/common.js — the helpers every HTTP route shares: URL split, the error page and JSON replies.
// Moved from server/index.js.
const escapeHtml = (s) => String(s).replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c]);
function errorPage(status, title, detail = '') {
return `<!doctype html><html lang="zh-CN"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<title>${status} · 卫戍协议:盟约</title><style>
:root{color-scheme:dark}body{margin:0;min-height:100vh;display:grid;place-items:center;background:#111614;color:#d8e3de;font:16px/1.6 "Noto Sans SC",system-ui,sans-serif}
main{border:1px solid #2c3a35;padding:32px 40px;max-width:520px;text-align:center}h1{margin:0;color:#4ed8af;font-size:56px;letter-spacing:4px}
p{margin:8px 0}a{color:#4ed8af}</style></head><body><main><h1>${status}</h1><p>${escapeHtml(title)}</p>
${detail ? `<p style="opacity:.6">${escapeHtml(detail)}</p>` : ''}<p><a href="/">返回首页 · Back to home</a></p></main></body></html>`;
}
export function sendError(req, res, status, title, detail) {
if (res.headersSent) { res.destroy(); return; }
const body = Buffer.from(errorPage(status, title, detail));
res.writeHead(status, { 'Content-Type': 'text/html; charset=utf-8', 'Content-Length': body.length, 'Cache-Control': 'no-store' });
res.end(req.method === 'HEAD' ? undefined : body);
}
export function sendJson(req, res, status, obj) {
const body = Buffer.from(JSON.stringify(obj));
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8', 'Content-Length': body.length, 'Cache-Control': 'no-store' });
res.end(req.method === 'HEAD' ? undefined : body);
}
/** Split an absolute request URL into raw path + query (also accepts absolute-form URLs). */
export function splitUrl(url) {
let u = url || '/';
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(u)) {
try { const parsed = new URL(u); u = parsed.pathname + parsed.search; } catch { return null; }
}
const q = u.indexOf('?');
const hashless = (s) => { const h = s.indexOf('#'); return h >= 0 ? s.slice(0, h) : s; };
return q >= 0 ? { rawPath: hashless(u.slice(0, q)), query: hashless(u.slice(q + 1)) } : { rawPath: hashless(u), query: '' };
}