import { Controller, Get, Param, Query, Res } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiQuery } from '@nestjs/swagger'; import { Response } from 'express'; import { AppVersionService } from '../../../application/services/app-version.service'; import { FileStorageService } from '../../../application/services/file-storage.service'; import { Platform } from '../../../domain/enums/platform.enum'; import { AppType } from '../../../domain/enums/app-type.enum'; @ApiTags('app-version') @Controller('app/version') export class AppVersionController { constructor( private readonly versionService: AppVersionService, private readonly fileStorage: FileStorageService, ) {} @Get('check') @ApiOperation({ summary: 'Check for app update (mobile client)' }) @ApiQuery({ name: 'app_type', required: false, enum: AppType, description: 'Default: GENEX_MOBILE' }) @ApiQuery({ name: 'platform', enum: ['android', 'ios', 'ANDROID', 'IOS'] }) @ApiQuery({ name: 'current_version_code', type: Number }) async checkUpdate( @Query('app_type') appType: string, @Query('platform') platform: string, @Query('current_version_code') currentVersionCode: string, ) { const appTypeEnum = (appType || 'GENEX_MOBILE').toUpperCase() as AppType; const platformEnum = platform.toUpperCase() as Platform; const result = await this.versionService.checkUpdate( appTypeEnum, platformEnum, parseInt(currentVersionCode, 10), ); return { code: 0, data: result }; } @Get('download/:id') @ApiOperation({ summary: 'Download app package — streams through service (MinIO not directly exposed)' }) async downloadVersion(@Param('id') id: string, @Res() res: Response) { const version = await this.versionService.getVersion(id); if (version.storageKey) { // Stream from MinIO through this service — MinIO is not publicly accessible const stat = await this.fileStorage.statFile(version.storageKey); const ext = version.storageKey.split('.').pop() || 'apk'; const filename = `app-${version.versionName}.${ext}`; res.setHeader('Content-Type', stat.contentType); res.setHeader('Content-Length', stat.size); res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); await this.fileStorage.streamFile(version.storageKey, res); return; } return res.redirect(302, version.downloadUrl); } }