mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-26 20:50:30 +08:00
fix format and lint
This commit is contained in:
parent
b7d6ea8e7a
commit
54bb819e28
@ -1,4 +0,0 @@
|
||||
node_modules
|
||||
dist
|
||||
out
|
||||
.gitignore
|
||||
@ -1,9 +0,0 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react/jsx-runtime',
|
||||
'@electron-toolkit/eslint-config-ts/recommended',
|
||||
'@electron-toolkit/eslint-config-prettier'
|
||||
]
|
||||
}
|
||||
48
eslint.config.cjs
Normal file
48
eslint.config.cjs
Normal file
@ -0,0 +1,48 @@
|
||||
const js = require('@eslint/js')
|
||||
const react = require('eslint-plugin-react')
|
||||
const { configs } = require('@electron-toolkit/eslint-config-ts')
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
ignores: ['**/node_modules/**', '**/dist/**', '**/out/**', '**/extra/**']
|
||||
},
|
||||
|
||||
js.configs.recommended,
|
||||
...configs.recommended,
|
||||
|
||||
{
|
||||
files: ['**/*.{js,jsx,ts,tsx}'],
|
||||
plugins: {
|
||||
react: react
|
||||
},
|
||||
rules: {
|
||||
...react.configs.recommended.rules,
|
||||
...react.configs['jsx-runtime'].rules
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect'
|
||||
}
|
||||
},
|
||||
languageOptions: {
|
||||
...react.configs.recommended.languageOptions
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
files: ['**/*.cjs', '**/*.mjs', '**/tailwind.config.js', '**/postcss.config.js'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off'
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': 0,
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'warn'
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/explicit-function-return-type */
|
||||
import fs from 'fs'
|
||||
import AdmZip from 'adm-zip'
|
||||
import path from 'path'
|
||||
|
||||
@ -9,9 +9,7 @@ import {
|
||||
} from './version-utils.mjs'
|
||||
|
||||
const chat_id = '@MihomoPartyChannel'
|
||||
const pkg = readFileSync('package.json', 'utf-8')
|
||||
const changelog = readFileSync('changelog.md', 'utf-8')
|
||||
const { version: packageVersion } = JSON.parse(pkg)
|
||||
|
||||
// 获取处理后的版本号
|
||||
const version = getProcessedVersion()
|
||||
|
||||
@ -7,9 +7,7 @@ import {
|
||||
generateDownloadLinksMarkdown
|
||||
} from './version-utils.mjs'
|
||||
|
||||
const pkg = readFileSync('package.json', 'utf-8')
|
||||
let changelog = readFileSync('changelog.md', 'utf-8')
|
||||
const { version: packageVersion } = JSON.parse(pkg)
|
||||
|
||||
// 获取处理后的版本号
|
||||
const version = getProcessedVersion()
|
||||
|
||||
@ -49,7 +49,7 @@ export async function patchControledMihomoConfig(patch: Partial<IMihomoConfig>):
|
||||
|
||||
// 从不接管状态恢复
|
||||
if (controlDns) {
|
||||
// 确保DNS配置包含所有必要的默认字段,特别是新增的fallback等
|
||||
// 确保 DNS 配置包含所有必要的默认字段,特别是新增的 fallback 等
|
||||
controledMihomoConfig.dns = deepMerge(
|
||||
defaultControledMihomoConfig.dns || {},
|
||||
controledMihomoConfig.dns || {}
|
||||
|
||||
@ -78,7 +78,7 @@ export async function createOverride(item: Partial<IOverrideItem>): Promise<IOve
|
||||
},
|
||||
responseType: 'text'
|
||||
})
|
||||
const data = res.data
|
||||
const data = res.data as string
|
||||
await setOverride(id, newItem.ext, data)
|
||||
break
|
||||
}
|
||||
|
||||
@ -418,7 +418,9 @@ export async function convertMrsRuleset(filePath: string, behavior: string): Pro
|
||||
} catch (error) {
|
||||
try {
|
||||
await unlink(tempFilePath)
|
||||
} catch {}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ let runtimeConfig: IMihomoConfig
|
||||
// 辅助函数:处理带偏移量的规则
|
||||
function processRulesWithOffset(ruleStrings: string[], currentRules: string[], isAppend = false) {
|
||||
const normalRules: string[] = []
|
||||
let rules = [...currentRules]
|
||||
const rules = [...currentRules]
|
||||
|
||||
ruleStrings.forEach((ruleStr) => {
|
||||
const parts = ruleStr.split(',')
|
||||
@ -65,7 +65,7 @@ export async function generateProfile(): Promise<void> {
|
||||
controlSniff = true,
|
||||
useNameserverPolicy
|
||||
} = await getAppConfig()
|
||||
let currentProfile = await overrideProfile(current, await getProfile(current))
|
||||
const currentProfile = await overrideProfile(current, await getProfile(current))
|
||||
let controledMihomoConfig = await getControledMihomoConfig()
|
||||
|
||||
// 根据开关状态过滤控制配置
|
||||
@ -132,7 +132,7 @@ export async function generateProfile(): Promise<void> {
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('读取或应用规则文件时出错:', error)
|
||||
console.error('读取或应用规则文件时出错:', error)
|
||||
}
|
||||
|
||||
const profile = deepMerge(currentProfile, controledMihomoConfig)
|
||||
|
||||
@ -330,8 +330,8 @@ async function cleanupWindowsNamedPipes(): Promise<void> {
|
||||
process.kill(pid, 0)
|
||||
process.kill(pid, 'SIGTERM')
|
||||
await managerLogger.info(`Terminated process ${pid} to free pipe`)
|
||||
} catch (error: any) {
|
||||
if (error.code !== 'ESRCH') {
|
||||
} catch (error: unknown) {
|
||||
if ((error as { code?: string })?.code !== 'ESRCH') {
|
||||
await managerLogger.warn(`Failed to terminate process ${pid}:`, error)
|
||||
}
|
||||
}
|
||||
@ -351,8 +351,8 @@ async function cleanupWindowsNamedPipes(): Promise<void> {
|
||||
process.kill(pid, 0)
|
||||
process.kill(pid, 'SIGTERM')
|
||||
await managerLogger.info(`Terminated process ${pid} to free pipe`)
|
||||
} catch (error: any) {
|
||||
if (error.code !== 'ESRCH') {
|
||||
} catch (error: unknown) {
|
||||
if ((error as { code?: string })?.code !== 'ESRCH') {
|
||||
await managerLogger.warn(`Failed to terminate process ${pid}:`, error)
|
||||
}
|
||||
}
|
||||
@ -589,8 +589,8 @@ export async function checkAdminPrivileges(): Promise<boolean> {
|
||||
await execPromise('chcp 65001 >nul 2>&1 && fltmc', { encoding: 'utf8' })
|
||||
await managerLogger.info('Admin privileges confirmed via fltmc')
|
||||
return true
|
||||
} catch (fltmcError: any) {
|
||||
const errorCode = fltmcError?.code || 0
|
||||
} catch (fltmcError: unknown) {
|
||||
const errorCode = (fltmcError as { code?: number })?.code || 0
|
||||
await managerLogger.debug(`fltmc failed with code ${errorCode}, trying net session as fallback`)
|
||||
|
||||
try {
|
||||
@ -598,8 +598,8 @@ export async function checkAdminPrivileges(): Promise<boolean> {
|
||||
await execPromise('chcp 65001 >nul 2>&1 && net session', { encoding: 'utf8' })
|
||||
await managerLogger.info('Admin privileges confirmed via net session')
|
||||
return true
|
||||
} catch (netSessionError: any) {
|
||||
const netErrorCode = netSessionError?.code || 0
|
||||
} catch (netSessionError: unknown) {
|
||||
const netErrorCode = (netSessionError as { code?: number })?.code || 0
|
||||
await managerLogger.debug(
|
||||
`Both fltmc and net session failed, no admin privileges. Error codes: fltmc=${errorCode}, net=${netErrorCode}`
|
||||
)
|
||||
@ -618,7 +618,8 @@ export async function showTunPermissionDialog(): Promise<boolean> {
|
||||
|
||||
const title = i18next.t('tun.permissions.title') || '需要管理员权限'
|
||||
const message =
|
||||
i18next.t('tun.permissions.message') || '启用TUN模式需要管理员权限,是否现在重启应用获取权限?'
|
||||
i18next.t('tun.permissions.message') ||
|
||||
'启用 TUN 模式需要管理员权限,是否现在重启应用获取权限?'
|
||||
const confirmText = i18next.t('common.confirm') || '确认'
|
||||
const cancelText = i18next.t('common.cancel') || '取消'
|
||||
|
||||
@ -679,7 +680,7 @@ export async function restartAsAdmin(forTun: boolean = true): Promise<void> {
|
||||
|
||||
await managerLogger.info('Restarting as administrator with command', command)
|
||||
|
||||
// 执行PowerShell命令
|
||||
// 执行 PowerShell 命令
|
||||
exec(command, { windowsHide: true }, async (error, _stdout, stderr) => {
|
||||
if (error) {
|
||||
await managerLogger.error('PowerShell execution error', error)
|
||||
@ -703,7 +704,7 @@ export async function checkMihomoCorePermissions(): Promise<boolean> {
|
||||
|
||||
try {
|
||||
if (process.platform === 'win32') {
|
||||
// Windows权限检查
|
||||
// Windows 权限检查
|
||||
return await checkAdminPrivileges()
|
||||
}
|
||||
|
||||
@ -834,7 +835,9 @@ async function checkHighPrivilegeMihomoProcess(): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {}
|
||||
} catch (error) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundProcesses) {
|
||||
@ -851,7 +854,7 @@ async function checkHighPrivilegeMihomoProcess(): Promise<boolean> {
|
||||
return false
|
||||
}
|
||||
|
||||
// TUN模式获取权限
|
||||
// TUN 模式获取权限
|
||||
export async function requestTunPermissions(): Promise<void> {
|
||||
if (process.platform === 'win32') {
|
||||
await restartAsAdmin()
|
||||
@ -894,7 +897,7 @@ export async function checkAdminRestartForTun(): Promise<void> {
|
||||
await managerLogger.error('Failed to auto-enable TUN after admin restart', error)
|
||||
}
|
||||
} else {
|
||||
// 检查TUN配置与权限的匹配,但不自动开启 TUN
|
||||
// 检查 TUN 配置与权限的匹配,但不自动开启 TUN
|
||||
await validateTunPermissionsOnStartup()
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ export async function initProfileUpdater(): Promise<void> {
|
||||
for (const item of items.filter((i) => i.id !== current)) {
|
||||
if (item.type === 'remote' && item.autoUpdate && item.interval) {
|
||||
if (typeof item.interval === 'number') {
|
||||
// 数字间隔使用setInterval
|
||||
// 数字间隔使用 setInterval
|
||||
intervalPool[item.id] = setInterval(
|
||||
async () => {
|
||||
try {
|
||||
@ -22,7 +22,7 @@ export async function initProfileUpdater(): Promise<void> {
|
||||
item.interval * 60 * 1000
|
||||
)
|
||||
} else if (typeof item.interval === 'string') {
|
||||
// 字符串间隔使用Cron
|
||||
// 字符串间隔使用 Cron
|
||||
intervalPool[item.id] = new Cron(item.interval, async () => {
|
||||
try {
|
||||
await addProfileItem(item)
|
||||
|
||||
@ -5,13 +5,13 @@ import { getAppConfig } from '../config'
|
||||
export async function subStoreSubs(): Promise<ISubStoreSub[]> {
|
||||
const { useCustomSubStore = false, customSubStoreUrl = '' } = await getAppConfig()
|
||||
const baseUrl = useCustomSubStore ? customSubStoreUrl : `http://127.0.0.1:${subStorePort}`
|
||||
const res = await chromeRequest.get(`${baseUrl}/api/subs`, { responseType: 'json' })
|
||||
return res.data.data as ISubStoreSub[]
|
||||
const res = await chromeRequest.get<{ data: ISubStoreSub[] }>(`${baseUrl}/api/subs`, { responseType: 'json' })
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
export async function subStoreCollections(): Promise<ISubStoreSub[]> {
|
||||
const { useCustomSubStore = false, customSubStoreUrl = '' } = await getAppConfig()
|
||||
const baseUrl = useCustomSubStore ? customSubStoreUrl : `http://127.0.0.1:${subStorePort}`
|
||||
const res = await chromeRequest.get(`${baseUrl}/api/collections`, { responseType: 'json' })
|
||||
return res.data.data as ISubStoreSub[]
|
||||
const res = await chromeRequest.get<{ data: ISubStoreSub[] }>(`${baseUrl}/api/collections`, { responseType: 'json' })
|
||||
return res.data.data
|
||||
}
|
||||
|
||||
@ -231,7 +231,7 @@ app.whenReady().then(async () => {
|
||||
const [startPromise] = await startCore()
|
||||
startPromise.then(async () => {
|
||||
await initProfileUpdater()
|
||||
await initWebdavBackupScheduler() // 初始化WebDAV定时备份任务
|
||||
await initWebdavBackupScheduler() // 初始化 WebDAV 定时备份任务
|
||||
// 上次是否为了开启 TUN 而重启
|
||||
await checkAdminRestartForTun()
|
||||
})
|
||||
|
||||
@ -26,7 +26,7 @@ export async function checkUpdate(): Promise<IAppVersion | undefined> {
|
||||
responseType: 'text'
|
||||
}
|
||||
)
|
||||
const latest = parse(res.data) as IAppVersion
|
||||
const latest = parse(res.data as string) as IAppVersion
|
||||
const currentVersion = app.getVersion()
|
||||
if (compareVersions(latest.version, currentVersion) > 0) {
|
||||
return latest
|
||||
@ -94,7 +94,7 @@ export async function downloadAndInstallUpdate(version: string): Promise<void> {
|
||||
'Content-Type': 'application/octet-stream'
|
||||
}
|
||||
})
|
||||
await writeFile(path.join(dataDir(), file), res.data)
|
||||
await writeFile(path.join(dataDir(), file), res.data as string | Buffer)
|
||||
}
|
||||
if (file.endsWith('.exe')) {
|
||||
try {
|
||||
@ -125,7 +125,7 @@ export async function downloadAndInstallUpdate(version: string): Promise<void> {
|
||||
} catch (installerError) {
|
||||
await appLogger.error('Failed to start installer, trying fallback', installerError)
|
||||
|
||||
// Fallback: 尝试使用shell.openPath打开安装包
|
||||
// Fallback: 尝试使用 shell.openPath 打开安装包
|
||||
try {
|
||||
await shell.openPath(path.join(dataDir(), file))
|
||||
await appLogger.info('Opened installer with shell.openPath as fallback')
|
||||
|
||||
@ -155,7 +155,7 @@ export async function webdavDelete(filename: string): Promise<void> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化WebDAV定时备份任务
|
||||
* 初始化 WebDAV 定时备份任务
|
||||
*/
|
||||
export async function initWebdavBackupScheduler(): Promise<void> {
|
||||
try {
|
||||
@ -167,7 +167,7 @@ export async function initWebdavBackupScheduler(): Promise<void> {
|
||||
|
||||
const { webdavBackupCron } = await getAppConfig()
|
||||
|
||||
// 如果配置了Cron表达式,则启动定时任务
|
||||
// 如果配置了 Cron 表达式,则启动定时任务
|
||||
if (webdavBackupCron) {
|
||||
backupCronJob = new Cron(webdavBackupCron, async () => {
|
||||
try {
|
||||
@ -189,7 +189,7 @@ export async function initWebdavBackupScheduler(): Promise<void> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止WebDAV定时备份任务
|
||||
* 停止 WebDAV 定时备份任务
|
||||
*/
|
||||
export async function stopWebdavBackupScheduler(): Promise<void> {
|
||||
if (backupCronJob) {
|
||||
@ -200,7 +200,7 @@ export async function stopWebdavBackupScheduler(): Promise<void> {
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新初始化WebDAV定时备份任务
|
||||
* 重新初始化 WebDAV 定时备份任务
|
||||
* 先停止现有任务,然后重新启动
|
||||
*/
|
||||
export async function reinitScheduler(): Promise<void> {
|
||||
|
||||
@ -9,7 +9,7 @@ import { floatingWindowLogger } from '../utils/logger'
|
||||
|
||||
export let floatingWindow: BrowserWindow | null = null
|
||||
|
||||
function logError(message: string, error?: any): void {
|
||||
function logError(message: string, error?: unknown): void {
|
||||
floatingWindowLogger.log(`FloatingWindow Error: ${message}`, error).catch(() => {})
|
||||
}
|
||||
|
||||
|
||||
@ -395,7 +395,7 @@ export async function createTray(): Promise<void> {
|
||||
image.setTemplateImage(true)
|
||||
tray?.setImage(image)
|
||||
})
|
||||
// macOS 默认行为: 左键显示窗口, 右键显示菜单
|
||||
// macOS 默认行为:左键显示窗口,右键显示菜单
|
||||
tray?.addListener('click', async () => {
|
||||
if (swapTrayClick) {
|
||||
await updateTrayMenu()
|
||||
@ -537,7 +537,7 @@ export function updateTrayIconImmediate(sysProxyEnabled: boolean, tunEnabled: bo
|
||||
tray.setImage(iconPath)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('更新托盘图标失败:', error)
|
||||
console.error('更新托盘图标失败:', error)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -560,6 +560,6 @@ export async function updateTrayIcon(): Promise<void> {
|
||||
tray.setImage(iconPath)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('更新托盘图标失败:', error)
|
||||
console.error('更新托盘图标失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,7 @@ import { triggerAutoProxy, triggerManualProxy } from '@mihomo-party/sysproxy'
|
||||
import { getAppConfig, getControledMihomoConfig } from '../config'
|
||||
import { pacPort, startPacServer, stopPacServer } from '../resolve/server'
|
||||
import { promisify } from 'util'
|
||||
import { execFile } from 'child_process'
|
||||
import { exec, execFile } from 'child_process'
|
||||
import path from 'path'
|
||||
import { resourcesFilesDir } from '../utils/dirs'
|
||||
import { net } from 'electron'
|
||||
@ -164,8 +164,6 @@ function isSocketFileExists(): boolean {
|
||||
async function requestSocketRecreation(): Promise<void> {
|
||||
try {
|
||||
// Send SIGUSR1 signal to helper process to recreate socket
|
||||
const { exec } = require('child_process')
|
||||
const { promisify } = require('util')
|
||||
const execPromise = promisify(exec)
|
||||
|
||||
// Use osascript with administrator privileges (same pattern as grantTunPermissions)
|
||||
|
||||
@ -17,7 +17,7 @@ export interface RequestOptions {
|
||||
maxRedirects?: number
|
||||
}
|
||||
|
||||
export interface Response<T = any> {
|
||||
export interface Response<T = unknown> {
|
||||
data: T
|
||||
status: number
|
||||
statusText: string
|
||||
@ -29,7 +29,7 @@ export interface Response<T = any> {
|
||||
* Make HTTP request using Chromium's network stack (via electron.net)
|
||||
* This provides better compatibility, HTTP/2 support, and system certificate integration
|
||||
*/
|
||||
export async function request<T = any>(
|
||||
export async function request<T = unknown>(
|
||||
url: string,
|
||||
options: RequestOptions = {}
|
||||
): Promise<Response<T>> {
|
||||
@ -45,7 +45,7 @@ export async function request<T = any>(
|
||||
} = options
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let sessionToUse = session.defaultSession
|
||||
let sessionToUse: Electron.Session | undefined = session.defaultSession
|
||||
let tempPartition: string | null = null
|
||||
|
||||
// Set up proxy if specified
|
||||
@ -64,7 +64,7 @@ export async function request<T = any>(
|
||||
if (tempPartition) {
|
||||
// Note: Electron doesn't provide session.destroy(), but temporary sessions
|
||||
// will be garbage collected when no longer referenced
|
||||
sessionToUse = null as any
|
||||
sessionToUse = undefined
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,7 +125,7 @@ export async function request<T = any>(
|
||||
if (timeoutId) clearTimeout(timeoutId)
|
||||
|
||||
const buffer = Buffer.concat(chunks)
|
||||
let data: any
|
||||
let data: unknown
|
||||
|
||||
try {
|
||||
switch (responseType) {
|
||||
@ -141,25 +141,25 @@ export async function request<T = any>(
|
||||
}
|
||||
|
||||
resolve({
|
||||
data,
|
||||
data: data as T,
|
||||
status: statusCode,
|
||||
statusText: statusMessage,
|
||||
headers: responseHeaders,
|
||||
url: url
|
||||
})
|
||||
} catch (error) {
|
||||
reject(new Error(`Failed to parse response: ${error}`))
|
||||
} catch (error: unknown) {
|
||||
reject(new Error(`Failed to parse response: ${String(error)}`))
|
||||
}
|
||||
})
|
||||
|
||||
res.on('error', (error) => {
|
||||
res.on('error', (error: unknown) => {
|
||||
cleanup()
|
||||
if (timeoutId) clearTimeout(timeoutId)
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
|
||||
req.on('error', (error) => {
|
||||
req.on('error', (error: unknown) => {
|
||||
cleanup()
|
||||
if (timeoutId) clearTimeout(timeoutId)
|
||||
reject(error)
|
||||
@ -182,9 +182,9 @@ export async function request<T = any>(
|
||||
|
||||
req.end()
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch((error: unknown) => {
|
||||
cleanup()
|
||||
reject(new Error(`Failed to setup proxy: ${error}`))
|
||||
reject(new Error(`Failed to setup proxy: ${String(error)}`))
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -192,7 +192,7 @@ export async function request<T = any>(
|
||||
/**
|
||||
* Convenience method for GET requests
|
||||
*/
|
||||
export const get = <T = any>(
|
||||
export const get = <T = unknown>(
|
||||
url: string,
|
||||
options?: Omit<RequestOptions, 'method' | 'body'>
|
||||
): Promise<Response<T>> => request<T>(url, { ...options, method: 'GET' })
|
||||
@ -200,9 +200,9 @@ export const get = <T = any>(
|
||||
/**
|
||||
* Convenience method for POST requests
|
||||
*/
|
||||
export const post = <T = any>(
|
||||
export const post = <T = unknown>(
|
||||
url: string,
|
||||
data: any,
|
||||
data: unknown,
|
||||
options?: Omit<RequestOptions, 'method' | 'body'>
|
||||
): Promise<Response<T>> => {
|
||||
const body = typeof data === 'string' ? data : JSON.stringify(data)
|
||||
@ -216,9 +216,9 @@ export const post = <T = any>(
|
||||
/**
|
||||
* Convenience method for PUT requests
|
||||
*/
|
||||
export const put = <T = any>(
|
||||
export const put = <T = unknown>(
|
||||
url: string,
|
||||
data: any,
|
||||
data: unknown,
|
||||
options?: Omit<RequestOptions, 'method' | 'body'>
|
||||
): Promise<Response<T>> => {
|
||||
const body = typeof data === 'string' ? data : JSON.stringify(data)
|
||||
@ -232,7 +232,7 @@ export const put = <T = any>(
|
||||
/**
|
||||
* Convenience method for DELETE requests
|
||||
*/
|
||||
export const del = <T = any>(
|
||||
export const del = <T = unknown>(
|
||||
url: string,
|
||||
options?: Omit<RequestOptions, 'method' | 'body'>
|
||||
): Promise<Response<T>> => request<T>(url, { ...options, method: 'DELETE' })
|
||||
@ -240,9 +240,9 @@ export const del = <T = any>(
|
||||
/**
|
||||
* Convenience method for PATCH requests
|
||||
*/
|
||||
export const patch = <T = any>(
|
||||
export const patch = <T = unknown>(
|
||||
url: string,
|
||||
data: any,
|
||||
data: unknown,
|
||||
options?: Omit<RequestOptions, 'method' | 'body'>
|
||||
): Promise<Response<T>> => {
|
||||
const body = typeof data === 'string' ? data : JSON.stringify(data)
|
||||
|
||||
@ -42,7 +42,7 @@ const PLATFORM_MAP: Record<string, string> = {
|
||||
const versionCache = new Map<string, VersionCache>()
|
||||
|
||||
/**
|
||||
* 获取GitHub仓库的标签列表(带缓存)
|
||||
* 获取 GitHub 仓库的标签列表(带缓存)
|
||||
* @param owner 仓库所有者
|
||||
* @param repo 仓库名称
|
||||
* @param forceRefresh 是否强制刷新缓存
|
||||
@ -109,8 +109,8 @@ export function clearVersionCache(owner: string, repo: string): void {
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载GitHub Release资产
|
||||
* @param url 下载URL
|
||||
* 下载 GitHub Release 资产
|
||||
* @param url 下载 URL
|
||||
* @param outputPath 输出路径
|
||||
*/
|
||||
async function downloadGitHubAsset(url: string, outputPath: string): Promise<void> {
|
||||
@ -133,7 +133,7 @@ async function downloadGitHubAsset(url: string, outputPath: string): Promise<voi
|
||||
}
|
||||
|
||||
/**
|
||||
* 安装特定版本的mihomo核心
|
||||
* 安装特定版本的 mihomo 核心
|
||||
* @param version 版本号
|
||||
*/
|
||||
export async function installMihomoCore(version: string): Promise<void> {
|
||||
@ -141,9 +141,9 @@ export async function installMihomoCore(version: string): Promise<void> {
|
||||
console.log(`[GitHub] Installing mihomo core version ${version}`)
|
||||
|
||||
const plat = platform()
|
||||
let arch = process.arch
|
||||
const arch = process.arch
|
||||
|
||||
// 映射平台和架构到GitHub Release文件名
|
||||
// 映射平台和架构到 GitHub Release 文件名
|
||||
const key = `${plat}-${arch}`
|
||||
const name = PLATFORM_MAP[key]
|
||||
|
||||
@ -185,7 +185,7 @@ export async function installMihomoCore(version: string): Promise<void> {
|
||||
throw new Error(`Executable file not found in zip: ${exeFile}`)
|
||||
}
|
||||
} else {
|
||||
// 处理.gz文件
|
||||
// 处理.gz 文件
|
||||
console.log(`[GitHub] Extracting GZ file ${tempZip}`)
|
||||
const readStream = createReadStream(tempZip)
|
||||
const writeStream = createWriteStream(targetPath)
|
||||
|
||||
@ -84,7 +84,7 @@ async function fixDataDirPermissions(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
// 比较修改geodata文件修改时间
|
||||
// 比较修改 geodata 文件修改时间
|
||||
async function isSourceNewer(sourcePath: string, targetPath: string): Promise<boolean> {
|
||||
try {
|
||||
const sourceStats = await stat(sourcePath)
|
||||
|
||||
@ -147,7 +147,7 @@ function ipcErrorWrapper<T>( // eslint-disable-next-line @typescript-eslint/no-e
|
||||
}
|
||||
}
|
||||
|
||||
// GitHub版本管理相关IPC处理程序
|
||||
// GitHub 版本管理相关 IPC 处理程序
|
||||
export async function fetchMihomoTags(
|
||||
forceRefresh = false
|
||||
): Promise<{ name: string; zipball_url: string; tarball_url: string }[]> {
|
||||
@ -367,20 +367,20 @@ export function registerIpcMainHandlers(): void {
|
||||
ipcMain.emit('updateTrayMenu')
|
||||
})
|
||||
|
||||
// 注册获取Mihomo标签的IPC处理程序
|
||||
// 注册获取 Mihomo 标签的 IPC 处理程序
|
||||
ipcMain.handle('fetchMihomoTags', (_e, forceRefresh) =>
|
||||
ipcErrorWrapper(fetchMihomoTags)(forceRefresh)
|
||||
)
|
||||
|
||||
// 注册安装特定版本Mihomo核心的IPC处理程序
|
||||
// 注册安装特定版本 Mihomo 核心的 IPC 处理程序
|
||||
ipcMain.handle('installSpecificMihomoCore', (_e, version) =>
|
||||
ipcErrorWrapper(installSpecificMihomoCore)(version)
|
||||
)
|
||||
|
||||
// 注册清除版本缓存的IPC处理程序
|
||||
// 注册清除版本缓存的 IPC 处理程序
|
||||
ipcMain.handle('clearMihomoVersionCache', () => ipcErrorWrapper(clearMihomoVersionCache)())
|
||||
|
||||
// 规则相关IPC处理程序
|
||||
// 规则相关 IPC 处理程序
|
||||
ipcMain.handle('getRuleStr', (_e, id) => ipcErrorWrapper(getRuleStr)(id))
|
||||
ipcMain.handle('setRuleStr', (_e, id, str) => ipcErrorWrapper(setRuleStr)(id, str))
|
||||
}
|
||||
|
||||
@ -14,13 +14,13 @@ class Logger {
|
||||
return new Date().toISOString()
|
||||
}
|
||||
|
||||
private formatLogMessage(level: LogLevel, message: string, error?: any): string {
|
||||
private formatLogMessage(level: LogLevel, message: string, error?: unknown): string {
|
||||
const timestamp = this.formatTimestamp()
|
||||
const errorStr = error ? `: ${error}` : ''
|
||||
const errorStr = error ? `: ${String(error)}` : ''
|
||||
return `[${timestamp}] [${level.toUpperCase()}] [${this.moduleName}] ${message}${errorStr}\n`
|
||||
}
|
||||
|
||||
private async writeToFile(level: LogLevel, message: string, error?: any): Promise<void> {
|
||||
private async writeToFile(level: LogLevel, message: string, error?: unknown): Promise<void> {
|
||||
try {
|
||||
const appLogPath = logPath()
|
||||
const logMessage = this.formatLogMessage(level, message, error)
|
||||
@ -35,7 +35,7 @@ class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
private logToConsole(level: LogLevel, message: string, error?: any): void {
|
||||
private logToConsole(level: LogLevel, message: string, error?: unknown): void {
|
||||
const prefix = `[${this.moduleName}] ${message}`
|
||||
|
||||
switch (level) {
|
||||
@ -54,28 +54,28 @@ class Logger {
|
||||
}
|
||||
}
|
||||
|
||||
async debug(message: string, error?: any): Promise<void> {
|
||||
async debug(message: string, error?: unknown): Promise<void> {
|
||||
await this.writeToFile('debug', message, error)
|
||||
this.logToConsole('debug', message, error)
|
||||
}
|
||||
|
||||
async info(message: string, error?: any): Promise<void> {
|
||||
async info(message: string, error?: unknown): Promise<void> {
|
||||
await this.writeToFile('info', message, error)
|
||||
this.logToConsole('info', message, error)
|
||||
}
|
||||
|
||||
async warn(message: string, error?: any): Promise<void> {
|
||||
async warn(message: string, error?: unknown): Promise<void> {
|
||||
await this.writeToFile('warn', message, error)
|
||||
this.logToConsole('warn', message, error)
|
||||
}
|
||||
|
||||
async error(message: string, error?: any): Promise<void> {
|
||||
async error(message: string, error?: unknown): Promise<void> {
|
||||
await this.writeToFile('error', message, error)
|
||||
this.logToConsole('error', message, error)
|
||||
}
|
||||
|
||||
// 兼容原有的 logFloatingWindow 函数签名
|
||||
async log(message: string, error?: any): Promise<void> {
|
||||
async log(message: string, error?: unknown): Promise<void> {
|
||||
if (error) {
|
||||
await this.error(message, error)
|
||||
} else {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import { GenIcon } from 'react-icons'
|
||||
import { GenIcon, IconBaseProps } from 'react-icons'
|
||||
|
||||
function MihomoIcon(props: any): React.JSX.Element {
|
||||
function MihomoIcon(props: IconBaseProps): React.JSX.Element {
|
||||
return GenIcon({
|
||||
tag: 'svg',
|
||||
attr: { viewBox: '0 0 58 61.53' },
|
||||
|
||||
@ -332,7 +332,7 @@ const ConnectionTable: React.FC<Props> = ({
|
||||
const handleSort = useCallback(
|
||||
(columnKey: string) => {
|
||||
let newDirection: 'asc' | 'desc' = 'asc'
|
||||
let newColumn = columnKey
|
||||
const newColumn = columnKey
|
||||
|
||||
if (sortColumn === columnKey) {
|
||||
newDirection = sortDirection === 'asc' ? 'desc' : 'asc'
|
||||
|
||||
@ -140,7 +140,7 @@ const EditInfoModal: React.FC<Props> = (props) => {
|
||||
value={values.interval?.toString() ?? ''}
|
||||
onValueChange={(v) => {
|
||||
// 输入限制
|
||||
if (/^[\d\s*\-,\/]*$/.test(v)) {
|
||||
if (/^[\d\s*\-,/]*$/.test(v)) {
|
||||
// 纯数字
|
||||
if (/^\d+$/.test(v)) {
|
||||
setValues({ ...values, interval: parseInt(v, 10) || 0 })
|
||||
|
||||
@ -411,8 +411,7 @@ interface RuleListItemProps {
|
||||
onRemove: (index: number) => void
|
||||
}
|
||||
|
||||
const RuleListItem = memo<RuleListItemProps>(
|
||||
({
|
||||
const RuleListItemBase: React.FC<RuleListItemProps> = ({
|
||||
rule,
|
||||
originalIndex,
|
||||
isDeleted,
|
||||
@ -421,7 +420,7 @@ const RuleListItem = memo<RuleListItemProps>(
|
||||
onMoveUp,
|
||||
onMoveDown,
|
||||
onRemove
|
||||
}) => {
|
||||
}) => {
|
||||
let bgColorClass = 'bg-content2'
|
||||
let textStyleClass = ''
|
||||
|
||||
@ -496,8 +495,9 @@ const RuleListItem = memo<RuleListItemProps>(
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
(prevProps, nextProps) => {
|
||||
}
|
||||
|
||||
const RuleListItem = memo(RuleListItemBase, (prevProps, nextProps) => {
|
||||
return (
|
||||
prevProps.rule === nextProps.rule &&
|
||||
prevProps.originalIndex === nextProps.originalIndex &&
|
||||
@ -505,8 +505,9 @@ const RuleListItem = memo<RuleListItemProps>(
|
||||
prevProps.isPrependOrAppend === nextProps.isPrependOrAppend &&
|
||||
prevProps.rulesLength === nextProps.rulesLength
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
RuleListItem.displayName = 'RuleListItem'
|
||||
|
||||
const EditRulesModal: React.FC<Props> = (props) => {
|
||||
const { id, onClose } = props
|
||||
@ -563,7 +564,7 @@ const EditRulesModal: React.FC<Props> = (props) => {
|
||||
const content = await getProfileStr(id)
|
||||
setProfileContent(content)
|
||||
|
||||
const parsed = yaml.load(content) as any
|
||||
const parsed = yaml.load(content) as Record<string, unknown> | undefined
|
||||
let initialRules: RuleItem[] = []
|
||||
|
||||
if (parsed && parsed.rules && Array.isArray(parsed.rules)) {
|
||||
@ -593,11 +594,19 @@ const EditRulesModal: React.FC<Props> = (props) => {
|
||||
|
||||
// 添加代理组和代理名称
|
||||
if (Array.isArray(parsed['proxy-groups'])) {
|
||||
groups.push(...parsed['proxy-groups'].map((group: any) => group?.name).filter(Boolean))
|
||||
groups.push(
|
||||
...((parsed['proxy-groups'] as Array<Record<string, unknown>>)
|
||||
.map((group) => (group && typeof group['name'] === 'string' ? (group['name'] as string) : ''))
|
||||
.filter(Boolean) as string[])
|
||||
)
|
||||
}
|
||||
|
||||
if (Array.isArray(parsed['proxies'])) {
|
||||
groups.push(...parsed['proxies'].map((proxy: any) => proxy?.name).filter(Boolean))
|
||||
groups.push(
|
||||
...((parsed['proxies'] as Array<Record<string, unknown>>)
|
||||
.map((proxy) => (proxy && typeof proxy['name'] === 'string' ? (proxy['name'] as string) : ''))
|
||||
.filter(Boolean) as string[])
|
||||
)
|
||||
}
|
||||
|
||||
// 预置出站 https://wiki.metacubex.one/config/proxies/built-in/
|
||||
@ -710,7 +719,7 @@ const EditRulesModal: React.FC<Props> = (props) => {
|
||||
}
|
||||
} catch (ruleError) {
|
||||
// 规则文件读取失败
|
||||
console.debug('规则文件读取失败:', ruleError)
|
||||
console.debug('规则文件读取失败:', ruleError)
|
||||
setRules(initialRules)
|
||||
// 清空规则标记
|
||||
setPrependRules(new Set())
|
||||
|
||||
@ -22,8 +22,7 @@ function delayColor(delay: number): 'primary' | 'success' | 'warning' | 'danger'
|
||||
return 'warning'
|
||||
}
|
||||
|
||||
const ProxyItem: React.FC<Props> = React.memo(
|
||||
(props) => {
|
||||
const ProxyItemBase: React.FC<Props> = (props) => {
|
||||
const { t } = useTranslation()
|
||||
const {
|
||||
mutateProxies,
|
||||
@ -61,10 +60,7 @@ const ProxyItem: React.FC<Props> = React.memo(
|
||||
})
|
||||
}, [proxy.name, group.testUrl, onProxyDelay, mutateProxies])
|
||||
|
||||
const fixed = useMemo(
|
||||
() => group.fixed && group.fixed === proxy.name,
|
||||
[group.fixed, proxy.name]
|
||||
)
|
||||
const fixed = useMemo(() => group.fixed && group.fixed === proxy.name, [group.fixed, proxy.name])
|
||||
|
||||
return (
|
||||
<Card
|
||||
@ -177,8 +173,9 @@ const ProxyItem: React.FC<Props> = React.memo(
|
||||
</CardBody>
|
||||
</Card>
|
||||
)
|
||||
},
|
||||
(prevProps, nextProps) => {
|
||||
}
|
||||
|
||||
const ProxyItem = React.memo(ProxyItemBase, (prevProps, nextProps) => {
|
||||
// 必要时重新渲染
|
||||
return (
|
||||
prevProps.proxy.name === nextProps.proxy.name &&
|
||||
@ -188,8 +185,7 @@ const ProxyItem: React.FC<Props> = React.memo(
|
||||
prevProps.group.fixed === nextProps.group.fixed &&
|
||||
prevProps.isGroupTesting === nextProps.isGroupTesting
|
||||
)
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
ProxyItem.displayName = 'ProxyItem'
|
||||
|
||||
|
||||
@ -208,7 +208,7 @@ const GeneralConfig: React.FC = () => {
|
||||
type="number"
|
||||
value={autoQuitWithoutCoreDelay.toString()}
|
||||
onValueChange={async (v: string) => {
|
||||
let num = parseInt(v)
|
||||
const num = parseInt(v)
|
||||
await patchAppConfig({ autoQuitWithoutCoreDelay: num })
|
||||
}}
|
||||
onBlur={async (e) => {
|
||||
|
||||
@ -59,7 +59,7 @@ const MihomoConfig: React.FC = () => {
|
||||
type="number"
|
||||
value={(subscriptionTimeout / 1000)?.toString()}
|
||||
onValueChange={async (v: string) => {
|
||||
let num = parseInt(v)
|
||||
const num = parseInt(v)
|
||||
await patchAppConfig({ subscriptionTimeout: num * 1000 })
|
||||
}}
|
||||
onBlur={async (e) => {
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import React, { createContext, ReactNode, useContext } from 'react'
|
||||
import { showError } from '@renderer/utils/error-display'
|
||||
import { toast } from '@renderer/components/base/toast'
|
||||
import useSWR from 'swr'
|
||||
import {
|
||||
addProfileItem as add,
|
||||
@ -104,7 +103,7 @@ export const ProfileConfigProvider: React.FC<{ children: ReactNode }> = ({ child
|
||||
// 异步执行后台切换,不阻塞 UI
|
||||
await pendingTask.current
|
||||
} catch (e) {
|
||||
const errorMsg = (e as any)?.message || String(e)
|
||||
const errorMsg = (e as { message?: string })?.message || String(e)
|
||||
// 处理 IPC 超时错误
|
||||
if (errorMsg.includes('reply was never sent')) {
|
||||
setTimeout(() => mutateProfileConfig(), 1000)
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
"settings.darkMode": "深色模式",
|
||||
"settings.lightMode": "浅色模式",
|
||||
"settings.autoStart": "开机自启",
|
||||
"settings.autoStart.permissions": "正在以管理员权限配置计划任务,请在UAC对话框中点击'是'...",
|
||||
"settings.autoStart.permissions": "正在以管理员权限配置计划任务,请在 UAC 对话框中点击'是'...",
|
||||
"settings.autoCheckUpdate": "自动检查更新",
|
||||
"settings.silentStart": "静默启动",
|
||||
"settings.autoQuitWithoutCore": "自动进入轻量模式",
|
||||
@ -104,9 +104,9 @@
|
||||
"settings.webui.secret": "密钥",
|
||||
"settings.webui.noSecret": "无密钥",
|
||||
"settings.webui.panelName": "面板名称",
|
||||
"settings.webui.panelUrl": "面板URL",
|
||||
"settings.webui.panelUrl": "面板 URL",
|
||||
"settings.webui.panelNamePlaceholder": "输入面板名称",
|
||||
"settings.webui.panelUrlPlaceholder": "输入面板URL",
|
||||
"settings.webui.panelUrlPlaceholder": "输入面板 URL",
|
||||
"settings.webui.variableHint": "可用变量",
|
||||
"settings.webui.addPanel": "添加面板",
|
||||
"settings.webui.panels": "面板列表",
|
||||
@ -169,7 +169,7 @@
|
||||
"mihomo.smartCoreStrategyStickySession": "粘性会话",
|
||||
"mihomo.smartCoreStrategyRoundRobin": "轮询",
|
||||
"mihomo.smartCoreUseLightGBMTooltip": "使用预训练的通用模型,可快速提升节点选择效果,但可能不适合您的特定网络环境",
|
||||
"mihomo.smartCoreCollectDataTooltip": "收集您的网络使用数据,可用于训练更适合您的网络环境的自定义模型(如果您不懂如何训练模型,请关闭)",
|
||||
"mihomo.smartCoreCollectDataTooltip": "收集您的网络使用数据,可用于训练更适合您的网络环境的自定义模型 (如果您不懂如何训练模型,请关闭)",
|
||||
"mihomo.smartCollectorSize": "数据收集文件大小(MB)",
|
||||
"mihomo.smartCollectorSizeTooltip": "限制数据收集文件大小,默认为 100MB",
|
||||
"mihomo.mixedPort": "混合端口",
|
||||
@ -280,7 +280,7 @@
|
||||
"sider.cards.override": "覆写",
|
||||
"sider.cards.connections": "连接",
|
||||
"sider.cards.core": "内核设置",
|
||||
"sider.cards.dns": "DNS覆写",
|
||||
"sider.cards.dns": "DNS 覆写",
|
||||
"sider.cards.sniff": "嗅探覆写",
|
||||
"sider.cards.logs": "日志",
|
||||
"sider.cards.substore": "Sub-Store",
|
||||
@ -363,28 +363,28 @@
|
||||
"sysproxy.pacEditor.title": "编辑 PAC 脚本",
|
||||
"sysproxy.bypass.title": "代理绕过",
|
||||
"sysproxy.bypass.addDefault": "添加默认代理绕过",
|
||||
"sysproxy.bypass.placeholder": "例: *.baidu.com",
|
||||
"sysproxy.bypass.placeholder": "例:*.baidu.com",
|
||||
"tun.title": "虚拟网卡",
|
||||
"tun.firewall.title": "重设防火墙",
|
||||
"tun.firewall.reset": "重设防火墙",
|
||||
"tun.core.title": "手动授权内核",
|
||||
"tun.core.auth": "手动授权内核",
|
||||
"tun.dns.autoSet": "自动设置系统DNS",
|
||||
"tun.dns.autoSet": "自动设置系统 DNS",
|
||||
"tun.stack.title": "Tun 模式堆栈",
|
||||
"tun.device.title": "Tun 网卡名称",
|
||||
"tun.strictRoute": "严格路由",
|
||||
"tun.autoRoute": "自动设置全局路由",
|
||||
"tun.autoRedirect": "自动设置TCP重定向",
|
||||
"tun.autoRedirect": "自动设置 TCP 重定向",
|
||||
"tun.autoDetectInterface": "自动选择流量出口接口",
|
||||
"tun.dnsHijack": "DNS 劫持",
|
||||
"tun.excludeAddress.title": "排除自定义网段",
|
||||
"tun.excludeAddress.placeholder": "例: 172.20.0.0/16",
|
||||
"tun.excludeAddress.placeholder": "例:172.20.0.0/16",
|
||||
"tun.notifications.coreAuthSuccess": "内核授权成功",
|
||||
"tun.notifications.firewallResetSuccess": "防火墙重设成功",
|
||||
"tun.permissions.title": "需要管理员权限",
|
||||
"tun.permissions.message": "启用TUN模式需要管理员权限,是否现在重启应用获取权限?",
|
||||
"tun.permissions.message": "启用 TUN 模式需要管理员权限,是否现在重启应用获取权限?",
|
||||
"tun.permissions.failed": "权限授权失败",
|
||||
"tun.permissions.restarting": "正在以管理员权限重启应用,请在UAC对话框中点击'是'...",
|
||||
"tun.permissions.restarting": "正在以管理员权限重启应用,请在 UAC 对话框中点击'是'...",
|
||||
"dns.title": "DNS 设置",
|
||||
"dns.enable": "启用 DNS",
|
||||
"dns.enhancedMode.title": "域名映射模式",
|
||||
@ -456,8 +456,8 @@
|
||||
"profiles.editInfo.intervalPlaceholder": "例如:30 或 '0 * * * *'",
|
||||
"profiles.editInfo.intervalInvalid": "不合法",
|
||||
"profiles.editInfo.intervalMinutes": "以分钟为单位的定时间隔",
|
||||
"profiles.editInfo.intervalCron": "有效的Cron表达式",
|
||||
"profiles.editInfo.intervalHint": "请输入数字或合法的Cron表达式(如:0 * * * *)",
|
||||
"profiles.editInfo.intervalCron": "有效的 Cron 表达式",
|
||||
"profiles.editInfo.intervalHint": "请输入数字或合法的 Cron 表达式(如:0 * * * *)",
|
||||
"profiles.editInfo.fixedInterval": "固定更新间隔",
|
||||
"profiles.editInfo.autoUpdate": "自动更新",
|
||||
"profiles.editInfo.override.title": "覆写",
|
||||
@ -486,8 +486,8 @@
|
||||
"profiles.editRules.noRules": "暂无规则",
|
||||
"profiles.editRules.noMatchingRules": "没有匹配的规则",
|
||||
"profiles.editRules.saveError": "保存规则时出错",
|
||||
"profiles.editRules.noResolve": "跳过DNS解析 (no-resolve)",
|
||||
"profiles.editRules.src": "匹配源IP (src)",
|
||||
"profiles.editRules.noResolve": "跳过 DNS 解析 (no-resolve)",
|
||||
"profiles.editRules.src": "匹配源 IP (src)",
|
||||
"profiles.openFile": "打开文件",
|
||||
"profiles.home": "主页",
|
||||
"profiles.notification.importSuccess": "订阅导入成功",
|
||||
@ -513,8 +513,8 @@
|
||||
"override.actions.newJs": "新建 JavaScript",
|
||||
"override.defaultContent.yaml": "# https://mihomo.party/docs/guide/override/yaml",
|
||||
"override.defaultContent.js": "// https://mihomo.party/docs/guide/override/javascript\nfunction main(config) {\n return config\n}",
|
||||
"override.newFile.yaml": "新建YAML",
|
||||
"override.newFile.js": "新建JS",
|
||||
"override.newFile.yaml": "新建 YAML",
|
||||
"override.newFile.js": "新建 JS",
|
||||
"override.editInfo.title": "编辑信息",
|
||||
"override.editInfo.name": "名称",
|
||||
"override.editInfo.url": "地址",
|
||||
@ -552,22 +552,22 @@
|
||||
"connections.detail.sniffHost": "嗅探主机",
|
||||
"connections.detail.processName": "进程名",
|
||||
"connections.detail.processPath": "进程路径",
|
||||
"connections.detail.sourceIP": "来源IP",
|
||||
"connections.detail.sourceGeoIP": "来源GeoIP",
|
||||
"connections.detail.sourceASN": "来源ASN",
|
||||
"connections.detail.destinationIP": "目标IP",
|
||||
"connections.detail.destinationGeoIP": "目标GeoIP",
|
||||
"connections.detail.destinationASN": "目标ASN",
|
||||
"connections.detail.sourceIP": "来源 IP",
|
||||
"connections.detail.sourceGeoIP": "来源 GeoIP",
|
||||
"connections.detail.sourceASN": "来源 ASN",
|
||||
"connections.detail.destinationIP": "目标 IP",
|
||||
"connections.detail.destinationGeoIP": "目标 GeoIP",
|
||||
"connections.detail.destinationASN": "目标 ASN",
|
||||
"connections.detail.sourcePort": "来源端口",
|
||||
"connections.detail.destinationPort": "目标端口",
|
||||
"connections.detail.inboundIP": "入站IP",
|
||||
"connections.detail.inboundIP": "入站 IP",
|
||||
"connections.detail.inboundPort": "入站端口",
|
||||
"connections.detail.copyRule": "复制规则",
|
||||
"connections.detail.inboundName": "入站名称",
|
||||
"connections.detail.inboundUser": "入站用户",
|
||||
"connections.detail.dscp": "DSCP",
|
||||
"connections.detail.remoteDestination": "远程目标",
|
||||
"connections.detail.dnsMode": "DNS模式",
|
||||
"connections.detail.dnsMode": "DNS 模式",
|
||||
"connections.detail.specialProxy": "特殊代理",
|
||||
"connections.detail.specialRules": "特殊规则",
|
||||
"connections.detail.close": "关闭",
|
||||
|
||||
@ -52,7 +52,7 @@
|
||||
"settings.darkMode": "深色模式",
|
||||
"settings.lightMode": "淺色模式",
|
||||
"settings.autoStart": "開機自啟",
|
||||
"settings.autoStart.permissions": "正在以管理員權限配置計畫任務,請在UAC對話框中點擊'是'...",
|
||||
"settings.autoStart.permissions": "正在以管理員權限配置計畫任務,請在 UAC 對話框中點擊'是'...",
|
||||
"settings.autoCheckUpdate": "自動檢查更新",
|
||||
"settings.silentStart": "靜默啟動",
|
||||
"settings.autoQuitWithoutCore": "自動進入輕量模式",
|
||||
@ -104,9 +104,9 @@
|
||||
"settings.webui.secret": "密鑰",
|
||||
"settings.webui.noSecret": "無密鑰",
|
||||
"settings.webui.panelName": "面板名稱",
|
||||
"settings.webui.panelUrl": "面板URL",
|
||||
"settings.webui.panelUrl": "面板 URL",
|
||||
"settings.webui.panelNamePlaceholder": "輸入面板名稱",
|
||||
"settings.webui.panelUrlPlaceholder": "輸入面板URL",
|
||||
"settings.webui.panelUrlPlaceholder": "輸入面板 URL",
|
||||
"settings.webui.variableHint": "可用變量",
|
||||
"settings.webui.addPanel": "添加面板",
|
||||
"settings.webui.panels": "面板列表",
|
||||
@ -169,7 +169,7 @@
|
||||
"mihomo.smartCoreStrategyStickySession": "粘性會話",
|
||||
"mihomo.smartCoreStrategyRoundRobin": "輪詢",
|
||||
"mihomo.smartCoreUseLightGBMTooltip": "使用預訓練的通用模型,可快速提升節點選擇效果,但可能不適合您的特定網絡環境",
|
||||
"mihomo.smartCoreCollectDataTooltip": "收集您的網絡使用數據,可用於訓練更适合您的網絡環境的自定義模型(如果您不懂如何訓練模型,請關閉)",
|
||||
"mihomo.smartCoreCollectDataTooltip": "收集您的網絡使用數據,可用於訓練更适合您的網絡環境的自定義模型 (如果您不懂如何訓練模型,請關閉)",
|
||||
"mihomo.smartCollectorSize": "數據收集文件大小(MB)",
|
||||
"mihomo.smartCollectorSizeTooltip": "限制數據收集文件大小,默認為 100MB",
|
||||
"mihomo.mixedPort": "混合埠",
|
||||
@ -280,7 +280,7 @@
|
||||
"sider.cards.override": "覆寫",
|
||||
"sider.cards.connections": "連接",
|
||||
"sider.cards.core": "內核設置",
|
||||
"sider.cards.dns": "DNS覆寫",
|
||||
"sider.cards.dns": "DNS 覆寫",
|
||||
"sider.cards.sniff": "嗅探覆寫",
|
||||
"sider.cards.logs": "日誌",
|
||||
"sider.cards.substore": "Sub-Store",
|
||||
@ -363,28 +363,28 @@
|
||||
"sysproxy.pacEditor.title": "編輯 PAC 腳本",
|
||||
"sysproxy.bypass.title": "代理繞過",
|
||||
"sysproxy.bypass.addDefault": "添加默認代理繞過",
|
||||
"sysproxy.bypass.placeholder": "例: *.baidu.com",
|
||||
"sysproxy.bypass.placeholder": "例:*.baidu.com",
|
||||
"tun.title": "虛擬網卡",
|
||||
"tun.firewall.title": "重設防火牆",
|
||||
"tun.firewall.reset": "重設防火牆",
|
||||
"tun.core.title": "手動授權內核",
|
||||
"tun.core.auth": "手動授權內核",
|
||||
"tun.dns.autoSet": "自動設置系統DNS",
|
||||
"tun.dns.autoSet": "自動設置系統 DNS",
|
||||
"tun.stack.title": "Tun 模式堆棧",
|
||||
"tun.device.title": "Tun 網卡名稱",
|
||||
"tun.strictRoute": "嚴格路由",
|
||||
"tun.autoRoute": "自動設置全局路由",
|
||||
"tun.autoRedirect": "自動設置TCP重定向",
|
||||
"tun.autoRedirect": "自動設置 TCP 重定向",
|
||||
"tun.autoDetectInterface": "自動選擇流量出口接口",
|
||||
"tun.dnsHijack": "DNS 劫持",
|
||||
"tun.excludeAddress.title": "排除自定義網段",
|
||||
"tun.excludeAddress.placeholder": "例: 172.20.0.0/16",
|
||||
"tun.excludeAddress.placeholder": "例:172.20.0.0/16",
|
||||
"tun.notifications.coreAuthSuccess": "內核授權成功",
|
||||
"tun.notifications.firewallResetSuccess": "防火牆重設成功",
|
||||
"tun.permissions.title": "需要系統管理員權限",
|
||||
"tun.permissions.message": "啟用TUN模式需要系統管理員權限,是否現在重啟應用獲取權限?",
|
||||
"tun.permissions.message": "啟用 TUN 模式需要系統管理員權限,是否現在重啟應用獲取權限?",
|
||||
"tun.permissions.failed": "權限授權失敗",
|
||||
"tun.permissions.restarting": "正在以系統管理員權限重啟應用,請在UAC對話框中點擊'是'...",
|
||||
"tun.permissions.restarting": "正在以系統管理員權限重啟應用,請在 UAC 對話框中點擊'是'...",
|
||||
"dns.title": "DNS 設置",
|
||||
"dns.enable": "啟用 DNS",
|
||||
"dns.enhancedMode.title": "域名映射模式",
|
||||
@ -456,8 +456,8 @@
|
||||
"profiles.editInfo.intervalPlaceholder": "例如:30 或 '0 * * * *'",
|
||||
"profiles.editInfo.intervalInvalid": "不合法",
|
||||
"profiles.editInfo.intervalMinutes": "以分鐘為單位的定時間隔",
|
||||
"profiles.editInfo.intervalCron": "有效的Cron表達式",
|
||||
"profiles.editInfo.intervalHint": "請輸入數字或合法的Cron表達式(如:0 * * * *)",
|
||||
"profiles.editInfo.intervalCron": "有效的 Cron 表達式",
|
||||
"profiles.editInfo.intervalHint": "請輸入數字或合法的 Cron 表達式(如:0 * * * *)",
|
||||
"profiles.editInfo.fixedInterval": "固定更新間隔",
|
||||
"profiles.editInfo.autoUpdate": "自動更新",
|
||||
"profiles.editInfo.override.title": "覆寫",
|
||||
@ -486,8 +486,8 @@
|
||||
"profiles.editRules.noRules": "暫無規則",
|
||||
"profiles.editRules.noMatchingRules": "沒有匹配的規則",
|
||||
"profiles.editRules.saveError": "保存規則時出錯",
|
||||
"profiles.editRules.noResolve": "跳過DNS解析 (no-resolve)",
|
||||
"profiles.editRules.src": "匹配源IP (src)",
|
||||
"profiles.editRules.noResolve": "跳過 DNS 解析 (no-resolve)",
|
||||
"profiles.editRules.src": "匹配源 IP (src)",
|
||||
"profiles.openFile": "打開檔案",
|
||||
"profiles.home": "主頁",
|
||||
"profiles.notification.importSuccess": "訂閱匯入成功",
|
||||
@ -513,8 +513,8 @@
|
||||
"override.actions.newJs": "新建 JavaScript",
|
||||
"override.defaultContent.yaml": "# https://mihomo.party/docs/guide/override/yaml",
|
||||
"override.defaultContent.js": "// https://mihomo.party/docs/guide/override/javascript\nfunction main(config) {\n return config\n}",
|
||||
"override.newFile.yaml": "新建YAML",
|
||||
"override.newFile.js": "新建JS",
|
||||
"override.newFile.yaml": "新建 YAML",
|
||||
"override.newFile.js": "新建 JS",
|
||||
"override.editInfo.title": "編輯信息",
|
||||
"override.editInfo.name": "名稱",
|
||||
"override.editInfo.url": "地址",
|
||||
@ -552,22 +552,22 @@
|
||||
"connections.detail.sniffHost": "嗅探主機",
|
||||
"connections.detail.processName": "進程名",
|
||||
"connections.detail.processPath": "進程路徑",
|
||||
"connections.detail.sourceIP": "來源IP",
|
||||
"connections.detail.sourceGeoIP": "來源GeoIP",
|
||||
"connections.detail.sourceASN": "來源ASN",
|
||||
"connections.detail.destinationIP": "目標IP",
|
||||
"connections.detail.destinationGeoIP": "目標GeoIP",
|
||||
"connections.detail.destinationASN": "目標ASN",
|
||||
"connections.detail.sourceIP": "來源 IP",
|
||||
"connections.detail.sourceGeoIP": "來源 GeoIP",
|
||||
"connections.detail.sourceASN": "來源 ASN",
|
||||
"connections.detail.destinationIP": "目標 IP",
|
||||
"connections.detail.destinationGeoIP": "目標 GeoIP",
|
||||
"connections.detail.destinationASN": "目標 ASN",
|
||||
"connections.detail.sourcePort": "來源埠",
|
||||
"connections.detail.destinationPort": "目標埠",
|
||||
"connections.detail.inboundIP": "入站IP",
|
||||
"connections.detail.inboundIP": "入站 IP",
|
||||
"connections.detail.inboundPort": "入站埠",
|
||||
"connections.detail.copyRule": "複製規則",
|
||||
"connections.detail.inboundName": "入站名稱",
|
||||
"connections.detail.inboundUser": "入站用戶",
|
||||
"connections.detail.dscp": "DSCP",
|
||||
"connections.detail.remoteDestination": "遠程目標",
|
||||
"connections.detail.dnsMode": "DNS模式",
|
||||
"connections.detail.dnsMode": "DNS 模式",
|
||||
"connections.detail.specialProxy": "特殊代理",
|
||||
"connections.detail.specialRules": "特殊規則",
|
||||
"connections.detail.close": "關閉",
|
||||
@ -625,7 +625,7 @@
|
||||
"guide.sysproxy.title": "系統代理",
|
||||
"guide.sysproxy.description": "匯入訂閱之後,內核已經開始運行並監聽指定埠,此时您已經可以通過指定代理埠来使用代理了,如果您要使大部分應用自動使用該埠的代理,您還需要打開系統代理開關",
|
||||
"guide.sysproxySetting.title": "系統代理設置",
|
||||
"guide.sysproxySetting.description": "在此您可以進行系統代理相關設置,選擇代理模式,如果某些 Windows 應用不遵循系統代理,还可以使用\"UWP 工具\"解除本地回環限制,對於\"手動代理模式\"和\"PAC 代理模式\"的區別,请自行Google",
|
||||
"guide.sysproxySetting.description": "在此您可以進行系統代理相關設置,選擇代理模式,如果某些 Windows 應用不遵循系統代理,还可以使用\"UWP 工具\"解除本地回環限制,對於\"手動代理模式\"和\"PAC 代理模式\"的區別,请自行 Google",
|
||||
"guide.tun.title": "虛擬網卡",
|
||||
"guide.tun.description": "虛擬網卡,即同類軟件中常見的\"Tun 模式\",對於某些不遵循系統代理的應用程式,您可以打開虛擬網卡以讓內核接管所有流量",
|
||||
"guide.tunSetting.title": "虛擬網卡設置",
|
||||
|
||||
@ -146,7 +146,7 @@ const DNS: React.FC = () => {
|
||||
await restartCore()
|
||||
}
|
||||
} catch (e) {
|
||||
showErrorSync(e, 'DNS配置保存失败')
|
||||
showErrorSync(e, 'DNS 配置保存失败')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -130,7 +130,7 @@ const Mihomo: React.FC = () => {
|
||||
const [searchTerm, setSearchTerm] = useState('')
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
|
||||
// WebUI管理状态
|
||||
// WebUI 管理状态
|
||||
const [isWebUIModalOpen, setIsWebUIModalOpen] = useState(false)
|
||||
const [allPanels, setAllPanels] = useState<WebUIPanel[]>([])
|
||||
const [editingPanel, setEditingPanel] = useState<WebUIPanel | null>(null)
|
||||
@ -150,10 +150,10 @@ const Mihomo: React.FC = () => {
|
||||
|
||||
const { host, port } = parseController()
|
||||
|
||||
// 生成随机端口(范围1024-65535)
|
||||
// 生成随机端口 (范围 1024-65535)
|
||||
const generateRandomPort = () => Math.floor(Math.random() * (65535 - 1024 + 1)) + 1024
|
||||
|
||||
// 默认WebUI面板选项
|
||||
// 默认 WebUI 面板选项
|
||||
const defaultWebUIPanels: WebUIPanel[] = [
|
||||
{
|
||||
id: 'metacubexd',
|
||||
@ -185,14 +185,14 @@ const Mihomo: React.FC = () => {
|
||||
}
|
||||
}, [])
|
||||
|
||||
// 保存面板列表到localStorage
|
||||
// 保存面板列表到 localStorage
|
||||
useEffect(() => {
|
||||
if (allPanels.length > 0) {
|
||||
localStorage.setItem('webui-panels', JSON.stringify(allPanels))
|
||||
}
|
||||
}, [allPanels])
|
||||
|
||||
// 在URL输入框光标处插入或替换变量
|
||||
// 在 URL 输入框光标处插入或替换变量
|
||||
const insertVariableAtCursor = (variable: string) => {
|
||||
if (!urlInputRef.current) return
|
||||
|
||||
@ -216,7 +216,7 @@ const Mihomo: React.FC = () => {
|
||||
}, 0)
|
||||
}
|
||||
|
||||
// 打开WebUI面板
|
||||
// 打开 WebUI 面板
|
||||
const openWebUI = (panel: WebUIPanel) => {
|
||||
const url = panel.url.replace('%host', host).replace('%port', port).replace('%secret', secret)
|
||||
window.open(url, '_blank')
|
||||
@ -274,7 +274,7 @@ const Mihomo: React.FC = () => {
|
||||
setAllPanels(defaultWebUIPanels)
|
||||
}
|
||||
|
||||
// 用于高亮显示URL中的变量
|
||||
// 用于高亮显示 URL 中的变量
|
||||
const HighlightedUrl: React.FC<{ url: string }> = ({ url }) => {
|
||||
const parts = url.split(/(%host|%port|%secret)/g)
|
||||
|
||||
@ -314,7 +314,7 @@ const Mihomo: React.FC = () => {
|
||||
await restartCore()
|
||||
}
|
||||
|
||||
const handleConfigChangeWithRestart = async (key: string, value: any) => {
|
||||
const handleConfigChangeWithRestart = async (key: string, value: unknown) => {
|
||||
try {
|
||||
await patchAppConfig({ [key]: value })
|
||||
await restartCore()
|
||||
@ -328,14 +328,14 @@ const Mihomo: React.FC = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取GitHub标签列表(带缓存)
|
||||
// 获取 GitHub 标签列表(带缓存)
|
||||
const fetchTags = async (forceRefresh = false) => {
|
||||
setLoadingTags(true)
|
||||
try {
|
||||
const data = await fetchMihomoTags(forceRefresh)
|
||||
setTags(Array.isArray(data) ? data : [])
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch tags:', error)
|
||||
} catch (error: unknown) {
|
||||
console.error('Failed to fetch tags:', String(error))
|
||||
setTags([])
|
||||
toast.error(t('mihomo.error.fetchTagsFailed'))
|
||||
} finally {
|
||||
@ -538,7 +538,7 @@ const Mihomo: React.FC = () => {
|
||||
| 'mihomo-alpha'
|
||||
| 'mihomo-smart'
|
||||
| 'mihomo-specific'
|
||||
// 如果切换到特定版本但没有设置specificVersion,则打开选择模态框
|
||||
// 如果切换到特定版本但没有设置 specificVersion,则打开选择模态框
|
||||
if (selectedCore === 'mihomo-specific' && !specificVersion) {
|
||||
handleOpenModal()
|
||||
} else {
|
||||
|
||||
@ -1,5 +1,3 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import { getPlatform, getVersion } from './ipc'
|
||||
// const originError = console.error
|
||||
// const originWarn = console.warn
|
||||
|
||||
@ -9,7 +9,7 @@ function ipcErrorWrapper(response: any): any {
|
||||
}
|
||||
}
|
||||
|
||||
// GitHub版本管理相关IPC调用
|
||||
// GitHub 版本管理相关 IPC 调用
|
||||
export async function fetchMihomoTags(
|
||||
forceRefresh = false
|
||||
): Promise<{ name: string; zipball_url: string; tarball_url: string }[]> {
|
||||
|
||||
@ -11,10 +11,10 @@
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"jsx": "react-jsx",
|
||||
"baseUrl": ".",
|
||||
"moduleResolution": "bundler",
|
||||
"paths": {
|
||||
"@renderer/*": [
|
||||
"src/renderer/src/*"
|
||||
"./src/renderer/src/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user