主界面修改及代理模块初始化
This commit is contained in:
@@ -0,0 +1,358 @@
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::sync::Mutex;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
use tauri::{AppHandle, Emitter, Manager};
|
||||
|
||||
/// 进程状态枚举
|
||||
#[derive(Serialize, Clone, Debug)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum ProcessStatus {
|
||||
Running,
|
||||
Stopped,
|
||||
Crashed,
|
||||
#[allow(dead_code)]
|
||||
Starting,
|
||||
}
|
||||
|
||||
/// 进程信息(返回给前端)
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct ProcessInfo {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub status: ProcessStatus,
|
||||
pub pid: Option<u32>,
|
||||
pub restart_count: u32,
|
||||
}
|
||||
|
||||
/// 进程启动参数(从前端传入)
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct StartProcessParams {
|
||||
pub id: String,
|
||||
pub executable: String,
|
||||
#[serde(default)]
|
||||
pub args: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub cwd: Option<String>,
|
||||
pub name: String,
|
||||
#[serde(default)]
|
||||
pub restart_on_crash: bool,
|
||||
#[serde(default = "default_max_restarts")]
|
||||
pub max_restarts: u32,
|
||||
}
|
||||
|
||||
fn default_max_restarts() -> u32 {
|
||||
3
|
||||
}
|
||||
|
||||
/// 内部进程条目
|
||||
struct ProcessEntry {
|
||||
child: Child,
|
||||
name: String,
|
||||
restart_count: u32,
|
||||
max_restarts: u32,
|
||||
restart_on_crash: bool,
|
||||
executable: String,
|
||||
args: Vec<String>,
|
||||
cwd: Option<String>,
|
||||
}
|
||||
|
||||
/// 进程管理器 —— 管理子进程的完整生命周期
|
||||
pub struct ProcessManager {
|
||||
processes: Mutex<HashMap<String, ProcessEntry>>,
|
||||
}
|
||||
|
||||
impl ProcessManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
processes: Mutex::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// 启动一个子进程
|
||||
pub fn start(&self, params: StartProcessParams) -> Result<ProcessInfo, String> {
|
||||
let mut processes = self.processes.lock().map_err(|e| e.to_string())?;
|
||||
|
||||
// 如果已有同名进程且仍在运行,返回错误
|
||||
if let Some(entry) = processes.get_mut(¶ms.id) {
|
||||
if entry.child.try_wait().map_err(|e| e.to_string())?.is_none() {
|
||||
return Err(format!("进程 '{}' 已在运行中", params.id));
|
||||
}
|
||||
// 进程已退出,移除旧记录
|
||||
processes.remove(¶ms.id);
|
||||
}
|
||||
|
||||
let mut cmd = Command::new(¶ms.executable);
|
||||
cmd.args(¶ms.args);
|
||||
if let Some(ref dir) = params.cwd {
|
||||
cmd.current_dir(dir);
|
||||
}
|
||||
// 子进程的 stdout/stderr/stdin 不继承主进程
|
||||
cmd.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.stdin(Stdio::null());
|
||||
|
||||
let child = cmd
|
||||
.spawn()
|
||||
.map_err(|e| format!("启动进程 '{}' 失败: {}", params.id, e))?;
|
||||
let pid = child.id();
|
||||
|
||||
let entry = ProcessEntry {
|
||||
child,
|
||||
name: params.name.clone(),
|
||||
restart_count: 0,
|
||||
max_restarts: params.max_restarts,
|
||||
restart_on_crash: params.restart_on_crash,
|
||||
executable: params.executable,
|
||||
args: params.args,
|
||||
cwd: params.cwd,
|
||||
};
|
||||
|
||||
processes.insert(params.id.clone(), entry);
|
||||
|
||||
Ok(ProcessInfo {
|
||||
id: params.id,
|
||||
name: params.name,
|
||||
status: ProcessStatus::Running,
|
||||
pid: Some(pid),
|
||||
restart_count: 0,
|
||||
})
|
||||
}
|
||||
|
||||
/// 停止指定进程
|
||||
pub fn stop(&self, id: &str) -> Result<(), String> {
|
||||
let mut processes = self.processes.lock().map_err(|e| e.to_string())?;
|
||||
|
||||
if let Some(mut entry) = processes.remove(id) {
|
||||
entry
|
||||
.child
|
||||
.kill()
|
||||
.map_err(|e| format!("终止进程 '{}' 失败: {}", id, e))?;
|
||||
let _ = entry.child.wait();
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!("进程 '{}' 不存在", id))
|
||||
}
|
||||
}
|
||||
|
||||
/// 停止所有进程(应用退出时调用)
|
||||
pub fn stop_all(&self) {
|
||||
if let Ok(mut processes) = self.processes.lock() {
|
||||
for (id, mut entry) in processes.drain() {
|
||||
let _ = entry.child.kill();
|
||||
let _ = entry.child.wait();
|
||||
println!("[ProcessManager] 已停止进程: {}", id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取单个进程状态
|
||||
pub fn get_status(&self, id: &str) -> Option<ProcessInfo> {
|
||||
let mut processes = self.processes.lock().ok()?;
|
||||
let entry = processes.get_mut(id)?;
|
||||
|
||||
let (status, pid) = match entry.child.try_wait() {
|
||||
Ok(None) => (ProcessStatus::Running, Some(entry.child.id())),
|
||||
Ok(Some(_)) => (ProcessStatus::Crashed, None),
|
||||
Err(_) => (ProcessStatus::Stopped, None),
|
||||
};
|
||||
|
||||
Some(ProcessInfo {
|
||||
id: id.to_string(),
|
||||
name: entry.name.clone(),
|
||||
status,
|
||||
pid,
|
||||
restart_count: entry.restart_count,
|
||||
})
|
||||
}
|
||||
|
||||
/// 获取所有进程状态
|
||||
pub fn get_all_status(&self) -> Vec<ProcessInfo> {
|
||||
let mut result = Vec::new();
|
||||
|
||||
if let Ok(mut processes) = self.processes.lock() {
|
||||
for (id, entry) in processes.iter_mut() {
|
||||
let (status, pid) = match entry.child.try_wait() {
|
||||
Ok(None) => (ProcessStatus::Running, Some(entry.child.id())),
|
||||
Ok(Some(_)) => (ProcessStatus::Crashed, None),
|
||||
Err(_) => (ProcessStatus::Stopped, None),
|
||||
};
|
||||
|
||||
result.push(ProcessInfo {
|
||||
id: id.clone(),
|
||||
name: entry.name.clone(),
|
||||
status,
|
||||
pid,
|
||||
restart_count: entry.restart_count,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
/// 检查所有进程,处理崩溃的进程(自动重启或移除)
|
||||
/// 返回状态发生变化的进程列表
|
||||
pub fn check_and_cleanup(&self) -> Vec<ProcessInfo> {
|
||||
let mut changes = Vec::new();
|
||||
|
||||
let mut processes = match self.processes.lock() {
|
||||
Ok(p) => p,
|
||||
Err(_) => return changes,
|
||||
};
|
||||
|
||||
let ids: Vec<String> = processes.keys().cloned().collect();
|
||||
|
||||
for id in ids {
|
||||
if let Some(entry) = processes.get_mut(&id) {
|
||||
match entry.child.try_wait() {
|
||||
Ok(None) => {
|
||||
// 仍在运行,无需处理
|
||||
}
|
||||
Ok(Some(_)) => {
|
||||
// 进程已退出
|
||||
if entry.restart_on_crash
|
||||
&& (entry.max_restarts == 0
|
||||
|| entry.restart_count < entry.max_restarts)
|
||||
{
|
||||
// 自动重启
|
||||
let restart_count = entry.restart_count + 1;
|
||||
let executable = entry.executable.clone();
|
||||
let args = entry.args.clone();
|
||||
let cwd = entry.cwd.clone();
|
||||
let name = entry.name.clone();
|
||||
|
||||
// 先终止旧进程
|
||||
let _ = entry.child.kill();
|
||||
let _ = entry.child.wait();
|
||||
|
||||
// 重新启动
|
||||
let mut cmd = Command::new(&executable);
|
||||
cmd.args(&args);
|
||||
if let Some(ref dir) = cwd {
|
||||
cmd.current_dir(dir);
|
||||
}
|
||||
cmd.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.stdin(Stdio::null());
|
||||
|
||||
match cmd.spawn() {
|
||||
Ok(new_child) => {
|
||||
let pid = new_child.id();
|
||||
entry.child = new_child;
|
||||
entry.restart_count = restart_count;
|
||||
|
||||
changes.push(ProcessInfo {
|
||||
id: id.clone(),
|
||||
name: name.clone(),
|
||||
status: ProcessStatus::Running,
|
||||
pid: Some(pid),
|
||||
restart_count,
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!(
|
||||
"[ProcessManager] 重启进程 '{}' 失败: {}",
|
||||
id, e
|
||||
);
|
||||
processes.remove(&id);
|
||||
changes.push(ProcessInfo {
|
||||
id: id.clone(),
|
||||
name,
|
||||
status: ProcessStatus::Crashed,
|
||||
pid: None,
|
||||
restart_count,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 不自动重启,移除记录
|
||||
let name = entry.name.clone();
|
||||
let restart_count = entry.restart_count;
|
||||
processes.remove(&id);
|
||||
changes.push(ProcessInfo {
|
||||
id: id.clone(),
|
||||
name,
|
||||
status: ProcessStatus::Stopped,
|
||||
pid: None,
|
||||
restart_count,
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
// try_wait 出错
|
||||
let name = entry.name.clone();
|
||||
let restart_count = entry.restart_count;
|
||||
processes.remove(&id);
|
||||
changes.push(ProcessInfo {
|
||||
id: id.clone(),
|
||||
name,
|
||||
status: ProcessStatus::Stopped,
|
||||
pid: None,
|
||||
restart_count,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
changes
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Tauri 命令 =====
|
||||
|
||||
#[tauri::command]
|
||||
pub fn start_process(
|
||||
state: tauri::State<'_, ProcessManager>,
|
||||
params: StartProcessParams,
|
||||
) -> Result<ProcessInfo, String> {
|
||||
state.start(params)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn stop_process(
|
||||
state: tauri::State<'_, ProcessManager>,
|
||||
id: String,
|
||||
) -> Result<(), String> {
|
||||
state.stop(&id)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_process_status(
|
||||
state: tauri::State<'_, ProcessManager>,
|
||||
id: String,
|
||||
) -> Option<ProcessInfo> {
|
||||
state.get_status(&id)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_all_process_status(
|
||||
state: tauri::State<'_, ProcessManager>,
|
||||
) -> Vec<ProcessInfo> {
|
||||
state.get_all_status()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn stop_all_processes(state: tauri::State<'_, ProcessManager>) {
|
||||
state.stop_all()
|
||||
}
|
||||
|
||||
/// 启动后台监控线程,定期检查进程状态并向前端发送事件
|
||||
pub fn start_monitoring_thread(app: AppHandle) {
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
thread::sleep(Duration::from_secs(3));
|
||||
|
||||
let state = app.state::<ProcessManager>();
|
||||
let changes = state.check_and_cleanup();
|
||||
|
||||
for change in changes {
|
||||
let _ = app.emit("process-status-changed", &change);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user