Files
pve-cloud-backups/backend/app/notifications.py
T
2026-07-15 01:00:08 +02:00

80 lines
2.5 KiB
Python

from __future__ import annotations
import json
from datetime import datetime
from typing import Any
import httpx
from .db import db, utc_now
from .settings_store import get_setting
COLORS = {
"backup_started": 0x3498DB,
"pve_completed": 0x2ECC71,
"upload_started": 0x9B59B6,
"backup_completed": 0x2ECC71,
"backup_failed": 0xE74C3C,
"remote_deleted": 0xF39C12,
"recovery_action": 0x95A5A6,
}
def _field(name: str, value: Any) -> dict:
return {"name": name, "value": str(value if value is not None else "-"), "inline": True}
def _duration_seconds(backup: dict) -> int | None:
started = backup.get("started_at")
ended = backup.get("completed_at") or backup.get("deleted_at") or backup.get("pve_completed_at")
if not started or not ended:
return None
try:
return int((datetime.fromisoformat(ended) - datetime.fromisoformat(started)).total_seconds())
except Exception:
return None
async def notify(event_type: str, *, backup: dict | None = None, message: str = "") -> None:
webhook_url = get_setting("discord_webhook_url", "")
fields = []
backup_id = None
if backup:
backup_id = backup.get("id")
duration = _duration_seconds(backup)
enriched = {**backup, "duration_seconds": duration}
for key in ("guest_name", "guest_vmid", "id", "state", "size_bytes", "duration_seconds", "error_message"):
if enriched.get(key) not in (None, ""):
fields.append(_field(key, enriched.get(key)))
if message:
fields.append(_field("message", message))
payload = {
"embeds": [
{
"title": event_type.replace("_", " ").title(),
"color": COLORS.get(event_type, 0x3498DB),
"timestamp": utc_now(),
"fields": fields,
}
]
}
success = False
error_message = None
if webhook_url:
try:
async with httpx.AsyncClient(timeout=15) as client:
response = await client.post(webhook_url, json=payload)
response.raise_for_status()
success = True
except Exception as exc: # Notifications must never fail backups.
error_message = str(exc)
with db() as conn:
conn.execute(
"""
INSERT INTO notification_logs(event_type, backup_id, payload, success, error_message, created_at)
VALUES (?, ?, ?, ?, ?, ?)
""",
(event_type, backup_id, json.dumps(payload), int(success), error_message, utc_now()),
)