From cfcebcf6b6e22cd08d27e5b5d5960b3ac346e803 Mon Sep 17 00:00:00 2001 From: maniac Date: Mon, 8 Jun 2026 13:43:48 +0200 Subject: [PATCH] Add linux/webhook.py --- linux/webhook.py | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 linux/webhook.py diff --git a/linux/webhook.py b/linux/webhook.py new file mode 100644 index 0000000..431a8b8 --- /dev/null +++ b/linux/webhook.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +""" +Webhook-Empfänger für Lexware-Dump-Benachrichtigungen vom Windows-Server. + +POST /restore?db=f1 → nur f1 wiederherstellen +POST /restore → alle DBs prüfen und bei Bedarf wiederherstellen +GET /health → Liveness-Check +""" + +import http.server +import subprocess +import threading +import urllib.parse +import logging +import sys +import os + +PORT = 9055 +RESTORE_SCRIPT = "/opt/lexware-dumps/restore-latest.sh" +LOG_FILE = "/var/log/lexware-restore.log" +VALID_DBS = {"f1", "f2", "lexkonto", "lexkk", "rk", "lxoffice", "lx", "lxcatalog"} + +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s [webhook] %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + handlers=[ + logging.FileHandler(LOG_FILE), + logging.StreamHandler(sys.stdout), + ], +) + + +def run_restore(db: str | None): + env = os.environ.copy() + cmd = [RESTORE_SCRIPT] + if db: + env["RESTORE_DB"] = db + logging.info(f"Starte Restore: db={db or 'alle'}") + result = subprocess.run(cmd, env=env, capture_output=True, text=True) + if result.stdout: + for line in result.stdout.strip().splitlines(): + logging.info(line) + if result.returncode not in (0, 1): + logging.error(f"Restore-Skript exit {result.returncode}") + + +class WebhookHandler(http.server.BaseHTTPRequestHandler): + def log_message(self, fmt, *args): + logging.info(f"{self.address_string()} {fmt % args}") + + def do_POST(self): + parsed = urllib.parse.urlparse(self.path) + params = urllib.parse.parse_qs(parsed.query) + + if parsed.path != "/restore": + self._respond(404, "Not found") + return + + db = params.get("db", [None])[0] + if db and db not in VALID_DBS: + self._respond(400, f"Unbekannte Datenbank: {db}") + return + + self._respond(200, f"OK: Restore gestartet (db={db or 'alle'})") + # Restore asynchron starten damit der Webhook sofort antwortet + threading.Thread(target=run_restore, args=(db,), daemon=True).start() + + def do_GET(self): + if self.path == "/health": + self._respond(200, "OK") + else: + self._respond(404, "Not found") + + def _respond(self, code: int, body: str): + data = body.encode() + self.send_response(code) + self.send_header("Content-Type", "text/plain") + self.send_header("Content-Length", str(len(data))) + self.end_headers() + self.wfile.write(data) + + +if __name__ == "__main__": + server = http.server.ThreadingHTTPServer(("0.0.0.0", PORT), WebhookHandler) + logging.info(f"Webhook-Server läuft auf Port {PORT}") + server.serve_forever() \ No newline at end of file