+# 每个任务关联的 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
+