mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-27 05:00:30 +08:00
support yaml override
This commit is contained in:
parent
6564b2bc58
commit
a46466600c
@ -48,9 +48,10 @@ export async function addOverrideItem(item: Partial<IOverrideItem>): Promise<voi
|
||||
|
||||
export async function removeOverrideItem(id: string): Promise<void> {
|
||||
const config = await getOverrideConfig()
|
||||
const item = await getOverrideItem(id)
|
||||
config.items = config.items?.filter((item) => item.id !== id)
|
||||
await setOverrideConfig(config)
|
||||
await rm(overridePath(id))
|
||||
await rm(overridePath(id, item?.ext || 'js'))
|
||||
}
|
||||
|
||||
export async function createOverride(item: Partial<IOverrideItem>): Promise<IOverrideItem> {
|
||||
@ -59,6 +60,7 @@ export async function createOverride(item: Partial<IOverrideItem>): Promise<IOve
|
||||
id,
|
||||
name: item.name || (item.type === 'remote' ? 'Remote File' : 'Local File'),
|
||||
type: item.type,
|
||||
ext: item.ext || 'js',
|
||||
url: item.url,
|
||||
updated: new Date().getTime()
|
||||
} as IOverrideItem
|
||||
@ -74,12 +76,12 @@ export async function createOverride(item: Partial<IOverrideItem>): Promise<IOve
|
||||
}
|
||||
})
|
||||
const data = res.data
|
||||
await setOverride(id, data)
|
||||
await setOverride(id, newItem.ext, data)
|
||||
break
|
||||
}
|
||||
case 'local': {
|
||||
const data = item.file || ''
|
||||
setOverride(id, data)
|
||||
setOverride(id, newItem.ext, data)
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -87,13 +89,13 @@ export async function createOverride(item: Partial<IOverrideItem>): Promise<IOve
|
||||
return newItem
|
||||
}
|
||||
|
||||
export async function getOverride(id: string): Promise<string> {
|
||||
if (!existsSync(overridePath(id))) {
|
||||
return `function main(config){ return config }`
|
||||
export async function getOverride(id: string, ext: 'js' | 'yaml'): Promise<string> {
|
||||
if (!existsSync(overridePath(id, ext))) {
|
||||
return ''
|
||||
}
|
||||
return await readFile(overridePath(id), 'utf-8')
|
||||
return await readFile(overridePath(id, ext), 'utf-8')
|
||||
}
|
||||
|
||||
export async function setOverride(id: string, content: string): Promise<void> {
|
||||
await writeFile(overridePath(id), content, 'utf-8')
|
||||
export async function setOverride(id: string, ext: 'js' | 'yaml', content: string): Promise<void> {
|
||||
await writeFile(overridePath(id, ext), content, 'utf-8')
|
||||
}
|
||||
|
||||
@ -3,7 +3,8 @@ import {
|
||||
getProfileConfig,
|
||||
getProfile,
|
||||
getProfileItem,
|
||||
getOverride
|
||||
getOverride,
|
||||
getOverrideItem
|
||||
} from '../config'
|
||||
import { mihomoWorkConfigPath } from '../utils/dirs'
|
||||
import yaml from 'yaml'
|
||||
@ -24,8 +25,16 @@ async function overrideProfile(
|
||||
): Promise<IMihomoConfig> {
|
||||
const { override = [] } = (await getProfileItem(current)) || {}
|
||||
for (const ov of override) {
|
||||
const script = await getOverride(ov)
|
||||
profile = runOverrideScript(profile, script)
|
||||
const item = await getOverrideItem(ov)
|
||||
const content = await getOverride(ov, item?.ext || 'js')
|
||||
switch (item?.ext) {
|
||||
case 'js':
|
||||
profile = runOverrideScript(profile, content)
|
||||
break
|
||||
case 'yaml':
|
||||
profile = deepMerge(profile, yaml.parse(content))
|
||||
break
|
||||
}
|
||||
}
|
||||
return profile
|
||||
}
|
||||
|
||||
@ -89,8 +89,8 @@ export function overrideConfigPath(): string {
|
||||
return path.join(dataDir(), 'override.yaml')
|
||||
}
|
||||
|
||||
export function overridePath(id: string): string {
|
||||
return path.join(overrideDir(), `${id}.js`)
|
||||
export function overridePath(id: string, ext: 'js' | 'yaml'): string {
|
||||
return path.join(overrideDir(), `${id}.${ext}`)
|
||||
}
|
||||
|
||||
export function mihomoWorkDir(): string {
|
||||
|
||||
@ -120,8 +120,8 @@ export function registerIpcMainHandlers(): void {
|
||||
ipcMain.handle('addOverrideItem', (_e, item) => ipcErrorWrapper(addOverrideItem)(item))
|
||||
ipcMain.handle('removeOverrideItem', (_e, id) => ipcErrorWrapper(removeOverrideItem)(id))
|
||||
ipcMain.handle('updateOverrideItem', (_e, item) => ipcErrorWrapper(updateOverrideItem)(item))
|
||||
ipcMain.handle('getOverride', (_e, id) => ipcErrorWrapper(getOverride)(id))
|
||||
ipcMain.handle('setOverride', (_e, id, str) => ipcErrorWrapper(setOverride)(id, str))
|
||||
ipcMain.handle('getOverride', (_e, id, ext) => ipcErrorWrapper(getOverride)(id, ext))
|
||||
ipcMain.handle('setOverride', (_e, id, ext, str) => ipcErrorWrapper(setOverride)(id, ext, str))
|
||||
ipcMain.handle('restartCore', ipcErrorWrapper(restartCore))
|
||||
ipcMain.handle('triggerSysProxy', (_e, enable) => ipcErrorWrapper(triggerSysProxy)(enable))
|
||||
ipcMain.handle('isEncryptionAvailable', isEncryptionAvailable)
|
||||
|
||||
@ -61,7 +61,7 @@ export const defaultControledMihomoConfig: Partial<IMihomoConfig> = {
|
||||
'use-hosts': false,
|
||||
'use-system-hosts': false,
|
||||
nameserver: ['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query'],
|
||||
'proxy-server-nameserver': ['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query'],
|
||||
'proxy-server-nameserver': ['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query']
|
||||
},
|
||||
sniffer: {
|
||||
enable: true,
|
||||
|
||||
@ -4,14 +4,15 @@ import { BaseEditor } from '../base/base-editor'
|
||||
import { getOverride, setOverride } from '@renderer/utils/ipc'
|
||||
interface Props {
|
||||
id: string
|
||||
language: 'javascript' | 'yaml'
|
||||
onClose: () => void
|
||||
}
|
||||
const EditFileModal: React.FC<Props> = (props) => {
|
||||
const { id, onClose } = props
|
||||
const { id, language, onClose } = props
|
||||
const [currData, setCurrData] = useState('')
|
||||
|
||||
const getContent = async (): Promise<void> => {
|
||||
setCurrData(await getOverride(id))
|
||||
setCurrData(await getOverride(id, language === 'javascript' ? 'js' : 'yaml'))
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@ -28,10 +29,12 @@ const EditFileModal: React.FC<Props> = (props) => {
|
||||
scrollBehavior="inside"
|
||||
>
|
||||
<ModalContent className="h-full w-[calc(100%-100px)]">
|
||||
<ModalHeader className="flex">编辑覆写脚本</ModalHeader>
|
||||
<ModalHeader className="flex">
|
||||
编辑覆写{language === 'javascript' ? '脚本' : '配置'}
|
||||
</ModalHeader>
|
||||
<ModalBody className="h-full">
|
||||
<BaseEditor
|
||||
language="javascript"
|
||||
language={language}
|
||||
value={currData}
|
||||
onChange={(value) => setCurrData(value)}
|
||||
/>
|
||||
@ -43,7 +46,7 @@ const EditFileModal: React.FC<Props> = (props) => {
|
||||
<Button
|
||||
color="primary"
|
||||
onPress={async () => {
|
||||
await setOverride(id, currData)
|
||||
await setOverride(id, language === 'javascript' ? 'js' : 'yaml', currData)
|
||||
onClose()
|
||||
}}
|
||||
>
|
||||
|
||||
@ -115,7 +115,13 @@ const OverrideItem: React.FC<Props> = (props) => {
|
||||
zIndex: isDragging ? 'calc(infinity)' : undefined
|
||||
}}
|
||||
>
|
||||
{openFile && <EditFileModal id={info.id} onClose={() => setOpenFile(false)} />}
|
||||
{openFile && (
|
||||
<EditFileModal
|
||||
id={info.id}
|
||||
language={info.ext === 'yaml' ? 'yaml' : 'javascript'}
|
||||
onClose={() => setOpenFile(false)}
|
||||
/>
|
||||
)}
|
||||
{openInfo && (
|
||||
<EditInfoModal
|
||||
item={info}
|
||||
@ -184,18 +190,21 @@ const OverrideItem: React.FC<Props> = (props) => {
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
{info.type === 'remote' && (
|
||||
<div className={`mt-2 flex justify-end`}>
|
||||
<small>{dayjs(info.updated).fromNow()}</small>
|
||||
</div>
|
||||
)}
|
||||
{info.type === 'local' && (
|
||||
<div className={`mt-2 flex justify-between`}>
|
||||
<div className="flex justify-between">
|
||||
<div className={`mt-2 flex justify-start`}>
|
||||
<Chip size="sm" variant="bordered">
|
||||
本地
|
||||
{info.type === 'local' ? '本地' : '远程'}
|
||||
</Chip>
|
||||
<Chip size="sm" variant="bordered" className="ml-2">
|
||||
{info.ext === 'yaml' ? 'YAML' : 'JavaScript'}
|
||||
</Chip>
|
||||
</div>
|
||||
)}
|
||||
{info.type === 'remote' && (
|
||||
<div className={`mt-2 flex justify-end`}>
|
||||
<small>{dayjs(info.updated).fromNow()}</small>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@ -2,6 +2,7 @@ import { Modal, ModalContent, ModalHeader, ModalBody, ModalFooter, Button } from
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { BaseEditor } from '../base/base-editor'
|
||||
import { getProfileStr, setProfileStr } from '@renderer/utils/ipc'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
interface Props {
|
||||
id: string
|
||||
onClose: () => void
|
||||
@ -9,6 +10,7 @@ interface Props {
|
||||
const EditFileModal: React.FC<Props> = (props) => {
|
||||
const { id, onClose } = props
|
||||
const [currData, setCurrData] = useState('')
|
||||
const navigate = useNavigate()
|
||||
|
||||
const getContent = async (): Promise<void> => {
|
||||
setCurrData(await getProfileStr(id))
|
||||
@ -28,7 +30,25 @@ const EditFileModal: React.FC<Props> = (props) => {
|
||||
scrollBehavior="inside"
|
||||
>
|
||||
<ModalContent className="h-full w-[calc(100%-100px)]">
|
||||
<ModalHeader className="flex">编辑订阅</ModalHeader>
|
||||
<ModalHeader className="flex">
|
||||
<div className="flex justify-start">
|
||||
<div className="flex items-center">编辑订阅</div>
|
||||
<small className="ml-2 text-default-500">
|
||||
注意:此处编辑配置更新订阅后会还原,如需要自定义配置请使用
|
||||
<Button
|
||||
size="sm"
|
||||
color="primary"
|
||||
variant="light"
|
||||
onPress={() => {
|
||||
navigate('/override')
|
||||
}}
|
||||
>
|
||||
覆写
|
||||
</Button>
|
||||
功能
|
||||
</small>
|
||||
</div>
|
||||
</ModalHeader>
|
||||
<ModalBody className="h-full">
|
||||
<BaseEditor language="yaml" value={currData} onChange={(value) => setCurrData(value)} />
|
||||
</ModalBody>
|
||||
|
||||
@ -92,14 +92,19 @@ const EditInfoModal: React.FC<Props> = (props) => {
|
||||
</SettingItem>
|
||||
</>
|
||||
)}
|
||||
<SettingItem title="覆写脚本">
|
||||
<SettingItem title="覆写">
|
||||
<Select
|
||||
className="w-[200px]"
|
||||
size="sm"
|
||||
selectionMode="multiple"
|
||||
selectedKeys={new Set(values.override || [])}
|
||||
onSelectionChange={(v) => {
|
||||
setValues({ ...values, override: Array.from(v).map((i) => i.toString()) })
|
||||
setValues({
|
||||
...values,
|
||||
override: Array.from(v)
|
||||
.map((i) => i.toString())
|
||||
.filter((i) => overrideItems.find((t) => t.id === i))
|
||||
})
|
||||
}}
|
||||
>
|
||||
{overrideItems.map((i) => (
|
||||
|
||||
@ -29,7 +29,10 @@ const DNS: React.FC = () => {
|
||||
'use-system-hosts': useSystemHosts = false,
|
||||
'respect-rules': respectRules = false,
|
||||
nameserver = ['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query'],
|
||||
'proxy-server-nameserver': proxyServerNameserver = ['https://doh.pub/dns-query', 'https://dns.alidns.com/dns-query'],
|
||||
'proxy-server-nameserver': proxyServerNameserver = [
|
||||
'https://doh.pub/dns-query',
|
||||
'https://dns.alidns.com/dns-query'
|
||||
]
|
||||
} = dns || {}
|
||||
|
||||
const [values, setValues] = useState({
|
||||
|
||||
@ -33,7 +33,12 @@ const Override: React.FC = () => {
|
||||
const handleImport = async (): Promise<void> => {
|
||||
setImporting(true)
|
||||
try {
|
||||
await addOverrideItem({ name: '', type: 'remote', url })
|
||||
await addOverrideItem({
|
||||
name: '',
|
||||
type: 'remote',
|
||||
url,
|
||||
ext: url.endsWith('.js') ? 'js' : 'yaml'
|
||||
})
|
||||
} finally {
|
||||
setImporting(false)
|
||||
}
|
||||
@ -71,10 +76,15 @@ const Override: React.FC = () => {
|
||||
event.stopPropagation()
|
||||
if (event.dataTransfer?.files) {
|
||||
const file = event.dataTransfer.files[0]
|
||||
if (file.name.endsWith('.js')) {
|
||||
if (file.name.endsWith('.js') || file.name.endsWith('.yaml')) {
|
||||
const content = await readTextFile(file.path)
|
||||
try {
|
||||
await addOverrideItem({ name: file.name, type: 'local', file: content })
|
||||
await addOverrideItem({
|
||||
name: file.name,
|
||||
type: 'local',
|
||||
file: content,
|
||||
ext: file.name.endsWith('.js') ? 'js' : 'yaml'
|
||||
})
|
||||
} finally {
|
||||
setFileOver(false)
|
||||
}
|
||||
@ -96,7 +106,7 @@ const Override: React.FC = () => {
|
||||
}, [items])
|
||||
|
||||
return (
|
||||
<BasePage ref={pageRef} title="覆写脚本">
|
||||
<BasePage ref={pageRef} title="覆写">
|
||||
<div className="sticky top-[48px] z-40 backdrop-blur bg-background/40 flex p-2">
|
||||
<Input
|
||||
variant="bordered"
|
||||
@ -133,11 +143,16 @@ const Override: React.FC = () => {
|
||||
color="primary"
|
||||
className="ml-2"
|
||||
onPress={() => {
|
||||
getFilePath(['js']).then(async (files) => {
|
||||
getFilePath(['js', 'yaml']).then(async (files) => {
|
||||
if (files?.length) {
|
||||
const content = await readTextFile(files[0])
|
||||
const fileName = files[0].split('/').pop()?.split('\\').pop()
|
||||
await addOverrideItem({ name: fileName, type: 'local', file: content })
|
||||
await addOverrideItem({
|
||||
name: fileName,
|
||||
type: 'local',
|
||||
file: content,
|
||||
ext: fileName?.endsWith('.js') ? 'js' : 'yaml'
|
||||
})
|
||||
}
|
||||
})
|
||||
}}
|
||||
|
||||
@ -181,12 +181,12 @@ export async function updateOverrideItem(item: IOverrideItem): Promise<void> {
|
||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('updateOverrideItem', item))
|
||||
}
|
||||
|
||||
export async function getOverride(id: string): Promise<string> {
|
||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getOverride', id))
|
||||
export async function getOverride(id: string, ext: 'js' | 'yaml'): Promise<string> {
|
||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getOverride', id, ext))
|
||||
}
|
||||
|
||||
export async function setOverride(id: string, str: string): Promise<void> {
|
||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setOverride', id, str))
|
||||
export async function setOverride(id: string, ext: 'js' | 'yaml', str: string): Promise<void> {
|
||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setOverride', id, ext, str))
|
||||
}
|
||||
|
||||
export async function restartCore(): Promise<void> {
|
||||
|
||||
1
src/shared/types.d.ts
vendored
1
src/shared/types.d.ts
vendored
@ -331,6 +331,7 @@ interface IProfileConfig {
|
||||
interface IOverrideItem {
|
||||
id: string
|
||||
type: 'remote' | 'local'
|
||||
ext: 'js' | 'yaml'
|
||||
name: string
|
||||
updated: number
|
||||
url?: string
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user