mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2026-04-12 23:50:31 +08:00
feat: add GitHub proxy selection with auto multi-source fallback
This commit is contained in:
parent
0f1cd352db
commit
06591b50f4
@ -10,25 +10,47 @@ import i18next from 'i18next'
|
||||
import { mainWindow } from '../window'
|
||||
import { appLogger } from '../utils/logger'
|
||||
import { dataDir, exeDir, exePath, isPortable, resourcesFilesDir } from '../utils/dirs'
|
||||
import { getControledMihomoConfig } from '../config'
|
||||
import { getAppConfig, getControledMihomoConfig } from '../config'
|
||||
import { checkAdminPrivileges } from '../core/manager'
|
||||
import { parse } from '../utils/yaml'
|
||||
import * as chromeRequest from '../utils/chromeRequest'
|
||||
|
||||
export async function checkUpdate(): Promise<IAppVersion | undefined> {
|
||||
const { 'mixed-port': mixedPort = 7890 } = await getControledMihomoConfig()
|
||||
const res = await chromeRequest.get(
|
||||
'https://github.com/mihomo-party-org/mihomo-party/releases/latest/download/latest.yml',
|
||||
{
|
||||
headers: { 'Content-Type': 'application/octet-stream' },
|
||||
proxy: {
|
||||
protocol: 'http',
|
||||
host: '127.0.0.1',
|
||||
port: mixedPort
|
||||
},
|
||||
responseType: 'text'
|
||||
const GITHUB_PROXIES = ['https://gh-proxy.org', 'https://ghfast.top', 'https://down.clashparty.org']
|
||||
|
||||
function buildDownloadUrls(githubUrl: string, proxyPref = ''): string[] {
|
||||
if (proxyPref === 'direct') return [githubUrl]
|
||||
if (proxyPref && proxyPref !== 'auto') return [`${proxyPref}/${githubUrl}`]
|
||||
// auto: try each proxy then fall back to direct
|
||||
return [...GITHUB_PROXIES.map((p) => `${p}/${githubUrl}`), githubUrl]
|
||||
}
|
||||
|
||||
async function tryDownload(
|
||||
urls: string[],
|
||||
options: Parameters<typeof chromeRequest.get>[1]
|
||||
): Promise<Awaited<ReturnType<typeof chromeRequest.get>>> {
|
||||
let lastError: unknown
|
||||
for (const url of urls) {
|
||||
try {
|
||||
return await chromeRequest.get(url, options)
|
||||
} catch (e) {
|
||||
lastError = e
|
||||
}
|
||||
)
|
||||
}
|
||||
throw lastError
|
||||
}
|
||||
|
||||
export async function checkUpdate(): Promise<IAppVersion | undefined> {
|
||||
const [{ 'mixed-port': mixedPort = 7890 }, { githubProxy = '' }] = await Promise.all([
|
||||
getControledMihomoConfig(),
|
||||
getAppConfig()
|
||||
])
|
||||
const githubUrl =
|
||||
'https://github.com/mihomo-party-org/mihomo-party/releases/latest/download/latest.yml'
|
||||
const res = await tryDownload(buildDownloadUrls(githubUrl, githubProxy), {
|
||||
headers: { 'Content-Type': 'application/octet-stream' },
|
||||
proxy: { protocol: 'http', host: '127.0.0.1', port: mixedPort },
|
||||
responseType: 'text'
|
||||
})
|
||||
const latest = parse(res.data as string) as IAppVersion
|
||||
const currentVersion = app.getVersion()
|
||||
if (compareVersions(latest.version, currentVersion) > 0) {
|
||||
@ -57,8 +79,11 @@ function compareVersions(a: string, b: string): number {
|
||||
}
|
||||
|
||||
export async function downloadAndInstallUpdate(version: string): Promise<void> {
|
||||
const { 'mixed-port': mixedPort = 7890 } = await getControledMihomoConfig()
|
||||
const baseUrl = `https://github.com/mihomo-party-org/mihomo-party/releases/download/v${version}/`
|
||||
const [{ 'mixed-port': mixedPort = 7890 }, { githubProxy = '' }] = await Promise.all([
|
||||
getControledMihomoConfig(),
|
||||
getAppConfig()
|
||||
])
|
||||
const githubBase = `https://github.com/mihomo-party-org/mihomo-party/releases/download/v${version}/`
|
||||
const fileMap = {
|
||||
'win32-x64': `clash-party-windows-${version}-x64-setup.exe`,
|
||||
'win32-ia32': `clash-party-windows-${version}-ia32-setup.exe`,
|
||||
@ -84,34 +109,27 @@ export async function downloadAndInstallUpdate(version: string): Promise<void> {
|
||||
file = file.replace('macos', 'catalina')
|
||||
}
|
||||
}
|
||||
const proxy = { protocol: 'http' as const, host: '127.0.0.1', port: mixedPort }
|
||||
try {
|
||||
if (!existsSync(path.join(dataDir(), file))) {
|
||||
const sha256Res = await chromeRequest.get(`${baseUrl}${file}.sha256`, {
|
||||
proxy: {
|
||||
protocol: 'http',
|
||||
host: '127.0.0.1',
|
||||
port: mixedPort
|
||||
},
|
||||
responseType: 'text'
|
||||
})
|
||||
const sha256Res = await tryDownload(
|
||||
buildDownloadUrls(`${githubBase}${file}.sha256`, githubProxy),
|
||||
{ proxy, responseType: 'text' }
|
||||
)
|
||||
const expectedHash = (sha256Res.data as string).trim().split(/\s+/)[0]
|
||||
const res = await chromeRequest.get(`${baseUrl}${file}`, {
|
||||
responseType: 'arraybuffer',
|
||||
timeout: 0,
|
||||
proxy: {
|
||||
protocol: 'http',
|
||||
host: '127.0.0.1',
|
||||
port: mixedPort
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream'
|
||||
},
|
||||
onProgress: (loaded, total) => {
|
||||
mainWindow?.webContents.send('updateDownloadProgress', {
|
||||
status: 'downloading',
|
||||
percent: Math.round((loaded / total) * 100)
|
||||
})
|
||||
}
|
||||
const res = await tryDownload(
|
||||
buildDownloadUrls(`${githubBase}${file}`, githubProxy),
|
||||
{
|
||||
responseType: 'arraybuffer',
|
||||
timeout: 0,
|
||||
proxy,
|
||||
headers: { 'Content-Type': 'application/octet-stream' },
|
||||
onProgress: (loaded: number, total: number) => {
|
||||
mainWindow?.webContents.send('updateDownloadProgress', {
|
||||
status: 'downloading',
|
||||
percent: Math.round((loaded / total) * 100)
|
||||
})
|
||||
}
|
||||
})
|
||||
mainWindow?.webContents.send('updateDownloadProgress', { status: 'verifying' })
|
||||
const fileBuffer = Buffer.from(res.data as ArrayBuffer)
|
||||
|
||||
@ -9,9 +9,18 @@ import { stopCore } from '../core/manager'
|
||||
import { mihomoCoreDir } from './dirs'
|
||||
import * as chromeRequest from './chromeRequest'
|
||||
import { createLogger } from './logger'
|
||||
import { getAppConfig } from '../config'
|
||||
|
||||
const log = createLogger('GitHub')
|
||||
|
||||
const GITHUB_PROXIES = ['https://gh-proxy.org', 'https://ghfast.top', 'https://down.clashparty.org']
|
||||
|
||||
function buildDownloadUrls(githubUrl: string, proxyPref = ''): string[] {
|
||||
if (proxyPref === 'direct') return [githubUrl]
|
||||
if (proxyPref && proxyPref !== 'auto') return [`${proxyPref}/${githubUrl}`]
|
||||
return [...GITHUB_PROXIES.map((p) => `${p}/${githubUrl}`), githubUrl]
|
||||
}
|
||||
|
||||
export interface GitHubTag {
|
||||
name: string
|
||||
zipball_url: string
|
||||
@ -115,22 +124,28 @@ export function clearVersionCache(owner: string, repo: string): void {
|
||||
* @param outputPath 输出路径
|
||||
*/
|
||||
async function downloadGitHubAsset(url: string, outputPath: string): Promise<void> {
|
||||
try {
|
||||
log.debug(`Downloading asset from ${url}`)
|
||||
const response = await chromeRequest.get(url, {
|
||||
responseType: 'arraybuffer',
|
||||
timeout: 30000
|
||||
})
|
||||
|
||||
await writeFile(outputPath, Buffer.from(response.data as Buffer))
|
||||
log.debug(`Successfully downloaded asset to ${outputPath}`)
|
||||
} catch (error) {
|
||||
log.error(`Failed to download asset from ${url}`, error)
|
||||
if (error instanceof Error) {
|
||||
throw new Error(`Download error: ${error.message}`)
|
||||
const { githubProxy = '' } = await getAppConfig()
|
||||
const urls = buildDownloadUrls(url, githubProxy)
|
||||
let lastError: unknown
|
||||
for (const candidate of urls) {
|
||||
try {
|
||||
log.debug(`Downloading asset from ${candidate}`)
|
||||
const response = await chromeRequest.get(candidate, {
|
||||
responseType: 'arraybuffer',
|
||||
timeout: 30000
|
||||
})
|
||||
await writeFile(outputPath, Buffer.from(response.data as Buffer))
|
||||
log.debug(`Successfully downloaded asset to ${outputPath}`)
|
||||
return
|
||||
} catch (error) {
|
||||
log.warn(`Download failed from ${candidate}, trying next`, error)
|
||||
lastError = error
|
||||
}
|
||||
throw new Error('Failed to download core file')
|
||||
}
|
||||
log.error(`Failed to download asset from all sources`, lastError)
|
||||
throw lastError instanceof Error
|
||||
? new Error(`Download error: ${lastError.message}`)
|
||||
: new Error('Failed to download core file')
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -66,6 +66,7 @@ const GeneralConfig: React.FC = () => {
|
||||
customTheme = 'default.css',
|
||||
envType = [platform === 'win32' ? 'powershell' : 'bash'],
|
||||
autoCheckUpdate,
|
||||
githubProxy = 'auto',
|
||||
appTheme = 'system',
|
||||
language = 'zh-CN',
|
||||
triggerMainWindowBehavior = 'show',
|
||||
@ -171,6 +172,24 @@ const GeneralConfig: React.FC = () => {
|
||||
}}
|
||||
/>
|
||||
</SettingItem>
|
||||
<SettingItem title={t('settings.githubProxy')} divider>
|
||||
<Select
|
||||
classNames={{ trigger: 'data-[hover=true]:bg-default-200' }}
|
||||
className="w-50"
|
||||
size="sm"
|
||||
selectedKeys={[githubProxy]}
|
||||
aria-label={t('settings.githubProxy')}
|
||||
onSelectionChange={(v) => {
|
||||
patchAppConfig({ githubProxy: Array.from(v)[0] as string })
|
||||
}}
|
||||
>
|
||||
<SelectItem key="auto">{t('settings.githubProxy.auto')}</SelectItem>
|
||||
<SelectItem key="direct">{t('settings.githubProxy.direct')}</SelectItem>
|
||||
<SelectItem key="https://gh-proxy.org">gh-proxy.org</SelectItem>
|
||||
<SelectItem key="https://ghfast.top">ghfast.top</SelectItem>
|
||||
<SelectItem key="https://down.clashparty.org">down.clashparty.org</SelectItem>
|
||||
</Select>
|
||||
</SettingItem>
|
||||
<SettingItem title={t('settings.silentStart')} divider>
|
||||
<Switch
|
||||
size="sm"
|
||||
@ -204,7 +223,7 @@ const GeneralConfig: React.FC = () => {
|
||||
<div className="flex items-center gap-2">
|
||||
<Input
|
||||
size="sm"
|
||||
className="w-[100px]"
|
||||
className="w-25"
|
||||
type="number"
|
||||
value={autoQuitWithoutCoreDelay.toString()}
|
||||
onValueChange={async (v: string) => {
|
||||
@ -240,7 +259,7 @@ const GeneralConfig: React.FC = () => {
|
||||
>
|
||||
<Select
|
||||
classNames={{ trigger: 'data-[hover=true]:bg-default-200' }}
|
||||
className="w-[150px]"
|
||||
className="w-37.5"
|
||||
size="sm"
|
||||
selectionMode="multiple"
|
||||
selectedKeys={new Set(envType)}
|
||||
|
||||
@ -707,5 +707,9 @@
|
||||
"network.topology.sourcePort": "Source Port",
|
||||
"network.topology.waiting": "Waiting for connections...",
|
||||
"network.topology.pause": "Pause",
|
||||
"network.topology.resume": "Resume"
|
||||
"network.topology.resume": "Resume",
|
||||
"guide.end.description": "Now that you understand the basic usage of the software, import your subscription and start using it. Enjoy!\nYou can also join our official <a href=\"https://t.me/mihomo_party_group\" target=\"_blank\">Telegram group</a> for the latest news.",
|
||||
"settings.githubProxy": "GitHub Download Proxy",
|
||||
"settings.githubProxy.auto": "Auto (proxy first)",
|
||||
"settings.githubProxy.direct": "Direct"
|
||||
}
|
||||
@ -671,5 +671,8 @@
|
||||
"network.topology.sourcePort": "پورت منبع",
|
||||
"network.topology.waiting": "در انتظار اتصال...",
|
||||
"network.topology.pause": "مکث",
|
||||
"network.topology.resume": "ادامه"
|
||||
"network.topology.resume": "ادامه",
|
||||
"settings.githubProxy": "پروکسی دانلود GitHub",
|
||||
"settings.githubProxy.auto": "خودکار (پروکسی اول)",
|
||||
"settings.githubProxy.direct": "مستقیم"
|
||||
}
|
||||
@ -673,5 +673,8 @@
|
||||
"network.topology.sourcePort": "Порт источника",
|
||||
"network.topology.waiting": "Ожидание подключений...",
|
||||
"network.topology.pause": "Пауза",
|
||||
"network.topology.resume": "Возобновить"
|
||||
"network.topology.resume": "Возобновить",
|
||||
"settings.githubProxy": "Прокси для загрузки GitHub",
|
||||
"settings.githubProxy.auto": "Авто (сначала прокси)",
|
||||
"settings.githubProxy.direct": "Прямое подключение"
|
||||
}
|
||||
@ -707,5 +707,8 @@
|
||||
"network.topology.sourcePort": "来源端口",
|
||||
"network.topology.waiting": "等待连接数据...",
|
||||
"network.topology.pause": "暂停",
|
||||
"network.topology.resume": "恢复"
|
||||
"network.topology.resume": "恢复",
|
||||
"settings.githubProxy": "GitHub 下载代理",
|
||||
"settings.githubProxy.auto": "自动(优先代理)",
|
||||
"settings.githubProxy.direct": "直连"
|
||||
}
|
||||
@ -707,5 +707,8 @@
|
||||
"network.topology.sourcePort": "來源連接埠",
|
||||
"network.topology.waiting": "等待連線資料...",
|
||||
"network.topology.pause": "暫停",
|
||||
"network.topology.resume": "繼續"
|
||||
"network.topology.resume": "繼續",
|
||||
"settings.githubProxy": "GitHub 下載代理",
|
||||
"settings.githubProxy.auto": "自動(優先代理)",
|
||||
"settings.githubProxy.direct": "直連"
|
||||
}
|
||||
1
src/shared/types.d.ts
vendored
1
src/shared/types.d.ts
vendored
@ -303,6 +303,7 @@ interface IAppConfig {
|
||||
appTheme: AppTheme
|
||||
customTheme?: string
|
||||
autoCheckUpdate: boolean
|
||||
githubProxy?: string
|
||||
silentStart: boolean
|
||||
autoCloseConnection: boolean
|
||||
sysProxy: ISysProxyConfig
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user