mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-27 05:00:30 +08:00
feat: request admin privileges when enabling TUN
This commit is contained in:
parent
5c1d30b454
commit
e27ddbd16e
@ -350,6 +350,60 @@ export async function grantTunPermissions(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Windows 管理员权限
|
||||||
|
|
||||||
|
export async function checkAdminPrivileges(): Promise<boolean> {
|
||||||
|
if (process.platform !== 'win32') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const execPromise = promisify(exec)
|
||||||
|
await execPromise('net session')
|
||||||
|
return true
|
||||||
|
} catch {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function requestAdminPrivileges(): Promise<void> {
|
||||||
|
if (process.platform !== 'win32') {
|
||||||
|
throw new Error('This function is only available on Windows')
|
||||||
|
}
|
||||||
|
|
||||||
|
const execPromise = promisify(exec)
|
||||||
|
const exePath = process.execPath
|
||||||
|
const args = process.argv.slice(1)
|
||||||
|
|
||||||
|
try {
|
||||||
|
const escapedExePath = exePath.replace(/'/g, "''")
|
||||||
|
const escapedArgs = args.map(arg => `'${arg.replace(/'/g, "''")}'`).join(', ')
|
||||||
|
|
||||||
|
let command: string
|
||||||
|
if (args.length > 0) {
|
||||||
|
command = `powershell -WindowStyle Hidden -Command "Start-Process -FilePath '${escapedExePath}' -ArgumentList ${escapedArgs} -Verb RunAs"`
|
||||||
|
} else {
|
||||||
|
command = `powershell -WindowStyle Hidden -Command "Start-Process -FilePath '${escapedExePath}' -Verb RunAs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Requesting admin privileges with command:', command)
|
||||||
|
|
||||||
|
await execPromise(command, { windowsHide: true })
|
||||||
|
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 1000))
|
||||||
|
|
||||||
|
const { app } = await import('electron')
|
||||||
|
app.quit()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Admin privilege request failed:', error)
|
||||||
|
throw new Error(`Failed to request admin privileges: ${error}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function manualGrantCorePermition(): Promise<void> {
|
||||||
|
return grantTunPermissions()
|
||||||
|
}
|
||||||
|
|
||||||
export async function getDefaultDevice(): Promise<string> {
|
export async function getDefaultDevice(): Promise<string> {
|
||||||
const execPromise = promisify(exec)
|
const execPromise = promisify(exec)
|
||||||
const { stdout: deviceOut } = await execPromise(`route -n get default`)
|
const { stdout: deviceOut } = await execPromise(`route -n get default`)
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import { mainWindow, showMainWindow, triggerMainWindow } from '..'
|
|||||||
import { app, clipboard, ipcMain, Menu, nativeImage, shell, Tray } from 'electron'
|
import { app, clipboard, ipcMain, Menu, nativeImage, shell, Tray } from 'electron'
|
||||||
import { dataDir, logDir, mihomoCoreDir, mihomoWorkDir } from '../utils/dirs'
|
import { dataDir, logDir, mihomoCoreDir, mihomoWorkDir } from '../utils/dirs'
|
||||||
import { triggerSysProxy } from '../sys/sysproxy'
|
import { triggerSysProxy } from '../sys/sysproxy'
|
||||||
import { quitWithoutCore, restartCore, checkTunPermissions, grantTunPermissions } from '../core/manager'
|
import { quitWithoutCore, restartCore, checkTunPermissions, grantTunPermissions, checkAdminPrivileges, requestAdminPrivileges } from '../core/manager'
|
||||||
import { floatingWindow, triggerFloatingWindow } from './floatingWindow'
|
import { floatingWindow, triggerFloatingWindow } from './floatingWindow'
|
||||||
import { t } from 'i18next'
|
import { t } from 'i18next'
|
||||||
|
|
||||||
@ -178,7 +178,27 @@ export const buildContextMenu = async (): Promise<Menu> => {
|
|||||||
const enable = item.checked
|
const enable = item.checked
|
||||||
try {
|
try {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
// 检查TUN权限
|
// Windows 管理员权限
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
try {
|
||||||
|
const isAdmin = await checkAdminPrivileges()
|
||||||
|
if (!isAdmin) {
|
||||||
|
try {
|
||||||
|
await requestAdminPrivileges()
|
||||||
|
return
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to request admin privileges:', error)
|
||||||
|
item.checked = false
|
||||||
|
ipcMain.emit('updateTrayMenu')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Admin check failed:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform !== 'win32') {
|
||||||
try {
|
try {
|
||||||
const hasPermissions = await checkTunPermissions()
|
const hasPermissions = await checkTunPermissions()
|
||||||
if (!hasPermissions) {
|
if (!hasPermissions) {
|
||||||
@ -194,6 +214,7 @@ export const buildContextMenu = async (): Promise<Menu> => {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Permission check failed:', error)
|
console.warn('Permission check failed:', error)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await patchControledMihomoConfig({ tun: { enable }, dns: { enable: true } })
|
await patchControledMihomoConfig({ tun: { enable }, dns: { enable: true } })
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -60,7 +60,10 @@ import {
|
|||||||
quitWithoutCore,
|
quitWithoutCore,
|
||||||
restartCore,
|
restartCore,
|
||||||
checkTunPermissions,
|
checkTunPermissions,
|
||||||
grantTunPermissions
|
grantTunPermissions,
|
||||||
|
manualGrantCorePermition,
|
||||||
|
checkAdminPrivileges,
|
||||||
|
requestAdminPrivileges
|
||||||
} from '../core/manager'
|
} from '../core/manager'
|
||||||
import { triggerSysProxy } from '../sys/sysproxy'
|
import { triggerSysProxy } from '../sys/sysproxy'
|
||||||
import { checkUpdate, downloadAndInstallUpdate } from '../resolve/autoUpdater'
|
import { checkUpdate, downloadAndInstallUpdate } from '../resolve/autoUpdater'
|
||||||
@ -190,6 +193,9 @@ export function registerIpcMainHandlers(): void {
|
|||||||
ipcMain.handle('restartCore', ipcErrorWrapper(restartCore))
|
ipcMain.handle('restartCore', ipcErrorWrapper(restartCore))
|
||||||
ipcMain.handle('startMonitor', (_e, detached) => ipcErrorWrapper(startMonitor)(detached))
|
ipcMain.handle('startMonitor', (_e, detached) => ipcErrorWrapper(startMonitor)(detached))
|
||||||
ipcMain.handle('triggerSysProxy', (_e, enable) => ipcErrorWrapper(triggerSysProxy)(enable))
|
ipcMain.handle('triggerSysProxy', (_e, enable) => ipcErrorWrapper(triggerSysProxy)(enable))
|
||||||
|
ipcMain.handle('manualGrantCorePermition', () => ipcErrorWrapper(manualGrantCorePermition)())
|
||||||
|
ipcMain.handle('checkAdminPrivileges', () => ipcErrorWrapper(checkAdminPrivileges)())
|
||||||
|
ipcMain.handle('requestAdminPrivileges', () => ipcErrorWrapper(requestAdminPrivileges)())
|
||||||
|
|
||||||
ipcMain.handle('checkTunPermissions', () => ipcErrorWrapper(checkTunPermissions)())
|
ipcMain.handle('checkTunPermissions', () => ipcErrorWrapper(checkTunPermissions)())
|
||||||
ipcMain.handle('grantTunPermissions', () => ipcErrorWrapper(grantTunPermissions)())
|
ipcMain.handle('grantTunPermissions', () => ipcErrorWrapper(grantTunPermissions)())
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { useControledMihomoConfig } from '@renderer/hooks/use-controled-mihomo-c
|
|||||||
import BorderSwitch from '@renderer/components/base/border-swtich'
|
import BorderSwitch from '@renderer/components/base/border-swtich'
|
||||||
import { TbDeviceIpadHorizontalBolt } from 'react-icons/tb'
|
import { TbDeviceIpadHorizontalBolt } from 'react-icons/tb'
|
||||||
import { useLocation, useNavigate } from 'react-router-dom'
|
import { useLocation, useNavigate } from 'react-router-dom'
|
||||||
import { restartCore, checkTunPermissions, grantTunPermissions } from '@renderer/utils/ipc'
|
import { restartCore } from '@renderer/utils/ipc'
|
||||||
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 React from 'react'
|
import React from 'react'
|
||||||
@ -38,15 +38,21 @@ const TunSwitcher: React.FC<Props> = (props) => {
|
|||||||
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
|
const transform = tf ? { x: tf.x, y: tf.y, scaleX: 1, scaleY: 1 } : null
|
||||||
const onChange = async (enable: boolean): Promise<void> => {
|
const onChange = async (enable: boolean): Promise<void> => {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
// 检查TUN权限
|
// Windows下检查管理员权限
|
||||||
|
if (window.electron.process.platform === 'win32') {
|
||||||
try {
|
try {
|
||||||
const hasPermissions = await checkTunPermissions()
|
const isAdmin = await window.electron.ipcRenderer.invoke('checkAdminPrivileges')
|
||||||
if (!hasPermissions) {
|
if (!isAdmin) {
|
||||||
const confirmed = confirm(t('tun.permissions.required'))
|
const confirmed = confirm(t('tun.permissions.required'))
|
||||||
if (confirmed) {
|
if (confirmed) {
|
||||||
try {
|
try {
|
||||||
await grantTunPermissions()
|
const notification = new Notification(t('tun.permissions.requesting'))
|
||||||
|
|
||||||
|
await window.electron.ipcRenderer.invoke('requestAdminPrivileges')
|
||||||
|
notification.close()
|
||||||
|
return
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error('Failed to request admin privileges:', error)
|
||||||
alert(t('tun.permissions.failed') + ': ' + error)
|
alert(t('tun.permissions.failed') + ': ' + error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -55,7 +61,17 @@ const TunSwitcher: React.FC<Props> = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('Permission check failed:', error)
|
console.warn('Admin check failed:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// macOS/Linux 获取权限
|
||||||
|
if (window.electron.process.platform !== 'win32') {
|
||||||
|
try {
|
||||||
|
await window.electron.ipcRenderer.invoke('manualGrantCorePermition')
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Permission grant failed:', error)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await patchControledMihomoConfig({ tun: { enable }, dns: { enable: true } })
|
await patchControledMihomoConfig({ tun: { enable }, dns: { enable: true } })
|
||||||
|
|||||||
@ -318,6 +318,8 @@
|
|||||||
"tun.error.tunPermissionDenied": "TUN interface start failed, please try to manually grant core permissions",
|
"tun.error.tunPermissionDenied": "TUN interface start failed, please try to manually grant core permissions",
|
||||||
"tun.permissions.required": "TUN mode requires administrator privileges. Grant permissions now?",
|
"tun.permissions.required": "TUN mode requires administrator privileges. Grant permissions now?",
|
||||||
"tun.permissions.failed": "Permission authorization failed",
|
"tun.permissions.failed": "Permission authorization failed",
|
||||||
|
"tun.permissions.windowsRestart": "On Windows, you need to restart the application as administrator to use TUN mode",
|
||||||
|
"tun.permissions.requesting": "Requesting administrator privileges, please click 'Yes' in the UAC dialog...",
|
||||||
"dns.title": "DNS Settings",
|
"dns.title": "DNS Settings",
|
||||||
"dns.enable": "Enable DNS",
|
"dns.enable": "Enable DNS",
|
||||||
"dns.enhancedMode.title": "Domain Mapping Mode",
|
"dns.enhancedMode.title": "Domain Mapping Mode",
|
||||||
|
|||||||
@ -318,6 +318,8 @@
|
|||||||
"tun.error.tunPermissionDenied": "虚拟网卡启动失败,请尝试手动授予内核权限",
|
"tun.error.tunPermissionDenied": "虚拟网卡启动失败,请尝试手动授予内核权限",
|
||||||
"tun.permissions.required": "启用TUN模式需要管理员权限,是否现在授权?",
|
"tun.permissions.required": "启用TUN模式需要管理员权限,是否现在授权?",
|
||||||
"tun.permissions.failed": "权限授权失败",
|
"tun.permissions.failed": "权限授权失败",
|
||||||
|
"tun.permissions.windowsRestart": "Windows下需要以管理员身份重新启动应用才能使用TUN模式",
|
||||||
|
"tun.permissions.requesting": "正在请求管理员权限,请在UAC对话框中点击'是'...",
|
||||||
"dns.title": "DNS 设置",
|
"dns.title": "DNS 设置",
|
||||||
"dns.enable": "启用 DNS",
|
"dns.enable": "启用 DNS",
|
||||||
"dns.enhancedMode.title": "域名映射模式",
|
"dns.enhancedMode.title": "域名映射模式",
|
||||||
|
|||||||
@ -237,6 +237,10 @@ export async function grantTunPermissions(): Promise<void> {
|
|||||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('grantTunPermissions'))
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('grantTunPermissions'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function manualGrantCorePermition(): Promise<void> {
|
||||||
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('manualGrantCorePermition'))
|
||||||
|
}
|
||||||
|
|
||||||
export async function getFilePath(ext: string[]): Promise<string[] | undefined> {
|
export async function getFilePath(ext: string[]): Promise<string[] | undefined> {
|
||||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getFilePath', ext))
|
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('getFilePath', ext))
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user