This commit is contained in:
薇薇安 2026-01-18 18:00:19 +08:00
parent 38ebbebd95
commit c92ce63a3a

View File

@ -1,6 +1,7 @@
import os
import re
import subprocess
from pathlib import Path
from typing import Any, Dict, Optional, Tuple
from fastapi import APIRouter, HTTPException, Header
@ -28,6 +29,22 @@ def _build_supervisorctl_cmd(args: list[str]) -> list[str]:
supervisor_conf = os.getenv("SUPERVISOR_CONF", "").strip()
use_sudo = os.getenv("SUPERVISOR_USE_SUDO", "false").lower() == "true"
# 如果没显式配置 SUPERVISOR_CONF就尝试自动探测常见路径宝塔/系统)
if not supervisor_conf:
candidates = [
"/www/server/panel/plugin/supervisor/supervisord.conf",
"/www/server/panel/plugin/supervisor/supervisor.conf",
"/etc/supervisor/supervisord.conf",
"/etc/supervisord.conf",
]
for p in candidates:
try:
if Path(p).exists():
supervisor_conf = p
break
except Exception:
continue
cmd: list[str] = []
if use_sudo:
# 需要你在 sudoers 配置 NOPASSWDsudo -n 才不会卡住)
@ -75,6 +92,89 @@ def _get_program_name() -> str:
return os.getenv("SUPERVISOR_TRADING_PROGRAM", "auto_sys").strip() or "auto_sys"
def _select_best_process_name(program: str, status_all_raw: str) -> Optional[str]:
"""
`supervisorctl status` 全量输出中找到最匹配的真实进程名
兼容 supervisor group:process 格式例如auto_sys:auto_sys_00
"""
if not status_all_raw:
return None
lines = [ln.strip() for ln in status_all_raw.splitlines() if ln.strip()]
names: list[str] = []
for ln in lines:
name = ln.split(None, 1)[0].strip()
if name:
names.append(name)
# 精确优先program / program_00 / program:program_00
preferred = [program, f"{program}_00", f"{program}:{program}_00"]
for cand in preferred:
if cand in names:
return cand
# 次优:任意以 program_ 开头
for name in names:
if name.startswith(program + "_"):
return name
# 次优:任意以 program: 开头
for name in names:
if name.startswith(program + ":"):
return name
return None
def _status_with_fallback(program: str) -> Tuple[str, Optional[str], Optional[str]]:
"""
- 优先 `status <program>`
- no such process返回全量 status并尝试解析真实 name例如 auto_sys:auto_sys_00
返回(raw, resolved_name, status_all)
"""
try:
raw = _run_supervisorctl(["status", program])
return raw, program, None
except Exception as e:
msg = str(e).lower()
if "no such process" not in msg:
raise
status_all = _run_supervisorctl(["status"])
resolved = _select_best_process_name(program, status_all)
if resolved:
try:
raw = _run_supervisorctl(["status", resolved])
return raw, resolved, status_all
except Exception:
# 兜底:至少把全量输出返回,方便你确认真实进程名
return status_all, None, status_all
return status_all, None, status_all
def _action_with_fallback(action: str, program: str) -> Tuple[str, Optional[str], Optional[str]]:
"""
start/stop/restart 做兜底如果 program 不存在尝试解析真实 name 再执行
返回(output, resolved_name, status_all)
"""
try:
out = _run_supervisorctl([action, program])
return out, program, None
except Exception as e:
msg = str(e).lower()
if "no such process" not in msg:
raise
status_all = _run_supervisorctl(["status"])
resolved = _select_best_process_name(program, status_all)
if not resolved:
# 没找到就把全量输出带上,方便定位
raise RuntimeError(f"no such process: {program}. 当前 supervisor 进程列表:\n{status_all}")
out = _run_supervisorctl([action, resolved])
return out, resolved, status_all
@router.post("/clear-cache")
async def clear_cache(x_admin_token: Optional[str] = Header(default=None, alias="X-Admin-Token")) -> Dict[str, Any]:
"""
@ -147,15 +247,17 @@ async def trading_status(x_admin_token: Optional[str] = Header(default=None, ali
program = _get_program_name()
try:
raw = _run_supervisorctl(["status", program])
raw, resolved_name, status_all = _status_with_fallback(program)
running, pid, state = _parse_supervisor_status(raw)
return {
"mode": "supervisor",
"program": program,
"resolved_name": resolved_name,
"running": running,
"pid": pid,
"state": state,
"raw": raw,
"status_all": status_all,
}
except Exception as e:
raise HTTPException(
@ -170,8 +272,8 @@ async def trading_start(x_admin_token: Optional[str] = Header(default=None, alia
program = _get_program_name()
try:
out = _run_supervisorctl(["start", program])
raw = _run_supervisorctl(["status", program])
out, resolved_name, status_all = _action_with_fallback("start", program)
raw, resolved_name2, status_all2 = _status_with_fallback(resolved_name or program)
running, pid, state = _parse_supervisor_status(raw)
return {
"message": "交易系统已启动supervisor",
@ -179,10 +281,12 @@ async def trading_start(x_admin_token: Optional[str] = Header(default=None, alia
"status": {
"mode": "supervisor",
"program": program,
"resolved_name": resolved_name2 or resolved_name,
"running": running,
"pid": pid,
"state": state,
"raw": raw,
"status_all": status_all2 or status_all,
},
}
except Exception as e:
@ -195,8 +299,8 @@ async def trading_stop(x_admin_token: Optional[str] = Header(default=None, alias
program = _get_program_name()
try:
out = _run_supervisorctl(["stop", program])
raw = _run_supervisorctl(["status", program])
out, resolved_name, status_all = _action_with_fallback("stop", program)
raw, resolved_name2, status_all2 = _status_with_fallback(resolved_name or program)
running, pid, state = _parse_supervisor_status(raw)
return {
"message": "交易系统已停止supervisor",
@ -204,10 +308,12 @@ async def trading_stop(x_admin_token: Optional[str] = Header(default=None, alias
"status": {
"mode": "supervisor",
"program": program,
"resolved_name": resolved_name2 or resolved_name,
"running": running,
"pid": pid,
"state": state,
"raw": raw,
"status_all": status_all2 or status_all,
},
}
except Exception as e:
@ -220,8 +326,8 @@ async def trading_restart(x_admin_token: Optional[str] = Header(default=None, al
program = _get_program_name()
try:
out = _run_supervisorctl(["restart", program])
raw = _run_supervisorctl(["status", program])
out, resolved_name, status_all = _action_with_fallback("restart", program)
raw, resolved_name2, status_all2 = _status_with_fallback(resolved_name or program)
running, pid, state = _parse_supervisor_status(raw)
return {
"message": "交易系统已重启supervisor",
@ -229,10 +335,12 @@ async def trading_restart(x_admin_token: Optional[str] = Header(default=None, al
"status": {
"mode": "supervisor",
"program": program,
"resolved_name": resolved_name2 or resolved_name,
"running": running,
"pid": pid,
"state": state,
"raw": raw,
"status_all": status_all2 or status_all,
},
}
except Exception as e: