connections and rules

This commit is contained in:
pompurin404 2024-08-01 14:50:51 +08:00
parent 85e1c27274
commit caa7c11537
No known key found for this signature in database
10 changed files with 170 additions and 10 deletions

View File

@ -29,6 +29,7 @@
"react-icons": "^5.2.1",
"react-router-dom": "^6.25.1",
"swr": "^2.2.5",
"ws": "^8.18.0",
"yaml": "^2.5.0"
},
"devDependencies": {
@ -38,6 +39,7 @@
"@types/node": "^22.0.0",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/ws": "^8.5.12",
"@vitejs/plugin-react": "^4.3.1",
"adm-zip": "^0.5.14",
"autoprefixer": "^10.4.19",

View File

@ -38,6 +38,9 @@ importers:
swr:
specifier: ^2.2.5
version: 2.2.5(react@18.3.1)
ws:
specifier: ^8.18.0
version: 8.18.0
yaml:
specifier: ^2.5.0
version: 2.5.0
@ -60,6 +63,9 @@ importers:
'@types/react-dom':
specifier: ^18.3.0
version: 18.3.0
'@types/ws':
specifier: ^8.5.12
version: 8.5.12
'@vitejs/plugin-react':
specifier: ^4.3.1
version: 4.3.1(vite@5.3.5(@types/node@22.0.0))
@ -1746,6 +1752,9 @@ packages:
'@types/verror@1.10.10':
resolution: {integrity: sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==}
'@types/ws@8.5.12':
resolution: {integrity: sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==}
'@types/yauzl@2.10.3':
resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==}
@ -4170,6 +4179,18 @@ packages:
wrappy@1.0.2:
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:
resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==}
engines: {node: '>=8.0'}
@ -6740,6 +6761,10 @@ snapshots:
'@types/verror@1.10.10':
optional: true
'@types/ws@8.5.12':
dependencies:
'@types/node': 22.0.0
'@types/yauzl@2.10.3':
dependencies:
'@types/node': 22.0.0
@ -9561,6 +9586,8 @@ snapshots:
wrappy@1.0.2: {}
ws@8.18.0: {}
xmlbuilder@15.1.1: {}
y18n@5.0.8: {}

View File

