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 <noreply@anthropic.com>
This commit is contained in:
Sebastian Serfling
2026-04-29 21:28:04 +02:00
parent 0c9a1d7791
commit 2d32574d68
3 changed files with 107 additions and 0 deletions
@@ -125,6 +125,21 @@ value:
lock: '!inline lock: '!inline
webhook_verarbeiten_&_naechsten_restore_auf_demselben_server_starten.lock' webhook_verarbeiten_&_naechsten_restore_auf_demselben_server_starten.lock'
language: python3 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:
$schema: https://json-schema.org/draft/2020-12/schema $schema: https://json-schema.org/draft/2020-12/schema
type: object type: object
@@ -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
@@ -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}