import axios, { AxiosInstance } from 'axios' import { getAppConfig, getControledMihomoConfig } from '../config' import { mainWindow } from '..' import WebSocket from 'ws' import { tray } from '../resolve/tray' import { calcTraffic } from '../utils/calc' let axiosIns: AxiosInstance = null! let mihomoTrafficWs: WebSocket | null = null let trafficRetry = 10 let mihomoMemoryWs: WebSocket | null = null let memoryRetry = 10 let mihomoLogsWs: WebSocket | null = null let logsRetry = 10 let mihomoConnectionsWs: WebSocket | null = null let connectionsRetry = 10 let trafficHopping = false export const getAxios = async (force: boolean = false): Promise => { if (axiosIns && !force) return axiosIns const controledMihomoConfig = await getControledMihomoConfig() let server = controledMihomoConfig['external-controller'] const secret = controledMihomoConfig.secret ?? '' if (server?.startsWith(':')) server = `127.0.0.1${server}` axiosIns = axios.create({ baseURL: `http://${server}`, proxy: false, headers: secret ? { Authorization: `Bearer ${secret}` } : {}, timeout: 15000 }) axiosIns.interceptors.response.use( (response) => { return response.data }, (error) => { if (error.response && error.response.data) { return Promise.reject(error.response.data) } return Promise.reject(error) } ) return axiosIns } export async function mihomoVersion(): Promise { const instance = await getAxios() return await instance.get('/version') } export const patchMihomoConfig = async (patch: Partial): Promise => { const instance = await getAxios() return await instance.patch('/configs', patch) } export const mihomoCloseConnection = async (id: string): Promise => { const instance = await getAxios() return await instance.delete(`/connections/${encodeURIComponent(id)}`) } export const mihomoCloseAllConnections = async (): Promise => { const instance = await getAxios() return await instance.delete('/connections') } export const mihomoRules = async (): Promise => { const instance = await getAxios() return await instance.get('/rules') } export const mihomoProxies = async (): Promise => { const instance = await getAxios() return await instance.get('/proxies') } export const mihomoProxyProviders = async (): Promise => { const instance = await getAxios() return await instance.get('/providers/proxies') } export const mihomoUpdateProxyProviders = async (name: string): Promise => { const instance = await getAxios() return await instance.put(`/providers/proxies/${encodeURIComponent(name)}`) } export const mihomoRuleProviders = async (): Promise => { const instance = await getAxios() return await instance.get('/providers/rules') } export const mihomoUpdateRuleProviders = async (name: string): Promise => { const instance = await getAxios() return await instance.put(`/providers/rules/${encodeURIComponent(name)}`) } export const mihomoChangeProxy = async (group: string, proxy: string): Promise => { const instance = await getAxios() return await instance.put(`/proxies/${encodeURIComponent(group)}`, { name: proxy }) } export const mihomoUpgradeGeo = async (): Promise => { const instance = await getAxios() return await instance.post('/configs/geo') } export const mihomoProxyDelay = async (proxy: string, url?: string): Promise => { const appConfig = await getAppConfig() const { delayTestUrl, delayTestTimeout } = appConfig const instance = await getAxios() return await instance.get(`/proxies/${encodeURIComponent(proxy)}/delay`, { params: { url: url || delayTestUrl || 'https://www.gstatic.com/generate_204', timeout: delayTestTimeout || 5000 } }) } export const mihomoGroupDelay = async (group: string, url?: string): Promise => { const appConfig = await getAppConfig() const { delayTestUrl, delayTestTimeout } = appConfig const instance = await getAxios() return await instance.get(`/group/${encodeURIComponent(group)}/delay`, { params: { url: url || delayTestUrl || 'https://www.gstatic.com/generate_204', timeout: delayTestTimeout || 5000 } }) } export const startMihomoTraffic = async (): Promise => { await mihomoTraffic() } export const stopMihomoTraffic = (): void => { if (mihomoTrafficWs) { mihomoTrafficWs.removeAllListeners() if (mihomoTrafficWs.readyState === WebSocket.OPEN) { mihomoTrafficWs.close() } mihomoTrafficWs = null } } const mihomoTraffic = async (): Promise => { const controledMihomoConfig = await getControledMihomoConfig() let server = controledMihomoConfig['external-controller'] const secret = controledMihomoConfig.secret ?? '' if (server?.startsWith(':')) server = `127.0.0.1${server}` stopMihomoTraffic() mihomoTrafficWs = new WebSocket(`ws://${server}/traffic?token=${encodeURIComponent(secret)}`) mihomoTrafficWs.onmessage = (e): void => { const data = e.data as string const json = JSON.parse(data) as IMihomoTrafficInfo if (trafficHopping) { tray?.setTitle('↑' + `${calcTraffic(json.up)}/s`.padStart(12), { fontType: 'monospaced' }) } else { tray?.setTitle('↓' + `${calcTraffic(json.down)}/s`.padStart(12), { fontType: 'monospaced' }) } trafficHopping = !trafficHopping tray?.setToolTip( '↑' + `${calcTraffic(json.up)}/s`.padStart(12) + '\n↓' + `${calcTraffic(json.down)}/s`.padStart(12) ) trafficRetry = 10 mainWindow?.webContents.send('mihomoTraffic', json) } mihomoTrafficWs.onclose = (): void => { if (trafficRetry) { trafficRetry-- mihomoTraffic() } } mihomoTrafficWs.onerror = (): void => { if (mihomoTrafficWs) { mihomoTrafficWs.close() mihomoTrafficWs = null } } } export const startMihomoMemory = async (): Promise => { await mihomoMemory() } export const stopMihomoMemory = (): void => { if (mihomoMemoryWs) { mihomoMemoryWs.removeAllListeners() if (mihomoMemoryWs.readyState === WebSocket.OPEN) { mihomoMemoryWs.close() } mihomoMemoryWs = null } } const mihomoMemory = async (): Promise => { const controledMihomoConfig = await getControledMihomoConfig() let server = controledMihomoConfig['external-controller'] const secret = controledMihomoConfig.secret ?? '' if (server?.startsWith(':')) server = `127.0.0.1${server}` stopMihomoMemory() mihomoMemoryWs = new WebSocket(`ws://${server}/memory?token=${encodeURIComponent(secret)}`) mihomoMemoryWs.onmessage = (e): void => { const data = e.data as string memoryRetry = 10 mainWindow?.webContents.send('mihomoMemory', JSON.parse(data) as IMihomoMemoryInfo) } mihomoMemoryWs.onclose = (): void => { if (memoryRetry) { memoryRetry-- mihomoMemory() } } mihomoMemoryWs.onerror = (): void => { if (mihomoMemoryWs) { mihomoMemoryWs.close() mihomoMemoryWs = null } } } export const startMihomoLogs = async (): Promise => { await mihomoLogs() } export const stopMihomoLogs = (): void => { if (mihomoLogsWs) { mihomoLogsWs.removeAllListeners() if (mihomoLogsWs.readyState === WebSocket.OPEN) { mihomoLogsWs.close() } mihomoLogsWs = null } } const mihomoLogs = async (): Promise => { const controledMihomoConfig = await getControledMihomoConfig() const { secret = '', 'log-level': level = 'info' } = controledMihomoConfig let { 'external-controller': server } = controledMihomoConfig if (server?.startsWith(':')) server = `127.0.0.1${server}` stopMihomoLogs() mihomoLogsWs = new WebSocket( `ws://${server}/logs?token=${encodeURIComponent(secret)}&level=${level}` ) mihomoLogsWs.onmessage = (e): void => { const data = e.data as string logsRetry = 10 mainWindow?.webContents.send('mihomoLogs', JSON.parse(data) as IMihomoLogInfo) } mihomoLogsWs.onclose = (): void => { if (logsRetry) { logsRetry-- mihomoLogs() } } mihomoLogsWs.onerror = (): void => { if (mihomoLogsWs) { mihomoLogsWs.close() mihomoLogsWs = null } } } export const startMihomoConnections = async (): Promise => { await mihomoConnections() } export const stopMihomoConnections = (): void => { if (mihomoConnectionsWs) { mihomoConnectionsWs.removeAllListeners() if (mihomoConnectionsWs.readyState === WebSocket.OPEN) { mihomoConnectionsWs.close() } mihomoConnectionsWs = null } } const mihomoConnections = async (): Promise => { const controledMihomoConfig = await getControledMihomoConfig() let server = controledMihomoConfig['external-controller'] const secret = controledMihomoConfig.secret ?? '' if (server?.startsWith(':')) server = `127.0.0.1${server}` stopMihomoConnections() mihomoConnectionsWs = new WebSocket( `ws://${server}/connections?token=${encodeURIComponent(secret)}` ) mihomoConnectionsWs.onmessage = (e): void => { const data = e.data as string connectionsRetry = 10 mainWindow?.webContents.send('mihomoConnections', JSON.parse(data) as IMihomoConnectionsInfo) } mihomoConnectionsWs.onclose = (): void => { if (connectionsRetry) { connectionsRetry-- mihomoConnections() } } mihomoConnectionsWs.onerror = (): void => { if (mihomoConnectionsWs) { mihomoConnectionsWs.close() mihomoConnectionsWs = null } } } export const pauseWebsockets = () => { const recoverList: (() => void)[] = [] // Traffic 始终开启 stopMihomoTraffic() recoverList.push(startMihomoTraffic) if (mihomoMemoryWs) { stopMihomoMemory() recoverList.push(startMihomoMemory) } if (mihomoLogsWs) { stopMihomoLogs() recoverList.push(startMihomoLogs) } if (mihomoConnectionsWs) { stopMihomoConnections() recoverList.push(startMihomoConnections) } return (): void => { recoverList.forEach((recover) => recover()) } }