support start on reboot (#132)

* move launcher to eastier lib
* support auto start after reboot
This commit is contained in:
Sijie.Sun 2024-06-04 23:06:10 +08:00 committed by GitHub
parent f9e6264f31
commit 6e77e6b5e7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 281 additions and 118 deletions

42
Cargo.lock generated
View File

@ -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"

View File

@ -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}'

View File

@ -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'

View File

@ -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"]

View File

@ -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 {

View File

@ -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']>

View File

@ -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 })
}

View File

@ -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)

View 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'
}

View File

@ -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>

View File

@ -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 []
}
}

View File

@ -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"

View File

@ -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)

View File

@ -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(())
}
}

View File

@ -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;