Files
clash-verge-rev/src/services/cmds.ts
T

592 lines
15 KiB
TypeScript

import { Channel, 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 getDnsConfigContent() {
return invoke<string | null>('get_dns_config_content')
}
export async function saveDnsConfig(dnsConfig: Record<string, unknown>) {
return invoke<void>('save_dns_config', { dnsConfig })
}
export async function validateDnsConfig() {
return invoke<ValidationOutcome>('validate_dns_config')
}
export async function applyDnsConfig(apply: boolean) {
return invoke<void>('apply_dns_config', { apply })
}
export async function takeDnsOverrideNotice() {
return invoke<boolean>('take_dns_override_notice')
}
export type ServiceFallbackNotice =
| { kind: 'unavailable' }
| { kind: 'coreRejected'; reason: string }
| { kind: 'notAutoStarted' }
export async function takeServiceFallbackNotice() {
return invoke<ServiceFallbackNotice | null>('take_service_fallback_notice')
}
export interface CoreFailure {
kind: 'startFailed' | 'serviceCoreStopped'
detail: string
}
export async function getCoreStartupError() {
return invoke<CoreFailure | null>('get_core_startup_error')
}
export async function takeServiceRepairNotice() {
return invoke<boolean>('take_service_repair_notice')
}
export async function takeServiceOwnerNotice() {
return invoke<string | null>('take_service_owner_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 interface SidecarFailureSnapshot {
revision: number
detail: string | null
}
export const getSidecarFailure = async () => {
return invoke<SidecarFailureSnapshot>('get_sidecar_failure')
}
export const getAppUptime = async () => {
return invoke<number>('get_app_uptime')
}
export type ServiceInstallOutcome =
| { status: 'installed' }
| { status: 'sidecar'; reason: string }
export const installService = async () => {
return invoke<ServiceInstallOutcome>('install_service')
}
export const uninstallService = async () => {
return invoke<void>('uninstall_service')
}
export const reinstallService = async () => {
return invoke<ServiceInstallOutcome>('reinstall_service')
}
export const repairService = async () => {
return invoke<ServiceInstallOutcome>('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 })
}
export interface UnlockItem {
name: string
status: string
region?: string | null
check_time?: string | null
}
export async function getUnlockItems() {
return invoke<UnlockItem[]>('get_unlock_items')
}
export async function checkMediaUnlock(onComplete: Channel<UnlockItem>) {
return invoke<UnlockItem[]>('check_media_unlock', { onComplete })
}
export async function checkMediaUnlockItem(name: string) {
return invoke<UnlockItem>('check_media_unlock_item', { name })
}