feat: auto-generate DNS records on first API startup

- NetworkManager.bootstrap_dns_records(): creates A records for all
  cell services (api, webui, calendar, files, mail, webmail, webdav,
  <cell_name>) using their static container IPs — only runs when the
  zone file doesn't exist yet (idempotent)
- API startup: _bootstrap_dns() thread reads cell_name/domain from
  config_manager and calls bootstrap — runs alongside enforcement thread
- Fix: add_dns_record(data) and remove_dns_record(data) now correctly
  unpack dict kwargs instead of passing dict as positional arg
- Fix: remove duplicate cell{} block in config/dns/Corefile

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-22 10:00:56 -04:00
parent 16af657376
commit 8e741b5729
2 changed files with 32 additions and 2 deletions
+12 -2
View File
@@ -197,10 +197,20 @@ def _apply_startup_enforcement():
except Exception as e:
logger.warning(f"Startup enforcement failed (non-fatal): {e}")
def _bootstrap_dns():
try:
identity = config_manager.configs.get('_identity', {})
cell_name = identity.get('cell_name', os.environ.get('CELL_NAME', 'mycell'))
domain = identity.get('domain', os.environ.get('CELL_DOMAIN', 'cell'))
network_manager.bootstrap_dns_records(cell_name, domain)
except Exception as e:
logger.warning(f"DNS bootstrap failed (non-fatal): {e}")
COREFILE_PATH = '/app/config/dns/Corefile'
# Run in background so startup isn't blocked waiting on docker exec
threading.Thread(target=_apply_startup_enforcement, daemon=True).start()
threading.Thread(target=_bootstrap_dns, daemon=True).start()
# Register services with service bus
service_bus.register_service('network', network_manager)
@@ -769,7 +779,7 @@ def add_dns_record():
data = request.get_json(silent=True)
if data is None:
return jsonify({"error": "No data provided"}), 400
result = network_manager.add_dns_record(data)
result = network_manager.add_dns_record(**data)
return jsonify(result)
except Exception as e:
logger.error(f"Error adding DNS record: {e}")
@@ -780,7 +790,7 @@ def remove_dns_record():
"""Remove DNS record."""
try:
data = request.get_json(silent=True)
result = network_manager.remove_dns_record(data)
result = network_manager.remove_dns_record(**data)
return jsonify(result)
except Exception as e:
logger.error(f"Error removing DNS record: {e}")