From 55c6e49d4a77ad8e8486e6a542452f061ac90511 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=91=E5=85=88=E6=A3=AE?= Date: Wed, 27 Nov 2024 19:24:19 +0800 Subject: [PATCH] =?UTF-8?q?=E8=87=AA=E5=BB=BA=E6=9C=8D=E5=8A=A1=E5=99=A8?= =?UTF-8?q?=E5=8D=95=E7=8B=AC=E9=87=8D=E5=86=99=EF=BC=8C=E6=88=90=E5=91=98?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E6=96=B0=E5=A2=9E=E6=96=AD=E5=BC=80=E8=BF=9E?= =?UTF-8?q?=E6=8E=A5=E5=88=A4=E6=96=AD=EF=BC=8C=E8=81=94=E6=9C=BA=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=96=AD=E5=BC=80=E8=81=94=E6=9C=BA=E7=9A=84=E6=A3=80?= =?UTF-8?q?=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composables/server.ts | 25 ++++ pages/index.vue | 203 ++++++++++++++-------------- pages/member.vue | 51 +++++-- pages/server.vue | 128 ++++++++++++++++++ src-tauri/capabilities/default.json | 3 +- src-tauri/src/lib.rs | 86 +++++++----- stores/index.ts | 14 +- utils/index.ts | 1 + zip.js | 2 +- 9 files changed, 367 insertions(+), 146 deletions(-) create mode 100644 composables/server.ts create mode 100644 pages/server.vue diff --git a/composables/server.ts b/composables/server.ts new file mode 100644 index 0000000..6a282ed --- /dev/null +++ b/composables/server.ts @@ -0,0 +1,25 @@ +import useMainStore from "@/stores/index"; +export const getServerArgs = () => { + const mainStore = useMainStore(); + const args = []; + if (!mainStore.serverConfig.port) { + mainStore.serverConfig.port = "11010"; + } + args.push("-l", mainStore.serverConfig.port); + if (mainStore.serverConfig.relayAllPeerrpc) { + args.push("--relay-all-peer-rpc"); + } + const whiteList = mainStore.serverConfig.serverWhiteList + .trim() + .split("\n") + .map(el => el.trim()) + .filter(el => el) + .join(" "); + if (whiteList && mainStore.serverConfig.enableWhiteList) { + args.push("--relay-network-whitelist", whiteList); + } + if (!whiteList && mainStore.serverConfig.enableWhiteList) { + args.push("--relay-network-whitelist"); + } + return args; +}; diff --git a/pages/index.vue b/pages/index.vue index 98e3585..9e2f0d3 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -179,80 +179,6 @@ -
@@ -287,7 +213,7 @@ :icon="SetUp" command="create_server" > - 我要开服(自建) + 自建服务({{ listenObj.server_thread_id.value ? "运行中" : "未运行" }}) { await handleConnection(); @@ -593,10 +521,12 @@ cidrVisible: false, advanceVisible: false, toolVisible: false, + serverVisible: false, winipBcPid: 0, //WinIPBroadcast进程id winipBcStart: false, memberVisible: false, log: "", + serverLog: "", //服务端日志 update: false, releaseList: [], coreVersion: "-", @@ -672,17 +602,29 @@ const listenObj: { [key: string]: any } = { unListenOutPut: null, unListenThreadId: null, + unListenServerOutPut: null, + unListenServerThreadId: null, unListenConfigStart: null, + unListenStartStopServer: null, thread_id: null, + server_thread_id: ref(null), async listenOutput() { // const appWindow = getCurrentWindow(); - const unListen = await listen("command-output", async event => { + const unListen = await listen("command-output", async event => { data.isStart = true; // console.error(event.payload); if (event.payload) { data.startLoading = false; let ipv4 = /dhcp ip changed. old: None, new: Some\((\d+\.\d+\.\d+\.\d+).*\)/g.exec(event.payload as string)?.[1]; let devName = /tun device ready. dev: (.*)/g.exec(event.payload as string)?.[1]; + if (event.payload.includes("peer connection removed")) { + data.isSuccessGetIp = false; + } + if (event.payload.includes("new peer connection added") && !data.isSuccessGetIp) { + await setTrayRunState(tray, true); + data.isSuccessGetIp = true; + await setTrayTooltip(tray, `IP: ${config.ipv4}`); + } if (config.dhcp || mainStore.configStartEnable) { if (ipv4) { config.ipv4 = ipv4; @@ -690,12 +632,6 @@ data.isSuccessGetIp = true; await setTrayTooltip(tray, `IP: ${ipv4}`); } - } else { - if ((event.payload as string).includes("new peer connection added") && !data.isSuccessGetIp) { - await setTrayRunState(tray, true); - data.isSuccessGetIp = true; - await setTrayTooltip(tray, `IP: ${config.ipv4}`); - } } if ( devName && @@ -733,6 +669,24 @@ }); this.unListenThreadId = unListen; }, + async listenServerOutPut() { + const unListen = await listen("server-command-output", async event => { + // console.log("server-command-output", event); + const logArr = data.serverLog.split("\n"); + const start = logArr.length > 1000 ? logArr.length - 1000 : 0; + data.serverLog = logArr.slice(start).join("\n"); + data.serverLog += (event.payload || "") + "\n"; + }); + this.unListenServerOutPut = unListen; + }, + async listenServerThreadId() { + const unListen = await listen("server-thread-id", event => { + if (event.payload) { + this.server_thread_id.value = event.payload; + } + }); + this.unListenServerThreadId = unListen; + }, async listenConfigStart() { const unListen = await listen("config", event => { // console.error("config", event.payload); @@ -741,6 +695,23 @@ config.ipv4 = ipv4; }); this.unListenConfigStart = unListen; + }, + async listenStartStopServer() { + const unListen = await listen<{ args: Array }>("startStopServer", async event => { + await listenObj?.unListenServerOutPut?.(); + await invoke("stop_command", { child_id: listenObj.server_thread_id.value || 0 }); + if (listenObj.server_thread_id.value) { + listenObj.server_thread_id.value = null; + } else { + data.serverLog = ""; + await listenObj.listenServerOutPut(); + await invoke("run_command", { + args: event.payload.args, + is_server: true + }); + } + }); + this.unListenStartStopServer = unListen; } }; @@ -953,7 +924,22 @@ }); }; + const initAutoStartServer = async () => { + if (mainStore.serverConfig.autoStart && data.coreVersion) { + const args = getServerArgs(); + await listenObj?.unListenServerOutPut?.(); + await invoke("stop_command", { child_id: listenObj.server_thread_id.value || 0 }); + data.serverLog = ""; + await listenObj.listenServerOutPut(); + await invoke("run_command", { + args, + is_server: true + }); + } + }; + let logsTimer: NodeJS.Timeout | null = null; + let serverLogsTimer: NodeJS.Timeout | null = null; onMounted(async () => { // await handleUpdateCore(); //默认不自动更新 @@ -963,8 +949,11 @@ await initStartWinIpBroadcast(); await getCoreVersion(); await listenObj.listenThreadId(); + await listenObj.listenServerThreadId(); await listenObj.listenConfigStart(); + await listenObj.listenStartStopServer(); await initConfigDir(); + await initAutoStartServer(); await initConnectAfterStart(); closePrevent(); }); @@ -973,7 +962,9 @@ unListenAll(); listenObj.unListenReleaseList && listenObj.unListenReleaseList(); listenObj.unListenConfigStart && listenObj.unListenConfigStart(); + listenObj.unListenStartStopServer && listenObj.unListenStartStopServer(); logsTimer && clearInterval(logsTimer); + serverLogsTimer && clearInterval(serverLogsTimer); }); const getArgs = async () => { @@ -1006,24 +997,6 @@ } } - // if (mainStore.enableCreateServer) { - // if (config.relayAllPeerrpc) { - // args.push("--relay-all-peer-rpc"); - // } - // const whiteList = mainStore.ServerWhiteList.trim() - // .split("\n") - // .map(el => el.trim()) - // .filter(el => el) - // .join(" "); - // if (whiteList && mainStore.enableWhiteList) { - // args.push("--relay-network-whitelist", whiteList); - // } - // if (!whiteList && mainStore.enableWhiteList) { - // args.push("--relay-network-whitelist"); - // } - // return args; - // } - if (config.dhcp) { args.push("-d"); } @@ -1194,6 +1167,7 @@ importConfigData.visible = true; } if (command === "create_server") { + await handleShowServerDialog(); // if (data.isStart) { // const [error] = await ElConfirmDanger("切换会停止{action},是否继续?", "提示", { // action: "`联机/服务`", @@ -1301,6 +1275,7 @@ }, 650); }, () => { + logsTimer && clearInterval(logsTimer); data.logVisible = false; } ); @@ -1362,4 +1337,30 @@ } ); }; + + const handleShowServerDialog = async () => { + await etWindows( + "server", + { + title: "自建服务器", + minWidth: 550, + minHeight: 460, + width: 550, + height: 460, + resizable: true, + url: "#/server" + }, + (_, appWindow) => { + data.serverVisible = true; + serverLogsTimer && clearInterval(serverLogsTimer); + serverLogsTimer = setInterval(() => { + appWindow.emitTo("server", "server_logs", { log: data.serverLog, threadId: listenObj.server_thread_id.value }); + }, 650); + }, + () => { + serverLogsTimer && clearInterval(serverLogsTimer); + data.serverVisible = false; + } + ); + }; diff --git a/pages/member.vue b/pages/member.vue index 1249c41..0e1007f 100644 --- a/pages/member.vue +++ b/pages/member.vue @@ -79,7 +79,9 @@ diff --git a/pages/server.vue b/pages/server.vue new file mode 100644 index 0000000..c57c540 --- /dev/null +++ b/pages/server.vue @@ -0,0 +1,128 @@ + + diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 37eada3..59c9aca 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -8,7 +8,8 @@ "member", "cidr", "advance", - "tool" + "tool", + "server" ], "permissions": [ "core:default", diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 5b86223..c3eb65c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -9,10 +9,7 @@ use std::fs::{self, File}; use std::io::{BufRead, BufReader, Write}; use std::os::windows::process::CommandExt; use std::process::{Command, Stdio}; -use std::sync::{ - atomic::{AtomicBool, Ordering}, - mpsc, Arc, -}; +use std::sync::mpsc; use std::{path, thread}; use sysinfo::System; use tauri::tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent}; @@ -142,15 +139,32 @@ fn get_members_by_cli() -> String { .output() { Ok(output) => { - let output_str = String::from_utf8_lossy(&output.stdout); - return output_str.trim().to_string(); + if output.status.success() { + let output_str = String::from_utf8_lossy(&output.stdout); + return output_str.trim().to_string(); + }else { + let output_str = String::from_utf8_lossy(&output.stderr); + log::error!("{}", output_str.trim().to_string()); + return "_EasytierGameCliFailedToConnect_".to_string(); + } } - Err(_e) => return _e.to_string(), + Err(_e) => { + log::error!("get member list error"); + return "".to_string(); + }, } } #[tauri::command(rename_all = "snake_case")] async fn download_easytier_zip(download_url: String, file_name: String) { + let cache_dir_path = get_tool_exe_path(String::from("\\easytier\\cache")); + let cache_file_name = format!("{}\\{}", cache_dir_path, file_name); + let cache_file_name_path = path::Path::new(&cache_file_name); + if cache_file_name_path.exists() { + unzip(cache_file_name_path); + return; + } + let target = format!("{}", download_url); let response = reqwest::get(target) .await @@ -177,10 +191,19 @@ async fn download_easytier_zip(download_url: String, file_name: String) { file.write_all(&content).expect("error to write easytier"); println!("写入完成"); unzip(path); - match fs::remove_file(path) { - Ok(_) => println!("删除zip文件成功"), - Err(_) => log::error!("删除zip文件失败"), + + let cache_dir_path = path::Path::new(&cache_dir_path); + if !cache_dir_path.exists() { + fs::create_dir_all(&cache_dir_path).unwrap(); } + match fs::rename(path, cache_file_name) { + Ok(_) => println!("保存zip文件至easytier/cache成功"), + Err(_) => log::error!("保存zip文件失败"), + } + // match fs::remove_file(path) { + // Ok(_) => println!("删除zip文件成功"), + // Err(_) => log::error!("删除zip文件失败"), + // } } fn unzip(fname: &path::Path) { @@ -228,18 +251,22 @@ fn unzip(fname: &path::Path) { } #[tauri::command(rename_all = "snake_case")] -fn run_command( - app_handle: tauri::AppHandle, - args: Vec, - stop_signal: tauri::State>, -) { +fn run_command(app_handle: tauri::AppHandle, args: Vec, is_server: Option) { + let is_server = is_server.unwrap_or(false); let (tx, rx) = mpsc::channel(); - stop_signal.store(false, Ordering::Relaxed); + let app_handle1 = app_handle.clone(); let app_handle2 = app_handle.clone(); - let stop_signal1 = Arc::clone(&stop_signal); - let stop_signal2 = Arc::clone(&stop_signal); + let args2 = args.clone(); + let mut thread_id_str = "thread-id"; + if is_server { + thread_id_str = "server-thread-id"; + } + let mut command_output_str = "command-output"; + if is_server { + command_output_str = "server-command-output"; + } thread::spawn(move || { let core_path = get_tool_exe_path(String::from("\\easytier\\easytier-core.exe")); // trace, debug, info, warn, error, off @@ -252,36 +279,36 @@ fn run_command( println!("child id: {}", child.id()); app_handle1 - .emit("thread-id", child.id()) + .emit(thread_id_str, child.id()) .expect("failed to emit id event"); app_handle1 - .emit("command-output", args2.join(" ")) + .emit(command_output_str, args2.join(" ")) .expect("error output args"); let stdout = child.stdout.take().expect("failed to capture stdout"); let reader = BufReader::new(stdout); for line in reader.lines() { match line { - Ok(line) => { - tx.send(line).expect("failed to send line"); - } + Ok(line) => match tx.send(line) { + Ok(_) => {} + Err(e) => { + log::error!("error sending line: {}", e); + break; + } + }, Err(e) => { log::error!("error reading line: {}", e); break; } } } - stop_signal1.store(true, Ordering::Relaxed); println!("end"); }); thread::spawn(move || { while let Ok(line) = rx.recv() { - if stop_signal2.load(Ordering::Relaxed) { - break; - } app_handle2 - .emit("command-output", line) + .emit(command_output_str, line) .expect("failed to emit event"); } }); @@ -523,8 +550,6 @@ pub fn run() { // 获取命令行参数 let args: Vec = std::env::args().collect(); - let stop_signal = Arc::new(AtomicBool::new(false)); // 创建一个原子布尔值,用于控制命令的停止 - let stop_signal_clone = Arc::clone(&stop_signal); // 创建一个原子布尔值的克隆,用于传递给命令 let context = tauri::generate_context!(); tauri::Builder::default() .plugin( @@ -592,7 +617,6 @@ pub fn run() { Ok(()) }) - .manage(stop_signal_clone) .invoke_handler(tauri::generate_handler![ run_command, stop_command, diff --git a/stores/index.ts b/stores/index.ts index 1ab7680..538d93e 100644 --- a/stores/index.ts +++ b/stores/index.ts @@ -32,11 +32,11 @@ export default defineStore("main", { netCardMetricValue: 1 //自定义网卡跃点值 }, serverConfig: { - enableCreateServer: false, // 自建服务器 enableWhiteList: true, // 是否启用白名单 - + relayAllPeerrpc: false, // 是否启用所有对等RPC serverWhiteList: "", // 服务器流量转发白名单 - enableListener: true, // 是否启用监听 + // enableListener: true, // 是否启用监听 + autoStart: false, //随软件自启 port: "11010", // 服务器端口 }, cidrEnable: false, @@ -84,6 +84,14 @@ export default defineStore("main", { "config.enableNetCardMetric", "config.netCardMetricValue", + "serverConfig.autoStart", + // 'serverConfig.enableListener', + 'serverConfig.enableWhiteList', + 'serverConfig.relayAllPeerrpc', + 'serverConfig.serverWhiteList', + 'serverConfig.port', + + "cidrEnable", "basePeers", "theme", diff --git a/utils/index.ts b/utils/index.ts index 787d995..5ceaaee 100644 --- a/utils/index.ts +++ b/utils/index.ts @@ -65,6 +65,7 @@ export const ATJ = (promise: Promise, errorExt: string | undefined = undefi }; export const parsePeerInfo = (content: string) => { + if(!content) return []; // 将表格字符串分割成行 const lines = content.split("\n"); diff --git a/zip.js b/zip.js index 4ba9943..4005738 100644 --- a/zip.js +++ b/zip.js @@ -6,7 +6,7 @@ const releaseDir = "./src-tauri/target/release"; const releaseDirEasytier = `${releaseDir}/easytier/`; const releaseExe = `${releaseDir}/easytier-game.exe`; const releaseHelp = `${releaseDir}/帮助.txt`; -const deleteEasytierFiles = ["logs/", "guiLogs/"]; +const deleteEasytierFiles = ["logs/", "guiLogs/", "cache/"]; const pkg = require("./package.json"); const fileName = `easytier-game_windows_x86_64_${pkg.version}.zip`; // 发布包格式 const releaseZipDir = "./release";