Eine WebPage die den Status vom Netzwerk und Docker-Containern angeben soll. == NodeJS: Server.js ========================== const express = require("express"); const pool = require("./db"); const scheduleChecks = require("./scheduler"); const runCheck = require("./checkRunner"); const app = express(); app.set("view engine", "ejs"); app.use(express.static("public")); app.get("/", async (req, res) => { const { rows } = await pool.query("SELECT * FROM checks ORDER BY id"); res.render("index", { checks: rows }); }); app.post("/run/:id", async (req, res) => { const checkId = req.params.id; const { rows } = await pool.query("SELECT * FROM checks WHERE id = $1", [checkId]); if (rows.length === 0) { return res.status(404).send("Check not found"); } const check = rows[0]; const result = await runCheck(check); await pool.query( `INSERT INTO results (check_id, status, message, duration_ms) VALUES ($1,$2,$3,$4)`, [check.id, result.status, result.message, result.duration] ); await pool.query( `UPDATE checks SET last_run = NOW(), last_status = $1 WHERE id = $2`, [result.status, check.id] ); res.redirect("/"); }); app.listen(3000, async () => { console.log("Server läuft auf Port 3000"); await scheduleChecks(); }); == NodeJS: scheduler.js ============================================= const cron = require("node-cron"); const pool = require("./db"); const runCheck = require("./checkRunner"); async function scheduleChecks() { const { rows } = await pool.query("SELECT * FROM checks WHERE active = true"); rows.forEach(check => { cron.schedule(`*/${check.schedule_seconds} * * * * *`, async () => { const result = await runCheck(check); await pool.query( `INSERT INTO results (check_id, status, message, duration_ms) VALUES ($1,$2,$3,$4)`, [check.id, result.status, result.message, result.duration] ); await pool.query( `UPDATE checks SET last_run = NOW(), last_status = $1 WHERE id = $2`, [result.status, check.id] ); }); }); } module.exports = scheduleChecks; == nodeJS: db.js ==================================================== const { Pool } = require("pg"); module.exports = new Pool({ host: process.env.DB_HOST, user: process.env.DB_USER, password: process.env.DB_PASSWORD, database: process.env.DB_NAME, port: process.env.DB_PORT }); == nodeJS: dockerApi.js =============================================================== const http = require("http"); function dockerRequest(path) { return new Promise((resolve, reject) => { const options = { socketPath: "/var/run/docker.sock", path, method: "GET" }; const req = http.request(options, (res) => { let data = ""; res.on("data", chunk => data += chunk); res.on("end", () => { try { resolve(JSON.parse(data)); } catch { resolve(data); } }); }); req.on("error", reject); req.end(); }); } async function isContainerRunning(name) { try { const data = await dockerRequest(`/containers/${name}/json`); return data.State?.Running === true; } catch { return false; } } async function networkContainsContainer(networkName, containerName) { try { const data = await dockerRequest(`/networks/${networkName}`); if (!data.Containers) return false; return Object.values(data.Containers) .some(c => c.Name === containerName); } catch { return false; } } module.exports = { isContainerRunning, networkContainsContainer }; == NodeJS: checkRunner ==================================================== const { spawn } = require("child_process"); const path = require("path"); const dockerApi = require("./dockerApi"); const CHECKS_DIR = path.join(__dirname, "checks"); async function runCheck(check) { const start = Date.now(); if (check.type === "docker_container") { const result = await dockerApi.isContainerRunning(check.target); return { status: result ? "OK" : "FAIL", message: `Container ${check.target} running: ${result}`, duration: Date.now() - start }; } if (check.type === "docker_network") { const [network, container] = check.target.split(","); const result = await dockerApi.networkContainsContainer(network, container); return { status: result ? "OK" : "FAIL", message: `Container ${container} in network ${network}: ${result}`, duration: Date.now() - start }; } if (check.type === "script") { if (!check.script_name) { return { status: "FAIL", message: "No script_name defined", duration: Date.now() - start }; } return new Promise((resolve) => { const scriptPath = path.join(CHECKS_DIR, check.script_name); const child = spawn("sh", [scriptPath], { timeout: (check.timeout_seconds || 10) * 1000 }); child.on("close", (code) => { resolve({ status: code === 0 ? "OK" : "FAIL", message: `Exit code: ${code}`, duration: Date.now() - start }); }); }); } } module.exports = runCheck; == index.ejs ============================ Docker Info Dashboard

System Status

<% checks.forEach(c => { %> <% }) %>
ID Name Status Last Run Action
<%= c.id %> <%= c.name %> <%= c.last_status || 'N/A' %> <%= c.last_run || 'N/A' %>
== checks/check_http_google.sh ============================= #!/bin/sh wget -q --spider https://google.com exit $? == DB =================================== CREATE TABLE checks ( id SERIAL PRIMARY KEY, name TEXT NOT NULL, type TEXT NOT NULL, -- script | docker_container | docker_network description TEXT, target TEXT, script_name TEXT, schedule_seconds INTEGER DEFAULT 60, timeout_seconds INTEGER DEFAULT 20, active BOOLEAN DEFAULT TRUE, last_run TIMESTAMP, last_status TEXT ); CREATE TABLE results ( id SERIAL PRIMARY KEY, check_id INTEGER REFERENCES checks(id) ON DELETE CASCADE, run_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, status TEXT, message TEXT, duration_ms INTEGER ); INSERT INTO checks (name, type, description, target) VALUES ('Google HTTP', 'script', 'Prüft google.com', NULL), ('Postgres Container Running', 'docker_container', 'Ist Postgres Container aktiv?', 'info-postgres'), ('Default Network contains Postgres', 'docker_network', 'Ist Postgres im default Netzwerk?', 'bridge'); == Docker-compose.yaml ========================================= services: app: image: node:20-alpine container_name: info-node-app working_dir: /app command: sh -c "npm install && node server.js" volumes: - ./app:/app - ./checks:/app/checks - /var/run/docker.sock:/var/run/docker.sock ports: - "3000:3000" depends_on: - db environment: DB_HOST: db DB_USER: postgres DB_PASSWORD: postgres DB_NAME: infodb DB_PORT: 5432 restart: unless-stopped db: image: postgres:16-alpine container_name: info-postgres restart: unless-stopped environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: infodb volumes: - ./pgdata:/var/lib/postgresql/data - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql ports: - "5432:5432" ==================================== ==================================== ====================================