]> 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 json
 import base64
 import asyncio
+import subprocess
 from datetime import datetime, timedelta
 from typing import Any, Optional
 from urllib.parse import unquote, urlparse, urlunparse
 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"
 )
 
     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):
 
 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 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")
 
 
 @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 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}")
 
 
 @app.get("/t/{task_id}")
index b7d660a3ad8488fcab1288ecdd17e59757a92f67..8b5337a7e5c320a58e6026953ecc8c711c575792 100644 (file)
@@ -2,11 +2,26 @@
   {
     "id": "aidemo",
     "name": "AIDemo",
   {
     "id": "aidemo",
     "name": "AIDemo",
-    "open": true
+    "open": false
   },
   {
     "id": "stationbuilder",
     "name": "StationBuilder",
   },
   {
     "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
     "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) => {
 
       <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}
           return (
             <li
               key={t.id}