"""Diff enabled routes in the DB against Traefik dynamic-config files on disk and repair drift. Structural echo of furtka/furtka/reconciler.py's "declared state -> drive external state, self-heal on drift" shape. publish_route()/unpublish_route() already write/remove the matching file synchronously, so this isn't on the hot path for normal operation — it's the safety net for anything that made the DB and the filesystem disagree (a crash mid-write, a file deleted by hand, etc). Not yet wired into app.py's server loop; run it from a systemd timer or an ad hoc `python -m control_plane.reconciler` invocation for now. """ from __future__ import annotations from control_plane import traefikconf from control_plane.db import Database def reconcile(db: Database, cert_resolver: str | None) -> None: wanted = db.query_all( "SELECT r.id, r.subdomain, r.target_port, b.wg_allowed_ip " "FROM routes r JOIN boxes b ON b.id = r.box_id " "WHERE r.enabled = 1" ) wanted_ids = set() for row in wanted: wanted_ids.add(row["id"]) target_ip = row["wg_allowed_ip"].split("/")[0] traefikconf.write_route(row["id"], row["subdomain"], target_ip, row["target_port"], cert_resolver) for orphan_id in traefikconf.existing_route_ids() - wanted_ids: traefikconf.remove_route(orphan_id)