(feature) Add run completion notifications

Add email and webhook notification targets with delivery tracking, and send
notifications when backup runs reach a terminal status.

Expose notification target management in the Django UI and keep delivery
failures recorded without failing the backup worker.
This commit is contained in:
2026-05-28 21:20:38 +02:00
parent 1f5c4e0756
commit 67ffd6101b
14 changed files with 819 additions and 4 deletions

View File

@@ -17,6 +17,7 @@ from pobsync.commands.run_scheduled import (
)
from pobsync_backend.config_source import DjangoConfigSource
from pobsync_backend.models import BackupRun, HostConfig
from pobsync_backend.notifications import notify_backup_run_completed
from pobsync_backend.retention import run_sql_retention_apply
from pobsync_backend.snapshot_discovery import infer_snapshot_kind, upsert_snapshot_record
@@ -85,6 +86,7 @@ def execute_backup_run(
"type": type(exc).__name__,
}
run.save(update_fields=["status", "ended_at", "result"])
notify_backup_run_completed(run)
raise
run.refresh_from_db()
@@ -151,6 +153,7 @@ def execute_backup_run(
"result",
],
)
notify_backup_run_completed(run)
return run
@@ -277,6 +280,7 @@ def _reconcile_running_run(*, run: BackupRun, grace_seconds: int, stale_worker_s
run.rsync_exit_code = exit_code or 255
run.result = result
run.save(update_fields=["status", "ended_at", "rsync_exit_code", "result"])
notify_backup_run_completed(run)
return True
if _running_rsync_process_missing(run=run, grace_seconds=grace_seconds):
result.update(
@@ -301,6 +305,7 @@ def _reconcile_running_run(*, run: BackupRun, grace_seconds: int, stale_worker_s
run.rsync_exit_code = exit_code or 255
run.result = result
run.save(update_fields=["status", "ended_at", "rsync_exit_code", "result"])
notify_backup_run_completed(run)
return True
if stale_worker:
result.update(
@@ -318,6 +323,7 @@ def _reconcile_running_run(*, run: BackupRun, grace_seconds: int, stale_worker_s
run.ended_at = timezone.now()
run.result = result
run.save(update_fields=["status", "ended_at", "result"])
notify_backup_run_completed(run)
return True
return False
@@ -353,6 +359,7 @@ def _reconcile_running_run(*, run: BackupRun, grace_seconds: int, stale_worker_s
run.rsync_exit_code = exit_code
run.result = result
run.save(update_fields=["status", "ended_at", "rsync_exit_code", "result"])
notify_backup_run_completed(run)
return True