@ -1,5 +1,11 @@
import { ipcMain } from 'electron'
import { mihomoConfig, mihomoVersion, patchMihomoConfig } from './mihomo-api'
import {
mihomoConfig,
mihomoConnections,
mihomoRules,
mihomoVersion,
patchMihomoConfig
} from './mihomo-api'
import { checkAutoRun, disableAutoRun, enableAutoRun } from './autoRun'
import {
getAppConfig,
@ -13,6 +19,8 @@ import { restartCore } from './manager'
export function registerIpcMainHandlers(): void {
ipcMain.handle('mihomoVersion', mihomoVersion)
ipcMain.handle('mihomoConfig', mihomoConfig)
ipcMain.handle('mihomoConnections', mihomoConnections)
ipcMain.handle('mihomeRules', mihomoRules)
ipcMain.handle('patchMihomoConfig', async (_e, patch) => await patchMihomoConfig(patch))
ipcMain.handle('checkAutoRun', checkAutoRun)
ipcMain.handle('enableAutoRun', enableAutoRun)

View File

@ -7,9 +7,9 @@ import { registerIpcMainHandlers } from './cmds'
import { initConfig, appConfig, controledMihomoConfig, setControledMihomoConfig } from './config'
import { stopCore, startCore } from './manager'
import { initDirs } from './dirs'
import { patchMihomoConfig } from './mihomo-api'
import { mihomoTraffic, patchMihomoConfig } from './mihomo-api'
let window: BrowserWindow | null = null
export let window: BrowserWindow | null = null
let tray: Tray | null = null
let trayContextMenu: Menu | null = null
@ -57,7 +57,7 @@ if (!gotTheLock) {
registerIpcMainHandlers()
createWindow()
createTray()
mihomoTraffic()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.

View File

@ -1,10 +1,11 @@
import axios, { AxiosInstance } from 'axios'
import { controledMihomoConfig } from './config'
import WebSocket from 'ws'
import { window } from '.'
let axiosIns: AxiosInstance = null!
let mihomoTrafficWs: WebSocket = null!
/// initialize some information
/// enable force update axiosIns
export const getAxios = async (force: boolean = false): Promise<AxiosInstance> => {
if (axiosIns && !force) return axiosIns
@ -32,8 +33,34 @@ export const mihomoConfig = async (): Promise<IMihomoConfig> => {
return instance.get('/configs') as Promise<IMihomoConfig>
}
/// Update current configs
export const patchMihomoConfig = async (patch: Partial<IMihomoConfig>): Promise<void> => {
const instance = await getAxios()
return instance.patch('/configs', patch)
}
export const mihomoConnections = async (): Promise<IMihomoConnectionsInfo> => {
const instance = await getAxios()
return instance.get('/connections') as Promise<IMihomoConnectionsInfo>
}
export const mihomoRules = async (): Promise<IMihomoRulesInfo> => {
const instance = await getAxios()
return instance.get('/rules') as Promise<IMihomoRulesInfo>
}
export const mihomoTraffic = (): void => {
let server = controledMihomoConfig['external-controller']
const secret = controledMihomoConfig.secret ?? ''
if (server?.startsWith(':')) server = `127.0.0.1${server}`
mihomoTrafficWs = new WebSocket(`ws://${server}/traffic?secret=${secret}`)
mihomoTrafficWs.onmessage = (e: { data: string }): void => {
window?.webContents.send('mihomoTraffic', JSON.parse(e.data) as IMihomoTrafficInfo)
}
mihomoTrafficWs.onerror = (): void => {
console.error('Traffic ws error')
mihomoConfig()
}
}

View File

@ -1,11 +1,27 @@
import { Button, Card, CardBody, CardFooter } from '@nextui-org/react'
import { IoLink } from 'react-icons/io5'
import { Button, Card, CardBody, CardFooter, Chip } from '@nextui-org/react'
import { useLocation, useNavigate } from 'react-router-dom'
import { IoLink } from 'react-icons/io5'
import { useEffect } from 'react'
import useSWR from 'swr'
import { mihomoConnections } from '@renderer/utils/ipc'
const ConnCard: React.FC = () => {
const navigate = useNavigate()
const location = useLocation()
const { data: connections } = useSWR<IMihomoConnectionsInfo>('/connections', mihomoConnections, {
refreshInterval: 5000
})
useEffect(() => {
window.electron.ipcRenderer.on('mihomoTraffic', (_e, info: IMihomoTrafficInfo) => {
console.log(info)
})
return (): void => {
window.electron.ipcRenderer.removeAllListeners('mihomoTraffic')
}
}, [])
return (
<Card
className={`w-[50%] mr-1 mb-2 ${location.pathname.includes('/connections') ? 'bg-primary' : ''}`}
@ -22,6 +38,9 @@ const ConnCard: React.FC = () => {
>
<IoLink color="default" className="text-[20px]" />
</Button>
<Chip size="sm" color="secondary" variant="bordered" className="mr-3 mt-2">
{connections?.connections?.length ?? 0}
</Chip>
</div>
</CardBody>
<CardFooter className="pt-1">

View File

@ -1,11 +1,17 @@
import { Button, Card, CardBody, CardFooter, Chip } from '@nextui-org/react'
import { mihomoRules } from '@renderer/utils/ipc'
import { IoGitNetwork } from 'react-icons/io5'
import { useLocation, useNavigate } from 'react-router-dom'
import useSWR from 'swr'
const RuleCard: React.FC = () => {
const navigate = useNavigate()
const location = useLocation()
const { data: rules } = useSWR<IMihomoRulesInfo>('/connections', mihomoRules, {
refreshInterval: 5000
})
return (
<Card
className={`w-[50%] mr-1 mb-2 ${location.pathname.includes('/rules') ? 'bg-primary' : ''}`}
@ -23,7 +29,7 @@ const RuleCard: React.FC = () => {
<IoGitNetwork color="default" className="text-[20px]" />
</Button>
<Chip size="sm" color="secondary" variant="bordered" className="mr-3 mt-2">
1103
{rules?.rules?.length ?? 0}
</Chip>
</div>
</CardBody>

View File

@ -0,0 +1,6 @@
export function calcTraffic(bit: number): string {
if (bit < 1024) return `${bit} B`
if (bit < 1024 * 1024) return `${(bit / 1024).toFixed(2)} KB`
if (bit < 1024 * 1024 * 1024) return `${(bit / 1024 / 1024).toFixed(2)} MB`
return `${(bit / 1024 / 1024 / 1024).toFixed(2)} GB`
}

View File

@ -6,6 +6,14 @@ export async function mihomoConfig(): Promise<IMihomoConfig> {
return await window.electron.ipcRenderer.invoke('mihomoConfig')
}
export async function mihomoConnections(): Promise<IMihomoConnectionsInfo> {
return await window.electron.ipcRenderer.invoke('mihomoConnections')
}
export async function mihomoRules(): Promise<IMihomoRulesInfo> {
return await window.electron.ipcRenderer.invoke('mihomoRules')
}
export async function patchMihomoConfig(patch: Partial<IMihomoConfig>): Promise<void> {
await window.electron.ipcRenderer.invoke('patchMihomoConfig', patch)
}

57
src/shared/types.d.ts vendored
View File

@ -6,6 +6,63 @@ interface IMihomoVersion {
meta: boolean
}
interface IMihomoTrafficInfo {
up: number
down: number
}
interface IMihomoRulesInfo {
rules: IMihomoRulesDetail[]
}
interface IMihomoRulesDetail {
type: string
payload: string
proxy: string
size: number
}
interface IMihomoConnectionsInfo {
downloadTotal: number
uploadTotal: number
connections?: IMihomoConnectionDetail[]
memory: number
}
interface IMihomoConnectionDetail {
id: string
metadata: {
network: 'tcp' | 'udp'
type: string
sourceIP: string
destinationIP: string
destinationGeoIP: string
destinationIPASN: string
sourcePort: string
destinationPort: string
inboundIP: string
inboundPort: string
inboundName: string
inboundUser: string
host: string
dnsMode: string
uid: number
process: string
processPath: string
specialProxy: string
specialRules: string
remoteDestination: string
dscp: number
sniffHost: string
}
upload: number
download: number
start: string
chains: string[]
rule: string
rulePayload: string
}
interface IAppConfig {
core: 'mihomo' | 'mihomo-alpha'
silentStart: boolean