mirror of
https://github.com/pompurin404/mihomo-party.git
synced 2024-11-16 11:42:19 +08:00
grant core permition
This commit is contained in:
parent
454ce10a9d
commit
2e194e9d35
|
@ -7,13 +7,15 @@ import {
|
|||
mihomoWorkDir
|
||||
} from '../utils/dirs'
|
||||
import { generateProfile } from '../resolve/factory'
|
||||
import { getAppConfig } from '../config'
|
||||
import { getAppConfig, setAppConfig } from '../config'
|
||||
import { safeStorage } from 'electron'
|
||||
import fs from 'fs'
|
||||
|
||||
let child: ChildProcess
|
||||
|
||||
export function startCore(): void {
|
||||
const corePath = mihomoCorePath(getAppConfig().core ?? 'mihomo')
|
||||
grantCorePermition(corePath)
|
||||
generateProfile()
|
||||
checkProfile()
|
||||
stopCore()
|
||||
|
@ -51,3 +53,26 @@ export function checkProfile(): void {
|
|||
const corePath = mihomoCorePath(getAppConfig().core ?? 'mihomo')
|
||||
execFileSync(corePath, ['-t', '-f', mihomoWorkConfigPath(), '-d', mihomoTestDir()])
|
||||
}
|
||||
|
||||
export function grantCorePermition(corePath: string): void {
|
||||
if (getAppConfig().encryptedPassword && isEncryptionAvailable()) {
|
||||
const password = safeStorage.decryptString(Buffer.from(getAppConfig().encryptedPassword ?? []))
|
||||
try {
|
||||
if (process.platform === 'linux') {
|
||||
execSync(
|
||||
`echo "${password}" | sudo -S setcap cap_net_bind_service,cap_net_admin,cap_dac_override,cap_net_raw=+ep ${corePath}`
|
||||
)
|
||||
}
|
||||
if (process.platform === 'darwin') {
|
||||
execSync(`echo "${password}" | sudo -S chown root:admin ${corePath}`)
|
||||
execSync(`echo "${password}" | sudo -S chmod +sx ${corePath}`)
|
||||
}
|
||||
} catch (e) {
|
||||
setAppConfig({ encryptedPassword: undefined })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function isEncryptionAvailable(): boolean {
|
||||
return safeStorage.isEncryptionAvailable()
|
||||
}
|
||||
|
|
|
@ -23,8 +23,6 @@ if (!gotTheLock) {
|
|||
app.quit()
|
||||
} else {
|
||||
init()
|
||||
startCore()
|
||||
|
||||
app.on('second-instance', () => {
|
||||
window?.show()
|
||||
window?.focusOnWebView()
|
||||
|
@ -51,7 +49,7 @@ if (!gotTheLock) {
|
|||
app.whenReady().then(() => {
|
||||
// Set app user model id for windows
|
||||
electronApp.setAppUserModelId('party.mihomo.app')
|
||||
|
||||
startCore()
|
||||
// Default open or close DevTools by F12 in development
|
||||
// and ignore CommandOrControl + R in production.
|
||||
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
||||
|
|
|
@ -10,7 +10,6 @@ export function generateProfile(): void {
|
|||
const { tun: controledTun } = controledMihomoConfig
|
||||
const tun = Object.assign(profileTun, controledTun)
|
||||
const profile = Object.assign(currentProfile, controledMihomoConfig)
|
||||
console.log('profile', profile)
|
||||
profile.tun = tun
|
||||
fs.writeFileSync(mihomoWorkConfigPath(), yaml.stringify(profile))
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { app, ipcMain } from 'electron'
|
||||
import { app, ipcMain, safeStorage } from 'electron'
|
||||
import {
|
||||
mihomoChangeProxy,
|
||||
mihomoCloseAllConnections,
|
||||
|
@ -25,7 +25,7 @@ import {
|
|||
addProfileItem,
|
||||
removeProfileItem
|
||||
} from '../config'
|
||||
import { restartCore } from '../core/manager'
|
||||
import { isEncryptionAvailable, restartCore } from '../core/manager'
|
||||
import { triggerSysProxy } from '../resolve/sysproxy'
|
||||
import { changeCurrentProfile } from '../config/profile'
|
||||
|
||||
|
@ -57,5 +57,7 @@ export function registerIpcMainHandlers(): void {
|
|||
ipcMain.handle('removeProfileItem', (_e, id) => removeProfileItem(id))
|
||||
ipcMain.handle('restartCore', restartCore)
|
||||
ipcMain.handle('triggerSysProxy', (_e, enable) => triggerSysProxy(enable))
|
||||
ipcMain.handle('isEncryptionAvailable', isEncryptionAvailable)
|
||||
ipcMain.handle('encryptString', (_e, str) => safeStorage.encryptString(str))
|
||||
ipcMain.handle('quitApp', () => app.quit())
|
||||
}
|
||||
|
|
41
src/renderer/src/components/base/base-password-modal.tsx
Normal file
41
src/renderer/src/components/base/base-password-modal.tsx
Normal file
|
@ -0,0 +1,41 @@
|
|||
import {
|
||||
Modal,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
Button,
|
||||
Input
|
||||
} from '@nextui-org/react'
|
||||
import React, { useState } from 'react'
|
||||
|
||||
interface Props {
|
||||
onCancel: () => void
|
||||
onConfirm: (script: string) => void
|
||||
}
|
||||
|
||||
const BasePasswordModal: React.FC<Props> = (props) => {
|
||||
const { onCancel, onConfirm } = props
|
||||
const [password, setPassword] = useState('')
|
||||
|
||||
return (
|
||||
<Modal hideCloseButton isOpen={true}>
|
||||
<ModalContent>
|
||||
<ModalHeader className="flex">请输入root密码</ModalHeader>
|
||||
<ModalBody>
|
||||
<Input fullWidth type="password" value={password} onValueChange={setPassword} />
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant="light" onPress={onCancel}>
|
||||
取消
|
||||
</Button>
|
||||
<Button color="primary" onPress={() => onConfirm(password)}>
|
||||
确认
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default BasePasswordModal
|
|
@ -3,50 +3,73 @@ import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-c
|
|||
import BorderSwitch from '@renderer/components/base/border-swtich'
|
||||
import { TbDeviceIpadHorizontalBolt } from 'react-icons/tb'
|
||||
import { useLocation, useNavigate } from 'react-router-dom'
|
||||
import { patchMihomoConfig } from '@renderer/utils/ipc'
|
||||
import React from 'react'
|
||||
import { encryptString, patchMihomoConfig, isEncryptionAvailable } from '@renderer/utils/ipc'
|
||||
import React, { useState } from 'react'
|
||||
import { useAppConfig } from '@renderer/hooks/use-app-config'
|
||||
import BasePasswordModal from '../base/base-password-modal'
|
||||
|
||||
const TunSwitcher: React.FC = () => {
|
||||
const navigate = useNavigate()
|
||||
const location = useLocation()
|
||||
const match = location.pathname.includes('/tun')
|
||||
|
||||
const [openPasswordModal, setOpenPasswordModal] = useState(false)
|
||||
const { appConfig, patchAppConfig } = useAppConfig()
|
||||
const { controledMihomoConfig, patchControledMihomoConfig } = useControledMihomoConfig(true)
|
||||
const { tun } = controledMihomoConfig || {}
|
||||
const { enable } = tun || {}
|
||||
|
||||
const onChange = async (enable: boolean): Promise<void> => {
|
||||
const encryptionAvailable = await isEncryptionAvailable()
|
||||
if (!appConfig?.encryptedPassword && encryptionAvailable) {
|
||||
setOpenPasswordModal(true)
|
||||
return
|
||||
}
|
||||
if (!encryptionAvailable) {
|
||||
alert('加密不可用,请手动给内核授权')
|
||||
}
|
||||
await patchControledMihomoConfig({ tun: { enable } })
|
||||
await patchMihomoConfig({ tun: { enable } })
|
||||
}
|
||||
|
||||
return (
|
||||
<Card
|
||||
className={`w-[50%] ml-1 ${match ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/tun')}
|
||||
>
|
||||
<CardBody className="pb-1 pt-0 px-0">
|
||||
<div className="flex justify-between">
|
||||
<Button
|
||||
isIconOnly
|
||||
className="bg-transparent pointer-events-none"
|
||||
variant="flat"
|
||||
color="default"
|
||||
>
|
||||
<TbDeviceIpadHorizontalBolt color="default" className="text-[24px] font-bold" />
|
||||
</Button>
|
||||
<BorderSwitch
|
||||
isShowBorder={match && enable}
|
||||
isSelected={enable}
|
||||
onValueChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="pt-1">
|
||||
<h3 className="select-none text-md font-bold">虚拟网卡</h3>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
<>
|
||||
{openPasswordModal && (
|
||||
<BasePasswordModal
|
||||
onCancel={() => setOpenPasswordModal(false)}
|
||||
onConfirm={async (password: string) => {
|
||||
const encrypted = await encryptString(password)
|
||||
patchAppConfig({ encryptedPassword: encrypted })
|
||||
setOpenPasswordModal(false)
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Card
|
||||
className={`w-[50%] ml-1 ${match ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/tun')}
|
||||
>
|
||||
<CardBody className="pb-1 pt-0 px-0">
|
||||
<div className="flex justify-between">
|
||||
<Button
|
||||
isIconOnly
|
||||
className="bg-transparent pointer-events-none"
|
||||
variant="flat"
|
||||
color="default"
|
||||
>
|
||||
<TbDeviceIpadHorizontalBolt color="default" className="text-[24px] font-bold" />
|
||||
</Button>
|
||||
<BorderSwitch
|
||||
isShowBorder={match && enable}
|
||||
isSelected={enable}
|
||||
onValueChange={onChange}
|
||||
/>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="pt-1">
|
||||
<h3 className="select-none text-md font-bold">虚拟网卡</h3>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -106,6 +106,13 @@ export async function triggerSysProxy(enable: boolean): Promise<void> {
|
|||
return await window.electron.ipcRenderer.invoke('triggerSysProxy', enable)
|
||||
}
|
||||
|
||||
export async function isEncryptionAvailable(): Promise<boolean> {
|
||||
return await window.electron.ipcRenderer.invoke('isEncryptionAvailable')
|
||||
}
|
||||
|
||||
export async function encryptString(str: string): Promise<Buffer> {
|
||||
return await window.electron.ipcRenderer.invoke('encryptString', str)
|
||||
}
|
||||
export async function quitApp(): Promise<void> {
|
||||
return await window.electron.ipcRenderer.invoke('quitApp')
|
||||
}
|
||||
|
|
1
src/shared/types.d.ts
vendored
1
src/shared/types.d.ts
vendored
|
@ -136,6 +136,7 @@ interface IAppConfig {
|
|||
userAgent?: string
|
||||
delayTestUrl?: string
|
||||
delayTestTimeout?: number
|
||||
encryptedPassword?: Buffer
|
||||
}
|
||||
|
||||
interface IMihomoTunConfig {
|
||||
|
|
Loading…
Reference in New Issue
Block a user