fix(update): 修复 app 版本更新检查路径 + 解决 MinIO presigned URL 24h 过期
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 <noreply@anthropic.com>
This commit is contained in:
parent
50d6c77dfd
commit
c6c434a07a
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Reference in New Issue