Compare commits

..
4 Commits
Author SHA1 Message Date
黑先森 f9e0b8e1db 新增 + 优化
+ 高级选项 增加2.1.0内核功能 压缩算法和ipv6监听地址
+ 增加服务器地址栏复制按钮
+ 更新内核为最新版本
+ config.json 新增压缩算法配置 可参考 config_template.json
2024-12-20 16:10:57 +08:00
黑先森 582a4ab819 增加子网代理使用的判断 2024-12-19 17:23:28 +08:00
黑先森 0fde619516 优化对githubApi的请求,减少被过滤无法获取的情况发生 2024-12-19 17:11:03 +08:00
黑先森 410f0dc32b 新增+修复+优化
+ 新增子网代理页面 表格查看代理情况
+ 修复子网代理启动联机被覆盖的bug
+ 优化部分代码逻辑
2024-12-19 16:41:09 +08:00
18 changed files with 368 additions and 108 deletions
+15 -15
View File
@@ -9,26 +9,26 @@
import { warn, debug, trace, info, error } from "@tauri-apps/plugin-log";
function forwardConsole(fnName: "log" | "debug" | "info" | "warn" | "error", logger: (message: string) => Promise<void>) {
const original = console[fnName];
console[fnName] = (message) => {
console[fnName] = message => {
original(message);
if (import.meta.env.PROD) {
try {
if (typeof message === "string") {
logger(message);
} else {
logger(JSON.stringify(message));
}
} catch (e) {
logger(`${message}`);
try {
if (typeof message === "string") {
logger(message);
} else {
logger(JSON.stringify(message));
}
} catch (e) {
logger(`${message}`);
}
};
}
forwardConsole("log", info);
forwardConsole("debug", debug);
forwardConsole("info", info);
forwardConsole("warn", warn);
forwardConsole("error", error);
if (import.meta.env.PROD) {
forwardConsole("log", info);
forwardConsole("debug", debug);
forwardConsole("info", info);
forwardConsole("warn", warn);
forwardConsole("error", error);
}
const mainStore = useMainStore();
// console.log(mainStore.theme)
+3
View File
@@ -0,0 +1,3 @@
<template>
<ElTag size="small" type="danger">内核 >= {{ $attrs.version }}</ElTag>
</template>
+2
View File
@@ -26,6 +26,8 @@ export const updateConfigJson = async (configJsonSeverUrl: Array<string> | strin
enableCustomListener,
enablePreventSleep,
customListenerData,
customListenerV6Data,
enableCustomListenerV6,
...otherConfig
} = mainStore.config;
let writeServerUrl: Array<string> | string = serverUrl;
+1 -1
View File
@@ -3,7 +3,7 @@
"private": true,
"author": "leizi97",
"description": "A simple network initiator based on Easytier",
"version": "1.3.1",
"version": "1.3.2",
"scripts": {
"dev": "nuxt dev --dotenv env/.env.dev --host 0.0.0.0",
"build": "nuxt generate --dotenv env/.env.prod",
+50 -7
View File
@@ -1,9 +1,9 @@
<template>
<div class="h-full overflow-auto flex flex-col items-start px-[25px]">
<div class="flex h-full flex-col items-start overflow-auto px-[25px]">
<div><ElCheckbox v-model="mainStore.config.disableIpv6">不使用IPv6</ElCheckbox></div>
<div class="flex items-center gap-[15px] flex-nowrap">
<div class="flex items-center gap-[5px] flex-nowrap">
<ElCheckbox v-model="mainStore.config.enableCustonProtocol">自定义联机时使用的默认协议</ElCheckbox>
<div class="flex flex-nowrap items-center gap-[15px]">
<div class="flex flex-nowrap items-center gap-[5px]">
<ElCheckbox v-model="mainStore.config.enableCustomProtocol">自定义联机时使用的默认协议</ElCheckbox>
<ElTooltip content="如果没有支持该协议的节点地址,程序会自动处理,请放心使用">
<ElIcon><QuestionFilled /></ElIcon>
</ElTooltip>
@@ -24,7 +24,7 @@
</ElSelect>
</div>
</div>
<div class="flex items-center gap-[15px] flex-nowrap">
<div class="flex flex-nowrap items-center gap-[15px]">
<ElCheckbox v-model="mainStore.config.saveErrorLog">输出日志到本地</ElCheckbox>
<div class="w-[140px]">
<ElSelect
@@ -49,7 +49,7 @@
</ElButton>
</div>
<div><ElCheckbox v-model="mainStore.config.connectAfterStart">软件启动后自动"启动联机"(搭配开机自启无感联机)</ElCheckbox></div>
<div class="flex items-center gap-[15px] flex-nowrap">
<div class="flex flex-nowrap items-center gap-[15px]">
<ElCheckbox v-model="mainStore.createConfigInEasytier">自动生成界面配置文件easytier/config.json</ElCheckbox>
<ElButton
@click="openConfigJsonDir"
@@ -86,7 +86,7 @@
:disabled="mainStore.config.disbleListenner"
v-model="mainStore.config.enableCustomListener"
>
自定义监听地址
自定义IPV4监听地址
</ElCheckbox>
<ElButton
size="small"
@@ -124,6 +124,27 @@
</template>
</ElDialog>
</div>
<div class="flex items-center gap-[10px]">
<ElCheckbox
:disabled="mainStore.config.disbleListenner"
v-model="mainStore.config.enableCustomListenerV6"
>
自定义IPV6监听地址
</ElCheckbox>
<div class="w-[200px]">
<ElInput
:disabled="mainStore.config.disbleListenner || mainStore.config.enableCustomListenerV6"
size="small"
:maxlength="100"
v-model="mainStore.config.customListenerV6Data"
placeholder="请输入自定义IPV6监听地址"
/>
</div>
<ElTooltip content="例如:tcp://[::]:11010,如果未设置,将在随机UDP端口上监听">
<ElIcon><QuestionFilled /></ElIcon>
</ElTooltip>
<CoreVersionWarning version="2.1.0" />
</div>
<ElDivider />
<div class="flex items-center gap-[10px]">
<ElCheckbox v-model="mainStore.config.devName">自定义网卡名</ElCheckbox>
@@ -165,6 +186,27 @@
<div><ElCheckbox v-model="mainStore.config.enablExitNode">允许此节点成为出口节点</ElCheckbox></div>
<div><ElCheckbox v-model="mainStore.config.disableEncryption">禁用对等节点通信的加密</ElCheckbox></div>
<div><ElCheckbox v-model="mainStore.config.multiThread">启用多线程运行</ElCheckbox></div>
<div class="flex items-center gap-[10px]">
<ElText>压缩算法</ElText>
<div class="w-[90px]">
<ElSelect
size="small"
v-model="mainStore.config.compression"
placeholder="请选择压缩算法"
>
<ElOption
v-for="item in [
['none', '默认'],
['zstd', 'zstd算法']
]"
:key="item[0]"
:label="item[1]"
:value="item[0]"
></ElOption>
</ElSelect>
</div>
<CoreVersionWarning version="2.1.0" />
</div>
<ElDivider />
<div><ElCheckbox v-model="mainStore.config.useSmoltcp">为子网代理启用smoltcp堆栈</ElCheckbox></div>
@@ -185,6 +227,7 @@
import { reactive } from "vue";
import { dataSubscribe } from "@/composables/windows";
import { supportProtocols } from "~/utils";
import CoreVersionWarning from "@/components/CoreVersionWarning.vue";
const protocols = supportProtocols();
const listenerDialogData = reactive<{ [key: string]: any }>({
+97 -3
View File
@@ -1,6 +1,9 @@
<template>
<div class="h-full flex flex-col gap-[10px]">
<ElRadioGroup v-model="mainStore.cidrEnable">
<div class="flex h-full flex-col gap-[10px]">
<ElRadioGroup
:disabled="!data.isSwitchEnable"
v-model="mainStore.cidrEnable"
>
<ElRadioButton :value="true">开启</ElRadioButton>
<ElRadioButton :value="false">关闭</ElRadioButton>
</ElRadioGroup>
@@ -9,16 +12,107 @@
placeholder="例如: 192.168.1.0/24 一行一个"
v-model="mainStore.config.proxyNetworks"
type="textarea"
:rows="30"
:rows="5"
resize="none"
></ElInput>
<ElTable
stripe
border
empty-text="暂无数据"
:data="data.route"
class="mt-[5px]"
>
<ElTableColumn
sortable
width="140"
label="主机名"
prop="hostname"
></ElTableColumn>
<ElTableColumn
width="120"
sortable
label="虚拟网IP"
prop="ipv4"
></ElTableColumn>
<ElTableColumn
sortable
label="被代理的子网"
prop="proxy_cidrs"
></ElTableColumn>
</ElTable>
</div>
</div>
</template>
<script setup lang="ts">
import useMainStore from "@/stores/index";
import { dataSubscribe } from "@/composables/windows";
import { invoke } from "@tauri-apps/api/core";
import { reactive, onMounted, onBeforeUnmount } from "vue";
import { ATJ, parseCliInfo } from "@/utils";
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
const mainStore = useMainStore();
const data = reactive<{ [key: string]: any }>({
route: [],
isStart: false,
isSwitchEnable: false,
isRunning: false
});
const listenOutput = async () => {
data.isRunning = true;
const [error, member] = await ATJ(invoke<string>("get_route_by_cli"));
data.isRunning = false;
// if(!member) return;
if (error) {
data.route = [];
return "";
}
if (member === "_EasytierGameCliFailedToConnect_") {
data.route = [];
await listenOutput();
return;
}
const routeInfo = parseCliInfo(member);
routeInfo.forEach(value => {
if (value.cost === "Local") {
value.cost = "本机";
}
if (value.ipv4 && value.ipv4.includes("/")) {
value.ipv4 = value.ipv4.split("/")[0];
}
});
data.route = routeInfo;
};
const listenStart = async () => {
const unListen = await listen<boolean>("route", async event => {
data.isStart = event.payload;
if (data.isStart) {
data.isSwitchEnable = false;
if (mainStore.cidrEnable && !data.isRunning) {
await listenOutput();
} else {
data.route = [];
}
} else {
data.isSwitchEnable = true;
data.route = [];
}
});
return unListen;
};
let unlistenStart: UnlistenFn | null = null;
onMounted(async () => {
unlistenStart = await listenStart();
});
onBeforeUnmount(() => {
unlistenStart && unlistenStart();
});
dataSubscribe(async (...a) => {
return { cidrEnable: mainStore.cidrEnable, config: { ...mainStore.config } };
});
+103 -39
View File
@@ -83,15 +83,34 @@
:value="item"
>
<div class="flex max-w-[calc(100vw-62px)] flex-nowrap items-center gap-[20px] overflow-hidden">
<ElTooltip v-if="item && item.length > 30" placement="top" :content="item || '-'">
<ElTooltip
v-if="item && item.length > 26"
placement="top"
:content="item || '-'"
>
<p class="truncate">{{ item }}</p>
</ElTooltip>
<p v-else class="truncate">{{ item }}</p>
<p
v-else
class="truncate"
>
{{ item }}
</p>
<div class="ml-auto flex-shrink-0">
<ElButton
@click.stop="copyServerUrl(item)"
circle
size="small"
title="复制"
:icon="CopyDocument"
></ElButton>
<ElButton
@click.stop="handleDeleteServerUrl(item)"
round
circle
size="small"
:icon="Delete"
title="删除"
type="danger"
></ElButton>
</div>
@@ -310,17 +329,24 @@
增强工具
</ElButton>
</div>
<ElBadge badge-class="!text-[9px] cursor-pointer" :hidden="!data.hasNewVersion" :offset="[6, 7]" value="N">
<ElBadge
badge-class="!text-[9px] cursor-pointer"
:hidden="!data.hasNewVersion"
:offset="[6, 7]"
value="N"
>
<!-- <ElTooltip :disabled="!data.hasNewVersion" content="有新版发布了!"> -->
<ElLink
class="ml-[8px] truncate pb-[2px] !text-[9px]"
type="info"
:underline="false"
@click="open('https://github.com/EasyTier/EasytierGame')"
>EasytierGame主页</ElLink>
<ElLink
class="ml-[8px] truncate pb-[2px] !text-[9px]"
type="info"
:underline="false"
@click="open('https://github.com/EasyTier/EasytierGame')"
>
EasytierGame主页
</ElLink>
<!-- </ElTooltip> -->
</ElBadge>
</ElBadge>
</div>
</div>
</div>
@@ -346,7 +372,10 @@
>
刷新
</ElButton>
<ElTooltip placement="top" content="打开内核缓存目录">
<ElTooltip
placement="top"
content="打开内核缓存目录"
>
<ElButton
:icon="Folder"
size="small"
@@ -510,7 +539,8 @@
Tools,
MagicStick,
SetUp,
Folder
Folder,
CopyDocument
} from "@element-plus/icons-vue";
import { reactive, onBeforeUnmount, onMounted, ref } from "vue";
import { useTray, setTrayRunState, setTrayTooltip, checkNewVersion } from "~/composables/tray";
@@ -518,15 +548,15 @@
import { initStartWinIpBroadcast } from "~/composables/netcard";
import useMainStore from "@/stores/index";
import { ElMessage, ElMessageBox } from "element-plus";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { getAllWebviewWindows } from "@tauri-apps/api/webviewWindow";
import { getCurrentWindow, type Window } from "@tauri-apps/api/window";
import { getAllWebviewWindows, WebviewWindow } from "@tauri-apps/api/webviewWindow";
import etWindows from "@/composables/windows";
import { resourceDir as getResourceDir, join } from "@tauri-apps/api/path";
import { readDir, exists, mkdir, BaseDirectory, readTextFile } from "@tauri-apps/plugin-fs";
import { readDir, exists, mkdir, BaseDirectory, readTextFile, readFile } from "@tauri-apps/plugin-fs";
import { updateConfigJson } from "~/composables/configJson";
import { writeText, readText } from "@tauri-apps/plugin-clipboard-manager";
import { sortedUniq, uniq } from "lodash-es";
import { bounce, addQQGroup, supportProtocols, preventSleep, stopPreventSleep } from "~/utils";
import { bounce, addQQGroup, supportProtocols, preventSleep, stopPreventSleep, ATJ } from "~/utils";
import { ElConfirmDanger, ElConfirmPrimary } from "~/utils/element";
import { getServerArgs } from "@/composables/server";
@@ -590,6 +620,17 @@
data: ""
});
const copyServerUrl = async (serverUrl: string) => {
const [err, _] = await ATJ(writeText(serverUrl));
if (!err) {
ElMessage.success("地址已复制");
} else {
console.error(err);
ElMessage.success("复制失败");
}
};
const closePrevent = async () => {
const appWindow = getCurrentWindow();
if (appWindow.label == "main") {
@@ -648,6 +689,17 @@
mainStore.basePeers = uniq([mainStore.config.serverUrl, ...mainStore.basePeers]);
};
// 手动触发持久化数据
const persist = async (appWindow: Window) => {
try {
appWindow.emitTo({ kind: "Any" }, "global-main-store", { store: { ...mainStore.$state } });
mainStore.$persist();
} catch (err) {
console.error(err);
ElMessage.error("手动持久数据失败");
}
};
const listenObj: { [key: string]: any } = {
unListenOutPut: null,
unListenThreadId: null,
@@ -742,19 +794,18 @@
const appWindow = getCurrentWindow();
const unListen = await listen("config", event => {
const ipv4 = config.ipv4;
console.log(event.payload)
mainStore.$patch(event.payload as any);
config.ipv4 = ipv4;
if(mainStore.config.enablePreventSleep) {
if (mainStore.config.enablePreventSleep) {
preventSleep();
}else {
} else {
stopPreventSleep();
}
appWindow.emitTo({ kind: "Any" }, "global-main-store", { store: { ...mainStore.$state } });
persist(appWindow);
});
const unListen2 = mainStore.$subscribe(() => {
appWindow.emitTo({ kind: "Any" }, "global-main-store", { store: { ...mainStore.$state } });
})
persist(appWindow);
});
this.unListenConfigStart = [unListen, unListen2];
},
async listenStartStopServer() {
@@ -894,9 +945,11 @@
const path = import.meta.env.VITE_CONFIG_FILE_NAME;
const isExists = await exists(path, { baseDir: BaseDirectory.Resource });
if (isExists) {
const guiJsonStr = await readTextFile(path, { baseDir: BaseDirectory.Resource });
if (guiJsonStr) {
const guiJsonStrUint8 = await readFile(path, { baseDir: BaseDirectory.Resource });
if (guiJsonStrUint8) {
try {
const decoder = new TextDecoder("utf-8");
const guiJsonStr = decoder.decode(guiJsonStrUint8);
const regex = /^(\s*\/\/.*)/gm;
const regex2 = /(,?)\s*\/\/.*(?=\n|$|\r\n)/gm;
const resultStr = guiJsonStr.replace(regex, "").replace(regex2, "$1");
@@ -926,7 +979,7 @@
}
} catch (err) {
console.error(err);
ElMessage.error(`config.json格式错误`);
ElMessage.error(`config.json格式或编码错误`);
} finally {
mainStore.$patch({
createConfigInEasytier: true // 发现本地存在config.json 默认启用该功能
@@ -960,9 +1013,9 @@
const initPreventSleep = async () => {
if (mainStore.config.enablePreventSleep) {
preventSleep();
preventSleep();
}
}
};
const mountedShow = async () => {
const args = await getMatches();
@@ -975,6 +1028,7 @@
let logsTimer: NodeJS.Timeout | null = null;
let serverLogsTimer: NodeJS.Timeout | null = null;
let cidrTimer: NodeJS.Timeout | null = null;
onMounted(async () => {
await initGuiJson();
@@ -1003,6 +1057,7 @@
listenObj.unListenStartStopServer && listenObj.unListenStartStopServer();
logsTimer && clearInterval(logsTimer);
serverLogsTimer && clearInterval(serverLogsTimer);
cidrTimer && clearInterval(cidrTimer);
stopPreventSleep();
});
@@ -1049,13 +1104,13 @@
args.push("--network-secret", config.networkPassword);
}
if (config.ipv4) {
args.push("--ipv4", config.ipv4);
args.push("--ipv4", config.ipv4.trim());
}
if (config.serverUrl) {
const formatUrl = config.serverUrl.replace(/\\/g, "/");
args.push("--peers", ...config.protocol.map(protocol => `${protocol}://${formatUrl}`));
}
if (config.enableCustonProtocol) {
if (config.enableCustomProtocol) {
const includes = config.protocol.includes(config.customProtocol);
if (includes) {
args.push("--default-protocol", config.customProtocol);
@@ -1080,17 +1135,22 @@
args.push("-l", ...customListener);
}
}
if (!config.disbleListenner && config.enableCustomListenerV6 && config.customListenerV6Data){
args.push("--ipv6-listener", config.customListenerV6Data)
}
if (!config.disbleListenner && !config.enableCustomListener && config.port) {
args.push("-l", config.port);
}
if (mainStore.cidrEnable && config.proxyNetworks) {
// console.error(config.proxyNetworks);
const reg = /\d+\.\d+\.\d+\.\d+\/\d+/g;
const formatProxyNetworks = config.proxyNetworks
.split("\n")
.map(item => item.trim())
.filter(item => item && reg.test(item));
args.push("--proxy-networks", ...formatProxyNetworks);
let formatProxyNetworks = config.proxyNetworks.trim().split("\n");
const newformatProxyNetworks = formatProxyNetworks
.map(el => el.trim())
.filter(cidr => {
return /^\d+\.\d+\.\d+\.\d+\/\d+$/g.test(cidr);
});
if (newformatProxyNetworks.length > 0) {
args.push("--proxy-networks", ...newformatProxyNetworks);
}
config.proxyNetworks = formatProxyNetworks.join("\n");
}
if (config.disableEncryption) {
@@ -1168,7 +1228,6 @@
data.startLoading = true;
await unListenAll();
await listenObj.listenOutput();
await invoke("run_command", {
args
});
@@ -1335,8 +1394,13 @@
},
(_, appWindow) => {
data.cidrVisible = true;
cidrTimer && clearInterval(cidrTimer);
cidrTimer = setInterval(() => {
appWindow.emitTo({ kind: "WebviewWindow", label: "cidr" }, "route", data.isStart);
}, 1000);
},
() => {
cidrTimer && clearInterval(cidrTimer);
data.cidrVisible = false;
}
);
+2 -2
View File
@@ -16,8 +16,8 @@
});
const listenStart = async () => {
const unListen = await listen("logs", event => {
data.log = (event.payload as string) || "";
const unListen = await listen<string>("logs", event => {
data.log = event.payload || "";
});
return unListen;
};
+20 -11
View File
@@ -1,7 +1,12 @@
<template>
<div class="overflow-auto h-full">
<div class="h-full overflow-auto">
<div class="w-[1200px]">
<ElText size="small" v-if="!data.member || data.member.length <= 0">{{ "等待成员信息中..." }}</ElText>
<ElText
size="small"
v-if="!data.member || data.member.length <= 0"
>
{{ "等待成员信息中..." }}
</ElText>
<ElTable
stripe
border
@@ -14,8 +19,12 @@
label="主机名"
prop="hostname"
>
<template #default="{row}">
{{ (row.hostname || "").toLowerCase().includes('publicserver') ? (row.hostname || "").replace("PublicServer", "服务器") : (row.hostname || "-") }}
<template #default="{ row }">
{{
(row.hostname || "").toLowerCase().includes("publicserver")
? (row.hostname || "").replace("PublicServer", "服务器")
: row.hostname || "-"
}}
</template>
</ElTableColumn>
<ElTableColumn
@@ -96,9 +105,9 @@
<script setup lang="ts">
import { invoke } from "@tauri-apps/api/core";
import { reactive, onMounted, onBeforeUnmount } from "vue";
import { ATJ, parsePeerInfo } from "@/utils";
import { ATJ, parseCliInfo } from "@/utils";
import { ElConfirmDanger } from "~/utils/element";
import { getCurrentWindow } from "@tauri-apps/api/window";
import { getCurrentWindow } from "@tauri-apps/api/window";
// enum NatType {
// // has NAT; but own a single public IP, port is not changed
// Unknown = 0;
@@ -126,7 +135,7 @@ import { getCurrentWindow } from "@tauri-apps/api/window";
};
type natKyes = keyof typeof natMaps;
const data = reactive<{ member: {hostname: string, cost: string, ipv4: string, lat_msg: string, loss_rate: string, nat_type: string}[] }>({
const data = reactive<{ member: { hostname: string; cost: string; ipv4: string; lat_msg: string; loss_rate: string; nat_type: string }[] }>({
member: []
});
@@ -139,8 +148,8 @@ import { getCurrentWindow } from "@tauri-apps/api/window";
["nat_type", "NAT类型"],
["version", "版本"],
["tunnel_proto", "隧道协议"],
['rx_bytes', '接收'],
['tx_bytes', '传输']
["rx_bytes", "接收"],
["tx_bytes", "传输"]
];
let timer: NodeJS.Timeout | null = null;
@@ -167,7 +176,7 @@ import { getCurrentWindow } from "@tauri-apps/api/window";
}
return;
}
const peerInfo = parsePeerInfo(member);
const peerInfo = parseCliInfo(member);
peerInfo.forEach(value => {
if (value.cost === "Local") {
value.cost = "本机";
@@ -191,7 +200,7 @@ import { getCurrentWindow } from "@tauri-apps/api/window";
const stopTimer = () => {
timer && clearTimeout(timer);
timer = null;
}
};
onMounted(async () => {
await listenOutput();
+2 -1
View File
@@ -1227,10 +1227,11 @@ checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
[[package]]
name = "easytier-game"
version = "1.3.1"
version = "1.3.2"
dependencies = [
"log",
"planif",
"rand 0.8.5",
"reqwest",
"serde",
"serde_json",
+2 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "easytier-game"
version = "1.3.1"
version = "1.3.2"
homepage = "https://github.com/EasyTier/EasyTier"
repository = "https://github.com/EasyTier/EasytierGame"
description = "A simple network initiator based on Easytier"
@@ -40,6 +40,7 @@ planif = { git = "https://github.com/mattrobineau/planif", tag = "1.0.1" }
whoami = "1.5.2"
tauri-plugin-fs = "2"
tauri-plugin-clipboard-manager = "2.0.2"
rand = "0.8.5"
tokio = { version = "1.41.1", features = ["process"] }
# prost = "0.13"
# prost-types = "0.13"
Binary file not shown.
Binary file not shown.
+54 -4
View File
@@ -26,6 +26,29 @@ use windows::Win32::System::Power::{
};
use tokio::process::Command as tokioCommand;
use rand::Rng;
fn generate_random_user_agent() -> String {
let user_agents = vec![
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{version} Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/{version} Safari/605.1.15",
"Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/{version} Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Linux; Android 10; SM-G973F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/{version} Mobile Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:{version}) Gecko/20100101 Firefox/{version}",
];
let mut rng = rand::thread_rng();
let template = user_agents[rng.gen_range(0..user_agents.len())];
// 生成随机版本号
let version_major: u8 = rng.gen_range(70..90);
let version_minor: u8 = rng.gen_range(0..10);
let version_patch: u8 = rng.gen_range(0..10);
let version = format!("{}.{}.{}", version_major, version_minor, version_patch);
// 替换模板中的版本号占位符
template.replace("{version}", &version)
}
#[tauri::command(rename_all = "snake_case")]
fn prevent_sleep() -> bool {
@@ -67,11 +90,11 @@ pub async fn fetch_releases() -> Result<Vec<Release>, Error> {
let client = Client::new();
// 构建请求的URL
let url = format!("https://api.github.com/repos/easytier/easytier/releases",);
let user_agent = generate_random_user_agent();
// 发送HTTP GET请求
let response = client
.get(&url)
.header("User-Agent", "Tauri-fetch")
.header("User-Agent", user_agent)
.send()
.await?;
// 反序列化响应体为Release列表
@@ -84,11 +107,11 @@ async fn fetch_game_releases() -> [String; 2] {
let client = Client::new();
// 构建请求的URL
let url = format!("https://api.github.com/repos/Easytier/EasytierGame/releases",);
let user_agent = generate_random_user_agent();
// 发送HTTP GET请求
if let Ok(response) = client
.get(&url)
.header("User-Agent", "Tauri-fetch")
.header("User-Agent", user_agent)
.send()
.await
{
@@ -221,6 +244,32 @@ async fn get_members_by_cli() -> String {
}
}
#[tauri::command(rename_all = "snake_case")]
async fn get_route_by_cli() -> String {
let cli_path = get_tool_exe_path("\\easytier\\easytier-cli.exe");
match tokioCommand::new(&cli_path)
.arg("route")
.creation_flags(0x08000000)
.output()
.await
{
Ok(output) => {
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) => {
log::error!("get route 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("\\easytier\\cache");
@@ -685,6 +734,7 @@ pub fn run() {
fetch_game_releases,
download_easytier_zip,
get_members_by_cli,
get_route_by_cli,
search_pid_by_pname,
get_exe_directory,
spawn_autostart,
+2 -2
View File
@@ -1,7 +1,7 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "easytier-game",
"version": "1.3.1",
"version": "1.3.2",
"identifier": "com.tauri.easytier-game",
"build": {
@@ -13,7 +13,7 @@
"app": {
"windows": [
{
"title": "easytier-game 1.3.1",
"title": "easytier-game 1.3.2",
"label": "main",
"minWidth": 340,
"width": 340,
+14 -4
View File
@@ -1,5 +1,5 @@
import { defineStore } from "pinia";
export default defineStore("main", {
import { acceptHMRUpdate, defineStore } from "pinia";
const store = defineStore("main", {
state() {
return {
config: {
@@ -15,6 +15,8 @@ export default defineStore("main", {
disableIpv6: false, // 是否禁用IPv6
port: "11010", // 监听端口号
enableCustomListener: false, // 是否自定义监听
customListenerV6Data: "", //自定义ipv6监听
enableCustomListenerV6: false, // 是否自定义监听
customListenerData: "", // 自定义监听地址
disbleListenner: false, // 是否禁用监听
disableEncryption: true, // 是否禁用加密
@@ -31,11 +33,12 @@ export default defineStore("main", {
logLevel: "error", //日志等级
devName: false, //自定义网卡名
devNameValue: "", //自定义网卡名
enableCustonProtocol: false, //自定义默认协议
enableCustomProtocol: false, //自定义默认协议
customProtocol: "tcp", //自定义默认协议
enableNetCardMetric: false, //启用网卡自定义跃点
netCardMetricValue: 1, //自定义网卡跃点值
enablePreventSleep: false, //组织系统休眠
compression: "none", //加密算法
},
serverConfig: {
enableWhiteList: true, // 是否启用白名单
@@ -71,7 +74,6 @@ export default defineStore("main", {
"config.autoStart",
"config.connectAfterStart",
"config.disableIpv6",
"config.disbleListenner",
"config.disableEncryption",
"config.multiThread",
"config.enablExitNode",
@@ -89,11 +91,17 @@ export default defineStore("main", {
"config.enableNetCardMetric",
"config.netCardMetricValue",
"config.port",
"config.disbleListenner",
"config.enableCustomListener",
"config.customListenerData",
"config.enableCustomListenerV6",
"config.customListenerV6Data",
"config.enableCustonProtocol",
"config.customProtocol",
"config.enablePreventSleep",
"config.compression",
"serverConfig.autoStart",
// 'serverConfig.enableListener',
@@ -115,3 +123,5 @@ export default defineStore("main", {
// omit: ["winipBcPid", "winipBcStart"]
}
});
if (import.meta.hot) import.meta.hot.accept(acceptHMRUpdate(store, import.meta.hot));
export default store;
+1 -1
View File
@@ -86,7 +86,7 @@ export const stopPreventSleep = () => {
_prevent_timer = null;
}
export const parsePeerInfo = (content: string) => {
export const parseCliInfo = (content: string) => {
if(!content) return [];
// 将表格字符串分割成行
const lines = content.split("\n");
-17
View File
@@ -1,5 +1,4 @@
import useMainStore from "@/stores/index";
import * as tauriAutoStart from "@tauri-apps/plugin-autostart";
import { open, Command } from "@tauri-apps/plugin-shell";
import { invoke } from "@tauri-apps/api/core";
@@ -52,19 +51,3 @@ const handleAutoStartByNssm = async () => {
}
}
};
// 兼容autostart插件
const compatibleAutoStart = async () => {
const config = useMainStore().config;
const is_enable = await tauriAutoStart.isEnabled();
if (is_enable) {
try {
await tauriAutoStart.disable();
config.autoStart = false;
} catch (err) {
// ElMessage.error(`取消自启失败`);
}
}
const serverStatus = await Command.create("nssm", ["status", import.meta.env.VITE_AUTO_START_SERVICE_NAME]).execute();
console.log(serverStatus);
};