use axios

This commit is contained in:
pompurin404 2024-09-30 10:44:11 +08:00
parent 79c7074b8e
commit db84e6d318
No known key found for this signature in database
4 changed files with 212 additions and 160 deletions

View File

@ -31,6 +31,7 @@
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"express": "^5.0.0", "express": "^5.0.0",
"webdav": "^5.7.1", "webdav": "^5.7.1",
"ws": "^8.18.0",
"yaml": "^2.5.1" "yaml": "^2.5.1"
}, },
"devDependencies": { "devDependencies": {

View File

@ -35,6 +35,9 @@ importers:
webdav: webdav:
specifier: ^5.7.1 specifier: ^5.7.1
version: 5.7.1 version: 5.7.1
ws:
specifier: ^8.18.0
version: 8.18.0
yaml: yaml:
specifier: ^2.5.1 specifier: ^2.5.1
version: 2.5.1 version: 2.5.1
@ -5264,6 +5267,18 @@ packages:
wrappy@1.0.2: wrappy@1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
ws@8.18.0:
resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
engines: {node: '>=10.0.0'}
peerDependencies:
bufferutil: ^4.0.1
utf-8-validate: '>=5.0.2'
peerDependenciesMeta:
bufferutil:
optional: true
utf-8-validate:
optional: true
xmlbuilder@15.1.1: xmlbuilder@15.1.1:
resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
engines: {node: '>=8.0'} engines: {node: '>=8.0'}
@ -11849,6 +11864,8 @@ snapshots:
wrappy@1.0.2: {} wrappy@1.0.2: {}
ws@8.18.0: {}
xmlbuilder@15.1.1: {} xmlbuilder@15.1.1: {}
y18n@5.0.8: {} y18n@5.0.8: {}

View File

@ -288,7 +288,7 @@ const resolveMonitor = async () => {
if (fs.existsSync(targetPath)) { if (fs.existsSync(targetPath)) {
fs.rmSync(targetPath, { recursive: true }) fs.rmSync(targetPath, { recursive: true })
} }
zip.extractAllTo(resDir, true) zip.extractAllTo(targetPath, true)
console.log(`[INFO]: TrafficMonitor finished`) console.log(`[INFO]: TrafficMonitor finished`)
} }

View File

@ -1,116 +1,78 @@
import net from 'net' import axios, { AxiosInstance } from 'axios'
import { getRuntimeConfig } from './factory'
import { getAppConfig, getControledMihomoConfig } from '../config' import { getAppConfig, getControledMihomoConfig } from '../config'
import { mainWindow } from '..' import { mainWindow } from '..'
import WebSocket from 'ws'
import { tray } from '../resolve/tray' import { tray } from '../resolve/tray'
import { calcTraffic } from '../utils/calc' import { calcTraffic } from '../utils/calc'
import { getRuntimeConfig } from './factory'
import { join } from 'path' import { join } from 'path'
import { mihomoWorkDir } from '../utils/dirs' import { mihomoWorkDir } from '../utils/dirs'
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' let axiosIns: AxiosInstance = null!
let mihomoTrafficWs: WebSocket | null = null
let mihomoTrafficWs: net.Socket | null = null
let trafficRetry = 10 let trafficRetry = 10
let mihomoMemoryWs: net.Socket | null = null let mihomoMemoryWs: WebSocket | null = null
let memoryRetry = 10 let memoryRetry = 10
let mihomoLogsWs: net.Socket | null = null let mihomoLogsWs: WebSocket | null = null
let logsRetry = 10 let logsRetry = 10
let mihomoConnectionsWs: net.Socket | null = null let mihomoConnectionsWs: WebSocket | null = null
let connectionsRetry = 10 let connectionsRetry = 10
function trimJson(data: string): string { export const getAxios = async (force: boolean = false): Promise<AxiosInstance> => {
if (data.trim().length === 0) return ''
const start = data.indexOf('{')
const end = data.lastIndexOf('}')
return data.slice(start, end + 1)
}
async function mihomoHttp<T>(method: HttpMethod, path: string, data?: object): Promise<T> {
const { const {
'external-controller-pipe': mihomoPipe = '\\\\.\\pipe\\MihomoParty\\mihomo', 'external-controller-pipe': mihomoPipe = '\\\\.\\pipe\\MihomoParty\\mihomo',
'external-controller-unix': mihomoUnix = 'mihomo-party.sock' 'external-controller-unix': mihomoUnix = 'mihomo-party.sock'
} = await getControledMihomoConfig() } = await getControledMihomoConfig()
return new Promise((resolve, reject) => { if (axiosIns && !force) return axiosIns
const client = net.connect(
process.platform === 'win32' ? mihomoPipe : join(mihomoWorkDir(), mihomoUnix) axiosIns = axios.create({
) baseURL: `http://localhost`,
const parseResult = (str: string): void => { socketPath: process.platform === 'win32' ? mihomoPipe : join(mihomoWorkDir(), mihomoUnix),
try { timeout: 15000
const data = str.split('\r\n\r\n')[1]
const json = trimJson(data)
if (str.includes('HTTP/1.1 4') || str.includes('HTTP/1.1 5')) {
reject(json ? JSON.parse(json) : data)
} else {
resolve(json ? JSON.parse(json) : undefined)
}
} catch (e) {
reject(e)
} finally {
client.end()
}
}
let buffer = ''
client.on('data', function (res) {
if (res.toString().includes('Transfer-Encoding: chunked') || buffer !== '') {
buffer += res.toString()
if (buffer.endsWith('\r\n\r\n')) {
parseResult(buffer)
}
} else {
parseResult(res.toString())
}
})
client.on('error', function (error) {
reject(error)
})
if (data) {
const json = JSON.stringify(data)
client.write(
`${method} ${path} HTTP/1.1\r\nHost: mihomo-party\r\nContent-Type: application/json\r\nContent-Length: ${Buffer.from(json).length}\r\n\r\n${json}`
)
} else {
client.write(`${method} ${path} HTTP/1.1\r\nHost: mihomo-party\r\n\r\n`)
}
}) })
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
} }
async function mihomoWs(path: string): Promise<net.Socket> { export async function mihomoVersion(): Promise<IMihomoVersion> {
const { const instance = await getAxios()
'external-controller-pipe': mihomoPipe = '\\\\.\\pipe\\MihomoParty\\mihomo', return await instance.get('/version')
'external-controller-unix': mihomoUnix = 'mihomo-party.sock'
} = await getControledMihomoConfig()
const client = net.connect(
process.platform === 'win32' ? mihomoPipe : join(mihomoWorkDir(), mihomoUnix)
)
client.write(
`GET ${path} HTTP/1.1\r\nHost: mihomo-party\r\nConnection: Upgrade\r\nUpgrade: websocket\r\nSec-WebSocket-Version: 13\r\nSec-WebSocket-Key: xxxxxxxxxxxxxxxxxxxxxxxx\r\n\r\n`
)
return client
}
export const mihomoVersion = async (): Promise<IMihomoVersion> => {
return await mihomoHttp('GET', '/version')
} }
export const patchMihomoConfig = async (patch: Partial<IMihomoConfig>): Promise<void> => { export const patchMihomoConfig = async (patch: Partial<IMihomoConfig>): Promise<void> => {
return await mihomoHttp('PATCH', '/configs', patch) const instance = await getAxios()
return await instance.patch('/configs', patch)
} }
export const mihomoCloseConnection = async (id: string): Promise<void> => { export const mihomoCloseConnection = async (id: string): Promise<void> => {
return await mihomoHttp('DELETE', `/connections/${id}`) const instance = await getAxios()
return await instance.delete(`/connections/${encodeURIComponent(id)}`)
} }
export const mihomoCloseAllConnections = async (): Promise<void> => { export const mihomoCloseAllConnections = async (): Promise<void> => {
return await mihomoHttp('DELETE', '/connections') const instance = await getAxios()
return await instance.delete('/connections')
} }
export const mihomoRules = async (): Promise<IMihomoRulesInfo> => { export const mihomoRules = async (): Promise<IMihomoRulesInfo> => {
return await mihomoHttp('GET', '/rules') const instance = await getAxios()
return await instance.get('/rules')
} }
export const mihomoProxies = async (): Promise<IMihomoProxies> => { export const mihomoProxies = async (): Promise<IMihomoProxies> => {
const proxies = (await mihomoHttp('GET', '/proxies')) as IMihomoProxies const instance = await getAxios()
const proxies = (await instance.get('/proxies')) as IMihomoProxies
if (!proxies.proxies['GLOBAL']) { if (!proxies.proxies['GLOBAL']) {
throw new Error('GLOBAL proxy not found') throw new Error('GLOBAL proxy not found')
} }
@ -141,199 +103,271 @@ export const mihomoGroups = async (): Promise<IMihomoMixedGroup[]> => {
} }
export const mihomoProxyProviders = async (): Promise<IMihomoProxyProviders> => { export const mihomoProxyProviders = async (): Promise<IMihomoProxyProviders> => {
return await mihomoHttp('GET', '/providers/proxies') const instance = await getAxios()
return await instance.get('/providers/proxies')
} }
export const mihomoUpdateProxyProviders = async (name: string): Promise<void> => { export const mihomoUpdateProxyProviders = async (name: string): Promise<void> => {
return await mihomoHttp('PUT', `/providers/proxies/${encodeURIComponent(name)}`) const instance = await getAxios()
return await instance.put(`/providers/proxies/${encodeURIComponent(name)}`)
} }
export const mihomoRuleProviders = async (): Promise<IMihomoRuleProviders> => { export const mihomoRuleProviders = async (): Promise<IMihomoRuleProviders> => {
return await mihomoHttp('GET', '/providers/rules') const instance = await getAxios()
return await instance.get('/providers/rules')
} }
export const mihomoUpdateRuleProviders = async (name: string): Promise<void> => { export const mihomoUpdateRuleProviders = async (name: string): Promise<void> => {
return await mihomoHttp('PUT', `/providers/rules/${encodeURIComponent(name)}`) const instance = await getAxios()
return await instance.put(`/providers/rules/${encodeURIComponent(name)}`)
} }
export const mihomoChangeProxy = async (group: string, proxy: string): Promise<IMihomoProxy> => { export const mihomoChangeProxy = async (group: string, proxy: string): Promise<IMihomoProxy> => {
return await mihomoHttp('PUT', `/proxies/${encodeURIComponent(group)}`, { name: proxy }) const instance = await getAxios()
return await instance.put(`/proxies/${encodeURIComponent(group)}`, { name: proxy })
} }
export const mihomoUnfixedProxy = async (group: string): Promise<void> => { export const mihomoUnfixedProxy = async (group: string): Promise<IMihomoProxy> => {
return await mihomoHttp('DELETE', `/proxies/${encodeURIComponent(group)}`) const instance = await getAxios()
return await instance.delete(`/proxies/${encodeURIComponent(group)}`)
} }
export const mihomoUpgradeGeo = async (): Promise<void> => { export const mihomoUpgradeGeo = async (): Promise<void> => {
return await mihomoHttp('POST', '/configs/geo') const instance = await getAxios()
return await instance.post('/configs/geo')
} }
export const mihomoProxyDelay = async (proxy: string, url?: string): Promise<IMihomoDelay> => { export const mihomoProxyDelay = async (proxy: string, url?: string): Promise<IMihomoDelay> => {
const appConfig = await getAppConfig() const appConfig = await getAppConfig()
const { delayTestUrl, delayTestTimeout } = appConfig const { delayTestUrl, delayTestTimeout } = appConfig
const instance = await getAxios()
return await mihomoHttp( return await instance.get(`/proxies/${encodeURIComponent(proxy)}/delay`, {
'GET', params: {
`/proxies/${encodeURIComponent(proxy)}/delay?url=${encodeURIComponent(url || delayTestUrl || 'https://www.gstatic.com/generate_204')}&timeout=${delayTestTimeout || 5000}` url: url || delayTestUrl || 'https://www.gstatic.com/generate_204',
) timeout: delayTestTimeout || 5000
}
})
} }
export const mihomoGroupDelay = async (group: string, url?: string): Promise<IMihomoGroupDelay> => { export const mihomoGroupDelay = async (group: string, url?: string): Promise<IMihomoGroupDelay> => {
const appConfig = await getAppConfig() const appConfig = await getAppConfig()
const { delayTestUrl, delayTestTimeout } = appConfig const { delayTestUrl, delayTestTimeout } = appConfig
return await mihomoHttp( const instance = await getAxios()
'GET', return await instance.get(`/group/${encodeURIComponent(group)}/delay`, {
`/group/${encodeURIComponent(group)}/delay?url=${encodeURIComponent(url || delayTestUrl || 'https://www.gstatic.com/generate_204')}&timeout=${delayTestTimeout || 5000}` params: {
) url: url || delayTestUrl || 'https://www.gstatic.com/generate_204',
timeout: delayTestTimeout || 5000
}
})
} }
export const mihomoUpgrade = async (): Promise<void> => { export const mihomoUpgrade = async (): Promise<void> => {
return await mihomoHttp('POST', '/upgrade') const instance = await getAxios()
return await instance.post('/upgrade')
} }
export const startMihomoTraffic = async (): Promise<void> => { export const startMihomoTraffic = async (): Promise<void> => {
await mihomoTraffic() await mihomoTraffic()
} }
export const stopMihomoTraffic = async (): Promise<void> => { export const stopMihomoTraffic = (): void => {
if (mihomoTrafficWs) { if (mihomoTrafficWs) {
mihomoTrafficWs.end() mihomoTrafficWs.removeAllListeners()
if (mihomoTrafficWs.readyState === WebSocket.OPEN) {
mihomoTrafficWs.close()
}
mihomoTrafficWs = null mihomoTrafficWs = null
} }
} }
const mihomoTraffic = async (): Promise<void> => { const mihomoTraffic = async (): Promise<void> => {
stopMihomoTraffic() const {
mihomoTrafficWs = await mihomoWs('/traffic') 'external-controller-pipe': mihomoPipe = '\\\\.\\pipe\\MihomoParty\\mihomo',
mihomoTrafficWs.on('data', (data) => { 'external-controller-unix': mihomoUnix = 'mihomo-party.sock'
} = await getControledMihomoConfig()
mihomoTrafficWs = new WebSocket(
`ws+unix:${process.platform === 'win32' ? mihomoPipe : join(mihomoWorkDir(), mihomoUnix)}:/traffic`
)
mihomoTrafficWs.onmessage = async (e): Promise<void> => {
const data = e.data as string
const json = JSON.parse(data) as IMihomoTrafficInfo
trafficRetry = 10
try { try {
const json = JSON.parse(trimJson(data.toString())) as IMihomoTrafficInfo
trafficRetry = 10
mainWindow?.webContents.send('mihomoTraffic', json) mainWindow?.webContents.send('mihomoTraffic', json)
tray?.setToolTip( if (process.platform !== 'linux') {
'↑' + tray?.setToolTip(
`${calcTraffic(json.up)}/s`.padStart(9) + '↑' +
'\n↓' + `${calcTraffic(json.up)}/s`.padStart(9) +
`${calcTraffic(json.down)}/s`.padStart(9) '\n↓' +
) `${calcTraffic(json.down)}/s`.padStart(9)
)
}
} catch { } catch {
// ignore // ignore
} }
}) }
mihomoTrafficWs.on('close', () => {
mihomoTrafficWs.onclose = (): void => {
if (trafficRetry) { if (trafficRetry) {
trafficRetry-- trafficRetry--
mihomoTraffic() mihomoTraffic()
} }
}) }
mihomoTrafficWs.on('error', (): void => { mihomoTrafficWs.onerror = (): void => {
stopMihomoTraffic() if (mihomoTrafficWs) {
}) mihomoTrafficWs.close()
mihomoTrafficWs = null
}
}
} }
export const startMihomoMemory = async (): Promise<void> => { export const startMihomoMemory = async (): Promise<void> => {
await mihomoMemory() await mihomoMemory()
} }
export const stopMihomoMemory = async (): Promise<void> => { export const stopMihomoMemory = (): void => {
if (mihomoMemoryWs) { if (mihomoMemoryWs) {
mihomoMemoryWs.end() mihomoMemoryWs.removeAllListeners()
if (mihomoMemoryWs.readyState === WebSocket.OPEN) {
mihomoMemoryWs.close()
}
mihomoMemoryWs = null mihomoMemoryWs = null
} }
} }
const mihomoMemory = async (): Promise<void> => { const mihomoMemory = async (): Promise<void> => {
stopMihomoMemory() const {
mihomoMemoryWs = await mihomoWs('/memory') 'external-controller-pipe': mihomoPipe = '\\\\.\\pipe\\MihomoParty\\mihomo',
mihomoMemoryWs.on('data', (data) => { 'external-controller-unix': mihomoUnix = 'mihomo-party.sock'
} = await getControledMihomoConfig()
mihomoMemoryWs = new WebSocket(
`ws+unix:${process.platform === 'win32' ? mihomoPipe : join(mihomoWorkDir(), mihomoUnix)}:/memory`
)
mihomoMemoryWs.onmessage = (e): void => {
const data = e.data as string
memoryRetry = 10
try { try {
const json = JSON.parse(trimJson(data.toString())) as IMihomoMemoryInfo mainWindow?.webContents.send('mihomoMemory', JSON.parse(data) as IMihomoMemoryInfo)
memoryRetry = 10
mainWindow?.webContents.send('mihomoMemory', json)
} catch { } catch {
// ignore // ignore
} }
}) }
mihomoMemoryWs.on('close', () => {
mihomoMemoryWs.onclose = (): void => {
if (memoryRetry) { if (memoryRetry) {
memoryRetry-- memoryRetry--
mihomoMemory() mihomoMemory()
} }
}) }
mihomoMemoryWs.on('error', (): void => { mihomoMemoryWs.onerror = (): void => {
stopMihomoMemory() if (mihomoMemoryWs) {
}) mihomoMemoryWs.close()
mihomoMemoryWs = null
}
}
} }
export const startMihomoLogs = async (): Promise<void> => { export const startMihomoLogs = async (): Promise<void> => {
await mihomoLogs() await mihomoLogs()
} }
export const stopMihomoLogs = async (): Promise<void> => { export const stopMihomoLogs = (): void => {
if (mihomoLogsWs) { if (mihomoLogsWs) {
mihomoLogsWs.end() mihomoLogsWs.removeAllListeners()
if (mihomoLogsWs.readyState === WebSocket.OPEN) {
mihomoLogsWs.close()
}
mihomoLogsWs = null mihomoLogsWs = null
} }
} }
const mihomoLogs = async (): Promise<void> => { const mihomoLogs = async (): Promise<void> => {
stopMihomoLogs() const {
const { 'log-level': logLevel } = await getControledMihomoConfig() 'external-controller-pipe': mihomoPipe = '\\\\.\\pipe\\MihomoParty\\mihomo',
mihomoLogsWs = await mihomoWs(`/logs?level=${logLevel}`) 'external-controller-unix': mihomoUnix = 'mihomo-party.sock',
mihomoLogsWs.on('data', (data) => { 'log-level': logLevel = 'info'
} = await getControledMihomoConfig()
mihomoLogsWs = new WebSocket(
`ws+unix:${process.platform === 'win32' ? mihomoPipe : join(mihomoWorkDir(), mihomoUnix)}:/logs?level=${logLevel}`
)
mihomoLogsWs.onmessage = (e): void => {
const data = e.data as string
logsRetry = 10
try { try {
const json = JSON.parse(trimJson(data.toString())) as IMihomoLogInfo mainWindow?.webContents.send('mihomoLogs', JSON.parse(data) as IMihomoLogInfo)
logsRetry = 10
mainWindow?.webContents.send('mihomoLogs', json)
} catch { } catch {
// ignore // ignore
} }
}) }
mihomoLogsWs.on('close', () => {
mihomoLogsWs.onclose = (): void => {
if (logsRetry) { if (logsRetry) {
logsRetry-- logsRetry--
mihomoLogs() mihomoLogs()
} }
}) }
mihomoLogsWs.on('error', (): void => { mihomoLogsWs.onerror = (): void => {
stopMihomoLogs() if (mihomoLogsWs) {
}) mihomoLogsWs.close()
mihomoLogsWs = null
}
}
} }
export const startMihomoConnections = async (): Promise<void> => { export const startMihomoConnections = async (): Promise<void> => {
await mihomoConnections() await mihomoConnections()
} }
export const stopMihomoConnections = async (): Promise<void> => { export const stopMihomoConnections = (): void => {
if (mihomoConnectionsWs) { if (mihomoConnectionsWs) {
mihomoConnectionsWs.end() mihomoConnectionsWs.removeAllListeners()
if (mihomoConnectionsWs.readyState === WebSocket.OPEN) {
mihomoConnectionsWs.close()
}
mihomoConnectionsWs = null mihomoConnectionsWs = null
} }
} }
const mihomoConnections = async (): Promise<void> => { const mihomoConnections = async (): Promise<void> => {
stopMihomoConnections() const {
mihomoConnectionsWs = await mihomoWs('/connections') 'external-controller-pipe': mihomoPipe = '\\\\.\\pipe\\MihomoParty\\mihomo',
mihomoConnectionsWs.on('data', (data) => { 'external-controller-unix': mihomoUnix = 'mihomo-party.sock'
} = await getControledMihomoConfig()
mihomoConnectionsWs = new WebSocket(
`ws+unix:${process.platform === 'win32' ? mihomoPipe : join(mihomoWorkDir(), mihomoUnix)}:/connections`
)
mihomoConnectionsWs.onmessage = (e): void => {
const data = e.data as string
connectionsRetry = 10
try { try {
const json = JSON.parse(trimJson(data.toString())) as IMihomoConnectionsInfo mainWindow?.webContents.send('mihomoConnections', JSON.parse(data) as IMihomoConnectionsInfo)
connectionsRetry = 10
mainWindow?.webContents.send('mihomoConnections', json)
} catch { } catch {
// ignore // ignore
} }
}) }
mihomoConnectionsWs.on('close', () => {
mihomoConnectionsWs.onclose = (): void => {
if (connectionsRetry) { if (connectionsRetry) {
connectionsRetry-- connectionsRetry--
mihomoConnections() mihomoConnections()
} }
}) }
mihomoConnectionsWs.on('error', (): void => { mihomoConnectionsWs.onerror = (): void => {
stopMihomoConnections() if (mihomoConnectionsWs) {
}) mihomoConnectionsWs.close()
mihomoConnectionsWs = null
}
}
} }