mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-23 04:02:10 +08:00
Save the DNS override switch and session confirmation per profile so automatic protection only disables the affected profile. Preserve the legacy switch as the initial preference and show the profile scope beside the settings gear and in the DNS editor. Refs #7993
526 lines
13 KiB
TypeScript
526 lines
13 KiB
TypeScript
import { invoke } from '@tauri-apps/api/core'
|
|
import dayjs from 'dayjs'
|
|
|
|
import type { CommandFailure } from '@/services/notice-service'
|
|
import { showNotice } from '@/services/notice-service'
|
|
import type { ProxyViewV1 } from '@/types/proxy-view'
|
|
import { debugLog } from '@/utils/debug'
|
|
|
|
export async function copyClashEnv() {
|
|
return invoke<void>('copy_clash_env')
|
|
}
|
|
|
|
export async function getProfiles() {
|
|
return invoke<IProfilesConfig>('get_profiles')
|
|
}
|
|
|
|
export async function enhanceProfiles() {
|
|
return (
|
|
(await invoke<ValidationOutcome>('enhance_profiles')).status === 'valid'
|
|
)
|
|
}
|
|
|
|
export async function patchProfilesConfig(profiles: IProfilesConfig) {
|
|
return invoke<ValidationOutcome>('patch_profiles_config', { profiles })
|
|
}
|
|
|
|
export async function createProfile(
|
|
item: Partial<IProfileItem>,
|
|
fileData?: string | null,
|
|
) {
|
|
return invoke<void>('create_profile', { item, fileData })
|
|
}
|
|
|
|
export async function viewProfile(index: string) {
|
|
return invoke<void>('view_profile', { index })
|
|
}
|
|
|
|
export async function readProfileFile(index: string) {
|
|
return invoke<string>('read_profile_file', { index })
|
|
}
|
|
|
|
export async function saveProfileFile(index: string, fileData: string) {
|
|
return (
|
|
(
|
|
await invoke<ValidationOutcome>('save_profile_file', {
|
|
index,
|
|
fileData,
|
|
})
|
|
).status === 'valid'
|
|
)
|
|
}
|
|
|
|
export async function importProfile(url: string, option?: IProfileOption) {
|
|
return invoke<void>('import_profile', {
|
|
url,
|
|
option: option || { with_proxy: true },
|
|
})
|
|
}
|
|
|
|
export async function reorderProfile(activeId: string, overId: string) {
|
|
return invoke<void>('reorder_profile', {
|
|
activeId,
|
|
overId,
|
|
})
|
|
}
|
|
|
|
export async function updateProfile(index: string, option?: IProfileOption) {
|
|
return invoke<void>('update_profile', { index, option })
|
|
}
|
|
|
|
export async function deleteProfile(index: string) {
|
|
return invoke<void>('delete_profile', { index })
|
|
}
|
|
|
|
export async function patchProfile(
|
|
index: string,
|
|
profile: Partial<IProfileItem>,
|
|
) {
|
|
return invoke<void>('patch_profile', { index, profile })
|
|
}
|
|
|
|
export async function getClashInfo() {
|
|
return invoke<IClashInfo | null>('get_clash_info')
|
|
}
|
|
|
|
// Fallback mode read independent of strict mihomo `/configs` deserialization.
|
|
export async function getClashMode() {
|
|
return invoke<string | null>('get_clash_mode')
|
|
}
|
|
|
|
export async function getRuntimeConfig() {
|
|
return invoke<IConfigData | null>('get_runtime_config')
|
|
}
|
|
|
|
export async function getRuntimeYaml() {
|
|
return invoke<string | null>('get_runtime_yaml')
|
|
}
|
|
|
|
export async function getRuntimeLogs() {
|
|
return invoke<Record<string, [string, string][]>>('get_runtime_logs')
|
|
}
|
|
|
|
export async function getRuntimeProxyChainConfig(proxyChainExitNode: string) {
|
|
return invoke<string>('get_runtime_proxy_chain_config', {
|
|
proxyChainExitNode,
|
|
})
|
|
}
|
|
|
|
export async function updateProxyChainConfigInRuntime(proxyChainConfig: any) {
|
|
return invoke<void>('update_proxy_chain_config_in_runtime', {
|
|
proxyChainConfig,
|
|
})
|
|
}
|
|
|
|
export async function patchClashConfig(payload: Partial<IConfigData>) {
|
|
return invoke<void>('patch_clash_config', { payload })
|
|
}
|
|
|
|
export async function patchClashMode(payload: string) {
|
|
return invoke<void>('patch_clash_mode', { payload })
|
|
}
|
|
|
|
export async function syncTrayProxySelection() {
|
|
return invoke<void>('sync_tray_proxy_selection')
|
|
}
|
|
|
|
/** Sends one selection pair so the backend can merge against current profile state. */
|
|
export async function recordSelectedNode(groupName: string, node: string) {
|
|
return invoke<void>('record_selected_node', { groupName, node })
|
|
}
|
|
|
|
export async function forgetSelectedNode(groupName: string) {
|
|
return invoke<void>('forget_selected_node', { groupName })
|
|
}
|
|
|
|
export async function getProxyView(): Promise<ProxyViewV1> {
|
|
const view = await invoke<ProxyViewV1>('get_proxy_view')
|
|
if (view.schemaVersion !== 1) {
|
|
throw new Error('Unsupported proxy view schema: ' + view.schemaVersion)
|
|
}
|
|
return view
|
|
}
|
|
|
|
export async function getClashLogs() {
|
|
const regex = /time="(.+?)"\s+level=(.+?)\s+msg="(.+?)"/
|
|
const newRegex = /(.+?)\s+(.+?)\s+(.+)/
|
|
const logs = await invoke<string[]>('get_clash_logs')
|
|
|
|
return logs.reduce<ILogItem[]>((acc, log) => {
|
|
const result = log.match(regex)
|
|
if (result) {
|
|
const [_, _time, type, payload] = result
|
|
const time = dayjs(_time).format('MM-DD HH:mm:ss')
|
|
acc.push({ time, type, payload })
|
|
return acc
|
|
}
|
|
|
|
const result2 = log.match(newRegex)
|
|
if (result2) {
|
|
const [_, time, type, payload] = result2
|
|
acc.push({ time, type, payload })
|
|
}
|
|
return acc
|
|
}, [])
|
|
}
|
|
|
|
export async function getVergeConfig() {
|
|
return invoke<IVergeConfig>('get_verge_config')
|
|
}
|
|
|
|
export async function patchVergeConfig(payload: IVergeConfig) {
|
|
return invoke<void>('patch_verge_config', { payload })
|
|
}
|
|
|
|
export async function setDnsOverride(
|
|
profileUid: string,
|
|
enabled: boolean,
|
|
confirmation?: string,
|
|
) {
|
|
return invoke<
|
|
{ status: 'applied' } | { status: 'confirmation_required'; source: string }
|
|
>('set_dns_override', { profileUid, enabled, confirmation })
|
|
}
|
|
|
|
export async function takeDnsOverrideNotice() {
|
|
return invoke<boolean>('take_dns_override_notice')
|
|
}
|
|
|
|
export async function takeServiceFallbackNotice() {
|
|
return invoke<boolean>('take_service_fallback_notice')
|
|
}
|
|
|
|
export async function takeServiceRepairNotice() {
|
|
return invoke<boolean>('take_service_repair_notice')
|
|
}
|
|
|
|
export async function takeDiscardedKeysNotice() {
|
|
return invoke<string | null>('take_discarded_keys_notice')
|
|
}
|
|
|
|
export async function getSystemProxy() {
|
|
return invoke<{
|
|
enable: boolean
|
|
server: string
|
|
bypass: string
|
|
}>('get_sys_proxy')
|
|
}
|
|
|
|
export async function getAutotemProxy() {
|
|
try {
|
|
debugLog('[API] 开始调用 get_auto_proxy')
|
|
const result = await invoke<{
|
|
enable: boolean
|
|
url: string
|
|
}>('get_auto_proxy')
|
|
debugLog('[API] get_auto_proxy 调用成功:', result)
|
|
return result
|
|
} catch (error) {
|
|
console.error('[API] get_auto_proxy 调用失败:', error)
|
|
return {
|
|
enable: false,
|
|
url: '',
|
|
}
|
|
}
|
|
}
|
|
|
|
export async function getEmbeddedServerPort() {
|
|
return invoke<number>('get_embedded_server_port')
|
|
}
|
|
|
|
export async function changeClashCore(clashCore: string) {
|
|
return invoke<CommandFailure | null>('change_clash_core', { clashCore })
|
|
}
|
|
|
|
export async function restartCore() {
|
|
return invoke<void>('restart_core')
|
|
}
|
|
|
|
export interface CoreUpgradeReport {
|
|
/** False when the managed core was already at the latest version. */
|
|
upgraded: boolean
|
|
from: string
|
|
to: string
|
|
}
|
|
|
|
export async function upgradeClashCore(force = false) {
|
|
return invoke<CoreUpgradeReport>('upgrade_clash_core', { force })
|
|
}
|
|
|
|
export async function restartApp() {
|
|
return invoke<void>('restart_app')
|
|
}
|
|
|
|
export async function getAppDir() {
|
|
return invoke<string>('get_app_dir')
|
|
}
|
|
|
|
export async function openAppDir() {
|
|
return invoke<void>('open_app_dir').catch((err) => showNotice.error(err))
|
|
}
|
|
|
|
export async function openCoreDir() {
|
|
return invoke<void>('open_core_dir').catch((err) => showNotice.error(err))
|
|
}
|
|
|
|
export async function openLogsDir() {
|
|
return invoke<void>('open_logs_dir').catch((err) => showNotice.error(err))
|
|
}
|
|
|
|
export async function syncRuntimeProviders() {
|
|
return invoke<void>('sync_runtime_providers').catch((err) => {
|
|
console.warn('failed to queue the provider cache sync', err)
|
|
})
|
|
}
|
|
|
|
export async function cmdTestDelay(url: string) {
|
|
return invoke<number>('test_delay', { url })
|
|
}
|
|
|
|
export async function invoke_uwp_tool() {
|
|
return invoke<void>('invoke_uwp_tool').catch((err) =>
|
|
showNotice.error(err, 1500),
|
|
)
|
|
}
|
|
|
|
export async function openDevTools() {
|
|
return invoke('open_devtools')
|
|
}
|
|
|
|
export async function exitApp() {
|
|
return invoke('exit_app')
|
|
}
|
|
|
|
export async function exportDiagnosticInfo() {
|
|
return invoke('export_diagnostic_info')
|
|
}
|
|
|
|
interface SystemInfo {
|
|
system_name: string
|
|
system_version: string
|
|
system_kernel_version: string
|
|
system_arch: string
|
|
app_version: string
|
|
app_core_mode: string
|
|
app_is_admin: boolean
|
|
}
|
|
|
|
export async function getSystemInfo() {
|
|
return invoke<SystemInfo>('get_system_info')
|
|
}
|
|
|
|
export async function copyIconFile(
|
|
path: string,
|
|
name: 'common' | 'sysproxy' | 'tun',
|
|
) {
|
|
const key = `icon_${name}_update_time`
|
|
const previousTime = localStorage.getItem(key) || ''
|
|
|
|
const currentTime = String(Date.now())
|
|
localStorage.setItem(key, currentTime)
|
|
|
|
const iconInfo = {
|
|
name,
|
|
previous_t: previousTime,
|
|
current_t: currentTime,
|
|
}
|
|
|
|
return invoke<void>('copy_icon_file', { path, iconInfo })
|
|
}
|
|
|
|
export async function downloadIconCache(url: string, name: string) {
|
|
return invoke<string>('download_icon_cache', { url, name })
|
|
}
|
|
|
|
export async function getNetworkInterfaces() {
|
|
return invoke<string[]>('get_network_interfaces')
|
|
}
|
|
|
|
export async function getSystemHostname() {
|
|
return invoke<string>('get_system_hostname')
|
|
}
|
|
|
|
export async function getNetworkInterfacesInfo() {
|
|
return invoke<INetworkInterface[]>('get_network_interfaces_info')
|
|
}
|
|
|
|
export async function createWebdavBackup() {
|
|
return invoke<void>('create_webdav_backup')
|
|
}
|
|
|
|
export async function createLocalBackup() {
|
|
return invoke<void>('create_local_backup')
|
|
}
|
|
|
|
export async function deleteWebdavBackup(filename: string) {
|
|
return invoke<void>('delete_webdav_backup', { filename })
|
|
}
|
|
|
|
export async function deleteLocalBackup(filename: string) {
|
|
return invoke<void>('delete_local_backup', { filename })
|
|
}
|
|
|
|
export async function restoreWebDavBackup(filename: string) {
|
|
return invoke<void>('restore_webdav_backup', { filename })
|
|
}
|
|
|
|
export async function restoreLocalBackup(filename: string) {
|
|
return invoke<void>('restore_local_backup', { filename })
|
|
}
|
|
|
|
export async function importLocalBackup(source: string) {
|
|
return invoke<string>('import_local_backup', { source })
|
|
}
|
|
|
|
export async function exportLocalBackup(filename: string, destination: string) {
|
|
return invoke<void>('export_local_backup', { filename, destination })
|
|
}
|
|
|
|
export async function saveWebdavConfig(
|
|
url: string,
|
|
username: string,
|
|
password: string,
|
|
) {
|
|
return invoke<void>('save_webdav_config', {
|
|
url,
|
|
username,
|
|
password,
|
|
})
|
|
}
|
|
|
|
export async function listWebDavBackup() {
|
|
const list: IWebDavFile[] = await invoke<IWebDavFile[]>('list_webdav_backup')
|
|
list.forEach((item) => {
|
|
item.filename = item.href.split('/').pop() as string
|
|
})
|
|
return list
|
|
}
|
|
|
|
export async function listLocalBackup() {
|
|
return invoke<ILocalBackupFile[]>('list_local_backup')
|
|
}
|
|
|
|
export type RunningMode = 'Service' | 'Sidecar' | 'NotRunning'
|
|
|
|
type ServiceHealth =
|
|
| 'unknown'
|
|
| 'ready'
|
|
| 'notInstalled'
|
|
| 'versionMismatch'
|
|
| 'unavailable'
|
|
|
|
type PendingServiceAction =
|
|
| 'install'
|
|
| 'uninstall'
|
|
| 'reinstall'
|
|
| 'forceReinstall'
|
|
|
|
/** Consistent core/service snapshot with backend-derived availability flags. */
|
|
export interface RunState {
|
|
mode: RunningMode
|
|
service: ServiceHealth
|
|
serviceUnavailableReason: string | null
|
|
pendingAction: PendingServiceAction | null
|
|
sidecarAllowed: boolean
|
|
isAdmin: boolean
|
|
opInFlight: boolean
|
|
serviceUsable: boolean
|
|
tunCapable: boolean
|
|
serviceNeedsAttention: boolean
|
|
}
|
|
|
|
export const getRuntimeState = async () => {
|
|
return invoke<RunState>('get_runtime_state')
|
|
}
|
|
|
|
export type FailedOperation =
|
|
| 'systemProxyEnable'
|
|
| 'systemProxyDisable'
|
|
| 'systemProxyRestore'
|
|
| 'systemProxyGuard'
|
|
|
|
export interface PendingFailure {
|
|
code: string
|
|
detail: string
|
|
operation: FailedOperation
|
|
sequence: number
|
|
}
|
|
|
|
export const getPendingFailures = async () => {
|
|
return invoke<PendingFailure[]>('get_pending_failures')
|
|
}
|
|
|
|
export const getAppUptime = async () => {
|
|
return invoke<number>('get_app_uptime')
|
|
}
|
|
|
|
export const installService = async () => {
|
|
return invoke<void>('install_service')
|
|
}
|
|
|
|
export const uninstallService = async () => {
|
|
return invoke<void>('uninstall_service')
|
|
}
|
|
|
|
export const reinstallService = async () => {
|
|
return invoke<void>('reinstall_service')
|
|
}
|
|
|
|
export const repairService = async () => {
|
|
return invoke<void>('repair_service')
|
|
}
|
|
|
|
export const continueWithSidecar = async () => {
|
|
return invoke<void>('continue_with_sidecar')
|
|
}
|
|
|
|
export const entry_lightweight_mode = async () => {
|
|
return invoke<void>('entry_lightweight_mode')
|
|
}
|
|
|
|
export async function getNextUpdateTime(uid: string) {
|
|
return invoke<number | null>('get_next_update_time', { uid })
|
|
}
|
|
|
|
interface ToggleableProxyPort {
|
|
enabled: boolean
|
|
port: number
|
|
}
|
|
|
|
export interface ProxyPortSettings {
|
|
mixedPort: number
|
|
socks: ToggleableProxyPort
|
|
http: ToggleableProxyPort
|
|
redir: ToggleableProxyPort
|
|
tproxy: ToggleableProxyPort
|
|
}
|
|
|
|
export type ListenerTransport = 'tcp' | 'udp'
|
|
|
|
export interface ListenerProbe {
|
|
address: string
|
|
transports: ListenerTransport[]
|
|
}
|
|
|
|
export type ListenerProbeOutcome =
|
|
| { status: 'available' }
|
|
| {
|
|
status: 'conflict'
|
|
port: number
|
|
transport: ListenerTransport
|
|
}
|
|
| { status: 'invalid'; message: string }
|
|
| { status: 'indeterminate'; message: string }
|
|
|
|
export type SaveProxyPortsOutcome =
|
|
| { status: 'saved' }
|
|
| { status: 'conflict'; port: number; transport: ListenerTransport }
|
|
|
|
export const probeListener = async (request: ListenerProbe) => {
|
|
return invoke<ListenerProbeOutcome>('probe_listener', { request })
|
|
}
|
|
|
|
export const saveProxyPorts = async (settings: ProxyPortSettings) => {
|
|
return invoke<SaveProxyPortsOutcome>('save_proxy_ports', { settings })
|
|
}
|