主界面修改及代理模块初始化

This commit is contained in:
zhongluofeng
2026-07-15 18:22:07 +08:00
parent 29a5f456cb
commit fb361aff9a
39 changed files with 4768 additions and 396 deletions
+358
View File
@@ -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(&params.id) {
if entry.child.try_wait().map_err(|e| e.to_string())?.is_none() {
return Err(format!("进程 '{}' 已在运行中", params.id));
}
// 进程已退出,移除旧记录
processes.remove(&params.id);
}
let mut cmd = Command::new(&params.executable);
cmd.args(&params.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);
}
}
});
}