mirror of
https://github.com/EasyTier/EasytierGame.git
synced 2026-10-21 04:54:08 +00:00
#Update 增加对成员列表kcp和quic代理显示 增加部分配置到configJson #23
This commit is contained in:
@@ -30,7 +30,6 @@ export const updateConfigJson = async (configJsonSeverUrl?: ConfigServerUrlType)
|
||||
const {
|
||||
proxyNetworks,
|
||||
autoStart,
|
||||
connectAfterStart,
|
||||
saveErrorLog,
|
||||
serverUrl,
|
||||
enableCustomListener,
|
||||
@@ -39,10 +38,6 @@ export const updateConfigJson = async (configJsonSeverUrl?: ConfigServerUrlType)
|
||||
acceptDNS,
|
||||
enablePortForward,
|
||||
portForwardData,
|
||||
tcpWhitelist,
|
||||
udpWhitelist,
|
||||
tcpWhitelistEnable,
|
||||
udpWhitelistEnable,
|
||||
...otherConfig
|
||||
} = mainStore.config;
|
||||
let writeServerUrl: Array<string> = serverUrl;
|
||||
|
||||
+65
-5
@@ -10,7 +10,7 @@
|
||||
<ElTable
|
||||
stripe
|
||||
border
|
||||
:data="data.member"
|
||||
:data="computedMember"
|
||||
v-else
|
||||
>
|
||||
<ElTableColumn
|
||||
@@ -65,7 +65,23 @@
|
||||
label="虚拟网IP"
|
||||
:sort-method="sortIpv4"
|
||||
:sort-orders="['ascending', 'descending', null]"
|
||||
></ElTableColumn>
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<div class="space-y-[5px]">
|
||||
<div>
|
||||
{{ row.ipv4 || "-" }}
|
||||
</div>
|
||||
<div v-if="row.proxy?.[0]">
|
||||
<ElTag
|
||||
size="small"
|
||||
:type="row.proxy?.[1] == 'Connected' ? 'success' : 'danger'"
|
||||
>
|
||||
{{ row.proxy?.[0] || "-" }}
|
||||
</ElTag>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn
|
||||
sortable
|
||||
prop="lat_ms"
|
||||
@@ -137,7 +153,7 @@
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { reactive, onMounted, onBeforeUnmount } from "vue";
|
||||
import { reactive, onMounted, onBeforeUnmount, computed } from "vue";
|
||||
import { ATJ, parseCliInfo } from "@/utils";
|
||||
import { ElConfirmDanger } from "~/utils/element";
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
@@ -157,6 +173,7 @@
|
||||
id: string;
|
||||
version: string;
|
||||
connections_addrs: string[];
|
||||
proxy?: [string, "Connected" | "Closed"];
|
||||
};
|
||||
|
||||
type ConnectionsType = {
|
||||
@@ -209,10 +226,35 @@
|
||||
symmetriceasyinc: "nat4-easyinc",
|
||||
symUdpfirewall: "nat4-udpfirewall"
|
||||
};
|
||||
|
||||
type Proxy = {
|
||||
src: string;
|
||||
dst: string;
|
||||
start_time: string;
|
||||
state: "Connected" | "Closed";
|
||||
transport_type: string;
|
||||
};
|
||||
|
||||
type natKyes = keyof typeof natMaps;
|
||||
|
||||
const data = reactive<{ member: Member[] }>({
|
||||
member: []
|
||||
const data = reactive<{ member: Member[]; proxy: Proxy[] }>({
|
||||
member: [],
|
||||
proxy: []
|
||||
});
|
||||
|
||||
const computedMember = computed(() => {
|
||||
const localMember = data.member.find(member => member?.cost == "Local");
|
||||
if (!localMember) return data.member;
|
||||
const ipv4 = localMember.ipv4;
|
||||
const proxyMember = data.proxy?.filter(proxy => proxy.src.startsWith(`${ipv4}:`));
|
||||
const proxyObj: Record<string, [string, "Connected" | "Closed"]> = {};
|
||||
for (const proxy of proxyMember) {
|
||||
proxyObj[proxy.src.split(":")[0]] = [proxy.transport_type, proxy.state];
|
||||
}
|
||||
return data.member.map(m => {
|
||||
m.proxy = proxyObj?.[m.ipv4.split(":")?.[0]] || m?.proxy || ["", "Closed"];
|
||||
return m;
|
||||
});
|
||||
});
|
||||
|
||||
// 延迟排序函数
|
||||
@@ -395,9 +437,27 @@
|
||||
data.member = JSON.parse(member) as Member[];
|
||||
}
|
||||
// console.log(data.member);
|
||||
getMembersProxy();
|
||||
startTimer();
|
||||
};
|
||||
|
||||
const getMembersProxy = async () => {
|
||||
const [error, proxy] = await ATJ(invoke<string>("get_members_proxy"));
|
||||
if (error) {
|
||||
data.proxy = [];
|
||||
return "";
|
||||
}
|
||||
if (proxy === "_EasytierGameCliFailedToGetProxy_") {
|
||||
data.proxy = [];
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
data.proxy = JSON.parse(proxy) as Proxy[];
|
||||
} catch (error) {
|
||||
data.proxy = [];
|
||||
}
|
||||
};
|
||||
|
||||
const startTimer = () => {
|
||||
timer && clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
|
||||
+61
-36
@@ -1,46 +1,53 @@
|
||||
<template>
|
||||
<div class="relative flex h-full flex-col flex-wrap gap-[0] overflow-hidden">
|
||||
<div class="flex h-[40px] w-full flex-nowrap items-center gap-[0_5px]">
|
||||
<div class="ml-[5px] mr-auto flex flex-1">
|
||||
<div class="flex h-[40px] w-full flex-nowrap items-center gap-[0_8px]">
|
||||
<div class="ml-[5px] mr-auto flex">
|
||||
<ElText class="text-lg font-semibold">公共服务器节点列表</ElText>
|
||||
</div>
|
||||
<div>
|
||||
<ElButton
|
||||
:loading="loading"
|
||||
size="small"
|
||||
type="primary"
|
||||
class="mr-[5px]"
|
||||
@click="refreshNodes"
|
||||
>
|
||||
刷新
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:disabled="selectedNodes.length === 0"
|
||||
size="small"
|
||||
type="success"
|
||||
class="mr-[5px]"
|
||||
@click="addSelectedToServerList"
|
||||
>
|
||||
添加选中 ({{ selectedNodes.length }})
|
||||
</ElButton>
|
||||
<ElTag
|
||||
v-if="lastUpdateTime"
|
||||
size="small"
|
||||
type="info"
|
||||
>
|
||||
最后更新: {{ lastUpdateTime }}
|
||||
</ElTag>
|
||||
<div class="flex w-fit flex-nowrap items-center gap-[0_8px]">
|
||||
<div>
|
||||
<ElInput
|
||||
placeholder="搜索名称/地址"
|
||||
v-model="searchName"
|
||||
clearable
|
||||
></ElInput>
|
||||
</div>
|
||||
<div>
|
||||
<ElButton
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="refreshNodes"
|
||||
>
|
||||
刷新
|
||||
</ElButton>
|
||||
<ElButton
|
||||
:disabled="selectedNodes.length === 0"
|
||||
size="small"
|
||||
type="success"
|
||||
class="mr-[8px]"
|
||||
@click="addSelectedToServerList"
|
||||
>
|
||||
添加选中 ({{ selectedNodes.length }})
|
||||
</ElButton>
|
||||
<ElTag
|
||||
size="small"
|
||||
type="info"
|
||||
>
|
||||
最后更新: {{ lastUpdateTime || "00:00:00" }}
|
||||
</ElTag>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex w-full flex-1 flex-wrap gap-[0] overflow-auto">
|
||||
<ElTable
|
||||
:data="nodes"
|
||||
:data="computedNodes"
|
||||
stripe
|
||||
height="100%"
|
||||
ref="tableRef"
|
||||
:empty-text="loading ? '加载中...' : '暂无可用服务器'"
|
||||
:default-sort="{ prop: 'last_response_time', order: 'ascending' }"
|
||||
@selection-change="handleSelectionChange"
|
||||
@select-all="handleSelectChange"
|
||||
@select="handleSelectChange"
|
||||
@row-click="handleRowClick"
|
||||
>
|
||||
<ElTableColumn
|
||||
@@ -151,10 +158,11 @@
|
||||
<script setup lang="ts">
|
||||
import useMainStore from "@/stores/index";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { reactive, onMounted, onBeforeUnmount, ref, useTemplateRef } from "vue";
|
||||
import { reactive, onMounted, onBeforeUnmount, ref, useTemplateRef, computed, nextTick } from "vue";
|
||||
import { ElMessage, type TableInstance } from "element-plus";
|
||||
import { dataSubscribe } from "~/composables/windows";
|
||||
import { uniq } from "lodash-es";
|
||||
import { inputBounceRef } from "@/utils";
|
||||
import { uniq } from "lodash-es";
|
||||
|
||||
const mainStore = useMainStore();
|
||||
// 节点数据
|
||||
@@ -162,6 +170,20 @@ import { uniq } from "lodash-es";
|
||||
const loading = ref(false);
|
||||
const lastUpdateTime = ref("");
|
||||
const selectedNodes = ref<NodeItem[]>([]);
|
||||
const searchName = ref<string>("");
|
||||
const computedNodes = computed(() => {
|
||||
const v = searchName.value.trim();
|
||||
let result = nodes;
|
||||
if (v) {
|
||||
result = nodes.filter(node => node.name.includes(v) || node.address.includes(v));
|
||||
}
|
||||
nextTick(() => {
|
||||
for (const node of selectedNodes.value) {
|
||||
tableRef.value?.toggleRowSelection(node, true);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
});
|
||||
const tableRef = useTemplateRef<TableInstance>("tableRef");
|
||||
|
||||
// 定时器
|
||||
@@ -191,14 +213,15 @@ import { uniq } from "lodash-es";
|
||||
// 刷新节点数据
|
||||
const refreshNodes = async () => {
|
||||
loading.value = true;
|
||||
let nodesList = await invoke<string>("fetch_nodes_list", { page: 1, limit: 1000 });
|
||||
try {
|
||||
let nodesList = await invoke<string>("fetch_nodes_list", { page: 1, limit: 1000 });
|
||||
if (nodesList === "_EasytierGameFetchNodesError_") {
|
||||
nodesList = "";
|
||||
return;
|
||||
}
|
||||
if (nodesList) {
|
||||
const nodesListData: NodesResponse = JSON.parse(nodesList);
|
||||
console.log(nodesListData);
|
||||
// console.log(nodesListData);
|
||||
if (nodesListData.success && nodesListData.data.items) {
|
||||
// 过滤可用节点
|
||||
const availableNodes = filterAvailableNodes(nodesListData.data.items);
|
||||
@@ -215,6 +238,7 @@ import { uniq } from "lodash-es";
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(nodesList);
|
||||
console.error("获取节点数据失败:", err);
|
||||
ElMessage.error("获取节点数据失败");
|
||||
} finally {
|
||||
@@ -303,7 +327,7 @@ import { uniq } from "lodash-es";
|
||||
const startRefreshTimer = () => {
|
||||
refreshTimer = setInterval(() => {
|
||||
refreshNodes();
|
||||
}, 10000); // 10秒刷新一次
|
||||
}, 60000); // 10秒刷新一次
|
||||
};
|
||||
|
||||
// 停止定时器
|
||||
@@ -315,12 +339,13 @@ import { uniq } from "lodash-es";
|
||||
};
|
||||
|
||||
// 处理选择变化
|
||||
const handleSelectionChange = (selection: NodeItem[]) => {
|
||||
const handleSelectChange = (selection: NodeItem[]) => {
|
||||
selectedNodes.value = selection;
|
||||
};
|
||||
|
||||
const handleRowClick = (row: NodeItem) => {
|
||||
tableRef.value?.toggleRowSelection(row);
|
||||
selectedNodes.value = tableRef.value?.getSelectionRows() || [];
|
||||
};
|
||||
|
||||
// 添加选中的服务器到主页面服务器列表
|
||||
|
||||
@@ -299,6 +299,34 @@ async fn get_members_by_cli() -> String {
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
async fn get_members_proxy() -> String {
|
||||
let cli_path = get_tool_exe_path("\\easytier\\easytier-cli.exe");
|
||||
match tokioCommand::new(&cli_path)
|
||||
.arg("-o")
|
||||
.arg("json")
|
||||
.arg("proxy")
|
||||
.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 "_EasytierGameCliFailedToGetProxy_".to_string();
|
||||
}
|
||||
}
|
||||
Err(_e) => {
|
||||
log::error!("get member list error");
|
||||
return "".to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command(rename_all = "snake_case")]
|
||||
async fn get_members_connections_cli() -> String {
|
||||
let cli_path = get_tool_exe_path("\\easytier\\easytier-cli.exe");
|
||||
|
||||
+23
-2
@@ -3,10 +3,11 @@ 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";
|
||||
import { customRef, ref } from "vue";
|
||||
|
||||
export const ENV = import.meta.env;
|
||||
|
||||
export const mixedArray = (a: Array<string>, b: Array<string>, split:string="") => {
|
||||
export const mixedArray = (a: Array<string>, b: Array<string>, split: string = "") => {
|
||||
return flatMap(a, protocol => map(b, port => `${protocol}${split}${port}`));
|
||||
};
|
||||
|
||||
@@ -21,6 +22,26 @@ export const bounce = (time = 3000) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const inputBounceRef = (value: string, cb?: Function, delay = 650) => {
|
||||
let timeout: number | null = null;
|
||||
return customRef((track, trigger) => {
|
||||
return {
|
||||
get() {
|
||||
track();
|
||||
return value;
|
||||
},
|
||||
set(newValue) {
|
||||
value = newValue;
|
||||
trigger();
|
||||
timeout && clearTimeout(timeout);
|
||||
timeout = setTimeout(() => {
|
||||
cb?.(newValue);
|
||||
}, delay);
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {number} size byte number
|
||||
@@ -73,7 +94,7 @@ export const ATJ = (promise: Promise<any>, errorExt: string | undefined = undefi
|
||||
});
|
||||
};
|
||||
|
||||
const _supportProtocols = ["tcp", "udp", "ws", "wss", 'srv', 'schema'];
|
||||
const _supportProtocols = ["tcp", "udp", "ws", "wss", "srv", "schema"];
|
||||
|
||||
export const supportProtocols = () => {
|
||||
return _supportProtocols.slice();
|
||||
|
||||
Reference in New Issue
Block a user