feat(gui): add service and remote mode support (#1578)

This PR fundamentally restructures the EasyTier GUI, introducing support for service mode and remote mode, transforming it from a simple desktop application into a powerful network management terminal. This change allows users to persistently run the EasyTier core as a background service or remotely manage multiple EasyTier instances, greatly improving deployment flexibility and manageability.
This commit is contained in:
Mg Pig
2025-11-25 13:59:27 +08:00
committed by GitHub
parent b44053f496
commit 1f2517c731
29 changed files with 2921 additions and 2097 deletions
+30 -3
View File
@@ -1,11 +1,19 @@
import { invoke } from '@tauri-apps/api/core'
import { Api, type NetworkTypes } from 'easytier-frontend-lib'
import { GetNetworkMetasResponse } from 'node_modules/easytier-frontend-lib/dist/modules/api'
import { getAutoLaunchStatusAsync } from '~/modules/auto_launch'
type NetworkConfig = NetworkTypes.NetworkConfig
type ValidateConfigResponse = Api.ValidateConfigResponse
type ListNetworkInstanceIdResponse = Api.ListNetworkInstanceIdResponse
interface ServiceOptions {
config_dir: string
rpc_portal: string
file_log_level: string
file_log_dir: string
}
export type ServiceStatus = "Running" | "Stopped" | "NotInstalled"
export async function parseNetworkConfig(cfg: NetworkConfig) {
return invoke<string>('parse_network_config', { cfg })
@@ -61,10 +69,29 @@ export async function getConfig(instanceId: string) {
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 })
return await invoke('load_configs', { configs: networkList, enabledNetworks: [] })
}
export async function getNetworkMetas(instanceIds: string[]) {
return await invoke<GetNetworkMetasResponse>('get_network_metas', { instanceIds })
}
export async function initService(opts?: ServiceOptions) {
return await invoke('init_service', { opts })
}
export async function setServiceStatus(enable: boolean) {
return await invoke('set_service_status', { enable })
}
export async function getServiceStatus() {
return await invoke<ServiceStatus>('get_service_status')
}
export async function initRpcConnection(url?: string) {
return await invoke('init_rpc_connection', { url })
}
export async function isClientRunning() {
return await invoke<boolean>('is_client_running')
}
-7
View File
@@ -4,7 +4,6 @@ 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',
@@ -15,11 +14,6 @@ function onSaveConfigs(event: Event<NetworkTypes.NetworkConfig[]>) {
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);
@@ -39,7 +33,6 @@ async function onVpnServiceStop(event: Event<string>) {
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),
+36
View File
@@ -0,0 +1,36 @@
interface NormalMode {
mode: 'normal'
}
export interface ServiceMode {
mode: 'service'
config_dir: string
rpc_portal: string
file_log_level: 'off' | 'warn' | 'info' | 'debug' | 'trace'
file_log_dir: string
}
export interface RemoteMode {
mode: 'remote'
remote_rpc_address: string
}
export function saveMode(mode: Mode) {
localStorage.setItem('app_mode', JSON.stringify(mode))
}
import { type } from '@tauri-apps/plugin-os';
export function loadMode(): Mode {
if (type() === 'android') {
return { mode: 'normal' };
}
const modeStr = localStorage.getItem('app_mode')
if (modeStr) {
return JSON.parse(modeStr) as Mode
} else {
return { mode: 'normal' }
}
}
export type Mode = NormalMode | ServiceMode | RemoteMode