most-box 0.2.7 → 0.2.9
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.
- package/README.md +9 -1
- package/electron/main.js +30 -420
- package/electron/preload.js +1 -16
- package/electron/updateChecker.js +6 -3
- package/electron/updateChecker.test.js +13 -97
- package/out/admin/index.html +0 -0
- package/out/app/index.html +0 -0
- package/out/assets/{FilePreviewOverlay-CW8lKJCw.js → FilePreviewOverlay-BYZIAMJ-.js} +1 -1
- package/out/assets/LanguageToggle-CSQtRZ3A.js +1 -0
- package/out/assets/MarketingHeader-ceWdRCpe.js +1 -0
- package/out/assets/MarketingLayout-Bun1d2wL.js +1 -0
- package/out/assets/{MarketingThemeToggle-Cl7PhFgv.js → MarketingThemeToggle-CIKP8x9W.js} +1 -1
- package/out/assets/{MilkdownEditor-B5SKAyjg.js → MilkdownEditor-CT6SHYKG.js} +1 -1
- package/out/assets/{OpenSidebarButton-Bdx86oG8.js → OpenSidebarButton-CLSCB7CG.js} +1 -1
- package/out/assets/{SidebarAccount-c8jirEMF.js → SidebarAccount-CNh0kvtB.js} +1 -1
- package/out/assets/SidebarHomeLink-DXv0c9Ob.js +1 -0
- package/out/assets/arrow-right-BU9c_SL4.js +1 -0
- package/out/assets/{circle-alert-Dkgyw_GE.js → circle-alert-iuHzciph.js} +1 -1
- package/out/assets/{copy-KIwotKoP.js → copy-DEzFUzeX.js} +1 -1
- package/out/assets/{download-DPA-3LpR.js → download-jEPn2njL.js} +1 -1
- package/out/assets/{downloadValidation-CH8BeY7_.js → downloadValidation-Bzl7cci9.js} +1 -1
- package/out/assets/{external-link-BmR2rZ80.js → external-link-BFaoZeK0.js} +1 -1
- package/out/assets/{file-text-BJQfDW9r.js → file-text-DPA1riJ_.js} +1 -1
- package/out/assets/{folder-Gb80aQnO.js → folder-Cfgc70_F.js} +1 -1
- package/out/assets/{hard-drive-P2dGA7FX.js → hard-drive-Dar_AZEq.js} +1 -1
- package/out/assets/i18n-NjvOSuV6.js +1 -0
- package/out/assets/index-BHZhApbo.css +1 -0
- package/out/assets/index-BI8oNSVC.js +31 -0
- package/out/assets/index.lazy-7omTRykl.js +2 -0
- package/out/assets/index.lazy-BOMHingE.js +3 -0
- package/out/assets/index.lazy-BSY5_gby.js +1 -0
- package/out/assets/{index.lazy-CIqPn0cy.js → index.lazy-C4ipXKZ7.js} +1 -1
- package/out/assets/index.lazy-CPZbEnJZ.js +1 -0
- package/out/assets/{index.lazy-Bh4jDrUl.js → index.lazy-Ch4Y17pJ.js} +1 -1
- package/out/assets/index.lazy-CvBIIYbm.js +1 -0
- package/out/assets/{index.lazy-B7u3Ztoe.js → index.lazy-Cz-OZwYL.js} +1 -1
- package/out/assets/index.lazy-D4zNn3D6.js +2 -0
- package/out/assets/index.lazy-DRY3xoQp.js +1 -0
- package/out/assets/{index.lazy-DAKkwnV2.js → index.lazy-_mEd0Q_u.js} +1 -1
- package/out/assets/index.lazy-bAp_Hgck.js +1 -0
- package/out/assets/index.lazy-ur0joIFl.js +1 -0
- package/out/assets/{key-round-DvpIlAJY.js → key-round-DNcpSuMw.js} +1 -1
- package/out/assets/{loader-8eVY8_SD.js → loader-BXQtdens.js} +1 -1
- package/out/assets/{lock-Cvtl60UJ.js → lock-CLIE8cct.js} +1 -1
- package/out/assets/{message-square-JK7ABH3O.js → message-square-Dp09WY1I.js} +1 -1
- package/out/assets/{mp-CMvgV1-c.js → mp-B6kdA94c.js} +1 -1
- package/out/assets/{music-YRSH1Jq6.js → music-B19Tg557.js} +1 -1
- package/out/assets/{notebook-pen-BjGx0coX.js → notebook-pen-C8TlK5sB.js} +1 -1
- package/out/assets/play-B-BjsTZz.js +1 -0
- package/out/assets/plus-4hygj-1Z.js +1 -0
- package/out/assets/{save-BcI68ZgS.js → save-BjrbPwhJ.js} +1 -1
- package/out/assets/search-DSkiWdvX.js +1 -0
- package/out/assets/{send-_ups0_3f.js → send-BL4d5iOt.js} +1 -1
- package/out/assets/{shield-check-Dzw5rdPe.js → shield-check-DzDuWO8r.js} +1 -1
- package/out/assets/{smartphone-DSQ5L_sr.js → smartphone-tRRFFO0a.js} +1 -1
- package/out/assets/{trash-2-BIrBn6Ce.js → trash-2-V12tJ8lI.js} +1 -1
- package/out/assets/triangle-alert-DGWc8ZHu.js +1 -0
- package/out/assets/{ui-u9yJHkb-.js → ui-BpDGD3IR.js} +2 -2
- package/out/assets/{upload-1jmNeGjH.js → upload-CoCptKya.js} +1 -1
- package/out/assets/{useChannelMessages-DNxfCew-.js → useChannelMessages-BeKF0gwo.js} +1 -1
- package/out/assets/{useGameRoom-Du07VEgb.js → useGameRoom-cs9YpYt7.js} +1 -1
- package/out/assets/{userProfile-CL1Ev2GK.js → userProfile-qcc60tHK.js} +1 -1
- package/out/assets/{wallet-BvM_X5nW.js → wallet-T7ONZ3Kg.js} +1 -1
- package/out/assets/{wifi-66t1dsgC.js → wifi-Bgo8eHY0.js} +1 -1
- package/out/chat/index.html +0 -0
- package/out/chat/join/index.html +0 -0
- package/out/download/index.html +2 -2
- package/out/game/gandengyan/index.html +0 -0
- package/out/game/index.html +0 -0
- package/out/game/zhajinhua/index.html +0 -0
- package/out/index.html +2 -2
- package/out/note/index.html +0 -0
- package/out/ping/index.html +2 -2
- package/out/profile/index.html +0 -0
- package/out/web3/index.html +0 -0
- package/package.json +1 -8
- package/server/src/core/releaseManifest.js +1 -18
- package/server/src/index.js +34 -286
- package/out/assets/LanguageToggle-Cfh33R2h.js +0 -1
- package/out/assets/MarketingHeader-BKBngzf5.js +0 -1
- package/out/assets/MarketingLayout-C3xrXmfU.js +0 -1
- package/out/assets/SidebarHomeLink-LzYXrJRr.js +0 -1
- package/out/assets/arrow-right-BC_GWahx.js +0 -1
- package/out/assets/i18n-Cqyi6NZb.js +0 -1
- package/out/assets/index-CSBg7dZN.css +0 -1
- package/out/assets/index-CiHRnvMj.js +0 -31
- package/out/assets/index.lazy-BdOfbfjo.js +0 -2
- package/out/assets/index.lazy-Bo6McUf1.js +0 -1
- package/out/assets/index.lazy-BxUje7fk.js +0 -1
- package/out/assets/index.lazy-CfuoMz6P.js +0 -1
- package/out/assets/index.lazy-DRaxm9OS.js +0 -1
- package/out/assets/index.lazy-DkrwcEBu.js +0 -2
- package/out/assets/index.lazy-K6IB-5kb.js +0 -3
- package/out/assets/index.lazy-Uu9DCtrI.js +0 -1
- package/out/assets/index.lazy-mXESMo49.js +0 -1
- package/out/assets/play-Ds19moWN.js +0 -1
- package/out/assets/plus-CvGkRhJV.js +0 -1
- package/out/assets/search-BIa4RSPA.js +0 -1
- 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`
|
|
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
|
-
|
|
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
|
-
|
|
627
|
-
|
|
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
|
-
|
|
244
|
+
if (result.response === 0) {
|
|
245
|
+
await shell.openExternal(update.downloadUrl)
|
|
246
|
+
}
|
|
631
247
|
} catch (error) {
|
|
632
|
-
console.warn('[Electron] Update check
|
|
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) => {
|
package/electron/preload.js
CHANGED
|
@@ -1,21 +1,6 @@
|
|
|
1
|
-
import { contextBridge
|
|
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
|
-
|
|
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
|
|
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
|
}
|