1- import { rm } from 'node:fs/promises'
21import { hostname } from 'node:os'
32
43import {
@@ -10,16 +9,22 @@ import {
109import pc from 'picocolors'
1110import { Observable , ReplaySubject } from 'rxjs'
1211
13- import { ADMIN_DASHBOARD_REPO } from '~/constants/admin.constant '
12+ import { ADMIN_UPDATE } from '~/app.config '
1413import { RedisService } from '~/processors/redis/redis.service'
1514
1615import { UpdateDownloadService } from './update-download.service'
1716import { UpdateInstallService } from './update-install.service'
1817
19- const repo = ADMIN_DASHBOARD_REPO
20-
2118const REDIS_KEY_PREFIX = 'update:admin'
2219
20+ interface AdminUpdateManifest {
21+ version : string
22+ file : string
23+ url : string
24+ sha256 ?: string
25+ tag ?: string
26+ }
27+
2328const LUA_RELEASE_LOCK = `
2429if redis.call("get", KEYS[1]) == ARGV[1] then
2530 return redis.call("del", KEYS[1])
@@ -196,47 +201,22 @@ export class UpdateService implements OnModuleInit, OnModuleDestroy {
196201 . catch ( ( ) => { } )
197202 }
198203
199- const endpoint = `https://api.github.com/repos/${ repo } /releases/tags/v${ version } `
200- await pushProgress ( `Getting release info from ${ endpoint } .\n` )
201-
202- const releaseInfo = await this . downloadService . fetchWithRetry ( endpoint , {
203- timeout : 30000 ,
204- headers : {
205- 'User-Agent' : 'Mix-Space-Admin-Updater' ,
206- Accept : 'application/vnd.github.v3+json' ,
207- } ,
208- } )
209-
210- if ( ! releaseInfo ?. assets ) {
211- throw new Error ( 'Release assets not found' )
212- }
213-
214- const asset = releaseInfo . assets . find (
215- ( asset : any ) => asset . name === 'release.zip' ,
216- )
217-
218- if ( ! asset ) {
219- const msg = `release.zip not found. Available: ${ releaseInfo . assets . map ( ( a : any ) => a . name ) . join ( ', ' ) } `
220- throw new Error ( msg )
204+ const manifest = await this . fetchAdminManifest ( )
205+ if ( manifest . version !== version ) {
206+ throw new Error (
207+ `Admin manifest version mismatch: requested ${ version } , got ${ manifest . version } ` ,
208+ )
221209 }
222-
223- const downloadUrl = asset . browser_download_url
224- const fileSize = asset . size
225-
226210 await pushProgress (
227- `Found release.zip ( ${ this . downloadService . formatBytes ( fileSize ) } ) \n` ,
211+ `Resolved admin v ${ manifest . version } → ${ manifest . url } \n` ,
228212 )
229213
230- const buffer = await this . downloadService . downloadWithMirrors (
231- downloadUrl ,
232- fileSize ,
214+ const buffer = await this . downloadService . downloadDirect (
215+ manifest . url ,
233216 async ( msg : string ) => pushProgress ( msg ) ,
217+ { sha256 : manifest . sha256 } ,
234218 )
235219
236- if ( ! buffer ) {
237- throw new Error ( 'All download sources failed' )
238- }
239-
240220 await pushProgress ( 'Storing buffer to Redis for other instances...\n' )
241221 await this . redis . setex (
242222 keys . bufferKey ,
@@ -300,7 +280,6 @@ export class UpdateService implements OnModuleInit, OnModuleDestroy {
300280 . catch ( ( ) => { } )
301281
302282 subscriber . next ( pc . red ( `Download failed: ${ errorMsg } \n` ) )
303- await rm ( 'admin-release.zip' , { force : true } ) . catch ( ( ) => { } )
304283 } finally {
305284 clearInterval ( heartbeat )
306285 await this . redis
@@ -548,24 +527,31 @@ export class UpdateService implements OnModuleInit, OnModuleDestroy {
548527 }
549528
550529 async getLatestAdminVersion ( ) {
551- const endpoint = `https://api.github.com/repos/${ repo } /releases/latest`
530+ const manifest = await this . fetchAdminManifest ( )
531+ return manifest . version
532+ }
552533
534+ private async fetchAdminManifest ( ) : Promise < AdminUpdateManifest > {
535+ const baseUrl = ADMIN_UPDATE . s3BaseUrl ?. replace ( / \/ + $ / , '' )
536+ if ( ! baseUrl ) {
537+ throw new Error (
538+ 'Admin update source is not configured (ADMIN_UPDATE_S3_BASE_URL)' ,
539+ )
540+ }
541+ const endpoint = `${ baseUrl } /latest.json`
553542 try {
554- const data = await this . downloadService . fetchWithRetry ( endpoint , {
555- headers : {
556- 'User-Agent' : 'Mix-Space-Admin-Updater' ,
557- Accept : 'application/vnd.github.v3+json' ,
558- } ,
559- } )
543+ const data = ( await this . downloadService . fetchWithRetry ( endpoint , {
544+ timeout : 30000 ,
545+ headers : { Accept : 'application/json' } ,
546+ } ) ) as Partial < AdminUpdateManifest > | undefined
560547
561- const tag = data ?. tag_name
562- if ( ! tag ) {
563- throw new Error ( 'tag_name not found in release info' )
548+ if ( ! data ?. version || ! data . url ) {
549+ throw new Error ( 'latest.json missing required fields (version, url)' )
564550 }
565- return tag . replace ( / ^ v / , '' )
551+ return data as AdminUpdateManifest
566552 } catch ( error ) {
567553 const errorMsg = error instanceof Error ? error . message : String ( error )
568- throw new Error ( `Failed to get latest version : ${ errorMsg } ` , {
554+ throw new Error ( `Failed to fetch admin manifest : ${ errorMsg } ` , {
569555 cause : error ,
570556 } )
571557 }
0 commit comments