Files
getDomain/domain-api/app/services/runtime_control_service.py
Your Name ebf632e651 first
2026-04-16 21:35:47 +08:00

112 lines
4.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from __future__ import annotations
import os
import subprocess
from pathlib import Path
from app.core.config import settings
from app.services.sync_push_service import push_runtime_projection_now
from app.services.runtime_settings_service import get_runtime_settings
from app.services.worker_control_service import _run_systemctl, start_worker, stop_worker
def _workspace_root() -> Path:
return Path(settings.domain_root).parent
def _run_shell(command: list[str], timeout: int = 30) -> subprocess.CompletedProcess[str]:
return subprocess.run(command, capture_output=True, text=True, timeout=timeout)
def restart_api() -> tuple[bool, str]:
runtime = get_runtime_settings()
api_service_name = runtime.get("api_service_name", settings.api_service_name)
worker_mode = runtime.get("worker_mode", settings.worker_mode)
if worker_mode == "linux-systemd":
# When the API restarts itself under systemd, wait-free restart avoids
# blocking the HTTP request until uvicorn is torn down.
result = _run_systemctl(["restart", api_service_name], timeout=5, no_block=True)
if result.returncode != 0:
return False, (result.stderr or result.stdout or "重启 Linux API 失败").strip()
return True, f"Linux API 重启命令已发送: {api_service_name}"
if os.name != "nt":
return False, "当前仅实现 Windows 本地 API 重启Linux 请将 worker_mode 设为 linux-systemd。"
workspace = _workspace_root()
stop_script = workspace / "stop_domain_api.ps1"
start_script = workspace / "start_domain_api.ps1"
if not stop_script.exists() or not start_script.exists():
return False, "未找到 API 启停脚本"
command = (
"Start-Process powershell "
"-ArgumentList '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', "
f"\"Start-Sleep -Seconds 2; & '{stop_script}'; Start-Sleep -Seconds 1; & '{start_script}'\""
)
result = _run_shell(["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command], timeout=20)
if result.returncode != 0:
return False, (result.stderr or result.stdout or "重启 API 失败").strip()
return True, "API 重启命令已发送"
def _run_systemd_action(service_name: str, action: str, *, no_block: bool = False) -> tuple[bool, str]:
systemctl_command = []
if no_block:
systemctl_command.append("--no-block")
systemctl_command.extend([action, service_name])
result = _run_systemctl(systemctl_command, timeout=10 if no_block else 30)
if result.returncode != 0:
return False, (result.stderr or result.stdout or f"{action} {service_name} 失败").strip()
return True, f"{service_name} {action} 命令已发送"
def start_sync_agent() -> tuple[bool, str]:
runtime = get_runtime_settings()
worker_mode = runtime.get("worker_mode", settings.worker_mode)
service_name = runtime.get("sync_agent_service_name", settings.sync_agent_service_name)
if worker_mode != "linux-systemd":
return False, "sync-agent 仅在 Linux systemd 多机部署中使用。"
return _run_systemd_action(service_name, "start")
def stop_sync_agent() -> tuple[bool, str]:
runtime = get_runtime_settings()
worker_mode = runtime.get("worker_mode", settings.worker_mode)
service_name = runtime.get("sync_agent_service_name", settings.sync_agent_service_name)
if worker_mode != "linux-systemd":
return False, "sync-agent 仅在 Linux systemd 多机部署中使用。"
return _run_systemd_action(service_name, "stop")
def runtime_action(action: str) -> tuple[bool, str, dict]:
if action == "start_worker":
ok, message = start_worker()
return ok, message, {"action": action, "poll_after_seconds": 2, "refresh_runtime": True}
if action == "stop_worker":
ok, message = stop_worker()
return ok, message, {"action": action, "poll_after_seconds": 2, "refresh_runtime": True}
if action == "restart_api":
ok, message = restart_api()
return ok, message, {"action": action, "poll_after_seconds": 4, "refresh_runtime": True}
if action == "start_sync_agent":
ok, message = start_sync_agent()
return ok, message, {"action": action, "poll_after_seconds": 2, "refresh_runtime": True}
if action == "stop_sync_agent":
ok, message = stop_sync_agent()
return ok, message, {"action": action, "poll_after_seconds": 2, "refresh_runtime": True}
if action == "push_sync":
ok, message, data = push_runtime_projection_now()
return ok, message, {
"action": action,
"poll_after_seconds": 2,
"refresh_runtime": True,
**(data or {}),
}
return False, f"不支持的运行时动作: {action}", {
"action": action,
"poll_after_seconds": 0,
"refresh_runtime": False,
}