Add status page and counters

This commit is contained in:
“Naeel” 2026-02-23 18:12:20 +04:00
parent e9b171b8df
commit 29206538ef

View File

@ -8,6 +8,14 @@ const status = {
lastSuccess: "", lastSuccess: "",
}; };
const metrics = {
processed: 0,
failed: 0,
lastError: "",
lastMessageAt: "",
lastAction: "",
};
function setStatus(state, error) { function setStatus(state, error) {
status.state = state; status.state = state;
status.error = error || ""; status.error = error || "";
@ -16,6 +24,62 @@ function setStatus(state, error) {
} }
} }
function renderStatusPage() {
const queueName = (process.env.RABBIT_QUEUES || "crud_queue").split(",")[0] || "crud_queue";
const tableName = process.env.PG_TABLE || "rabbit_messages";
return `<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Rabbit NodeJS Worker</title>
<style>
body { font-family: Arial, sans-serif; background: #f6f7fb; color: #1c1c1c; margin: 0; }
.wrap { max-width: 900px; margin: 40px auto; padding: 0 20px; }
.card { background: #fff; border-radius: 14px; box-shadow: 0 8px 24px rgba(0,0,0,0.06); padding: 24px; }
.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; }
.stat { padding: 16px; border: 1px solid #e5e7eb; border-radius: 12px; background: #fafbff; }
.label { color: #6b7280; font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; }
.value { font-size: 20px; font-weight: 700; margin-top: 6px; }
.small { font-size: 12px; color: #6b7280; margin-top: 6px; }
.status { padding: 6px 12px; border-radius: 999px; display: inline-block; font-weight: 700; }
.ok { background: #e7f6ee; color: #1e4620; }
.bad { background: #fdeeee; color: #b42318; }
</style>
</head>
<body>
<div class="wrap">
<div class="card">
<h2>NodeJS Worker</h2>
<div class="small">Queue: ${queueName} | Table: ${tableName}</div>
<div style="margin: 12px 0;">
<span class="status ${status.state === "running" ? "ok" : "bad"}">${status.state}</span>
</div>
<div class="grid">
<div class="stat">
<div class="label">Processed</div>
<div class="value">${metrics.processed}</div>
</div>
<div class="stat">
<div class="label">Failed</div>
<div class="value">${metrics.failed}</div>
</div>
<div class="stat">
<div class="label">Last Action</div>
<div class="value">${metrics.lastAction || "-"}</div>
<div class="small">${metrics.lastMessageAt || ""}</div>
</div>
<div class="stat">
<div class="label">Last Error</div>
<div class="value">${metrics.lastError || "-"}</div>
<div class="small">${status.lastSuccess || ""}</div>
</div>
</div>
</div>
</div>
</body>
</html>`;
}
function buildPgConfig() { function buildPgConfig() {
if (process.env.DATABASE_URL) { if (process.env.DATABASE_URL) {
return { return {
@ -111,8 +175,8 @@ function startHttpServer() {
res.end(JSON.stringify(status)); res.end(JSON.stringify(status));
return; return;
} }
res.writeHead(200, { "Content-Type": "text/plain" }); res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
res.end("rabbit-nodeworker ready"); res.end(renderStatusPage());
}); });
server.listen(port, () => { server.listen(port, () => {
console.log(`health server listening on ${port}`); console.log(`health server listening on ${port}`);
@ -149,8 +213,14 @@ async function consumeLoop() {
try { try {
const parsed = parseMessage(msg.content); const parsed = parseMessage(msg.content);
await handleMessage(table, parsed); await handleMessage(table, parsed);
metrics.processed += 1;
metrics.lastAction = parsed.action || "create";
metrics.lastMessageAt = new Date().toISOString();
channel.ack(msg); channel.ack(msg);
} catch (err) { } catch (err) {
metrics.failed += 1;
metrics.lastError = String(err.message || err);
metrics.lastMessageAt = new Date().toISOString();
if (String(err.message || "").includes("missing id") || String(err.message || "").includes("invalid action")) { if (String(err.message || "").includes("missing id") || String(err.message || "").includes("invalid action")) {
channel.nack(msg, false, false); channel.nack(msg, false, false);
return; return;