Fix dashboard template bugs, acme date parsing, wireguard sudoers match, and stale docs
- dashboard.html: Fix zones, leases, wg_status, cert key names, add services var
- server.py: Pass services to dashboard template via _get_service_status()
- lib/acme.py: Fix dead third date format (%Y%m%d%H%M%z) using astimezone(UTC)
- lib/wireguard.py: Add -- separator to cp command to match sudoers rule
- lib/nginx.py: Replace shallow dict.copy() with {**...} for DEFAULT_SSL
- AGENTS.md: Update test count 149 -> 154
- docs/api.md: Rename cert field expiry -> expires_at
This commit is contained in:
+35
-7
@@ -491,13 +491,32 @@ def remove_forward_port(
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _parse_forward_ports(value: str) -> list[str]:
|
||||
"""Parse the 'forward-ports' line into individual forward-port specifiers.
|
||||
def _parse_forward_port(raw: str) -> dict[str, Any]:
|
||||
"""Parse a single forward-port specifier into a structured dict.
|
||||
|
||||
Multiple entries are space-separated; each looks like
|
||||
``port=443/proto=tcp/toaddr=192.168.1.5/toport=8080``.
|
||||
Input: ``port=443/proto=tcp/toaddr=192.168.1.5/toport=8080``
|
||||
"""
|
||||
return value.split() if value else []
|
||||
result: dict[str, Any] = {}
|
||||
for piece in raw.split("/"):
|
||||
if "=" not in piece:
|
||||
continue
|
||||
key, _, val = piece.partition("=")
|
||||
if key == "port":
|
||||
result["port"] = int(val)
|
||||
elif key == "proto":
|
||||
result["proto"] = val
|
||||
elif key == "toaddr":
|
||||
result["toaddr"] = val
|
||||
elif key == "toport":
|
||||
result["toport"] = int(val)
|
||||
return result
|
||||
|
||||
|
||||
def _parse_forward_ports(value: str) -> list[dict[str, Any]]:
|
||||
"""Parse the 'forward-ports' line into a list of structured dicts."""
|
||||
if not value:
|
||||
return []
|
||||
return [_parse_forward_port(raw) for raw in value.split()]
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -594,14 +613,23 @@ def restore_backup(state: dict[str, Any]) -> None:
|
||||
if zinfo.get("masquerade"):
|
||||
set_masquerade(zone_name, True)
|
||||
|
||||
# Forward ports (stored as raw strings in zinfo)
|
||||
# Forward ports (stored as dicts, or raw strings from old backups)
|
||||
for fp in zinfo.get("forward-ports", []):
|
||||
if isinstance(fp, str):
|
||||
fp_str = fp
|
||||
else:
|
||||
parts = [f"port={fp['port']}", f"proto={fp['proto']}"]
|
||||
if "toaddr" in fp:
|
||||
parts.append(f"toaddr={fp['toaddr']}")
|
||||
if "toport" in fp:
|
||||
parts.append(f"toport={fp['toport']}")
|
||||
fp_str = "/".join(parts)
|
||||
_run(
|
||||
[
|
||||
"sudo",
|
||||
"firewall-cmd",
|
||||
f"--zone={zone_name}",
|
||||
f"--add-forward-port={fp}",
|
||||
f"--add-forward-port={fp_str}",
|
||||
"--permanent",
|
||||
],
|
||||
check=False,
|
||||
|
||||
Reference in New Issue
Block a user