]> acesimba.cloud Git - StationAIDemo.git/commitdiff
feat: 拆分 AIDC 轻量版为独立开关(aidc-lite),与完整版门禁互不干扰 main
authorCodeBuddy <codebuddy@acesimba.cloud>
Wed, 30 Sep 2026 05:15:33 +0000 (13:15 +0800)
committerCodeBuddy <codebuddy@acesimba.cloud>
Wed, 30 Sep 2026 05:15:33 +0000 (13:15 +0800)
backend/main.py
backend/tasks.json
frontend/src/List.tsx

index 5150a3a39a3f7da0fb8cfc223fb47f302c6d1fa0..c59fb5457363131c6227f00f043eb7289c8559cd 100644 (file)
@@ -3,6 +3,7 @@ import re
 import json
 import base64
 import asyncio
+import subprocess
 from datetime import datetime, timedelta
 from typing import Any, Optional
 from urllib.parse import unquote, urlparse, urlunparse
@@ -36,6 +37,51 @@ TASK_INDEX_HTML = os.path.join(
     os.path.dirname(os.path.dirname(__file__)), "frontend", "dist", "index.html"
 )
 
+# 每个任务关联的 systemd 单元:开启即启动,关闭即停止以释放资源。
+# aidemo 为控制面(承载 /list 与门禁),其后端 :8000 不可停止,故为空列表。
+TASK_SERVICES = {
+    "aidemo": [],
+    "stationbuilder": [
+        "stationbuilder-prod.service",
+        "stationbuilder-db.service",
+        "stationbuilder-sidecar.service",
+    ],
+    "stationbuilder-vue": [
+        "stationbuilder-vue.service",
+        "stationbuilder-vue-db.service",
+        "stationbuilder-vue-sidecar.service",
+    ],
+    # AIDC 为纯前端静态站点(无后端进程),由其专属 nginx 路由(静态托管 /home/AIDC/dist)
+    # 配合门禁(auth_request)控制访问;关闭即门禁拦截、不提供服务,无需 systemd 进程启停。
+    # aidc-lite 为轻量版,独立开关,同样无后端进程。
+    "aidc": [],
+    "aidc-lite": [],
+}
+
+
+def control_services(task_id: str, action: str) -> dict:
+    """对任务关联的 systemd 单元执行 start/stop。返回 {unit: 状态}。
+
+    action 为 'start' 或 'stop'。单元不存在或操作失败会在结果中以 err: 前缀标注,
+    不会抛出,避免影响任务开关本身的持久化。
+    """
+    units = TASK_SERVICES.get(task_id, [])
+    result = {}
+    for unit in units:
+        try:
+            proc = subprocess.run(
+                ["systemctl", action, unit],
+                capture_output=True,
+                text=True,
+                timeout=30,
+            )
+            result[unit] = (
+                "ok" if proc.returncode == 0 else f"err:{proc.stderr.strip()[:200]}"
+            )
+        except Exception as e:
+            result[unit] = f"err:{str(e)[:200]}"
+    return result
+
 
 def load_tasks() -> list[dict]:
     if not os.path.exists(TASKS_FILE):
@@ -1043,7 +1089,8 @@ async def open_task(task_id: str, request: Request):
         return JSONResponse(status_code=404, content={"ok": False, "message": "任务不存在"})
     task["open"] = True
     save_tasks(tasks)
-    return {"ok": True, "task": task}
+    services = control_services(task_id, "start")
+    return {"ok": True, "task": task, "services": services}
 
 
 @app.post("/api/tasks/{task_id}/close")
@@ -1056,7 +1103,8 @@ async def close_task(task_id: str, request: Request):
         return JSONResponse(status_code=404, content={"ok": False, "message": "任务不存在"})
     task["open"] = False
     save_tasks(tasks)
-    return {"ok": True, "task": task}
+    services = control_services(task_id, "stop")
+    return {"ok": True, "task": task, "services": services}
 
 
 @app.get("/t/{task_id}")
index b7d660a3ad8488fcab1288ecdd17e59757a92f67..8b5337a7e5c320a58e6026953ecc8c711c575792 100644 (file)
@@ -2,11 +2,26 @@
   {
     "id": "aidemo",
     "name": "AIDemo",
-    "open": true
+    "open": false
   },
   {
     "id": "stationbuilder",
     "name": "StationBuilder",
+    "open": false
+  },
+  {
+    "id": "stationbuilder-vue",
+    "name": "StationBuilder (Vue 移植版)",
+    "open": false
+  },
+  {
+    "id": "aidc",
+    "name": "AIDC 智慧运维系统",
+    "open": true
+  },
+  {
+    "id": "aidc-lite",
+    "name": "AIDC 智慧运维系统(轻量版)",
     "open": true
   }
 ]
\ No newline at end of file
index 4fa25b9ad2be3961fcdb1848d788da2af9467ff8..013225b9451a9759efbb79b9693e55b413643a32 100644 (file)
@@ -144,7 +144,10 @@ export default function TaskList() {
 
       <ul style={{ listStyle: "none", padding: 0, margin: 0 }}>
         {tasks.map((t) => {
-          const shareUrl = `${window.location.origin}/t/${t.id}`;
+          const shareUrl =
+            t.id === "aidc-lite"
+              ? `${window.location.origin}/t/aidc/lite`
+              : `${window.location.origin}/t/${t.id}`;
           return (
             <li
               key={t.id}