65 lines
2.5 KiB
JavaScript
65 lines
2.5 KiB
JavaScript
#!/usr/bin/env node
|
|
// Serves companion/dist/ over HTTPS using the mkcert-issued test.locqr.dev
|
|
// cert (dev.md — same domain as the test website, different port, so the
|
|
// companion stub gets a secure context for getUserMedia()). Requires
|
|
// `npm run build` in companion/ first — this serves the built output, not
|
|
// the TypeScript source, mirroring how the extension is loaded from
|
|
// extension/dist/.
|
|
//
|
|
// Plain Node https + fs — same shape as serve-website.js.
|
|
|
|
import { createServer } from 'node:https';
|
|
import { readFile, stat } from 'node:fs/promises';
|
|
import { extname, join, normalize } from 'node:path';
|
|
import { dirname } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
const COMPANION_DIST_DIR = join(ROOT, 'companion', 'dist');
|
|
const PORT = 5174;
|
|
|
|
const MIME = {
|
|
'.html': 'text/html; charset=utf-8',
|
|
'.js': 'text/javascript; charset=utf-8',
|
|
'.css': 'text/css; charset=utf-8',
|
|
'.json': 'application/json',
|
|
};
|
|
|
|
async function resolveFile(urlPath) {
|
|
const safePath = normalize(urlPath === '/' ? '/index.html' : urlPath).replace(/^(\.\.[/\\])+/, '');
|
|
const filePath = join(COMPANION_DIST_DIR, safePath);
|
|
if (!filePath.startsWith(COMPANION_DIST_DIR)) return null; // path traversal guard
|
|
|
|
try {
|
|
const stats = await stat(filePath);
|
|
return stats.isFile() ? filePath : null;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
const options = {
|
|
cert: await readFile(join(ROOT, 'dev', 'certs', 'test.locqr.dev.pem')),
|
|
key: await readFile(join(ROOT, 'dev', 'certs', 'test.locqr.dev-key.pem')),
|
|
};
|
|
|
|
createServer(options, async (req, res) => {
|
|
const url = new URL(req.url ?? '/', 'https://test.locqr.dev');
|
|
const filePath = await resolveFile(url.pathname);
|
|
|
|
if (!filePath) {
|
|
res.writeHead(404).end('Not found — did you run `npm run build` in companion/?');
|
|
return;
|
|
}
|
|
|
|
const body = await readFile(filePath);
|
|
// No caching, ever — this is a dev stub rebuilt on every source edit,
|
|
// and mobile browsers in particular are prone to heuristically caching
|
|
// a same-URL response across page reloads when the server sends no
|
|
// explicit Cache-Control at all, silently serving a stale bundle.
|
|
res.writeHead(200, { 'Content-Type': MIME[extname(filePath)] ?? 'application/octet-stream', 'Cache-Control': 'no-store' });
|
|
res.end(body);
|
|
}).listen(PORT, () => {
|
|
console.log(`Serving companion/dist/ -> https://test.locqr.dev:${PORT}`);
|
|
});
|