Compare commits

...
2 Commits
15 changed files with 2010 additions and 2791 deletions
+8 -1
View File
@@ -39,7 +39,7 @@ html.dark body {
}
.full-label .el-form-item__label {
width: 100%;
width: 100%!important;
padding-right: 0;
}
@@ -50,6 +50,13 @@ html.dark body {
flex: 1;
}
.el-select__selection {
/* display: none!important; */
min-height: 20px!important;
flex-wrap: nowrap!important;
overflow: hidden!important;
}
.el-table {
font-size: 12px !important;
}
+12 -16
View File
@@ -5,7 +5,7 @@ import { uniq, intersection, isNil } from "lodash-es";
import { bounce } from "~/utils";
const b = bounce(600);
export type ConfigServerUrlType = Array<string> | string | null | undefined | null;
export type ConfigServerUrlType = Array<string>;
export const updateConfigJsonBounce = (configJsonSeverUrl?: ConfigServerUrlType) => {
b(async () => {
@@ -22,8 +22,10 @@ export const updateConfigJson = async (configJsonSeverUrl?: ConfigServerUrlType)
if (isNil(configJsonSeverUrl)) {
configJsonSeverUrl = [];
}
const isArray = Array.isArray(configJsonSeverUrl);
const isString = typeof configJsonSeverUrl === "string";
if(typeof(configJsonSeverUrl) == 'string') {
configJsonSeverUrl = [configJsonSeverUrl];
}
configJsonSeverUrl = configJsonSeverUrl || [];
try {
const {
proxyNetworks,
@@ -43,20 +45,14 @@ export const updateConfigJson = async (configJsonSeverUrl?: ConfigServerUrlType)
udpWhitelistEnable,
...otherConfig
} = mainStore.config;
let writeServerUrl: Array<string> | string = serverUrl;
let writeServerUrl: Array<string> = serverUrl;
let writeCustomListenerData: Array<string> = (customListenerData || "").split("\n");
if (isArray) {
writeServerUrl = intersection(
uniq([serverUrl, ...configJsonSeverUrl]).filter(boolean => boolean),
mainStore.basePeers
);
}
if (isString && configJsonSeverUrl) {
writeServerUrl = intersection(
uniq([serverUrl, ...(configJsonSeverUrl as string).split(",")]).filter(boolean => boolean),
mainStore.basePeers
).join(",");
}
writeServerUrl = intersection(
uniq([...serverUrl, ...configJsonSeverUrl]).filter(boolean => boolean),
mainStore.basePeers
);
await writeTextFile(
path,
JSON.stringify({ serverUrl: writeServerUrl, enableCustomListener, customListenerData: writeCustomListenerData, ...otherConfig }, null, 4),
+2 -2
View File
@@ -3,7 +3,7 @@
"private": true,
"author": "leizi97",
"description": "A simple network initiator based on Easytier",
"version": "1.4.8",
"version": "1.4.9",
"scripts": {
"dev": "nuxt dev --dotenv env/.env.dev --host 0.0.0.0",
"build": "nuxt generate --dotenv env/.env.prod",
@@ -31,7 +31,7 @@
"element-plus": "2.10.7",
"less": "4.2.1",
"lodash-es": "^4.17.21",
"nuxt": "3.18.1",
"nuxt": "3.19.2",
"pinia": "3.0.3",
"pinia-plugin-persistedstate": "4.4.1",
"postcss": "^8.4.38",
+65 -28
View File
@@ -45,7 +45,7 @@
</div>
<ElTooltip content="请先停止自建服务和联机后再使用,否则内核被占用的情况下,无法进行内核更换">
<ElBadge
badge-class="!text-[9px] cursor-pointer"
badge-class="!text-[9px] cursor-pointer ml-auto"
:hidden="!haveNewCoreVersion"
:offset="[-5, 6]"
@click.stop="handleCoreManagement"
@@ -68,6 +68,9 @@
<ElSelect
allow-create
filterable
multiple
collapse-tags
collapse-tags-tooltip
placeholder="请选择服务器地址"
default-first-option
v-model="mainStore.config.serverUrl"
@@ -75,14 +78,13 @@
@change="handleServerUrlChange"
>
<template #prefix>
<div :class="mainStore.config.protocol && mainStore.config.protocol.length > 1 ? 'w-[120px]' : 'w-[80px]'">
<div :class="mainStore.config.protocol && mainStore.config.protocol.length > 1 ? 'w-[110px]' : 'w-[80px]'">
<ElSelect
placeholder="协议"
multiple
collapse-tags
@click.stop
v-model="mainStore.config.protocol"
@change="handleServerUrlChange"
>
<ElOption
v-for="item in protocols"
@@ -780,7 +782,7 @@
import { updateConfigJson, updateConfigJsonBounce } from "~/composables/configJson";
import { writeText, readText } from "@tauri-apps/plugin-clipboard-manager";
import { sortedUniq, uniq } from "lodash-es";
import { addQQGroup, supportProtocols, preventSleep, stopPreventSleep, ATJ, copyText, isValidWindowsFileName } from "~/utils";
import { addQQGroup, supportProtocols, preventSleep, stopPreventSleep, ATJ, copyText, isValidWindowsFileName, mixedArray } from "~/utils";
import { ElConfirmDanger, ElConfirmPrimary } from "~/utils/element";
import { getServerArgs } from "@/composables/server";
import { isIPv6 } from "is-ip";
@@ -828,7 +830,7 @@
startLoading: false,
isStart: false,
connectionSuccess: false,
configJsonSeverUrl: "" // 本地保存一次,用于回填config.json
configJsonSeverUrl: [] // 本地保存一次,用于回填config.json
});
const configStart = reactive<{ list: Array<{ path: string; name: string }>; [key: string]: any }>({
@@ -939,30 +941,40 @@
const newBasePeers = [...mainStore.basePeers];
const idx = newBasePeers.indexOf(url);
if (idx >= 0) {
if (mainStore.config.serverUrl === url) {
mainStore.config.serverUrl = "";
const selectIdx = mainStore.config.serverUrl.indexOf(url);
if (selectIdx >= 0) {
mainStore.config.serverUrl.splice(selectIdx, 1);
}
newBasePeers.splice(idx, 1);
}
mainStore.basePeers = uniq([...newBasePeers]);
if (mainStore.basePeers.length > 0) {
mainStore.config.serverUrl = mainStore.basePeers[0];
if (mainStore.basePeers.length > 0 && mainStore.config.serverUrl.length <= 0) {
mainStore.config.serverUrl = [mainStore.basePeers[0]];
}
};
const handleServerUrlChange = () => {
let inputProtocols: string | null = null;
let inputProtocols: string[] = [];
// console.log(mainStore.config.serverUrl);
for (const p of protocols) {
if (mainStore.config.serverUrl.toLowerCase().startsWith(`${p}://`)) {
inputProtocols = p;
break;
for (const url of mainStore.config.serverUrl) {
if (url.toLowerCase().startsWith(`${p}://`)) {
inputProtocols.push(p);
}
}
}
if (inputProtocols) {
mainStore.config.protocol = [inputProtocols];
mainStore.config.serverUrl = mainStore.config.serverUrl.slice(inputProtocols.length + 3);
mainStore.config.protocol = uniq([...mainStore.config.protocol, ...inputProtocols]);
mainStore.config.serverUrl = (mainStore.config.serverUrl || []).map(url => {
for (const p of protocols) {
if (url.toLowerCase().startsWith(`${p}://`)) {
return url.slice(inputProtocols.length + 3);
}
}
return url;
});
}
mainStore.basePeers = uniq([mainStore.config.serverUrl, ...mainStore.basePeers]);
mainStore.basePeers = uniq([...mainStore.config.serverUrl, ...mainStore.basePeers]);
};
const listenObj: { [key: string]: any } = {
@@ -1242,6 +1254,13 @@
}
};
// 多选serverUrl兼容
const compatibleInitServerUrl = async () => {
if (typeof mainStore.config.serverUrl == "string") {
mainStore.config.serverUrl = [mainStore.config.serverUrl || ""];
}
};
const initGuiJson = async () => {
const path = import.meta.env.VITE_CONFIG_FILE_NAME;
const isExists = await exists(path, { baseDir: BaseDirectory.Resource });
@@ -1257,19 +1276,21 @@
// const resultStr = guiJsonStr.replace(regex, "").replace(regex2, "$1");
// console.log(resultStr);
const guiJson = JSON.parse(guiJsonStr);
let saveServerUrl = "";
let saveServerUrl = [];
if (guiJson.serverUrl) {
if (Array.isArray(guiJson.serverUrl)) {
mainStore.basePeers = uniq([...guiJson.serverUrl, ...mainStore.basePeers]);
saveServerUrl = uniq([...guiJson.serverUrl]);
}
if (typeof guiJson.serverUrl === "string") {
mainStore.basePeers = uniq([...guiJson.serverUrl.split(","), ...mainStore.basePeers]);
saveServerUrl = [mainStore.basePeers[0] || ""];
guiJson.serverUrl = guiJson.serverUrl ? guiJson.serverUrl.split(",") : [];
}
saveServerUrl = mainStore.basePeers[0] || "";
} else {
saveServerUrl = mainStore.basePeers.length > 0 ? mainStore.basePeers[0] || "" : "";
saveServerUrl = [mainStore.basePeers.length > 0 ? mainStore.basePeers[0] || "" : ""];
}
data.configJsonSeverUrl = guiJson.serverUrl;
data.configJsonSeverUrl = [...guiJson.serverUrl] as any;
if (guiJson.enableKcpProxy && guiJson.enableQuicProxy) {
// 如果同时开启kcp代理和quic代理,则禁用quic代理
guiJson.enableQuicProxy = false;
@@ -1338,11 +1359,12 @@
});
};
let logsTimer: NodeJS.Timeout | null = null;
let serverLogsTimer: NodeJS.Timeout | null = null;
let cidrTimer: NodeJS.Timeout | null = null;
let logsTimer: number | null = null;
let serverLogsTimer: number | null = null;
let cidrTimer: number | null = null;
onMounted(async () => {
await compatibleInitServerUrl();
await initGuiJson();
await compatibleInitAutoStart();
// await initAutoStart();
@@ -1414,7 +1436,16 @@
return [];
}
}
if (mainStore.config.serverUrl?.length <= 0) {
ElMessage.warning(`请至少选择一个服务器`);
return [];
}
// lodash-es 如何将两个数组的值混合 比如["tcp","udp"] [1,2]
// 混合后["tcp1","tcp2","udp1","udp2"]
if (mainStore.config.protocol?.length <= 0) {
ElMessage.warning(`请至少选择一个协议`);
return [];
}
if (mainStore.config.dhcp) {
args.push("-d");
}
@@ -1431,8 +1462,8 @@
args.push("--ipv4", mainStore.config.ipv4.trim());
}
if (mainStore.config.serverUrl) {
const formatUrl = mainStore.config.serverUrl.replace(/\\/g, "/");
args.push("--peers", ...mainStore.config.protocol.map(protocol => `${protocol}://${formatUrl}`));
const formatUrlArr = mainStore.config.serverUrl.map((url: string) => url.replace(/\\/g, "/"));
args.push("--peers", ...mixedArray(mainStore.config.protocol, formatUrlArr, "://"));
}
if (mainStore.config.enableCustomProtocol) {
const includes = mainStore.config.protocol.includes(mainStore.config.customProtocol);
@@ -1885,7 +1916,13 @@
});
if (!err) {
const payload = JSON.parse(decodeURIComponent(atob(importConfigData.data)));
payload.config.serverUrl = payload?.config?.serverUrl?.trim() || mainStore.config.serverUrl;
payload.config.serverUrl = Array.isArray(payload?.config?.serverUrl)
? payload?.config?.serverUrl
: typeof payload?.config?.serverUrl == "string"
? [payload?.config?.serverUrl.split(",")]
: typeof mainStore.config.serverUrl == "string"
? [mainStore.config.serverUrl]
: [];
mainStore.$patch({
config: {
...mainStore.config,
@@ -1894,7 +1931,7 @@
});
ElMessage.success("导入成功");
importConfigData.visible = false;
mainStore.basePeers = uniq([mainStore.config.serverUrl, ...mainStore.basePeers].filter(el => el.trim()));
mainStore.basePeers = uniq([...(mainStore.config.serverUrl || []), ...mainStore.basePeers].filter(el => el.trim()));
} else {
console.error(err);
if (err !== "cancel") {
+7 -1
View File
@@ -12,7 +12,10 @@
@scroll="handleScroll"
class="overflow-auto"
>
<div v-if="!data.log || data.log.length <= 0" class="p-[5px]">
<div
v-if="!data.log || data.log.length <= 0"
class="p-[5px]"
>
<ElText type="info">等待日志中请先'启动联机'...</ElText>
</div>
<div
@@ -106,6 +109,9 @@
.split("\n")
.filter(text => text.trim())
.map((text, idx) => {
if (idx === 0) {
return { id: `${text}-${idx}`, text, type: "primary" };
}
const toLowerCaseText = text.toLocaleLowerCase();
return {
id: `${text}-${idx}`,
+34 -34
View File
@@ -214,7 +214,7 @@
};
// 字节数据排序函数
const sortBytes = (a: any, b: any, field: string = 'rx_bytes'): number => {
const sortBytes = (a: any, b: any, field: string = "rx_bytes"): number => {
const parseBytes = (bytesStr: string): number => {
if (!bytesStr || bytesStr === "-" || bytesStr === "") {
return 0;
@@ -222,7 +222,7 @@
// 统一转换为小写并去除空格
const cleanStr = bytesStr.toLowerCase().trim();
// 使用正则表达式匹配数字和单位
const match = cleanStr.match(/^(\d+(?:\.\d+)?)\s*([a-z]*)$/);
if (!match) {
@@ -238,30 +238,30 @@
// 根据单位转换为字节数
switch (unit) {
case 'b':
case 'byte':
case 'bytes':
case '':
case "b":
case "byte":
case "bytes":
case "":
return value;
case 'k':
case 'kb':
case 'kib':
case "k":
case "kb":
case "kib":
return value * 1024;
case 'm':
case 'mb':
case 'mib':
case "m":
case "mb":
case "mib":
return value * 1024 * 1024;
case 'g':
case 'gb':
case 'gib':
case "g":
case "gb":
case "gib":
return value * 1024 * 1024 * 1024;
case 't':
case 'tb':
case 'tib':
case "t":
case "tb":
case "tib":
return value * 1024 * 1024 * 1024 * 1024;
case 'p':
case 'pb':
case 'pib':
case "p":
case "pb":
case "pib":
return value * 1024 * 1024 * 1024 * 1024 * 1024;
default:
return value; // 未知单位当作字节处理
@@ -305,11 +305,10 @@
["tx_bytes", "传输"]
];
let timer: NodeJS.Timeout | null = null;
let timer: number | null = null;
const listenOutput = async () => {
const [error, member] = await ATJ(invoke<string>("get_members_by_cli"));
// if(!member) return;
if (error) {
data.member = [];
return "";
@@ -329,17 +328,18 @@
}
return;
}
const peerInfo = parseCliInfo(member);
peerInfo.forEach(value => {
if (value.cost === "Local") {
value.cost = "本机";
}
if (value.ipv4 && value.ipv4.includes("/")) {
value.ipv4 = value.ipv4.split("/")[0];
}
});
// console.log(peerInfo)
data.member = peerInfo;
// const peerInfo = parseCliInfo(member);
// peerInfo.forEach(value => {
// if (value.cost === "Local") {
// value.cost = "本机";
// }
// if (value.ipv4 && value.ipv4.includes("/")) {
// value.ipv4 = value.ipv4.split("/")[0];
// }
// });
// data.member = peerInfo;
data.member = JSON.parse(member);
startTimer();
};
+1175 -2164
View File
File diff suppressed because it is too large Load Diff
+661 -531
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "easytier-game"
version = "1.4.8"
version = "1.4.9"
homepage = "https://github.com/EasyTier/EasyTier"
repository = "https://github.com/EasyTier/EasytierGame"
description = "A simple network initiator based on Easytier"
Binary file not shown.
Binary file not shown.
+9 -1
View File
@@ -13,7 +13,6 @@ use sysinfo::System;
use tauri::tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent};
use tauri::Emitter;
use tauri::Manager;
use windows::Win32::System::Variant::VARIANT;
use windows::core::{Interface, BSTR};
use windows::Win32::Foundation::VARIANT_BOOL;
use windows::Win32::NetworkManagement::IpHelper::{
@@ -22,6 +21,7 @@ use windows::Win32::NetworkManagement::IpHelper::{
use windows::Win32::Networking::WinSock::AF_UNSPEC;
use windows::Win32::System::Com::*;
use windows::Win32::System::TaskScheduler::*;
use windows::Win32::System::Variant::VARIANT;
// use std::ffi::CStr;
use windows::Win32::System::Power::SetThreadExecutionState;
@@ -273,6 +273,8 @@ struct MyResponse {
async fn get_members_by_cli() -> String {
let cli_path = get_tool_exe_path("\\easytier\\easytier-cli.exe");
match tokioCommand::new(&cli_path)
.arg("-o")
.arg("json")
.arg("peer")
.arg("list")
.creation_flags(0x08000000)
@@ -351,6 +353,12 @@ async fn download_easytier_zip(
let path = path::Path::new(&file_path);
println!("download easytier to {}", path.display());
if path.exists() {
match fs::remove_file(path) {
Ok(_) => println!("删除上次下载的Zip文件成功"),
Err(_) => log::error!("删除上次下载Zip的文件失败"),
}
}
let mut file = match File::create(&path) {
Err(why) => panic!("couldn't create {}", why),
+2 -2
View File
@@ -1,7 +1,7 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "easytier-game",
"version": "1.4.8",
"version": "1.4.9",
"identifier": "com.tauri.easytier-game",
"build": {
@@ -12,7 +12,7 @@
"app": {
"windows": [
{
"title": "easytier-game 1.4.8",
"title": "easytier-game 1.4.9",
"label": "main",
"minWidth": 340,
"width": 340,
+2 -2
View File
@@ -7,7 +7,7 @@ const store = defineStore("main", {
return {
config: {
protocol: ["tcp"], // 网络协议
serverUrl: "public.easytier.top:11010",
serverUrl: ["public.easytier.top:11010"],
networkName: "",
networkPassword: "",
hostname: "",
@@ -21,7 +21,7 @@ const store = defineStore("main", {
autoStart: false, // 是否自动启动
connectAfterStart: false, //软件打开后,是否自动连接
disableIpv6: false, // 是否禁用IPv6
port: "11010", // 监听端口号
port: "0", // 监听端口号
enableCustomListener: true, // 是否自定义监听
customListenerData: "tcp://0.0.0.0:11010\nudp://0.0.0.0:11010", // 自定义监听地址
disbleListenner: false, // 是否禁用监听
+32 -8
View File
@@ -2,11 +2,17 @@ import { invoke } from "@tauri-apps/api/core";
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
import { open } from "@tauri-apps/plugin-shell";
import { ElMessage } from "element-plus";
import { flatMap, map } from "lodash-es";
export const ENV = import.meta.env;
export const mixedArray = (a: Array<string>, b: Array<string>, split:string="") => {
return flatMap(a, protocol => map(b, port => `${protocol}${split}${port}`));
};
//防抖
export const bounce = (time = 3000) => {
let bounceTimer: NodeJS.Timeout | null = null;
let bounceTimer: number | null = null;
return (cb: Function) => {
bounceTimer && clearTimeout(bounceTimer);
bounceTimer = setTimeout(() => {
@@ -73,7 +79,7 @@ export const supportProtocols = () => {
return _supportProtocols.slice();
};
let _prevent_timer: NodeJS.Timeout | null = null;
let _prevent_timer: number | null = null;
let _prevent_timer_count = 15; // 秒
export const preventSleep = () => {
_prevent_timer && clearInterval(_prevent_timer);
@@ -127,17 +133,35 @@ export const parseCliInfo = (content: string) => {
};
export function isValidWindowsFileName(name: string) {
if(name.length > 255) return false;
if (name.length > 255) return false;
const reserved_names = [
"CON", "PRN", "AUX", "NUL",
"COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8", "COM9",
"LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9"
"CON",
"PRN",
"AUX",
"NUL",
"COM1",
"COM2",
"COM3",
"COM4",
"COM5",
"COM6",
"COM7",
"COM8",
"COM9",
"LPT1",
"LPT2",
"LPT3",
"LPT4",
"LPT5",
"LPT6",
"LPT7",
"LPT8",
"LPT9"
];
if(reserved_names.includes(name.toUpperCase())) {
if (reserved_names.includes(name.toUpperCase())) {
return false;
}
return /^[^<>:"/\\|?*\x00-\x1F]+[^<>:"/\\|?*\x00-\x1F .]$/g.test(name);
}
export const addQQGroup = () => {