fix htmx refactor route mismatches and remaining TODO items

- wireguard: POST /peers with JSON encoding (was /add-peer)
- rules: delete by rule_id in URL path (was JSON body); pass rule objects with id from server; add hx-disable to initial render
- nat: port forward delete uses URL path params to match blueprint
- nat: masquerade toggle uses native hx-post/hx-vals (was inline fetch)
- app.js renderers updated to use URL path deletes for rules and forwards
- remove TODO.md
This commit is contained in:
2026-05-17 01:15:52 +00:00
parent 0e7090a2cb
commit 37039351be
26 changed files with 1737 additions and 848 deletions
+30 -3
View File
@@ -6,6 +6,7 @@ static leases, and custom DNS records through sudo.
"""
import json
import logging
import os
import subprocess
from copy import deepcopy
@@ -15,6 +16,8 @@ from typing import Any
from jinja2 import Environment, FileSystemLoader
logger = logging.getLogger(__name__)
PROJECT_DIR = Path(__file__).resolve().parent.parent
CONFIG_DIR = PROJECT_DIR / "config" / "dnsmasq"
DATA_DIR = PROJECT_DIR / "data" / "dnsmasq"
@@ -101,6 +104,7 @@ def save_config(cfg: dict) -> None:
_ensure_dirs()
merged = _deep_merge(deepcopy(DEFAULT_CFG), cfg)
_save_json(CONFIG_PATH, merged)
logger.info("dnsmasq config saved")
def apply_config() -> None:
@@ -118,6 +122,7 @@ def apply_config() -> None:
check=True,
)
_sudo("systemctl", "reload", "dnsmasq")
logger.info("dnsmasq config written and reloaded")
# ───────── config generation ─────────────────────────────────────────
@@ -187,6 +192,23 @@ def set_dhcp_range(
ranges.append(entry)
save_config(cfg)
logger.info("DHCP range set for interface '%s': %s-%s", iface, start, end)
def remove_dhcp_range(iface: str, start: str, end: str) -> None:
"""Remove a DHCP range by interface + IP range."""
cfg = get_config()
cfg["dhcp"]["ranges"] = [
r
for r in cfg["dhcp"]["ranges"]
if not (
r.get("interface") == iface
and r.get("start") == start
and r.get("end") == end
)
]
save_config(cfg)
logger.info("DHCP range removed for interface '%s': %s-%s", iface, start, end)
def add_static_lease(mac: str, ip: str, hostname: str | None = None) -> None:
@@ -200,6 +222,7 @@ def add_static_lease(mac: str, ip: str, hostname: str | None = None) -> None:
if hostname:
leases[i]["hostname"] = hostname
save_config(cfg)
logger.info("Static DHCP lease updated: %s -> %s", mac, ip)
return
entry: dict[str, Any] = {"mac": mac, "ip": ip}
@@ -207,6 +230,7 @@ def add_static_lease(mac: str, ip: str, hostname: str | None = None) -> None:
entry["hostname"] = hostname
leases.append(entry)
save_config(cfg)
logger.info("Static DHCP lease added: %s -> %s", mac, ip)
def remove_static_lease(mac: str) -> None:
@@ -218,6 +242,7 @@ def remove_static_lease(mac: str) -> None:
if lease["mac"].lower() != mac.lower()
]
save_config(cfg)
logger.info("Static DHCP lease removed for MAC %s", mac)
# ───────── dns record management ─────────────────────────────────────
@@ -234,6 +259,7 @@ def add_dns_record(name: str, address: str, hostname: str | None = None) -> None
if hostname:
records[i]["hostname"] = hostname
save_config(cfg)
logger.info("DNS record updated: %s -> %s", name, address)
return
entry: dict[str, Any] = {"name": name, "address": address}
@@ -241,6 +267,7 @@ def add_dns_record(name: str, address: str, hostname: str | None = None) -> None
entry["hostname"] = hostname
records.append(entry)
save_config(cfg)
logger.info("DNS record added: %s -> %s", name, address)
def remove_dns_record(name: str) -> None:
@@ -250,6 +277,7 @@ def remove_dns_record(name: str) -> None:
r for r in cfg["dns"]["custom_records"] if r["name"] != name
]
save_config(cfg)
logger.info("DNS record removed: %s", name)
# ───────── lease table ───────────────────────────────────────────────
@@ -299,6 +327,7 @@ def set_upstreams(servers: list[str]) -> None:
cfg = get_config()
cfg["dns"]["upstreams"] = list(servers)
save_config(cfg)
logger.info("DNS upstreams set to %s", servers)
def set_domain(domain: str | None) -> None:
@@ -306,6 +335,7 @@ def set_domain(domain: str | None) -> None:
cfg = get_config()
cfg["dns"]["domain"] = domain if domain else None
save_config(cfg)
logger.info("DNS domain set to '%s'", domain)
# ───────── status / info ─────────────────────────────────────────────
@@ -315,7 +345,6 @@ def get_status() -> dict:
"""Return service status, config summary, and current lease count."""
cfg = get_config()
# dnsmasq process check
try:
proc = subprocess.run(
["sudo", "systemctl", "is-active", "dnsmasq"],
@@ -326,7 +355,6 @@ def get_status() -> dict:
except Exception:
active = False
# config on disk
conf_exists = os.path.isfile(DNSMASQ_CONF)
if conf_exists:
try:
@@ -337,7 +365,6 @@ def get_status() -> dict:
else:
conf_on_disk = ""
# current expected config
expected = generate_conf(cfg)
leases = get_lease_table()