refactor: daemon collectors, thin webui proxies, pure config reads
- move state collectors from lib/state.py to daemon/collectors/ (7 modules, registration side-effect; daemon/server.py imports the package before the first populate()) - webui/api: new daemon_route() decorator factory in common.py collapses the try/except daemon-proxy boilerplate in all 8 blueprints (rules/params/body/transform keep responses identical) - firewall: interface-coverage invariant — config is the source of truth for zone interfaces (absent key = empty, no hands-off zones); pure validate_coverage() enforced at save (400) and apply (409, force: true overrides), top-level `unmanaged` exemption - lib: get_config() reads are now pure (no dir creation or writes); new lib/bootstrap.py creates runtime dirs and persists the one-shot nginx legacy migration at daemon start, after system_import (lib.nginx.migrate_config_file) - lib/common: compute_pending() apply-bookkeeping helper - daemon: emit_and_refresh() handler helper; refresh_state(bump=) so /status/refresh no longer bumps versions (poll/mutation only) - acme: move --log last so acme.sh never treats a real arg as the log-file argument - docs: AGENTS.md, config.md, state-model.md, api.md updated; HARDEN.md dropped (plan implemented); apply-confirm force wording Tests: 917 passed; ruff check + format clean.
This commit is contained in:
+16
-75
@@ -8,6 +8,7 @@ from typing import Any
|
||||
|
||||
from jinja2 import Environment, FileSystemLoader
|
||||
|
||||
from daemon.handlers.common import emit_and_refresh
|
||||
from daemon.iface import (
|
||||
DELETE_DNSMASQ_DNS_RECORD_REMOVE,
|
||||
DELETE_DNSMASQ_RANGES_REMOVE,
|
||||
@@ -24,7 +25,7 @@ from daemon.iface import (
|
||||
POST_DNSMASQ_STATIC_LEASE_ADD,
|
||||
POST_DNSMASQ_UPSTREAMS,
|
||||
)
|
||||
from daemon.server import NotFoundError, refresh_state, registry
|
||||
from daemon.server import NotFoundError, registry
|
||||
from lib.common import (
|
||||
deep_merge,
|
||||
ensure_dirs,
|
||||
@@ -35,7 +36,6 @@ from lib.common import (
|
||||
stamp_applied,
|
||||
strip_apply_meta,
|
||||
)
|
||||
from lib.sync import SyncEvent, bus
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -152,10 +152,7 @@ def save_config_handler(_request: Any, body: dict[str, Any] | None) -> dict[str,
|
||||
if not body:
|
||||
raise ValueError("Request body required")
|
||||
_save_config(body)
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("dnsmasq", "config_saved", {"action": "config_saved"})
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
emit_and_refresh("dnsmasq", {"action": "config_saved"})
|
||||
return {"config_saved": True}
|
||||
|
||||
|
||||
@@ -171,10 +168,7 @@ def patch_config(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]:
|
||||
current = _get_config()
|
||||
merged = deep_merge(current, body)
|
||||
_save_config(merged)
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("dnsmasq", "config_saved", {"action": "config_patched"})
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
emit_and_refresh("dnsmasq", {"action": "config_patched"})
|
||||
return {"config_saved": True}
|
||||
|
||||
|
||||
@@ -201,11 +195,8 @@ def apply_config(_request: Any, _body: Any) -> dict[str, Any]:
|
||||
cfg_after = _get_config()
|
||||
stamp_applied(cfg_after)
|
||||
_save_config(cfg_after)
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("dnsmasq", "config_saved", {"action": "config_applied"})
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
return {"applied": True, "synced": sync_result.affected_subsystems}
|
||||
synced = emit_and_refresh("dnsmasq", {"action": "config_applied"})
|
||||
return {"applied": True, "synced": synced}
|
||||
|
||||
|
||||
@registry.register(GET_DNSMASQ_STATUS)
|
||||
@@ -281,12 +272,7 @@ def set_dhcp_range(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]
|
||||
entry["dns"] = body["dns"]
|
||||
ranges.append(entry)
|
||||
_save_config(cfg)
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq", "config_saved", {"action": "range_added", "interface": iface}
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
emit_and_refresh("dnsmasq", {"action": "range_added", "interface": iface})
|
||||
return {"interface": iface, "start": start, "end": end}
|
||||
|
||||
|
||||
@@ -321,12 +307,7 @@ def remove_dhcp_range(_request: Any, body: dict[str, Any] | None) -> dict[str, A
|
||||
f"DHCP range for interface '{iface}' ({start}-{end}) not found"
|
||||
)
|
||||
_save_config(cfg)
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq", "config_saved", {"action": "range_removed", "interface": iface}
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
emit_and_refresh("dnsmasq", {"action": "range_removed", "interface": iface})
|
||||
return {"interface": iface, "start": start, "end": end}
|
||||
|
||||
|
||||
@@ -365,26 +346,14 @@ def add_static_lease(_request: Any, body: dict[str, Any] | None) -> dict[str, An
|
||||
if hostname is not None:
|
||||
leases[i]["hostname"] = hostname
|
||||
_save_config(cfg)
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq",
|
||||
"config_saved",
|
||||
{"action": "static_lease_added", "mac": mac},
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
emit_and_refresh("dnsmasq", {"action": "static_lease_added", "mac": mac})
|
||||
return {"mac": mac, "ip": ip, "hostname": hostname}
|
||||
entry: dict[str, Any] = {"mac": mac, "ip": ip}
|
||||
if hostname:
|
||||
entry["hostname"] = hostname
|
||||
leases.append(entry)
|
||||
_save_config(cfg)
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq", "config_saved", {"action": "static_lease_added", "mac": mac}
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
emit_and_refresh("dnsmasq", {"action": "static_lease_added", "mac": mac})
|
||||
return {"mac": mac, "ip": ip, "hostname": hostname}
|
||||
|
||||
|
||||
@@ -409,12 +378,7 @@ def remove_static_lease(_request: Any, body: dict[str, Any] | None) -> dict[str,
|
||||
if len(cfg["dhcp"]["static_leases"]) == before:
|
||||
raise NotFoundError(f"Static lease for MAC '{mac}' not found")
|
||||
_save_config(cfg)
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq", "config_saved", {"action": "static_lease_removed", "mac": mac}
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
emit_and_refresh("dnsmasq", {"action": "static_lease_removed", "mac": mac})
|
||||
return {"mac": mac}
|
||||
|
||||
|
||||
@@ -440,26 +404,14 @@ def add_dns_record(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]
|
||||
if hostname is not None:
|
||||
records[i]["hostname"] = hostname
|
||||
_save_config(cfg)
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq",
|
||||
"config_saved",
|
||||
{"action": "dns_record_added", "name": name},
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
emit_and_refresh("dnsmasq", {"action": "dns_record_added", "name": name})
|
||||
return {"name": name, "address": address, "hostname": hostname}
|
||||
entry: dict[str, Any] = {"name": name, "address": address}
|
||||
if hostname:
|
||||
entry["hostname"] = hostname
|
||||
records.append(entry)
|
||||
_save_config(cfg)
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq", "config_saved", {"action": "dns_record_added", "name": name}
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
emit_and_refresh("dnsmasq", {"action": "dns_record_added", "name": name})
|
||||
return {"name": name, "address": address, "hostname": hostname}
|
||||
|
||||
|
||||
@@ -482,12 +434,7 @@ def remove_dns_record(_request: Any, body: dict[str, Any] | None) -> dict[str, A
|
||||
if len(cfg["dns"]["custom_records"]) == before:
|
||||
raise NotFoundError(f"DNS record '{name}' not found")
|
||||
_save_config(cfg)
|
||||
sync_result = bus.emit(
|
||||
SyncEvent(
|
||||
"dnsmasq", "config_saved", {"action": "dns_record_removed", "name": name}
|
||||
)
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
emit_and_refresh("dnsmasq", {"action": "dns_record_removed", "name": name})
|
||||
return {"name": name}
|
||||
|
||||
|
||||
@@ -503,10 +450,7 @@ def set_upstreams(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]:
|
||||
cfg = _get_config()
|
||||
cfg["dns"]["upstreams"] = list(body["servers"])
|
||||
_save_config(cfg)
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("dnsmasq", "config_saved", {"action": "upstreams_set"})
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
emit_and_refresh("dnsmasq", {"action": "upstreams_set"})
|
||||
return {"upstreams": cfg["dns"]["upstreams"]}
|
||||
|
||||
|
||||
@@ -523,8 +467,5 @@ def set_domain(_request: Any, body: dict[str, Any] | None) -> dict[str, Any]:
|
||||
cfg = _get_config()
|
||||
cfg["dns"]["domain"] = domain if domain else None
|
||||
_save_config(cfg)
|
||||
sync_result = bus.emit(
|
||||
SyncEvent("dnsmasq", "config_saved", {"action": "domain_set"})
|
||||
)
|
||||
refresh_state(["dnsmasq", *sync_result.affected_subsystems])
|
||||
emit_and_refresh("dnsmasq", {"action": "domain_set"})
|
||||
return {"domain": cfg["dns"]["domain"]}
|
||||
|
||||
Reference in New Issue
Block a user