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 @@
+
+
+
+
+
+
+ 白名单
+
+
+
+
+ 自动启动
+
+
+ 转发所有对等节点的RPC数据包
+
+
+
+
+
+
+
+
+
+
+ {{ !data.isStart ? "启动服务器" : "停止服务器" }}
+
+
+
+
+
+
+
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";