From 2d32574d68940bb1b7936daa1a04c210f04d979d Mon Sep 17 00:00:00 2001 From: Sebastian Serfling Date: Wed, 29 Apr 2026 21:28:04 +0200 Subject: [PATCH] feat: Nextcloud Talk Error Handler als failure_module Bei jedem Flow-Fehler: Talk-Nachricht mit Step und Fehlermeldung. Markiert laufenden DB-Job als failed und gibt Server frei. Co-Authored-By: Claude Sonnet 4.6 --- .../flow.yaml | 15 ++++ .../flow_fehler_handler.lock | 17 +++++ .../flow_fehler_handler.py | 75 +++++++++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 f/Backup/backup_restore_orchestrator__flow/flow_fehler_handler.lock create mode 100644 f/Backup/backup_restore_orchestrator__flow/flow_fehler_handler.py diff --git a/f/Backup/backup_restore_orchestrator__flow/flow.yaml b/f/Backup/backup_restore_orchestrator__flow/flow.yaml index e644581..eb014de 100644 --- a/f/Backup/backup_restore_orchestrator__flow/flow.yaml +++ b/f/Backup/backup_restore_orchestrator__flow/flow.yaml @@ -125,6 +125,21 @@ value: lock: '!inline webhook_verarbeiten_&_naechsten_restore_auf_demselben_server_starten.lock' language: python3 + failure_module: + id: failure + summary: Flow-Fehler per Nextcloud Talk melden + value: + type: rawscript + content: '!inline flow_fehler_handler.py' + input_transforms: + error: + type: javascript + expr: error + flow_input: + type: javascript + expr: flow_input + lock: '!inline flow_fehler_handler.lock' + language: python3 schema: $schema: https://json-schema.org/draft/2020-12/schema type: object diff --git a/f/Backup/backup_restore_orchestrator__flow/flow_fehler_handler.lock b/f/Backup/backup_restore_orchestrator__flow/flow_fehler_handler.lock new file mode 100644 index 0000000..48ff042 --- /dev/null +++ b/f/Backup/backup_restore_orchestrator__flow/flow_fehler_handler.lock @@ -0,0 +1,17 @@ +# py: 3.12 +anyio==4.12.1 +bcrypt==5.0.0 +certifi==2026.2.25 +cffi==2.0.0 +cryptography==46.0.5 +h11==0.16.0 +httpcore==1.0.9 +httpx==0.28.1 +idna==3.11 +invoke==2.2.1 +mysql-connector-python==9.6.0 +paramiko==4.0.0 +pycparser==3.0 +pynacl==1.6.2 +typing-extensions==4.15.0 +wmill==1.657.2 diff --git a/f/Backup/backup_restore_orchestrator__flow/flow_fehler_handler.py b/f/Backup/backup_restore_orchestrator__flow/flow_fehler_handler.py new file mode 100644 index 0000000..85afa17 --- /dev/null +++ b/f/Backup/backup_restore_orchestrator__flow/flow_fehler_handler.py @@ -0,0 +1,75 @@ +import wmill, base64, httpx, json, mysql.connector + +def _send_talk(message: str): + try: + nc_url = wmill.get_variable("f/Backup/nextcloud_talk_url").rstrip("/") + nc_room = wmill.get_variable("f/Backup/nextcloud_talk_room") + nc_user = wmill.get_variable("f/Backup/nextcloud_talk_user") + nc_password = wmill.get_variable("f/Backup/nextcloud_talk_password") + credentials = base64.b64encode(f"{nc_user}:{nc_password}".encode()).decode() + httpx.post( + f"{nc_url}/ocs/v2.php/apps/spreed/api/v1/chat/{nc_room}", + headers={ + "Authorization": f"Basic {credentials}", + "OCS-APIREQUEST": "true", + "Content-Type": "application/json", + "Accept": "application/json", + }, + json={"message": message}, + timeout=15, + verify=False, + ) + except Exception as e: + print(f"Talk-Fehler (nicht kritisch): {e}") + +def main(error: dict, flow_input: dict = {}): + msg = error.get("message", "Unbekannter Fehler") + name = error.get("name", "") + step_id = error.get("step_id", "") + + # Laufenden Job in DB als failed markieren und Server freigeben + try: + db_cfg = json.loads(wmill.get_variable("f/Backup/mysql_config")) + conn = mysql.connector.connect(**db_cfg) + cur = conn.cursor(dictionary=True) + + cur.execute(""" + SELECT job_uuid FROM Kunden.`bronze.restore.jobs` + WHERE status = 'running' + LIMIT 1 + """) + row = cur.fetchone() + if row: + job_uuid = row["job_uuid"] + cur.execute(""" + UPDATE Kunden.`bronze.restore.jobs` + SET status = 'failed', finished_at = NOW() + WHERE job_uuid = %s + """, (job_uuid,)) + cur.execute(""" + UPDATE Kunden.`bronze.restore.server` + SET current_job_uuid = NULL + WHERE current_job_uuid = %s + """, (job_uuid,)) + cur.execute(""" + DELETE FROM Kunden.`bronze.restore.session` + WHERE job_uuid = %s + """, (job_uuid,)) + conn.commit() + print(f"DB: Job {job_uuid} als failed markiert, Server freigegeben.") + + cur.close(); conn.close() + except Exception as e: + print(f"DB-Cleanup fehlgeschlagen (nicht kritisch): {e}") + + step_line = f"\nStep: `{step_id}`" if step_id else "" + type_line = f"\nTyp: {name}" if name else "" + talk_msg = ( + f"🚨 **Backup Restore Flow Fehler**{step_line}\n" + f"Fehler: {msg[:300]}" + f"{type_line}" + ) + + _send_talk(talk_msg) + print(f"Talk-Nachricht gesendet:\n{talk_msg}") + return {"notified": True}