"""Flask web management panel for WinDHCPD.""" from __future__ import annotations import logging import threading from typing import Optional from flask import Flask, jsonify, request, render_template from ..config import Config, StaticBinding from ..lease import Lease, LeaseDB from ..server import DHCPd log = logging.getLogger("dhcpd.web") app = Flask(__name__) # ── global state (set by run.py) ──────────────────────────────────────── _server: Optional[DHCPd] = None _config: Optional[Config] = None _db: Optional[LeaseDB] = None def init(srv: DHCPd, cfg: Config, db: LeaseDB) -> None: global _server, _config, _db _server = srv _config = cfg _db = db def run(host: str, port: int) -> None: log.info("Web panel on http://%s:%d", host, port) app.run(host=host, port=port, threaded=True, debug=False) # ── routes ────────────────────────────────────────────────────────────── def _safe_db() -> LeaseDB: return _db # type: ignore @app.route("/") def index(): cfg = _config # type: ignore db = _safe_db() srv = _server # type: ignore leases = db.list_all() if db else [] scopes = cfg.scopes if cfg else [] return render_template("index.html", scopes=scopes, leases=leases, stats=srv.stats if srv else {}, bindings=cfg.bindings if cfg else [], count_active=db.count_active() if db else 0, count_total=db.count() if db else 0, ) @app.route("/api/leases") def api_leases(): db = _safe_db() leases = db.list_all() if db else [] return jsonify([l.to_json() for l in leases]) @app.route("/api/leases//release", methods=["POST"]) def api_release(mac: str): db = _safe_db() if db: db.release(mac) return jsonify({"ok": True}) @app.route("/api/leases/prune", methods=["POST"]) def api_prune(): db = _safe_db() n = 0 if db: n = db.prune_expired() return jsonify({"pruned": n}) @app.route("/api/scopes") def api_scopes(): cfg = _config # type: ignore out = [] for sc in cfg.scopes: out.append({ "name": sc.name, "network": sc.network, "start_ip": str(sc.start_ip), "end_ip": str(sc.end_ip), "subnet_mask": sc.subnet_mask, "router": sc.router, "dns_servers": sc.dns_servers, "lease_seconds": sc.lease_seconds, "enable": sc.enable, }) return jsonify(out) @app.route("/api/stats") def api_stats(): srv = _server # type: ignore db = _safe_db() stats: dict = {} if srv: stats.update(srv.stats) if db: stats["leases_total"] = db.count() stats["leases_active"] = db.count_active() return jsonify(stats) @app.route("/api/bindings") def api_bindings(): cfg = _config # type: ignore return jsonify([{"mac": b.mac_display, "ip": b.ip, "scope": b.scope_name, "hostname": b.hostname} for b in cfg.bindings])