mirror of
https://github.com/EasyTier/EasyTier.git
synced 2024-11-16 03:32:43 +08:00
🌈 style: eslint lint
This commit is contained in:
parent
8b94b3cab0
commit
c37fc13404
|
@ -32,7 +32,7 @@ settings: Settings
|
|||
exchange_language: 切换中文
|
||||
exit: Exit
|
||||
|
||||
chips_placeholder: "e.g: {0}, press Enter to add"
|
||||
chips_placeholder: 'e.g: {0}, press Enter to add'
|
||||
off_text: Press to disable
|
||||
on_text: Press to enable
|
||||
show_config: Show Config
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
"eslint-plugin-format": "^0.1.1",
|
||||
"naive-ui": "^2.38.2",
|
||||
"postcss": "^8.4.38",
|
||||
"prettier": "^3.2.5",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "^5.4.5",
|
||||
"unplugin-auto-import": "^0.17.5",
|
||||
|
@ -45,7 +46,6 @@
|
|||
"vite": "^5.2.11",
|
||||
"vite-plugin-vue-devtools": "^7.1.3",
|
||||
"vite-plugin-vue-layouts": "^0.11.0",
|
||||
"prettier": "^3.2.5",
|
||||
"vue-i18n": "^9.13.1",
|
||||
"vue-tsc": "^2.0.16"
|
||||
}
|
||||
|
|
|
@ -1,39 +1,37 @@
|
|||
<script setup lang="ts">
|
||||
import InputGroup from "primevue/inputgroup";
|
||||
import InputGroupAddon from "primevue/inputgroupaddon";
|
||||
import { i18n } from "~/modules/i18n";
|
||||
import { NetworkingMethod } from "~/types/network";
|
||||
import InputGroup from 'primevue/inputgroup'
|
||||
import InputGroupAddon from 'primevue/inputgroupaddon'
|
||||
import { i18n } from '~/modules/i18n'
|
||||
import { NetworkingMethod } from '~/types/network'
|
||||
|
||||
const props = defineProps<{
|
||||
configInvalid?: boolean
|
||||
instanceId?: string
|
||||
}>()
|
||||
|
||||
defineEmits(['runNetwork'])
|
||||
|
||||
const networking_methods = ref([
|
||||
{ value: NetworkingMethod.PublicServer, label: i18n.global.t('public_server') },
|
||||
{ value: NetworkingMethod.Manual, label: i18n.global.t('manual') },
|
||||
{ value: NetworkingMethod.Standalone, label: i18n.global.t('standalone') },
|
||||
]);
|
||||
])
|
||||
|
||||
const props = defineProps<{
|
||||
configInvalid?: boolean,
|
||||
instanceId?: string,
|
||||
}>()
|
||||
|
||||
defineEmits(["runNetwork"]);
|
||||
|
||||
const networkStore = useNetworkStore();
|
||||
const networkStore = useNetworkStore()
|
||||
const curNetwork = computed(() => {
|
||||
if (props.instanceId) {
|
||||
console.log("instanceId", props.instanceId);
|
||||
const c = networkStore.networkList.find(n => n.instance_id == props.instanceId);
|
||||
if (c != undefined) {
|
||||
return c;
|
||||
}
|
||||
// console.log('instanceId', props.instanceId)
|
||||
const c = networkStore.networkList.find(n => n.instance_id === props.instanceId)
|
||||
if (c !== undefined)
|
||||
return c
|
||||
}
|
||||
|
||||
return networkStore.curNetwork;
|
||||
});
|
||||
return networkStore.curNetwork
|
||||
})
|
||||
|
||||
const presetPublicServers = [
|
||||
"tcp://easytier.public.kkrainbow.top:11010",
|
||||
];
|
||||
|
||||
'tcp://easytier.public.kkrainbow.top:11010',
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -41,9 +39,7 @@ const presetPublicServers = [
|
|||
<div class="flex flex-column">
|
||||
<div class="w-10/12 max-w-fit self-center ">
|
||||
<Panel header="Basic Settings">
|
||||
|
||||
<div class="flex flex-column gap-y-2">
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||
<label for="virtual_ip">{{ $t('virtual_ipv4') }}</label>
|
||||
|
@ -63,8 +59,10 @@ const presetPublicServers = [
|
|||
</div>
|
||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||
<label for="network_secret">{{ $t('network_secret') }}</label>
|
||||
<InputText id="network_secret" v-model="curNetwork.network_secret"
|
||||
aria-describedby=" network_secret-help" />
|
||||
<InputText
|
||||
id="network_secret" v-model="curNetwork.network_secret"
|
||||
aria-describedby=" network_secret-help"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -72,15 +70,21 @@ const presetPublicServers = [
|
|||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||
<label for="nm">{{ $t('networking_method') }}</label>
|
||||
<div class="items-center flex flex-row p-fluid gap-x-1">
|
||||
<Dropdown v-model="curNetwork.networking_method" :options="networking_methods" optionLabel="label"
|
||||
optionValue="value" placeholder="Select Method" class="" />
|
||||
<Chips id="chips" v-model="curNetwork.peer_urls"
|
||||
:placeholder="$t('chips_placeholder', ['tcp://8.8.8.8:11010'])" separator=" " class="grow"
|
||||
v-if="curNetwork.networking_method == NetworkingMethod.Manual" />
|
||||
<Dropdown
|
||||
v-model="curNetwork.networking_method" :options="networking_methods" option-label="label"
|
||||
option-value="value" placeholder="Select Method" class=""
|
||||
/>
|
||||
<Chips
|
||||
v-if="curNetwork.networking_method === NetworkingMethod.Manual" id="chips"
|
||||
v-model="curNetwork.peer_urls" :placeholder="$t('chips_placeholder', ['tcp://8.8.8.8:11010'])"
|
||||
separator=" " class="grow"
|
||||
/>
|
||||
|
||||
<Dropdown :editable="true" v-model="curNetwork.public_server_url" class="grow"
|
||||
<Dropdown
|
||||
v-if="curNetwork.networking_method === NetworkingMethod.PublicServer"
|
||||
v-model="curNetwork.public_server_url" :editable="true" class="grow"
|
||||
:options="presetPublicServers"
|
||||
v-if="curNetwork.networking_method == NetworkingMethod.PublicServer" />
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -88,8 +92,10 @@ const presetPublicServers = [
|
|||
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
||||
<div class="flex flex-column gap-2 grow p-fluid">
|
||||
<label for="username">{{ $t('proxy_cidrs') }}</label>
|
||||
<Chips id="chips" v-model="curNetwork.proxy_cidrs"
|
||||
:placeholder="$t('chips_placeholder', ['10.0.0.0/24'])" separator=" " class="w-full" />
|
||||
<Chips
|
||||
id="chips" v-model="curNetwork.proxy_cidrs"
|
||||
:placeholder="$t('chips_placeholder', ['10.0.0.0/24'])" separator=" " class="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -97,24 +103,29 @@ const presetPublicServers = [
|
|||
<div class="flex flex-column gap-2 grow">
|
||||
<label for="username">VPN Portal</label>
|
||||
<div class="items-center flex flex-row gap-x-4">
|
||||
<ToggleButton onIcon="pi pi-check" offIcon="pi pi-times" v-model="curNetwork.enable_vpn_portal"
|
||||
:onLabel="$t('off_text')" :offLabel="$t('on_text')" />
|
||||
<div class="grow" v-if="curNetwork.enable_vpn_portal">
|
||||
<ToggleButton
|
||||
v-model="curNetwork.enable_vpn_portal" on-icon="pi pi-check" off-icon="pi pi-times"
|
||||
:on-label="$t('off_text')" :off-label="$t('on_text')"
|
||||
/>
|
||||
<div v-if="curNetwork.enable_vpn_portal" class="grow">
|
||||
<InputGroup>
|
||||
<InputText :placeholder="$t('vpn_portal_client_network')"
|
||||
v-model="curNetwork.vpn_portal_client_network_addr" />
|
||||
<InputText
|
||||
v-model="curNetwork.vpn_portal_client_network_addr"
|
||||
:placeholder="$t('vpn_portal_client_network')"
|
||||
/>
|
||||
<InputGroupAddon>
|
||||
<span>/{{ curNetwork.vpn_portal_client_network_len }}</span>
|
||||
</InputGroupAddon>
|
||||
</InputGroup>
|
||||
</div>
|
||||
<InputNumber :placeholder="$t('vpn_portal_listen_port')" class="" v-if="curNetwork.enable_vpn_portal"
|
||||
:format="false" v-model="curNetwork.vpn_portal_listne_port" :min="0" :max="65535" />
|
||||
<InputNumber
|
||||
v-if="curNetwork.enable_vpn_portal" v-model="curNetwork.vpn_portal_listne_port"
|
||||
:placeholder="$t('vpn_portal_listen_port')" class="" :format="false" :min="0" :max="65535"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</Panel>
|
||||
|
||||
<Divider />
|
||||
|
@ -124,17 +135,20 @@ const presetPublicServers = [
|
|||
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
||||
<div class="flex flex-column gap-2 grow p-fluid">
|
||||
<label for="listener_urls">{{ $t('listener_urls') }}</label>
|
||||
<Chips id="listener_urls" v-model="curNetwork.listener_urls"
|
||||
:placeholder="$t('chips_placeholder', ['tcp://1.1.1.1:11010'])" separator=" " class="w-full" />
|
||||
<Chips
|
||||
id="listener_urls" v-model="curNetwork.listener_urls"
|
||||
:placeholder="$t('chips_placeholder', ['tcp://1.1.1.1:11010'])" separator=" " class="w-full"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||
<label for="rpc_port">{{ $t('rpc_port') }}</label>
|
||||
<InputNumber id="rpc_port" v-model="curNetwork.rpc_port" aria-describedby="username-help"
|
||||
:format="false" :min="0" :max="65535" />
|
||||
<InputNumber
|
||||
id="rpc_port" v-model="curNetwork.rpc_port" aria-describedby="username-help"
|
||||
:format="false" :min="0" :max="65535"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -142,10 +156,11 @@ const presetPublicServers = [
|
|||
|
||||
<Divider />
|
||||
|
||||
|
||||
<div class="flex pt-4 justify-content-center">
|
||||
<Button :label="$t('run_network')" icon="pi pi-arrow-right" iconPos="right"
|
||||
@click="$emit('runNetwork', curNetwork)" :disabled="configInvalid" />
|
||||
<Button
|
||||
:label="$t('run_network')" icon="pi pi-arrow-right" icon-pos="right" :disabled="configInvalid"
|
||||
@click="$emit('runNetwork', curNetwork)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,354 +1,363 @@
|
|||
<script setup lang="ts">
|
||||
const networkStore = useNetworkStore();
|
||||
|
||||
const props = defineProps<{
|
||||
instanceId?: string,
|
||||
instanceId?: string
|
||||
}>()
|
||||
|
||||
const networkStore = useNetworkStore()
|
||||
|
||||
const curNetwork = computed(() => {
|
||||
if (props.instanceId) {
|
||||
console.log("instanceId", props.instanceId);
|
||||
const c = networkStore.networkList.find(n => n.instance_id == props.instanceId);
|
||||
if (c != undefined) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
if (props.instanceId) {
|
||||
// console.log('instanceId', props.instanceId)
|
||||
const c = networkStore.networkList.find(n => n.instance_id === props.instanceId)
|
||||
if (c !== undefined)
|
||||
return c
|
||||
}
|
||||
|
||||
return networkStore.curNetwork;
|
||||
});
|
||||
return networkStore.curNetwork
|
||||
})
|
||||
|
||||
let curNetworkInst = computed(() => {
|
||||
return networkStore.networkInstances.find(n => n.instance_id == curNetwork.value.instance_id);
|
||||
});
|
||||
const curNetworkInst = computed(() => {
|
||||
return networkStore.networkInstances.find(n => n.instance_id === curNetwork.value.instance_id)
|
||||
})
|
||||
|
||||
let peerRouteInfos = computed(() => {
|
||||
if (curNetworkInst.value) {
|
||||
return curNetworkInst.value.detail.peer_route_pairs;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
const peerRouteInfos = computed(() => {
|
||||
if (curNetworkInst.value)
|
||||
return curNetworkInst.value.detail.peer_route_pairs
|
||||
|
||||
let routeCost = (info: any) => {
|
||||
if (info.route) {
|
||||
const cost = info.route.cost;
|
||||
return cost == 1 ? "p2p" : `relay(${cost})`
|
||||
}
|
||||
return '?';
|
||||
};
|
||||
return []
|
||||
})
|
||||
|
||||
function resolveObjPath(path: string, obj = self, separator = '.') {
|
||||
var properties = Array.isArray(path) ? path : path.split(separator)
|
||||
return properties.reduce((prev, curr) => prev?.[curr], obj)
|
||||
function routeCost(info: any) {
|
||||
if (info.route) {
|
||||
const cost = info.route.cost
|
||||
return cost === 1 ? 'p2p' : `relay(${cost})`
|
||||
}
|
||||
return '?'
|
||||
}
|
||||
|
||||
let statsCommon = (info: any, field: string) => {
|
||||
if (!info.peer) {
|
||||
return undefined;
|
||||
}
|
||||
let conns = info.peer.conns;
|
||||
return conns.reduce((acc: number, conn: any) => {
|
||||
return acc + resolveObjPath(field, conn);
|
||||
}, 0);
|
||||
};
|
||||
function resolveObjPath(path: string, obj = globalThis, separator = '.') {
|
||||
const properties = Array.isArray(path) ? path : path.split(separator)
|
||||
return properties.reduce((prev, curr) => prev?.[curr], obj)
|
||||
}
|
||||
|
||||
function statsCommon(info: any, field: string) {
|
||||
if (!info.peer)
|
||||
return undefined
|
||||
|
||||
const conns = info.peer.conns
|
||||
return conns.reduce((acc: number, conn: any) => {
|
||||
return acc + resolveObjPath(field, conn)
|
||||
}, 0)
|
||||
}
|
||||
|
||||
function humanFileSize(bytes: number, si = false, dp = 1) {
|
||||
const thresh = si ? 1000 : 1024;
|
||||
const thresh = si ? 1000 : 1024
|
||||
|
||||
if (Math.abs(bytes) < thresh) {
|
||||
return bytes + ' B';
|
||||
}
|
||||
if (Math.abs(bytes) < thresh)
|
||||
return `${bytes} B`
|
||||
|
||||
const units = si
|
||||
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
|
||||
let u = -1;
|
||||
const r = 10 ** dp;
|
||||
const units = si
|
||||
? ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
|
||||
: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
|
||||
let u = -1
|
||||
const r = 10 ** dp
|
||||
|
||||
do {
|
||||
bytes /= thresh;
|
||||
++u;
|
||||
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1);
|
||||
do {
|
||||
bytes /= thresh
|
||||
++u
|
||||
} while (Math.round(Math.abs(bytes) * r) / r >= thresh && u < units.length - 1)
|
||||
|
||||
|
||||
return bytes.toFixed(dp) + ' ' + units[u];
|
||||
return `${bytes.toFixed(dp)} ${units[u]}`
|
||||
}
|
||||
|
||||
let latencyMs = (info: any) => {
|
||||
let lat_us_sum = statsCommon(info, 'stats.latency_us');
|
||||
return lat_us_sum ? `${lat_us_sum / 1000 / info.peer.conns.length}ms` : '';
|
||||
};
|
||||
|
||||
let txBytes = (info: any) => {
|
||||
let tx = statsCommon(info, 'stats.tx_bytes');
|
||||
return tx ? humanFileSize(tx) : '';
|
||||
function latencyMs(info: any) {
|
||||
const lat_us_sum = statsCommon(info, 'stats.latency_us')
|
||||
return lat_us_sum ? `${lat_us_sum / 1000 / info.peer.conns.length}ms` : ''
|
||||
}
|
||||
|
||||
let rxBytes = (info: any) => {
|
||||
let rx = statsCommon(info, 'stats.rx_bytes');
|
||||
return rx ? humanFileSize(rx) : '';
|
||||
function txBytes(info: any) {
|
||||
const tx = statsCommon(info, 'stats.tx_bytes')
|
||||
return tx ? humanFileSize(tx) : ''
|
||||
}
|
||||
|
||||
let lossRate = (info: any) => {
|
||||
let lossRate = statsCommon(info, 'loss_rate');
|
||||
return lossRate != undefined ? `${Math.round(lossRate * 100)}%` : '';
|
||||
function rxBytes(info: any) {
|
||||
const rx = statsCommon(info, 'stats.rx_bytes')
|
||||
return rx ? humanFileSize(rx) : ''
|
||||
}
|
||||
|
||||
function lossRate(info: any) {
|
||||
const lossRate = statsCommon(info, 'loss_rate')
|
||||
return lossRate !== undefined ? `${Math.round(lossRate * 100)}%` : ''
|
||||
}
|
||||
|
||||
const myNodeInfo = computed(() => {
|
||||
if (!curNetworkInst.value) {
|
||||
return {};
|
||||
}
|
||||
return curNetworkInst.value.detail?.my_node_info;
|
||||
});
|
||||
if (!curNetworkInst.value)
|
||||
return {}
|
||||
|
||||
return curNetworkInst.value.detail?.my_node_info
|
||||
})
|
||||
|
||||
interface Chip {
|
||||
label: string;
|
||||
icon: string;
|
||||
label: string
|
||||
icon: string
|
||||
}
|
||||
|
||||
let myNodeInfoChips = computed(() => {
|
||||
if (!curNetworkInst.value) {
|
||||
return [];
|
||||
const myNodeInfoChips = computed(() => {
|
||||
if (!curNetworkInst.value)
|
||||
return []
|
||||
|
||||
const chips: Array<Chip> = []
|
||||
const my_node_info = curNetworkInst.value.detail?.my_node_info
|
||||
if (!my_node_info)
|
||||
return chips
|
||||
|
||||
// local ipv4s
|
||||
const local_ipv4s = my_node_info.ips?.interface_ipv4s
|
||||
for (const [idx, ip] of local_ipv4s?.entries()) {
|
||||
chips.push({
|
||||
label: `Local IPv4 ${idx}: ${ip}`,
|
||||
icon: '',
|
||||
} as Chip)
|
||||
}
|
||||
|
||||
// local ipv6s
|
||||
const local_ipv6s = my_node_info.ips?.interface_ipv6s
|
||||
for (const [idx, ip] of local_ipv6s?.entries()) {
|
||||
chips.push({
|
||||
label: `Local IPv6 ${idx}: ${ip}`,
|
||||
icon: '',
|
||||
} as Chip)
|
||||
}
|
||||
|
||||
// public ip
|
||||
const public_ip = my_node_info.ips?.public_ipv4
|
||||
if (public_ip) {
|
||||
chips.push({
|
||||
label: `Public IP: ${public_ip}`,
|
||||
icon: '',
|
||||
} as Chip)
|
||||
}
|
||||
|
||||
// listeners:
|
||||
const listeners = my_node_info.listeners
|
||||
for (const [idx, listener] of listeners?.entries()) {
|
||||
chips.push({
|
||||
label: `Listener ${idx}: ${listener}`,
|
||||
icon: '',
|
||||
} as Chip)
|
||||
}
|
||||
|
||||
// udp nat type
|
||||
enum NatType {
|
||||
// has NAT; but own a single public IP, port is not changed
|
||||
Unknown = 0,
|
||||
OpenInternet = 1,
|
||||
NoPAT = 2,
|
||||
FullCone = 3,
|
||||
Restricted = 4,
|
||||
PortRestricted = 5,
|
||||
Symmetric = 6,
|
||||
SymUdpFirewall = 7,
|
||||
};
|
||||
const udpNatType: NatType = my_node_info.stun_info?.udp_nat_type
|
||||
if (udpNatType !== undefined) {
|
||||
const udpNatTypeStrMap = {
|
||||
[NatType.Unknown]: 'Unknown',
|
||||
[NatType.OpenInternet]: 'Open Internet',
|
||||
[NatType.NoPAT]: 'No PAT',
|
||||
[NatType.FullCone]: 'Full Cone',
|
||||
[NatType.Restricted]: 'Restricted',
|
||||
[NatType.PortRestricted]: 'Port Restricted',
|
||||
[NatType.Symmetric]: 'Symmetric',
|
||||
[NatType.SymUdpFirewall]: 'Symmetric UDP Firewall',
|
||||
}
|
||||
|
||||
let chips: Array<Chip> = [];
|
||||
let my_node_info = curNetworkInst.value.detail?.my_node_info;
|
||||
if (!my_node_info) {
|
||||
return chips;
|
||||
}
|
||||
chips.push({
|
||||
label: `UDP NAT Type: ${udpNatTypeStrMap[udpNatType]}`,
|
||||
icon: '',
|
||||
} as Chip)
|
||||
}
|
||||
|
||||
// local ipv4s
|
||||
let local_ipv4s = my_node_info.ips?.interface_ipv4s;
|
||||
for (let [idx, ip] of local_ipv4s?.entries()) {
|
||||
chips.push({
|
||||
label: `Local IPv4 ${idx}: ${ip}`,
|
||||
icon: '',
|
||||
} as Chip);
|
||||
}
|
||||
return chips
|
||||
})
|
||||
|
||||
// local ipv6s
|
||||
let local_ipv6s = my_node_info.ips?.interface_ipv6s;
|
||||
for (let [idx, ip] of local_ipv6s?.entries()) {
|
||||
chips.push({
|
||||
label: `Local IPv6 ${idx}: ${ip}`,
|
||||
icon: '',
|
||||
} as Chip);
|
||||
}
|
||||
function globalSumCommon(field: string) {
|
||||
let sum = 0
|
||||
if (!peerRouteInfos.value)
|
||||
return sum
|
||||
|
||||
// public ip
|
||||
let public_ip = my_node_info.ips?.public_ipv4;
|
||||
if (public_ip) {
|
||||
chips.push({
|
||||
label: `Public IP: ${public_ip}`,
|
||||
icon: '',
|
||||
} as Chip);
|
||||
}
|
||||
|
||||
|
||||
// listeners:
|
||||
let listeners = my_node_info.listeners;
|
||||
for (let [idx, listener] of listeners?.entries()) {
|
||||
chips.push({
|
||||
label: `Listener ${idx}: ${listener}`,
|
||||
icon: '',
|
||||
} as Chip);
|
||||
}
|
||||
|
||||
// udp nat type
|
||||
enum NatType {
|
||||
// has NAT; but own a single public IP, port is not changed
|
||||
Unknown = 0,
|
||||
OpenInternet = 1,
|
||||
NoPAT = 2,
|
||||
FullCone = 3,
|
||||
Restricted = 4,
|
||||
PortRestricted = 5,
|
||||
Symmetric = 6,
|
||||
SymUdpFirewall = 7,
|
||||
};
|
||||
let udpNatType: NatType = my_node_info.stun_info?.udp_nat_type;
|
||||
if (udpNatType != undefined) {
|
||||
let udpNatTypeStrMap = {
|
||||
[NatType.Unknown]: 'Unknown',
|
||||
[NatType.OpenInternet]: 'Open Internet',
|
||||
[NatType.NoPAT]: 'No PAT',
|
||||
[NatType.FullCone]: 'Full Cone',
|
||||
[NatType.Restricted]: 'Restricted',
|
||||
[NatType.PortRestricted]: 'Port Restricted',
|
||||
[NatType.Symmetric]: 'Symmetric',
|
||||
[NatType.SymUdpFirewall]: 'Symmetric UDP Firewall',
|
||||
};
|
||||
|
||||
chips.push({
|
||||
label: `UDP NAT Type: ${udpNatTypeStrMap[udpNatType]}`,
|
||||
icon: '',
|
||||
} as Chip);
|
||||
|
||||
}
|
||||
|
||||
return chips;
|
||||
});
|
||||
|
||||
const globalSumCommon = (field: string) => {
|
||||
let sum = 0;
|
||||
if (!peerRouteInfos.value) {
|
||||
return sum;
|
||||
}
|
||||
for (let info of peerRouteInfos.value) {
|
||||
let tx = statsCommon(info, field);
|
||||
if (tx) {
|
||||
sum += tx;
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
};
|
||||
|
||||
const txGlobalSum = () => {
|
||||
return globalSumCommon('stats.tx_bytes');
|
||||
};
|
||||
|
||||
const rxGlobalSum = () => {
|
||||
return globalSumCommon('stats.rx_bytes');
|
||||
for (const info of peerRouteInfos.value) {
|
||||
const tx = statsCommon(info, field)
|
||||
if (tx)
|
||||
sum += tx
|
||||
}
|
||||
return sum
|
||||
}
|
||||
|
||||
function txGlobalSum() {
|
||||
return globalSumCommon('stats.tx_bytes')
|
||||
}
|
||||
|
||||
function rxGlobalSum() {
|
||||
return globalSumCommon('stats.rx_bytes')
|
||||
}
|
||||
|
||||
const peerCount = computed(() => {
|
||||
if (!peerRouteInfos.value) {
|
||||
return 0;
|
||||
}
|
||||
return peerRouteInfos.value.length;
|
||||
});
|
||||
if (!peerRouteInfos.value)
|
||||
return 0
|
||||
|
||||
return peerRouteInfos.value.length
|
||||
})
|
||||
|
||||
// calculate tx/rx rate every 2 seconds
|
||||
let rateIntervalId = 0;
|
||||
let rateInterval = 2000;
|
||||
let prevTxSum = 0;
|
||||
let prevRxSum = 0;
|
||||
let txRate = ref('0');
|
||||
let rxRate = ref('0');
|
||||
let rateIntervalId = 0
|
||||
const rateInterval = 2000
|
||||
let prevTxSum = 0
|
||||
let prevRxSum = 0
|
||||
const txRate = ref('0')
|
||||
const rxRate = ref('0')
|
||||
onMounted(() => {
|
||||
rateIntervalId = window.setInterval(() => {
|
||||
let curTxSum = txGlobalSum();
|
||||
txRate.value = humanFileSize((curTxSum - prevTxSum) / (rateInterval / 1000));
|
||||
prevTxSum = curTxSum;
|
||||
rateIntervalId = window.setInterval(() => {
|
||||
const curTxSum = txGlobalSum()
|
||||
txRate.value = humanFileSize((curTxSum - prevTxSum) / (rateInterval / 1000))
|
||||
prevTxSum = curTxSum
|
||||
|
||||
let curRxSum = rxGlobalSum();
|
||||
rxRate.value = humanFileSize((curRxSum - prevRxSum) / (rateInterval / 1000));
|
||||
prevRxSum = curRxSum;
|
||||
}, rateInterval);
|
||||
});
|
||||
const curRxSum = rxGlobalSum()
|
||||
rxRate.value = humanFileSize((curRxSum - prevRxSum) / (rateInterval / 1000))
|
||||
prevRxSum = curRxSum
|
||||
}, rateInterval)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
clearInterval(rateIntervalId);
|
||||
});
|
||||
clearInterval(rateIntervalId)
|
||||
})
|
||||
|
||||
const dialogVisible = ref(false);
|
||||
const dialogContent = ref('');
|
||||
const dialogVisible = ref(false)
|
||||
const dialogContent = ref('')
|
||||
|
||||
const showVpnPortalConfig = () => {
|
||||
let my_node_info = myNodeInfo.value;
|
||||
if (!my_node_info) {
|
||||
return;
|
||||
}
|
||||
const url = "https://www.wireguardconfig.com/qrcode";
|
||||
dialogContent.value = `${my_node_info.vpn_portal_cfg}\n\n # can generate QR code: ${url}`;
|
||||
dialogVisible.value = true;
|
||||
function showVpnPortalConfig() {
|
||||
const my_node_info = myNodeInfo.value
|
||||
if (!my_node_info)
|
||||
return
|
||||
|
||||
const url = 'https://www.wireguardconfig.com/qrcode'
|
||||
dialogContent.value = `${my_node_info.vpn_portal_cfg}\n\n # can generate QR code: ${url}`
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
const showEventLogs = () => {
|
||||
let detail = curNetworkInst.value?.detail;
|
||||
if (!detail) {
|
||||
return;
|
||||
}
|
||||
dialogContent.value = detail.events;
|
||||
dialogVisible.value = true;
|
||||
}
|
||||
function showEventLogs() {
|
||||
const detail = curNetworkInst.value?.detail
|
||||
if (!detail)
|
||||
return
|
||||
|
||||
dialogContent.value = detail.events
|
||||
dialogVisible.value = true
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<Dialog v-model:visible="dialogVisible" modal header="Dialog" :style="{ width: '70%' }">
|
||||
<Panel>
|
||||
<ScrollPanel style="width: 100%; height: 400px">
|
||||
<pre>{{ dialogContent }}</pre>
|
||||
</ScrollPanel>
|
||||
</Panel>
|
||||
<Divider />
|
||||
<div class="flex justify-content-end gap-2">
|
||||
<Button type="button" label="Close" @click="dialogVisible = false"></Button>
|
||||
<div>
|
||||
<Dialog v-model:visible="dialogVisible" modal header="Dialog" :style="{ width: '70%' }">
|
||||
<Panel>
|
||||
<ScrollPanel style="width: 100%; height: 400px">
|
||||
<pre>{{ dialogContent }}</pre>
|
||||
</ScrollPanel>
|
||||
</Panel>
|
||||
<Divider />
|
||||
<div class="flex justify-content-end gap-2">
|
||||
<Button type="button" label="Close" @click="dialogVisible = false" />
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
<Card v-if="curNetworkInst?.error_msg">
|
||||
<template #title>
|
||||
Run Network Error
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="flex flex-column gap-y-5">
|
||||
<div class="text-red-500">
|
||||
{{ curNetworkInst.error_msg }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<Card v-if="!curNetworkInst?.error_msg">
|
||||
<template #title>
|
||||
{{ $t('my_node_info') }}
|
||||
</template>
|
||||
<template #content>
|
||||
<div class="flex w-full flex-column gap-y-5">
|
||||
<div class="m-0 flex flex-row justify-center gap-x-5">
|
||||
<div
|
||||
class="rounded-full w-36 h-36 flex flex-column align-items-center pt-4"
|
||||
style="border: 1px solid green"
|
||||
>
|
||||
<div class="font-bold">
|
||||
{{ $t('peer_count') }}
|
||||
</div>
|
||||
<div class="text-5xl mt-1">
|
||||
{{ peerCount }}
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
<Card v-if="curNetworkInst?.error_msg">
|
||||
<template #title>Run Network Error</template>
|
||||
<template #content>
|
||||
<div class="flex flex-column gap-y-5">
|
||||
<div class="text-red-500">
|
||||
{{ curNetworkInst.error_msg }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
<div
|
||||
class="rounded-full w-36 h-36 flex flex-column align-items-center pt-4"
|
||||
style="border: 1px solid purple"
|
||||
>
|
||||
<div class="font-bold">
|
||||
{{ $t('upload') }}
|
||||
</div>
|
||||
<div class="text-xl mt-2">
|
||||
{{ txRate }}/s
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card v-if="!curNetworkInst?.error_msg">
|
||||
<template #title>{{ $t('my_node_info') }}</template>
|
||||
<template #content>
|
||||
<div class="flex w-full flex-column gap-y-5">
|
||||
<div class="m-0 flex flex-row justify-center gap-x-5">
|
||||
<div class="rounded-full w-36 h-36 flex flex-column align-items-center pt-4"
|
||||
style="border: 1px solid green">
|
||||
<div class="font-bold">
|
||||
{{ $t('peer_count') }}
|
||||
</div>
|
||||
<div class="text-5xl mt-1">{{ peerCount }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="rounded-full w-36 h-36 flex flex-column align-items-center pt-4"
|
||||
style="border: 1px solid fuchsia"
|
||||
>
|
||||
<div class="font-bold">
|
||||
{{ $t('download') }}
|
||||
</div>
|
||||
<div class="text-xl mt-2">
|
||||
{{ rxRate }}/s
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rounded-full w-36 h-36 flex flex-column align-items-center pt-4"
|
||||
style="border: 1px solid purple">
|
||||
<div class="font-bold">
|
||||
{{ $t('upload') }}
|
||||
</div>
|
||||
<div class="text-xl mt-2">{{ txRate }}/s</div>
|
||||
</div>
|
||||
<div class="flex flex-row align-items-center flex-wrap w-full">
|
||||
<Chip
|
||||
v-for="(chip, i) in myNodeInfoChips" :key="i" :label="chip.label" :icon="chip.icon"
|
||||
class="mr-2 mt-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="rounded-full w-36 h-36 flex flex-column align-items-center pt-4"
|
||||
style="border: 1px solid fuchsia">
|
||||
<div class="font-bold">
|
||||
{{ $t('download') }}
|
||||
</div>
|
||||
<div class="text-xl mt-2">{{ rxRate }}/s</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="myNodeInfo" class="m-0 flex flex-row justify-center gap-x-5 text-sm">
|
||||
<Button severity="info" :label="$t('show_vpn_portal_config')" @click="showVpnPortalConfig" />
|
||||
<Button severity="info" :label="$t('show_event_log')" @click="showEventLogs" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<div class="flex flex-row align-items-center flex-wrap w-full">
|
||||
<Chip v-for="chip in myNodeInfoChips" :label="chip.label" :icon="chip.icon" class="mr-2 mt-2">
|
||||
</Chip>
|
||||
</div>
|
||||
<Divider />
|
||||
|
||||
<div class="m-0 flex flex-row justify-center gap-x-5 text-sm" v-if="myNodeInfo">
|
||||
<Button severity="info" :label="$t('show_vpn_portal_config')" @click="showVpnPortalConfig" />
|
||||
<Button severity="info" :label="$t('show_event_log')" @click="showEventLogs" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
<Divider />
|
||||
|
||||
<Card v-if="!curNetworkInst?.error_msg">
|
||||
<template #title>{{ $t('peer_info') }}</template>
|
||||
<template #content>
|
||||
<DataTable :value="peerRouteInfos" tableStyle="min-width: 50rem">
|
||||
<Column field="route.ipv4_addr" :header="$t('virtual_ipv4')"></Column>
|
||||
<Column field="route.hostname" :header="$t('hostname')"></Column>
|
||||
<Column :field="routeCost" :header="$t('route_cost')"></Column>
|
||||
<Column :field="latencyMs" :header="$t('latency')"></Column>
|
||||
<Column :field="txBytes" :header="$t('upload_bytes')"></Column>
|
||||
<Column :field="rxBytes" :header="$t('download_bytes')"></Column>
|
||||
<Column :field="lossRate" :header="$t('loss_rate')"></Column>
|
||||
</DataTable>
|
||||
</template>
|
||||
</Card>
|
||||
|
||||
</div>
|
||||
<Card v-if="!curNetworkInst?.error_msg">
|
||||
<template #title>
|
||||
{{ $t('peer_info') }}
|
||||
</template>
|
||||
<template #content>
|
||||
<DataTable :value="peerRouteInfos" table-style="min-width: 50rem">
|
||||
<Column field="route.ipv4_addr" :header="$t('virtual_ipv4')" />
|
||||
<Column field="route.hostname" :header="$t('hostname')" />
|
||||
<Column :field="routeCost" :header="$t('route_cost')" />
|
||||
<Column :field="latencyMs" :header="$t('latency')" />
|
||||
<Column :field="txBytes" :header="$t('upload_bytes')" />
|
||||
<Column :field="rxBytes" :header="$t('download_bytes')" />
|
||||
<Column :field="lossRate" :header="$t('loss_rate')" />
|
||||
</DataTable>
|
||||
</template>
|
||||
</Card>
|
||||
</div>
|
||||
</template>
|
|
@ -1,22 +1,22 @@
|
|||
import { NetworkConfig } from "~/types/network";
|
||||
import { invoke } from "@tauri-apps/api/tauri";
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
import type { NetworkConfig } from '~/types/network'
|
||||
|
||||
export async function parseNetworkConfig(cfg: NetworkConfig): Promise<string> {
|
||||
const ret: string = await invoke("parse_network_config", { cfg: JSON.stringify(cfg) });
|
||||
return ret;
|
||||
const ret: string = await invoke('parse_network_config', { cfg: JSON.stringify(cfg) })
|
||||
return ret
|
||||
}
|
||||
|
||||
export async function runNetworkInstance(cfg: NetworkConfig) {
|
||||
const ret: string = await invoke("run_network_instance", { cfg: JSON.stringify(cfg) });
|
||||
return ret;
|
||||
const ret: string = await invoke('run_network_instance', { cfg: JSON.stringify(cfg) })
|
||||
return ret
|
||||
}
|
||||
|
||||
export async function retainNetworkInstance(instanceIds: Array<string>) {
|
||||
const ret: string = await invoke("retain_network_instance", { instanceIds: JSON.stringify(instanceIds) });
|
||||
return ret;
|
||||
const ret: string = await invoke('retain_network_instance', { instanceIds: JSON.stringify(instanceIds) })
|
||||
return ret
|
||||
}
|
||||
|
||||
export async function collectNetworkInfos() {
|
||||
const ret: string = await invoke("collect_network_infos", {});
|
||||
return JSON.parse(ret);
|
||||
const ret: string = await invoke('collect_network_infos', {})
|
||||
return JSON.parse(ret)
|
||||
}
|
||||
|
|
|
@ -1,37 +1,37 @@
|
|||
import { setupLayouts } from "virtual:generated-layouts";
|
||||
import { createRouter, createWebHistory } from "vue-router/auto";
|
||||
import { setupLayouts } from 'virtual:generated-layouts'
|
||||
import { createRouter, createWebHistory } from 'vue-router/auto'
|
||||
|
||||
import PrimeVue from 'primevue/config';
|
||||
import ToastService from 'primevue/toastservice';
|
||||
import App from "~/App.vue";
|
||||
import PrimeVue from 'primevue/config'
|
||||
import ToastService from 'primevue/toastservice'
|
||||
import App from '~/App.vue'
|
||||
|
||||
import "~/styles.css";
|
||||
import "primevue/resources/themes/aura-light-green/theme.css";
|
||||
import "primeicons/primeicons.css";
|
||||
import "primeflex/primeflex.css";
|
||||
import { i18n, loadLanguageAsync } from "~/modules/i18n";
|
||||
import '~/styles.css'
|
||||
import 'primevue/resources/themes/aura-light-green/theme.css'
|
||||
import 'primeicons/primeicons.css'
|
||||
import 'primeflex/primeflex.css'
|
||||
import { i18n, loadLanguageAsync } from '~/modules/i18n'
|
||||
|
||||
if (import.meta.env.PROD) {
|
||||
document.addEventListener("keydown", (event) => {
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (
|
||||
event.key === "F5" ||
|
||||
(event.ctrlKey && event.key === "r") ||
|
||||
(event.metaKey && event.key === "r")
|
||||
event.key === 'F5'
|
||||
|| (event.ctrlKey && event.key === 'r')
|
||||
|| (event.metaKey && event.key === 'r')
|
||||
)
|
||||
event.preventDefault();
|
||||
});
|
||||
event.preventDefault()
|
||||
})
|
||||
|
||||
document.addEventListener("contextmenu", (event) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
document.addEventListener('contextmenu', (event) => {
|
||||
event.preventDefault()
|
||||
})
|
||||
}
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(),
|
||||
extendRoutes: (routes) => setupLayouts(routes),
|
||||
});
|
||||
extendRoutes: routes => setupLayouts(routes),
|
||||
})
|
||||
|
||||
app.use(router)
|
||||
app.use(createPinia())
|
||||
|
@ -40,5 +40,4 @@ loadLanguageAsync(localStorage.getItem('lang') || 'en')
|
|||
|
||||
app.use(PrimeVue)
|
||||
app.use(ToastService)
|
||||
app.mount("#app")
|
||||
|
||||
app.mount('#app')
|
||||
|
|
|
@ -21,7 +21,7 @@ const loadedLanguages: string[] = []
|
|||
|
||||
function setI18nLanguage(lang: Locale) {
|
||||
i18n.global.locale.value = lang as any
|
||||
localStorage.setItem("locale", lang);
|
||||
localStorage.setItem('locale', lang)
|
||||
return lang
|
||||
}
|
||||
|
||||
|
@ -35,12 +35,13 @@ export async function loadLanguageAsync(lang: string): Promise<Locale> {
|
|||
return setI18nLanguage(lang)
|
||||
|
||||
// If the language hasn't been loaded yet
|
||||
let messages;
|
||||
let messages
|
||||
|
||||
try {
|
||||
messages = await localesMap[lang]()
|
||||
} catch {
|
||||
messages = await localesMap['en']()
|
||||
}
|
||||
catch {
|
||||
messages = await localesMap.en()
|
||||
}
|
||||
|
||||
i18n.global.setLocaleMessage(lang, messages.default)
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<script setup lang="ts">
|
||||
import Stepper from 'primevue/stepper';
|
||||
import StepperPanel from 'primevue/stepperpanel';
|
||||
import Stepper from 'primevue/stepper'
|
||||
import StepperPanel from 'primevue/stepperpanel'
|
||||
|
||||
import { useToast } from "primevue/usetoast";
|
||||
import { useToast } from 'primevue/usetoast'
|
||||
|
||||
import { exit } from '@tauri-apps/api/process'
|
||||
import Config from '~/components/Config.vue'
|
||||
import Status from '~/components/Status.vue'
|
||||
|
||||
import Config from '~/components/Config.vue';
|
||||
import Status from '~/components/Status.vue';
|
||||
import type { NetworkConfig } from '~/types/network'
|
||||
import { loadLanguageAsync } from '~/modules/i18n'
|
||||
|
||||
import { exit } from '@tauri-apps/api/process';
|
||||
import { NetworkConfig } from '~/types/network';
|
||||
import { loadLanguageAsync } from '~/modules/i18n';
|
||||
const { t, locale } = useI18n()
|
||||
const visible = ref(false);
|
||||
const tomlConfig = ref("");
|
||||
const visible = ref(false)
|
||||
const tomlConfig = ref('')
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
|
@ -21,103 +21,106 @@ const items = ref([
|
|||
icon: 'pi pi-file-edit',
|
||||
command: async () => {
|
||||
try {
|
||||
const ret = await parseNetworkConfig(networkStore.curNetwork);
|
||||
tomlConfig.value = ret;
|
||||
} catch (e: any) {
|
||||
tomlConfig.value = e;
|
||||
const ret = await parseNetworkConfig(networkStore.curNetwork)
|
||||
tomlConfig.value = ret
|
||||
}
|
||||
visible.value = true;
|
||||
}
|
||||
catch (e: any) {
|
||||
tomlConfig.value = e
|
||||
}
|
||||
visible.value = true
|
||||
},
|
||||
},
|
||||
{
|
||||
label: () => t('del_cur_network'),
|
||||
icon: 'pi pi-times',
|
||||
command: async () => {
|
||||
networkStore.removeNetworkInstance(networkStore.curNetwork.instance_id);
|
||||
await retainNetworkInstance(networkStore.networkInstanceIds);
|
||||
networkStore.delCurNetwork();
|
||||
networkStore.removeNetworkInstance(networkStore.curNetwork.instance_id)
|
||||
await retainNetworkInstance(networkStore.networkInstanceIds)
|
||||
networkStore.delCurNetwork()
|
||||
},
|
||||
disabled: () => networkStore.networkList.length <= 1,
|
||||
},
|
||||
])
|
||||
|
||||
enum Severity {
|
||||
None = "none",
|
||||
Success = "success",
|
||||
Info = "info",
|
||||
Warn = "warn",
|
||||
Error = "error",
|
||||
None = 'none',
|
||||
Success = 'success',
|
||||
Info = 'info',
|
||||
Warn = 'warn',
|
||||
Error = 'error',
|
||||
}
|
||||
|
||||
const messageBarSeverity = ref(Severity.None);
|
||||
const messageBarContent = ref("");
|
||||
const messageBarSeverity = ref(Severity.None)
|
||||
const messageBarContent = ref('')
|
||||
|
||||
const toast = useToast();
|
||||
const toast = useToast()
|
||||
|
||||
const networkStore = useNetworkStore();
|
||||
const networkStore = useNetworkStore()
|
||||
|
||||
const addNewNetwork = () => {
|
||||
networkStore.addNewNetwork();
|
||||
networkStore.curNetwork = networkStore.lastNetwork;
|
||||
function addNewNetwork() {
|
||||
networkStore.addNewNetwork()
|
||||
networkStore.curNetwork = networkStore.lastNetwork
|
||||
}
|
||||
|
||||
const networkMenuName = (network: NetworkConfig) => {
|
||||
return network.network_name + " (" + network.instance_id + ")";
|
||||
function networkMenuName(network: NetworkConfig) {
|
||||
return `${network.network_name} (${network.instance_id})`
|
||||
}
|
||||
|
||||
networkStore.$subscribe(async () => {
|
||||
networkStore.saveToLocalStorage();
|
||||
networkStore.saveToLocalStorage()
|
||||
try {
|
||||
await parseNetworkConfig(networkStore.curNetwork);
|
||||
messageBarSeverity.value = Severity.None;
|
||||
} catch (e: any) {
|
||||
messageBarContent.value = e;
|
||||
messageBarSeverity.value = Severity.Error;
|
||||
await parseNetworkConfig(networkStore.curNetwork)
|
||||
messageBarSeverity.value = Severity.None
|
||||
}
|
||||
});
|
||||
catch (e: any) {
|
||||
messageBarContent.value = e
|
||||
messageBarSeverity.value = Severity.Error
|
||||
}
|
||||
})
|
||||
|
||||
async function runNetworkCb(cfg: NetworkConfig, cb: (e: MouseEvent) => void) {
|
||||
cb({} as MouseEvent);
|
||||
networkStore.removeNetworkInstance(cfg.instance_id);
|
||||
await retainNetworkInstance(networkStore.networkInstanceIds);
|
||||
networkStore.addNetworkInstance(cfg.instance_id);
|
||||
cb({} as MouseEvent)
|
||||
networkStore.removeNetworkInstance(cfg.instance_id)
|
||||
await retainNetworkInstance(networkStore.networkInstanceIds)
|
||||
networkStore.addNetworkInstance(cfg.instance_id)
|
||||
|
||||
try {
|
||||
await runNetworkInstance(cfg);
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
toast.add({ severity: 'info', detail: e });
|
||||
await runNetworkInstance(cfg)
|
||||
}
|
||||
catch (e: any) {
|
||||
// console.error(e)
|
||||
toast.add({ severity: 'info', detail: e })
|
||||
}
|
||||
}
|
||||
|
||||
async function stopNetworkCb(cfg: NetworkConfig, cb: (e: MouseEvent) => void) {
|
||||
console.log("stopNetworkCb", cfg, cb);
|
||||
cb({} as MouseEvent);
|
||||
networkStore.removeNetworkInstance(cfg.instance_id);
|
||||
await retainNetworkInstance(networkStore.networkInstanceIds);
|
||||
// console.log('stopNetworkCb', cfg, cb)
|
||||
cb({} as MouseEvent)
|
||||
networkStore.removeNetworkInstance(cfg.instance_id)
|
||||
await retainNetworkInstance(networkStore.networkInstanceIds)
|
||||
}
|
||||
|
||||
async function updateNetworkInfos() {
|
||||
networkStore.updateWithNetworkInfos(await collectNetworkInfos());
|
||||
networkStore.updateWithNetworkInfos(await collectNetworkInfos())
|
||||
}
|
||||
|
||||
let intervalId = 0;
|
||||
let intervalId = 0
|
||||
onMounted(() => {
|
||||
intervalId = window.setInterval(async () => {
|
||||
await updateNetworkInfos();
|
||||
}, 500);
|
||||
});
|
||||
await updateNetworkInfos()
|
||||
}, 500)
|
||||
})
|
||||
onUnmounted(() => clearInterval(intervalId))
|
||||
|
||||
const curNetworkHasInstance = computed(() => {
|
||||
return networkStore.networkInstanceIds.includes(networkStore.curNetworkId);
|
||||
});
|
||||
return networkStore.networkInstanceIds.includes(networkStore.curNetworkId)
|
||||
})
|
||||
|
||||
const activeStep = computed(() => {
|
||||
return curNetworkHasInstance.value ? 1 : 0;
|
||||
});
|
||||
return curNetworkHasInstance.value ? 1 : 0
|
||||
})
|
||||
|
||||
const setting_menu = ref();
|
||||
const setting_menu = ref()
|
||||
const setting_menu_items = ref([
|
||||
{
|
||||
label: () => t('settings'),
|
||||
|
@ -126,28 +129,30 @@ const setting_menu_items = ref([
|
|||
label: () => t('exchange_language'),
|
||||
icon: 'pi pi-language',
|
||||
command: async () => {
|
||||
await loadLanguageAsync((locale.value === 'en' ? 'cn' : 'en'));
|
||||
}
|
||||
await loadLanguageAsync((locale.value === 'en' ? 'cn' : 'en'))
|
||||
},
|
||||
},
|
||||
{
|
||||
label: () => t('exit'),
|
||||
icon: 'pi pi-times',
|
||||
command: async () => {
|
||||
await exit(1);
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
await exit(1)
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
])
|
||||
|
||||
const toggle_setting_menu = (event: any) => {
|
||||
setting_menu.value.toggle(event);
|
||||
};
|
||||
function toggle_setting_menu(event: any) {
|
||||
setting_menu.value.toggle(event)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
networkStore.loadFromLocalStorage();
|
||||
});
|
||||
networkStore.loadFromLocalStorage()
|
||||
})
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -161,7 +166,7 @@ onMounted(async () => {
|
|||
</Panel>
|
||||
<Divider />
|
||||
<div class="flex justify-content-end gap-2">
|
||||
<Button type="button" :label="$t('close')" @click="visible = false"></Button>
|
||||
<Button type="button" :label="$t('close')" @click="visible = false" />
|
||||
</div>
|
||||
</Dialog>
|
||||
|
||||
|
@ -169,23 +174,29 @@ onMounted(async () => {
|
|||
<Toolbar>
|
||||
<template #start>
|
||||
<div class="flex align-items-center gap-2">
|
||||
<Button icon="pi pi-plus" class="mr-2" severity="primary" :label="$t('add_new_network')"
|
||||
@click="addNewNetwork" />
|
||||
<Button
|
||||
icon="pi pi-plus" class="mr-2" severity="primary" :label="$t('add_new_network')"
|
||||
@click="addNewNetwork"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #center>
|
||||
<div class="min-w-80 mr-20">
|
||||
<Dropdown v-model="networkStore.curNetwork" :options="networkStore.networkList"
|
||||
:optionLabel="networkMenuName" :placeholder="$t('select_network')" :highlightOnSelect="true"
|
||||
:checkmark="true" class="w-full md:w-32rem" />
|
||||
<Dropdown
|
||||
v-model="networkStore.curNetwork" :options="networkStore.networkList"
|
||||
:option-label="networkMenuName" :placeholder="$t('select_network')" :highlight-on-select="true"
|
||||
:checkmark="true" class="w-full md:w-32rem"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #end>
|
||||
<Button icon="pi pi-cog" class="mr-2" severity="secondary" aria-haspopup="true" @click="toggle_setting_menu"
|
||||
:label="$t('settings')" aria-controls="overlay_setting_menu" />
|
||||
<Menu ref="setting_menu" id="overlay_setting_menu" :model="setting_menu_items" :popup="true" />
|
||||
<Button
|
||||
icon="pi pi-cog" class="mr-2" severity="secondary" aria-haspopup="true" :label="$t('settings')"
|
||||
aria-controls="overlay_setting_menu" @click="toggle_setting_menu"
|
||||
/>
|
||||
<Menu id="overlay_setting_menu" ref="setting_menu" :model="setting_menu_items" :popup="true" />
|
||||
</template>
|
||||
</Toolbar>
|
||||
</div>
|
||||
|
@ -193,8 +204,10 @@ onMounted(async () => {
|
|||
<Stepper class="h-full overflow-y-auto" :active-step="activeStep">
|
||||
<StepperPanel :header="$t('config_network')" class="w">
|
||||
<template #content="{ nextCallback }">
|
||||
<Config @run-network="runNetworkCb($event, nextCallback)" :instance-id="networkStore.curNetworkId"
|
||||
:config-invalid="messageBarSeverity != Severity.None" />
|
||||
<Config
|
||||
:instance-id="networkStore.curNetworkId" :config-invalid="messageBarSeverity !== Severity.None"
|
||||
@run-network="runNetworkCb($event, nextCallback)"
|
||||
/>
|
||||
</template>
|
||||
</StepperPanel>
|
||||
<StepperPanel :header="$t('running')">
|
||||
|
@ -203,21 +216,22 @@ onMounted(async () => {
|
|||
<Status :instance-id="networkStore.curNetworkId" />
|
||||
</div>
|
||||
<div class="flex pt-4 justify-content-center">
|
||||
<Button :label="$t('stop_network')" severity="danger" icon="pi pi-arrow-left"
|
||||
@click="stopNetworkCb(networkStore.curNetwork, prevCallback)" />
|
||||
<Button
|
||||
:label="$t('stop_network')" severity="danger" icon="pi pi-arrow-left"
|
||||
@click="stopNetworkCb(networkStore.curNetwork, prevCallback)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</StepperPanel>
|
||||
</Stepper>
|
||||
|
||||
<div>
|
||||
<Menubar :model="items" breakpoint="300px">
|
||||
</Menubar>
|
||||
<Menubar :model="items" breakpoint="300px" />
|
||||
<InlineMessage v-if="messageBarSeverity !== Severity.None" class="absolute bottom-0 right-0" severity="error">
|
||||
{{ messageBarContent }}</InlineMessage>
|
||||
{{ messageBarContent }}
|
||||
</InlineMessage>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
@ -246,7 +260,3 @@ body {
|
|||
height: 100%;
|
||||
} */
|
||||
</style>
|
||||
|
||||
|
||||
<script lang="ts">
|
||||
</script>
|
|
@ -1,8 +1,9 @@
|
|||
import { DEFAULT_NETWORK_CONFIG, NetworkConfig, NetworkInstance } from '~/types/network';
|
||||
import type { NetworkConfig, NetworkInstance } from '~/types/network'
|
||||
import { DEFAULT_NETWORK_CONFIG } from '~/types/network'
|
||||
|
||||
export const useNetworkStore = defineStore('networkStore', {
|
||||
state: () => {
|
||||
const networkList = [DEFAULT_NETWORK_CONFIG()];
|
||||
const networkList = [DEFAULT_NETWORK_CONFIG()]
|
||||
return {
|
||||
// for initially empty lists
|
||||
networkList: networkList as NetworkConfig[],
|
||||
|
@ -18,79 +19,79 @@ export const useNetworkStore = defineStore('networkStore', {
|
|||
|
||||
getters: {
|
||||
lastNetwork(): NetworkConfig {
|
||||
return this.networkList[this.networkList.length - 1];
|
||||
return this.networkList[this.networkList.length - 1]
|
||||
},
|
||||
|
||||
curNetworkId(): string {
|
||||
return this.curNetwork.instance_id;
|
||||
return this.curNetwork.instance_id
|
||||
},
|
||||
|
||||
networkInstances(): Array<NetworkInstance> {
|
||||
return Object.values(this.instances);
|
||||
return Object.values(this.instances)
|
||||
},
|
||||
|
||||
networkInstanceIds(): Array<string> {
|
||||
return Object.keys(this.instances);
|
||||
}
|
||||
return Object.keys(this.instances)
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
addNewNetwork() {
|
||||
this.networkList.push(DEFAULT_NETWORK_CONFIG());
|
||||
this.networkList.push(DEFAULT_NETWORK_CONFIG())
|
||||
},
|
||||
|
||||
delCurNetwork() {
|
||||
const curNetworkIdx = this.networkList.indexOf(this.curNetwork);
|
||||
this.networkList.splice(curNetworkIdx, 1);
|
||||
const nextCurNetworkIdx = Math.min(curNetworkIdx, this.networkList.length - 1);
|
||||
this.curNetwork = this.networkList[nextCurNetworkIdx];
|
||||
const curNetworkIdx = this.networkList.indexOf(this.curNetwork)
|
||||
this.networkList.splice(curNetworkIdx, 1)
|
||||
const nextCurNetworkIdx = Math.min(curNetworkIdx, this.networkList.length - 1)
|
||||
this.curNetwork = this.networkList[nextCurNetworkIdx]
|
||||
},
|
||||
|
||||
removeNetworkInstance(instanceId: string) {
|
||||
delete this.instances[instanceId];
|
||||
delete this.instances[instanceId]
|
||||
},
|
||||
|
||||
addNetworkInstance(instanceId: string) {
|
||||
this.instances[instanceId] = {
|
||||
instance_id: instanceId,
|
||||
running: false,
|
||||
error_msg: "",
|
||||
error_msg: '',
|
||||
detail: {},
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
updateWithNetworkInfos(networkInfos: Record<string, any>) {
|
||||
this.networkInfos = networkInfos;
|
||||
this.networkInfos = networkInfos
|
||||
for (const [instanceId, info] of Object.entries(networkInfos)) {
|
||||
if (this.instances[instanceId] === undefined) {
|
||||
this.addNetworkInstance(instanceId);
|
||||
}
|
||||
this.instances[instanceId].running = info["running"];
|
||||
this.instances[instanceId].error_msg = info["error_msg"];
|
||||
this.instances[instanceId].detail = info;
|
||||
if (this.instances[instanceId] === undefined)
|
||||
this.addNetworkInstance(instanceId)
|
||||
|
||||
this.instances[instanceId].running = info.running
|
||||
this.instances[instanceId].error_msg = info.error_msg
|
||||
this.instances[instanceId].detail = info
|
||||
}
|
||||
},
|
||||
|
||||
loadFromLocalStorage() {
|
||||
const networkList = JSON.parse(localStorage.getItem("networkList") || '[]');
|
||||
let result = [];
|
||||
const networkList = JSON.parse(localStorage.getItem('networkList') || '[]')
|
||||
const result = []
|
||||
for (const cfg of networkList) {
|
||||
result.push({
|
||||
...DEFAULT_NETWORK_CONFIG,
|
||||
...cfg,
|
||||
});
|
||||
})
|
||||
}
|
||||
if (result.length === 0) {
|
||||
result.push(DEFAULT_NETWORK_CONFIG);
|
||||
}
|
||||
this.networkList = result;
|
||||
this.curNetwork = this.networkList[0];
|
||||
if (result.length === 0)
|
||||
result.push(DEFAULT_NETWORK_CONFIG)
|
||||
|
||||
this.networkList = result
|
||||
this.curNetwork = this.networkList[0]
|
||||
},
|
||||
|
||||
saveToLocalStorage() {
|
||||
localStorage.setItem("networkList", JSON.stringify(this.networkList));
|
||||
}
|
||||
}
|
||||
localStorage.setItem('networkList', JSON.stringify(this.networkList))
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (import.meta.hot)
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
@layer tailwind-base, primevue, tailwind-utilities;
|
||||
|
||||
@layer tailwind-base {
|
||||
@tailwind base;
|
||||
@tailwind base;
|
||||
}
|
||||
|
||||
@layer tailwind-utilities {
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
}
|
||||
|
||||
:root {
|
||||
|
@ -26,8 +26,8 @@
|
|||
}
|
||||
|
||||
.card {
|
||||
background: var(--surface-card);
|
||||
padding: 2rem;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 1rem;
|
||||
background: var(--surface-card);
|
||||
padding: 2rem;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 1rem;
|
||||
}
|
|
@ -1,72 +1,72 @@
|
|||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
export enum NetworkingMethod {
|
||||
PublicServer = "PublicServer",
|
||||
Manual = "Manual",
|
||||
Standalone = "Standalone",
|
||||
PublicServer = 'PublicServer',
|
||||
Manual = 'Manual',
|
||||
Standalone = 'Standalone',
|
||||
}
|
||||
|
||||
export interface NetworkConfig {
|
||||
instance_id: string,
|
||||
instance_id: string
|
||||
|
||||
virtual_ipv4: string
|
||||
network_name: string
|
||||
network_secret: string
|
||||
|
||||
networking_method: NetworkingMethod,
|
||||
networking_method: NetworkingMethod
|
||||
|
||||
public_server_url: string,
|
||||
peer_urls: Array<string>,
|
||||
public_server_url: string
|
||||
peer_urls: Array<string>
|
||||
|
||||
proxy_cidrs: Array<string>,
|
||||
proxy_cidrs: Array<string>
|
||||
|
||||
enable_vpn_portal: boolean,
|
||||
vpn_portal_listne_port: number,
|
||||
vpn_portal_client_network_addr: string,
|
||||
vpn_portal_client_network_len: number,
|
||||
enable_vpn_portal: boolean
|
||||
vpn_portal_listne_port: number
|
||||
vpn_portal_client_network_addr: string
|
||||
vpn_portal_client_network_len: number
|
||||
|
||||
advanced_settings: boolean,
|
||||
advanced_settings: boolean
|
||||
|
||||
listener_urls: Array<string>,
|
||||
rpc_port: number,
|
||||
listener_urls: Array<string>
|
||||
rpc_port: number
|
||||
}
|
||||
|
||||
export const DEFAULT_NETWORK_CONFIG = (): NetworkConfig => {
|
||||
export function DEFAULT_NETWORK_CONFIG(): NetworkConfig {
|
||||
return {
|
||||
instance_id: uuidv4(),
|
||||
|
||||
virtual_ipv4: "",
|
||||
network_name: "default",
|
||||
network_secret: "",
|
||||
virtual_ipv4: '',
|
||||
network_name: 'default',
|
||||
network_secret: '',
|
||||
|
||||
networking_method: NetworkingMethod.PublicServer,
|
||||
|
||||
public_server_url: "tcp://easytier.public.kkrainbow.top:11010",
|
||||
public_server_url: 'tcp://easytier.public.kkrainbow.top:11010',
|
||||
peer_urls: [],
|
||||
|
||||
proxy_cidrs: [],
|
||||
|
||||
enable_vpn_portal: false,
|
||||
vpn_portal_listne_port: 22022,
|
||||
vpn_portal_client_network_addr: "",
|
||||
vpn_portal_client_network_addr: '',
|
||||
vpn_portal_client_network_len: 24,
|
||||
|
||||
advanced_settings: false,
|
||||
|
||||
listener_urls: [
|
||||
"tcp://0.0.0.0:11010",
|
||||
"udp://0.0.0.0:11010",
|
||||
"wg://0.0.0.0:11011",
|
||||
'tcp://0.0.0.0:11010',
|
||||
'udp://0.0.0.0:11010',
|
||||
'wg://0.0.0.0:11011',
|
||||
],
|
||||
rpc_port: 15888,
|
||||
}
|
||||
}
|
||||
|
||||
export interface NetworkInstance {
|
||||
instance_id: string,
|
||||
instance_id: string
|
||||
|
||||
running: boolean,
|
||||
error_msg: string,
|
||||
running: boolean
|
||||
error_msg: string
|
||||
|
||||
detail: any,
|
||||
detail: any
|
||||
}
|
|
@ -1,12 +1,11 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/**/*.{vue,js,ts,jsx,tsx}",
|
||||
'./index.html',
|
||||
'./src/**/*.{vue,js,ts,jsx,tsx}',
|
||||
],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
||||
|
|
|
@ -9,8 +9,6 @@
|
|||
"baseUrl": ".",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
"paths": {
|
||||
"~/*": [
|
||||
"src/*"
|
||||
|
@ -23,10 +21,12 @@
|
|||
"unplugin-vue-macros/macros-global",
|
||||
"unplugin-vue-router/client"
|
||||
],
|
||||
"allowImportingTsExtensions": true,
|
||||
"allowJs": true,
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"noUnusedLocals": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
|
|
|
@ -9,13 +9,13 @@ import VueI18n from '@intlify/unplugin-vue-i18n/vite'
|
|||
import VueDevTools from 'vite-plugin-vue-devtools'
|
||||
import VueRouter from 'unplugin-vue-router/vite'
|
||||
import { VueRouterAutoImports } from 'unplugin-vue-router'
|
||||
import { PrimeVueResolver } from 'unplugin-vue-components/resolvers';
|
||||
import { PrimeVueResolver } from 'unplugin-vue-components/resolvers'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
resolve: {
|
||||
alias: {
|
||||
"~/": `${path.resolve(__dirname, "src")}/`,
|
||||
'~/': `${path.resolve(__dirname, 'src')}/`,
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
|
@ -64,8 +64,8 @@ export default defineConfig(async () => ({
|
|||
include: [/\.vue$/, /\.vue\?vue/, /\.md$/],
|
||||
dts: 'src/components.d.ts',
|
||||
resolvers: [
|
||||
PrimeVueResolver()
|
||||
]
|
||||
PrimeVueResolver(),
|
||||
],
|
||||
}),
|
||||
|
||||
// https://github.com/intlify/bundle-tools/tree/main/packages/unplugin-vue-i18n
|
||||
|
@ -90,7 +90,7 @@ export default defineConfig(async () => ({
|
|||
strictPort: true,
|
||||
watch: {
|
||||
// 3. tell vite to ignore watching `src-tauri`
|
||||
ignored: ["**/src-tauri/**"],
|
||||
ignored: ['**/src-tauri/**'],
|
||||
},
|
||||
},
|
||||
}));
|
||||
}))
|
||||
|
|
Loading…
Reference in New Issue
Block a user