41 lines
1.8 KiB
JavaScript
41 lines
1.8 KiB
JavaScript
// Local-only phone review server. Reports contain timing and browser metrics.
|
|
import { createServer } from "vite";
|
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
import { resolve } from "node:path";
|
|
import { randomUUID } from "node:crypto";
|
|
|
|
const output = resolve(".tools/mobile-calibration");
|
|
await mkdir(output, { recursive: true });
|
|
const server = await createServer({
|
|
server: { host: "0.0.0.0", port: 5189, strictPort: true },
|
|
plugins: [{
|
|
name: "local-mobile-calibration",
|
|
configureServer(server) {
|
|
server.middlewares.use("/__mobile-review", (req, res) => {
|
|
if (req.method !== "POST" || req.headers.origin !== `http://${req.headers.host}`) {
|
|
res.writeHead(403).end(); return;
|
|
}
|
|
let body = "";
|
|
req.on("data", chunk => {
|
|
body += chunk;
|
|
if (body.length > 131072) { res.writeHead(413).end(); req.destroy(); }
|
|
});
|
|
req.on("end", async () => {
|
|
try {
|
|
const report = JSON.parse(body);
|
|
if (report.version !== 1 || !Array.isArray(report.samples) || report.samples.length > 30)
|
|
throw new Error("Invalid report");
|
|
const id = `${Date.now()}-${randomUUID()}`;
|
|
await writeFile(resolve(output, `${id}.json`), JSON.stringify({ receivedAt: new Date().toISOString(), ...report }, null, 2));
|
|
res.writeHead(200, { "Content-Type": "application/json" }).end(JSON.stringify({ id }));
|
|
console.log(`Mobile calibration received: ${id} (${String(report.userAgent).slice(0, 180)})`);
|
|
} catch { res.writeHead(400).end(); }
|
|
});
|
|
});
|
|
},
|
|
}],
|
|
});
|
|
await server.listen();
|
|
server.printUrls();
|
|
console.log("Open /reference/mobile-review.html on the phone; keep the tab visible during measurement.");
|