view runtime config

This commit is contained in:
pompurin404 2024-08-08 15:22:37 +08:00
parent 1ecaea5667
commit 7f8e1ac386
No known key found for this signature in database
9 changed files with 114 additions and 41 deletions

View File

@ -2,6 +2,7 @@ import { controledMihomoConfigPath } from '../utils/dirs'
import yaml from 'yaml' import yaml from 'yaml'
import fs from 'fs' import fs from 'fs'
import { getAxios, startMihomoMemory, startMihomoTraffic } from '../core/mihomoApi' import { getAxios, startMihomoMemory, startMihomoTraffic } from '../core/mihomoApi'
import { generateProfile } from '../resolve/factory'
export let controledMihomoConfig: Partial<IMihomoConfig> // mihomo.yaml export let controledMihomoConfig: Partial<IMihomoConfig> // mihomo.yaml
@ -34,5 +35,6 @@ export function setControledMihomoConfig(patch: Partial<IMihomoConfig>): void {
startMihomoMemory() startMihomoMemory()
startMihomoTraffic() startMihomoTraffic()
} }
generateProfile()
fs.writeFileSync(controledMihomoConfigPath(), yaml.stringify(controledMihomoConfig)) fs.writeFileSync(controledMihomoConfigPath(), yaml.stringify(controledMihomoConfig))
} }

View File

