sync healthcheck feature

This commit is contained in:
2026-08-07 20:56:12 +02:00
parent 838619e392
commit 94fcd5f685
3 changed files with 23 additions and 4 deletions
+4 -2
View File
@@ -10,14 +10,16 @@
{"label": "LinkedIn", "url": "https://www.linkedin.com/search/results/all/?keywords=[fullname]"}, {"label": "LinkedIn", "url": "https://www.linkedin.com/search/results/all/?keywords=[fullname]"},
{"label": "Google Maps Home", "url": "https://www.google.com/maps?q=[homecity]"}, {"label": "Google Maps Home", "url": "https://www.google.com/maps?q=[homecity]"},
{"label": "Google Maps Work", "url": "https://www.google.com/maps?q=[workcity]"} {"label": "Google Maps Work", "url": "https://www.google.com/maps?q=[workcity]"}
] ],
"healthcheck_url": "https://healthchecks.example.de/ping/abc123"
}, },
{ {
"name": "partner", "name": "partner",
"apple_email": "partner@icloud.com", "apple_email": "partner@icloud.com",
"apple_app_password": "yyyy-yyyy-yyyy-yyyy", "apple_app_password": "yyyy-yyyy-yyyy-yyyy",
"authelia_user": "partner.user", "authelia_user": "partner.user",
"custom_links": [] "custom_links": [],
"healthcheck_url": ""
} }
], ],
"admins": [ "admins": [
+4 -2
View File
@@ -13,12 +13,13 @@ ICLOUD_BASE_URL = "https://contacts.icloud.com/"
class Account: class Account:
def __init__(self, name: str, apple_email: str, apple_app_password: str, authelia_user: str | None, def __init__(self, name: str, apple_email: str, apple_app_password: str, authelia_user: str | None,
custom_links: list[dict] | None = None): custom_links: list[dict] | None = None, healthcheck_url: str = ""):
self.name = name self.name = name
self.apple_email = apple_email self.apple_email = apple_email
self.apple_app_password = apple_app_password self.apple_app_password = apple_app_password
self.authelia_user = authelia_user self.authelia_user = authelia_user
self.custom_links = custom_links or [] self.custom_links = custom_links or []
self.healthcheck_url = healthcheck_url
class Config: class Config:
@@ -101,7 +102,8 @@ class Config:
raise RuntimeError(f"Account-Name '{name}' ist nicht eindeutig") raise RuntimeError(f"Account-Name '{name}' ist nicht eindeutig")
seen_names.add(name) seen_names.add(name)
custom_links = entry.get("custom_links", []) custom_links = entry.get("custom_links", [])
accounts.append(Account(name, email, pwd, authelia_user, custom_links)) healthcheck_url = entry.get("healthcheck_url", "")
accounts.append(Account(name, email, pwd, authelia_user, custom_links, healthcheck_url))
return accounts return accounts
@classmethod @classmethod
+15
View File
@@ -9,6 +9,8 @@ vollständiger Re-Sync."""
import logging import logging
import sys import sys
import requests
import db import db
from carddav_client import ICLOUD_BASE_URL, CardDAVClient, SyncTokenInvalid from carddav_client import ICLOUD_BASE_URL, CardDAVClient, SyncTokenInvalid
from config import Config from config import Config
@@ -18,6 +20,16 @@ logging.basicConfig(level=Config.LOG_LEVEL, format="%(asctime)s [%(levelname)s]
logger = logging.getLogger("sync") logger = logging.getLogger("sync")
def _call_healthcheck(account):
if not account.healthcheck_url:
return
try:
resp = requests.get(account.healthcheck_url, timeout=10)
logger.info("[%s] Healthcheck OK (%d)", account.name, resp.status_code)
except Exception as exc:
logger.warning("[%s] Healthcheck fehlgeschlagen: %s", account.name, exc)
def _classify_vcards(raw_vcards, raw_etags, account_name): def _classify_vcards(raw_vcards, raw_etags, account_name):
contacts, groups = [], [] contacts, groups = [], []
for v, etag in zip(raw_vcards, raw_etags): for v, etag in zip(raw_vcards, raw_etags):
@@ -56,6 +68,7 @@ def sync_account(conn, account, href_to_uid_cache: dict):
db.save_sync_token(conn, account.name, new_token) db.save_sync_token(conn, account.name, new_token)
db.finish_sync_run(conn, run_id, "success", upserted=len(contacts), deleted=0) db.finish_sync_run(conn, run_id, "success", upserted=len(contacts), deleted=0)
logger.info("[%s] Initialer Sync abgeschlossen: %d Kontakte, %d Gruppen", account.name, len(contacts), len(groups)) logger.info("[%s] Initialer Sync abgeschlossen: %d Kontakte, %d Gruppen", account.name, len(contacts), len(groups))
_call_healthcheck(account)
return return
try: try:
@@ -72,6 +85,7 @@ def sync_account(conn, account, href_to_uid_cache: dict):
db.save_sync_token(conn, account.name, new_token) db.save_sync_token(conn, account.name, new_token)
db.finish_sync_run(conn, run_id, "success", upserted=len(contacts), deleted=0) db.finish_sync_run(conn, run_id, "success", upserted=len(contacts), deleted=0)
logger.info("[%s] Re-Sync abgeschlossen: %d Kontakte, %d Gruppen", account.name, len(contacts), len(groups)) logger.info("[%s] Re-Sync abgeschlossen: %d Kontakte, %d Gruppen", account.name, len(contacts), len(groups))
_call_healthcheck(account)
return return
contacts, groups = _classify_vcards(changed_vcards, etags, account.name) contacts, groups = _classify_vcards(changed_vcards, etags, account.name)
@@ -97,6 +111,7 @@ def sync_account(conn, account, href_to_uid_cache: dict):
"[%s] Delta-Sync abgeschlossen: %d geändert/neu, %d gelöscht (%d Gruppen geändert)", "[%s] Delta-Sync abgeschlossen: %d geändert/neu, %d gelöscht (%d Gruppen geändert)",
account.name, len(contacts), len(deleted_uids), len(groups), account.name, len(contacts), len(deleted_uids), len(groups),
) )
_call_healthcheck(account)
except Exception as exc: except Exception as exc:
logger.exception("[%s] Sync-Lauf %s fehlgeschlagen", account.name, run_id) logger.exception("[%s] Sync-Lauf %s fehlgeschlagen", account.name, run_id)
print(f"SYNC-FEHLER [{account.name}]: {exc}", file=sys.stderr, flush=True) print(f"SYNC-FEHLER [{account.name}]: {exc}", file=sys.stderr, flush=True)