From e6f864d409e013cf563e3933e5fba939073c1c2c Mon Sep 17 00:00:00 2001 From: hailin Date: Fri, 6 Mar 2026 06:04:27 -0800 Subject: [PATCH] fix(version-service+gateway+app): fix APK download 404 and SHA-256 false failure Three coordinated fixes to make in-app APK download work end-to-end: 1. version-service/main.ts: serve uploaded files as static assets via NestExpressApplication.useStaticAssets('/data/versions', prefix: '/downloads/versions'), so GET /downloads/versions/{platform}/{file} returns the actual APK stored in the Docker volume. 2. kong.yml: add /downloads/versions route to Kong so requests from the Flutter app can reach version-service through the API gateway. Previously only /api/v1/versions and /api/app/version were routed; the download URL returned by the check endpoint was unreachable (404). 3. download_manager.dart: skip SHA-256 verification when sha256Expected is empty string. The check endpoint always returns sha256:"" because version-service doesn't store file hashes. The previous code compared actual_hash == "" which always failed, causing the downloaded file to be deleted after a successful download. Co-Authored-By: Claude Sonnet 4.6 --- .../lib/core/updater/download_manager.dart | 22 +++++++++++-------- packages/gateway/config/kong.yml | 4 ++++ packages/services/version-service/src/main.ts | 7 +++++- 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/it0_app/lib/core/updater/download_manager.dart b/it0_app/lib/core/updater/download_manager.dart index 4882353..fa0e618 100644 --- a/it0_app/lib/core/updater/download_manager.dart +++ b/it0_app/lib/core/updater/download_manager.dart @@ -118,18 +118,22 @@ class DownloadManager { await tempFile.rename(savePath); debugPrint('Download completed'); - _status = DownloadStatus.verifying; - // 校验 SHA-256 - final isValid = await _verifySha256(file, sha256Expected); - if (!isValid) { - debugPrint('SHA-256 verification failed'); - await file.delete(); - _status = DownloadStatus.failed; - return null; + // 校验 SHA-256(仅在服务器提供了哈希值时校验) + if (sha256Expected.isNotEmpty) { + _status = DownloadStatus.verifying; + final isValid = await _verifySha256(file, sha256Expected); + if (!isValid) { + debugPrint('SHA-256 verification failed'); + await file.delete(); + _status = DownloadStatus.failed; + return null; + } + debugPrint('SHA-256 verified'); + } else { + debugPrint('SHA-256 not provided, skipping verification'); } - debugPrint('SHA-256 verified'); _status = DownloadStatus.completed; return file; } on DioException catch (e) { diff --git a/packages/gateway/config/kong.yml b/packages/gateway/config/kong.yml index 43d25c1..a369bed 100644 --- a/packages/gateway/config/kong.yml +++ b/packages/gateway/config/kong.yml @@ -124,6 +124,10 @@ services: paths: - /api/app/version strip_path: false + - name: app-version-download-route + paths: + - /downloads/versions + strip_path: false - name: billing-service url: http://billing-service:3010 diff --git a/packages/services/version-service/src/main.ts b/packages/services/version-service/src/main.ts index d98d8ea..58f110e 100644 --- a/packages/services/version-service/src/main.ts +++ b/packages/services/version-service/src/main.ts @@ -1,4 +1,5 @@ import { NestFactory } from '@nestjs/core'; +import { NestExpressApplication } from '@nestjs/platform-express'; import { Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { VersionModule } from './version.module'; @@ -13,9 +14,13 @@ process.on('uncaughtException', (error) => { }); async function bootstrap() { - const app = await NestFactory.create(VersionModule); + const app = await NestFactory.create(VersionModule); const config = app.get(ConfigService); const port = config.get('VERSION_SERVICE_PORT', 3009); + // Serve uploaded APK/IPA files as static assets + // Files are stored at /data/versions/{platform}/filename + // Accessible via GET /downloads/versions/{platform}/filename + app.useStaticAssets('/data/versions', { prefix: '/downloads/versions' }); await app.listen(port); logger.log(`version-service running on port ${port}`); }