Configurable startup delay before the first reconciliation pass so that
multiple receivers behind a load balancer can be offset without relying
on container start order (which is lost on reboot). Set to half the
interval on the secondary receiver — e.g. interval 60m → delay 30m.
Default is 0 (no change to existing behaviour). Stop event is respected
during the delay so the worker shuts down cleanly even mid-wait.
- worker.py: third persistent retry queue with exponential backoff (30s→30m,
max 5 attempts); failed backends tracked per-item so retries target only the
failing nodes; zone_data stored in DB after every successful write
- Domain model: zone_data TEXT + zone_updated_at DATETIME columns; additive
migration applied on startup so existing deployments upgrade in place
- ReconciliationWorker: Option C healing pass — checks every configured backend
for zone presence after each reconciliation cycle and re-queues any zone
missing from a backend using stored zone_data, enabling automatic recovery
from prolonged backend outages without waiting for DirectAdmin to re-push
- 82 tests, all passing
Move all outbound DirectAdmin HTTP logic out of ReconciliationWorker and
into a dedicated, independently testable DirectAdminClient class:
- directdnsonly/app/da/client.py: list_domains (paginated JSON + legacy
fallback), get (authenticated GET to any CMD_* endpoint), _login
(DA Evo session-cookie fallback), _parse_legacy_domain_list
- directdnsonly/app/da/__init__.py: public re-export of DirectAdminClient
- reconciler.py: now purely reconciliation logic; instantiates a client
per configured server — no HTTP code remaining
- tests/test_da_client.py: 16 dedicated tests for DirectAdminClient
- tests/test_reconciler.py: mocks at the DirectAdminClient class boundary
instead of the internal _fetch_da_domains method
Bumped to 2.2.0 — DirectAdminClient is now a first-class public API.