import { BadRequestException, Controller, Get, Inject, NotFoundException, Param, Query, UseGuards, } from '@nestjs/common'; import path from 'node:path'; import { AuthGuard } from '../auth/auth.guard.js'; import { CoordService } from './coord.service.js'; /** Only paths under these roots are allowed for coord queries. */ const ALLOWED_ROOTS = [process.cwd()]; function resolveAndValidatePath(raw: string | undefined): string { const resolved = path.resolve(raw ?? process.cwd()); const isAllowed = ALLOWED_ROOTS.some( (root) => resolved === root || resolved.startsWith(`${root}/`), ); if (!isAllowed) { throw new BadRequestException('projectPath is outside the allowed workspace'); } return resolved; } @Controller('api/coord') @UseGuards(AuthGuard) export class CoordController { constructor(@Inject(CoordService) private readonly coordService: CoordService) {} @Get('status') async missionStatus(@Query('projectPath') projectPath?: string) { const resolvedPath = resolveAndValidatePath(projectPath); const status = await this.coordService.getMissionStatus(resolvedPath); if (!status) throw new NotFoundException('No active coord mission found'); return status; } @Get('tasks') async listTasks(@Query('projectPath') projectPath?: string) { const resolvedPath = resolveAndValidatePath(projectPath); return this.coordService.listTasks(resolvedPath); } @Get('tasks/:taskId') async taskStatus(@Param('taskId') taskId: string, @Query('projectPath') projectPath?: string) { const resolvedPath = resolveAndValidatePath(projectPath); const detail = await this.coordService.getTaskStatus(resolvedPath, taskId); if (!detail) throw new NotFoundException(`Task ${taskId} not found in coord mission`); return detail; } }