mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-10-15 00:56:15 -08:00
Use admitted native members to configure VpnService and attach its single fd to the core shared TUN. Wait for attachment completion and record the actual attached members before refreshing VPN state. Report rejected members in the GUI and preserve the VPN for unchanged healthy members. Reconcile ordinary configuration notifications without invalidating an arbitrary member. Invalidate attachments on instance lifecycle changes, including restarts that reuse an instance ID. Forward public IPv6 changes alongside proxy route changes to refresh the platform VPN configuration. Exclude confirmed IPv4-less members from readiness gating while retaining retries for unavailable status and pending addresses. Quick Settings stops all running TUN members and restores that group on the next start, skipping removed configurations and no_tun networks. Keep the FFI set_tun_fd call scoped to one instance and report attachment errors. Cover mobile configuration, member rejection and replacement, attachment completion, collection notifications, IPv4-less members, and tile stop/start restoration in GUI regression tests. Validate with 36 mobile VPN tests, vue-tsc --noEmit, Android GUI cargo check, workspace clippy, and shared TUN network namespace tests.
126 lines
4.4 KiB
TypeScript
126 lines
4.4 KiB
TypeScript
import { Event, listen } from "@tauri-apps/api/event";
|
|
import { type } from "@tauri-apps/plugin-os";
|
|
import { NetworkTypes } from "easytier-frontend-lib"
|
|
import { Utils } from "easytier-frontend-lib";
|
|
import { normalizeConfigSource } from './config_source'
|
|
import { onNetworkInstancesChange, onNetworkInstanceUpdate } from './mobile_vpn'
|
|
|
|
interface StoredGuiConfig {
|
|
config: NetworkTypes.NetworkConfig
|
|
source?: unknown
|
|
}
|
|
|
|
const EVENTS = Object.freeze({
|
|
SAVE_CONFIGS: 'save_configs',
|
|
PRE_RUN_NETWORK_INSTANCE: 'pre_run_network_instance',
|
|
POST_RUN_NETWORK_INSTANCE: 'post_run_network_instance',
|
|
VPN_SERVICE_STOP: 'vpn_service_stop',
|
|
VPN_SERVICE_CONFIG_CHANGED: 'vpn_service_config_changed',
|
|
DHCP_IP_CHANGED: 'dhcp_ip_changed',
|
|
PROXY_CIDRS_UPDATED: 'proxy_cidrs_updated',
|
|
EVENT_LAGGED: 'event_lagged',
|
|
});
|
|
|
|
function onSaveConfigs(event: Event<StoredGuiConfig[]>) {
|
|
console.log(`Received event '${EVENTS.SAVE_CONFIGS}': ${event.payload}`);
|
|
localStorage.setItem(
|
|
'networkList',
|
|
JSON.stringify(event.payload.map(({ config, source }) => ({
|
|
config: NetworkTypes.normalizeNetworkConfig(config),
|
|
source: normalizeConfigSource(source),
|
|
}))),
|
|
);
|
|
}
|
|
|
|
function normalizeInstanceIdPayload(payload: unknown): string {
|
|
if (typeof payload === 'string') {
|
|
return payload
|
|
}
|
|
|
|
if (payload && typeof payload === 'object') {
|
|
const uuid = payload as Partial<Utils.UUID>
|
|
if (
|
|
typeof uuid.part1 === 'number'
|
|
&& typeof uuid.part2 === 'number'
|
|
&& typeof uuid.part3 === 'number'
|
|
&& typeof uuid.part4 === 'number'
|
|
) {
|
|
return Utils.UuidToStr(uuid as Utils.UUID)
|
|
}
|
|
}
|
|
|
|
if (payload == null) {
|
|
return ''
|
|
}
|
|
|
|
const fallback = String(payload)
|
|
return fallback === '[object Object]' ? '' : fallback
|
|
}
|
|
|
|
async function onPreRunNetworkInstance(event: Event<unknown>) {
|
|
const instanceId = normalizeInstanceIdPayload(event.payload)
|
|
console.log(`Received event '${EVENTS.PRE_RUN_NETWORK_INSTANCE}', raw payload:`, event.payload, 'normalized:', instanceId)
|
|
if (type() === 'android') {
|
|
await prepareVpnService(instanceId);
|
|
}
|
|
}
|
|
|
|
async function onPostRunNetworkInstance(event: Event<unknown>) {
|
|
const instanceId = normalizeInstanceIdPayload(event.payload)
|
|
console.log(`Received event '${EVENTS.POST_RUN_NETWORK_INSTANCE}', raw payload:`, event.payload, 'normalized:', instanceId)
|
|
if (type() === 'android') {
|
|
await onNetworkInstanceChange(instanceId);
|
|
}
|
|
}
|
|
|
|
async function onVpnServiceStop(event: Event<unknown>) {
|
|
console.log(`Received event '${EVENTS.VPN_SERVICE_STOP}', raw payload:`, event.payload)
|
|
await syncMobileVpnService();
|
|
}
|
|
|
|
async function onVpnServiceConfigChanged(event: Event<unknown>) {
|
|
console.log(`Received event '${EVENTS.VPN_SERVICE_CONFIG_CHANGED}'`, event.payload)
|
|
if (type() === 'android') {
|
|
await onNetworkInstancesChange();
|
|
}
|
|
}
|
|
|
|
async function onDhcpIpChanged(event: Event<unknown>) {
|
|
const instanceId = normalizeInstanceIdPayload(event.payload)
|
|
console.log(`Received event '${EVENTS.DHCP_IP_CHANGED}' for instance: ${instanceId}`);
|
|
if (type() === 'android') {
|
|
await onNetworkInstanceUpdate(instanceId);
|
|
}
|
|
}
|
|
|
|
async function onProxyCidrsUpdated(event: Event<unknown>) {
|
|
const instanceId = normalizeInstanceIdPayload(event.payload)
|
|
console.log(`Received event '${EVENTS.PROXY_CIDRS_UPDATED}' for instance: ${instanceId}`);
|
|
if (type() === 'android') {
|
|
await onNetworkInstanceUpdate(instanceId);
|
|
}
|
|
}
|
|
|
|
async function onEventLagged(event: Event<unknown>) {
|
|
if (type() === 'android') {
|
|
await onNetworkInstanceUpdate(normalizeInstanceIdPayload(event.payload));
|
|
}
|
|
}
|
|
|
|
export async function listenGlobalEvents() {
|
|
const unlisteners = [
|
|
await listen(EVENTS.SAVE_CONFIGS, onSaveConfigs),
|
|
await listen(EVENTS.PRE_RUN_NETWORK_INSTANCE, onPreRunNetworkInstance),
|
|
await listen(EVENTS.POST_RUN_NETWORK_INSTANCE, onPostRunNetworkInstance),
|
|
await listen(EVENTS.VPN_SERVICE_STOP, onVpnServiceStop),
|
|
await listen(EVENTS.VPN_SERVICE_CONFIG_CHANGED, onVpnServiceConfigChanged),
|
|
await listen(EVENTS.DHCP_IP_CHANGED, onDhcpIpChanged),
|
|
await listen(EVENTS.PROXY_CIDRS_UPDATED, onProxyCidrsUpdated),
|
|
await listen(EVENTS.EVENT_LAGGED, onEventLagged),
|
|
];
|
|
|
|
return () => {
|
|
unlisteners.forEach(unlisten => unlisten());
|
|
};
|
|
}
|