import { Body, Controller, Delete, Get, Param, Patch, Post, Req, UploadedFile, UseGuards, UseInterceptors, } from '@nestjs/common'; import { ApiBearerAuth, ApiBody, ApiConsumes, ApiOperation, ApiTags, } from '@nestjs/swagger'; import { FileInterceptor } from '@nestjs/platform-express'; import { memoryStorage } from 'multer'; import { Audit } from '../../common/decorators/audit.decorator'; import { AdminJwtGuard } from '../../common/guards/admin-jwt.guard'; import { AdminAuditInterceptor } from '../../common/interceptors/audit/admin-audit.interceptor'; import type { RequestWithContext } from '../../common/interfaces/request-with-context.interface'; import { UpdateArtifactStatusDto } from './dto/update-artifact-status.dto'; import { UploadArtifactDto } from './dto/upload-artifact.dto'; import { AdminArtifactsService } from './admin-artifacts.service'; @ApiTags('admin-artifacts') @ApiBearerAuth('admin-access-token') @UseGuards(AdminJwtGuard) @UseInterceptors(AdminAuditInterceptor) @Controller('admin/tools/:id/artifacts') export class AdminArtifactsController { constructor(private readonly adminArtifactsService: AdminArtifactsService) {} @Post() @Audit({ action: 'artifact.upload', resourceType: 'artifact', resourceIdParam: 'id' }) @ApiOperation({ summary: 'Upload artifact file for tool' }) @ApiConsumes('multipart/form-data') @ApiBody({ schema: { type: 'object', properties: { file: { type: 'string', format: 'binary' }, version: { type: 'string', example: '1.0.0' }, releaseNotes: { type: 'string', example: 'Initial release' }, isLatest: { type: 'boolean', example: true }, }, required: ['file', 'version'], }, }) @UseInterceptors( FileInterceptor('file', { storage: memoryStorage(), limits: { fileSize: 512 * 1024 * 1024, }, }), ) uploadArtifact( @Param('id') id: string, @UploadedFile() file: Express.Multer.File | undefined, @Body() body: UploadArtifactDto, @Req() request: RequestWithContext, ) { return this.adminArtifactsService.uploadArtifact(id, file, body, request.user?.sub); } @Get() @ApiOperation({ summary: 'List tool artifacts' }) listArtifacts(@Param('id') id: string) { return this.adminArtifactsService.listToolArtifacts(id); } @Patch(':artifactId/latest') @Audit({ action: 'artifact.set_latest', resourceType: 'artifact', resourceIdParam: 'artifactId', }) @ApiOperation({ summary: 'Set latest artifact' }) setLatestArtifact(@Param('id') id: string, @Param('artifactId') artifactId: string) { return this.adminArtifactsService.setLatestArtifact(id, artifactId); } @Patch(':artifactId/status') @Audit({ action: 'artifact.update_status', resourceType: 'artifact', resourceIdParam: 'artifactId', }) @ApiOperation({ summary: 'Update artifact status' }) updateArtifactStatus( @Param('id') id: string, @Param('artifactId') artifactId: string, @Body() body: UpdateArtifactStatusDto, ) { return this.adminArtifactsService.updateArtifactStatus(id, artifactId, body.status); } @Delete(':artifactId') @Audit({ action: 'artifact.delete', resourceType: 'artifact', resourceIdParam: 'artifactId' }) @ApiOperation({ summary: 'Delete artifact metadata (soft via status=deleted)' }) deleteArtifact(@Param('id') id: string, @Param('artifactId') artifactId: string) { return this.adminArtifactsService.deleteArtifact(id, artifactId); } }