refactor(gui): refactor gui to use RemoteClient trait and RemoteManagement component (#1489)
* refactor(gui): refactor gui to use RemoteClient trait and RemoteManagement component * feat(gui): Add network config saving and refactor RemoteManagement
This commit is contained in:
Vendored
+32
-22
@@ -10,7 +10,7 @@ declare global {
|
||||
const MenuItemExit: typeof import('./composables/tray')['MenuItemExit']
|
||||
const MenuItemShow: typeof import('./composables/tray')['MenuItemShow']
|
||||
const acceptHMRUpdate: typeof import('pinia')['acceptHMRUpdate']
|
||||
const collectNetworkInfos: typeof import('./composables/network')['collectNetworkInfos']
|
||||
const collectNetworkInfo: typeof import('./composables/backend')['collectNetworkInfo']
|
||||
const computed: typeof import('vue')['computed']
|
||||
const createApp: typeof import('vue')['createApp']
|
||||
const createPinia: typeof import('pinia')['createPinia']
|
||||
@@ -18,22 +18,24 @@ declare global {
|
||||
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||
const defineComponent: typeof import('vue')['defineComponent']
|
||||
const defineStore: typeof import('pinia')['defineStore']
|
||||
const deleteNetworkInstance: typeof import('./composables/backend')['deleteNetworkInstance']
|
||||
const effectScope: typeof import('vue')['effectScope']
|
||||
const generateMenuItem: typeof import('./composables/tray')['generateMenuItem']
|
||||
const generateNetworkConfig: typeof import('./composables/network')['generateNetworkConfig']
|
||||
const generateNetworkConfig: typeof import('./composables/backend')['generateNetworkConfig']
|
||||
const getActivePinia: typeof import('pinia')['getActivePinia']
|
||||
const getConfig: typeof import('./composables/backend')['getConfig']
|
||||
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||
const getCurrentScope: typeof import('vue')['getCurrentScope']
|
||||
const getEasytierVersion: typeof import('./composables/network')['getEasytierVersion']
|
||||
const getOsHostname: typeof import('./composables/network')['getOsHostname']
|
||||
const getEasytierVersion: typeof import('./composables/backend')['getEasytierVersion']
|
||||
const h: typeof import('vue')['h']
|
||||
const initMobileVpnService: typeof import('./composables/mobile_vpn')['initMobileVpnService']
|
||||
const inject: typeof import('vue')['inject']
|
||||
const isAutostart: typeof import('./composables/network')['isAutostart']
|
||||
const isProxy: typeof import('vue')['isProxy']
|
||||
const isReactive: typeof import('vue')['isReactive']
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const listNetworkInstanceIds: typeof import('./composables/backend')['listNetworkInstanceIds']
|
||||
const listenGlobalEvents: typeof import('./composables/event')['listenGlobalEvents']
|
||||
const mapActions: typeof import('pinia')['mapActions']
|
||||
const mapGetters: typeof import('pinia')['mapGetters']
|
||||
const mapState: typeof import('pinia')['mapState']
|
||||
@@ -50,6 +52,7 @@ declare global {
|
||||
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||
const onMounted: typeof import('vue')['onMounted']
|
||||
const onNetworkInstanceChange: typeof import('./composables/mobile_vpn')['onNetworkInstanceChange']
|
||||
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||
const onScopeDispose: typeof import('vue')['onScopeDispose']
|
||||
@@ -57,22 +60,23 @@ declare global {
|
||||
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||
const onUpdated: typeof import('vue')['onUpdated']
|
||||
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
|
||||
const parseNetworkConfig: typeof import('./composables/network')['parseNetworkConfig']
|
||||
const parseNetworkConfig: typeof import('./composables/backend')['parseNetworkConfig']
|
||||
const prepareVpnService: typeof import('./composables/mobile_vpn')['prepareVpnService']
|
||||
const provide: typeof import('vue')['provide']
|
||||
const reactive: typeof import('vue')['reactive']
|
||||
const readonly: typeof import('vue')['readonly']
|
||||
const ref: typeof import('vue')['ref']
|
||||
const resolveComponent: typeof import('vue')['resolveComponent']
|
||||
const retainNetworkInstance: typeof import('./composables/network')['retainNetworkInstance']
|
||||
const runNetworkInstance: typeof import('./composables/network')['runNetworkInstance']
|
||||
const runNetworkInstance: typeof import('./composables/backend')['runNetworkInstance']
|
||||
const saveNetworkConfig: typeof import('./composables/backend')['saveNetworkConfig']
|
||||
const sendConfigs: typeof import('./composables/backend')['sendConfigs']
|
||||
const setActivePinia: typeof import('pinia')['setActivePinia']
|
||||
const setLoggingLevel: typeof import('./composables/network')['setLoggingLevel']
|
||||
const setLoggingLevel: typeof import('./composables/backend')['setLoggingLevel']
|
||||
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
|
||||
const setTrayMenu: typeof import('./composables/tray')['setTrayMenu']
|
||||
const setTrayRunState: typeof import('./composables/tray')['setTrayRunState']
|
||||
const setTrayTooltip: typeof import('./composables/tray')['setTrayTooltip']
|
||||
const setTunFd: typeof import('./composables/network')['setTunFd']
|
||||
const setTunFd: typeof import('./composables/backend')['setTunFd']
|
||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||
const shallowRef: typeof import('vue')['shallowRef']
|
||||
@@ -83,6 +87,7 @@ declare global {
|
||||
const toValue: typeof import('vue')['toValue']
|
||||
const triggerRef: typeof import('vue')['triggerRef']
|
||||
const unref: typeof import('vue')['unref']
|
||||
const updateNetworkConfigState: typeof import('./composables/backend')['updateNetworkConfigState']
|
||||
const useAttrs: typeof import('vue')['useAttrs']
|
||||
const useCssModule: typeof import('vue')['useCssModule']
|
||||
const useCssVars: typeof import('vue')['useCssVars']
|
||||
@@ -90,12 +95,12 @@ declare global {
|
||||
const useId: typeof import('vue')['useId']
|
||||
const useLink: typeof import('vue-router/auto')['useLink']
|
||||
const useModel: typeof import('vue')['useModel']
|
||||
const useNetworkStore: typeof import('./stores/network')['useNetworkStore']
|
||||
const useRoute: typeof import('vue-router')['useRoute']
|
||||
const useRouter: typeof import('vue-router')['useRouter']
|
||||
const useSlots: typeof import('vue')['useSlots']
|
||||
const useTemplateRef: typeof import('vue')['useTemplateRef']
|
||||
const useTray: typeof import('./composables/tray')['useTray']
|
||||
const validateConfig: typeof import('./composables/backend')['validateConfig']
|
||||
const watch: typeof import('vue')['watch']
|
||||
const watchEffect: typeof import('vue')['watchEffect']
|
||||
const watchPostEffect: typeof import('vue')['watchPostEffect']
|
||||
@@ -117,7 +122,7 @@ declare module 'vue' {
|
||||
readonly MenuItemExit: UnwrapRef<typeof import('./composables/tray')['MenuItemExit']>
|
||||
readonly MenuItemShow: UnwrapRef<typeof import('./composables/tray')['MenuItemShow']>
|
||||
readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
|
||||
readonly collectNetworkInfos: UnwrapRef<typeof import('./composables/network')['collectNetworkInfos']>
|
||||
readonly collectNetworkInfo: UnwrapRef<typeof import('./composables/backend')['collectNetworkInfo']>
|
||||
readonly computed: UnwrapRef<typeof import('vue')['computed']>
|
||||
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
|
||||
readonly createPinia: UnwrapRef<typeof import('pinia')['createPinia']>
|
||||
@@ -125,22 +130,24 @@ declare module 'vue' {
|
||||
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
|
||||
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
|
||||
readonly defineStore: UnwrapRef<typeof import('pinia')['defineStore']>
|
||||
readonly deleteNetworkInstance: UnwrapRef<typeof import('./composables/backend')['deleteNetworkInstance']>
|
||||
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||
readonly generateMenuItem: UnwrapRef<typeof import('./composables/tray')['generateMenuItem']>
|
||||
readonly generateNetworkConfig: UnwrapRef<typeof import('./composables/network')['generateNetworkConfig']>
|
||||
readonly generateNetworkConfig: UnwrapRef<typeof import('./composables/backend')['generateNetworkConfig']>
|
||||
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
|
||||
readonly getConfig: UnwrapRef<typeof import('./composables/backend')['getConfig']>
|
||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||
readonly getEasytierVersion: UnwrapRef<typeof import('./composables/network')['getEasytierVersion']>
|
||||
readonly getOsHostname: UnwrapRef<typeof import('./composables/network')['getOsHostname']>
|
||||
readonly getEasytierVersion: UnwrapRef<typeof import('./composables/backend')['getEasytierVersion']>
|
||||
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||
readonly initMobileVpnService: UnwrapRef<typeof import('./composables/mobile_vpn')['initMobileVpnService']>
|
||||
readonly inject: UnwrapRef<typeof import('vue')['inject']>
|
||||
readonly isAutostart: UnwrapRef<typeof import('./composables/network')['isAutostart']>
|
||||
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||
readonly listNetworkInstanceIds: UnwrapRef<typeof import('./composables/backend')['listNetworkInstanceIds']>
|
||||
readonly listenGlobalEvents: UnwrapRef<typeof import('./composables/event')['listenGlobalEvents']>
|
||||
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
|
||||
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
|
||||
readonly mapState: UnwrapRef<typeof import('pinia')['mapState']>
|
||||
@@ -157,6 +164,7 @@ declare module 'vue' {
|
||||
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
|
||||
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
|
||||
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
|
||||
readonly onNetworkInstanceChange: UnwrapRef<typeof import('./composables/mobile_vpn')['onNetworkInstanceChange']>
|
||||
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
|
||||
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
|
||||
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
|
||||
@@ -164,22 +172,23 @@ declare module 'vue' {
|
||||
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
|
||||
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
|
||||
readonly onWatcherCleanup: UnwrapRef<typeof import('vue')['onWatcherCleanup']>
|
||||
readonly parseNetworkConfig: UnwrapRef<typeof import('./composables/network')['parseNetworkConfig']>
|
||||
readonly parseNetworkConfig: UnwrapRef<typeof import('./composables/backend')['parseNetworkConfig']>
|
||||
readonly prepareVpnService: UnwrapRef<typeof import('./composables/mobile_vpn')['prepareVpnService']>
|
||||
readonly provide: UnwrapRef<typeof import('vue')['provide']>
|
||||
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
|
||||
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
|
||||
readonly ref: UnwrapRef<typeof import('vue')['ref']>
|
||||
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
|
||||
readonly retainNetworkInstance: UnwrapRef<typeof import('./composables/network')['retainNetworkInstance']>
|
||||
readonly runNetworkInstance: UnwrapRef<typeof import('./composables/network')['runNetworkInstance']>
|
||||
readonly runNetworkInstance: UnwrapRef<typeof import('./composables/backend')['runNetworkInstance']>
|
||||
readonly saveNetworkConfig: UnwrapRef<typeof import('./composables/backend')['saveNetworkConfig']>
|
||||
readonly sendConfigs: UnwrapRef<typeof import('./composables/backend')['sendConfigs']>
|
||||
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
|
||||
readonly setLoggingLevel: UnwrapRef<typeof import('./composables/network')['setLoggingLevel']>
|
||||
readonly setLoggingLevel: UnwrapRef<typeof import('./composables/backend')['setLoggingLevel']>
|
||||
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
|
||||
readonly setTrayMenu: UnwrapRef<typeof import('./composables/tray')['setTrayMenu']>
|
||||
readonly setTrayRunState: UnwrapRef<typeof import('./composables/tray')['setTrayRunState']>
|
||||
readonly setTrayTooltip: UnwrapRef<typeof import('./composables/tray')['setTrayTooltip']>
|
||||
readonly setTunFd: UnwrapRef<typeof import('./composables/network')['setTunFd']>
|
||||
readonly setTunFd: UnwrapRef<typeof import('./composables/backend')['setTunFd']>
|
||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
|
||||
@@ -190,6 +199,7 @@ declare module 'vue' {
|
||||
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
|
||||
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
|
||||
readonly unref: UnwrapRef<typeof import('vue')['unref']>
|
||||
readonly updateNetworkConfigState: UnwrapRef<typeof import('./composables/backend')['updateNetworkConfigState']>
|
||||
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
|
||||
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
|
||||
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
|
||||
@@ -197,12 +207,12 @@ declare module 'vue' {
|
||||
readonly useId: UnwrapRef<typeof import('vue')['useId']>
|
||||
readonly useLink: UnwrapRef<typeof import('vue-router/auto')['useLink']>
|
||||
readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
|
||||
readonly useNetworkStore: UnwrapRef<typeof import('./stores/network')['useNetworkStore']>
|
||||
readonly useRoute: UnwrapRef<typeof import('vue-router')['useRoute']>
|
||||
readonly useRouter: UnwrapRef<typeof import('vue-router')['useRouter']>
|
||||
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||
readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
|
||||
readonly useTray: UnwrapRef<typeof import('./composables/tray')['useTray']>
|
||||
readonly validateConfig: UnwrapRef<typeof import('./composables/backend')['validateConfig']>
|
||||
readonly watch: UnwrapRef<typeof import('vue')['watch']>
|
||||
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
|
||||
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { getEasytierVersion } from '~/composables/network'
|
||||
import { getEasytierVersion } from '~/composables/backend'
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { Api, type NetworkTypes } from 'easytier-frontend-lib'
|
||||
import { getAutoLaunchStatusAsync } from '~/modules/auto_launch'
|
||||
|
||||
type NetworkConfig = NetworkTypes.NetworkConfig
|
||||
type ValidateConfigResponse = Api.ValidateConfigResponse
|
||||
type ListNetworkInstanceIdResponse = Api.ListNetworkInstanceIdResponse
|
||||
|
||||
export async function parseNetworkConfig(cfg: NetworkConfig) {
|
||||
return invoke<string>('parse_network_config', { cfg })
|
||||
}
|
||||
|
||||
export async function generateNetworkConfig(tomlConfig: string) {
|
||||
return invoke<NetworkConfig>('generate_network_config', { tomlConfig })
|
||||
}
|
||||
|
||||
export async function runNetworkInstance(cfg: NetworkConfig) {
|
||||
return invoke('run_network_instance', { cfg })
|
||||
}
|
||||
|
||||
export async function collectNetworkInfo(instanceId: string) {
|
||||
return await invoke<Api.CollectNetworkInfoResponse>('collect_network_info', { instanceId })
|
||||
}
|
||||
|
||||
export async function setLoggingLevel(level: string) {
|
||||
return await invoke('set_logging_level', { level })
|
||||
}
|
||||
|
||||
export async function setTunFd(fd: number) {
|
||||
return await invoke('set_tun_fd', { fd })
|
||||
}
|
||||
|
||||
export async function getEasytierVersion() {
|
||||
return await invoke<string>('easytier_version')
|
||||
}
|
||||
|
||||
export async function listNetworkInstanceIds() {
|
||||
return await invoke<ListNetworkInstanceIdResponse>('list_network_instance_ids')
|
||||
}
|
||||
|
||||
export async function deleteNetworkInstance(instanceId: string) {
|
||||
return await invoke('remove_network_instance', { instanceId })
|
||||
}
|
||||
|
||||
export async function updateNetworkConfigState(instanceId: string, disabled: boolean) {
|
||||
return await invoke('update_network_config_state', { instanceId, disabled })
|
||||
}
|
||||
|
||||
export async function saveNetworkConfig(cfg: NetworkConfig) {
|
||||
return await invoke('save_network_config', { cfg })
|
||||
}
|
||||
|
||||
export async function validateConfig(cfg: NetworkConfig) {
|
||||
return await invoke<ValidateConfigResponse>('validate_config', { cfg })
|
||||
}
|
||||
|
||||
export async function getConfig(instanceId: string) {
|
||||
return await invoke<NetworkConfig>('get_config', { instanceId })
|
||||
}
|
||||
|
||||
export async function sendConfigs() {
|
||||
let networkList: NetworkConfig[] = JSON.parse(localStorage.getItem('networkList') || '[]');
|
||||
let autoStartInstIds = getAutoLaunchStatusAsync() ? JSON.parse(localStorage.getItem('autoStartInstIds') || '[]') : []
|
||||
return await invoke('load_configs', { configs: networkList, enabledNetworks: autoStartInstIds })
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Event, listen } from "@tauri-apps/api/event";
|
||||
import { type } from "@tauri-apps/plugin-os";
|
||||
import { NetworkTypes } from "easytier-frontend-lib"
|
||||
|
||||
const EVENTS = Object.freeze({
|
||||
SAVE_CONFIGS: 'save_configs',
|
||||
SAVE_ENABLED_NETWORKS: 'save_enabled_networks',
|
||||
PRE_RUN_NETWORK_INSTANCE: 'pre_run_network_instance',
|
||||
POST_RUN_NETWORK_INSTANCE: 'post_run_network_instance',
|
||||
VPN_SERVICE_STOP: 'vpn_service_stop',
|
||||
});
|
||||
|
||||
function onSaveConfigs(event: Event<NetworkTypes.NetworkConfig[]>) {
|
||||
console.log(`Received event '${EVENTS.SAVE_CONFIGS}': ${event.payload}`);
|
||||
localStorage.setItem('networkList', JSON.stringify(event.payload));
|
||||
}
|
||||
|
||||
function onSaveEnabledNetworks(event: Event<string[]>) {
|
||||
console.log(`Received event '${EVENTS.SAVE_ENABLED_NETWORKS}': ${event.payload}`);
|
||||
localStorage.setItem('autoStartInstIds', JSON.stringify(event.payload));
|
||||
}
|
||||
|
||||
async function onPreRunNetworkInstance(event: Event<string>) {
|
||||
if (type() === 'android') {
|
||||
await prepareVpnService(event.payload);
|
||||
}
|
||||
}
|
||||
|
||||
async function onPostRunNetworkInstance(event: Event<string>) {
|
||||
if (type() === 'android') {
|
||||
await onNetworkInstanceChange(event.payload);
|
||||
}
|
||||
}
|
||||
|
||||
async function onVpnServiceStop(event: Event<string>) {
|
||||
await onNetworkInstanceChange(event.payload);
|
||||
}
|
||||
|
||||
export async function listenGlobalEvents() {
|
||||
const unlisteners = [
|
||||
await listen(EVENTS.SAVE_CONFIGS, onSaveConfigs),
|
||||
await listen(EVENTS.SAVE_ENABLED_NETWORKS, onSaveEnabledNetworks),
|
||||
await listen(EVENTS.PRE_RUN_NETWORK_INSTANCE, onPreRunNetworkInstance),
|
||||
await listen(EVENTS.POST_RUN_NETWORK_INSTANCE, onPostRunNetworkInstance),
|
||||
await listen(EVENTS.VPN_SERVICE_STOP, onVpnServiceStop),
|
||||
];
|
||||
|
||||
return () => {
|
||||
unlisteners.forEach(unlisten => unlisten());
|
||||
};
|
||||
}
|
||||
@@ -5,8 +5,6 @@ import { prepare_vpn, start_vpn, stop_vpn } from 'tauri-plugin-vpnservice-api'
|
||||
|
||||
type Route = NetworkTypes.Route
|
||||
|
||||
const networkStore = useNetworkStore()
|
||||
|
||||
interface vpnStatus {
|
||||
running: boolean
|
||||
ipv4Addr: string | null | undefined
|
||||
@@ -69,7 +67,7 @@ async function onVpnServiceStart(payload: any) {
|
||||
console.log('vpn service start', JSON.stringify(payload))
|
||||
curVpnStatus.running = true
|
||||
if (payload.fd) {
|
||||
setTunFd(networkStore.networkInstanceIds[0], payload.fd)
|
||||
setTunFd(payload.fd)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,20 +114,17 @@ function getRoutesForVpn(routes: Route[], node_config: NetworkTypes.NetworkConfi
|
||||
return Array.from(new Set(ret)).sort()
|
||||
}
|
||||
|
||||
async function onNetworkInstanceChange() {
|
||||
console.error('vpn service watch network instance change ids', JSON.stringify(networkStore.networkInstanceIds))
|
||||
const insts = networkStore.networkInstanceIds
|
||||
const no_tun = networkStore.isNoTunEnabled(insts[0])
|
||||
if (no_tun) {
|
||||
export async function onNetworkInstanceChange(instanceId: string) {
|
||||
console.error('vpn service network instance change id', instanceId)
|
||||
if (!instanceId) {
|
||||
await doStopVpn()
|
||||
return
|
||||
}
|
||||
if (!insts) {
|
||||
await doStopVpn()
|
||||
const config = await getConfig(instanceId)
|
||||
if (config.no_tun) {
|
||||
return
|
||||
}
|
||||
|
||||
const curNetworkInfo = networkStore.networkInfos[insts[0]]
|
||||
const curNetworkInfo = (await collectNetworkInfo(instanceId)).info.map[instanceId]
|
||||
if (!curNetworkInfo || curNetworkInfo?.error_msg?.length) {
|
||||
await doStopVpn()
|
||||
return
|
||||
@@ -146,7 +141,7 @@ async function onNetworkInstanceChange() {
|
||||
network_length = 24
|
||||
}
|
||||
|
||||
const routes = getRoutesForVpn(curNetworkInfo?.routes, networkStore.curNetwork)
|
||||
const routes = getRoutesForVpn(curNetworkInfo?.routes, config)
|
||||
|
||||
const ipChanged = virtual_ip !== curVpnStatus.ipv4Addr
|
||||
const routesChanged = JSON.stringify(routes) !== JSON.stringify(curVpnStatus.routes)
|
||||
@@ -164,48 +159,25 @@ async function onNetworkInstanceChange() {
|
||||
await doStartVpn(virtual_ip, 24, routes)
|
||||
}
|
||||
catch (e) {
|
||||
console.error('start vpn service failed, clear all network insts.', e)
|
||||
networkStore.clearNetworkInstances()
|
||||
await retainNetworkInstance(networkStore.networkInstanceIds)
|
||||
console.error('start vpn service failed, stop all other network insts.', e)
|
||||
await runNetworkInstance(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function watchNetworkInstance() {
|
||||
let subscribe_running = false
|
||||
networkStore.$subscribe(async () => {
|
||||
if (subscribe_running) {
|
||||
return
|
||||
}
|
||||
subscribe_running = true
|
||||
try {
|
||||
await onNetworkInstanceChange()
|
||||
}
|
||||
catch (_) {
|
||||
}
|
||||
subscribe_running = false
|
||||
})
|
||||
console.error('vpn service watch network instance')
|
||||
}
|
||||
|
||||
function isNoTunEnabled(instanceId: string | undefined) {
|
||||
async function isNoTunEnabled(instanceId: string | undefined) {
|
||||
if (!instanceId) {
|
||||
return false
|
||||
}
|
||||
const no_tun = networkStore.isNoTunEnabled(instanceId)
|
||||
if (no_tun) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return (await getConfig(instanceId)).no_tun ?? false
|
||||
}
|
||||
|
||||
export async function initMobileVpnService() {
|
||||
await registerVpnServiceListener()
|
||||
await watchNetworkInstance()
|
||||
}
|
||||
|
||||
export async function prepareVpnService(instanceId: string) {
|
||||
if (isNoTunEnabled(instanceId)) {
|
||||
if (await isNoTunEnabled(instanceId)) {
|
||||
return
|
||||
}
|
||||
console.log('prepare vpn')
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
import type { NetworkTypes } from 'easytier-frontend-lib'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
|
||||
type NetworkConfig = NetworkTypes.NetworkConfig
|
||||
type NetworkInstanceRunningInfo = NetworkTypes.NetworkInstanceRunningInfo
|
||||
|
||||
export async function parseNetworkConfig(cfg: NetworkConfig) {
|
||||
return invoke<string>('parse_network_config', { cfg })
|
||||
}
|
||||
|
||||
export async function generateNetworkConfig(tomlConfig: string) {
|
||||
return invoke<NetworkConfig>('generate_network_config', { tomlConfig })
|
||||
}
|
||||
|
||||
export async function runNetworkInstance(cfg: NetworkConfig) {
|
||||
return invoke('run_network_instance', { cfg })
|
||||
}
|
||||
|
||||
export async function retainNetworkInstance(instanceIds: string[]) {
|
||||
return invoke('retain_network_instance', { instanceIds })
|
||||
}
|
||||
|
||||
export async function collectNetworkInfos() {
|
||||
return await invoke<Record<string, NetworkInstanceRunningInfo>>('collect_network_infos')
|
||||
}
|
||||
|
||||
export async function getOsHostname() {
|
||||
return await invoke<string>('get_os_hostname')
|
||||
}
|
||||
|
||||
export async function isAutostart() {
|
||||
return await invoke<boolean>('is_autostart')
|
||||
}
|
||||
|
||||
export async function setLoggingLevel(level: string) {
|
||||
return await invoke('set_logging_level', { level })
|
||||
}
|
||||
|
||||
export async function setTunFd(instanceId: string, fd: number) {
|
||||
return await invoke('set_tun_fd', { instanceId, fd })
|
||||
}
|
||||
|
||||
export async function getEasytierVersion() {
|
||||
return await invoke<string>('easytier_version')
|
||||
}
|
||||
+12
-10
@@ -1,15 +1,15 @@
|
||||
import Aura from '@primeuix/themes/aura';
|
||||
import PrimeVue from 'primevue/config'
|
||||
import ToastService from 'primevue/toastservice'
|
||||
import PrimeVue from 'primevue/config';
|
||||
|
||||
import { createRouter, createWebHistory } from 'vue-router/auto'
|
||||
import { routes } from 'vue-router/auto-routes'
|
||||
import App from '~/App.vue'
|
||||
import EasyTierFrontendLib, { I18nUtils } from 'easytier-frontend-lib'
|
||||
import EasyTierFrontendLib, { I18nUtils } from 'easytier-frontend-lib';
|
||||
import { createRouter, createWebHistory } from 'vue-router/auto';
|
||||
import { routes } from 'vue-router/auto-routes';
|
||||
import App from '~/App.vue';
|
||||
|
||||
import { getAutoLaunchStatusAsync, loadAutoLaunchStatusAsync } from './modules/auto_launch'
|
||||
import '~/styles.css'
|
||||
import 'easytier-frontend-lib/style.css'
|
||||
import 'easytier-frontend-lib/style.css';
|
||||
import { ConfirmationService, DialogService, ToastService } from 'primevue';
|
||||
import '~/styles.css';
|
||||
import { getAutoLaunchStatusAsync, loadAutoLaunchStatusAsync } from './modules/auto_launch';
|
||||
|
||||
if (import.meta.env.PROD) {
|
||||
document.addEventListener('keydown', (event) => {
|
||||
@@ -55,7 +55,9 @@ async function main() {
|
||||
},
|
||||
},
|
||||
})
|
||||
app.use(ToastService as any)
|
||||
app.use(ToastService)
|
||||
app.use(DialogService)
|
||||
app.use(ConfirmationService)
|
||||
app.mount('#app')
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import { type Api, type NetworkTypes } from "easytier-frontend-lib";
|
||||
import * as backend from "~/composables/backend";
|
||||
|
||||
export class GUIRemoteClient implements Api.RemoteClient {
|
||||
async validate_config(config: NetworkTypes.NetworkConfig): Promise<Api.ValidateConfigResponse> {
|
||||
return backend.validateConfig(config);
|
||||
}
|
||||
async run_network(config: NetworkTypes.NetworkConfig): Promise<undefined> {
|
||||
await backend.runNetworkInstance(config);
|
||||
}
|
||||
async get_network_info(inst_id: string): Promise<NetworkTypes.NetworkInstanceRunningInfo | undefined> {
|
||||
return backend.collectNetworkInfo(inst_id).then(infos => infos.info.map[inst_id]);
|
||||
}
|
||||
async list_network_instance_ids(): Promise<Api.ListNetworkInstanceIdResponse> {
|
||||
return backend.listNetworkInstanceIds();
|
||||
}
|
||||
async delete_network(inst_id: string): Promise<undefined> {
|
||||
await backend.deleteNetworkInstance(inst_id);
|
||||
}
|
||||
async update_network_instance_state(inst_id: string, disabled: boolean): Promise<undefined> {
|
||||
await backend.updateNetworkConfigState(inst_id, disabled);
|
||||
}
|
||||
async save_config(config: NetworkTypes.NetworkConfig): Promise<undefined> {
|
||||
await backend.saveNetworkConfig(config);
|
||||
}
|
||||
async get_network_config(inst_id: string): Promise<NetworkTypes.NetworkConfig> {
|
||||
return backend.getConfig(inst_id);
|
||||
}
|
||||
async generate_config(config: NetworkTypes.NetworkConfig): Promise<Api.GenerateConfigResponse> {
|
||||
try {
|
||||
return { toml_config: await backend.parseNetworkConfig(config) };
|
||||
} catch (e) {
|
||||
return { error: e + "" };
|
||||
}
|
||||
}
|
||||
async parse_config(toml_config: string): Promise<Api.ParseConfigResponse> {
|
||||
try {
|
||||
return { config: await backend.generateNetworkConfig(toml_config) }
|
||||
} catch (e) {
|
||||
return { error: e + "" };
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,148 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import { appLogDir } from '@tauri-apps/api/path'
|
||||
|
||||
import { getCurrentWindow } from '@tauri-apps/api/window'
|
||||
import { writeText } from '@tauri-apps/plugin-clipboard-manager'
|
||||
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 { NetworkTypes, Config, Status, Utils, I18nUtils, ConfigEditDialog } from 'easytier-frontend-lib'
|
||||
|
||||
import { isAutostart, setLoggingLevel } from '~/composables/network'
|
||||
import { appLogDir } from '@tauri-apps/api/path'
|
||||
import { writeText } from '@tauri-apps/plugin-clipboard-manager'
|
||||
import { exit } from '@tauri-apps/plugin-process'
|
||||
import { I18nUtils, RemoteManagement } from "easytier-frontend-lib"
|
||||
import type { MenuItem } from 'primevue/menuitem'
|
||||
import { useTray } from '~/composables/tray'
|
||||
import { GUIRemoteClient } from '~/modules/api'
|
||||
import { getAutoLaunchStatusAsync as getAutoLaunchStatus, loadAutoLaunchStatusAsync } from '~/modules/auto_launch'
|
||||
import { getDockVisibilityStatus, loadDockVisibilityAsync } from '~/modules/dock_visibility'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
const visible = ref(false)
|
||||
const aboutVisible = ref(false)
|
||||
const tomlConfig = ref('')
|
||||
|
||||
useTray(true)
|
||||
|
||||
const items = ref([
|
||||
{
|
||||
label: () => activeStep.value == "2" ? t('show_config') : t('edit_config'),
|
||||
icon: 'pi pi-file-edit',
|
||||
command: async () => {
|
||||
try {
|
||||
const ret = await parseNetworkConfig(networkStore.curNetwork)
|
||||
tomlConfig.value = ret
|
||||
}
|
||||
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()
|
||||
},
|
||||
disabled: () => networkStore.networkList.length <= 1,
|
||||
},
|
||||
])
|
||||
const remoteClient = computed(() => new GUIRemoteClient());
|
||||
const instanceId = ref<string | undefined>(undefined);
|
||||
|
||||
enum Severity {
|
||||
None = 'none',
|
||||
Success = 'success',
|
||||
Info = 'info',
|
||||
Warn = 'warn',
|
||||
Error = 'error',
|
||||
}
|
||||
|
||||
const messageBarSeverity = ref(Severity.None)
|
||||
const messageBarContent = ref('')
|
||||
const toast = useToast()
|
||||
|
||||
const networkStore = useNetworkStore()
|
||||
|
||||
const curNetworkConfig = computed(() => {
|
||||
if (networkStore.curNetworkId) {
|
||||
// console.log('instanceId', props.instanceId)
|
||||
const c = networkStore.networkList.find(n => n.instance_id === networkStore.curNetworkId)
|
||||
if (c !== undefined)
|
||||
return c
|
||||
}
|
||||
|
||||
return networkStore.curNetwork
|
||||
})
|
||||
|
||||
const curNetworkInst = computed<NetworkTypes.NetworkInstance | null>(() => {
|
||||
let ret = networkStore.networkInstances.find(n => n.instance_id === curNetworkConfig.value.instance_id)
|
||||
console.log('curNetworkInst', ret)
|
||||
if (ret === undefined) {
|
||||
return null;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
})
|
||||
|
||||
function addNewNetwork() {
|
||||
networkStore.addNewNetwork()
|
||||
networkStore.curNetwork = networkStore.lastNetwork
|
||||
}
|
||||
|
||||
networkStore.$subscribe(async () => {
|
||||
networkStore.saveToLocalStorage()
|
||||
try {
|
||||
await parseNetworkConfig(networkStore.curNetwork)
|
||||
messageBarSeverity.value = Severity.None
|
||||
}
|
||||
catch (e: any) {
|
||||
messageBarContent.value = e
|
||||
messageBarSeverity.value = Severity.Error
|
||||
}
|
||||
})
|
||||
|
||||
async function runNetworkCb(cfg: NetworkTypes.NetworkConfig, cb: () => void) {
|
||||
if (type() === 'android') {
|
||||
await prepareVpnService(cfg.instance_id)
|
||||
networkStore.clearNetworkInstances()
|
||||
}
|
||||
else {
|
||||
networkStore.removeNetworkInstance(cfg.instance_id)
|
||||
}
|
||||
|
||||
await retainNetworkInstance(networkStore.networkInstanceIds)
|
||||
networkStore.addNetworkInstance(cfg.instance_id)
|
||||
|
||||
try {
|
||||
await runNetworkInstance(cfg)
|
||||
networkStore.addAutoStartInstId(cfg.instance_id)
|
||||
}
|
||||
catch (e: any) {
|
||||
// console.error(e)
|
||||
toast.add({ severity: 'info', detail: e })
|
||||
}
|
||||
|
||||
cb()
|
||||
}
|
||||
|
||||
async function stopNetworkCb(cfg: NetworkTypes.NetworkConfig, cb: () => void) {
|
||||
// console.log('stopNetworkCb', cfg, cb)
|
||||
cb()
|
||||
networkStore.removeNetworkInstance(cfg.instance_id)
|
||||
await retainNetworkInstance(networkStore.networkInstanceIds)
|
||||
networkStore.removeAutoStartInstId(cfg.instance_id)
|
||||
}
|
||||
|
||||
async function updateNetworkInfos() {
|
||||
networkStore.updateWithNetworkInfos(await collectNetworkInfos())
|
||||
}
|
||||
|
||||
let intervalId = 0
|
||||
onMounted(async () => {
|
||||
intervalId = window.setInterval(async () => {
|
||||
await updateNetworkInfos()
|
||||
}, 500)
|
||||
|
||||
window.setTimeout(async () => {
|
||||
await setTrayMenu([
|
||||
await MenuItemShow(t('tray.show')),
|
||||
@@ -150,16 +28,47 @@ onMounted(async () => {
|
||||
])
|
||||
}, 1000)
|
||||
})
|
||||
onUnmounted(() => clearInterval(intervalId))
|
||||
|
||||
const activeStep = computed(() => {
|
||||
return networkStore.networkInstanceIds.includes(networkStore.curNetworkId) ? '2' : '1'
|
||||
})
|
||||
|
||||
let current_log_level = 'off'
|
||||
|
||||
const setting_menu = ref()
|
||||
const setting_menu_items = ref([
|
||||
const log_menu = ref()
|
||||
const log_menu_items_popup: Ref<MenuItem[]> = ref([
|
||||
...['off', 'warn', 'info', 'debug', 'trace'].map(level => ({
|
||||
label: () => t(`logging_level_${level}`) + (current_log_level === level ? ' ✓' : ''),
|
||||
command: async () => {
|
||||
current_log_level = level
|
||||
await setLoggingLevel(level)
|
||||
},
|
||||
})),
|
||||
{
|
||||
separator: true,
|
||||
},
|
||||
{
|
||||
label: () => t('logging_open_dir'),
|
||||
icon: 'pi pi-folder-open',
|
||||
command: async () => {
|
||||
// console.log('open log dir', await appLogDir())
|
||||
await open(await appLogDir())
|
||||
},
|
||||
},
|
||||
{
|
||||
label: () => t('logging_copy_dir'),
|
||||
icon: 'pi pi-tablet',
|
||||
command: async () => {
|
||||
await writeText(await appLogDir())
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
function toggle_log_menu(event: any) {
|
||||
log_menu.value.toggle(event)
|
||||
}
|
||||
|
||||
function getLabel(item: MenuItem) {
|
||||
return typeof item.label === 'function' ? item.label() : item.label
|
||||
}
|
||||
|
||||
const setting_menu_items: Ref<MenuItem[]> = ref([
|
||||
{
|
||||
label: () => t('exchange_language'),
|
||||
icon: 'pi pi-language',
|
||||
@@ -187,40 +96,10 @@ const setting_menu_items = ref([
|
||||
visible: () => type() === 'macos',
|
||||
},
|
||||
{
|
||||
key: 'logging_menu',
|
||||
label: () => t('logging'),
|
||||
icon: 'pi pi-file',
|
||||
items: (function () {
|
||||
const levels = ['off', 'warn', 'info', 'debug', 'trace']
|
||||
const items = []
|
||||
for (const level of levels) {
|
||||
items.push({
|
||||
label: () => t(`logging_level_${level}`) + (current_log_level === level ? ' ✓' : ''),
|
||||
command: async () => {
|
||||
current_log_level = level
|
||||
await setLoggingLevel(level)
|
||||
},
|
||||
})
|
||||
}
|
||||
items.push({
|
||||
separator: true,
|
||||
})
|
||||
items.push({
|
||||
label: () => t('logging_open_dir'),
|
||||
icon: 'pi pi-folder-open',
|
||||
command: async () => {
|
||||
// console.log('open log dir', await appLogDir())
|
||||
await open(await appLogDir())
|
||||
},
|
||||
})
|
||||
items.push({
|
||||
label: () => t('logging_copy_dir'),
|
||||
icon: 'pi pi-tablet',
|
||||
command: async () => {
|
||||
await writeText(await appLogDir())
|
||||
},
|
||||
})
|
||||
return items
|
||||
})(),
|
||||
items: [], // Keep this to show it's a parent menu
|
||||
},
|
||||
{
|
||||
label: () => t('about.title'),
|
||||
@@ -238,25 +117,6 @@ const setting_menu_items = ref([
|
||||
},
|
||||
])
|
||||
|
||||
function toggle_setting_menu(event: any) {
|
||||
setting_menu.value.toggle(event)
|
||||
}
|
||||
|
||||
onBeforeMount(async () => {
|
||||
networkStore.loadFromLocalStorage()
|
||||
if (type() !== 'android' && getAutoLaunchStatus() && await isAutostart()) {
|
||||
getCurrentWindow().hide()
|
||||
const autoStartIds = networkStore.autoStartInstIds
|
||||
for (const id of autoStartIds) {
|
||||
const cfg = networkStore.networkList.find((item: NetworkTypes.NetworkConfig) => item.instance_id === id)
|
||||
if (cfg) {
|
||||
networkStore.addNetworkInstance(cfg.instance_id)
|
||||
await runNetworkInstance(cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
if (type() === 'android') {
|
||||
try {
|
||||
@@ -266,125 +126,37 @@ onMounted(async () => {
|
||||
console.error("easytier init vpn service failed", e)
|
||||
}
|
||||
}
|
||||
const unlisten = await listenGlobalEvents()
|
||||
await sendConfigs()
|
||||
return () => {
|
||||
unlisten()
|
||||
}
|
||||
})
|
||||
|
||||
function isRunning(id: string) {
|
||||
return networkStore.networkInstanceIds.includes(id)
|
||||
}
|
||||
|
||||
async function saveTomlConfig(tomlConfig: string) {
|
||||
const config = await generateNetworkConfig(tomlConfig)
|
||||
networkStore.replaceCurNetwork(config);
|
||||
toast.add({ severity: 'success', detail: t('config_saved'), life: 3000 })
|
||||
visible.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="root" class="flex flex-col">
|
||||
<ConfigEditDialog v-model:visible="visible" :cur-network="curNetworkConfig" :readonly="activeStep !== '1'"
|
||||
:save-config="saveTomlConfig" :generate-config="parseNetworkConfig" />
|
||||
|
||||
<Dialog v-model:visible="aboutVisible" modal :header="t('about.title')" :style="{ width: '70%' }">
|
||||
<About />
|
||||
</Dialog>
|
||||
<Menu ref="log_menu" :model="log_menu_items_popup" :popup="true" />
|
||||
|
||||
<div class="w-full">
|
||||
<div class="flex items-center gap-4 p-4 h-20">
|
||||
<!-- 网络按钮 -->
|
||||
<div class="flex shrink-0 items-center">
|
||||
<Button icon="pi pi-plus" severity="primary" :label="t('add_new_network')" class="hidden md:inline-flex"
|
||||
@click="addNewNetwork" />
|
||||
<Button icon="pi pi-plus" severity="primary" class="md:hidden px-6" @click="addNewNetwork" />
|
||||
</div>
|
||||
<RemoteManagement class="flex-1 overflow-y-auto" :api="remoteClient" v-bind:instance-id="instanceId" />
|
||||
|
||||
<!-- 网络选择 - 占据中间剩余空间 -->
|
||||
<Select v-model="networkStore.curNetwork" :options="networkStore.networkList" :highlight-on-select="false"
|
||||
:placeholder="t('select_network')" class="flex-1 h-full min-w-0">
|
||||
<template #value="slotProps">
|
||||
<div class="flex items-center content-center min-w-0">
|
||||
<div class="mr-4 flex-col min-w-0 flex-1">
|
||||
<span class="truncate block"> {{ slotProps.value.network_name }}</span>
|
||||
</div>
|
||||
<Tag class="my-auto leading-3 shrink-0"
|
||||
:severity="isRunning(slotProps.value.instance_id) ? 'success' : 'info'"
|
||||
:value="t(isRunning(slotProps.value.instance_id) ? 'network_running' : 'network_stopped')" />
|
||||
</div>
|
||||
</template>
|
||||
<template #option="slotProps">
|
||||
<div class="flex flex-col items-start content-center max-w-full">
|
||||
<div class="flex items-center min-w-0 w-full">
|
||||
<div class="mr-4 min-w-0 flex-1">
|
||||
<span class="truncate block">{{ t('network_name') }}: {{ slotProps.option.network_name }}</span>
|
||||
</div>
|
||||
<Tag class="my-auto leading-3 shrink-0"
|
||||
:severity="isRunning(slotProps.option.instance_id) ? 'success' : 'info'"
|
||||
:value="t(isRunning(slotProps.option.instance_id) ? 'network_running' : 'network_stopped')" />
|
||||
</div>
|
||||
<div v-if="slotProps.option.networking_method !== NetworkTypes.NetworkingMethod.Standalone"
|
||||
class="max-w-full overflow-hidden text-ellipsis">
|
||||
{{ slotProps.option.networking_method === NetworkTypes.NetworkingMethod.Manual
|
||||
? slotProps.option.peer_urls.join(', ')
|
||||
: slotProps.option.public_server_url }}
|
||||
</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)">
|
||||
{{
|
||||
Utils.ipv4InetToString(networkStore.instances[slotProps.option.instance_id].detail?.my_node_info.virtual_ipv4)
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</Select>
|
||||
|
||||
<!-- 设置按钮 -->
|
||||
<div class="flex items-center shrink-0">
|
||||
<Button icon="pi pi-cog" severity="secondary" aria-haspopup="true" :label="t('settings')"
|
||||
class="hidden md:inline-flex" aria-controls="overlay_setting_menu" @click="toggle_setting_menu" />
|
||||
<Button icon="pi pi-cog" severity="secondary" aria-haspopup="true" class="md:hidden px-6"
|
||||
aria-controls="overlay_setting_menu" @click="toggle_setting_menu" />
|
||||
<TieredMenu id="overlay_setting_menu" ref="setting_menu" :model="setting_menu_items" :popup="true" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Panel class="h-full overflow-y-auto">
|
||||
<Stepper :value="activeStep">
|
||||
<StepList value="1">
|
||||
<Step value="1">
|
||||
{{ t('config_network') }}
|
||||
</Step>
|
||||
<Step value="2">
|
||||
{{ t('running') }}
|
||||
</Step>
|
||||
</StepList>
|
||||
<StepPanels value="1">
|
||||
<StepPanel v-slot="{ activateCallback = (s: string) => { } } = {}" value="1">
|
||||
<Config :instance-id="networkStore.curNetworkId" :config-invalid="messageBarSeverity !== Severity.None"
|
||||
:cur-network="curNetworkConfig" @run-network="runNetworkCb($event, () => activateCallback('2'))" />
|
||||
</StepPanel>
|
||||
<StepPanel v-slot="{ activateCallback = (s: string) => { } } = {}" value="2">
|
||||
<div class="flex flex-col">
|
||||
<Status :cur-network-inst="curNetworkInst" />
|
||||
</div>
|
||||
<div class="flex pt-6 justify-center">
|
||||
<Button :label="t('stop_network')" severity="danger" icon="pi pi-arrow-left"
|
||||
@click="stopNetworkCb(networkStore.curNetwork, () => activateCallback('1'))" />
|
||||
</div>
|
||||
</StepPanel>
|
||||
</StepPanels>
|
||||
</Stepper>
|
||||
</Panel>
|
||||
|
||||
<div>
|
||||
<Menubar :model="items" breakpoint="300px" />
|
||||
<InlineMessage v-if="messageBarSeverity !== Severity.None" class="absolute bottom-0 right-0" severity="error">
|
||||
{{ messageBarContent }}
|
||||
</InlineMessage>
|
||||
</div>
|
||||
<Menubar :model="setting_menu_items" breakpoint="560px">
|
||||
<template #item="{ item, props }">
|
||||
<a v-if="item.key === 'logging_menu'" v-bind="props.action" @click="toggle_log_menu">
|
||||
<span :class="item.icon" />
|
||||
<span class="p-menubar-item-label">{{ getLabel(item) }}</span>
|
||||
<span class="pi pi-angle-down p-menubar-item-icon text-[9px]"></span>
|
||||
</a>
|
||||
<a v-else v-bind="props.action">
|
||||
<span :class="item.icon" />
|
||||
<span class="p-menubar-item-label">{{ getLabel(item) }}</span>
|
||||
</a>
|
||||
</template>
|
||||
</Menubar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
import { NetworkTypes } from 'easytier-frontend-lib'
|
||||
|
||||
export const useNetworkStore = defineStore('networkStore', {
|
||||
state: () => {
|
||||
const networkList = [NetworkTypes.DEFAULT_NETWORK_CONFIG()]
|
||||
return {
|
||||
// for initially empty lists
|
||||
networkList: networkList as NetworkTypes.NetworkConfig[],
|
||||
// for data that is not yet loaded
|
||||
curNetwork: networkList[0],
|
||||
|
||||
// uuid -> instance
|
||||
instances: {} as Record<string, NetworkTypes.NetworkInstance>,
|
||||
|
||||
networkInfos: {} as Record<string, NetworkTypes.NetworkInstanceRunningInfo>,
|
||||
|
||||
autoStartInstIds: [] as string[],
|
||||
}
|
||||
},
|
||||
|
||||
getters: {
|
||||
lastNetwork(): NetworkTypes.NetworkConfig {
|
||||
return this.networkList[this.networkList.length - 1]
|
||||
},
|
||||
|
||||
curNetworkId(): string {
|
||||
return this.curNetwork.instance_id
|
||||
},
|
||||
|
||||
networkInstances(): Array<NetworkTypes.NetworkInstance> {
|
||||
return Object.values(this.instances)
|
||||
},
|
||||
|
||||
networkInstanceIds(): Array<string> {
|
||||
return Object.keys(this.instances)
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
addNewNetwork() {
|
||||
this.networkList.push(NetworkTypes.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]
|
||||
},
|
||||
|
||||
replaceCurNetwork(cfg: NetworkTypes.NetworkConfig) {
|
||||
const curNetworkIdx = this.networkList.indexOf(this.curNetwork)
|
||||
this.networkList[curNetworkIdx] = cfg
|
||||
this.curNetwork = cfg
|
||||
},
|
||||
|
||||
removeNetworkInstance(instanceId: string) {
|
||||
delete this.instances[instanceId]
|
||||
},
|
||||
|
||||
addNetworkInstance(instanceId: string) {
|
||||
this.instances[instanceId] = {
|
||||
instance_id: instanceId,
|
||||
running: false,
|
||||
error_msg: '',
|
||||
detail: undefined,
|
||||
}
|
||||
},
|
||||
|
||||
clearNetworkInstances() {
|
||||
this.instances = {}
|
||||
},
|
||||
|
||||
updateWithNetworkInfos(networkInfos: Record<string, NetworkTypes.NetworkInstanceRunningInfo>) {
|
||||
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
|
||||
}
|
||||
},
|
||||
|
||||
loadFromLocalStorage() {
|
||||
let networkList: NetworkTypes.NetworkConfig[]
|
||||
|
||||
// if localStorage default is [{}], instanceId will be undefined
|
||||
networkList = JSON.parse(localStorage.getItem('networkList') || '[]')
|
||||
networkList = networkList.map((cfg) => {
|
||||
return { ...NetworkTypes.DEFAULT_NETWORK_CONFIG(), ...cfg } as NetworkTypes.NetworkConfig
|
||||
})
|
||||
|
||||
// prevent a empty list from localStorage, should not happen
|
||||
if (networkList.length === 0)
|
||||
networkList = [NetworkTypes.DEFAULT_NETWORK_CONFIG()]
|
||||
|
||||
this.networkList = networkList
|
||||
this.curNetwork = this.networkList[0]
|
||||
|
||||
this.loadAutoStartInstIdsFromLocalStorage()
|
||||
},
|
||||
|
||||
saveToLocalStorage() {
|
||||
localStorage.setItem('networkList', JSON.stringify(this.networkList))
|
||||
},
|
||||
|
||||
saveAutoStartInstIdsToLocalStorage() {
|
||||
localStorage.setItem('autoStartInstIds', JSON.stringify(this.autoStartInstIds))
|
||||
},
|
||||
|
||||
loadAutoStartInstIdsFromLocalStorage() {
|
||||
try {
|
||||
this.autoStartInstIds = JSON.parse(localStorage.getItem('autoStartInstIds') || '[]')
|
||||
}
|
||||
catch (e) {
|
||||
console.error(e)
|
||||
this.autoStartInstIds = []
|
||||
}
|
||||
},
|
||||
|
||||
addAutoStartInstId(instanceId: string) {
|
||||
if (!this.autoStartInstIds.includes(instanceId)) {
|
||||
this.autoStartInstIds.push(instanceId)
|
||||
}
|
||||
this.saveAutoStartInstIdsToLocalStorage()
|
||||
},
|
||||
|
||||
removeAutoStartInstId(instanceId: string) {
|
||||
const idx = this.autoStartInstIds.indexOf(instanceId)
|
||||
if (idx !== -1) {
|
||||
this.autoStartInstIds.splice(idx, 1)
|
||||
}
|
||||
this.saveAutoStartInstIdsToLocalStorage()
|
||||
},
|
||||
|
||||
isNoTunEnabled(instanceId: string): boolean {
|
||||
const cfg = this.networkList.find((cfg) => cfg.instance_id === instanceId)
|
||||
if (!cfg)
|
||||
return false
|
||||
return cfg.no_tun ?? false
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if (import.meta.hot)
|
||||
import.meta.hot.accept(acceptHMRUpdate(useNetworkStore as any, import.meta.hot))
|
||||
Reference in New Issue
Block a user