gcx/backend/services/admin-service/src/interface/http/controllers/admin-version.controller.ts

171 lines
5.4 KiB
TypeScript

import {
Controller, Get, Post, Put, Patch, Delete, Inject,
Param, Query, Body, UseGuards, UseInterceptors, UploadedFile, Req,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { ApiTags, ApiOperation, ApiBearerAuth, ApiConsumes } from '@nestjs/swagger';
import { JwtAuthGuard, Roles, RolesGuard, UserRole } from '@genex/common';
import { AppVersionService } from '../../../application/services/app-version.service';
import { FileStorageService } from '../../../application/services/file-storage.service';
import { PACKAGE_PARSER, IPackageParser } from '../../../domain/ports/package-parser.interface';
import { Platform } from '../../../domain/enums/platform.enum';
@ApiTags('admin-versions')
@Controller('admin/versions')
@UseGuards(JwtAuthGuard, RolesGuard)
@Roles(UserRole.ADMIN)
@ApiBearerAuth()
export class AdminVersionController {
constructor(
private readonly versionService: AppVersionService,
private readonly fileStorage: FileStorageService,
@Inject(PACKAGE_PARSER) private readonly packageParser: IPackageParser,
) {}
@Get()
@ApiOperation({ summary: 'List app versions' })
async listVersions(
@Query('platform') platform?: string,
@Query('includeDisabled') includeDisabled?: string,
) {
const platformEnum = platform
? (platform.toUpperCase() as Platform)
: undefined;
const versions = await this.versionService.listVersions(
platformEnum,
includeDisabled === 'true',
);
return { code: 0, data: versions };
}
@Get(':id')
@ApiOperation({ summary: 'Get version details' })
async getVersion(@Param('id') id: string) {
const version = await this.versionService.getVersion(id);
return { code: 0, data: version };
}
@Post()
@ApiOperation({ summary: 'Create version manually' })
async createVersion(
@Body() body: {
platform: string;
versionCode: number;
versionName: string;
buildNumber: string;
downloadUrl: string;
fileSize: string;
fileSha256: string;
changelog: string;
isForceUpdate: boolean;
minOsVersion?: string;
releaseDate?: string;
},
@Req() req: any,
) {
const version = await this.versionService.createVersion({
...body,
platform: body.platform.toUpperCase() as Platform,
releaseDate: body.releaseDate ? new Date(body.releaseDate) : undefined,
createdBy: req.user?.sub,
});
return { code: 0, data: version };
}
@Post('upload')
@UseInterceptors(FileInterceptor('file'))
@ApiConsumes('multipart/form-data')
@ApiOperation({ summary: 'Upload APK/IPA and create version' })
async uploadVersion(
@UploadedFile() file: Express.Multer.File,
@Body() body: {
platform: string;
versionCode?: string;
versionName?: string;
buildNumber?: string;
changelog?: string;
isForceUpdate?: string;
minOsVersion?: string;
releaseDate?: string;
},
@Req() req: any,
) {
// Parse package to extract metadata (auto-fill when not provided)
const parsedInfo = await this.packageParser.parse(file.buffer, file.originalname);
const platform: Platform = body.platform
? (body.platform.toUpperCase() as Platform)
: (parsedInfo.platform as Platform);
const versionCode = body.versionCode
? parseInt(body.versionCode, 10)
: parsedInfo.versionCode || Date.now();
const versionName = body.versionName || parsedInfo.versionName || '1.0.0';
const buildNumber = body.buildNumber || versionCode.toString();
// Upload to MinIO
const uploadResult = await this.fileStorage.uploadFile(
file.buffer,
file.originalname,
platform,
versionName,
);
const version = await this.versionService.createVersion({
platform,
versionCode,
versionName,
buildNumber,
downloadUrl: uploadResult.downloadUrl,
fileSize: uploadResult.fileSize,
fileSha256: uploadResult.sha256,
changelog: body.changelog || '',
isForceUpdate: body.isForceUpdate === 'true',
minOsVersion: body.minOsVersion || parsedInfo.minSdkVersion,
releaseDate: body.releaseDate ? new Date(body.releaseDate) : undefined,
createdBy: req.user?.sub,
});
return { code: 0, data: version };
}
@Post('parse')
@UseInterceptors(FileInterceptor('file'))
@ApiConsumes('multipart/form-data')
@ApiOperation({ summary: 'Parse APK/IPA without saving (preview metadata)' })
async parsePackage(@UploadedFile() file: Express.Multer.File) {
const info = await this.packageParser.parse(file.buffer, file.originalname);
return { code: 0, data: info };
}
@Put(':id')
@ApiOperation({ summary: 'Update version' })
async updateVersion(
@Param('id') id: string,
@Body() body: any,
@Req() req: any,
) {
const version = await this.versionService.updateVersion(id, {
...body,
updatedBy: req.user?.sub,
});
return { code: 0, data: version };
}
@Patch(':id/toggle')
@ApiOperation({ summary: 'Enable/disable version' })
async toggleVersion(
@Param('id') id: string,
@Body() body: { isEnabled: boolean },
) {
const result = await this.versionService.toggleVersion(id, body.isEnabled);
return { code: 0, data: result };
}
@Delete(':id')
@ApiOperation({ summary: 'Delete version' })
async deleteVersion(@Param('id') id: string) {
const result = await this.versionService.deleteVersion(id);
return { code: 0, data: result };
}
}