From a85bbd2411b4b740acf88884f6e2a670154c0558 Mon Sep 17 00:00:00 2001 From: "Gan, Jimmy" Date: Sat, 11 Jul 2026 01:33:19 +0800 Subject: [PATCH] refactor: S06.2 backend router auto-discovery via ROUTER_CONFIGS manifest (replaces 18 explicit imports) --- dashboard/backend/main.py | 98 ++++++++------------------- dashboard/backend/routers/__init__.py | 24 +++++++ 2 files changed, 54 insertions(+), 68 deletions(-) diff --git a/dashboard/backend/main.py b/dashboard/backend/main.py index c5712bf..bf1f965 100644 --- a/dashboard/backend/main.py +++ b/dashboard/backend/main.py @@ -21,25 +21,6 @@ import auth_service as auth_module import config from config import CORS_ORIGINS, TELEGRAM_BOT_TOKEN, TELEGRAM_CHAT_ID, VOLUME_ROOT, get_client_ip from rbac import require_page -from routers import auth as auth_router -from routers import ( - chat_summary, - conversation_tracker, - files, - gitea, - litellm, - opc_agents, - opc_projects, - opc_tasks, - opc_ws, - passkey, - security, - system, - terminal, - totp, - transmission, -) - _tz_cst = timezone(timedelta(hours=8)) @@ -271,57 +252,38 @@ async def _inject_user(request: Request, user=Depends(auth_module.get_current_us return user -# Public auth endpoints -app.include_router(auth_router.router, prefix="/api/auth", tags=["auth"]) -app.include_router(passkey.router, prefix="/api/auth", tags=["passkey"]) -app.include_router(totp.router, prefix="/api/auth", tags=["totp"]) +# Auto-discovered router registration (from ROUTER_CONFIGS manifest) +from routers import ROUTER_CONFIGS, WS_ROUTES +import importlib +import re -# Protected endpoints with page-level RBAC -app.include_router( - gitea.router, prefix="/api/gitea", dependencies=[Depends(_inject_user), Depends(require_page("gitea"))] -) -app.include_router( - files.router, prefix="/api/files", dependencies=[Depends(_inject_user)] -) -# Public file download endpoint (no auth required for token-based downloads) -app.include_router(files.public_router, prefix="/api/files") -app.include_router( - system.router, prefix="/api/system", dependencies=[Depends(_inject_user), Depends(require_page("dashboard"))] -) -app.include_router( - litellm.router, prefix="/api/litellm", dependencies=[Depends(_inject_user), Depends(require_page("dashboard"))] -) -app.include_router( - chat_summary.router, - prefix="/api/chat-summary", - dependencies=[Depends(_inject_user), Depends(require_page("chat-digest"))], -) -app.include_router( - conversation_tracker.router, - prefix="/api/conversations", - dependencies=[Depends(_inject_user), Depends(require_page("conversations"))], -) -app.include_router( - security.router, prefix="/api/security", dependencies=[Depends(_inject_user), Depends(require_page("security"))] -) -app.include_router( - transmission.router, dependencies=[Depends(_inject_user), Depends(require_page("transmission"))] -) +for cfg in ROUTER_CONFIGS: + mod = importlib.import_module('routers.' + cfg['module']) + router_attr = cfg.get('router_attr', 'router') + router_obj = getattr(mod, router_attr) + prefix = cfg.get('prefix') + tags = cfg.get('tags') + deps = cfg.get('deps', []) + dep_objects = [] + for d in deps: + if d == '_inject_user': + dep_objects.append(Depends(_inject_user)) + elif d.startswith('require_page('): + # Extract page name from require_page('gitea') or require_page(gitea) + m = re.search(r'require_page\(["\'](.*?)["\']\)', d) + page_name = m.group(1) if m else d.split('(')[1].rstrip(')') + dep_objects.append(Depends(require_page(page_name))) + kwargs = {'dependencies': dep_objects} if dep_objects else {} + if prefix is not None: + kwargs['prefix'] = prefix + if tags: + kwargs['tags'] = tags + app.include_router(router_obj, **kwargs) -# OPC endpoints -app.include_router( - opc_tasks.router, prefix="/api/opc", dependencies=[Depends(_inject_user), Depends(require_page("opc"))] -) -app.include_router( - opc_agents.router, prefix="/api/opc", dependencies=[Depends(_inject_user), Depends(require_page("opc"))] -) -app.include_router( - opc_projects.router, prefix="/api/opc", dependencies=[Depends(_inject_user), Depends(require_page("opc"))] -) - -# WebSocket endpoint (auth handled inside handler via auth cookie; query token fallback temporary) -app.add_api_websocket_route("/ws/terminal", terminal.ws_endpoint) -app.add_api_websocket_route("/ws/opc", opc_ws.websocket_endpoint) +for ws in WS_ROUTES: + mod = importlib.import_module('routers.' + ws['module']) + handler = getattr(mod, ws['handler']) + app.add_api_websocket_route(ws['path'], handler) # Mount static files (skip if directory doesn't exist, e.g., in test environments) import os diff --git a/dashboard/backend/routers/__init__.py b/dashboard/backend/routers/__init__.py index e69de29..65c44f5 100644 --- a/dashboard/backend/routers/__init__.py +++ b/dashboard/backend/routers/__init__.py @@ -0,0 +1,24 @@ +"""Router auto-discovery manifest. +Each entry defines how a router module gets registered. +""" +ROUTER_CONFIGS = [ + {"module": "auth", "prefix": "/api/auth", "tags": ["auth"]}, + {"module": "passkey", "prefix": "/api/auth", "tags": ["passkey"]}, + {"module": "totp", "prefix": "/api/auth", "tags": ["totp"]}, + {"module": "gitea", "prefix": "/api/gitea", "deps": ["_inject_user", "require_page(\"gitea\")"]}, + {"module": "files", "prefix": "/api/files", "deps": ["_inject_user"]}, + {"module": "files", "router_attr": "public_router", "prefix": "/api/files"}, + {"module": "system", "prefix": "/api/system", "deps": ["_inject_user", "require_page(\"dashboard\")"]}, + {"module": "litellm", "prefix": "/api/litellm", "deps": ["_inject_user", "require_page(\"dashboard\")"]}, + {"module": "chat_summary", "prefix": "/api/chat-summary", "deps": ["_inject_user", "require_page(\"chat-digest\")"]}, + {"module": "conversation_tracker", "prefix": "/api/conversations", "deps": ["_inject_user", "require_page(\"conversations\")"]}, + {"module": "security", "prefix": "/api/security", "deps": ["_inject_user", "require_page(\"security\")"]}, + {"module": "transmission", "prefix": None, "deps": ["_inject_user", "require_page(\"transmission\")"]}, + {"module": "opc_tasks", "prefix": "/api/opc", "deps": ["_inject_user", "require_page(\"opc\")"]}, + {"module": "opc_agents", "prefix": "/api/opc", "deps": ["_inject_user", "require_page(\"opc\")"]}, + {"module": "opc_projects", "prefix": "/api/opc", "deps": ["_inject_user", "require_page(\"opc\")"]}, +] +WS_ROUTES = [ + {"module": "terminal", "path": "/ws/terminal", "handler": "ws_endpoint"}, + {"module": "opc_ws", "path": "/ws/opc", "handler": "websocket_endpoint"}, +]