feat: initial vps-health-api service

Zero-dependency Node.js health endpoint exposing CPU, RAM, disk and
uptime metrics. Bearer token auth, Docker-ready (node:22-alpine).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
le king fu 2026-02-26 20:48:09 -05:00
commit d6eb06302c
5 changed files with 123 additions and 0 deletions

2
.env.example Normal file
View file

@ -0,0 +1,2 @@
PORT=3001
HEALTH_TOKEN=change-me-to-a-strong-secret

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
node_modules/
.env
.env.local

6
Dockerfile Normal file
View file

@ -0,0 +1,6 @@
FROM node:22-alpine
WORKDIR /app
COPY package.json index.js ./
EXPOSE 3001
USER node
CMD ["node", "index.js"]

103
index.js Normal file
View file

@ -0,0 +1,103 @@
const http = require("node:http");
const os = require("node:os");
const { execSync } = require("node:child_process");
const PORT = parseInt(process.env.PORT || "3001", 10);
const TOKEN = process.env.HEALTH_TOKEN;
function readProcStat() {
try {
const line = execSync("head -1 /proc/stat", { encoding: "utf-8" }).trim();
const parts = line.split(/\s+/).slice(1).map(Number);
const idle = parts[3] + parts[4];
const total = parts.reduce((a, b) => a + b, 0);
return { idle, total };
} catch {
return null;
}
}
function getCpuPercent() {
const t1 = readProcStat();
if (!t1) return 0;
const { idle: idle1, total: total1 } = t1;
// Sample over 500ms
execSync("sleep 0.5");
const t2 = readProcStat();
if (!t2) return 0;
const dIdle = t2.idle - idle1;
const dTotal = t2.total - total1;
if (dTotal === 0) return 0;
return Math.round((1 - dIdle / dTotal) * 100);
}
function getDisk() {
try {
const out = execSync("df / --output=size,used,avail -B1", {
encoding: "utf-8",
});
const parts = out.trim().split("\n")[1].trim().split(/\s+/).map(Number);
const totalGB = +(parts[0] / 1e9).toFixed(1);
const usedGB = +(parts[1] / 1e9).toFixed(1);
const freeGB = +(parts[2] / 1e9).toFixed(1);
const usagePercent = totalGB > 0 ? Math.round((usedGB / totalGB) * 100) : 0;
return { totalGB, usedGB, freeGB, usagePercent };
} catch {
return { totalGB: 0, usedGB: 0, freeGB: 0, usagePercent: 0 };
}
}
function getHealth() {
const cpus = os.cpus();
const totalMem = os.totalmem();
const freeMem = os.freemem();
const usedMem = totalMem - freeMem;
return {
timestamp: new Date().toISOString(),
hostname: os.hostname(),
uptime: Math.floor(os.uptime()),
cpu: {
model: cpus[0]?.model?.trim() || "unknown",
cores: cpus.length,
loadAvg: os.loadavg().map((l) => +l.toFixed(2)),
usagePercent: getCpuPercent(),
},
memory: {
totalGB: +(totalMem / 1e9).toFixed(1),
usedGB: +(usedMem / 1e9).toFixed(1),
freeGB: +(freeMem / 1e9).toFixed(1),
usagePercent: Math.round((usedMem / totalMem) * 100),
},
disk: getDisk(),
};
}
const server = http.createServer((req, res) => {
res.setHeader("Content-Type", "application/json");
if (req.url !== "/health" || req.method !== "GET") {
res.writeHead(404);
res.end(JSON.stringify({ error: "Not found" }));
return;
}
if (TOKEN) {
const auth = req.headers["authorization"];
if (auth !== `Bearer ${TOKEN}`) {
res.writeHead(401);
res.end(JSON.stringify({ error: "Unauthorized" }));
return;
}
}
const data = getHealth();
res.writeHead(200);
res.end(JSON.stringify(data));
});
server.listen(PORT, () => {
console.log(`vps-health-api listening on :${PORT}`);
});

9
package.json Normal file
View file

@ -0,0 +1,9 @@
{
"name": "vps-health-api",
"version": "1.0.0",
"description": "Lightweight VPS health monitoring endpoint",
"main": "index.js",
"scripts": {
"start": "node index.js"
}
}