Add update-vendor.sh symlink support, unify install.sh vendor flow

- update-vendor.sh now creates webui/vendor symlinks (htm.js)
- install.sh calls update-vendor.sh after package install
- Add vendor/.empty and webui/vendor/.empty as directory placeholders in git
This commit is contained in:
2026-07-01 00:44:08 +00:00
parent 575cf06a4b
commit 8c13ad55ce
32 changed files with 1371 additions and 445 deletions
+45 -19
View File
@@ -5,8 +5,6 @@ state instead of invoking subprocesses on every request.
"""
import contextlib
import hashlib
import json
import logging
import os
from copy import deepcopy
@@ -14,7 +12,7 @@ from datetime import UTC, datetime
from pathlib import Path
from typing import Any, ClassVar
from lib.common import load_json, run, run_proc
from lib.common import _APPLY_HASH_KEY, config_hash, load_json, run, run_proc
from lib.firewall import (
_parse_active_zones,
_parse_all_zones_output,
@@ -590,18 +588,13 @@ def _collect_dnsmasq() -> dict[str, Any]:
# Check config file on disk
conf_exists = Path(DNSMASQ_CONF).is_file()
# Check if JSON config has changed since last apply
_APPLY_HASH_KEY = "_last_applied_hash"
pending_changes = True
if _APPLY_HASH_KEY in cfg:
clean = {k: v for k, v in cfg.items() if k != _APPLY_HASH_KEY}
current_hash = hashlib.sha256(
json.dumps(clean, sort_keys=True).encode()
).hexdigest()
pending_changes = cfg[_APPLY_HASH_KEY] != current_hash
pending_changes = _APPLY_HASH_KEY not in cfg or cfg[_APPLY_HASH_KEY] != config_hash(
cfg
)
safe_cfg = {k: v for k, v in cfg.items() if k != _APPLY_HASH_KEY}
return {
"config": cfg,
"config": safe_cfg,
"status": {
"service_active": service_active,
"config_file_exists": conf_exists,
@@ -684,9 +677,15 @@ def _collect_nginx() -> dict[str, Any]:
entry["is_websocket"] = True
domains.append(entry)
pending_changes = _APPLY_HASH_KEY not in cfg or cfg[_APPLY_HASH_KEY] != config_hash(
cfg
)
safe_cfg = {k: v for k, v in cfg.items() if k != _APPLY_HASH_KEY}
return {
"config": cfg,
"config": safe_cfg,
"domains": domains,
"status": {"pending_changes": pending_changes},
"timestamp": _now_iso(),
}
@@ -918,8 +917,12 @@ def _collect_wireguard() -> dict[str, Any]:
except Exception:
pass
# Safe config (strip private key)
safe = dict(cfg)
pending_changes = _APPLY_HASH_KEY not in cfg or cfg[_APPLY_HASH_KEY] != config_hash(
cfg
)
# Safe config (strip private key and internal hash)
safe = {k: v for k, v in cfg.items() if k != _APPLY_HASH_KEY}
if "interface" in safe:
safe["interface"] = dict(safe["interface"])
safe["interface"].pop("private_key", None)
@@ -1000,6 +1003,7 @@ def _collect_wireguard() -> dict[str, Any]:
except Exception:
pass
status["pending_changes"] = pending_changes
return {
"config": safe,
"status": status,
@@ -1029,24 +1033,46 @@ def _collect_networkd() -> dict[str, Any]:
"""Collect networkd interface state from networkctl.
Returns:
Dict with interface runtime state parsed from networkctl output.
Returns empty data if networkctl is not available.
Dict with interface runtime state parsed from networkctl output,
config, and pending changes status.
"""
CONFIG_PATH = PROJECT_DIR / "config" / "network" / "config.json"
# Load config
net_cfg: dict[str, Any] = {}
if CONFIG_PATH.exists():
with contextlib.suppress(Exception):
net_cfg = load_json(CONFIG_PATH)
pending_changes = _APPLY_HASH_KEY not in net_cfg or net_cfg[
_APPLY_HASH_KEY
] != config_hash(net_cfg)
result: dict[str, dict[str, Any]] = {}
safe_net_cfg = {k: v for k, v in net_cfg.items() if k != _APPLY_HASH_KEY}
try:
raw = run(["networkctl", "status", "--json=short", "--all"], sudo=True)
result = parse_networkctl_status(raw)
if not result:
return {"interfaces": {}, "timestamp": _now_iso()}
return {
"interfaces": {},
"config": safe_net_cfg,
"status": {"pending_changes": pending_changes},
"timestamp": _now_iso(),
}
except Exception:
return {
"interfaces": {},
"config": safe_net_cfg,
"status": {"pending_changes": pending_changes},
"timestamp": _now_iso(),
}
return {
"interfaces": result,
"config": safe_net_cfg,
"status": {"pending_changes": pending_changes},
"timestamp": _now_iso(),
}