diff --git a/app/checkRunner.js b/app/checkRunner.js
index 4ee95a1..d4d5c90 100644
--- a/app/checkRunner.js
+++ b/app/checkRunner.js
@@ -27,10 +27,20 @@ async function runCheck(check) {
}
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 * 1000
+ timeout: (check.timeout_seconds || 10) * 1000
});
child.on("close", (code) => {
diff --git a/app/checks/check_http_schooltech.sh b/app/checks/check_http_schooltech.sh
new file mode 100755
index 0000000..c5c8fae
--- /dev/null
+++ b/app/checks/check_http_schooltech.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+wget -q --spider https://server.schooltech.de/ || exit 1
+exit $?
\ No newline at end of file
diff --git a/app/server.js b/app/server.js
index 243573d..2113e31 100644
--- a/app/server.js
+++ b/app/server.js
@@ -2,6 +2,8 @@ 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"));
@@ -11,6 +13,32 @@ app.get("/", async (req, res) => {
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();
diff --git a/app/views/index.ejs b/app/views/index.ejs
index 28ad693..95bda8d 100644
--- a/app/views/index.ejs
+++ b/app/views/index.ejs
@@ -12,16 +12,23 @@
Name |
Status |
Last Run |
+Action |
<% checks.forEach(c => { %>
-| <%= c.id %> |
+
+ <%= c.display_id || c.id %> |
<%= c.name %> |
<%= c.last_status || 'N/A' %>
|
<%= c.last_run || 'N/A' %> |
+
+
+ |
<% }) %>
diff --git a/db/init.sql b/db/init.sql
index 75ba892..36ad80a 100644
--- a/db/init.sql
+++ b/db/init.sql
@@ -26,3 +26,26 @@ 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');
+
+UPDATE checks
+SET script_name = 'check_http_google.sh'
+WHERE name = 'Google HTTP';
+
+UPDATE checks
+SET target = 'appServer_InfoDB'
+WHERE name = 'Postgres Container Running';
+
+UPDATE checks
+SET target = 'appserver_default,appServer_InfoDB'
+WHERE name = 'Default Network contains Postgres';
+
+ALTER TABLE checks
+ADD COLUMN display_id TEXT;
+
+UPDATE checks
+SET display_id = id
+WHERE name = 'Google HTTP';
+
+INSERT INTO checks (name, type, description, target, script_name, display_id)
+VALUES
+('schooltech.ch erreichbar', 'script', 'Prüft schooltech.ch', NULL, 'check_http_schooltech.sh', '1.1')
\ No newline at end of file
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 64b2acb..7173f86 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -11,9 +11,9 @@ services:
- ./checks:/app/checks
- /var/run/docker.sock:/var/run/docker.sock
ports:
- - "3000:3000"
+ - "3030:3000"
depends_on:
- - db
+ - appServerInfoDB
environment:
DB_HOST: db
DB_USER: postgres
@@ -22,7 +22,7 @@ services:
DB_PORT: 5432
restart: unless-stopped
- db:
+ appServerInfoDB:
image: postgres:16-alpine
container_name: info-postgres
restart: unless-stopped
@@ -35,3 +35,19 @@ services:
- ./db/init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
+
+ appServerInfoPgadmin:
+ image: dpage/pgadmin4:latest
+ container_name: appServer_InfoPgadmin
+ environment:
+ PGADMIN_DEFAULT_EMAIL: admin@local.dev
+ PGADMIN_DEFAULT_PASSWORD: admin
+ PGADMIN_CONFIG_SERVER_MODE: 'False'
+ ports:
+ - "5050:80"
+ depends_on:
+ - appServerInfoDB
+ volumes:
+ - /home/chk/Documents/appServerInfo/pgadmin/data:/var/lib/pgadmin
+ - /home/chk/Documents/appServerInfo/pgadmin/servers.json:/pgadmin4/servers.json
+ restart: unless-stopped
diff --git a/pgadmin/servers.json b/pgadmin/servers.json
new file mode 100755
index 0000000..32ec183
--- /dev/null
+++ b/pgadmin/servers.json
@@ -0,0 +1,13 @@
+{
+ "Servers": {
+ "1": {
+ "Name": "appServerInfoDB",
+ "Group": "Servers",
+ "Host": "appServer_InfoDB",
+ "Port": 5432,
+ "MaintenanceDB": "infodb",
+ "Username": "postgres",
+ "SSLMode": "prefer"
+ }
+ }
+}
\ No newline at end of file
diff --git a/question_GPT.txt b/question_GPT.txt
new file mode 100755
index 0000000..cf83f0d
--- /dev/null
+++ b/question_GPT.txt
@@ -0,0 +1,343 @@
+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
+
+
+| ID |
+Name |
+Status |
+Last Run |
+Action |
+
+
+<% checks.forEach(c => { %>
+
+| <%= 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"
+
+
+====================================
+====================================
+====================================
+
+