fix: 收紧远程配置拉取并强化更新安装确认(resolve #137)

This commit is contained in:
digua
2026-04-14 19:45:45 +08:00
committed by digua
parent a330e87257
commit 7b93e89189
2 changed files with 58 additions and 11 deletions
+52 -4
View File
@@ -12,6 +12,26 @@ type AppWithQuitFlag = typeof app & { isQuiting?: boolean }
// 通过类型扩展记录应用退出意图,避免使用 @ts-ignore。
const appWithQuitFlag = app as AppWithQuitFlag
const REMOTE_CONFIG_ALLOWED_DOMAINS = ['chatlab.fun', '1app.top']
const REMOTE_CONFIG_TIMEOUT_MS = 8000
const REMOTE_CONFIG_MAX_BYTES = 1024 * 1024 // 1MB
function isAllowedRemoteConfigUrl(rawUrl: string): boolean {
let parsed: URL
try {
parsed = new URL(rawUrl)
} catch {
return false
}
if (parsed.protocol !== 'https:') return false
if (parsed.username || parsed.password) return false
if (parsed.port && parsed.port !== '443') return false
const hostname = parsed.hostname.toLowerCase()
return REMOTE_CONFIG_ALLOWED_DOMAINS.some((domain) => hostname === domain || hostname.endsWith(`.${domain}`))
}
/**
* 注册窗口和文件系统操作 IPC 处理器
*/
@@ -89,27 +109,55 @@ export function registerWindowHandlers(ctx: IpcContext): void {
// 获取远程配置(支持 JSON 和纯文本/Markdown
ipcMain.handle('app:fetchRemoteConfig', async (_, url: string) => {
const normalizedUrl = typeof url === 'string' ? url.trim() : ''
if (!isAllowedRemoteConfigUrl(normalizedUrl)) {
return { success: false, error: 'URL is not allowed' }
}
const abortController = new AbortController()
const timeout = setTimeout(() => abortController.abort(), REMOTE_CONFIG_TIMEOUT_MS)
try {
const response = await fetch(url)
const response = await fetch(normalizedUrl, { signal: abortController.signal })
const finalUrl = response.url || normalizedUrl
if (!isAllowedRemoteConfigUrl(finalUrl)) {
return { success: false, error: 'Redirect URL is not allowed' }
}
const contentType = response.headers.get('content-type') || ''
const contentLength = Number(response.headers.get('content-length') || 0)
if (Number.isFinite(contentLength) && contentLength > REMOTE_CONFIG_MAX_BYTES) {
return { success: false, error: 'Response is too large' }
}
if (!response.ok) {
return { success: false, error: `HTTP ${response.status}: ${response.statusText}` }
}
const buffer = Buffer.from(await response.arrayBuffer())
if (buffer.length > REMOTE_CONFIG_MAX_BYTES) {
return { success: false, error: 'Response is too large' }
}
// 根据 Content-Type 或 URL 后缀决定解析方式
const isJson = contentType.includes('application/json') || url.endsWith('.json')
const isJson = contentType.includes('application/json') || finalUrl.endsWith('.json')
if (isJson) {
const data = await response.json()
const data = JSON.parse(buffer.toString('utf-8'))
return { success: true, data }
} else {
// 纯文本/Markdown 等其他格式
const data = await response.text()
const data = buffer.toString('utf-8')
return { success: true, data }
}
} catch (error) {
if (error instanceof Error && error.name === 'AbortError') {
return { success: false, error: 'Request timeout' }
}
return { success: false, error: String(error) }
} finally {
clearTimeout(timeout)
}
})
+6 -7
View File
@@ -16,7 +16,7 @@ const R2_MIRROR_URL = 'https://chatlab.1app.top/releases/download'
// 更新源类型
type UpdateSource = 'github' | 'r2'
// 当前使用的更新源(默认 R2 优先)
// 当前使用的更新源(默认 R2 优先GitHub 作为网络失败兜底
let currentSource: UpdateSource = 'r2'
// 是否已尝试过备用源
@@ -57,7 +57,6 @@ function switchToR2Mirror(): void {
*/
function switchToGitHub(): void {
currentSource = 'github'
// 使用 GitHub 作为 generic provider
autoUpdater.setFeedURL({
provider: 'github',
owner: 'hellodigua',
@@ -117,7 +116,7 @@ const checkUpdate = (win) => {
configureUpdateProxy()
autoUpdater.autoDownload = false // 自动下载
autoUpdater.autoInstallOnAppQuit = true // 应用退出自动安装
autoUpdater.autoInstallOnAppQuit = false // 关闭退出自动安装,必须显式确认安装
// 开发模式下模拟更新检测(需要创建 dev-app-update.yml 文件)
// 取消下面的注释来启用开发模式更新测试
@@ -187,9 +186,9 @@ const checkUpdate = (win) => {
.showMessageBox({
title: t('update.downloadComplete'),
message: t('update.readyToInstall'),
buttons: [t('update.install'), platform.isMacOS ? t('update.remindLater') : t('update.installOnQuit')],
buttons: [t('update.install'), t('update.remindLater')],
defaultId: 1,
cancelId: 2,
cancelId: 1,
type: 'question',
})
.then(async (result) => {
@@ -230,11 +229,11 @@ const checkUpdate = (win) => {
}
})
// 错误处理(智能切换备用源)
// 错误处理(网络失败时切换备用源)
autoUpdater.on('error', (err) => {
logger.error(`[Update] Update error (${currentSource}): ${err.message || err}`)
// 如果是 R2 源且为网络错误,尝试切换到 GitHub 备用源
// 默认 R2 源网络失败时,尝试切换到 GitHub
if (currentSource === 'r2' && !hasTriedFallback && isNetworkError(err)) {
hasTriedFallback = true
logger.info('[Update] R2 mirror failed, trying GitHub fallback...')