most-box 0.2.7 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/README.md +9 -1
  2. package/electron/main.js +30 -420
  3. package/electron/preload.js +1 -16
  4. package/electron/updateChecker.js +6 -3
  5. package/electron/updateChecker.test.js +13 -97
  6. package/out/admin/index.html +0 -0
  7. package/out/app/index.html +0 -0
  8. package/out/assets/{FilePreviewOverlay-CW8lKJCw.js → FilePreviewOverlay-D8H61qbp.js} +1 -1
  9. package/out/assets/LanguageToggle-CSQtRZ3A.js +1 -0
  10. package/out/assets/MarketingHeader-CrYsQsQ5.js +1 -0
  11. package/out/assets/MarketingLayout-CQurMOSD.js +1 -0
  12. package/out/assets/{MarketingThemeToggle-Cl7PhFgv.js → MarketingThemeToggle-CIKP8x9W.js} +1 -1
  13. package/out/assets/{MilkdownEditor-B5SKAyjg.js → MilkdownEditor-DrsyaR4h.js} +1 -1
  14. package/out/assets/{OpenSidebarButton-Bdx86oG8.js → OpenSidebarButton-DnEgqP0S.js} +1 -1
  15. package/out/assets/{SidebarAccount-c8jirEMF.js → SidebarAccount-B4OAl0as.js} +1 -1
  16. package/out/assets/SidebarHomeLink-BdsUM4Kt.js +1 -0
  17. package/out/assets/arrow-right-BU9c_SL4.js +1 -0
  18. package/out/assets/{circle-alert-Dkgyw_GE.js → circle-alert-iuHzciph.js} +1 -1
  19. package/out/assets/{copy-KIwotKoP.js → copy-DEzFUzeX.js} +1 -1
  20. package/out/assets/{download-DPA-3LpR.js → download-jEPn2njL.js} +1 -1
  21. package/out/assets/{downloadValidation-CH8BeY7_.js → downloadValidation-DbVOwdmC.js} +1 -1
  22. package/out/assets/{external-link-BmR2rZ80.js → external-link-BFaoZeK0.js} +1 -1
  23. package/out/assets/{file-text-BJQfDW9r.js → file-text-DPA1riJ_.js} +1 -1
  24. package/out/assets/{folder-Gb80aQnO.js → folder-Cfgc70_F.js} +1 -1
  25. package/out/assets/{hard-drive-P2dGA7FX.js → hard-drive-Dar_AZEq.js} +1 -1
  26. package/out/assets/i18n-NjvOSuV6.js +1 -0
  27. package/out/assets/index-BZ-f0CZd.js +31 -0
  28. package/out/assets/index-Cde4g_dk.css +1 -0
  29. package/out/assets/index.lazy-B7AbDiMU.js +3 -0
  30. package/out/assets/{index.lazy-DAKkwnV2.js → index.lazy-BsciBHf_.js} +1 -1
  31. package/out/assets/{index.lazy-B7u3Ztoe.js → index.lazy-BxoGfWEW.js} +1 -1
  32. package/out/assets/index.lazy-CKbrHko5.js +1 -0
  33. package/out/assets/index.lazy-CgKPe02l.js +1 -0
  34. package/out/assets/index.lazy-Citucq1G.js +1 -0
  35. package/out/assets/index.lazy-CvKEcWlH.js +2 -0
  36. package/out/assets/{index.lazy-CIqPn0cy.js → index.lazy-DB9Ht9LJ.js} +1 -1
  37. package/out/assets/index.lazy-DVeqmqaA.js +1 -0
  38. package/out/assets/index.lazy-hF807bh0.js +1 -0
  39. package/out/assets/index.lazy-oFaoa4qT.js +1 -0
  40. package/out/assets/{index.lazy-Bh4jDrUl.js → index.lazy-rfgjFZNH.js} +1 -1
  41. package/out/assets/index.lazy-zpQXhgmn.js +2 -0
  42. package/out/assets/{key-round-DvpIlAJY.js → key-round-DNcpSuMw.js} +1 -1
  43. package/out/assets/{loader-8eVY8_SD.js → loader-BXQtdens.js} +1 -1
  44. package/out/assets/{lock-Cvtl60UJ.js → lock-CLIE8cct.js} +1 -1
  45. package/out/assets/{message-square-JK7ABH3O.js → message-square-Dp09WY1I.js} +1 -1
  46. package/out/assets/{mp-CMvgV1-c.js → mp-B6kdA94c.js} +1 -1
  47. package/out/assets/{music-YRSH1Jq6.js → music-B19Tg557.js} +1 -1
  48. package/out/assets/{notebook-pen-BjGx0coX.js → notebook-pen-C8TlK5sB.js} +1 -1
  49. package/out/assets/play-B-BjsTZz.js +1 -0
  50. package/out/assets/plus-4hygj-1Z.js +1 -0
  51. package/out/assets/{save-BcI68ZgS.js → save-BjrbPwhJ.js} +1 -1
  52. package/out/assets/search-DSkiWdvX.js +1 -0
  53. package/out/assets/{send-_ups0_3f.js → send-BL4d5iOt.js} +1 -1
  54. package/out/assets/{shield-check-Dzw5rdPe.js → shield-check-DzDuWO8r.js} +1 -1
  55. package/out/assets/{smartphone-DSQ5L_sr.js → smartphone-tRRFFO0a.js} +1 -1
  56. package/out/assets/{trash-2-BIrBn6Ce.js → trash-2-V12tJ8lI.js} +1 -1
  57. package/out/assets/triangle-alert-DGWc8ZHu.js +1 -0
  58. package/out/assets/{ui-u9yJHkb-.js → ui-BpDGD3IR.js} +2 -2
  59. package/out/assets/{upload-1jmNeGjH.js → upload-CoCptKya.js} +1 -1
  60. package/out/assets/{useChannelMessages-DNxfCew-.js → useChannelMessages-B6RMaNDH.js} +1 -1
  61. package/out/assets/{useGameRoom-Du07VEgb.js → useGameRoom-CRJL0kcm.js} +1 -1
  62. package/out/assets/{userProfile-CL1Ev2GK.js → userProfile-DBkH4s29.js} +1 -1
  63. package/out/assets/{wallet-BvM_X5nW.js → wallet-T7ONZ3Kg.js} +1 -1
  64. package/out/assets/{wifi-66t1dsgC.js → wifi-Bgo8eHY0.js} +1 -1
  65. package/out/chat/index.html +0 -0
  66. package/out/chat/join/index.html +0 -0
  67. package/out/download/index.html +2 -2
  68. package/out/game/gandengyan/index.html +0 -0
  69. package/out/game/index.html +0 -0
  70. package/out/game/zhajinhua/index.html +0 -0
  71. package/out/index.html +2 -2
  72. package/out/note/index.html +0 -0
  73. package/out/ping/index.html +2 -2
  74. package/out/profile/index.html +0 -0
  75. package/out/web3/index.html +0 -0
  76. package/package.json +1 -8
  77. package/server/src/core/releaseManifest.js +1 -18
  78. package/server/src/index.js +0 -284
  79. package/out/assets/LanguageToggle-Cfh33R2h.js +0 -1
  80. package/out/assets/MarketingHeader-BKBngzf5.js +0 -1
  81. package/out/assets/MarketingLayout-C3xrXmfU.js +0 -1
  82. package/out/assets/SidebarHomeLink-LzYXrJRr.js +0 -1
  83. package/out/assets/arrow-right-BC_GWahx.js +0 -1
  84. package/out/assets/i18n-Cqyi6NZb.js +0 -1
  85. package/out/assets/index-CSBg7dZN.css +0 -1
  86. package/out/assets/index-CiHRnvMj.js +0 -31
  87. package/out/assets/index.lazy-BdOfbfjo.js +0 -2
  88. package/out/assets/index.lazy-Bo6McUf1.js +0 -1
  89. package/out/assets/index.lazy-BxUje7fk.js +0 -1
  90. package/out/assets/index.lazy-CfuoMz6P.js +0 -1
  91. package/out/assets/index.lazy-DRaxm9OS.js +0 -1
  92. package/out/assets/index.lazy-DkrwcEBu.js +0 -2
  93. package/out/assets/index.lazy-K6IB-5kb.js +0 -3
  94. package/out/assets/index.lazy-Uu9DCtrI.js +0 -1
  95. package/out/assets/index.lazy-mXESMo49.js +0 -1
  96. package/out/assets/play-Ds19moWN.js +0 -1
  97. package/out/assets/plus-CvGkRhJV.js +0 -1
  98. package/out/assets/search-BIa4RSPA.js +0 -1
  99. package/out/assets/triangle-alert-CgWIEl4u.js +0 -1
