From e11a98b19816f6f61e4f5c71ce393ee5c7a8e101 Mon Sep 17 00:00:00 2001 From: Pompurin404 Date: Fri, 2 Aug 2024 11:25:43 +0800 Subject: [PATCH] trafficInfo --- src/main/config/index.ts | 10 +-- src/main/config/profile.ts | 78 +++++++++++++------ src/main/core/manager.ts | 4 +- src/main/core/mihomoApi.ts | 10 +-- src/main/core/tray.ts | 17 ++-- src/main/index.ts | 4 +- src/main/resolve/factory.ts | 4 +- src/main/resolve/init.ts | 12 +-- src/main/resolve/sysproxy.ts | 4 +- src/main/utils/cmds.ts | 2 +- src/renderer/src/App.tsx | 6 +- .../src/components/sider/conn-card.tsx | 47 +++++------ .../src/components/sider/log-card.tsx | 2 +- .../src/components/sider/override-card.tsx | 2 +- .../src/components/sider/profile-card.tsx | 26 +++---- .../src/components/sider/rule-card.tsx | 4 +- .../src/components/sider/test-card.tsx | 36 +++++++++ src/renderer/src/hooks/use-profile.tsx | 3 - src/renderer/src/pages/tests.tsx | 5 ++ src/renderer/src/routes/index.tsx | 5 ++ src/renderer/src/utils/calc.ts | 19 ++++- src/shared/types.d.ts | 17 ++-- 22 files changed, 195 insertions(+), 122 deletions(-) create mode 100644 src/renderer/src/components/sider/test-card.tsx create mode 100644 src/renderer/src/pages/tests.tsx diff --git a/src/main/config/index.ts b/src/main/config/index.ts index 93e1ae91..29c91b16 100644 --- a/src/main/config/index.ts +++ b/src/main/config/index.ts @@ -1,12 +1,6 @@ -export { appConfig, getAppConfig, setAppConfig } from './app' +export { getAppConfig, setAppConfig } from './app' +export { getControledMihomoConfig, setControledMihomoConfig } from './controledMihomo' export { - controledMihomoConfig, - getControledMihomoConfig, - setControledMihomoConfig -} from './controledMihomo' -export { - profileConfig, - currentProfile, getCurrentProfile, getCurrentProfileItem, getProfileItem, diff --git a/src/main/config/profile.ts b/src/main/config/profile.ts index c334eaff..9311f86f 100644 --- a/src/main/config/profile.ts +++ b/src/main/config/profile.ts @@ -1,12 +1,14 @@ -import { controledMihomoConfig } from './controledMihomo' +import { getControledMihomoConfig } from './controledMihomo' import { profileConfigPath, profilePath } from '../utils/dirs' -import { app } from 'electron' +import { restartCore } from '../core/manager' +import { getAppConfig } from './app' +import { window } from '..' import axios from 'axios' import yaml from 'yaml' import fs from 'fs' -export let profileConfig: IProfileConfig // profile.yaml -export let currentProfile: Partial // profiles/xxx.yaml +let profileConfig: IProfileConfig // profile.yaml +let currentProfile: Partial // profiles/xxx.yaml export function getProfileConfig(force = false): IProfileConfig { if (force || !profileConfig) { @@ -16,19 +18,25 @@ export function getProfileConfig(force = false): IProfileConfig { } export function getProfileItem(id: string | undefined): IProfileItem { - const items = profileConfig.items + const items = getProfileConfig().items return items?.find((item) => item.id === id) || { id: 'default', type: 'local', name: '空白订阅' } } export async function addProfileItem(item: Partial): Promise { const newItem = await createProfile(item) + profileConfig.items = getProfileConfig().items.filter((item) => item.id !== newItem.id) profileConfig.items.push(newItem) - console.log(!profileConfig.current) - if (!profileConfig.current) { + let changeProfile = false + if (!getProfileConfig().current) { profileConfig.current = newItem.id + changeProfile = true } - console.log(profileConfig.current) fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig)) + window?.webContents.send('profileConfigUpdated') + if (changeProfile) { + getCurrentProfile(true) + restartCore() + } } export function removeProfileItem(id: string): void { @@ -37,10 +45,33 @@ export function removeProfileItem(id: string): void { profileConfig.current = profileConfig.items[0]?.id } fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig)) + window?.webContents.send('profileConfigUpdated') } export function getCurrentProfileItem(): IProfileItem { - return getProfileItem(profileConfig.current) + return getProfileItem(getProfileConfig().current) +} + +// attachment;filename=xxx.yaml; filename*=UTF-8''%xx%xx%xx +function parseFilename(str: string): string { + if (str.includes("filename*=UTF-8''")) { + const filename = decodeURIComponent(str.split("filename*=UTF-8''")[1]) + return filename + } else { + const filename = str.split('filename=')[1] + return filename + } +} + +// subscription-userinfo: upload=1234; download=2234; total=1024000; expire=2218532293 +function parseSubinfo(str: string): ISubscriptionUserInfo { + const parts = str.split('; ') + const obj = {} as ISubscriptionUserInfo + parts.forEach((part) => { + const [key, value] = part.split('=') + obj[key] = parseInt(value) + }) + return obj } export async function createProfile(item: Partial): Promise { @@ -50,6 +81,7 @@ export async function createProfile(item: Partial): Promise): Promise item.split('=')[1].trim()) - newItem.extra = { - upload: parseInt(extra[0]), - download: parseInt(extra[1]), - total: parseInt(extra[2]), - expire: parseInt(extra[3]) - } + newItem.extra = parseSubinfo(headers['subscription-userinfo']) } fs.writeFileSync(profilePath(id), data, 'utf-8') } catch (e) { @@ -106,8 +137,9 @@ export async function createProfile(item: Partial): Promise { if (force || !currentProfile) { - if (profileConfig.current) { - currentProfile = yaml.parse(fs.readFileSync(profilePath(profileConfig.current), 'utf-8')) + const current = getProfileConfig().current + if (current) { + currentProfile = yaml.parse(fs.readFileSync(profilePath(current), 'utf-8')) } else { currentProfile = yaml.parse(fs.readFileSync(profilePath('default'), 'utf-8')) } diff --git a/src/main/core/manager.ts b/src/main/core/manager.ts index eb1a2c3f..cf13510a 100644 --- a/src/main/core/manager.ts +++ b/src/main/core/manager.ts @@ -1,12 +1,12 @@ import { ChildProcess, execSync, spawn } from 'child_process' import { logPath, mihomoCorePath, mihomoWorkDir } from '../utils/dirs' import { generateProfile } from '../resolve/factory' -import { appConfig } from '../config' +import { getAppConfig } from '../config' import fs from 'fs' let child: ChildProcess export async function startCore(): Promise { - const corePath = mihomoCorePath(appConfig.core ?? 'mihomo') + const corePath = mihomoCorePath(getAppConfig().core ?? 'mihomo') generateProfile() stopCore() if (process.platform !== 'win32') { diff --git a/src/main/core/mihomoApi.ts b/src/main/core/mihomoApi.ts index 90de5e22..6c713a9f 100644 --- a/src/main/core/mihomoApi.ts +++ b/src/main/core/mihomoApi.ts @@ -1,5 +1,5 @@ import axios, { AxiosInstance } from 'axios' -import { controledMihomoConfig } from '../config' +import { getControledMihomoConfig } from '../config' import WebSocket from 'ws' import { window } from '..' @@ -9,8 +9,8 @@ let mihomoTrafficWs: WebSocket = null! export const getAxios = async (force: boolean = false): Promise => { if (axiosIns && !force) return axiosIns - let server = controledMihomoConfig['external-controller'] - const secret = controledMihomoConfig.secret ?? '' + let server = getControledMihomoConfig()['external-controller'] + const secret = getControledMihomoConfig().secret ?? '' if (server?.startsWith(':')) server = `127.0.0.1${server}` axiosIns = axios.create({ @@ -49,8 +49,8 @@ export const mihomoRules = async (): Promise => { } export const mihomoTraffic = (): void => { - let server = controledMihomoConfig['external-controller'] - const secret = controledMihomoConfig.secret ?? '' + let server = getControledMihomoConfig()['external-controller'] + const secret = getControledMihomoConfig().secret ?? '' if (server?.startsWith(':')) server = `127.0.0.1${server}` mihomoTrafficWs = new WebSocket(`ws://${server}/traffic?secret=${secret}`) diff --git a/src/main/core/tray.ts b/src/main/core/tray.ts index 40163eb4..e7862190 100644 --- a/src/main/core/tray.ts +++ b/src/main/core/tray.ts @@ -1,4 +1,9 @@ -import { appConfig, controledMihomoConfig, setAppConfig, setControledMihomoConfig } from '../config' +import { + getAppConfig, + getControledMihomoConfig, + setAppConfig, + setControledMihomoConfig +} from '../config' import icoIcon from '../../../resources/icon.ico?asset' import pngIcon from '../../../resources/icon.png?asset' import { patchMihomoConfig } from './mihomoApi' @@ -24,7 +29,7 @@ const buildContextMenu = (): Menu => { id: 'rule', label: '规则模式', type: 'radio', - checked: controledMihomoConfig.mode === 'rule', + checked: getControledMihomoConfig().mode === 'rule', click: (): void => { setControledMihomoConfig({ mode: 'rule' }) patchMihomoConfig({ mode: 'rule' }) @@ -36,7 +41,7 @@ const buildContextMenu = (): Menu => { id: 'global', label: '全局模式', type: 'radio', - checked: controledMihomoConfig.mode === 'global', + checked: getControledMihomoConfig().mode === 'global', click: (): void => { setControledMihomoConfig({ mode: 'global' }) patchMihomoConfig({ mode: 'global' }) @@ -48,7 +53,7 @@ const buildContextMenu = (): Menu => { id: 'direct', label: '直连模式', type: 'radio', - checked: controledMihomoConfig.mode === 'direct', + checked: getControledMihomoConfig().mode === 'direct', click: (): void => { setControledMihomoConfig({ mode: 'direct' }) patchMihomoConfig({ mode: 'direct' }) @@ -60,7 +65,7 @@ const buildContextMenu = (): Menu => { { type: 'checkbox', label: '系统代理', - checked: appConfig.sysProxy?.enable ?? false, + checked: getAppConfig().sysProxy?.enable ?? false, click: (item): void => { const enable = item.checked setAppConfig({ sysProxy: { enable } }) @@ -72,7 +77,7 @@ const buildContextMenu = (): Menu => { { type: 'checkbox', label: '虚拟网卡', - checked: controledMihomoConfig.tun?.enable ?? false, + checked: getControledMihomoConfig().tun?.enable ?? false, click: (item): void => { const enable = item.checked setControledMihomoConfig({ tun: { enable } }) diff --git a/src/main/index.ts b/src/main/index.ts index c1206089..007eef7b 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -6,7 +6,7 @@ import icon from '../../resources/icon.png?asset' import { mihomoTraffic } from './core/mihomoApi' import { createTray } from './core/tray' import { init } from './resolve/init' -import { appConfig } from './config' +import { getAppConfig } from './config' import { join } from 'path' export let window: BrowserWindow | null = null @@ -80,7 +80,7 @@ function createWindow(): void { }) window.on('ready-to-show', () => { - if (!appConfig.silentStart) { + if (!getAppConfig().silentStart) { window?.show() window?.focusOnWebView() } diff --git a/src/main/resolve/factory.ts b/src/main/resolve/factory.ts index 88c35f8a..db2677c9 100644 --- a/src/main/resolve/factory.ts +++ b/src/main/resolve/factory.ts @@ -1,9 +1,9 @@ -import { controledMihomoConfig, currentProfile } from '../config' +import { getControledMihomoConfig, getCurrentProfile } from '../config' import { mihomoWorkConfigPath } from '../utils/dirs' import yaml from 'yaml' import fs from 'fs' export function generateProfile(): void { - const profile = Object.assign(currentProfile, controledMihomoConfig) + const profile = Object.assign(getCurrentProfile(), getControledMihomoConfig()) fs.writeFileSync(mihomoWorkConfigPath(), yaml.stringify(profile)) } diff --git a/src/main/resolve/init.ts b/src/main/resolve/init.ts index 65abea47..9e42dc6a 100644 --- a/src/main/resolve/init.ts +++ b/src/main/resolve/init.ts @@ -9,12 +9,6 @@ import { profilesDir, resourcesFilesDir } from '../utils/dirs' -import { - getAppConfig, - getControledMihomoConfig, - getCurrentProfile, - getProfileConfig -} from '../config' import { defaultConfig, defaultControledMihomoConfig, @@ -53,16 +47,12 @@ function initConfig(): void { if (!fs.existsSync(controledMihomoConfigPath())) { fs.writeFileSync(controledMihomoConfigPath(), yaml.stringify(defaultControledMihomoConfig)) } - getAppConfig(true) - getControledMihomoConfig(true) - getProfileConfig(true) - getCurrentProfile(true) } function initFiles(): void { const fileList = ['Country.mmdb', 'geoip.dat', 'geosite.dat'] for (const file of fileList) { - const targetPath = path.join(profilesDir(), file) + const targetPath = path.join(mihomoWorkDir(), file) const sourcePath = path.join(resourcesFilesDir(), file) if (!fs.existsSync(targetPath) && fs.existsSync(sourcePath)) { fs.copyFileSync(sourcePath, targetPath) diff --git a/src/main/resolve/sysproxy.ts b/src/main/resolve/sysproxy.ts index 67cd8569..8296b48a 100644 --- a/src/main/resolve/sysproxy.ts +++ b/src/main/resolve/sysproxy.ts @@ -1,4 +1,4 @@ -import { controledMihomoConfig } from '../config' +import { getControledMihomoConfig } from '../config' export function triggerSysProxy(enable: boolean): void { if (enable) { @@ -9,7 +9,7 @@ export function triggerSysProxy(enable: boolean): void { } export function enableSysProxy(): void { - console.log('enableSysProxy', controledMihomoConfig['mixed-port']) + console.log('enableSysProxy', getControledMihomoConfig()['mixed-port']) } export function disableSysProxy(): void { diff --git a/src/main/utils/cmds.ts b/src/main/utils/cmds.ts index 356bf856..8da44dfc 100644 --- a/src/main/utils/cmds.ts +++ b/src/main/utils/cmds.ts @@ -25,7 +25,7 @@ export function registerIpcMainHandlers(): void { ipcMain.handle('mihomoVersion', mihomoVersion) ipcMain.handle('mihomoConfig', mihomoConfig) ipcMain.handle('mihomoConnections', mihomoConnections) - ipcMain.handle('mihomeRules', mihomoRules) + ipcMain.handle('mihomoRules', mihomoRules) ipcMain.handle('patchMihomoConfig', async (_e, patch) => await patchMihomoConfig(patch)) ipcMain.handle('checkAutoRun', checkAutoRun) ipcMain.handle('enableAutoRun', enableAutoRun) diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index bf115d0f..da19a739 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -14,6 +14,7 @@ import OverrideCard from '@renderer/components/sider/override-card' import ConnCard from '@renderer/components/sider/conn-card' import LogCard from '@renderer/components/sider/log-card' import MihomoCoreCard from './components/sider/mihomo-core-card.tsx' +import TestCard from './components/sider/test-card.js' const App: React.FC = () => { const { setTheme } = useTheme() @@ -74,14 +75,15 @@ const App: React.FC = () => { +
- +
- +
{/* */} diff --git a/src/renderer/src/components/sider/conn-card.tsx b/src/renderer/src/components/sider/conn-card.tsx index 982f49b0..3a0b3f45 100644 --- a/src/renderer/src/components/sider/conn-card.tsx +++ b/src/renderer/src/components/sider/conn-card.tsx @@ -1,22 +1,22 @@ -import { Button, Card, CardBody, CardFooter, Chip } from '@nextui-org/react' +import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' +import { FaCircleArrowDown, FaCircleArrowUp } from 'react-icons/fa6' import { useLocation, useNavigate } from 'react-router-dom' +import { calcTraffic } from '@renderer/utils/calc' +import { useEffect, useState } from 'react' 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 match = location.pathname.includes('/connections') - const { data: connections } = useSWR('/connections', mihomoConnections, { - refreshInterval: 5000 - }) + const [upload, setUpload] = useState(0) + const [download, setDownload] = useState(0) useEffect(() => { window.electron.ipcRenderer.on('mihomoTraffic', (_e, info: IMihomoTrafficInfo) => { - console.log(info) + setUpload(info.up) + setDownload(info.down) }) return (): void => { window.electron.ipcRenderer.removeAllListeners('mihomoTraffic') @@ -25,7 +25,8 @@ const ConnCard: React.FC = () => { return ( navigate('/connections')} > @@ -39,24 +40,16 @@ const ConnCard: React.FC = () => { > - - {connections?.connections?.length ?? 0} - +
+
+
{calcTraffic(upload)}/s
+ +
+
+
{calcTraffic(download)}/s
+ +
+
diff --git a/src/renderer/src/components/sider/log-card.tsx b/src/renderer/src/components/sider/log-card.tsx index be57bcf3..22bca6c6 100644 --- a/src/renderer/src/components/sider/log-card.tsx +++ b/src/renderer/src/components/sider/log-card.tsx @@ -8,7 +8,7 @@ const LogCard: React.FC = () => { const match = location.pathname.includes('/logs') return ( navigate('/logs')} > diff --git a/src/renderer/src/components/sider/override-card.tsx b/src/renderer/src/components/sider/override-card.tsx index 8c462bb0..0c79dade 100644 --- a/src/renderer/src/components/sider/override-card.tsx +++ b/src/renderer/src/components/sider/override-card.tsx @@ -1,4 +1,4 @@ -import { Button, Card, CardBody, CardFooter, cn } from '@nextui-org/react' +import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' import React, { useState } from 'react' import { MdFormatOverline } from 'react-icons/md' import { useLocation, useNavigate } from 'react-router-dom' diff --git a/src/renderer/src/components/sider/profile-card.tsx b/src/renderer/src/components/sider/profile-card.tsx index ded19dd6..683529c4 100644 --- a/src/renderer/src/components/sider/profile-card.tsx +++ b/src/renderer/src/components/sider/profile-card.tsx @@ -1,29 +1,25 @@ import { Button, Card, CardBody, CardFooter, Progress } from '@nextui-org/react' -import { getCurrentProfileItem } from '@renderer/utils/ipc' -import { useEffect } from 'react' -import { IoMdRefresh } from 'react-icons/io' +import { useProfileConfig } from '@renderer/hooks/use-profile' import { useLocation, useNavigate } from 'react-router-dom' import { calcTraffic } from '@renderer/utils/calc' -import useSWR from 'swr' +import { IoMdRefresh } from 'react-icons/io' const ProfileCard: React.FC = () => { const navigate = useNavigate() const location = useLocation() const match = location.pathname.includes('/profiles') - const { data: info, mutate } = useSWR('getCurrentProfileItem', getCurrentProfileItem) + const { profileConfig } = useProfileConfig() + const { current, items } = profileConfig ?? {} + const info = items?.find((item) => item.id === current) ?? { + id: 'default', + type: 'local', + name: '空白订阅' + } const extra = info?.extra const usage = (extra?.upload ?? 0) + (extra?.download ?? 0) const total = extra?.total ?? 0 - useEffect(() => { - window.electron.ipcRenderer.on('profileConfigUpdated', () => { - mutate() - }) - return (): void => { - window.electron.ipcRenderer.removeAllListeners('profileConfigUpdated') - } - }) return ( { diff --git a/src/renderer/src/components/sider/rule-card.tsx b/src/renderer/src/components/sider/rule-card.tsx index 33098e6a..4953c72c 100644 --- a/src/renderer/src/components/sider/rule-card.tsx +++ b/src/renderer/src/components/sider/rule-card.tsx @@ -8,13 +8,13 @@ const RuleCard: React.FC = () => { const navigate = useNavigate() const location = useLocation() const match = location.pathname.includes('/rules') - const { data: rules } = useSWR('/connections', mihomoRules, { + const { data: rules } = useSWR('/rules', mihomoRules, { refreshInterval: 5000 }) return ( navigate('/rules')} > diff --git a/src/renderer/src/components/sider/test-card.tsx b/src/renderer/src/components/sider/test-card.tsx new file mode 100644 index 00000000..540a9911 --- /dev/null +++ b/src/renderer/src/components/sider/test-card.tsx @@ -0,0 +1,36 @@ +import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' +import React from 'react' +import { TbWorldCheck } from 'react-icons/tb' +import { useLocation, useNavigate } from 'react-router-dom' + +const TestCard: React.FC = () => { + const navigate = useNavigate() + const location = useLocation() + const match = location.pathname.includes('/tests') + + return ( + navigate('/tests')} + > + +
+ +
+
+ +

测试

+
+
+ ) +} + +export default TestCard diff --git a/src/renderer/src/hooks/use-profile.tsx b/src/renderer/src/hooks/use-profile.tsx index 8a60ed30..111fe846 100644 --- a/src/renderer/src/hooks/use-profile.tsx +++ b/src/renderer/src/hooks/use-profile.tsx @@ -21,15 +21,12 @@ export const useProfileConfig = (): RetuenType => { const addProfileItem = async (item: Partial): Promise => { await add(item) mutateProfileConfig() - window.electron.ipcRenderer.send('profileConfigUpdated') } const removeProfileItem = async (id: string): Promise => { await remove(id) mutateProfileConfig() - window.electron.ipcRenderer.send('profileConfigUpdated') } - useEffect(() => { window.electron.ipcRenderer.on('profileConfigUpdated', () => { mutateProfileConfig() diff --git a/src/renderer/src/pages/tests.tsx b/src/renderer/src/pages/tests.tsx new file mode 100644 index 00000000..4f54bced --- /dev/null +++ b/src/renderer/src/pages/tests.tsx @@ -0,0 +1,5 @@ +const Tests: React.FC = () => { + return
Tests
+} + +export default Tests diff --git a/src/renderer/src/routes/index.tsx b/src/renderer/src/routes/index.tsx index 5e079531..c62b7497 100644 --- a/src/renderer/src/routes/index.tsx +++ b/src/renderer/src/routes/index.tsx @@ -9,6 +9,7 @@ import Connections from '@renderer/pages/connections' import Mihomo from '@renderer/pages/mihomo' import Sysproxy from '@renderer/pages/syspeoxy' import Tun from '@renderer/pages/tun' +import Tests from '@renderer/pages/tests' const routes = [ { @@ -31,6 +32,10 @@ const routes = [ path: '/rules', element: }, + { + path: '/tests', + element: + }, { path: '/logs', element: diff --git a/src/renderer/src/utils/calc.ts b/src/renderer/src/utils/calc.ts index 023d5c96..132b5457 100644 --- a/src/renderer/src/utils/calc.ts +++ b/src/renderer/src/utils/calc.ts @@ -1,6 +1,19 @@ 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` + bit /= 1024 + if (bit < 1024) return `${bit.toFixed(2)} KB` + bit /= 1024 + if (bit < 1024) return `${bit.toFixed(2)} MB` + bit /= 1024 + if (bit < 1024) return `${bit.toFixed(2)} GB` + bit /= 1024 + if (bit < 1024) return `${bit.toFixed(2)} TB` + bit /= 1024 + if (bit < 1024) return `${bit.toFixed(2)} PB` + bit /= 1024 + if (bit < 1024) return `${bit.toFixed(2)} EB` + bit /= 1024 + if (bit < 1024) return `${bit.toFixed(2)} ZB` + bit /= 1024 + return `${bit.toFixed(2)} YB` } diff --git a/src/shared/types.d.ts b/src/shared/types.d.ts index 95205768..2e02b11d 100644 --- a/src/shared/types.d.ts +++ b/src/shared/types.d.ts @@ -74,6 +74,7 @@ interface IAppConfig { core: 'mihomo' | 'mihomo-alpha' silentStart: boolean sysProxy: ISysProxyConfig + userAgent?: string } interface IMihomoTunConfig { @@ -127,17 +128,21 @@ interface IProfileConfig { items: IProfileItem[] } +interface ISubscriptionUserInfo { + upload: number + download: number + total: number + expire: number +} + interface IProfileItem { id: string type: 'remote' | 'local' name: string url?: string // remote file?: string // local + interval?: number + home?: string updated?: number - extra?: { - upload: number - download: number - total: number - expire: number - } + extra?: ISubscriptionUserInfo }