Repository navigation
Expand file tree
/
Copy pathserver.js
More file actions
67 lines (61 loc) · 2.35 KB
/
Copy pathserver.js
File metadata and controls
67 lines (61 loc) · 2.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// Static dev server for site/. No dependencies.
// Usage: PORT=3100 node server.js (steps to the next free port if taken)
const http = require('http');
const fs = require('fs');
const path = require('path');
const net = require('net');
const ROOT = path.join(__dirname, 'site');
const START_PORT = Number(process.env.PORT) || 3100;
const LOG = process.env.LOG === '1';
const MIME = {
'.html': 'text/html; charset=utf-8',
'.css': 'text/css; charset=utf-8',
'.js': 'text/javascript; charset=utf-8',
'.json': 'application/json; charset=utf-8',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.jpg': 'image/jpeg',
'.webp': 'image/webp',
'.ico': 'image/x-icon',
'.woff2': 'font/woff2',
'.woff': 'font/woff',
'.txt': 'text/plain; charset=utf-8',
};
function resolve(urlPath) {
let p = decodeURIComponent(urlPath.split('?')[0]);
if (p.endsWith('/')) p += 'index.html';
const abs = path.normalize(path.join(ROOT, p));
if (!abs.startsWith(ROOT)) return null;
if (fs.existsSync(abs) && fs.statSync(abs).isDirectory()) return path.join(abs, 'index.html');
if (!path.extname(abs) && fs.existsSync(abs + '.html')) return abs + '.html';
return abs;
}
const server = http.createServer((req, res) => {
const file = resolve(req.url);
const send = (code, body, type) => {
res.writeHead(code, { 'Content-Type': type || 'text/plain; charset=utf-8', 'Cache-Control': 'no-store' });
res.end(body);
if (LOG) console.log(code, req.url);
};
if (!file || !fs.existsSync(file) || fs.statSync(file).isDirectory()) {
const nf = path.join(ROOT, '404.html');
return fs.existsSync(nf) ? send(404, fs.readFileSync(nf), MIME['.html']) : send(404, 'Not found');
}
send(200, fs.readFileSync(file), MIME[path.extname(file).toLowerCase()] || 'application/octet-stream');
});
// A port is only ours if nothing answers on either IP stack (see aside-com/CLAUDE.md).
function portFree(port) {
const probe = (host) => new Promise((ok) => {
const s = net.connect({ port, host });
s.once('connect', () => { s.destroy(); ok(false); });
s.once('error', () => ok(true));
});
return Promise.all([probe('127.0.0.1'), probe('::1')]).then((r) => r.every(Boolean));
}
(async () => {
let port = START_PORT;
while (!(await portFree(port))) port += 1;
server.listen(port, () => {
console.log(`\n IconKit landing\n http://127.0.0.1:${port}/\n`);
});
})();