import { Body, Controller, Delete, ForbiddenException, Get, HttpCode, HttpStatus, Inject, NotFoundException, Param, Patch, Post, UseGuards, } from '@nestjs/common'; import type { Brain } from '@mosaic/brain'; import { BRAIN } from '../brain/brain.tokens.js'; import { AuthGuard } from '../auth/auth.guard.js'; import { CurrentUser } from '../auth/current-user.decorator.js'; import { assertOwner } from '../auth/resource-ownership.js'; import { CreateMissionDto, UpdateMissionDto } from './missions.dto.js'; @Controller('api/missions') @UseGuards(AuthGuard) export class MissionsController { constructor(@Inject(BRAIN) private readonly brain: Brain) {} @Get() async list() { return this.brain.missions.findAll(); } @Get(':id') async findOne(@Param('id') id: string, @CurrentUser() user: { id: string }) { return this.getOwnedMission(id, user.id); } @Post() async create(@Body() dto: CreateMissionDto) { return this.brain.missions.create({ name: dto.name, description: dto.description, projectId: dto.projectId, status: dto.status, }); } @Patch(':id') async update( @Param('id') id: string, @Body() dto: UpdateMissionDto, @CurrentUser() user: { id: string }, ) { await this.getOwnedMission(id, user.id); if (dto.projectId) { await this.getOwnedProject(dto.projectId, user.id, 'Mission'); } const mission = await this.brain.missions.update(id, dto); if (!mission) throw new NotFoundException('Mission not found'); return mission; } @Delete(':id') @HttpCode(HttpStatus.NO_CONTENT) async remove(@Param('id') id: string, @CurrentUser() user: { id: string }) { await this.getOwnedMission(id, user.id); const deleted = await this.brain.missions.remove(id); if (!deleted) throw new NotFoundException('Mission not found'); } private async getOwnedMission(id: string, userId: string) { const mission = await this.brain.missions.findById(id); if (!mission) throw new NotFoundException('Mission not found'); await this.getOwnedProject(mission.projectId, userId, 'Mission'); return mission; } private async getOwnedProject( projectId: string | null | undefined, userId: string, resourceName: string, ) { if (!projectId) { throw new ForbiddenException(`${resourceName} does not belong to the current user`); } const project = await this.brain.projects.findById(projectId); if (!project) { throw new ForbiddenException(`${resourceName} does not belong to the current user`); } assertOwner(project.ownerId, userId, resourceName); return project; } }