mirror of
https://github.com/EasyTier/EasyTier.git
synced 2024-11-16 11:42:27 +08:00
support start on reboot (#132)
* move launcher to eastier lib * support auto start after reboot
This commit is contained in:
parent
f9e6264f31
commit
6e77e6b5e7
42
Cargo.lock
generated
42
Cargo.lock
generated
|
@ -247,6 +247,17 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9a3820bc9e9aaf60c8389c2a4808548599f4ff254ce6bdb608ac3631d4ad76"
|
||||
|
||||
[[package]]
|
||||
name = "auto-launch"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f012b8cc0c850f34117ec8252a44418f2e34a2cf501de89e29b241ae5f79471"
|
||||
dependencies = [
|
||||
"dirs",
|
||||
"thiserror",
|
||||
"winreg 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "auto_impl"
|
||||
version = "1.2.0"
|
||||
|
@ -1128,6 +1139,15 @@ dependencies = [
|
|||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "4.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
|
@ -1138,6 +1158,17 @@ dependencies = [
|
|||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
|
@ -1260,8 +1291,10 @@ name = "easytier-gui"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auto-launch",
|
||||
"chrono",
|
||||
"dashmap",
|
||||
"dunce",
|
||||
"easytier",
|
||||
"gethostname",
|
||||
"once_cell",
|
||||
|
@ -6260,6 +6293,15 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.50.0"
|
||||
|
|
|
@ -32,6 +32,8 @@ retain_network_instance: 保留网络实例
|
|||
collect_network_infos: 收集网络信息
|
||||
settings: 设置
|
||||
exchange_language: Switch to English
|
||||
disable_auto_launch: 关闭开机自启
|
||||
enable_auto_launch: 开启开机自启
|
||||
exit: 退出
|
||||
chips_placeholder: 例如: {0}, 按回车添加
|
||||
hostname_placeholder: '留空默认为主机名: {0}'
|
||||
|
|
|
@ -32,6 +32,8 @@ retain_network_instance: Retain Network Instance
|
|||
collect_network_infos: Collect Network Infos
|
||||
settings: Settings
|
||||
exchange_language: 切换中文
|
||||
disable_auto_launch: Disable Launch on Reboot
|
||||
enable_auto_launch: Enable Launch on Reboot
|
||||
exit: Exit
|
||||
|
||||
chips_placeholder: 'e.g: {0}, press Enter to add'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[package]
|
||||
name = "easytier-gui"
|
||||
version = "0.0.0"
|
||||
description = "A Tauri App"
|
||||
description = "EasyTier GUI"
|
||||
authors = ["you"]
|
||||
edition = "2021"
|
||||
|
||||
|
@ -29,6 +29,10 @@ dashmap = "5.5.3"
|
|||
|
||||
privilege = "0.3"
|
||||
gethostname = "0.4.3"
|
||||
|
||||
auto-launch = "0.5.0"
|
||||
dunce = "1.0.4"
|
||||
|
||||
[features]
|
||||
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
|
||||
custom-protocol = ["tauri/custom-protocol"]
|
||||
|
|
|
@ -1,22 +1,17 @@
|
|||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
mod launcher;
|
||||
|
||||
use std::{collections::BTreeMap, env::current_exe, process};
|
||||
|
||||
use anyhow::Context;
|
||||
use chrono::{DateTime, Local};
|
||||
use auto_launch::AutoLaunchBuilder;
|
||||
use dashmap::DashMap;
|
||||
use easytier::{
|
||||
common::{
|
||||
config::{ConfigLoader, NetworkIdentity, PeerConfig, TomlConfigLoader, VpnPortalConfig},
|
||||
global_ctx::GlobalCtxEvent,
|
||||
common::config::{
|
||||
ConfigLoader, NetworkIdentity, PeerConfig, TomlConfigLoader, VpnPortalConfig,
|
||||
},
|
||||
rpc::{PeerInfo, Route},
|
||||
utils::{list_peer_route_pair, PeerRoutePair},
|
||||
launcher::{NetworkInstance, NetworkInstanceRunningInfo},
|
||||
};
|
||||
use launcher::{EasyTierLauncher, MyNodeInfo};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use tauri::{
|
||||
|
@ -167,71 +162,6 @@ impl NetworkConfig {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
struct NetworkInstanceRunningInfo {
|
||||
my_node_info: MyNodeInfo,
|
||||
events: Vec<(DateTime<Local>, GlobalCtxEvent)>,
|
||||
node_info: MyNodeInfo,
|
||||
routes: Vec<Route>,
|
||||
peers: Vec<PeerInfo>,
|
||||
peer_route_pairs: Vec<PeerRoutePair>,
|
||||
running: bool,
|
||||
error_msg: Option<String>,
|
||||
}
|
||||
|
||||
struct NetworkInstance {
|
||||
config: TomlConfigLoader,
|
||||
launcher: Option<EasyTierLauncher>,
|
||||
}
|
||||
|
||||
impl NetworkInstance {
|
||||
fn new(cfg: NetworkConfig) -> Result<Self, anyhow::Error> {
|
||||
Ok(Self {
|
||||
config: cfg.gen_config()?,
|
||||
launcher: None,
|
||||
})
|
||||
}
|
||||
|
||||
fn is_easytier_running(&self) -> bool {
|
||||
self.launcher.is_some() && self.launcher.as_ref().unwrap().running()
|
||||
}
|
||||
|
||||
fn get_running_info(&self) -> Option<NetworkInstanceRunningInfo> {
|
||||
if self.launcher.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let launcher = self.launcher.as_ref().unwrap();
|
||||
|
||||
let peers = launcher.get_peers();
|
||||
let routes = launcher.get_routes();
|
||||
let peer_route_pairs = list_peer_route_pair(peers.clone(), routes.clone());
|
||||
|
||||
Some(NetworkInstanceRunningInfo {
|
||||
my_node_info: launcher.get_node_info(),
|
||||
events: launcher.get_events(),
|
||||
node_info: launcher.get_node_info(),
|
||||
routes,
|
||||
peers,
|
||||
peer_route_pairs,
|
||||
running: launcher.running(),
|
||||
error_msg: launcher.error_msg(),
|
||||
})
|
||||
}
|
||||
|
||||
fn start(&mut self) -> Result<(), anyhow::Error> {
|
||||
if self.is_easytier_running() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut launcher = EasyTierLauncher::new();
|
||||
launcher.start(|| Ok(self.config.clone()));
|
||||
|
||||
self.launcher = Some(launcher);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
static INSTANCE_MAP: once_cell::sync::Lazy<DashMap<String, NetworkInstance>> =
|
||||
once_cell::sync::Lazy::new(DashMap::new);
|
||||
|
||||
|
@ -249,7 +179,8 @@ fn run_network_instance(cfg: NetworkConfig) -> Result<(), String> {
|
|||
}
|
||||
let instance_id = cfg.instance_id.clone();
|
||||
|
||||
let mut instance = NetworkInstance::new(cfg).map_err(|e| e.to_string())?;
|
||||
let cfg = cfg.gen_config().map_err(|e| e.to_string())?;
|
||||
let mut instance = NetworkInstance::new(cfg);
|
||||
instance.start().map_err(|e| e.to_string())?;
|
||||
|
||||
println!("instance {} started", instance_id);
|
||||
|
@ -286,6 +217,11 @@ fn get_os_hostname() -> Result<String, String> {
|
|||
Ok(gethostname::gethostname().to_string_lossy().to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn set_auto_launch_status(app_handle: tauri::AppHandle, enable: bool) -> Result<bool, String> {
|
||||
Ok(init_launch(&app_handle, enable).map_err(|e| e.to_string())?)
|
||||
}
|
||||
|
||||
fn toggle_window_visibility(window: &Window) {
|
||||
if window.is_visible().unwrap() {
|
||||
window.hide().unwrap();
|
||||
|
@ -307,6 +243,65 @@ fn check_sudo() -> bool {
|
|||
is_elevated
|
||||
}
|
||||
|
||||
/// init the auto launch
|
||||
pub fn init_launch(_app_handle: &tauri::AppHandle, enable: bool) -> Result<bool, anyhow::Error> {
|
||||
let app_exe = current_exe()?;
|
||||
let app_exe = dunce::canonicalize(app_exe)?;
|
||||
let app_name = app_exe
|
||||
.file_stem()
|
||||
.and_then(|f| f.to_str())
|
||||
.ok_or(anyhow::anyhow!("failed to get file stem"))?;
|
||||
|
||||
let app_path = app_exe
|
||||
.as_os_str()
|
||||
.to_str()
|
||||
.ok_or(anyhow::anyhow!("failed to get app_path"))?
|
||||
.to_string();
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
let app_path = format!("\"{app_path}\"");
|
||||
|
||||
// use the /Applications/easytier-gui.app
|
||||
#[cfg(target_os = "macos")]
|
||||
let app_path = (|| -> Option<String> {
|
||||
let path = std::path::PathBuf::from(&app_path);
|
||||
let path = path.parent()?.parent()?.parent()?;
|
||||
let extension = path.extension()?.to_str()?;
|
||||
match extension == "app" {
|
||||
true => Some(path.as_os_str().to_str()?.to_string()),
|
||||
false => None,
|
||||
}
|
||||
})()
|
||||
.unwrap_or(app_path);
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
let app_path = {
|
||||
let appimage = _app_handle.env().appimage;
|
||||
appimage
|
||||
.and_then(|p| p.to_str().map(|s| s.to_string()))
|
||||
.unwrap_or(app_path)
|
||||
};
|
||||
|
||||
let auto = AutoLaunchBuilder::new()
|
||||
.set_app_name(app_name)
|
||||
.set_app_path(&app_path)
|
||||
.build()
|
||||
.with_context(|| "failed to build auto launch")?;
|
||||
|
||||
if enable && !auto.is_enabled().unwrap_or(false) {
|
||||
// 避免重复设置登录项
|
||||
let _ = auto.disable();
|
||||
auto.enable()
|
||||
.with_context(|| "failed to enable auto launch")?
|
||||
} else if !enable {
|
||||
let _ = auto.disable();
|
||||
}
|
||||
|
||||
let enabled = auto.is_enabled()?;
|
||||
|
||||
Ok(enabled)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if !check_sudo() {
|
||||
process::exit(0);
|
||||
|
@ -324,7 +319,8 @@ fn main() {
|
|||
run_network_instance,
|
||||
retain_network_instance,
|
||||
collect_network_infos,
|
||||
get_os_hostname
|
||||
get_os_hostname,
|
||||
set_auto_launch_status
|
||||
])
|
||||
.system_tray(SystemTray::new().with_menu(tray_menu))
|
||||
.on_system_tray_event(|app, event| match event {
|
||||
|
|
6
easytier-gui/src/auto-imports.d.ts
vendored
6
easytier-gui/src/auto-imports.d.ts
vendored
|
@ -27,6 +27,7 @@ declare global {
|
|||
const isReactive: typeof import('vue')['isReactive']
|
||||
const isReadonly: typeof import('vue')['isReadonly']
|
||||
const isRef: typeof import('vue')['isRef']
|
||||
const loadRunningInstanceIdsFromLocalStorage: typeof import('./stores/network')['loadRunningInstanceIdsFromLocalStorage']
|
||||
const mapActions: typeof import('pinia')['mapActions']
|
||||
const mapGetters: typeof import('pinia')['mapGetters']
|
||||
const mapState: typeof import('pinia')['mapState']
|
||||
|
@ -58,6 +59,7 @@ declare global {
|
|||
const retainNetworkInstance: typeof import('./composables/network')['retainNetworkInstance']
|
||||
const runNetworkInstance: typeof import('./composables/network')['runNetworkInstance']
|
||||
const setActivePinia: typeof import('pinia')['setActivePinia']
|
||||
const setAutoLaunchStatus: typeof import('./composables/network')['setAutoLaunchStatus']
|
||||
const setMapStoreSuffix: typeof import('pinia')['setMapStoreSuffix']
|
||||
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||
|
@ -116,6 +118,7 @@ declare module 'vue' {
|
|||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||
readonly loadRunningInstanceIdsFromLocalStorage: UnwrapRef<typeof import('./stores/network')['loadRunningInstanceIdsFromLocalStorage']>
|
||||
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
|
||||
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
|
||||
readonly mapState: UnwrapRef<typeof import('pinia')['mapState']>
|
||||
|
@ -147,6 +150,7 @@ declare module 'vue' {
|
|||
readonly retainNetworkInstance: UnwrapRef<typeof import('./composables/network')['retainNetworkInstance']>
|
||||
readonly runNetworkInstance: UnwrapRef<typeof import('./composables/network')['runNetworkInstance']>
|
||||
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
|
||||
readonly setAutoLaunchStatus: UnwrapRef<typeof import('./composables/network')['setAutoLaunchStatus']>
|
||||
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
|
||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||
|
@ -198,6 +202,7 @@ declare module '@vue/runtime-core' {
|
|||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||
readonly loadRunningInstanceIdsFromLocalStorage: UnwrapRef<typeof import('./stores/network')['loadRunningInstanceIdsFromLocalStorage']>
|
||||
readonly mapActions: UnwrapRef<typeof import('pinia')['mapActions']>
|
||||
readonly mapGetters: UnwrapRef<typeof import('pinia')['mapGetters']>
|
||||
readonly mapState: UnwrapRef<typeof import('pinia')['mapState']>
|
||||
|
@ -229,6 +234,7 @@ declare module '@vue/runtime-core' {
|
|||
readonly retainNetworkInstance: UnwrapRef<typeof import('./composables/network')['retainNetworkInstance']>
|
||||
readonly runNetworkInstance: UnwrapRef<typeof import('./composables/network')['runNetworkInstance']>
|
||||
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
|
||||
readonly setAutoLaunchStatus: UnwrapRef<typeof import('./composables/network')['setAutoLaunchStatus']>
|
||||
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
|
||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
|
||||
|
|
|
@ -20,3 +20,7 @@ export async function collectNetworkInfos() {
|
|||
export async function getOsHostname() {
|
||||
return await invoke<string>('get_os_hostname')
|
||||
}
|
||||
|
||||
export async function setAutoLaunchStatus(enable: boolean) {
|
||||
return await invoke<boolean>('set_auto_launch_status', { enable })
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ import 'primevue/resources/themes/aura-light-green/theme.css'
|
|||
import 'primeicons/primeicons.css'
|
||||
import 'primeflex/primeflex.css'
|
||||
import { i18n, loadLanguageAsync } from '~/modules/i18n'
|
||||
import { loadAutoLaunchStatusAsync, getAutoLaunchStatusAsync } from './modules/auto_launch'
|
||||
|
||||
if (import.meta.env.PROD) {
|
||||
document.addEventListener('keydown', (event) => {
|
||||
|
@ -28,6 +29,7 @@ if (import.meta.env.PROD) {
|
|||
|
||||
async function main() {
|
||||
await loadLanguageAsync(localStorage.getItem('lang') || 'en')
|
||||
await loadAutoLaunchStatusAsync(getAutoLaunchStatusAsync())
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
|
|
16
easytier-gui/src/modules/auto_launch.ts
Normal file
16
easytier-gui/src/modules/auto_launch.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
import { setAutoLaunchStatus } from "~/composables/network"
|
||||
|
||||
export async function loadAutoLaunchStatusAsync(enable: boolean): Promise<boolean> {
|
||||
try {
|
||||
const ret = await setAutoLaunchStatus(enable)
|
||||
localStorage.setItem('auto_launch', JSON.stringify(ret))
|
||||
return ret
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
export function getAutoLaunchStatusAsync(): boolean {
|
||||
return localStorage.getItem('auto_launch') === 'true'
|
||||
}
|
|
@ -10,6 +10,8 @@ import Status from '~/components/Status.vue'
|
|||
|
||||
import type { NetworkConfig } from '~/types/network'
|
||||
import { loadLanguageAsync } from '~/modules/i18n'
|
||||
import { getAutoLaunchStatusAsync as getAutoLaunchStatus, loadAutoLaunchStatusAsync } from '~/modules/auto_launch'
|
||||
import { loadRunningInstanceIdsFromLocalStorage } from '~/stores/network'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
const visible = ref(false)
|
||||
|
@ -63,6 +65,7 @@ function addNewNetwork() {
|
|||
|
||||
networkStore.$subscribe(async () => {
|
||||
networkStore.saveToLocalStorage()
|
||||
networkStore.saveRunningInstanceIdsToLocalStorage()
|
||||
try {
|
||||
await parseNetworkConfig(networkStore.curNetwork)
|
||||
messageBarSeverity.value = Severity.None
|
||||
|
@ -123,9 +126,16 @@ const setting_menu_items = ref([
|
|||
await loadLanguageAsync((locale.value === 'en' ? 'cn' : 'en'))
|
||||
},
|
||||
},
|
||||
{
|
||||
label: () => getAutoLaunchStatus() ? t('disable_auto_launch') : t('enable_auto_launch'),
|
||||
icon: 'pi pi-desktop',
|
||||
command: async () => {
|
||||
await loadAutoLaunchStatusAsync(!getAutoLaunchStatus())
|
||||
},
|
||||
},
|
||||
{
|
||||
label: () => t('exit'),
|
||||
icon: 'pi pi-times',
|
||||
icon: 'pi pi-power-off',
|
||||
command: async () => {
|
||||
await exit(1)
|
||||
},
|
||||
|
@ -140,6 +150,16 @@ function toggle_setting_menu(event: any) {
|
|||
|
||||
onMounted(async () => {
|
||||
networkStore.loadFromLocalStorage()
|
||||
if (getAutoLaunchStatus()) {
|
||||
let prev_running_ids = loadRunningInstanceIdsFromLocalStorage()
|
||||
for (let id of prev_running_ids) {
|
||||
let cfg = networkStore.networkList.find((item) => item.instance_id === id)
|
||||
if (cfg) {
|
||||
networkStore.addNetworkInstance(cfg.instance_id)
|
||||
await runNetworkInstance(cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
function isRunning(id: string) {
|
||||
|
@ -168,35 +188,28 @@ function isRunning(id: string) {
|
|||
<Toolbar>
|
||||
<template #start>
|
||||
<div class="flex align-items-center gap-2">
|
||||
<Button
|
||||
icon="pi pi-plus" class="mr-2" severity="primary" :label="t('add_new_network')"
|
||||
@click="addNewNetwork"
|
||||
/>
|
||||
<Button icon="pi pi-plus" class="mr-2" severity="primary" :label="t('add_new_network')"
|
||||
@click="addNewNetwork" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #center>
|
||||
<div class="min-w-80 mr-20">
|
||||
<Dropdown
|
||||
v-model="networkStore.curNetwork" :options="networkStore.networkList" :highlight-on-select="false"
|
||||
:placeholder="t('select_network')" class="w-full"
|
||||
>
|
||||
<Dropdown v-model="networkStore.curNetwork" :options="networkStore.networkList" :highlight-on-select="false"
|
||||
:placeholder="t('select_network')" class="w-full">
|
||||
<template #value="slotProps">
|
||||
<div class="flex items-start content-center">
|
||||
<div class="mr-3">
|
||||
<span>{{ slotProps.value.network_name }}</span>
|
||||
<span
|
||||
v-if="isRunning(slotProps.value.instance_id) && networkStore.instances[slotProps.value.instance_id].detail && (networkStore.instances[slotProps.value.instance_id].detail?.my_node_info.virtual_ipv4 !== '')"
|
||||
class="ml-3"
|
||||
>
|
||||
class="ml-3">
|
||||
{{ networkStore.instances[slotProps.value.instance_id].detail
|
||||
? networkStore.instances[slotProps.value.instance_id].detail?.my_node_info.virtual_ipv4 : '' }}
|
||||
</span>
|
||||
</div>
|
||||
<Tag
|
||||
class="my-auto" :severity="isRunning(slotProps.value.instance_id) ? 'success' : 'info'"
|
||||
:value="t(isRunning(slotProps.value.instance_id) ? 'network_running' : 'network_stopped')"
|
||||
/>
|
||||
<Tag class="my-auto" :severity="isRunning(slotProps.value.instance_id) ? 'success' : 'info'"
|
||||
:value="t(isRunning(slotProps.value.instance_id) ? 'network_running' : 'network_stopped')" />
|
||||
</div>
|
||||
</template>
|
||||
<template #option="slotProps">
|
||||
|
@ -205,10 +218,8 @@ function isRunning(id: string) {
|
|||
<div class="mr-3">
|
||||
{{ t('network_name') }}: {{ slotProps.option.network_name }}
|
||||
</div>
|
||||
<Tag
|
||||
class="my-auto" :severity="isRunning(slotProps.option.instance_id) ? 'success' : 'info'"
|
||||
:value="t(isRunning(slotProps.option.instance_id) ? 'network_running' : 'network_stopped')"
|
||||
/>
|
||||
<Tag class="my-auto" :severity="isRunning(slotProps.option.instance_id) ? 'success' : 'info'"
|
||||
:value="t(isRunning(slotProps.option.instance_id) ? 'network_running' : 'network_stopped')" />
|
||||
</div>
|
||||
<div>{{ slotProps.option.public_server_url }}</div>
|
||||
</div>
|
||||
|
@ -218,10 +229,8 @@ function isRunning(id: string) {
|
|||
</template>
|
||||
|
||||
<template #end>
|
||||
<Button
|
||||
icon="pi pi-cog" class="mr-2" severity="secondary" aria-haspopup="true" :label="t('settings')"
|
||||
aria-controls="overlay_setting_menu" @click="toggle_setting_menu"
|
||||
/>
|
||||
<Button icon="pi pi-cog" class="mr-2" severity="secondary" aria-haspopup="true" :label="t('settings')"
|
||||
aria-controls="overlay_setting_menu" @click="toggle_setting_menu" />
|
||||
<Menu id="overlay_setting_menu" ref="setting_menu" :model="setting_menu_items" :popup="true" />
|
||||
</template>
|
||||
</Toolbar>
|
||||
|
@ -230,10 +239,8 @@ function isRunning(id: string) {
|
|||
<Stepper class="h-full overflow-y-auto" :active-step="activeStep">
|
||||
<StepperPanel :header="t('config_network')">
|
||||
<template #content="{ nextCallback }">
|
||||
<Config
|
||||
:instance-id="networkStore.curNetworkId" :config-invalid="messageBarSeverity !== Severity.None"
|
||||
@run-network="runNetworkCb($event, nextCallback)"
|
||||
/>
|
||||
<Config :instance-id="networkStore.curNetworkId" :config-invalid="messageBarSeverity !== Severity.None"
|
||||
@run-network="runNetworkCb($event, nextCallback)" />
|
||||
</template>
|
||||
</StepperPanel>
|
||||
<StepperPanel :header="t('running')">
|
||||
|
@ -242,10 +249,8 @@ function isRunning(id: string) {
|
|||
<Status :instance-id="networkStore.curNetworkId" />
|
||||
</div>
|
||||
<div class="flex pt-4 justify-content-center">
|
||||
<Button
|
||||
:label="t('stop_network')" severity="danger" icon="pi pi-arrow-left"
|
||||
@click="stopNetworkCb(networkStore.curNetwork, prevCallback)"
|
||||
/>
|
||||
<Button :label="t('stop_network')" severity="danger" icon="pi pi-arrow-left"
|
||||
@click="stopNetworkCb(networkStore.curNetwork, prevCallback)" />
|
||||
</div>
|
||||
</template>
|
||||
</StepperPanel>
|
||||
|
|
|
@ -70,6 +70,7 @@ export const useNetworkStore = defineStore('networkStore', {
|
|||
this.instances[instanceId].error_msg = info.error_msg || ''
|
||||
this.instances[instanceId].detail = info
|
||||
}
|
||||
this.saveRunningInstanceIdsToLocalStorage()
|
||||
},
|
||||
|
||||
loadFromLocalStorage() {
|
||||
|
@ -92,8 +93,22 @@ export const useNetworkStore = defineStore('networkStore', {
|
|||
saveToLocalStorage() {
|
||||
localStorage.setItem('networkList', JSON.stringify(this.networkList))
|
||||
},
|
||||
|
||||
saveRunningInstanceIdsToLocalStorage() {
|
||||
let instance_ids = Object.keys(this.instances).filter((instanceId) => this.instances[instanceId].running)
|
||||
localStorage.setItem('runningInstanceIds', JSON.stringify(instance_ids))
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
if (import.meta.hot)
|
||||
import.meta.hot.accept(acceptHMRUpdate(useNetworkStore as any, import.meta.hot))
|
||||
|
||||
export function loadRunningInstanceIdsFromLocalStorage(): string[] {
|
||||
try {
|
||||
return JSON.parse(localStorage.getItem('runningInstanceIds') || '[]')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ auto_impl = "1.1.0"
|
|||
crossbeam = "0.8.4"
|
||||
time = "0.3"
|
||||
toml = "0.8.12"
|
||||
chrono = "0.4.35"
|
||||
chrono = { version = "0.4.37", features = ["serde"] }
|
||||
|
||||
gethostname = "0.4.3"
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ async fn run_shell_cmd(cmd: &str) -> Result<(), Error> {
|
|||
{
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
cmd_out = Command::new("cmd")
|
||||
.stdin(std::process::Stdio::null())
|
||||
.arg("/C")
|
||||
.arg(cmd)
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
|
|
|
@ -3,8 +3,7 @@ use std::{
|
|||
sync::{atomic::AtomicBool, Arc, RwLock},
|
||||
};
|
||||
|
||||
use chrono::{DateTime, Local};
|
||||
use easytier::{
|
||||
use crate::{
|
||||
common::{
|
||||
config::{ConfigLoader, TomlConfigLoader},
|
||||
global_ctx::GlobalCtxEvent,
|
||||
|
@ -16,7 +15,9 @@ use easytier::{
|
|||
cli::{PeerInfo, Route, StunInfo},
|
||||
peer::GetIpListResponse,
|
||||
},
|
||||
utils::{list_peer_route_pair, PeerRoutePair},
|
||||
};
|
||||
use chrono::{DateTime, Local};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
|
||||
|
@ -212,3 +213,68 @@ impl Drop for EasyTierLauncher {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct NetworkInstanceRunningInfo {
|
||||
pub my_node_info: MyNodeInfo,
|
||||
pub events: Vec<(DateTime<Local>, GlobalCtxEvent)>,
|
||||
pub node_info: MyNodeInfo,
|
||||
pub routes: Vec<Route>,
|
||||
pub peers: Vec<PeerInfo>,
|
||||
pub peer_route_pairs: Vec<PeerRoutePair>,
|
||||
pub running: bool,
|
||||
pub error_msg: Option<String>,
|
||||
}
|
||||
|
||||
pub struct NetworkInstance {
|
||||
config: TomlConfigLoader,
|
||||
launcher: Option<EasyTierLauncher>,
|
||||
}
|
||||
|
||||
impl NetworkInstance {
|
||||
pub fn new(config: TomlConfigLoader) -> Self {
|
||||
Self {
|
||||
config,
|
||||
launcher: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_easytier_running(&self) -> bool {
|
||||
self.launcher.is_some() && self.launcher.as_ref().unwrap().running()
|
||||
}
|
||||
|
||||
pub fn get_running_info(&self) -> Option<NetworkInstanceRunningInfo> {
|
||||
if self.launcher.is_none() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let launcher = self.launcher.as_ref().unwrap();
|
||||
|
||||
let peers = launcher.get_peers();
|
||||
let routes = launcher.get_routes();
|
||||
let peer_route_pairs = list_peer_route_pair(peers.clone(), routes.clone());
|
||||
|
||||
Some(NetworkInstanceRunningInfo {
|
||||
my_node_info: launcher.get_node_info(),
|
||||
events: launcher.get_events(),
|
||||
node_info: launcher.get_node_info(),
|
||||
routes,
|
||||
peers,
|
||||
peer_route_pairs,
|
||||
running: launcher.running(),
|
||||
error_msg: launcher.error_msg(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn start(&mut self) -> Result<(), anyhow::Error> {
|
||||
if self.is_easytier_running() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut launcher = EasyTierLauncher::new();
|
||||
launcher.start(|| Ok(self.config.clone()));
|
||||
|
||||
self.launcher = Some(launcher);
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,13 +1,15 @@
|
|||
#![allow(dead_code)]
|
||||
|
||||
pub mod arch;
|
||||
mod arch;
|
||||
mod connector;
|
||||
mod gateway;
|
||||
mod instance;
|
||||
mod peer_center;
|
||||
mod peers;
|
||||
mod vpn_portal;
|
||||
|
||||
pub mod common;
|
||||
pub mod connector;
|
||||
pub mod gateway;
|
||||
pub mod instance;
|
||||
pub mod peer_center;
|
||||
pub mod peers;
|
||||
pub mod launcher;
|
||||
pub mod rpc;
|
||||
pub mod tunnel;
|
||||
pub mod utils;
|
||||
pub mod vpn_portal;
|
||||
|
|
Loading…
Reference in New Issue
Block a user