import { Controller, ForbiddenException, Get, NotFoundException, Param, UseGuards, } from '@nestjs/common'; import { AuthGuard } from '../auth/auth.guard.js'; import { CurrentUser } from '../auth/current-user.decorator.js'; import { TeamsService } from './teams.service.js'; type RequestUser = { id: string; role?: string }; @Controller('api/teams') @UseGuards(AuthGuard) export class TeamsController { constructor(private readonly teams: TeamsService) {} @Get() async list(@CurrentUser() user: RequestUser) { if (user.role === 'admin') { return this.teams.findAll(); } return this.teams.findAllForUser(user.id); } @Get(':teamId') async findOne(@Param('teamId') teamId: string, @CurrentUser() user: RequestUser) { return this.getAccessibleTeam(teamId, user); } @Get(':teamId/members') async listMembers(@Param('teamId') teamId: string, @CurrentUser() user: RequestUser) { await this.getAccessibleTeam(teamId, user); return this.teams.listMembers(teamId); } @Get(':teamId/members/:userId') async checkMembership( @Param('teamId') teamId: string, @Param('userId') userId: string, @CurrentUser() user: RequestUser, ) { // A user may always ask about their own membership; anything else is // team-scoped like the other routes. if (userId !== user.id) { await this.getAccessibleTeam(teamId, user); } const isMember = await this.teams.isMember(teamId, userId); return { isMember }; } /** * Team-scoped access: admins see any team; everyone else only teams they * are a member of. NotFoundException when the team does not exist and * ForbiddenException when the user lacks access (same convention as the * projects controller). */ private async getAccessibleTeam(teamId: string, user: RequestUser) { const team = await this.teams.findById(teamId); if (!team) throw new NotFoundException('Team not found'); if (user.role === 'admin') return team; const isMember = await this.teams.isMember(teamId, user.id); if (!isMember) throw new ForbiddenException('Not a member of this team'); return team; } }