web (node) renders the seeded customers table plus container hostname. db (postgres:16-alpine) seeds five PII-shaped rows from db/init.sql.
73 lines
2.3 KiB
JavaScript
73 lines
2.3 KiB
JavaScript
const http = require("http");
|
|
const os = require("os");
|
|
const { Pool } = require("pg");
|
|
|
|
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
|
|
const label = process.env.APP_LABEL || "VeilStream demo";
|
|
const port = Number(process.env.PORT || 3000);
|
|
|
|
const esc = (s) =>
|
|
String(s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ }[c]));
|
|
|
|
async function page() {
|
|
let rows = [];
|
|
let dbError = null;
|
|
try {
|
|
const r = await pool.query(
|
|
"SELECT id, full_name, email, phone, city, plan, mrr_cents FROM customers ORDER BY id"
|
|
);
|
|
rows = r.rows;
|
|
} catch (err) {
|
|
dbError = err.message;
|
|
}
|
|
|
|
const body = dbError
|
|
? `<p class="err">Database unreachable: ${esc(dbError)}</p>`
|
|
: `<table>
|
|
<tr><th>id</th><th>name</th><th>email</th><th>phone</th><th>city</th><th>plan</th><th>mrr</th></tr>
|
|
${rows
|
|
.map(
|
|
(r) =>
|
|
`<tr><td>${r.id}</td><td>${esc(r.full_name)}</td><td>${esc(r.email)}</td><td>${esc(
|
|
r.phone
|
|
)}</td><td>${esc(r.city)}</td><td>${esc(r.plan)}</td><td>$${(r.mrr_cents / 100).toFixed(
|
|
2
|
|
)}</td></tr>`
|
|
)
|
|
.join("\n ")}
|
|
</table>
|
|
<p class="meta">${rows.length} rows. If these values look like real people, the seed was not sanitized.</p>`;
|
|
|
|
return `<!doctype html>
|
|
<html><head><meta charset="utf-8"><title>${esc(label)}</title>
|
|
<style>
|
|
body{font:15px/1.5 -apple-system,system-ui,sans-serif;margin:2rem auto;max-width:52rem;padding:0 1rem}
|
|
table{border-collapse:collapse;width:100%;margin:1rem 0}
|
|
th,td{border:1px solid #ccc;padding:.4rem .6rem;text-align:left}
|
|
th{background:#f4f4f4}
|
|
.meta{color:#666}
|
|
.err{color:#b00}
|
|
dl{display:grid;grid-template-columns:max-content 1fr;gap:.2rem 1rem}
|
|
dt{color:#666}
|
|
</style></head><body>
|
|
<h1>${esc(label)}</h1>
|
|
<dl>
|
|
<dt>hostname</dt><dd>${esc(os.hostname())}</dd>
|
|
<dt>served at</dt><dd>${new Date().toISOString()}</dd>
|
|
<dt>node</dt><dd>${esc(process.version)}</dd>
|
|
</dl>
|
|
${body}
|
|
</body></html>`;
|
|
}
|
|
|
|
http
|
|
.createServer(async (req, res) => {
|
|
if (req.url === "/healthz") {
|
|
res.writeHead(200, { "content-type": "text/plain" });
|
|
return res.end("ok\n");
|
|
}
|
|
res.writeHead(200, { "content-type": "text/html; charset=utf-8" });
|
|
res.end(await page());
|
|
})
|
|
.listen(port, "0.0.0.0", () => console.log(`listening on ${port}`));
|