mirror of
https://github.com/pompurin404/mihomo-party.git
synced 2024-11-16 03:32:17 +08:00
use nodejs vm and get script log
This commit is contained in:
parent
82d42be8d5
commit
1e2b0b92cc
|
@ -89,7 +89,7 @@ export async function createOverride(item: Partial<IOverrideItem>): Promise<IOve
|
||||||
return newItem
|
return newItem
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOverride(id: string, ext: 'js' | 'yaml'): Promise<string> {
|
export async function getOverride(id: string, ext: 'js' | 'yaml' | 'log'): Promise<string> {
|
||||||
if (!existsSync(overridePath(id, ext))) {
|
if (!existsSync(overridePath(id, ext))) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,12 @@ import {
|
||||||
getOverride,
|
getOverride,
|
||||||
getOverrideItem
|
getOverrideItem
|
||||||
} from '../config'
|
} from '../config'
|
||||||
import { mihomoWorkConfigPath } from '../utils/dirs'
|
import { mihomoWorkConfigPath, overridePath } from '../utils/dirs'
|
||||||
import yaml from 'yaml'
|
import yaml from 'yaml'
|
||||||
import { readFile, writeFile } from 'fs/promises'
|
import { readFile, writeFile } from 'fs/promises'
|
||||||
import { deepMerge } from '../utils/merge'
|
import { deepMerge } from '../utils/merge'
|
||||||
|
import vm from 'vm'
|
||||||
|
import { writeFileSync } from 'fs'
|
||||||
|
|
||||||
export async function generateProfile(): Promise<void> {
|
export async function generateProfile(): Promise<void> {
|
||||||
const { current } = await getProfileConfig()
|
const { current } = await getProfileConfig()
|
||||||
|
@ -29,7 +31,7 @@ async function overrideProfile(
|
||||||
const content = await getOverride(ov, item?.ext || 'js')
|
const content = await getOverride(ov, item?.ext || 'js')
|
||||||
switch (item?.ext) {
|
switch (item?.ext) {
|
||||||
case 'js':
|
case 'js':
|
||||||
profile = runOverrideScript(profile, content)
|
profile = runOverrideScript(profile, content, item)
|
||||||
break
|
break
|
||||||
case 'yaml': {
|
case 'yaml': {
|
||||||
const patch = yaml.parse(content)
|
const patch = yaml.parse(content)
|
||||||
|
@ -47,13 +49,45 @@ async function overrideProfile(
|
||||||
return profile
|
return profile
|
||||||
}
|
}
|
||||||
|
|
||||||
function runOverrideScript(profile: IMihomoConfig, script: string): IMihomoConfig {
|
function runOverrideScript(
|
||||||
|
profile: IMihomoConfig,
|
||||||
|
script: string,
|
||||||
|
item: IOverrideItem
|
||||||
|
): IMihomoConfig {
|
||||||
|
const log = (type: string, data: string, flag = 'a'): void => {
|
||||||
|
writeFileSync(overridePath(item.id, 'log'), `[${type}] ${data}\n`, {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
flag
|
||||||
|
})
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const func = eval(`${script} main`)
|
const ctx = {
|
||||||
const newProfile = func(profile)
|
console: Object.freeze({
|
||||||
if (typeof newProfile !== 'object') return profile
|
log(data: never) {
|
||||||
|
log('log', JSON.stringify(data))
|
||||||
|
},
|
||||||
|
info(data: never) {
|
||||||
|
log('info', JSON.stringify(data))
|
||||||
|
},
|
||||||
|
error(data: never) {
|
||||||
|
log('error', JSON.stringify(data))
|
||||||
|
},
|
||||||
|
debug(data: never) {
|
||||||
|
log('debug', JSON.stringify(data))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
vm.createContext(ctx)
|
||||||
|
const code = `${script} main(${JSON.stringify(profile)})`
|
||||||
|
log('info', '开始执行脚本', 'w')
|
||||||
|
const newProfile = vm.runInContext(code, ctx)
|
||||||
|
if (typeof newProfile !== 'object') {
|
||||||
|
throw new Error('脚本返回值必须是对象')
|
||||||
|
}
|
||||||
|
log('info', '脚本执行成功')
|
||||||
return newProfile
|
return newProfile
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
log('exception', `脚本执行失败: ${e}`)
|
||||||
return profile
|
return profile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,7 +89,7 @@ export function overrideConfigPath(): string {
|
||||||
return path.join(dataDir(), 'override.yaml')
|
return path.join(dataDir(), 'override.yaml')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function overridePath(id: string, ext: 'js' | 'yaml'): string {
|
export function overridePath(id: string, ext: 'js' | 'yaml' | 'log'): string {
|
||||||
return path.join(overrideDir(), `${id}.${ext}`)
|
return path.join(overrideDir(), `${id}.${ext}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
58
src/renderer/src/components/override/exec-log-modal.tsx
Normal file
58
src/renderer/src/components/override/exec-log-modal.tsx
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import {
|
||||||
|
Modal,
|
||||||
|
ModalContent,
|
||||||
|
ModalHeader,
|
||||||
|
ModalBody,
|
||||||
|
ModalFooter,
|
||||||
|
Button,
|
||||||
|
Divider
|
||||||
|
} from '@nextui-org/react'
|
||||||
|
import React, { useEffect, useState } from 'react'
|
||||||
|
import { getOverride } from '@renderer/utils/ipc'
|
||||||
|
interface Props {
|
||||||
|
id: string
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
const ExecLogModal: React.FC<Props> = (props) => {
|
||||||
|
const { id, onClose } = props
|
||||||
|
const [logs, setLogs] = useState<string[]>([])
|
||||||
|
|
||||||
|
const getLog = async (): Promise<void> => {
|
||||||
|
setLogs((await getOverride(id, 'log')).split('\n').filter(Boolean))
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getLog()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
backdrop="blur"
|
||||||
|
hideCloseButton
|
||||||
|
isOpen={true}
|
||||||
|
onOpenChange={onClose}
|
||||||
|
scrollBehavior="inside"
|
||||||
|
>
|
||||||
|
<ModalContent>
|
||||||
|
<ModalHeader className="flex">执行日志</ModalHeader>
|
||||||
|
<ModalBody>
|
||||||
|
{logs.map((log) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<small className="break-all">{log}</small>
|
||||||
|
<Divider />
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</ModalBody>
|
||||||
|
<ModalFooter>
|
||||||
|
<Button variant="light" onPress={onClose}>
|
||||||
|
关闭
|
||||||
|
</Button>
|
||||||
|
</ModalFooter>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ExecLogModal
|
|
@ -15,6 +15,7 @@ import EditFileModal from './edit-file-modal'
|
||||||
import EditInfoModal from './edit-info-modal'
|
import EditInfoModal from './edit-info-modal'
|
||||||
import { useSortable } from '@dnd-kit/sortable'
|
import { useSortable } from '@dnd-kit/sortable'
|
||||||
import { CSS } from '@dnd-kit/utilities'
|
import { CSS } from '@dnd-kit/utilities'
|
||||||
|
import ExecLogModal from './exec-log-modal'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
info: IOverrideItem
|
info: IOverrideItem
|
||||||
|
@ -43,6 +44,13 @@ const menuItems: MenuItem[] = [
|
||||||
{
|
{
|
||||||
key: 'edit-file',
|
key: 'edit-file',
|
||||||
label: '编辑文件',
|
label: '编辑文件',
|
||||||
|
showDivider: false,
|
||||||
|
color: 'default',
|
||||||
|
className: ''
|
||||||
|
} as MenuItem,
|
||||||
|
{
|
||||||
|
key: 'exec-log',
|
||||||
|
label: '执行日志',
|
||||||
showDivider: true,
|
showDivider: true,
|
||||||
color: 'default',
|
color: 'default',
|
||||||
className: ''
|
className: ''
|
||||||
|
@ -62,6 +70,7 @@ const OverrideItem: React.FC<Props> = (props) => {
|
||||||
const [updating, setUpdating] = useState(false)
|
const [updating, setUpdating] = useState(false)
|
||||||
const [openInfo, setOpenInfo] = useState(false)
|
const [openInfo, setOpenInfo] = useState(false)
|
||||||
const [openFile, setOpenFile] = useState(false)
|
const [openFile, setOpenFile] = useState(false)
|
||||||
|
const [openLog, setOpenLog] = useState(false)
|
||||||
const {
|
const {
|
||||||
attributes,
|
attributes,
|
||||||
listeners,
|
listeners,
|
||||||
|
@ -85,6 +94,10 @@ const OverrideItem: React.FC<Props> = (props) => {
|
||||||
setOpenFile(true)
|
setOpenFile(true)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
case 'exec-log': {
|
||||||
|
setOpenLog(true)
|
||||||
|
break
|
||||||
|
}
|
||||||
case 'delete': {
|
case 'delete': {
|
||||||
removeOverrideItem(info.id)
|
removeOverrideItem(info.id)
|
||||||
mutateOverrideConfig()
|
mutateOverrideConfig()
|
||||||
|
@ -129,6 +142,7 @@ const OverrideItem: React.FC<Props> = (props) => {
|
||||||
updateOverrideItem={updateOverrideItem}
|
updateOverrideItem={updateOverrideItem}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{openLog && <ExecLogModal id={info.id} onClose={() => setOpenLog(false)} />}
|
||||||
<Card
|
<Card
|
||||||
fullWidth
|
fullWidth
|
||||||
isPressable
|
isPressable
|
||||||
|
|
|
@ -181,7 +181,7 @@ export async function updateOverrideItem(item: IOverrideItem): Promise<void> {
|
||||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('updateOverrideItem', item))
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('updateOverrideItem', item))
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getOverride(id: string, ext: 'js' | 'yaml'): Promise<string> {
|
export async function getOverride(id: string, ext: 'js' | 'yaml' | 'log'): Promise<string> {
|
||||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getOverride', id, ext))
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getOverride', id, ext))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user