feat(dashboard): tab Infrastructura, panouri colapsabile si compacte

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KddsXCqEbKMhdFJDYbAsx8
This commit is contained in:
Claude Agent
2026-09-13 10:18:12 +00:00
parent dc61c43b32
commit 56027e2e6c
4 changed files with 761 additions and 134 deletions

View File

@@ -44,6 +44,7 @@ for _p in (str(_BRIDGE), str(_DASH)):
sys.path.insert(0, _p)
import config # noqa: E402
import infra_actions # noqa: E402
from limits import parse_cap # noqa: E402
# ── constante ───────────────────────────────────────────────────────────
@@ -847,6 +848,18 @@ class Handler(SimpleHTTPRequestHandler):
return self.send_json({"clients": maria_clients()})
if path == "/api/maria/fire":
return self.send_json({"fire": maria_fire()})
if path == "/api/infra/status":
fresh = qs.get("fresh", ["0"])[0] not in ("0", "", "false")
return self.send_json(infra_actions.status(fresh=fresh))
if path == "/api/infra/log":
action_id = qs.get("id", [""])[0]
if action_id not in infra_actions.ACTIONS:
return self.send_json({"error": "actiune necunoscuta"}, 400)
try:
n = min(max(int(qs.get("n", ["60"])[0]), 1), 2000)
except ValueError:
n = 60
return self.send_json(infra_actions.log_tail(action_id, n))
if path == "/api/maria/logs":
try:
n = min(max(int(qs.get("lines", ["200"])[0]), 1), 2000)
@@ -874,6 +887,10 @@ class Handler(SimpleHTTPRequestHandler):
return self.deny()
if path == "/api/service":
return self.handle_service()
if path == "/api/infra/run":
return self.handle_infra_run()
if path == "/api/infra/stop":
return self.handle_infra_stop()
if path == "/api/cleanup":
data = self.read_json()
return self.send_json(orphans_report(dry_run=bool(data.get("dry_run", True))))
@@ -959,6 +976,26 @@ class Handler(SimpleHTTPRequestHandler):
return self.send_json({"ok": bool(ok), "request_id": rid, "decision": decision},
200 if ok else 404)
def handle_infra_run(self):
data = self.read_json()
action_id = str(data.get("id") or "")
dry_run = bool(data.get("dry_run"))
who = tailnet_user(self.headers) or "local"
print(f"[actiune] infra {action_id} dry={dry_run}, cerut de {who}",
file=sys.stderr, flush=True)
res = infra_actions.run(action_id, dry_run)
return self.send_json(res, res["code"])
def handle_infra_stop(self):
data = self.read_json()
action_id = str(data.get("id") or "")
if action_id not in infra_actions.ACTIONS:
return self.send_json({"ok": False, "code": 400, "error": "actiune necunoscuta"}, 400)
who = tailnet_user(self.headers) or "local"
print(f"[actiune] infra stop {action_id}, cerut de {who}", file=sys.stderr, flush=True)
res = infra_actions.stop(action_id)
return self.send_json(res, res["code"])
def handle_maria_service(self):
"""start / stop / restart pe una din UNITATILE FIXE ale Mariei — acelasi
principiu ca handle_service: numele vine dintr-o cheie (bridge/rag), nu