Files
Dashboard-CAS/backend/main.py
I. Alcaraz Salazar 81bcdcd696 feat: add admin panel at /admin for full dashboard configuration
- Backend: ConfigManager write methods (atomic YAML save), admin CRUD
  router for settings (display/odoo/refresh) and nodes, WS broadcast
  on config changes, fix nmap scan blocking event loop with to_thread
- Frontend: admin UI with tab navigation, overview dashboard, node
  CRUD table with modal form, Odoo/display/refresh settings pages,
  typed API wrappers, active views filtering, config_changed WS handler
- Infra: nginx no-cache headers for HTML, cache-forever for hashed assets
- Fixes: WebSocket reconnect loop (ref pattern), rotation index OOB
  when views shrink, mutable node list cache

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 02:34:05 +00:00

102 lines
2.8 KiB
Python

import asyncio
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from modules.config_manager import ConfigManager
from modules.odoo_client import OdooClient
CONFIG_DIR = Path(__file__).parent / "config"
app_config = ConfigManager(
settings_path=str(CONFIG_DIR / "settings.yaml"),
services_path=str(CONFIG_DIR / "services.yaml"),
)
settings = app_config.get_settings()
odoo_settings = settings.get("odoo", {})
odoo_client = OdooClient(
url=odoo_settings.get("url", "http://localhost:8069"),
database=odoo_settings.get("database", "odoo"),
username=odoo_settings.get("username", "admin"),
password=odoo_settings.get("password", "admin"),
)
async def refresh_loop():
from routers.ws import broadcast
from modules.network_scanner import NetworkScanner
refresh = app_config.get_settings().get("refresh", {})
odoo_interval = refresh.get("odoo_minutes", 5) * 60
network_interval = refresh.get("network_minutes", 10) * 60
ping_interval = refresh.get("ping_seconds", 60)
last_odoo = 0
last_network = 0
last_ping = 0
while True:
now = asyncio.get_event_loop().time()
if now - last_ping >= ping_interval:
try:
scan_config = app_config.get_network_scan_config()
scanner = NetworkScanner(scan_config.get("subnet", "192.168.1.0/24"))
nodes = app_config.get_nodes()
ips = [n["ip"] for n in nodes]
statuses = await scanner.ping_all(ips)
await broadcast("ping_update", statuses)
except Exception:
pass
last_ping = now
if now - last_odoo >= odoo_interval:
try:
await broadcast("odoo_refresh", {"trigger": "scheduled"})
except Exception:
pass
last_odoo = now
await asyncio.sleep(10)
@asynccontextmanager
async def lifespan(app: FastAPI):
try:
await odoo_client.authenticate()
except Exception as e:
print(f"Warning: Could not connect to Odoo: {e}")
task = asyncio.create_task(refresh_loop())
yield
task.cancel()
await odoo_client.close()
app = FastAPI(title="TV Dashboard API", lifespan=lifespan)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
from routers import network, tasks, calendar, services, ws, admin
admin.init(app_config)
app.include_router(network.router)
app.include_router(tasks.router)
app.include_router(calendar.router)
app.include_router(services.router)
app.include_router(ws.router)
app.include_router(admin.router)
@app.get("/api/health")
async def health():
return {"status": "ok", "odoo_connected": odoo_client.uid is not None}