@ -1,7 +1,7 @@
export { getAppConfig, setAppConfig } from './app' export { getAppConfig, setAppConfig } from './app'
export { getControledMihomoConfig, setControledMihomoConfig } from './controledMihomo' export { getControledMihomoConfig, setControledMihomoConfig } from './controledMihomo'
export { export {
getCurrentProfile, getProfile,
getCurrentProfileItem, getCurrentProfileItem,
getProfileItem, getProfileItem,
getProfileConfig, getProfileConfig,

View File

@ -10,7 +10,6 @@ import { dialog } from 'electron'
import { addProfileUpdater } from '../core/profileUpdater' import { addProfileUpdater } from '../core/profileUpdater'
let profileConfig: IProfileConfig // profile.yaml let profileConfig: IProfileConfig // profile.yaml
let currentProfile: Partial<IMihomoConfig> // profiles/xxx.yaml
export function getProfileConfig(force = false): IProfileConfig { export function getProfileConfig(force = false): IProfileConfig {
if (force || !profileConfig) { if (force || !profileConfig) {
@ -27,12 +26,10 @@ export function getProfileItem(id: string | undefined): IProfileItem {
export async function changeCurrentProfile(id: string): Promise<void> { export async function changeCurrentProfile(id: string): Promise<void> {
const oldId = getProfileConfig().current const oldId = getProfileConfig().current
profileConfig.current = id profileConfig.current = id
getCurrentProfile(true)
try { try {
await startCore() await startCore()
} catch (e) { } catch (e) {
profileConfig.current = oldId profileConfig.current = oldId
getCurrentProfile(true)
} finally { } finally {
window?.webContents.send('profileConfigUpdated') window?.webContents.send('profileConfigUpdated')
fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig)) fs.writeFileSync(profileConfigPath(), yaml.stringify(profileConfig))
@ -180,19 +177,10 @@ export function getProfileStr(id: string): string {
export async function setProfileStr(id: string, content: string): Promise<void> { export async function setProfileStr(id: string, content: string): Promise<void> {
fs.writeFileSync(profilePath(id), content, 'utf-8') fs.writeFileSync(profilePath(id), content, 'utf-8')
if (id === getProfileConfig().current) { if (id === getProfileConfig().current) {
getCurrentProfile(true)
await startCore() await startCore()
} }
} }
export function getCurrentProfile(force = false): Partial<IMihomoConfig> { export function getProfile(id: string): IMihomoConfig {
if (force || !currentProfile) { return yaml.parse(getProfileStr(id))
const current = getProfileConfig().current
if (current) {
currentProfile = yaml.parse(getProfileStr(current))
} else {
currentProfile = yaml.parse(getProfileStr('default'))
}
}
return currentProfile
} }

View File

@ -33,13 +33,6 @@ export async function mihomoVersion(): Promise<IMihomoVersion> {
})) as IMihomoVersion })) as IMihomoVersion
} }
export const mihomoConfig = async (): Promise<IMihomoConfig> => {
const instance = await getAxios()
return (await instance.get('/configs').catch(() => {
return {}
})) as IMihomoConfig
}
export const patchMihomoConfig = async (patch: Partial<IMihomoConfig>): Promise<void> => { export const patchMihomoConfig = async (patch: Partial<IMihomoConfig>): Promise<void> => {
const instance = await getAxios() const instance = await getAxios()
return (await instance.patch('/configs', patch).catch((e) => { return (await instance.patch('/configs', patch).catch((e) => {

View File

@ -1,10 +1,12 @@
import { getControledMihomoConfig, getCurrentProfile } from '../config' import { getControledMihomoConfig, getProfileConfig, getProfile } from '../config'
import { mihomoWorkConfigPath } from '../utils/dirs' import { mihomoWorkConfigPath } from '../utils/dirs'
import yaml from 'yaml' import yaml from 'yaml'
import fs from 'fs' import fs from 'fs'
export function generateProfile(): void { export function generateProfile(): void {
const currentProfile = getCurrentProfile() const current = getProfileConfig().current
if (!current) return
const currentProfile = getProfile(current)
const controledMihomoConfig = getControledMihomoConfig() const controledMihomoConfig = getControledMihomoConfig()
const { tun: profileTun = {} } = currentProfile const { tun: profileTun = {} } = currentProfile
const { tun: controledTun } = controledMihomoConfig const { tun: controledTun } = controledMihomoConfig

View File

@ -3,7 +3,6 @@ import {
mihomoChangeProxy, mihomoChangeProxy,
mihomoCloseAllConnections, mihomoCloseAllConnections,
mihomoCloseConnection, mihomoCloseConnection,
mihomoConfig,
mihomoProxies, mihomoProxies,
mihomoProxyDelay, mihomoProxyDelay,
mihomoRules, mihomoRules,
@ -33,13 +32,12 @@ import {
import { isEncryptionAvailable, startCore } from '../core/manager' import { isEncryptionAvailable, startCore } from '../core/manager'
import { triggerSysProxy } from '../resolve/sysproxy' import { triggerSysProxy } from '../resolve/sysproxy'
import { checkUpdate } from '../resolve/autoUpdater' import { checkUpdate } from '../resolve/autoUpdater'
import { exePath, mihomoCorePath } from './dirs' import { exePath, mihomoCorePath, mihomoWorkConfigPath } from './dirs'
import { execSync } from 'child_process' import { execSync } from 'child_process'
import fs from 'fs' import fs from 'fs'
export function registerIpcMainHandlers(): void { export function registerIpcMainHandlers(): void {
ipcMain.handle('mihomoVersion', mihomoVersion) ipcMain.handle('mihomoVersion', mihomoVersion)
ipcMain.handle('mihomoConfig', mihomoConfig)
ipcMain.handle('mihomoCloseConnection', (_e, id) => mihomoCloseConnection(id)) ipcMain.handle('mihomoCloseConnection', (_e, id) => mihomoCloseConnection(id))
ipcMain.handle('mihomoCloseAllConnections', mihomoCloseAllConnections) ipcMain.handle('mihomoCloseAllConnections', mihomoCloseAllConnections)
ipcMain.handle('mihomoRules', mihomoRules) ipcMain.handle('mihomoRules', mihomoRules)
@ -73,6 +71,7 @@ export function registerIpcMainHandlers(): void {
ipcMain.handle('encryptString', (_e, str) => safeStorage.encryptString(str)) ipcMain.handle('encryptString', (_e, str) => safeStorage.encryptString(str))
ipcMain.handle('getFilePath', getFilePath) ipcMain.handle('getFilePath', getFilePath)
ipcMain.handle('readTextFile', (_e, filePath) => readTextFile(filePath)) ipcMain.handle('readTextFile', (_e, filePath) => readTextFile(filePath))
ipcMain.handle('getRuntimeConfig', getRuntimeConfig)
ipcMain.handle('checkUpdate', () => checkUpdate()) ipcMain.handle('checkUpdate', () => checkUpdate())
ipcMain.handle('getVersion', () => app.getVersion()) ipcMain.handle('getVersion', () => app.getVersion())
ipcMain.handle('platform', () => process.platform) ipcMain.handle('platform', () => process.platform)
@ -92,6 +91,10 @@ function readTextFile(filePath: string): string {
return fs.readFileSync(filePath, 'utf8') return fs.readFileSync(filePath, 'utf8')
} }
function getRuntimeConfig(): string {
return fs.readFileSync(mihomoWorkConfigPath(), 'utf8')
}
async function setupFirewall(): Promise<void> { async function setupFirewall(): Promise<void> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const removeCommand = ` const removeCommand = `

View File

@ -0,0 +1,67 @@
import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from '@nextui-org/react'
import React, { useEffect, useState } from 'react'
import MonacoEditor, { monaco } from 'react-monaco-editor'
import { useTheme } from 'next-themes'
import { getRuntimeConfig } from '@renderer/utils/ipc'
interface Props {
onClose: () => void
}
const ConfigViewer: React.FC<Props> = (props) => {
const { onClose } = props
const [currData, setCurrData] = useState('')
const { theme } = useTheme()
const editorDidMount = (editor: monaco.editor.IStandaloneCodeEditor): void => {
window.electron.ipcRenderer.on('resize', () => {
editor.layout()
})
}
const editorWillUnmount = (editor: monaco.editor.IStandaloneCodeEditor): void => {
window.electron.ipcRenderer.removeAllListeners('resize')
editor.dispose()
}
const getContent = async (): Promise<void> => {
setCurrData(await getRuntimeConfig())
}
useEffect(() => {
getContent()
}, [])
return (
<Modal size="5xl" hideCloseButton isOpen={true} scrollBehavior="inside">
<ModalContent className="h-full w-[calc(100%-100px)]">
<ModalHeader className="flex"></ModalHeader>
<ModalBody className="h-full">
<MonacoEditor
height="100%"
language="yaml"
value={currData}
theme={theme === 'dark' ? 'vs-dark' : 'vs'}
options={{
readOnly: true,
minimap: {
enabled: false
},
mouseWheelZoom: true,
fontFamily: `Fira Code, JetBrains Mono, Roboto Mono, "Source Code Pro", Consolas, Menlo, Monaco, monospace, "Courier New", "Apple Color Emoji"`,
fontLigatures: true, // 连字符
smoothScrolling: true // 平滑滚动
}}
editorDidMount={editorDidMount}
editorWillUnmount={editorWillUnmount}
/>
</ModalBody>
<ModalFooter>
<Button variant="light" onPress={onClose}>
</Button>
</ModalFooter>
</ModalContent>
</Modal>
)
}
export default ConfigViewer

View File

@ -2,11 +2,13 @@ import { Button, Card, CardBody, CardFooter, Chip, Progress } from '@nextui-org/
import { useProfileConfig } from '@renderer/hooks/use-profile-config' import { useProfileConfig } from '@renderer/hooks/use-profile-config'
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from 'react-router-dom'
import { calcTraffic, calcPercent } from '@renderer/utils/calc' import { calcTraffic, calcPercent } from '@renderer/utils/calc'
import { LiaGripfire } from 'react-icons/lia'
import { IoMdRefresh } from 'react-icons/io' import { IoMdRefresh } from 'react-icons/io'
import relativeTime from 'dayjs/plugin/relativeTime' import relativeTime from 'dayjs/plugin/relativeTime'
import 'dayjs/locale/zh-cn' import 'dayjs/locale/zh-cn'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { useState } from 'react' import { useState } from 'react'
import ConfigViewer from './config-viewer'
dayjs.extend(relativeTime) dayjs.extend(relativeTime)
dayjs.locale('zh-cn') dayjs.locale('zh-cn')
@ -16,6 +18,7 @@ const ProfileCard: React.FC = () => {
const location = useLocation() const location = useLocation()
const match = location.pathname.includes('/profiles') const match = location.pathname.includes('/profiles')
const [updating, setUpdating] = useState(false) const [updating, setUpdating] = useState(false)
const [showRuntimeConfig, setShowRuntimeConfig] = useState(false)
const { profileConfig, addProfileItem } = useProfileConfig() const { profileConfig, addProfileItem } = useProfileConfig()
const { current, items } = profileConfig ?? {} const { current, items } = profileConfig ?? {}
const info = items?.find((item) => item.id === current) ?? { const info = items?.find((item) => item.id === current) ?? {
@ -35,6 +38,7 @@ const ProfileCard: React.FC = () => {
isPressable isPressable
onPress={() => navigate('/profiles')} onPress={() => navigate('/profiles')}
> >
{showRuntimeConfig && <ConfigViewer onClose={() => setShowRuntimeConfig(false)} />}
<CardBody className="pb-1"> <CardBody className="pb-1">
<div className="flex justify-between h-[32px]"> <div className="flex justify-between h-[32px]">
<h3 <h3
@ -42,25 +46,39 @@ const ProfileCard: React.FC = () => {
> >
{info?.name} {info?.name}
</h3> </h3>
{info.type === 'remote' && ( <div>
<Button <Button
isIconOnly isIconOnly
size="sm" size="sm"
disabled={updating} title="查看当前运行时配置"
variant="light" variant="light"
color="default" color="default"
onPress={() => { onPress={() => {
setUpdating(true) setShowRuntimeConfig(true)
addProfileItem(info).finally(() => {
setUpdating(false)
})
}} }}
> >
<IoMdRefresh <LiaGripfire className={`text-[24px] ${match ? 'text-white' : 'text-foreground'}`} />
className={`text-[24px] ${match ? 'text-white' : 'text-foreground'} ${updating ? 'animate-spin' : ''}`}
/>
</Button> </Button>
)} {info.type === 'remote' && (
<Button
isIconOnly
size="sm"
disabled={updating}
variant="light"
color="default"
onPress={() => {
setUpdating(true)
addProfileItem(info).finally(() => {
setUpdating(false)
})
}}
>
<IoMdRefresh
className={`text-[24px] ${match ? 'text-white' : 'text-foreground'} ${updating ? 'animate-spin' : ''}`}
/>
</Button>
)}
</div>
</div> </div>
{info.type === 'remote' && ( {info.type === 'remote' && (
<div <div

View File

@ -2,10 +2,6 @@ export async function mihomoVersion(): Promise<IMihomoVersion> {
return await window.electron.ipcRenderer.invoke('mihomoVersion') return await window.electron.ipcRenderer.invoke('mihomoVersion')
} }
export async function mihomoConfig(): Promise<IMihomoConfig> {
return await window.electron.ipcRenderer.invoke('mihomoConfig')
}
export async function mihomoCloseConnection(id: string): Promise<void> { export async function mihomoCloseConnection(id: string): Promise<void> {
return await window.electron.ipcRenderer.invoke('mihomoCloseConnection', id) return await window.electron.ipcRenderer.invoke('mihomoCloseConnection', id)
} }
@ -139,6 +135,10 @@ export async function readTextFile(filePath: string): Promise<string> {
return await window.electron.ipcRenderer.invoke('readTextFile', filePath) return await window.electron.ipcRenderer.invoke('readTextFile', filePath)
} }
export async function getRuntimeConfig(): Promise<string> {
return await window.electron.ipcRenderer.invoke('getRuntimeConfig')
}
export async function checkUpdate(): Promise<string | undefined> { export async function checkUpdate(): Promise<string | undefined> {
return await window.electron.ipcRenderer.invoke('checkUpdate') return await window.electron.ipcRenderer.invoke('checkUpdate')
} }