mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-27 21:20:29 +08:00
Compare commits
No commits in common. "f541b5ead1eb1955fa065beb636023683bbd137e" and "b76757bc192f3221bdd93640965193895569adaa" have entirely different histories.
f541b5ead1
...
b76757bc19
@ -142,7 +142,6 @@ export async function createProfile(item: Partial<IProfileItem>): Promise<IProfi
|
|||||||
useProxy: item.useProxy || false,
|
useProxy: item.useProxy || false,
|
||||||
allowFixedInterval: item.allowFixedInterval || false,
|
allowFixedInterval: item.allowFixedInterval || false,
|
||||||
autoUpdate: item.autoUpdate ?? false,
|
autoUpdate: item.autoUpdate ?? false,
|
||||||
authToken: item.authToken,
|
|
||||||
updated: new Date().getTime()
|
updated: new Date().getTime()
|
||||||
} as IProfileItem
|
} as IProfileItem
|
||||||
switch (newItem.type) {
|
switch (newItem.type) {
|
||||||
@ -160,24 +159,14 @@ export async function createProfile(item: Partial<IProfileItem>): Promise<IProfi
|
|||||||
} else {
|
} else {
|
||||||
urlObj.searchParams.delete('proxy')
|
urlObj.searchParams.delete('proxy')
|
||||||
}
|
}
|
||||||
const headers: Record<string, string> = {
|
|
||||||
'User-Agent': userAgent || `mihomo.party/v${app.getVersion()} (clash.meta)`
|
|
||||||
}
|
|
||||||
if (item.authToken) {
|
|
||||||
headers['Authorization'] = item.authToken
|
|
||||||
}
|
|
||||||
res = await chromeRequest.get(urlObj.toString(), {
|
res = await chromeRequest.get(urlObj.toString(), {
|
||||||
headers,
|
headers: {
|
||||||
|
'User-Agent': userAgent || `mihomo.party/v${app.getVersion()} (clash.meta)`
|
||||||
|
},
|
||||||
responseType: 'text',
|
responseType: 'text',
|
||||||
timeout: subscriptionTimeout
|
timeout: subscriptionTimeout
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
const headers: Record<string, string> = {
|
|
||||||
'User-Agent': userAgent || `mihomo.party/v${app.getVersion()} (clash.meta)`
|
|
||||||
}
|
|
||||||
if (item.authToken) {
|
|
||||||
headers['Authorization'] = item.authToken
|
|
||||||
}
|
|
||||||
res = await chromeRequest.get(item.url, {
|
res = await chromeRequest.get(item.url, {
|
||||||
proxy: newItem.useProxy
|
proxy: newItem.useProxy
|
||||||
? {
|
? {
|
||||||
@ -186,7 +175,9 @@ export async function createProfile(item: Partial<IProfileItem>): Promise<IProfi
|
|||||||
port: mixedPort
|
port: mixedPort
|
||||||
}
|
}
|
||||||
: false,
|
: false,
|
||||||
headers,
|
headers: {
|
||||||
|
'User-Agent': userAgent || `mihomo.party/v${app.getVersion()} (clash.meta)`
|
||||||
|
},
|
||||||
responseType: 'text',
|
responseType: 'text',
|
||||||
timeout: subscriptionTimeout
|
timeout: subscriptionTimeout
|
||||||
})
|
})
|
||||||
|
|||||||
@ -163,7 +163,7 @@ export async function startCore(detached = false): Promise<Promise<void>[]> {
|
|||||||
// 内核日志输出到独立的 core-日期.log 文件
|
// 内核日志输出到独立的 core-日期.log 文件
|
||||||
const stdout = createWriteStream(coreLogPath(), { flags: 'a' })
|
const stdout = createWriteStream(coreLogPath(), { flags: 'a' })
|
||||||
const stderr = createWriteStream(coreLogPath(), { flags: 'a' })
|
const stderr = createWriteStream(coreLogPath(), { flags: 'a' })
|
||||||
|
|
||||||
child = spawn(
|
child = spawn(
|
||||||
corePath,
|
corePath,
|
||||||
['-d', diffWorkDir ? mihomoProfileWorkDir(current) : mihomoWorkDir(), ctlParam, dynamicIpcPath],
|
['-d', diffWorkDir ? mihomoProfileWorkDir(current) : mihomoWorkDir(), ctlParam, dynamicIpcPath],
|
||||||
@ -307,7 +307,7 @@ async function cleanupWindowsNamedPipes(): Promise<void> {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const { stdout } = await execPromise(
|
const { stdout } = await execPromise(
|
||||||
`powershell -NoProfile -Command "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-Process | Where-Object {$_.ProcessName -like '*mihomo*'} | Select-Object Id,ProcessName | ConvertTo-Json"`,
|
`powershell -Command "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; Get-Process | Where-Object {$_.ProcessName -like '*mihomo*'} | Select-Object Id,ProcessName | ConvertTo-Json"`,
|
||||||
{ encoding: 'utf8' }
|
{ encoding: 'utf8' }
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -458,7 +458,7 @@ async function checkProfile(): Promise<void> {
|
|||||||
const { current } = await getProfileConfig()
|
const { current } = await getProfileConfig()
|
||||||
const corePath = mihomoCorePath(core)
|
const corePath = mihomoCorePath(core)
|
||||||
const execFilePromise = promisify(execFile)
|
const execFilePromise = promisify(execFile)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await execFilePromise(corePath, [
|
await execFilePromise(corePath, [
|
||||||
'-t',
|
'-t',
|
||||||
@ -598,7 +598,7 @@ export async function checkAdminPrivileges(): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const execPromise = promisify(exec)
|
const execPromise = promisify(exec)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// fltmc 检测管理员权限
|
// fltmc 检测管理员权限
|
||||||
await execPromise('chcp 65001 >nul 2>&1 && fltmc', { encoding: 'utf8' })
|
await execPromise('chcp 65001 >nul 2>&1 && fltmc', { encoding: 'utf8' })
|
||||||
@ -607,7 +607,7 @@ export async function checkAdminPrivileges(): Promise<boolean> {
|
|||||||
} catch (fltmcError: any) {
|
} catch (fltmcError: any) {
|
||||||
const errorCode = fltmcError?.code || 0
|
const errorCode = fltmcError?.code || 0
|
||||||
await managerLogger.debug(`fltmc failed with code ${errorCode}, trying net session as fallback`)
|
await managerLogger.debug(`fltmc failed with code ${errorCode}, trying net session as fallback`)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// net session 备用
|
// net session 备用
|
||||||
await execPromise('chcp 65001 >nul 2>&1 && net session', { encoding: 'utf8' })
|
await execPromise('chcp 65001 >nul 2>&1 && net session', { encoding: 'utf8' })
|
||||||
@ -678,13 +678,13 @@ export async function restartAsAdmin(forTun: boolean = true): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
// 处理路径和参数的引号
|
// 处理路径和参数的引号
|
||||||
const escapedExePath = exePath.replace(/'/g, "''")
|
const escapedExePath = exePath.replace(/'/g, "''")
|
||||||
const argsString = restartArgs.map((arg) => arg.replace(/'/g, "''")).join("', '")
|
const argsString = restartArgs.map(arg => arg.replace(/'/g, "''")).join("', '")
|
||||||
|
|
||||||
let command: string
|
let command: string
|
||||||
if (restartArgs.length > 0) {
|
if (restartArgs.length > 0) {
|
||||||
command = `powershell -NoProfile -Command "Start-Process -FilePath '${escapedExePath}' -ArgumentList '${argsString}' -Verb RunAs"`
|
command = `powershell -Command "Start-Process -FilePath '${escapedExePath}' -ArgumentList '${argsString}' -Verb RunAs"`
|
||||||
} else {
|
} else {
|
||||||
command = `powershell -NoProfile -Command "Start-Process -FilePath '${escapedExePath}' -Verb RunAs"`
|
command = `powershell -Command "Start-Process -FilePath '${escapedExePath}' -Verb RunAs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
await managerLogger.info('Restarting as administrator with command', command)
|
await managerLogger.info('Restarting as administrator with command', command)
|
||||||
|
|||||||
@ -100,7 +100,7 @@ export async function downloadAndInstallUpdate(version: string): Promise<void> {
|
|||||||
try {
|
try {
|
||||||
const installerPath = path.join(dataDir(), file)
|
const installerPath = path.join(dataDir(), file)
|
||||||
const isAdmin = await checkAdminPrivileges()
|
const isAdmin = await checkAdminPrivileges()
|
||||||
|
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
await appLogger.info('Running installer with existing admin privileges')
|
await appLogger.info('Running installer with existing admin privileges')
|
||||||
spawn(installerPath, ['/S', '--force-run'], {
|
spawn(installerPath, ['/S', '--force-run'], {
|
||||||
@ -111,20 +111,20 @@ export async function downloadAndInstallUpdate(version: string): Promise<void> {
|
|||||||
// 提升权限安装
|
// 提升权限安装
|
||||||
const escapedPath = installerPath.replace(/'/g, "''")
|
const escapedPath = installerPath.replace(/'/g, "''")
|
||||||
const args = ['/S', '--force-run']
|
const args = ['/S', '--force-run']
|
||||||
const argsString = args.map((arg) => arg.replace(/'/g, "''")).join("', '")
|
const argsString = args.map(arg => arg.replace(/'/g, "''")).join("', '")
|
||||||
|
|
||||||
const command = `powershell -NoProfile -Command "Start-Process -FilePath '${escapedPath}' -ArgumentList '${argsString}' -Verb RunAs -WindowStyle Hidden"`
|
const command = `powershell -Command "Start-Process -FilePath '${escapedPath}' -ArgumentList '${argsString}' -Verb RunAs -WindowStyle Hidden"`
|
||||||
|
|
||||||
await appLogger.info('Starting installer with elevated privileges')
|
await appLogger.info('Starting installer with elevated privileges')
|
||||||
|
|
||||||
const execPromise = promisify(exec)
|
const execPromise = promisify(exec)
|
||||||
await execPromise(command, { windowsHide: true })
|
await execPromise(command, { windowsHide: true })
|
||||||
|
|
||||||
await appLogger.info('Installer started successfully with elevation')
|
await appLogger.info('Installer started successfully with elevation')
|
||||||
}
|
}
|
||||||
} catch (installerError) {
|
} catch (installerError) {
|
||||||
await appLogger.error('Failed to start installer, trying fallback', installerError)
|
await appLogger.error('Failed to start installer, trying fallback', installerError)
|
||||||
|
|
||||||
// Fallback: 尝试使用shell.openPath打开安装包
|
// Fallback: 尝试使用shell.openPath打开安装包
|
||||||
try {
|
try {
|
||||||
await shell.openPath(path.join(dataDir(), file))
|
await shell.openPath(path.join(dataDir(), file))
|
||||||
|
|||||||
@ -88,13 +88,11 @@ export async function enableAutoRun(): Promise<void> {
|
|||||||
const isAdmin = await checkAdminPrivileges()
|
const isAdmin = await checkAdminPrivileges()
|
||||||
await writeFile(taskFilePath, Buffer.from(`\ufeff${getTaskXml(isAdmin)}`, 'utf-16le'))
|
await writeFile(taskFilePath, Buffer.from(`\ufeff${getTaskXml(isAdmin)}`, 'utf-16le'))
|
||||||
if (isAdmin) {
|
if (isAdmin) {
|
||||||
await execPromise(
|
await execPromise(`%SystemRoot%\\System32\\schtasks.exe /create /tn "${appName}" /xml "${taskFilePath}" /f`)
|
||||||
`%SystemRoot%\\System32\\schtasks.exe /create /tn "${appName}" /xml "${taskFilePath}" /f`
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
await execPromise(
|
await execPromise(
|
||||||
`powershell -NoProfile -Command "Start-Process schtasks -Verb RunAs -ArgumentList '/create', '/tn', '${appName}', '/xml', '${taskFilePath}', '/f' -WindowStyle Hidden"`
|
`powershell -Command "Start-Process schtasks -Verb RunAs -ArgumentList '/create', '/tn', '${appName}', '/xml', '${taskFilePath}', '/f' -WindowStyle Hidden"`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
@ -142,9 +140,7 @@ export async function disableAutoRun(): Promise<void> {
|
|||||||
await execPromise(`%SystemRoot%\\System32\\schtasks.exe /delete /tn "${appName}" /f`)
|
await execPromise(`%SystemRoot%\\System32\\schtasks.exe /delete /tn "${appName}" /f`)
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
await execPromise(
|
await execPromise(`powershell -Command "Start-Process schtasks -Verb RunAs -ArgumentList '/delete', '/tn', '${appName}', '/f' -WindowStyle Hidden"`)
|
||||||
`powershell -NoProfile -Command "Start-Process schtasks -Verb RunAs -ArgumentList '/delete', '/tn', '${appName}', '/f' -WindowStyle Hidden"`
|
|
||||||
)
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await managerLogger.info('Maybe the user rejected the UAC dialog?')
|
await managerLogger.info('Maybe the user rejected the UAC dialog?')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,7 +43,7 @@ export async function openUWPTool(): Promise<void> {
|
|||||||
|
|
||||||
if (!isAdmin) {
|
if (!isAdmin) {
|
||||||
const escapedPath = uwpToolPath.replace(/'/g, "''")
|
const escapedPath = uwpToolPath.replace(/'/g, "''")
|
||||||
const command = `powershell -NoProfile -Command "Start-Process -FilePath '${escapedPath}' -Verb RunAs -Wait"`
|
const command = `powershell -Command "Start-Process -FilePath '${escapedPath}' -Verb RunAs -Wait"`
|
||||||
|
|
||||||
await execPromise(command, { windowsHide: true })
|
await execPromise(command, { windowsHide: true })
|
||||||
return
|
return
|
||||||
|
|||||||
@ -91,18 +91,6 @@ const EditInfoModal: React.FC<Props> = (props) => {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
<SettingItem title={t('profiles.editInfo.authToken')}>
|
|
||||||
<Input
|
|
||||||
size="sm"
|
|
||||||
type="password"
|
|
||||||
className={cn(inputWidth)}
|
|
||||||
value={values.authToken || ''}
|
|
||||||
onValueChange={(v) => {
|
|
||||||
setValues({ ...values, authToken: v })
|
|
||||||
}}
|
|
||||||
placeholder={t('profiles.editInfo.authTokenPlaceholder')}
|
|
||||||
/>
|
|
||||||
</SettingItem>
|
|
||||||
<SettingItem title={t('profiles.editInfo.useProxy')}>
|
<SettingItem title={t('profiles.editInfo.useProxy')}>
|
||||||
<Switch
|
<Switch
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@ -438,8 +438,6 @@
|
|||||||
"profiles.editInfo.title": "Edit Information",
|
"profiles.editInfo.title": "Edit Information",
|
||||||
"profiles.editInfo.name": "Name",
|
"profiles.editInfo.name": "Name",
|
||||||
"profiles.editInfo.url": "Subscription URL",
|
"profiles.editInfo.url": "Subscription URL",
|
||||||
"profiles.editInfo.authToken": "Authorization Token",
|
|
||||||
"profiles.editInfo.authTokenPlaceholder": "Bearer token or other auth header value",
|
|
||||||
"profiles.editInfo.useProxy": "Use Proxy to Update",
|
"profiles.editInfo.useProxy": "Use Proxy to Update",
|
||||||
"profiles.editInfo.interval": "Upd. Interval",
|
"profiles.editInfo.interval": "Upd. Interval",
|
||||||
"profiles.editInfo.intervalPlaceholder": "e.g.: 30 or '0 * * * *'",
|
"profiles.editInfo.intervalPlaceholder": "e.g.: 30 or '0 * * * *'",
|
||||||
|
|||||||
@ -443,8 +443,6 @@
|
|||||||
"profiles.editInfo.title": "编辑信息",
|
"profiles.editInfo.title": "编辑信息",
|
||||||
"profiles.editInfo.name": "名称",
|
"profiles.editInfo.name": "名称",
|
||||||
"profiles.editInfo.url": "订阅地址",
|
"profiles.editInfo.url": "订阅地址",
|
||||||
"profiles.editInfo.authToken": "授权令牌",
|
|
||||||
"profiles.editInfo.authTokenPlaceholder": "Bearer token 或其他认证头值",
|
|
||||||
"profiles.editInfo.useProxy": "使用代理更新",
|
"profiles.editInfo.useProxy": "使用代理更新",
|
||||||
"profiles.editInfo.interval": "更新间隔",
|
"profiles.editInfo.interval": "更新间隔",
|
||||||
"profiles.editInfo.intervalPlaceholder": "例如:30 或 '0 * * * *'",
|
"profiles.editInfo.intervalPlaceholder": "例如:30 或 '0 * * * *'",
|
||||||
|
|||||||
@ -7,8 +7,7 @@ import {
|
|||||||
DropdownItem,
|
DropdownItem,
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownTrigger,
|
DropdownTrigger,
|
||||||
Input,
|
Input
|
||||||
Tooltip
|
|
||||||
} from '@heroui/react'
|
} from '@heroui/react'
|
||||||
import BasePage from '@renderer/components/base/base-page'
|
import BasePage from '@renderer/components/base/base-page'
|
||||||
import ProfileItem from '@renderer/components/profiles/profile-item'
|
import ProfileItem from '@renderer/components/profiles/profile-item'
|
||||||
@ -51,8 +50,6 @@ const Profiles: React.FC = () => {
|
|||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const [sortedItems, setSortedItems] = useState(items)
|
const [sortedItems, setSortedItems] = useState(items)
|
||||||
const [useProxy, setUseProxy] = useState(false)
|
const [useProxy, setUseProxy] = useState(false)
|
||||||
const [authToken, setAuthToken] = useState('')
|
|
||||||
const [showAdvanced, setShowAdvanced] = useState(false)
|
|
||||||
const [subStoreImporting, setSubStoreImporting] = useState(false)
|
const [subStoreImporting, setSubStoreImporting] = useState(false)
|
||||||
const [importing, setImporting] = useState(false)
|
const [importing, setImporting] = useState(false)
|
||||||
const [updating, setUpdating] = useState(false)
|
const [updating, setUpdating] = useState(false)
|
||||||
@ -128,15 +125,8 @@ const Profiles: React.FC = () => {
|
|||||||
}, [subs, collections])
|
}, [subs, collections])
|
||||||
const handleImport = async (): Promise<void> => {
|
const handleImport = async (): Promise<void> => {
|
||||||
setImporting(true)
|
setImporting(true)
|
||||||
await addProfileItem({
|
await addProfileItem({ name: '', type: 'remote', url, useProxy })
|
||||||
name: '',
|
|
||||||
type: 'remote',
|
|
||||||
url,
|
|
||||||
useProxy,
|
|
||||||
authToken: authToken || undefined
|
|
||||||
})
|
|
||||||
setUrl('')
|
setUrl('')
|
||||||
setAuthToken('')
|
|
||||||
setImporting(false)
|
setImporting(false)
|
||||||
}
|
}
|
||||||
const pageRef = useRef<HTMLDivElement>(null)
|
const pageRef = useRef<HTMLDivElement>(null)
|
||||||
@ -235,79 +225,68 @@ const Profiles: React.FC = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div className="sticky profiles-sticky top-0 z-40 bg-background">
|
<div className="sticky profiles-sticky top-0 z-40 bg-background">
|
||||||
<div className="flex flex-col gap-2 p-2">
|
<div className="flex p-2">
|
||||||
<div className="flex gap-2">
|
<Input
|
||||||
<Input
|
size="sm"
|
||||||
size="sm"
|
placeholder={t('profiles.input.placeholder')}
|
||||||
placeholder={t('profiles.input.placeholder')}
|
value={url}
|
||||||
value={url}
|
onValueChange={setUrl}
|
||||||
onValueChange={setUrl}
|
onKeyUp={handleInputKeyUp}
|
||||||
onKeyUp={handleInputKeyUp}
|
endContent={
|
||||||
className="flex-1"
|
<>
|
||||||
endContent={
|
<Button
|
||||||
<>
|
size="md"
|
||||||
<Button
|
isIconOnly
|
||||||
size="md"
|
variant="light"
|
||||||
isIconOnly
|
onPress={() => {
|
||||||
variant="light"
|
navigator.clipboard.readText().then((text) => {
|
||||||
onPress={() => {
|
setUrl(text)
|
||||||
navigator.clipboard.readText().then((text) => {
|
})
|
||||||
setUrl(text)
|
}}
|
||||||
})
|
className="mr-2"
|
||||||
}}
|
>
|
||||||
className="mr-2"
|
<MdContentPaste className="text-lg" />
|
||||||
>
|
</Button>
|
||||||
<MdContentPaste className="text-lg" />
|
<Checkbox
|
||||||
</Button>
|
className="whitespace-nowrap"
|
||||||
<Checkbox
|
checked={useProxy}
|
||||||
className="whitespace-nowrap"
|
onValueChange={setUseProxy}
|
||||||
checked={useProxy}
|
>
|
||||||
onValueChange={setUseProxy}
|
{t('profiles.useProxy')}
|
||||||
>
|
</Checkbox>
|
||||||
{t('profiles.useProxy')}
|
</>
|
||||||
</Checkbox>
|
}
|
||||||
</>
|
/>
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Tooltip content={t('profiles.editInfo.authToken')} placement="bottom">
|
<Button
|
||||||
<Button
|
size="sm"
|
||||||
size="sm"
|
color="primary"
|
||||||
variant={showAdvanced ? 'solid' : 'light'}
|
className="ml-2"
|
||||||
isIconOnly
|
isDisabled={isUrlEmpty}
|
||||||
onPress={() => setShowAdvanced(!showAdvanced)}
|
isLoading={importing}
|
||||||
>
|
onPress={handleImport}
|
||||||
🔑
|
>
|
||||||
</Button>
|
{t('profiles.import')}
|
||||||
</Tooltip>
|
</Button>
|
||||||
<Button
|
{useSubStore && (
|
||||||
size="sm"
|
<Dropdown
|
||||||
color="primary"
|
onOpenChange={() => {
|
||||||
isDisabled={isUrlEmpty}
|
mutateSubs()
|
||||||
isLoading={importing}
|
mutateCollections()
|
||||||
onPress={handleImport}
|
}}
|
||||||
>
|
>
|
||||||
{t('profiles.import')}
|
<DropdownTrigger>
|
||||||
</Button>
|
<Button
|
||||||
{useSubStore && (
|
isLoading={subStoreImporting}
|
||||||
<Dropdown
|
title="Sub-Store"
|
||||||
onOpenChange={() => {
|
className="ml-2 substore-import"
|
||||||
mutateSubs()
|
size="sm"
|
||||||
mutateCollections()
|
isIconOnly
|
||||||
}}
|
color="primary"
|
||||||
>
|
>
|
||||||
<DropdownTrigger>
|
<SubStoreIcon className="text-lg" />
|
||||||
<Button
|
</Button>
|
||||||
isLoading={subStoreImporting}
|
</DropdownTrigger>
|
||||||
title="Sub-Store"
|
|
||||||
className="substore-import"
|
|
||||||
size="sm"
|
|
||||||
isIconOnly
|
|
||||||
color="primary"
|
|
||||||
>
|
|
||||||
<SubStoreIcon className="text-lg" />
|
|
||||||
</Button>
|
|
||||||
</DropdownTrigger>
|
|
||||||
<DropdownMenu
|
<DropdownMenu
|
||||||
className="max-h-[calc(100vh-200px)] overflow-y-auto"
|
className="max-h-[calc(100vh-200px)] overflow-y-auto"
|
||||||
onAction={async (key) => {
|
onAction={async (key) => {
|
||||||
@ -363,53 +342,40 @@ const Profiles: React.FC = () => {
|
|||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
))}
|
))}
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</Dropdown>
|
|
||||||
)}
|
|
||||||
<Dropdown>
|
|
||||||
<DropdownTrigger>
|
|
||||||
<Button className="new-profile" size="sm" isIconOnly color="primary">
|
|
||||||
<FaPlus />
|
|
||||||
</Button>
|
|
||||||
</DropdownTrigger>
|
|
||||||
<DropdownMenu
|
|
||||||
onAction={async (key) => {
|
|
||||||
if (key === 'open') {
|
|
||||||
try {
|
|
||||||
const files = await getFilePath(['yml', 'yaml'])
|
|
||||||
if (files?.length) {
|
|
||||||
const content = await readTextFile(files[0])
|
|
||||||
const fileName = files[0].split('/').pop()?.split('\\').pop()
|
|
||||||
await addProfileItem({ name: fileName, type: 'local', file: content })
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
alert(e)
|
|
||||||
}
|
|
||||||
} else if (key === 'new') {
|
|
||||||
await addProfileItem({
|
|
||||||
name: t('profiles.newProfile'),
|
|
||||||
type: 'local',
|
|
||||||
file: 'proxies: []\nproxy-groups: []\nrules: []'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<DropdownItem key="open">{t('profiles.open')}</DropdownItem>
|
|
||||||
<DropdownItem key="new">{t('profiles.new')}</DropdownItem>
|
|
||||||
</DropdownMenu>
|
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
|
||||||
{showAdvanced && (
|
|
||||||
<Input
|
|
||||||
size="sm"
|
|
||||||
type="password"
|
|
||||||
placeholder={t('profiles.editInfo.authTokenPlaceholder')}
|
|
||||||
value={authToken}
|
|
||||||
onValueChange={setAuthToken}
|
|
||||||
onKeyUp={handleInputKeyUp}
|
|
||||||
className="w-full"
|
|
||||||
startContent={<span className="text-default-400 text-sm">🔑</span>}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
|
<Dropdown>
|
||||||
|
<DropdownTrigger>
|
||||||
|
<Button className="ml-2 new-profile" size="sm" isIconOnly color="primary">
|
||||||
|
<FaPlus />
|
||||||
|
</Button>
|
||||||
|
</DropdownTrigger>
|
||||||
|
<DropdownMenu
|
||||||
|
onAction={async (key) => {
|
||||||
|
if (key === 'open') {
|
||||||
|
try {
|
||||||
|
const files = await getFilePath(['yml', 'yaml'])
|
||||||
|
if (files?.length) {
|
||||||
|
const content = await readTextFile(files[0])
|
||||||
|
const fileName = files[0].split('/').pop()?.split('\\').pop()
|
||||||
|
await addProfileItem({ name: fileName, type: 'local', file: content })
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
alert(e)
|
||||||
|
}
|
||||||
|
} else if (key === 'new') {
|
||||||
|
await addProfileItem({
|
||||||
|
name: t('profiles.newProfile'),
|
||||||
|
type: 'local',
|
||||||
|
file: 'proxies: []\nproxy-groups: []\nrules: []'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DropdownItem key="open">{t('profiles.open')}</DropdownItem>
|
||||||
|
<DropdownItem key="new">{t('profiles.new')}</DropdownItem>
|
||||||
|
</DropdownMenu>
|
||||||
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
<Divider />
|
<Divider />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
1
src/shared/types.d.ts
vendored
1
src/shared/types.d.ts
vendored
@ -492,7 +492,6 @@ interface IProfileItem {
|
|||||||
substore?: boolean
|
substore?: boolean
|
||||||
allowFixedInterval?: boolean
|
allowFixedInterval?: boolean
|
||||||
autoUpdate?: boolean
|
autoUpdate?: boolean
|
||||||
authToken?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ISubStoreSub {
|
interface ISubStoreSub {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user