import { Controller, Get, Post, Patch, Delete, Body, Param, UseGuards, Request, UseInterceptors, UploadedFile, Logger, Inject, forwardRef, } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { FileInterceptor } from '@nestjs/platform-express'; import { ApiTags, ApiOperation, ApiBearerAuth, ApiConsumes } from '@nestjs/swagger'; import { ApplicationsService } from './applications.service'; import { CreateApplicationDto, UpdateApplicationDto, ScaleResourcesDto } from './dto/application.dto'; import { RolesGuard } from '../common/guards/roles.guard'; import { Roles } from '../common/decorators/roles.decorator'; import { UserRole } from '../common/enums'; import { KubernetesService } from '../kubernetes/kubernetes.service'; import { DeploymentsService } from '../deployments/deployments.service'; @ApiTags('Applications') @ApiBearerAuth() @Controller('applications') @UseGuards(AuthGuard('jwt'), RolesGuard) export class ApplicationsController { private readonly logger = new Logger(ApplicationsController.name); constructor( private readonly applicationsService: ApplicationsService, private readonly kubernetesService: KubernetesService, @Inject(forwardRef(() => DeploymentsService)) private readonly deploymentsService: DeploymentsService, ) {} @Post() @ApiOperation({ summary: 'Create a new application' }) async create(@Request() req: any, @Body() dto: CreateApplicationDto) { return this.applicationsService.create(req.user.id, dto); } @Post(':id/upload') @ApiOperation({ summary: 'Upload application code (zip file)' }) @ApiConsumes('multipart/form-data') @UseInterceptors(FileInterceptor('file', { limits: { fileSize: 100 * 1024 * 1024 }, // 100MB })) async uploadCode( @Param('id') id: string, @Request() req: any, @UploadedFile() file: Express.Multer.File, ) { return this.applicationsService.uploadCode(id, req.user.id, file); } @Get() @ApiOperation({ summary: 'List my applications' }) async findAll(@Request() req: any) { if (req.user.role === UserRole.ADMIN) { return this.applicationsService.findAll(); } return this.applicationsService.findAllByUser(req.user.id); } @Get(':id') @ApiOperation({ summary: 'Get application details' }) async findOne(@Param('id') id: string, @Request() req: any) { if (req.user.role === UserRole.ADMIN) { return this.applicationsService.findOne(id); } return this.applicationsService.findOne(id, req.user.id); } @Patch(':id') @ApiOperation({ summary: 'Update application configuration' }) async update( @Param('id') id: string, @Request() req: any, @Body() dto: UpdateApplicationDto, ) { return this.applicationsService.update(id, req.user.id, dto); } @Get(':id/resources') @ApiOperation({ summary: 'Get real-time resource usage for an application' }) async getResources(@Param('id') id: string, @Request() req: any) { const app = await this.applicationsService.findOne( id, req.user.role === UserRole.ADMIN ? undefined : req.user.id, ); return this.kubernetesService.getResourceUsage(app); } @Patch(':id/resources') @ApiOperation({ summary: 'Update application resources (CPU/Memory/Replicas)' }) async updateResources( @Param('id') id: string, @Request() req: any, @Body() dto: ScaleResourcesDto, ) { const app = await this.applicationsService.findOne(id, req.user.id); // Update in K8s (live) await this.kubernetesService.updateResources(app, dto); // Update in DB const updateFields: any = {}; if (dto.cpuRequest) updateFields.cpuRequest = dto.cpuRequest; if (dto.cpuLimit) updateFields.cpuLimit = dto.cpuLimit; if (dto.memoryRequest) updateFields.memoryRequest = dto.memoryRequest; if (dto.memoryLimit) updateFields.memoryLimit = dto.memoryLimit; if (dto.replicas !== undefined) updateFields.replicas = dto.replicas; const updated = await this.applicationsService.update(id, req.user.id, updateFields); this.logger.log(`Updated resources for ${app.name}: ${JSON.stringify(dto)}`); return updated; } @Delete(':id') @ApiOperation({ summary: 'Delete an application and all its resources' }) async delete(@Param('id') id: string, @Request() req: any) { // 1. Get the app first const app = await this.applicationsService.findOne(id, req.user.id); // 2. Delete K8s resources (deployment, service, ingress, db, secrets) try { if (app.clusterId && app.latestImageTag) { await this.kubernetesService.deleteApplication(app); this.logger.log(`Deleted K8s resources for ${app.name}`); } } catch (e: any) { this.logger.warn(`K8s cleanup failed for ${app.name}: ${e.message}`); } // 3. Delete deployment records from DB try { await this.deploymentsService.deleteAllForApplication(app.id); } catch (e: any) { this.logger.warn(`Deployment records cleanup failed for ${app.name}: ${e.message}`); } // 4. Delete app (also deletes uploaded files) await this.applicationsService.delete(id, req.user.id); return { message: `Application "${app.name}" and all resources deleted` }; } }