From c6c434a07a6b75ca72312e6584fea3bb744b7f40 Mon Sep 17 00:00:00 2001 From: hailin Date: Tue, 3 Mar 2026 21:13:13 -0800 Subject: [PATCH] =?UTF-8?q?fix(update):=20=E4=BF=AE=E5=A4=8D=20app=20?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E6=9B=B4=E6=96=B0=E6=A3=80=E6=9F=A5=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=20+=20=E8=A7=A3=E5=86=B3=20MinIO=20presigned=20URL=20?= =?UTF-8?q?24h=20=E8=BF=87=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mobile 端: - version_checker.dart: /api/app/... → /api/v1/app/... (与 Kong 路由匹配) Backend (admin-service): - AppVersion 实体新增 storage_key 字段(已执行 ALTER TABLE) - FileStorageService: uploadFile 不再返回 presigned URL,只返回 objectName - AdminVersionController: upload 后保存 storageKey,downloadUrl 设为 /api/v1/app/version/download/{id}(稳定 API 地址,不过期) - AppVersionController.downloadVersion: storageKey 存在时每次请求动态 生成 presigned URL(1小时有效,只需够下载完成即可) - AppVersionService.checkUpdate: 有 storageKey 的版本统一返回 API 下载地址 Co-Authored-By: Claude Sonnet 4.6 --- .../src/application/services/app-version.service.ts | 6 +++++- .../src/application/services/file-storage.service.ts | 8 -------- .../src/domain/entities/app-version.entity.ts | 3 +++ .../http/controllers/admin-version.controller.ts | 10 ++++++++-- .../http/controllers/app-version.controller.ts | 8 ++++++-- .../genex-mobile/lib/core/updater/version_checker.dart | 2 +- 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/backend/services/admin-service/src/application/services/app-version.service.ts b/backend/services/admin-service/src/application/services/app-version.service.ts index 1ec4f5b..37c2e43 100644 --- a/backend/services/admin-service/src/application/services/app-version.service.ts +++ b/backend/services/admin-service/src/application/services/app-version.service.ts @@ -24,7 +24,10 @@ export class AppVersionService { forceUpdate: latest.isForceUpdate && latest.isEnabled, version: latest.versionName, versionCode: latest.versionCode, - downloadUrl: latest.downloadUrl, + // Use stable API download endpoint; storageKey versions generate fresh presigned URL on download + downloadUrl: latest.storageKey + ? `/api/v1/app/version/download/${latest.id}` + : latest.downloadUrl, fileSize: Number(latest.fileSize), fileSizeFriendly: this.formatFileSize(BigInt(latest.fileSize)), sha256: latest.fileSha256, @@ -54,6 +57,7 @@ export class AppVersionService { versionName: string; buildNumber: string; downloadUrl: string; + storageKey?: string; fileSize: string; fileSha256: string; changelog: string; diff --git a/backend/services/admin-service/src/application/services/file-storage.service.ts b/backend/services/admin-service/src/application/services/file-storage.service.ts index f429db9..e5a3bce 100644 --- a/backend/services/admin-service/src/application/services/file-storage.service.ts +++ b/backend/services/admin-service/src/application/services/file-storage.service.ts @@ -49,16 +49,8 @@ export class FileStorageService { : 'application/octet-stream', }); - // Generate presigned download URL (24h) - const downloadUrl = await this.minio.presignedGetObject( - BUCKET, - objectName, - 24 * 3600, - ); - return { objectName, - downloadUrl, fileSize: buffer.length.toString(), sha256, }; diff --git a/backend/services/admin-service/src/domain/entities/app-version.entity.ts b/backend/services/admin-service/src/domain/entities/app-version.entity.ts index c4e3a11..8687f26 100644 --- a/backend/services/admin-service/src/domain/entities/app-version.entity.ts +++ b/backend/services/admin-service/src/domain/entities/app-version.entity.ts @@ -29,6 +29,9 @@ export class AppVersion { @Column({ name: 'download_url', type: 'text' }) downloadUrl: string; + @Column({ name: 'storage_key', type: 'text', nullable: true }) + storageKey: string | null; + @Column({ name: 'file_size', type: 'bigint', default: 0 }) fileSize: string; // bigint as string in TypeORM diff --git a/backend/services/admin-service/src/interface/http/controllers/admin-version.controller.ts b/backend/services/admin-service/src/interface/http/controllers/admin-version.controller.ts index 856a3f1..35ed8ea 100644 --- a/backend/services/admin-service/src/interface/http/controllers/admin-version.controller.ts +++ b/backend/services/admin-service/src/interface/http/controllers/admin-version.controller.ts @@ -128,7 +128,8 @@ export class AdminVersionController { versionCode, versionName, buildNumber, - downloadUrl: uploadResult.downloadUrl, + storageKey: uploadResult.objectName, + downloadUrl: '', // will be updated after id is known fileSize: uploadResult.fileSize, fileSha256: uploadResult.sha256, changelog: body.changelog || '', @@ -138,7 +139,12 @@ export class AdminVersionController { createdBy: req.user?.sub, }); - return { code: 0, data: version }; + // Set stable download URL pointing to this service's download endpoint + const updated = await this.versionService.updateVersion(version.id, { + downloadUrl: `/api/v1/app/version/download/${version.id}`, + }); + + return { code: 0, data: updated }; } @Post('parse') diff --git a/backend/services/admin-service/src/interface/http/controllers/app-version.controller.ts b/backend/services/admin-service/src/interface/http/controllers/app-version.controller.ts index f8e0068..8edcc9d 100644 --- a/backend/services/admin-service/src/interface/http/controllers/app-version.controller.ts +++ b/backend/services/admin-service/src/interface/http/controllers/app-version.controller.ts @@ -35,10 +35,14 @@ export class AppVersionController { } @Get('download/:id') - @ApiOperation({ summary: 'Download app package' }) + @ApiOperation({ summary: 'Download app package (regenerates fresh presigned URL each request)' }) async downloadVersion(@Param('id') id: string, @Res() res: Response) { const version = await this.versionService.getVersion(id); - // Redirect to the download URL (presigned MinIO URL or external URL) + if (version.storageKey) { + // Generate a fresh 1-hour presigned URL per request — no expiry issue + const freshUrl = await this.fileStorage.getDownloadUrl(version.storageKey); + return res.redirect(302, freshUrl); + } return res.redirect(302, version.downloadUrl); } } diff --git a/frontend/genex-mobile/lib/core/updater/version_checker.dart b/frontend/genex-mobile/lib/core/updater/version_checker.dart index 626a0e0..6f2ebfa 100644 --- a/frontend/genex-mobile/lib/core/updater/version_checker.dart +++ b/frontend/genex-mobile/lib/core/updater/version_checker.dart @@ -28,7 +28,7 @@ class VersionChecker { debugPrint('[VersionChecker] 当前版本: ${currentInfo.version}, buildNumber: ${currentInfo.buildNumber}'); final response = await _dio.get( - '/api/app/version/check', + '/api/v1/app/version/check', queryParameters: { 'platform': 'android', 'current_version': currentInfo.version,