From 58b490b4f116ff422620cfc098d9f2300c17bff0 Mon Sep 17 00:00:00 2001 From: ChKendel <79915315+ChKendel@users.noreply.github.com> Date: Sun, 15 Feb 2026 21:44:57 +0100 Subject: [PATCH] first commit --- app/checkRunner.js | 47 ++++++++++++++++++++++++++++++++ app/checks/check_http_google.sh | 3 +++ app/db.js | 9 +++++++ app/dockerApi.js | 48 +++++++++++++++++++++++++++++++++ app/init.sh | 2 ++ app/package.json | 11 ++++++++ app/public/style.css | 5 ++++ app/scheduler.js | 26 ++++++++++++++++++ app/server.js | 17 ++++++++++++ app/views/index.ejs | 30 +++++++++++++++++++++ db/init.sql | 28 +++++++++++++++++++ docker-compose.yaml | 37 +++++++++++++++++++++++++ 12 files changed, 263 insertions(+) create mode 100644 app/checkRunner.js create mode 100644 app/checks/check_http_google.sh create mode 100644 app/db.js create mode 100644 app/dockerApi.js create mode 100644 app/init.sh create mode 100644 app/package.json create mode 100644 app/public/style.css create mode 100644 app/scheduler.js create mode 100644 app/server.js create mode 100644 app/views/index.ejs create mode 100644 db/init.sql create mode 100644 docker-compose.yaml diff --git a/app/checkRunner.js b/app/checkRunner.js new file mode 100644 index 0000000..4ee95a1 --- /dev/null +++ b/app/checkRunner.js @@ -0,0 +1,47 @@ +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") { + return new Promise((resolve) => { + const scriptPath = path.join(CHECKS_DIR, check.script_name); + const child = spawn("sh", [scriptPath], { + timeout: check.timeout_seconds * 1000 + }); + + child.on("close", (code) => { + resolve({ + status: code === 0 ? "OK" : "FAIL", + message: `Exit code: ${code}`, + duration: Date.now() - start + }); + }); + }); + } +} + +module.exports = runCheck; diff --git a/app/checks/check_http_google.sh b/app/checks/check_http_google.sh new file mode 100644 index 0000000..2f38fd2 --- /dev/null +++ b/app/checks/check_http_google.sh @@ -0,0 +1,3 @@ +#!/bin/sh +wget -q --spider https://google.com +exit $? diff --git a/app/db.js b/app/db.js new file mode 100644 index 0000000..c24395b --- /dev/null +++ b/app/db.js @@ -0,0 +1,9 @@ +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 +}); diff --git a/app/dockerApi.js b/app/dockerApi.js new file mode 100644 index 0000000..f505d6b --- /dev/null +++ b/app/dockerApi.js @@ -0,0 +1,48 @@ +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 }; diff --git a/app/init.sh b/app/init.sh new file mode 100644 index 0000000..97d5c7e --- /dev/null +++ b/app/init.sh @@ -0,0 +1,2 @@ +chmod +x checks/*.sh +docker compose up --build \ No newline at end of file diff --git a/app/package.json b/app/package.json new file mode 100644 index 0000000..4e08df5 --- /dev/null +++ b/app/package.json @@ -0,0 +1,11 @@ +{ + "name": "docker-info-dashboard", + "version": "2.0.0", + "main": "server.js", + "dependencies": { + "ejs": "^3.1.9", + "express": "^4.18.2", + "node-cron": "^3.0.3", + "pg": "^8.11.3" + } +} diff --git a/app/public/style.css b/app/public/style.css new file mode 100644 index 0000000..8c0548a --- /dev/null +++ b/app/public/style.css @@ -0,0 +1,5 @@ +body { font-family: Arial; } +table { border-collapse: collapse; width: 80%; } +th, td { border: 1px solid #ccc; padding: 8px; } +.ok { color: green; font-weight: bold; } +.fail { color: red; font-weight: bold; } diff --git a/app/scheduler.js b/app/scheduler.js new file mode 100644 index 0000000..0a99b46 --- /dev/null +++ b/app/scheduler.js @@ -0,0 +1,26 @@ +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; diff --git a/app/server.js b/app/server.js new file mode 100644 index 0000000..243573d --- /dev/null +++ b/app/server.js @@ -0,0 +1,17 @@ +const express = require("express"); +const pool = require("./db"); +const scheduleChecks = require("./scheduler"); + +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.listen(3000, async () => { + console.log("Server läuft auf Port 3000"); + await scheduleChecks(); +}); diff --git a/app/views/index.ejs b/app/views/index.ejs new file mode 100644 index 0000000..28ad693 --- /dev/null +++ b/app/views/index.ejs @@ -0,0 +1,30 @@ + + +
+| ID | +Name | +Status | +Last Run | +
|---|---|---|---|
| <%= c.id %> | +<%= c.name %> | ++<%= c.last_status || 'N/A' %> + | +<%= c.last_run || 'N/A' %> | +