package/README.md CHANGED
@@ -278,7 +278,7 @@ git push origin vx.x.x
278
278
 
279
279
  1. **npm 包发布** — 发布 `most-box` 到 npm registry
280
280
  2. **Windows 打包** — 分别构建 `.exe` 安装包(x64 / arm64)并上传 Release
281
- 3. **macOS 打包** — 构建 `.dmg` 安装包和 `.zip` 更新包(x64 + arm64)并上传 Release
281
+ 3. **macOS 打包** — 构建 `.dmg` 安装包(x64 + arm64)并上传 Release
282
282
  4. **Linux 打包** — 构建 `.AppImage` 安装包(x64 + arm64)并上传 Release
283
283
  5. **下载镜像** — 将 Release 资产同步到 Cloudflare R2,并生成 `releases/latest.json`
284
284
 
@@ -288,6 +288,14 @@ GitHub Release 是可信备用源;下载页优先读取 R2 的 `releases/lates
288
288
 
289
289
  R2 发布资产使用独立公开桶,默认 bucket 为 `most-box-releases`,默认公开域名为
290
290
  `https://download.most.box`。不要复用 `api.most.box` 项目的 `most-box-backup` 备份桶。
291
+ Release workflow 不设置 Infrequent Access,R2 对象保持默认 Standard 存储层,并在上传后用
292
+ `head-object` 校验存储层与缓存头。版本化安装包使用
293
+ `public, max-age=31536000, immutable`;`releases/latest.json` 使用
294
+ `public, max-age=60, stale-while-revalidate=300`。
295
+ 新发版只在 manifest 和 R2 中发布当前系统可手动打开的 installer(Windows `.exe`、
296
+ macOS `.dmg`、Linux `.AppImage`),不再发布 updater / blockmap 资产。
297
+ 需要临时复查线上对象时,可手动运行 GitHub Actions 的 `Verify R2 Release` workflow;
298
+ 它复用仓库 R2 secrets 做只读 `head-object` 检查,不需要在本机输入 R2 密钥。
291
299
 
