import { Body, Controller, Delete, Get, Param, Post, Req, UseGuards } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { ChatService } from './chat.service'; import { ChatGateway } from './chat.gateway'; import { CreateChannelDto } from './dto/create-channel.dto'; import { CreateDirectChannelDto } from './dto/create-direct-channel.dto'; import { AddParticipantDto } from './dto/add-participant.dto'; import { Roles } from '../common/roles.decorator'; import { RolesGuard } from '../common/roles.guard'; import { Role } from '../common/role.enum'; import { AuthenticatedRequest } from '../auth/authenticated-request'; import { GuestJwtPayload } from '../auth/guest-auth.service'; import { resolveChatCaller } from './caller.util'; type ChatRequest = AuthenticatedRequest & { user?: AuthenticatedRequest['user'] | GuestJwtPayload }; @Controller('chat') export class ChatController { constructor( private readonly chat: ChatService, private readonly gateway: ChatGateway, ) {} /// Channel administration (Gemeinde-Gruppen, LT-Kanäle, Broadcasts) is Leitungsteam-only. @Post(':kcId/channels') @UseGuards(AuthGuard('authentik'), RolesGuard) @Roles(Role.LEITUNGSTEAM) createChannel(@Param('kcId') kcId: string, @Body() dto: CreateChannelDto) { return this.chat.createChannel(kcId, dto.type, dto.gemeindeId); } /// Free-form group chat ("Gruppenchat"): a Leitungsteam member (any KC) or /// a Gemeinde Verantwortliche/r (their own KC, enforced by RolesGuard's /// kcId scoping) can create one and pick any mix of team users and Konfis /// (guests) from this KC as initial participants. @Post(':kcId/gruppen') @UseGuards(AuthGuard(['authentik', 'team']), RolesGuard) @Roles(Role.LEITUNGSTEAM, Role.GEMEINDE_VERANTWORTLICHER) createGruppe( @Param('kcId') kcId: string, @Body() dto: CreateChannelDto, @Req() req: AuthenticatedRequest, ) { return this.chat.createGruppe(kcId, dto.name, req.user!.userId, { userIds: dto.participantUserIds, guestIds: dto.participantGuestIds, }); } /// Candidates (team users + Konfis) a caller may add to a Gruppenchat in /// this KC. Allowed for LT or a Verantwortliche/r of this KC. @Get(':kcId/gruppen/participant-candidates') @UseGuards(AuthGuard(['authentik', 'team'])) listPossibleParticipants(@Param('kcId') kcId: string, @Req() req: AuthenticatedRequest) { return this.chat.listPossibleParticipants(kcId, req.user!); } /// Add a team user or Konfi to a Gruppenchat. Allowed for the channel's /// creator, any Leitungsteam member, or a Verantwortliche/r of that KC. @Post('gruppen/:channelId/participants') @UseGuards(AuthGuard(['authentik', 'team'])) addParticipant( @Param('channelId') channelId: string, @Body() dto: AddParticipantDto, @Req() req: AuthenticatedRequest, ) { return this.chat .addParticipant( channelId, { kind: 'user', user: req.user! }, { userId: dto.userId, guestId: dto.guestId }, ) .then((result) => { this.gateway.notifyParticipantsChanged(channelId); return result; }); } /// Remove a team user or Konfi from a Gruppenchat. Same authorization as add. @Delete('gruppen/:channelId/participants') @UseGuards(AuthGuard(['authentik', 'team'])) removeParticipant( @Param('channelId') channelId: string, @Body() dto: AddParticipantDto, @Req() req: AuthenticatedRequest, ) { return this.chat .removeParticipant( channelId, { kind: 'user', user: req.user! }, { userId: dto.userId, guestId: dto.guestId }, ) .then((result) => { this.gateway.notifyParticipantsChanged(channelId); return result; }); } /// Any two team members of the same KC can start a direct conversation /// (Authentik-backed members and local Gemeinde Teamer alike). @Post('direct') @UseGuards(AuthGuard(['authentik', 'team'])) createDirectChannel(@Body() dto: CreateDirectChannelDto, @Req() req: AuthenticatedRequest) { return this.chat.getOrCreateDirectChannel(dto.kcId, req.user!.userId, dto.otherUserId); } @Get(':kcId/channels') @UseGuards(AuthGuard(['authentik', 'team', 'guest'])) listChannels(@Param('kcId') kcId: string, @Req() req: ChatRequest) { return this.chat.listChannelsForCaller(kcId, resolveChatCaller(req.user!)); } @Get('channels/:channelId/messages') @UseGuards(AuthGuard(['authentik', 'team', 'guest'])) listMessages(@Param('channelId') channelId: string, @Req() req: ChatRequest) { return this.chat.listMessages(channelId, resolveChatCaller(req.user!)); } }