mirror of
https://github.com/EasyTier/EasyTier.git
synced 2024-11-16 11:42:27 +08:00
Perf/front page (#316)
* 🐳 chore: dependencies * 🐞 fix: minor style issues fixed background white patches in dark mode fixed the line height of the status label, which resulted in a bloated appearance * 🌈 style: lint * ✨ feat: about
This commit is contained in:
parent
1609c97574
commit
4342be29d7
|
@ -11,7 +11,7 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
"eslint.experimental.useFlatConfig": true,
|
"eslint.useFlatConfig": true,
|
||||||
"prettier.enable": false,
|
"prettier.enable": false,
|
||||||
"editor.formatOnSave": false,
|
"editor.formatOnSave": false,
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
|
|
|
@ -34,7 +34,6 @@ rustup target add aarch64-linux-android
|
||||||
install java 20
|
install java 20
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Java version depend on gradle version specified in (easytier-gui\src-tauri\gen\android\build.gradle.kts)
|
Java version depend on gradle version specified in (easytier-gui\src-tauri\gen\android\build.gradle.kts)
|
||||||
|
|
||||||
See [Gradle compatibility matrix](https://docs.gradle.org/current/userguide/compatibility.html) for detail .
|
See [Gradle compatibility matrix](https://docs.gradle.org/current/userguide/compatibility.html) for detail .
|
||||||
|
|
|
@ -75,3 +75,12 @@ dhcp_experimental_warning: 实验性警告!使用DHCP时如果组网环境中
|
||||||
tray:
|
tray:
|
||||||
show: 显示 / 隐藏
|
show: 显示 / 隐藏
|
||||||
exit: 退出
|
exit: 退出
|
||||||
|
|
||||||
|
about:
|
||||||
|
title: 关于
|
||||||
|
version: 版本
|
||||||
|
author: 作者
|
||||||
|
homepage: 主页
|
||||||
|
license: 许可证
|
||||||
|
description: 一个简单、安全、去中心化的内网穿透 VPN 组网方案,使用 Rust 语言和 Tokio 框架实现。
|
||||||
|
check_update: 检查更新
|
||||||
|
|
|
@ -75,3 +75,12 @@ dhcp_experimental_warning: Experimental warning! if there is an IP conflict in t
|
||||||
tray:
|
tray:
|
||||||
show: Show / Hide
|
show: Show / Hide
|
||||||
exit: Exit
|
exit: Exit
|
||||||
|
|
||||||
|
about:
|
||||||
|
title: About
|
||||||
|
version: Version
|
||||||
|
author: Author
|
||||||
|
homepage: Homepage
|
||||||
|
license: License
|
||||||
|
description: 'EasyTier is a simple, safe and decentralized VPN networking solution implemented with the Rust language and Tokio framework.'
|
||||||
|
check_update: Check Update
|
||||||
|
|
|
@ -12,50 +12,49 @@
|
||||||
"lint:fix": "eslint . --ignore-pattern src-tauri --fix"
|
"lint:fix": "eslint . --ignore-pattern src-tauri --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@primevue/themes": "^4.0.4",
|
"@primevue/themes": "^4.0.5",
|
||||||
"@tauri-apps/plugin-autostart": "2.0.0-rc.0",
|
"@tauri-apps/plugin-autostart": "2.0.0-rc.1",
|
||||||
"@tauri-apps/plugin-clipboard-manager": "2.0.0-rc.0",
|
"@tauri-apps/plugin-clipboard-manager": "2.0.0-rc.1",
|
||||||
"@tauri-apps/plugin-os": "2.0.0-rc.0",
|
"@tauri-apps/plugin-os": "2.0.0-rc.1",
|
||||||
"@tauri-apps/plugin-process": "2.0.0-rc.0",
|
"@tauri-apps/plugin-process": "2.0.0-rc.1",
|
||||||
"@tauri-apps/plugin-shell": "2.0.0-rc.0",
|
"@tauri-apps/plugin-shell": "2.0.0-rc.1",
|
||||||
"aura": "link:@primevue/themes/aura",
|
"aura": "link:@primevue/themes/aura",
|
||||||
"pinia": "^2.2.1",
|
"pinia": "^2.2.2",
|
||||||
"primeflex": "^3.3.1",
|
"primeflex": "^3.3.1",
|
||||||
"primeicons": "^7.0.0",
|
"primeicons": "^7.0.0",
|
||||||
"primevue": "^4.0.4",
|
"primevue": "^4.0.5",
|
||||||
"tauri-plugin-vpnservice-api": "link:../tauri-plugin-vpnservice",
|
"tauri-plugin-vpnservice-api": "link:../tauri-plugin-vpnservice",
|
||||||
"vue": "^3.4.38",
|
"vue": "^3.5.3",
|
||||||
"vue-i18n": "^9.13.1",
|
"vue-i18n": "^10.0.0",
|
||||||
"vue-router": "^4.4.3"
|
"vue-router": "^4.4.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@antfu/eslint-config": "^2.25.1",
|
"@antfu/eslint-config": "^3.5.0",
|
||||||
"@intlify/unplugin-vue-i18n": "^4.0.0",
|
"@intlify/unplugin-vue-i18n": "^5.0.0",
|
||||||
"@primevue/auto-import-resolver": "^4.0.4",
|
"@primevue/auto-import-resolver": "^4.0.5",
|
||||||
"@sveltejs/vite-plugin-svelte": "^3.1.1",
|
|
||||||
"@tauri-apps/api": "2.0.0-rc.0",
|
"@tauri-apps/api": "2.0.0-rc.0",
|
||||||
"@tauri-apps/cli": "2.0.0-rc.3",
|
"@tauri-apps/cli": "2.0.0-rc.3",
|
||||||
"@types/node": "^20.14.15",
|
"@types/node": "^22.5.4",
|
||||||
"@types/uuid": "^9.0.8",
|
"@types/uuid": "^10.0.0",
|
||||||
"@vitejs/plugin-vue": "^5.1.2",
|
"@vitejs/plugin-vue": "^5.1.3",
|
||||||
"@vue-macros/volar": "^0.19.1",
|
"@vue-macros/volar": "^0.29.1",
|
||||||
"autoprefixer": "^10.4.20",
|
"autoprefixer": "^10.4.20",
|
||||||
"eslint": "^9.9.0",
|
"eslint": "^9.10.0",
|
||||||
"eslint-plugin-format": "^0.1.2",
|
"eslint-plugin-format": "^0.1.2",
|
||||||
"internal-ip": "^8.0.0",
|
"internal-ip": "^8.0.0",
|
||||||
"postcss": "^8.4.41",
|
"postcss": "^8.4.45",
|
||||||
"tailwindcss": "^3.4.10",
|
"tailwindcss": "^3.4.10",
|
||||||
"typescript": "^5.5.4",
|
"typescript": "^5.6.2",
|
||||||
"unplugin-auto-import": "^0.17.8",
|
"unplugin-auto-import": "^0.18.2",
|
||||||
"unplugin-vue-components": "^0.27.4",
|
"unplugin-vue-components": "^0.27.4",
|
||||||
"unplugin-vue-macros": "^2.11.5",
|
"unplugin-vue-macros": "^2.11.11",
|
||||||
"unplugin-vue-markdown": "^0.26.2",
|
"unplugin-vue-markdown": "^0.26.2",
|
||||||
"unplugin-vue-router": "^0.8.8",
|
"unplugin-vue-router": "^0.10.8",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^10.0.0",
|
||||||
"vite": "^5.4.1",
|
"vite": "^5.4.3",
|
||||||
"vite-plugin-vue-devtools": "^7.3.8",
|
"vite-plugin-vue-devtools": "^7.4.4",
|
||||||
"vite-plugin-vue-layouts": "^0.11.0",
|
"vite-plugin-vue-layouts": "^0.11.0",
|
||||||
"vue-i18n": "^9.13.1",
|
"vue-i18n": "^10.0.0",
|
||||||
"vue-tsc": "^2.0.29"
|
"vue-tsc": "^2.1.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -171,6 +171,11 @@ static INSTANCE_MAP: once_cell::sync::Lazy<DashMap<String, NetworkInstance>> =
|
||||||
static mut LOGGER_LEVEL_SENDER: once_cell::sync::Lazy<Option<NewFilterSender>> =
|
static mut LOGGER_LEVEL_SENDER: once_cell::sync::Lazy<Option<NewFilterSender>> =
|
||||||
once_cell::sync::Lazy::new(Default::default);
|
once_cell::sync::Lazy::new(Default::default);
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn easytier_version() -> Result<String, String> {
|
||||||
|
Ok(easytier::VERSION.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn is_autostart() -> Result<bool, String> {
|
fn is_autostart() -> Result<bool, String> {
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
@ -365,7 +370,8 @@ pub fn run() {
|
||||||
get_os_hostname,
|
get_os_hostname,
|
||||||
set_logging_level,
|
set_logging_level,
|
||||||
set_tun_fd,
|
set_tun_fd,
|
||||||
is_autostart
|
is_autostart,
|
||||||
|
easytier_version
|
||||||
])
|
])
|
||||||
.on_window_event(|_win, event| match event {
|
.on_window_event(|_win, event| match event {
|
||||||
#[cfg(not(target_os = "android"))]
|
#[cfg(not(target_os = "android"))]
|
||||||
|
|
19
easytier-gui/src/auto-imports.d.ts
vendored
19
easytier-gui/src/auto-imports.d.ts
vendored
|
@ -24,6 +24,7 @@ declare global {
|
||||||
const getActivePinia: typeof import('pinia')['getActivePinia']
|
const getActivePinia: typeof import('pinia')['getActivePinia']
|
||||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||||
|
const getEasytierVersion: typeof import('./composables/network')['getEasytierVersion']
|
||||||
const getOsHostname: typeof import('./composables/network')['getOsHostname']
|
const getOsHostname: typeof import('./composables/network')['getOsHostname']
|
||||||
const h: typeof import('vue')['h']
|
const h: typeof import('vue')['h']
|
||||||
const initMobileService: typeof import('./composables/mobile_vpn')['initMobileService']
|
const initMobileService: typeof import('./composables/mobile_vpn')['initMobileService']
|
||||||
|
@ -44,8 +45,8 @@ declare global {
|
||||||
const nextTick: typeof import('vue')['nextTick']
|
const nextTick: typeof import('vue')['nextTick']
|
||||||
const onActivated: typeof import('vue')['onActivated']
|
const onActivated: typeof import('vue')['onActivated']
|
||||||
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||||
const onBeforeRouteLeave: typeof import('vue-router/auto')['onBeforeRouteLeave']
|
const onBeforeRouteLeave: typeof import('vue-router')['onBeforeRouteLeave']
|
||||||
const onBeforeRouteUpdate: typeof import('vue-router/auto')['onBeforeRouteUpdate']
|
const onBeforeRouteUpdate: typeof import('vue-router')['onBeforeRouteUpdate']
|
||||||
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||||
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||||
const onDeactivated: typeof import('vue')['onDeactivated']
|
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||||
|
@ -90,8 +91,8 @@ declare global {
|
||||||
const useI18n: typeof import('vue-i18n')['useI18n']
|
const useI18n: typeof import('vue-i18n')['useI18n']
|
||||||
const useLink: typeof import('vue-router/auto')['useLink']
|
const useLink: typeof import('vue-router/auto')['useLink']
|
||||||
const useNetworkStore: typeof import('./stores/network')['useNetworkStore']
|
const useNetworkStore: typeof import('./stores/network')['useNetworkStore']
|
||||||
const useRoute: typeof import('vue-router/auto')['useRoute']
|
const useRoute: typeof import('vue-router')['useRoute']
|
||||||
const useRouter: typeof import('vue-router/auto')['useRouter']
|
const useRouter: typeof import('vue-router')['useRouter']
|
||||||
const useSlots: typeof import('vue')['useSlots']
|
const useSlots: typeof import('vue')['useSlots']
|
||||||
const useTray: typeof import('./composables/tray')['useTray']
|
const useTray: typeof import('./composables/tray')['useTray']
|
||||||
const watch: typeof import('vue')['watch']
|
const watch: typeof import('vue')['watch']
|
||||||
|
@ -121,13 +122,13 @@ declare module 'vue' {
|
||||||
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
|
||||||
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
||||||
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
||||||
readonly definePage: UnwrapRef<typeof import('unplugin-vue-router/runtime')['definePage']>
|
|
||||||
readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
|
readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
|
||||||
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||||
readonly generateMenuItem: UnwrapRef<typeof import('./composables/tray')['generateMenuItem']>
|
readonly generateMenuItem: UnwrapRef<typeof import('./composables/tray')['generateMenuItem']>
|
||||||
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
|
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
|
||||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||||
|
readonly getEasytierVersion: UnwrapRef<typeof import('./composables/network')['getEasytierVersion']>
|
||||||
readonly getOsHostname: UnwrapRef<typeof import('./composables/network')['getOsHostname']>
|
readonly getOsHostname: UnwrapRef<typeof import('./composables/network')['getOsHostname']>
|
||||||
readonly h: UnwrapRef<typeof import('vue')['h']>
|
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||||
readonly initMobileVpnService: UnwrapRef<typeof import('./composables/mobile_vpn')['initMobileVpnService']>
|
readonly initMobileVpnService: UnwrapRef<typeof import('./composables/mobile_vpn')['initMobileVpnService']>
|
||||||
|
@ -146,8 +147,8 @@ declare module 'vue' {
|
||||||
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
|
||||||
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
|
||||||
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
|
||||||
readonly onBeforeRouteLeave: UnwrapRef<typeof import('vue-router/auto')['onBeforeRouteLeave']>
|
readonly onBeforeRouteLeave: UnwrapRef<typeof import('vue-router')['onBeforeRouteLeave']>
|
||||||
readonly onBeforeRouteUpdate: UnwrapRef<typeof import('vue-router/auto')['onBeforeRouteUpdate']>
|
readonly onBeforeRouteUpdate: UnwrapRef<typeof import('vue-router')['onBeforeRouteUpdate']>
|
||||||
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
|
||||||
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
|
||||||
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
||||||
|
@ -191,8 +192,8 @@ declare module 'vue' {
|
||||||
readonly useI18n: UnwrapRef<typeof import('vue-i18n')['useI18n']>
|
readonly useI18n: UnwrapRef<typeof import('vue-i18n')['useI18n']>
|
||||||
readonly useLink: UnwrapRef<typeof import('vue-router/auto')['useLink']>
|
readonly useLink: UnwrapRef<typeof import('vue-router/auto')['useLink']>
|
||||||
readonly useNetworkStore: UnwrapRef<typeof import('./stores/network')['useNetworkStore']>
|
readonly useNetworkStore: UnwrapRef<typeof import('./stores/network')['useNetworkStore']>
|
||||||
readonly useRoute: UnwrapRef<typeof import('vue-router/auto')['useRoute']>
|
readonly useRoute: UnwrapRef<typeof import('vue-router')['useRoute']>
|
||||||
readonly useRouter: UnwrapRef<typeof import('vue-router/auto')['useRouter']>
|
readonly useRouter: UnwrapRef<typeof import('vue-router')['useRouter']>
|
||||||
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||||
readonly useTray: UnwrapRef<typeof import('./composables/tray')['useTray']>
|
readonly useTray: UnwrapRef<typeof import('./composables/tray')['useTray']>
|
||||||
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
||||||
|
|
27
easytier-gui/src/components/About.vue
Normal file
27
easytier-gui/src/components/About.vue
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getEasytierVersion } from '~/composables/network'
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
|
const etVersion = ref('')
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
etVersion.value = await getEasytierVersion()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<Card>
|
||||||
|
<template #title>
|
||||||
|
Easytier - {{ t('about.version') }}: {{ etVersion }}
|
||||||
|
</template>
|
||||||
|
<template #content>
|
||||||
|
<p class="mb-1">
|
||||||
|
{{ t('about.description') }}
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</Card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="postcss">
|
||||||
|
</style>
|
|
@ -1,11 +1,10 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import InputGroup from 'primevue/inputgroup'
|
import InputGroup from 'primevue/inputgroup'
|
||||||
import InputGroupAddon from 'primevue/inputgroupaddon'
|
import InputGroupAddon from 'primevue/inputgroupaddon'
|
||||||
import { getOsHostname } from '~/composables/network'
|
|
||||||
import { NetworkingMethod } from '~/types/network'
|
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
import { ping } from 'tauri-plugin-vpnservice-api'
|
import { ping } from 'tauri-plugin-vpnservice-api'
|
||||||
|
import { getOsHostname } from '~/composables/network'
|
||||||
|
|
||||||
|
import { NetworkingMethod } from '~/types/network'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
configInvalid?: boolean
|
configInvalid?: boolean
|
||||||
|
@ -14,6 +13,8 @@ const props = defineProps<{
|
||||||
|
|
||||||
defineEmits(['runNetwork'])
|
defineEmits(['runNetwork'])
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const networking_methods = ref([
|
const networking_methods = ref([
|
||||||
{ value: NetworkingMethod.PublicServer, label: () => t('public_server') },
|
{ value: NetworkingMethod.PublicServer, label: () => t('public_server') },
|
||||||
{ value: NetworkingMethod.Manual, label: () => t('manual') },
|
{ value: NetworkingMethod.Manual, label: () => t('manual') },
|
||||||
|
@ -32,24 +33,26 @@ const curNetwork = computed(() => {
|
||||||
return networkStore.curNetwork
|
return networkStore.curNetwork
|
||||||
})
|
})
|
||||||
|
|
||||||
const protos:{ [proto: string] : number; } = {'tcp': 11010, 'udp': 11010, 'wg':11011, 'ws': 11011, 'wss': 11012}
|
const protos: { [proto: string]: number } = { tcp: 11010, udp: 11010, wg: 11011, ws: 11011, wss: 11012 }
|
||||||
|
|
||||||
function searchUrlSuggestions(e: { query: string }): string[] {
|
function searchUrlSuggestions(e: { query: string }): string[] {
|
||||||
const query = e.query
|
const query = e.query
|
||||||
let ret = []
|
const ret = []
|
||||||
// if query match "^\w+:.*", then no proto prefix
|
// if query match "^\w+:.*", then no proto prefix
|
||||||
if (query.match(/^\w+:.*/)) {
|
if (query.match(/^\w+:.*/)) {
|
||||||
// if query is a valid url, then add to suggestions
|
// if query is a valid url, then add to suggestions
|
||||||
try {
|
try {
|
||||||
new URL(query)
|
new URL(query)
|
||||||
ret.push(query)
|
ret.push(query)
|
||||||
} catch (e) {}
|
}
|
||||||
} else {
|
catch (e) {}
|
||||||
for (let proto in protos) {
|
}
|
||||||
let item = proto + '://' + query
|
else {
|
||||||
|
for (const proto in protos) {
|
||||||
|
let item = `${proto}://${query}`
|
||||||
// if query match ":\d+$", then no port suffix
|
// if query match ":\d+$", then no port suffix
|
||||||
if (!query.match(/:\d+$/)) {
|
if (!query.match(/:\d+$/)) {
|
||||||
item += ':' + protos[proto]
|
item += `:${protos[proto]}`
|
||||||
}
|
}
|
||||||
ret.push(item)
|
ret.push(item)
|
||||||
}
|
}
|
||||||
|
@ -58,42 +61,42 @@ function searchUrlSuggestions(e: { query: string }): string[] {
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const publicServerSuggestions = ref([''])
|
const publicServerSuggestions = ref([''])
|
||||||
|
|
||||||
const searchPresetPublicServers = (e: { query: string }) => {
|
function searchPresetPublicServers(e: { query: string }) {
|
||||||
const presetPublicServers = [
|
const presetPublicServers = [
|
||||||
'tcp://easytier.public.kkrainbow.top:11010',
|
'tcp://easytier.public.kkrainbow.top:11010',
|
||||||
]
|
]
|
||||||
|
|
||||||
let query = e.query
|
const query = e.query
|
||||||
// if query is sub string of presetPublicServers, add to suggestions
|
// if query is sub string of presetPublicServers, add to suggestions
|
||||||
let ret = presetPublicServers.filter((item) => item.includes(query))
|
let ret = presetPublicServers.filter(item => item.includes(query))
|
||||||
// add additional suggestions
|
// add additional suggestions
|
||||||
if (query.length > 0) {
|
if (query.length > 0) {
|
||||||
ret = ret.concat(searchUrlSuggestions(e))
|
ret = ret.concat(searchUrlSuggestions(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
publicServerSuggestions.value = ret
|
publicServerSuggestions.value = ret
|
||||||
}
|
}
|
||||||
|
|
||||||
const peerSuggestions = ref([''])
|
const peerSuggestions = ref([''])
|
||||||
|
|
||||||
const searchPeerSuggestions = (e: { query: string }) => {
|
function searchPeerSuggestions(e: { query: string }) {
|
||||||
peerSuggestions.value = searchUrlSuggestions(e)
|
peerSuggestions.value = searchUrlSuggestions(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
const listenerSuggestions = ref([''])
|
const listenerSuggestions = ref([''])
|
||||||
|
|
||||||
const searchListenerSuggestiong = (e: { query: string }) => {
|
function searchListenerSuggestiong(e: { query: string }) {
|
||||||
let ret = []
|
const ret = []
|
||||||
|
|
||||||
for (let proto in protos) {
|
for (const proto in protos) {
|
||||||
let item = proto + '://0.0.0.0:';
|
let item = `${proto}://0.0.0.0:`
|
||||||
// if query is a number, use it as port
|
// if query is a number, use it as port
|
||||||
if (e.query.match(/^\d+$/)) {
|
if (e.query.match(/^\d+$/)) {
|
||||||
item += e.query
|
item += e.query
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
item += protos[proto]
|
item += protos[proto]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +115,7 @@ const searchListenerSuggestiong = (e: { query: string }) => {
|
||||||
function validateHostname() {
|
function validateHostname() {
|
||||||
if (curNetwork.value.hostname) {
|
if (curNetwork.value.hostname) {
|
||||||
// eslint no-useless-escape
|
// eslint no-useless-escape
|
||||||
let name = curNetwork.value.hostname!.replaceAll(/[^\u4E00-\u9FA5a-zA-Z0-9\-]*/g, '')
|
let name = curNetwork.value.hostname!.replaceAll(/[^\u4E00-\u9FA5a-z0-9\-]*/gi, '')
|
||||||
if (name.length > 32)
|
if (name.length > 32)
|
||||||
name = name.substring(0, 32)
|
name = name.substring(0, 32)
|
||||||
|
|
||||||
|
@ -132,7 +135,7 @@ onMounted(async () => {
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-column h-full">
|
<div class="flex flex-column h-full">
|
||||||
<div class="flex flex-column">
|
<div class="flex flex-column">
|
||||||
<div class="w-10/12 self-center ">
|
<div class="w-10/12 self-center mb-3">
|
||||||
<Message severity="warn">
|
<Message severity="warn">
|
||||||
{{ t('dhcp_experimental_warning') }}
|
{{ t('dhcp_experimental_warning') }}
|
||||||
</Message>
|
</Message>
|
||||||
|
@ -151,8 +154,10 @@ onMounted(async () => {
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<InputGroup>
|
<InputGroup>
|
||||||
<InputText id="virtual_ip" v-model="curNetwork.virtual_ipv4" :disabled="curNetwork.dhcp"
|
<InputText
|
||||||
aria-describedby="virtual_ipv4-help" />
|
id="virtual_ip" v-model="curNetwork.virtual_ipv4" :disabled="curNetwork.dhcp"
|
||||||
|
aria-describedby="virtual_ipv4-help"
|
||||||
|
/>
|
||||||
<InputGroupAddon>
|
<InputGroupAddon>
|
||||||
<span>/24</span>
|
<span>/24</span>
|
||||||
</InputGroupAddon>
|
</InputGroupAddon>
|
||||||
|
@ -167,23 +172,29 @@ onMounted(async () => {
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||||
<label for="network_secret">{{ t('network_secret') }}</label>
|
<label for="network_secret">{{ t('network_secret') }}</label>
|
||||||
<InputText id="network_secret" v-model="curNetwork.network_secret"
|
<InputText
|
||||||
aria-describedby=" network_secret-help" />
|
id="network_secret" v-model="curNetwork.network_secret"
|
||||||
|
aria-describedby=" network_secret-help"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||||
<label for="nm">{{ t('networking_method') }}</label>
|
<label for="nm">{{ t('networking_method') }}</label>
|
||||||
<SelectButton v-model="curNetwork.networking_method" :options="networking_methods" :option-label="(v) => v.label()" option-value="value"></SelectButton>
|
<SelectButton v-model="curNetwork.networking_method" :options="networking_methods" :option-label="(v) => v.label()" option-value="value" />
|
||||||
<div class="items-center flex flex-row p-fluid gap-x-1">
|
<div class="items-center flex flex-row p-fluid gap-x-1">
|
||||||
<AutoComplete v-if="curNetwork.networking_method === NetworkingMethod.Manual" id="chips"
|
<AutoComplete
|
||||||
|
v-if="curNetwork.networking_method === NetworkingMethod.Manual" id="chips"
|
||||||
v-model="curNetwork.peer_urls" :placeholder="t('chips_placeholder', ['tcp://8.8.8.8:11010'])"
|
v-model="curNetwork.peer_urls" :placeholder="t('chips_placeholder', ['tcp://8.8.8.8:11010'])"
|
||||||
class="grow" multiple fluid :suggestions="peerSuggestions" @complete="searchPeerSuggestions"/>
|
class="grow" multiple fluid :suggestions="peerSuggestions" @complete="searchPeerSuggestions"
|
||||||
|
/>
|
||||||
|
|
||||||
<AutoComplete v-if="curNetwork.networking_method === NetworkingMethod.PublicServer" :suggestions="publicServerSuggestions"
|
<AutoComplete
|
||||||
:virtualScrollerOptions="{ itemSize: 38 }" class="grow" dropdown @complete="searchPresetPublicServers" :completeOnFocus="true"
|
v-if="curNetwork.networking_method === NetworkingMethod.PublicServer" v-model="curNetwork.public_server_url"
|
||||||
v-model="curNetwork.public_server_url"/>
|
:suggestions="publicServerSuggestions" :virtual-scroller-options="{ itemSize: 38 }" class="grow" dropdown :complete-on-focus="true"
|
||||||
|
@complete="searchPresetPublicServers"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -197,64 +208,80 @@ onMounted(async () => {
|
||||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||||
<label for="hostname">{{ t('hostname') }}</label>
|
<label for="hostname">{{ t('hostname') }}</label>
|
||||||
<InputText id="hostname" v-model="curNetwork.hostname" aria-describedby="hostname-help" :format="true"
|
<InputText
|
||||||
:placeholder="t('hostname_placeholder', [osHostname])" @blur="validateHostname" />
|
id="hostname" v-model="curNetwork.hostname" aria-describedby="hostname-help" :format="true"
|
||||||
|
:placeholder="t('hostname_placeholder', [osHostname])" @blur="validateHostname"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
<div class="flex flex-row gap-x-9 flex-wrap w-full">
|
||||||
<div class="flex flex-column gap-2 grow p-fluid">
|
<div class="flex flex-column gap-2 grow p-fluid">
|
||||||
<label for="username">{{ t('proxy_cidrs') }}</label>
|
<label for="username">{{ t('proxy_cidrs') }}</label>
|
||||||
<Chips id="chips" v-model="curNetwork.proxy_cidrs"
|
<Chips
|
||||||
:placeholder="t('chips_placeholder', ['10.0.0.0/24'])" separator=" " class="w-full" />
|
id="chips" v-model="curNetwork.proxy_cidrs"
|
||||||
|
:placeholder="t('chips_placeholder', ['10.0.0.0/24'])" separator=" " class="w-full"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-row gap-x-9 flex-wrap ">
|
<div class="flex flex-row gap-x-9 flex-wrap ">
|
||||||
<div class="flex flex-column gap-2 grow">
|
<div class="flex flex-column gap-2 grow">
|
||||||
<label for="username">VPN Portal</label>
|
<label for="username">VPN Portal</label>
|
||||||
<ToggleButton v-model="curNetwork.enable_vpn_portal" on-icon="pi pi-check" off-icon="pi pi-times"
|
<ToggleButton
|
||||||
:on-label="t('off_text')" :off-label="t('on_text')" class="w-48"/>
|
v-model="curNetwork.enable_vpn_portal" on-icon="pi pi-check" off-icon="pi pi-times"
|
||||||
<div class="items-center flex flex-row gap-x-4" v-if="curNetwork.enable_vpn_portal">
|
:on-label="t('off_text')" :off-label="t('on_text')" class="w-48"
|
||||||
<div class="min-w-64">
|
/>
|
||||||
<InputGroup>
|
<div v-if="curNetwork.enable_vpn_portal" class="items-center flex flex-row gap-x-4">
|
||||||
<InputText v-model="curNetwork.vpn_portal_client_network_addr"
|
<div class="min-w-64">
|
||||||
:placeholder="t('vpn_portal_client_network')" />
|
<InputGroup>
|
||||||
<InputGroupAddon>
|
<InputText
|
||||||
<span>/{{ curNetwork.vpn_portal_client_network_len }}</span>
|
v-model="curNetwork.vpn_portal_client_network_addr"
|
||||||
</InputGroupAddon>
|
:placeholder="t('vpn_portal_client_network')"
|
||||||
</InputGroup>
|
/>
|
||||||
|
<InputGroupAddon>
|
||||||
|
<span>/{{ curNetwork.vpn_portal_client_network_len }}</span>
|
||||||
|
</InputGroupAddon>
|
||||||
|
</InputGroup>
|
||||||
|
|
||||||
<InputNumber v-model="curNetwork.vpn_portal_listen_port" :allow-empty="false"
|
<InputNumber
|
||||||
:format="false" :min="0" :max="65535" class="w-8" fluid/>
|
v-model="curNetwork.vpn_portal_listen_port" :allow-empty="false"
|
||||||
</div>
|
:format="false" :min="0" :max="65535" class="w-8" fluid
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||||
<div class="flex flex-column gap-2 grow p-fluid">
|
<div class="flex flex-column gap-2 grow p-fluid">
|
||||||
<label for="listener_urls">{{ t('listener_urls') }}</label>
|
<label for="listener_urls">{{ t('listener_urls') }}</label>
|
||||||
<AutoComplete id="listener_urls" :suggestions="listenerSuggestions"
|
<AutoComplete
|
||||||
class="w-full" dropdown @complete="searchListenerSuggestiong" :completeOnFocus="true"
|
id="listener_urls" v-model="curNetwork.listener_urls"
|
||||||
|
:suggestions="listenerSuggestions" class="w-full" dropdown :complete-on-focus="true"
|
||||||
:placeholder="t('chips_placeholder', ['tcp://1.1.1.1:11010'])"
|
:placeholder="t('chips_placeholder', ['tcp://1.1.1.1:11010'])"
|
||||||
v-model="curNetwork.listener_urls" multiple/>
|
multiple @complete="searchListenerSuggestiong"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-row gap-x-9 flex-wrap">
|
<div class="flex flex-row gap-x-9 flex-wrap">
|
||||||
<div class="flex flex-column gap-2 basis-5/12 grow">
|
<div class="flex flex-column gap-2 basis-5/12 grow">
|
||||||
<label for="rpc_port">{{ t('rpc_port') }}</label>
|
<label for="rpc_port">{{ t('rpc_port') }}</label>
|
||||||
<InputNumber id="rpc_port" v-model="curNetwork.rpc_port" aria-describedby="username-help"
|
<InputNumber
|
||||||
:format="false" :min="0" :max="65535" />
|
id="rpc_port" v-model="curNetwork.rpc_port" aria-describedby="username-help"
|
||||||
|
:format="false" :min="0" :max="65535"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
<div class="flex pt-4 justify-content-center">
|
<div class="flex pt-4 justify-content-center">
|
||||||
<Button :label="t('run_network')" icon="pi pi-arrow-right" icon-pos="right" :disabled="configInvalid"
|
<Button
|
||||||
@click="$emit('runNetwork', curNetwork)" />
|
:label="t('run_network')" icon="pi pi-arrow-right" icon-pos="right" :disabled="configInvalid"
|
||||||
|
@click="$emit('runNetwork', curNetwork)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { NodeInfo } from '~/types/network'
|
import type { NodeInfo } from '~/types/network'
|
||||||
const { t } = useI18n()
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
instanceId?: string
|
instanceId?: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const networkStore = useNetworkStore()
|
const networkStore = useNetworkStore()
|
||||||
|
|
||||||
const curNetwork = computed(() => {
|
const curNetwork = computed(() => {
|
||||||
|
|
|
@ -1,183 +1,184 @@
|
||||||
import { addPluginListener } from '@tauri-apps/api/core';
|
import { addPluginListener } from '@tauri-apps/api/core'
|
||||||
import { prepare_vpn, start_vpn, stop_vpn } from 'tauri-plugin-vpnservice-api';
|
import { prepare_vpn, start_vpn, stop_vpn } from 'tauri-plugin-vpnservice-api'
|
||||||
import { Route } from '~/types/network';
|
import type { Route } from '~/types/network'
|
||||||
|
|
||||||
const networkStore = useNetworkStore()
|
const networkStore = useNetworkStore()
|
||||||
|
|
||||||
interface vpnStatus {
|
interface vpnStatus {
|
||||||
running: boolean
|
running: boolean
|
||||||
ipv4Addr: string | null | undefined
|
ipv4Addr: string | null | undefined
|
||||||
ipv4Cidr: number | null | undefined
|
ipv4Cidr: number | null | undefined
|
||||||
routes: string[]
|
routes: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
var curVpnStatus: vpnStatus = {
|
const curVpnStatus: vpnStatus = {
|
||||||
running: false,
|
running: false,
|
||||||
ipv4Addr: undefined,
|
ipv4Addr: undefined,
|
||||||
ipv4Cidr: undefined,
|
ipv4Cidr: undefined,
|
||||||
routes: []
|
routes: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
async function waitVpnStatus(target_status: boolean, timeout_sec: number) {
|
async function waitVpnStatus(target_status: boolean, timeout_sec: number) {
|
||||||
let start_time = Date.now()
|
const start_time = Date.now()
|
||||||
while (curVpnStatus.running !== target_status) {
|
while (curVpnStatus.running !== target_status) {
|
||||||
if (Date.now() - start_time > timeout_sec * 1000) {
|
if (Date.now() - start_time > timeout_sec * 1000) {
|
||||||
throw new Error('wait vpn status timeout')
|
throw new Error('wait vpn status timeout')
|
||||||
}
|
|
||||||
await new Promise(r => setTimeout(r, 50))
|
|
||||||
}
|
}
|
||||||
|
await new Promise(r => setTimeout(r, 50))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doStopVpn() {
|
async function doStopVpn() {
|
||||||
if (!curVpnStatus.running) {
|
if (!curVpnStatus.running) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
console.log('stop vpn')
|
console.log('stop vpn')
|
||||||
let stop_ret = await stop_vpn()
|
const stop_ret = await stop_vpn()
|
||||||
console.log('stop vpn', JSON.stringify((stop_ret)))
|
console.log('stop vpn', JSON.stringify((stop_ret)))
|
||||||
await waitVpnStatus(false, 3)
|
await waitVpnStatus(false, 3)
|
||||||
|
|
||||||
curVpnStatus.ipv4Addr = undefined
|
curVpnStatus.ipv4Addr = undefined
|
||||||
curVpnStatus.routes = []
|
curVpnStatus.routes = []
|
||||||
}
|
}
|
||||||
|
|
||||||
async function doStartVpn(ipv4Addr: string, cidr: number, routes: string[]) {
|
async function doStartVpn(ipv4Addr: string, cidr: number, routes: string[]) {
|
||||||
if (curVpnStatus.running) {
|
if (curVpnStatus.running) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('start vpn')
|
console.log('start vpn')
|
||||||
let start_ret = await start_vpn({
|
const start_ret = await start_vpn({
|
||||||
"ipv4Addr": ipv4Addr + '/' + cidr,
|
ipv4Addr: `${ipv4Addr}/${cidr}`,
|
||||||
"routes": routes,
|
routes,
|
||||||
"disallowedApplications": ["com.kkrainbow.easytier"],
|
disallowedApplications: ['com.kkrainbow.easytier'],
|
||||||
"mtu": 1300,
|
mtu: 1300,
|
||||||
});
|
})
|
||||||
if (start_ret?.errorMsg?.length) {
|
if (start_ret?.errorMsg?.length) {
|
||||||
throw new Error(start_ret.errorMsg)
|
throw new Error(start_ret.errorMsg)
|
||||||
}
|
}
|
||||||
await waitVpnStatus(true, 3)
|
await waitVpnStatus(true, 3)
|
||||||
|
|
||||||
curVpnStatus.ipv4Addr = ipv4Addr
|
curVpnStatus.ipv4Addr = ipv4Addr
|
||||||
curVpnStatus.routes = routes
|
curVpnStatus.routes = routes
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onVpnServiceStart(payload: any) {
|
async function onVpnServiceStart(payload: any) {
|
||||||
console.log('vpn service start', JSON.stringify(payload))
|
console.log('vpn service start', JSON.stringify(payload))
|
||||||
curVpnStatus.running = true
|
curVpnStatus.running = true
|
||||||
if (payload.fd) {
|
if (payload.fd) {
|
||||||
setTunFd(networkStore.networkInstanceIds[0], payload.fd)
|
setTunFd(networkStore.networkInstanceIds[0], payload.fd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onVpnServiceStop(payload: any) {
|
async function onVpnServiceStop(payload: any) {
|
||||||
console.log('vpn service stop', JSON.stringify(payload))
|
console.log('vpn service stop', JSON.stringify(payload))
|
||||||
curVpnStatus.running = false
|
curVpnStatus.running = false
|
||||||
}
|
}
|
||||||
|
|
||||||
async function registerVpnServiceListener() {
|
async function registerVpnServiceListener() {
|
||||||
console.log('register vpn service listener')
|
console.log('register vpn service listener')
|
||||||
await addPluginListener(
|
await addPluginListener(
|
||||||
'vpnservice',
|
'vpnservice',
|
||||||
'vpn_service_start',
|
'vpn_service_start',
|
||||||
onVpnServiceStart
|
onVpnServiceStart,
|
||||||
)
|
)
|
||||||
|
|
||||||
await addPluginListener(
|
await addPluginListener(
|
||||||
'vpnservice',
|
'vpnservice',
|
||||||
'vpn_service_stop',
|
'vpn_service_stop',
|
||||||
onVpnServiceStop
|
onVpnServiceStop,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRoutesForVpn(routes: Route[]): string[] {
|
function getRoutesForVpn(routes: Route[]): string[] {
|
||||||
if (!routes) {
|
if (!routes) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret = []
|
const ret = []
|
||||||
for (let r of routes) {
|
for (const r of routes) {
|
||||||
for (let cidr of r.proxy_cidrs) {
|
for (let cidr of r.proxy_cidrs) {
|
||||||
if (cidr.indexOf('/') === -1) {
|
if (!cidr.includes('/')) {
|
||||||
cidr += '/32'
|
cidr += '/32'
|
||||||
}
|
}
|
||||||
ret.push(cidr)
|
ret.push(cidr)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// sort and dedup
|
// sort and dedup
|
||||||
return Array.from(new Set(ret)).sort()
|
return Array.from(new Set(ret)).sort()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onNetworkInstanceChange() {
|
async function onNetworkInstanceChange() {
|
||||||
let insts = networkStore.networkInstanceIds
|
const insts = networkStore.networkInstanceIds
|
||||||
if (!insts) {
|
if (!insts) {
|
||||||
await doStopVpn()
|
await doStopVpn()
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const curNetworkInfo = networkStore.networkInfos[insts[0]]
|
||||||
|
if (!curNetworkInfo || curNetworkInfo?.error_msg?.length) {
|
||||||
|
await doStopVpn()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const virtual_ip = curNetworkInfo?.node_info?.virtual_ipv4
|
||||||
|
if (!virtual_ip || !virtual_ip.length) {
|
||||||
|
await doStopVpn()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const routes = getRoutesForVpn(curNetworkInfo?.routes)
|
||||||
|
|
||||||
|
const ipChanged = virtual_ip !== curVpnStatus.ipv4Addr
|
||||||
|
const routesChanged = JSON.stringify(routes) !== JSON.stringify(curVpnStatus.routes)
|
||||||
|
|
||||||
|
if (ipChanged || routesChanged) {
|
||||||
|
console.log('virtual ip changed', JSON.stringify(curVpnStatus), virtual_ip)
|
||||||
|
try {
|
||||||
|
await doStopVpn()
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.error(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
const curNetworkInfo = networkStore.networkInfos[insts[0]]
|
try {
|
||||||
if (!curNetworkInfo || curNetworkInfo?.error_msg?.length) {
|
await doStartVpn(virtual_ip, 24, routes)
|
||||||
await doStopVpn()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
const virtual_ip = curNetworkInfo?.node_info?.virtual_ipv4
|
console.error('start vpn failed, clear all network insts.', e)
|
||||||
if (!virtual_ip || !virtual_ip.length) {
|
networkStore.clearNetworkInstances()
|
||||||
await doStopVpn()
|
await retainNetworkInstance(networkStore.networkInstanceIds)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const routes = getRoutesForVpn(curNetworkInfo?.routes)
|
|
||||||
|
|
||||||
var ipChanged = virtual_ip !== curVpnStatus.ipv4Addr
|
|
||||||
var routesChanged = JSON.stringify(routes) !== JSON.stringify(curVpnStatus.routes)
|
|
||||||
|
|
||||||
if (ipChanged || routesChanged) {
|
|
||||||
console.log('virtual ip changed', JSON.stringify(curVpnStatus), virtual_ip)
|
|
||||||
try {
|
|
||||||
await doStopVpn()
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await doStartVpn(virtual_ip, 24, routes)
|
|
||||||
} catch (e) {
|
|
||||||
console.error("start vpn failed, clear all network insts.", e)
|
|
||||||
networkStore.clearNetworkInstances()
|
|
||||||
await retainNetworkInstance(networkStore.networkInstanceIds)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function watchNetworkInstance() {
|
async function watchNetworkInstance() {
|
||||||
var subscribe_running = false
|
let subscribe_running = false
|
||||||
networkStore.$subscribe(async () => {
|
networkStore.$subscribe(async () => {
|
||||||
if (subscribe_running) {
|
if (subscribe_running) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
subscribe_running = true
|
subscribe_running = true
|
||||||
try {
|
try {
|
||||||
await onNetworkInstanceChange()
|
await onNetworkInstanceChange()
|
||||||
} catch (_) {
|
}
|
||||||
}
|
catch (_) {
|
||||||
subscribe_running = false
|
}
|
||||||
})
|
subscribe_running = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function initMobileVpnService() {
|
export async function initMobileVpnService() {
|
||||||
await registerVpnServiceListener()
|
await registerVpnServiceListener()
|
||||||
await watchNetworkInstance()
|
await watchNetworkInstance()
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function prepareVpnService() {
|
export async function prepareVpnService() {
|
||||||
console.log('prepare vpn')
|
console.log('prepare vpn')
|
||||||
let prepare_ret = await prepare_vpn()
|
const prepare_ret = await prepare_vpn()
|
||||||
console.log('prepare vpn', JSON.stringify((prepare_ret)))
|
console.log('prepare vpn', JSON.stringify((prepare_ret)))
|
||||||
if (prepare_ret?.errorMsg?.length) {
|
if (prepare_ret?.errorMsg?.length) {
|
||||||
throw new Error(prepare_ret.errorMsg)
|
throw new Error(prepare_ret.errorMsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { invoke } from "@tauri-apps/api/core"
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
import type { NetworkConfig, NetworkInstanceRunningInfo } from '~/types/network'
|
import type { NetworkConfig, NetworkInstanceRunningInfo } from '~/types/network'
|
||||||
|
|
||||||
|
@ -33,3 +33,7 @@ export async function setLoggingLevel(level: string) {
|
||||||
export async function setTunFd(instanceId: string, fd: number) {
|
export async function setTunFd(instanceId: string, fd: number) {
|
||||||
return await invoke('set_tun_fd', { instanceId, fd })
|
return await invoke('set_tun_fd', { instanceId, fd })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getEasytierVersion() {
|
||||||
|
return await invoke<string>('easytier_version')
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
|
||||||
import { Menu, MenuItem, PredefinedMenuItem } from '@tauri-apps/api/menu'
|
import { Menu, MenuItem, PredefinedMenuItem } from '@tauri-apps/api/menu'
|
||||||
import { TrayIcon } from '@tauri-apps/api/tray'
|
import { TrayIcon } from '@tauri-apps/api/tray'
|
||||||
|
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||||
import pkg from '~/../package.json'
|
import pkg from '~/../package.json'
|
||||||
|
|
||||||
const DEFAULT_TRAY_NAME = 'main'
|
const DEFAULT_TRAY_NAME = 'main'
|
||||||
|
@ -8,14 +8,15 @@ const DEFAULT_TRAY_NAME = 'main'
|
||||||
async function toggleVisibility() {
|
async function toggleVisibility() {
|
||||||
if (await getCurrentWindow().isVisible()) {
|
if (await getCurrentWindow().isVisible()) {
|
||||||
await getCurrentWindow().hide()
|
await getCurrentWindow().hide()
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
await getCurrentWindow().show()
|
await getCurrentWindow().show()
|
||||||
await getCurrentWindow().setFocus()
|
await getCurrentWindow().setFocus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function useTray(init: boolean = false) {
|
export async function useTray(init: boolean = false) {
|
||||||
let tray;
|
let tray
|
||||||
try {
|
try {
|
||||||
tray = await TrayIcon.getById(DEFAULT_TRAY_NAME)
|
tray = await TrayIcon.getById(DEFAULT_TRAY_NAME)
|
||||||
if (!tray) {
|
if (!tray) {
|
||||||
|
@ -29,17 +30,18 @@ export async function useTray(init: boolean = false) {
|
||||||
}),
|
}),
|
||||||
action: async () => {
|
action: async () => {
|
||||||
toggleVisibility()
|
toggleVisibility()
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}
|
||||||
|
catch (error) {
|
||||||
console.warn('Error while creating tray icon:', error)
|
console.warn('Error while creating tray icon:', error)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (init) {
|
if (init) {
|
||||||
tray.setTooltip(`EasyTier\n${pkg.version}`)
|
tray.setTooltip(`EasyTier\n${pkg.version}`)
|
||||||
tray.setMenuOnLeftClick(false);
|
tray.setMenuOnLeftClick(false)
|
||||||
tray.setMenu(await Menu.new({
|
tray.setMenu(await Menu.new({
|
||||||
id: 'main',
|
id: 'main',
|
||||||
items: await generateMenuItem(),
|
items: await generateMenuItem(),
|
||||||
|
@ -59,7 +61,7 @@ export async function generateMenuItem() {
|
||||||
|
|
||||||
export async function MenuItemExit(text: string) {
|
export async function MenuItemExit(text: string) {
|
||||||
return await PredefinedMenuItem.new({
|
return await PredefinedMenuItem.new({
|
||||||
text: text,
|
text,
|
||||||
item: 'Quit',
|
item: 'Quit',
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -69,14 +71,15 @@ export async function MenuItemShow(text: string) {
|
||||||
id: 'show',
|
id: 'show',
|
||||||
text,
|
text,
|
||||||
action: async () => {
|
action: async () => {
|
||||||
await toggleVisibility();
|
await toggleVisibility()
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setTrayMenu(items: (MenuItem | PredefinedMenuItem)[] | undefined = undefined) {
|
export async function setTrayMenu(items: (MenuItem | PredefinedMenuItem)[] | undefined = undefined) {
|
||||||
const tray = await useTray()
|
const tray = await useTray()
|
||||||
if (!tray) return
|
if (!tray)
|
||||||
|
return
|
||||||
const menu = await Menu.new({
|
const menu = await Menu.new({
|
||||||
id: 'main',
|
id: 'main',
|
||||||
items: items || await generateMenuItem(),
|
items: items || await generateMenuItem(),
|
||||||
|
@ -86,14 +89,16 @@ export async function setTrayMenu(items: (MenuItem | PredefinedMenuItem)[] | und
|
||||||
|
|
||||||
export async function setTrayRunState(isRunning: boolean = false) {
|
export async function setTrayRunState(isRunning: boolean = false) {
|
||||||
const tray = await useTray()
|
const tray = await useTray()
|
||||||
if (!tray) return
|
if (!tray)
|
||||||
|
return
|
||||||
tray.setIcon(isRunning ? 'icons/icon-inactive.ico' : 'icons/icon.ico')
|
tray.setIcon(isRunning ? 'icons/icon-inactive.ico' : 'icons/icon.ico')
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setTrayTooltip(tooltip: string) {
|
export async function setTrayTooltip(tooltip: string) {
|
||||||
if (tooltip) {
|
if (tooltip) {
|
||||||
const tray = await useTray()
|
const tray = await useTray()
|
||||||
if (!tray) return
|
if (!tray)
|
||||||
|
return
|
||||||
tray.setTooltip(`EasyTier\n${pkg.version}\n${tooltip}`)
|
tray.setTooltip(`EasyTier\n${pkg.version}\n${tooltip}`)
|
||||||
tray.setTitle(`EasyTier\n${pkg.version}\n${tooltip}`)
|
tray.setTitle(`EasyTier\n${pkg.version}\n${tooltip}`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
import { setupLayouts } from 'virtual:generated-layouts'
|
import Aura from '@primevue/themes/aura'
|
||||||
import { createRouter, createWebHistory } from 'vue-router/auto'
|
|
||||||
|
|
||||||
import PrimeVue from 'primevue/config'
|
import PrimeVue from 'primevue/config'
|
||||||
import ToastService from 'primevue/toastservice'
|
import ToastService from 'primevue/toastservice'
|
||||||
import App from '~/App.vue'
|
|
||||||
|
|
||||||
|
import { createRouter, createWebHistory } from 'vue-router/auto'
|
||||||
|
import { routes } from 'vue-router/auto-routes'
|
||||||
|
import App from '~/App.vue'
|
||||||
|
import { i18n, loadLanguageAsync } from '~/modules/i18n'
|
||||||
|
|
||||||
|
import { getAutoLaunchStatusAsync, loadAutoLaunchStatusAsync } from './modules/auto_launch'
|
||||||
import '~/styles.css'
|
import '~/styles.css'
|
||||||
import Aura from '@primevue/themes/aura'
|
|
||||||
import 'primeicons/primeicons.css'
|
import 'primeicons/primeicons.css'
|
||||||
import 'primeflex/primeflex.css'
|
import 'primeflex/primeflex.css'
|
||||||
import { i18n, loadLanguageAsync } from '~/modules/i18n'
|
|
||||||
import { loadAutoLaunchStatusAsync, getAutoLaunchStatusAsync } from './modules/auto_launch'
|
|
||||||
|
|
||||||
if (import.meta.env.PROD) {
|
if (import.meta.env.PROD) {
|
||||||
document.addEventListener('keydown', (event) => {
|
document.addEventListener('keydown', (event) => {
|
||||||
|
@ -18,8 +18,9 @@ if (import.meta.env.PROD) {
|
||||||
event.key === 'F5'
|
event.key === 'F5'
|
||||||
|| (event.ctrlKey && event.key === 'r')
|
|| (event.ctrlKey && event.key === 'r')
|
||||||
|| (event.metaKey && event.key === 'r')
|
|| (event.metaKey && event.key === 'r')
|
||||||
)
|
) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
document.addEventListener('contextmenu', (event) => {
|
document.addEventListener('contextmenu', (event) => {
|
||||||
|
@ -35,7 +36,7 @@ async function main() {
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(),
|
history: createWebHistory(),
|
||||||
extendRoutes: routes => setupLayouts(routes),
|
routes,
|
||||||
})
|
})
|
||||||
|
|
||||||
app.use(router)
|
app.use(router)
|
||||||
|
@ -45,11 +46,12 @@ async function main() {
|
||||||
theme: {
|
theme: {
|
||||||
preset: Aura,
|
preset: Aura,
|
||||||
options: {
|
options: {
|
||||||
prefix: 'p',
|
prefix: 'p',
|
||||||
darkModeSelector: 'system',
|
darkModeSelector: 'system',
|
||||||
cssLayer: false
|
cssLayer: false,
|
||||||
}
|
},
|
||||||
}})
|
},
|
||||||
|
})
|
||||||
app.use(ToastService)
|
app.use(ToastService)
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
import { disable, enable, isEnabled } from '@tauri-apps/plugin-autostart'
|
import { disable, enable, isEnabled } from '@tauri-apps/plugin-autostart'
|
||||||
|
|
||||||
export async function loadAutoLaunchStatusAsync(target_enable: boolean): Promise<boolean> {
|
export async function loadAutoLaunchStatusAsync(target_enable: boolean): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
target_enable ? await enable() : await disable()
|
target_enable ? await enable() : await disable()
|
||||||
localStorage.setItem('auto_launch', JSON.stringify(await isEnabled()))
|
localStorage.setItem('auto_launch', JSON.stringify(await isEnabled()))
|
||||||
return isEnabled()
|
return isEnabled()
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAutoLaunchStatusAsync(): boolean {
|
export function getAutoLaunchStatusAsync(): boolean {
|
||||||
return localStorage.getItem('auto_launch') === 'true'
|
return localStorage.getItem('auto_launch') === 'true'
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { Locale } from 'vue-i18n'
|
|
||||||
import { createI18n } from 'vue-i18n'
|
import { createI18n } from 'vue-i18n'
|
||||||
|
import type { Locale } from 'vue-i18n'
|
||||||
|
|
||||||
// Import i18n resources
|
// Import i18n resources
|
||||||
// https://vitejs.dev/guide/features.html#glob-import
|
// https://vitejs.dev/guide/features.html#glob-import
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useToast } from 'primevue/usetoast'
|
|
||||||
|
|
||||||
import { exit } from '@tauri-apps/plugin-process'
|
|
||||||
import TieredMenu from 'primevue/tieredmenu'
|
|
||||||
import { open } from '@tauri-apps/plugin-shell'
|
|
||||||
import { appLogDir } from '@tauri-apps/api/path'
|
import { appLogDir } from '@tauri-apps/api/path'
|
||||||
|
|
||||||
|
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||||
import { writeText } from '@tauri-apps/plugin-clipboard-manager'
|
import { writeText } from '@tauri-apps/plugin-clipboard-manager'
|
||||||
import { type } from '@tauri-apps/plugin-os'
|
import { type } from '@tauri-apps/plugin-os'
|
||||||
|
import { exit } from '@tauri-apps/plugin-process'
|
||||||
|
import { open } from '@tauri-apps/plugin-shell'
|
||||||
|
import TieredMenu from 'primevue/tieredmenu'
|
||||||
|
import { useToast } from 'primevue/usetoast'
|
||||||
import Config from '~/components/Config.vue'
|
import Config from '~/components/Config.vue'
|
||||||
import Status from '~/components/Status.vue'
|
|
||||||
|
|
||||||
import { type NetworkConfig, NetworkingMethod } from '~/types/network'
|
import Status from '~/components/Status.vue'
|
||||||
import { loadLanguageAsync } from '~/modules/i18n'
|
|
||||||
import { getAutoLaunchStatusAsync as getAutoLaunchStatus, loadAutoLaunchStatusAsync } from '~/modules/auto_launch'
|
|
||||||
import { isAutostart, setLoggingLevel } from '~/composables/network'
|
import { isAutostart, setLoggingLevel } from '~/composables/network'
|
||||||
import { useTray } from '~/composables/tray'
|
import { useTray } from '~/composables/tray'
|
||||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
import { getAutoLaunchStatusAsync as getAutoLaunchStatus, loadAutoLaunchStatusAsync } from '~/modules/auto_launch'
|
||||||
|
import { loadLanguageAsync } from '~/modules/i18n'
|
||||||
|
import { type NetworkConfig, NetworkingMethod } from '~/types/network'
|
||||||
|
|
||||||
const { t, locale } = useI18n()
|
const { t, locale } = useI18n()
|
||||||
const visible = ref(false)
|
const visible = ref(false)
|
||||||
|
const aboutVisible = ref(false)
|
||||||
const tomlConfig = ref('')
|
const tomlConfig = ref('')
|
||||||
|
|
||||||
useTray(true)
|
useTray(true)
|
||||||
|
@ -85,7 +86,8 @@ async function runNetworkCb(cfg: NetworkConfig, cb: () => void) {
|
||||||
if (type() === 'android') {
|
if (type() === 'android') {
|
||||||
await prepareVpnService()
|
await prepareVpnService()
|
||||||
networkStore.clearNetworkInstances()
|
networkStore.clearNetworkInstances()
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
networkStore.removeNetworkInstance(cfg.instance_id)
|
networkStore.removeNetworkInstance(cfg.instance_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +148,7 @@ const setting_menu_items = ref([
|
||||||
await loadLanguageAsync((locale.value === 'en' ? 'cn' : 'en'))
|
await loadLanguageAsync((locale.value === 'en' ? 'cn' : 'en'))
|
||||||
await setTrayMenu([
|
await setTrayMenu([
|
||||||
await MenuItemExit(t('tray.exit')),
|
await MenuItemExit(t('tray.exit')),
|
||||||
await MenuItemShow(t('tray.show'))
|
await MenuItemShow(t('tray.show')),
|
||||||
])
|
])
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -193,6 +195,13 @@ const setting_menu_items = ref([
|
||||||
return items
|
return items
|
||||||
})(),
|
})(),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: () => t('about.title'),
|
||||||
|
icon: 'pi pi-at',
|
||||||
|
command: async () => {
|
||||||
|
aboutVisible.value = true
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: () => t('exit'),
|
label: () => t('exit'),
|
||||||
icon: 'pi pi-power-off',
|
icon: 'pi pi-power-off',
|
||||||
|
@ -249,6 +258,10 @@ function isRunning(id: string) {
|
||||||
</div>
|
</div>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
|
<Dialog v-model:visible="aboutVisible" modal :header="t('about.title')" :style="{ width: '70%' }">
|
||||||
|
<About />
|
||||||
|
</Dialog>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<template #start>
|
<template #start>
|
||||||
|
@ -259,15 +272,19 @@ function isRunning(id: string) {
|
||||||
|
|
||||||
<template #center>
|
<template #center>
|
||||||
<div class="min-w-40">
|
<div class="min-w-40">
|
||||||
<Dropdown v-model="networkStore.curNetwork" :options="networkStore.networkList" :highlight-on-select="false"
|
<Dropdown
|
||||||
:placeholder="t('select_network')" class="w-full">
|
v-model="networkStore.curNetwork" :options="networkStore.networkList" :highlight-on-select="false"
|
||||||
|
:placeholder="t('select_network')" class="w-full"
|
||||||
|
>
|
||||||
<template #value="slotProps">
|
<template #value="slotProps">
|
||||||
<div class="flex items-start content-center">
|
<div class="flex items-start content-center">
|
||||||
<div class="mr-3 flex-column">
|
<div class="mr-3 flex-column">
|
||||||
<span>{{ slotProps.value.network_name }}</span>
|
<span>{{ slotProps.value.network_name }}</span>
|
||||||
</div>
|
</div>
|
||||||
<Tag class="my-auto" :severity="isRunning(slotProps.value.instance_id) ? 'success' : 'info'"
|
<Tag
|
||||||
:value="t(isRunning(slotProps.value.instance_id) ? 'network_running' : 'network_stopped')" />
|
class="my-auto leading-3" :severity="isRunning(slotProps.value.instance_id) ? 'success' : 'info'"
|
||||||
|
:value="t(isRunning(slotProps.value.instance_id) ? 'network_running' : 'network_stopped')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #option="slotProps">
|
<template #option="slotProps">
|
||||||
|
@ -276,17 +293,23 @@ function isRunning(id: string) {
|
||||||
<div class="mr-3">
|
<div class="mr-3">
|
||||||
{{ t('network_name') }}: {{ slotProps.option.network_name }}
|
{{ t('network_name') }}: {{ slotProps.option.network_name }}
|
||||||
</div>
|
</div>
|
||||||
<Tag class="my-auto" :severity="isRunning(slotProps.option.instance_id) ? 'success' : 'info'"
|
<Tag
|
||||||
:value="t(isRunning(slotProps.option.instance_id) ? 'network_running' : 'network_stopped')" />
|
class="my-auto leading-3"
|
||||||
|
:severity="isRunning(slotProps.option.instance_id) ? 'success' : 'info'"
|
||||||
|
:value="t(isRunning(slotProps.option.instance_id) ? 'network_running' : 'network_stopped')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="slotProps.option.networking_method !== NetworkingMethod.Standalone"
|
<div
|
||||||
class="max-w-full overflow-hidden text-ellipsis">
|
v-if="slotProps.option.networking_method !== NetworkingMethod.Standalone"
|
||||||
|
class="max-w-full overflow-hidden text-ellipsis"
|
||||||
|
>
|
||||||
{{ slotProps.option.networking_method === NetworkingMethod.Manual
|
{{ slotProps.option.networking_method === NetworkingMethod.Manual
|
||||||
? slotProps.option.peer_urls.join(', ')
|
? slotProps.option.peer_urls.join(', ')
|
||||||
: slotProps.option.public_server_url }}
|
: slotProps.option.public_server_url }}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
v-if="isRunning(slotProps.option.instance_id) && networkStore.instances[slotProps.option.instance_id].detail && (networkStore.instances[slotProps.option.instance_id].detail?.my_node_info.virtual_ipv4 !== '')">
|
v-if="isRunning(slotProps.option.instance_id) && networkStore.instances[slotProps.option.instance_id].detail && (networkStore.instances[slotProps.option.instance_id].detail?.my_node_info.virtual_ipv4 !== '')"
|
||||||
|
>
|
||||||
{{ networkStore.instances[slotProps.option.instance_id].detail
|
{{ networkStore.instances[slotProps.option.instance_id].detail
|
||||||
? networkStore.instances[slotProps.option.instance_id].detail?.my_node_info.virtual_ipv4 : '' }}
|
? networkStore.instances[slotProps.option.instance_id].detail?.my_node_info.virtual_ipv4 : '' }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -297,8 +320,10 @@ function isRunning(id: string) {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #end>
|
<template #end>
|
||||||
<Button icon="pi pi-cog" severity="secondary" aria-haspopup="true" :label="t('settings')"
|
<Button
|
||||||
aria-controls="overlay_setting_menu" @click="toggle_setting_menu" />
|
icon="pi pi-cog" severity="secondary" aria-haspopup="true" :label="t('settings')"
|
||||||
|
aria-controls="overlay_setting_menu" @click="toggle_setting_menu"
|
||||||
|
/>
|
||||||
<TieredMenu id="overlay_setting_menu" ref="setting_menu" :model="setting_menu_items" :popup="true" />
|
<TieredMenu id="overlay_setting_menu" ref="setting_menu" :model="setting_menu_items" :popup="true" />
|
||||||
</template>
|
</template>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
@ -316,16 +341,20 @@ function isRunning(id: string) {
|
||||||
</StepList>
|
</StepList>
|
||||||
<StepPanels value="1">
|
<StepPanels value="1">
|
||||||
<StepPanel v-slot="{ activateCallback = (s: string) => { } } = {}" value="1">
|
<StepPanel v-slot="{ activateCallback = (s: string) => { } } = {}" value="1">
|
||||||
<Config :instance-id="networkStore.curNetworkId" :config-invalid="messageBarSeverity !== Severity.None"
|
<Config
|
||||||
@run-network="runNetworkCb($event, () => activateCallback('2'))" />
|
:instance-id="networkStore.curNetworkId" :config-invalid="messageBarSeverity !== Severity.None"
|
||||||
|
@run-network="runNetworkCb($event, () => activateCallback('2'))"
|
||||||
|
/>
|
||||||
</StepPanel>
|
</StepPanel>
|
||||||
<StepPanel v-slot="{ activateCallback = (s: string) => { } } = {}" value="2">
|
<StepPanel v-slot="{ activateCallback = (s: string) => { } } = {}" value="2">
|
||||||
<div class="flex flex-column">
|
<div class="flex flex-column">
|
||||||
<Status :instance-id="networkStore.curNetworkId" />
|
<Status :instance-id="networkStore.curNetworkId" />
|
||||||
</div>
|
</div>
|
||||||
<div class="flex pt-4 justify-content-center">
|
<div class="flex pt-4 justify-content-center">
|
||||||
<Button :label="t('stop_network')" severity="danger" icon="pi pi-arrow-left"
|
<Button
|
||||||
@click="stopNetworkCb(networkStore.curNetwork, () => activateCallback('1'))" />
|
:label="t('stop_network')" severity="danger" icon="pi pi-arrow-left"
|
||||||
|
@click="stopNetworkCb(networkStore.curNetwork, () => activateCallback('1'))"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</StepPanel>
|
</StepPanel>
|
||||||
</StepPanels>
|
</StepPanels>
|
||||||
|
|
|
@ -108,7 +108,8 @@ export const useNetworkStore = defineStore('networkStore', {
|
||||||
loadAutoStartInstIdsFromLocalStorage() {
|
loadAutoStartInstIdsFromLocalStorage() {
|
||||||
try {
|
try {
|
||||||
this.autoStartInstIds = JSON.parse(localStorage.getItem('autoStartInstIds') || '[]')
|
this.autoStartInstIds = JSON.parse(localStorage.getItem('autoStartInstIds') || '[]')
|
||||||
} catch (e) {
|
}
|
||||||
|
catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
this.autoStartInstIds = []
|
this.autoStartInstIds = []
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
|
||||||
color: #0f0f0f;
|
color: #0f0f0f;
|
||||||
background-color: white;
|
|
||||||
|
|
||||||
font-synthesis: none;
|
font-synthesis: none;
|
||||||
text-rendering: optimizeLegibility;
|
text-rendering: optimizeLegibility;
|
||||||
|
|
2
easytier-gui/src/typed-router.d.ts
vendored
2
easytier-gui/src/typed-router.d.ts
vendored
|
@ -12,7 +12,7 @@ declare module 'vue-router/auto-routes' {
|
||||||
ParamValueOneOrMore,
|
ParamValueOneOrMore,
|
||||||
ParamValueZeroOrMore,
|
ParamValueZeroOrMore,
|
||||||
ParamValueZeroOrOne,
|
ParamValueZeroOrOne,
|
||||||
} from 'unplugin-vue-router/types'
|
} from 'vue-router'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Route name map generated by unplugin-vue-router
|
* Route name map generated by unplugin-vue-router
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import { defineConfig } from 'vite'
|
import process from 'node:process'
|
||||||
import Vue from '@vitejs/plugin-vue'
|
|
||||||
import Layouts from 'vite-plugin-vue-layouts'
|
|
||||||
import Components from 'unplugin-vue-components/vite'
|
|
||||||
import AutoImport from 'unplugin-auto-import/vite'
|
|
||||||
import VueMacros from 'unplugin-vue-macros/vite'
|
|
||||||
import VueI18n from '@intlify/unplugin-vue-i18n/vite'
|
import VueI18n from '@intlify/unplugin-vue-i18n/vite'
|
||||||
import VueDevTools from 'vite-plugin-vue-devtools'
|
import { PrimeVueResolver } from '@primevue/auto-import-resolver'
|
||||||
import VueRouter from 'unplugin-vue-router/vite'
|
import Vue from '@vitejs/plugin-vue'
|
||||||
|
import { internalIpV4Sync } from 'internal-ip'
|
||||||
|
import AutoImport from 'unplugin-auto-import/vite'
|
||||||
|
import Components from 'unplugin-vue-components/vite'
|
||||||
|
import VueMacros from 'unplugin-vue-macros/vite'
|
||||||
import { VueRouterAutoImports } from 'unplugin-vue-router'
|
import { VueRouterAutoImports } from 'unplugin-vue-router'
|
||||||
import { PrimeVueResolver } from '@primevue/auto-import-resolver';
|
import VueRouter from 'unplugin-vue-router/vite'
|
||||||
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
import { defineConfig } from 'vite'
|
||||||
import { internalIpV4Sync } from 'internal-ip';
|
import VueDevTools from 'vite-plugin-vue-devtools'
|
||||||
|
import Layouts from 'vite-plugin-vue-layouts'
|
||||||
|
|
||||||
const host = process.env.TAURI_DEV_HOST;
|
const host = process.env.TAURI_DEV_HOST
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig(async () => ({
|
export default defineConfig(async () => ({
|
||||||
|
@ -23,7 +23,6 @@ export default defineConfig(async () => ({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
svelte(),
|
|
||||||
VueMacros({
|
VueMacros({
|
||||||
plugins: {
|
plugins: {
|
||||||
vue: Vue({
|
vue: Vue({
|
||||||
|
@ -100,10 +99,10 @@ export default defineConfig(async () => ({
|
||||||
},
|
},
|
||||||
hmr: host
|
hmr: host
|
||||||
? {
|
? {
|
||||||
protocol: 'ws',
|
protocol: 'ws',
|
||||||
host: internalIpV4Sync(),
|
host: internalIpV4Sync(),
|
||||||
port: 1430,
|
port: 1430,
|
||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
},
|
},
|
||||||
}))
|
}))
|
||||||
|
|
|
@ -13,3 +13,5 @@ pub mod launcher;
|
||||||
pub mod rpc;
|
pub mod rpc;
|
||||||
pub mod tunnel;
|
pub mod tunnel;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
|
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||||
|
|
Loading…
Reference in New Issue
Block a user