292
300
  在仓库 Settings → Secrets and variables → Actions 中添加:
293
301
 
package/electron/main.js CHANGED
@@ -3,17 +3,16 @@ import {
3
3
  BrowserWindow,
4
4
  Menu,
5
5
  Tray,
6
- ipcMain,
6
+ dialog,
7
7
  nativeImage,
8
8
  shell,
9
9
  } from 'electron'
10
- import { spawn } from 'node:child_process'
11
10
  import fs from 'node:fs'
12
11
  import path from 'node:path'
13
- import { Readable } from 'node:stream'
14
12
  import { fileURLToPath } from 'node:url'
15
13
 
16
14
  import {
15
+ formatBytes,
17
16
  getAvailableUpdate,
18
17
  getCurrentArch,
19
18
  getCurrentPlatform,
@@ -23,7 +22,6 @@ import {
23
22
  createMostDeepLinkTarget,
24
23
  findMostDeepLinkArg,
25
24
  } from './deepLink.js'
26
- import { calculateCid } from '../server/src/core/cid.js'
27
25
 
28
26
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
29
27
  const PORT = 1976
@@ -34,16 +32,6 @@ let tray = null
34
32
  let isQuitting = false
35
33
  let hasCheckedForUpdates = false
36
34
  let pendingDeepLinkUrl = ''
37
- let updateState = {
38
- status: 'idle',
39
- version: '',
40
- filename: '',
41
- source: '',
42
- progress: 0,
43
- error: '',
44
- path: '',
45
- cid: '',
46
- }
47
35
 
48
36
  const gotSingleInstanceLock = app.requestSingleInstanceLock()
49
37
 
@@ -101,57 +89,6 @@ function getInitialWindowUrl() {
101
89
  return initialUrl
102
90
  }
103
91
 
104
- function getPublicUpdateState() {
105
- return {
106
- status: updateState.status,
107
- version: updateState.version,
108
- filename: updateState.filename,
109
- source: updateState.source,
110
- progress: updateState.progress,
111
- error: updateState.error,
112
- cid: updateState.cid,
113
- }
114
- }
115
-
116
- function setUpdateState(patch) {
117
- updateState = {
118
- ...updateState,
119
- ...patch,
120
- }
121
- mainWindow?.webContents.send('updates:state', getPublicUpdateState())
122
- }
123
-
124
- function getUpdateAssetFilename(update) {
125
- return path.basename(
126
- update.asset.filename ||
127
- `MostBox-${update.version}-${getCurrentPlatform()}-${getCurrentArch()}`
128
- )
129
- }
130
-
131
- function getSafeVersionDir(version) {
132
- return String(version || 'unknown').replace(/[^a-zA-Z0-9._-]/g, '_')
133
- }
134
-
135
- function getUpdateCachePath(update) {
136
- const filename = getUpdateAssetFilename(update)
137
- return path.join(
138
- app.getPath('userData'),
139
- 'updates',
140
- getSafeVersionDir(update.version),
141
- filename
142
- )
143
- }
144
-
145
- async function verifyFileCid(filePath, expectedCid) {
146
- const { cid } = await calculateCid(filePath)
147
- const actualCid = cid.toString()
148
- if (actualCid !== expectedCid) {
149
- throw new Error(
150
- `Update CID mismatch. Expected ${expectedCid}, got ${actualCid}.`
151
- )
152
- }
153
- }
154
-
155
92
  function registerMostProtocolClient() {
156
93
  if (process.defaultApp && process.argv.length >= 2) {
157
94
  app.setAsDefaultProtocolClient('most', process.execPath, [
@@ -163,14 +100,6 @@ function registerMostProtocolClient() {
163
100
  app.setAsDefaultProtocolClient('most')
164
101
  }
165
102
 
166
- function registerUpdateIpc() {
167
- ipcMain.handle('updates:get-state', () => getPublicUpdateState())
168
- ipcMain.handle('updates:install-and-restart', async () => {
169
- await installDownloadedUpdate()
170
- return getPublicUpdateState()
171
- })
172
- }
173
-
174
103
  function openMostDeepLink(link) {
175
104
  const targetUrl = createMostDeepLinkTarget(
176
105
  link,
@@ -272,348 +201,14 @@ async function fetchReleaseManifest(manifestUrl) {
272
201
  }
273
202
  }
274
203
 
275
- async function downloadUpdateFromHttp(update, targetPath) {
276
- if (!update.downloadUrl) {
277
- throw new Error('No HTTP fallback URL is available for this update')
278
- }
279
-
280
- const tempPath = `${targetPath}.http.part`
281
- fs.rmSync(tempPath, { force: true })
282
- fs.mkdirSync(path.dirname(targetPath), { recursive: true })
283
-
284
- const response = await fetch(update.downloadUrl, { cache: 'no-store' })
285
- if (!response.ok || !response.body) {
286
- throw new Error(`HTTP update download failed: ${response.status}`)
287
- }
288
-
289
- const total = Number(response.headers.get('content-length')) || 0
290
- let loaded = 0
291
-
292
- await new Promise((resolve, reject) => {
293
- const rs = Readable.fromWeb(response.body)
294
- const ws = fs.createWriteStream(tempPath)
295
-
296
- const cleanup = err => {
297
- rs.destroy(err)
298
- ws.destroy()
299
- fs.unlink(tempPath, () => {})
300
- }
301
-
302
- rs.on('data', chunk => {
303
- loaded += chunk.length
304
- if (total > 0) {
305
- setUpdateState({
306
- status: 'downloading',
307
- source: 'http',
308
- progress: Math.min(99, Math.round((loaded / total) * 100)),
309
- })
310
- }
311
- })
312
-
313
- rs.on('error', err => {
314
- cleanup(err)
315
- reject(err)
316
- })
317
- ws.on('error', err => {
318
- cleanup(err)
319
- reject(err)
320
- })
321
- ws.on('finish', resolve)
322
- rs.pipe(ws)
323
- })
324
-
325
- try {
326
- await verifyFileCid(tempPath, update.cid)
327
- fs.rmSync(targetPath, { force: true })
328
- fs.renameSync(tempPath, targetPath)
329
- } catch (error) {
330
- fs.rmSync(tempPath, { force: true })
331
- throw error
332
- }
333
-
334
- try {
335
- await engine?.seedCidFileFromPath(update.cid, targetPath, {
336
- fileName: getUpdateAssetFilename(update),
337
- source: 'update',
338
- })
339
- } catch (error) {
340
- console.warn('[Electron] Update downloaded but seeding failed:', error)
341
- }
342
- }
343
-
344
- async function downloadUpdateByCid(update, targetPath) {
345
- if (!engine?.downloadCidToPath) {
346
- throw new Error('MostBox engine update download API is unavailable')
347
- }
348
-
349
- const taskId = `update_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
350
- const filename = getUpdateAssetFilename(update)
351
-
352
- const onProgress = data => {
353
- if (data?.taskId !== taskId) return
354
- setUpdateState({
355
- status: 'downloading',
356
- source: 'cid',
357
- progress: Math.min(99, Number(data.percent) || 0),
358
- })
359
- }
360
- const onStatus = data => {
361
- if (data?.taskId !== taskId) return
362
- setUpdateState({
363
- status: 'downloading',
364
- source: 'cid',
365
- filename,
366
- })
367
- }
368
-
369
- engine.on('download:progress', onProgress)
370
- engine.on('download:status', onStatus)
371
- try {
372
- await engine.downloadCidToPath({
373
- cid: update.cid,
374
- fileName: filename,
375
- targetPath,
376
- taskId,
377
- timeout: 30000,
378
- overwrite: true,
379
- source: 'update',
380
- })
381
- } finally {
382
- engine.off('download:progress', onProgress)
383
- engine.off('download:status', onStatus)
384
- }
385
- }
386
-
387
- async function downloadUpdate(update) {
388
- const targetPath = getUpdateCachePath(update)
389
- const filename = getUpdateAssetFilename(update)
390
-
391
- setUpdateState({
392
- status: 'downloading',
393
- version: update.version,
394
- filename,
395
- source: 'cid',
396
- progress: 0,
397
- error: '',
398
- path: '',
399
- cid: update.cid,
400
- })
401
-
402
- try {
403
- await downloadUpdateByCid(update, targetPath)
404
- } catch (cidError) {
405
- console.warn('[Electron] CID update download failed, trying fallback:', cidError)
406
- setUpdateState({
407
- status: 'downloading',
408
- source: 'http',
409
- progress: 0,
410
- error: '',
411
- })
412
- await downloadUpdateFromHttp(update, targetPath)
413
- }
414
-
415
- await verifyFileCid(targetPath, update.cid)
416
- setUpdateState({
417
- status: 'downloaded',
418
- version: update.version,
419
- filename,
420
- source: updateState.source || 'cid',
421
- progress: 100,
422
- error: '',
423
- path: targetPath,
424
- cid: update.cid,
425
- })
426
- }
427
-
428
- function quitForUpdate() {
429
- isQuitting = true
430
- if (tray) {
431
- tray.destroy()
432
- tray = null
433
- }
434
- app.quit()
435
- }
436
-
437
- function getPowerShellPath() {
438
- if (process.env.SystemRoot) {
439
- const candidate = path.join(
440
- process.env.SystemRoot,
441
- 'System32',
442
- 'WindowsPowerShell',
443
- 'v1.0',
444
- 'powershell.exe'
445
- )
446
- if (fs.existsSync(candidate)) return candidate
447
- }
448
-
449
- return 'powershell.exe'
450
- }
451
-
452
- function installWindowsUpdate(updatePath) {
453
- const scriptPath = path.join(
454
- app.getPath('userData'),
455
- 'updates',
456
- 'install-windows.ps1'
457
- )
458
- const exePath = app.getPath('exe')
459
- const script = `param(
460
- [Parameter(Mandatory=$true)][string]$UpdatePath,
461
- [Parameter(Mandatory=$true)][int]$AppPid,
462
- [Parameter(Mandatory=$true)][string]$ExePath
463
- )
464
-
465
- $ErrorActionPreference = "Stop"
466
- try {
467
- Wait-Process -Id $AppPid -ErrorAction SilentlyContinue
468
- } catch {}
469
-
470
- $installer = Start-Process -FilePath $UpdatePath -ArgumentList "/S" -Wait -PassThru
471
- if (($null -eq $installer.ExitCode -or $installer.ExitCode -eq 0) -and (Test-Path -LiteralPath $ExePath)) {
472
- Start-Process -FilePath $ExePath
473
- }
474
- `
475
-
476
- fs.mkdirSync(path.dirname(scriptPath), { recursive: true })
477
- fs.writeFileSync(scriptPath, script, 'utf8')
478
- const child = spawn(
479
- getPowerShellPath(),
480
- [
481
- '-NoProfile',
482
- '-ExecutionPolicy',
483
- 'Bypass',
484
- '-File',
485
- scriptPath,
486
- updatePath,
487
- String(process.pid),
488
- exePath,
489
- ],
490
- {
491
- detached: true,
492
- stdio: 'ignore',
493
- windowsHide: true,
494
- }
495
- )
496
- child.unref()
497
- quitForUpdate()
498
- }
499
-
500
- function getMacAppBundlePath() {
501
- return path.resolve(app.getPath('exe'), '../../..')
502
- }
503
-
504
- function installMacUpdate(updatePath) {
505
- const scriptPath = path.join(app.getPath('userData'), 'updates', 'install-mac.sh')
506
- const appBundlePath = getMacAppBundlePath()
507
- const script = `#!/bin/bash
508
- set -e
509
- APP_PATH="$1"
510
- ZIP_PATH="$2"
511
- TMP_DIR="$(mktemp -d)"
512
- cleanup() {
513
- rm -rf "$TMP_DIR"
514
- }
515
- trap cleanup EXIT
516
- while pgrep -f "$APP_PATH/Contents/MacOS" >/dev/null 2>&1; do
517
- sleep 1
518
- done
519
- ditto -x -k "$ZIP_PATH" "$TMP_DIR"
520
- NEW_APP="$(find "$TMP_DIR" -maxdepth 1 -name "*.app" -type d | head -n 1)"
521
- if [ -z "$NEW_APP" ]; then
522
- exit 1
523
- fi
524
- rm -rf "$APP_PATH"
525
- ditto "$NEW_APP" "$APP_PATH"
526
- open "$APP_PATH"
527
- `
528
-
529
- fs.mkdirSync(path.dirname(scriptPath), { recursive: true })
530
- fs.writeFileSync(scriptPath, script, { mode: 0o755 })
531
- const child = spawn('/bin/bash', [scriptPath, appBundlePath, updatePath], {
532
- detached: true,
533
- stdio: 'ignore',
534
- })
535
- child.unref()
536
- quitForUpdate()
537
- }
538
-
539
- function installLinuxUpdate(updatePath) {
540
- const currentAppImage = process.env.APPIMAGE
541
- if (!currentAppImage) {
542
- shell.openPath(updatePath).catch(() => {})
543
- throw new Error('当前 Linux 运行方式不支持自动替换 AppImage')
544
- }
545
-
546
- const scriptPath = path.join(app.getPath('userData'), 'updates', 'install-linux.sh')
547
- const script = `#!/bin/sh
548
- set -e
549
- APPIMAGE_PATH="$1"
550
- UPDATE_PATH="$2"
551
- APP_PID="$3"
552
- while kill -0 "$APP_PID" >/dev/null 2>&1; do
553
- sleep 1
554
- done
555
- cp "$UPDATE_PATH" "$APPIMAGE_PATH"
556
- chmod +x "$APPIMAGE_PATH"
557
- nohup "$APPIMAGE_PATH" >/dev/null 2>&1 &
558
- `
559
-
560
- fs.mkdirSync(path.dirname(scriptPath), { recursive: true })
561
- fs.writeFileSync(scriptPath, script, { mode: 0o755 })
562
- const child = spawn('/bin/sh', [scriptPath, currentAppImage, updatePath, String(process.pid)], {
563
- detached: true,
564
- stdio: 'ignore',
565
- })
566
- child.unref()
567
- quitForUpdate()
568
- }
569
-
570
- async function installDownloadedUpdate() {
571
- if (updateState.status !== 'downloaded' || !updateState.path) {
572
- throw new Error('No downloaded update is ready to install')
573
- }
574
-
575
- setUpdateState({ status: 'installing', error: '' })
576
-
577
- try {
578
- if (process.platform === 'win32') {
579
- installWindowsUpdate(updateState.path)
580
- return
581
- }
582
- if (process.platform === 'darwin') {
583
- installMacUpdate(updateState.path)
584
- return
585
- }
586
- if (process.platform === 'linux') {
587
- installLinuxUpdate(updateState.path)
588
- return
589
- }
590
-
591
- throw new Error('当前平台不支持自动安装更新')
592
- } catch (error) {
593
- setUpdateState({
594
- status: 'error',
595
- error: error?.message || 'Update install failed',
596
- })
597
- throw error
598
- }
599
- }
600
-
601
204
  async function checkForUpdates() {
602
205
  if (hasCheckedForUpdates) return
603
206
  hasCheckedForUpdates = true
604
207
 
605
208
  try {
606
- setUpdateState({
607
- status: 'checking',
608
- progress: 0,
609
- error: '',
610
- })
611
209
  const platform = getCurrentPlatform()
612
210
  const arch = getCurrentArch()
613
- if (!platform || !arch) {
614
- setUpdateState({ status: 'idle' })
615
- return
616
- }
211
+ if (!platform || !arch) return
617
212
 
618
213
  const manifest = await fetchReleaseManifest(getReleaseManifestUrl())
619
214
  const update = getAvailableUpdate(manifest, {
@@ -622,19 +217,35 @@ async function checkForUpdates() {
622
217
  arch,
623
218
  })
624
219
 
625
- if (!update) {
626
- setUpdateState({ status: 'idle' })
627
- return
628
- }
220
+ if (!update) return
221
+
222
+ const sizeText = formatBytes(update.asset.size)
223
+ const detail = [
224
+ `当前版本:${app.getVersion()}`,
225
+ `最新版本:${update.version}`,
226
+ sizeText ? `安装包大小:${sizeText}` : null,
227
+ '',
228
+ '是否现在下载当前系统版本的安装包?下载完成后请手动打开安装。',
229
+ ]
230
+ .filter(line => line !== null)
231
+ .join('\n')
232
+
233
+ const result = await dialog.showMessageBox(mainWindow, {
234
+ type: 'info',
235
+ buttons: ['立即下载', '稍后'],
236
+ defaultId: 0,
237
+ cancelId: 1,
238
+ title: '发现 MostBox 新版本',
239
+ message: `MostBox ${update.version} 已可下载`,
240
+ detail,
241
+ noLink: true,
242
+ })
629
243
 
630
- await downloadUpdate(update)
244
+ if (result.response === 0) {
245
+ await shell.openExternal(update.downloadUrl)
246
+ }
631
247
  } catch (error) {
632
- console.warn('[Electron] Update check failed:', error)
633
- setUpdateState({
634
- status: 'error',
635
- error: error?.message || 'Update check failed',
636
- progress: 0,
637
- })
248
+ console.warn('[Electron] Update check skipped:', error)
638
249
  }
639
250
  }
640
251
 
@@ -643,7 +254,6 @@ if (!gotSingleInstanceLock) {
643
254
  app.quit()
644
255
  } else {
645
256
  registerMostProtocolClient()
646
- registerUpdateIpc()
647
257
  openMostDeepLink(findMostDeepLinkArg(process.argv))
648
258
 
649
259
  app.on('second-instance', (_event, commandLine) => {
@@ -1,21 +1,6 @@
1
- import { contextBridge, ipcRenderer } from 'electron'
1
+ import { contextBridge } from 'electron'
2
2
 
3
3
  contextBridge.exposeInMainWorld('electronAPI', {
4
4
  platform: process.platform,
5
5
  isElectron: true,
6
- updates: {
7
- getState: () => ipcRenderer.invoke('updates:get-state'),
8
- installAndRestart: () => ipcRenderer.invoke('updates:install-and-restart'),
9
- onStateChange: callback => {
10
- if (typeof callback !== 'function') return () => {}
11
-
12
- const listener = (_event, state) => {
13
- callback(state)
14
- }
15
- ipcRenderer.on('updates:state', listener)
16
- return () => {
17
- ipcRenderer.off('updates:state', listener)
18
- }
19
- },
20
- },
21
6
  })
@@ -1,5 +1,5 @@
1
1
  import {
2
- findCompatibleUpdateAsset,
2
+ getInstallerReleaseAssets,
3
3
  isReleaseManifest,
4
4
  resolveReleaseAssetDownload,
5
5
  } from '../server/src/core/releaseManifest.js'
@@ -50,7 +50,11 @@ export function isNewerVersion(candidateVersion, currentVersion) {
50
50
  }
51
51
 
52
52
  export function findUpdateAsset(manifest, platform, arch) {
53
- return findCompatibleUpdateAsset(manifest, platform, arch)
53
+ return (
54
+ getInstallerReleaseAssets(manifest).find(
55
+ asset => asset.platform === platform && asset.arch === arch
56
+ ) || null
57
+ )
54
58
  }
55
59
 
56
60
  export function formatBytes(size) {
@@ -81,7 +85,6 @@ export function getAvailableUpdate(manifest, options = {}) {
81
85
  return {
82
86
  version: manifest.version,
83
87
  asset,
84
- cid: asset.cid,
85
88
  downloadUrl,
86
89
  }
87
90
  }