import { Controller, Get, Post, Delete, Body, Param, Query, UseGuards, Request, Res, HttpCode, HttpStatus, } from '@nestjs/common'; import type { Response } from 'express'; import { ApiTags, ApiBearerAuth } from '@nestjs/swagger'; import { DMsService } from './dms.service'; import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; import { CreateDMConversationDto, SendDMDto, SendDMMessageDto, QueryDMMessagesDto, QueryDMConversationsDto, } from './dto'; interface RequestWithUser extends Request { user: { id: string; email: string; username?: string; }; } @ApiTags('Direct Messages') @UseGuards(JwtAuthGuard) @ApiBearerAuth() @Controller('dms') export class DMsController { constructor(private readonly dmsService: DMsService) {} /** * POST /dms * Get or create DM conversation */ @Post() @HttpCode(HttpStatus.CREATED) async createOrGetConversation( @Body() dto: CreateDMConversationDto, @Request() req: RequestWithUser, ): Promise { const conversation = await this.dmsService.getOrCreateConversation( req.user.id, dto, ); return { success: true, data: conversation, }; } /** * GET /dms * Get user's DM conversations */ @Get() @HttpCode(HttpStatus.OK) async getConversations( @Query() query: QueryDMConversationsDto, @Request() req: RequestWithUser, @Res({ passthrough: true }) res: Response, ): Promise { const result = await this.dmsService.getConversations(req.user.id, query); // Cache 30 seconds on client side res.setHeader('Cache-Control', 'private, max-age=30'); return { success: true, data: result.conversations, meta: { total: result.total, hasMore: result.hasMore, page: query.page || 1, limit: query.limit || 20, }, }; } /** * GET /dms/:conversationId/messages * Get messages in a DM conversation */ @Get(':conversationId/messages') @HttpCode(HttpStatus.OK) async getMessages( @Param('conversationId') conversationId: string, @Query() query: QueryDMMessagesDto, @Request() req: RequestWithUser, ): Promise { const result = await this.dmsService.getMessages( conversationId, req.user.id, req.user.username || '', query, ); return { success: true, data: result.messages, meta: { total: result.total, hasMore: result.hasMore, page: query.page || 1, limit: query.limit || 20, }, }; } /** * POST /dms/:conversationId/messages * Send a message in an existing DM conversation */ @Post(':conversationId/messages') @HttpCode(HttpStatus.CREATED) async sendMessage( @Param('conversationId') conversationId: string, @Body() dto: SendDMDto, @Request() req: RequestWithUser, ): Promise { const message = await this.dmsService.sendMessage( conversationId, req.user.id, dto, ); return { success: true, data: message, }; } /** * POST /dms/messages * Send a DM message (auto-creates conversation if needed) * * Use this endpoint when: * - Starting a new conversation: provide recipientId * - Sending to existing conversation: provide conversationId */ @Post('messages') @HttpCode(HttpStatus.CREATED) async sendDMMessage( @Body() dto: SendDMMessageDto, @Request() req: RequestWithUser, ): Promise { const result = await this.dmsService.sendDMMessage(req.user.id, dto); return { success: true, data: result, }; } /** * DELETE /dms/messages/:messageId * Delete a DM message */ @Delete('messages/:messageId') @HttpCode(HttpStatus.OK) async deleteMessage( @Param('messageId') messageId: string, @Request() req: RequestWithUser, ): Promise { await this.dmsService.deleteMessage(messageId, req.user.id); return { success: true, message: 'Message deleted successfully', }; } /** * POST /dms/:conversationId/read * Mark all messages in conversation as read */ @Post(':conversationId/read') @HttpCode(HttpStatus.OK) async markAsRead( @Param('conversationId') conversationId: string, @Request() req: RequestWithUser, ): Promise { await this.dmsService.markAsRead( conversationId, req.user.id, req.user.username || '', ); return { success: true, message: 'Messages marked as read', }; } /** * GET /dms/messages/:messageId/read-receipts * Get DM message read receipts with timestamps and latency */ @Get('messages/:messageId/read-receipts') @HttpCode(HttpStatus.OK) async getDMMessageReadReceipts( @Param('messageId') messageId: string, ): Promise { const latency = await this.dmsService.getDMReadLatency(messageId); return { success: true, data: latency, }; } /** * POST /dms/block/:userId * Block a user */ @Post('block/:userId') @HttpCode(HttpStatus.OK) async blockUser( @Param('userId') userId: string, @Request() req: RequestWithUser, ): Promise { await this.dmsService.blockUser(req.user.id, userId); return { success: true, message: 'User blocked successfully', }; } /** * DELETE /dms/block/:userId * Unblock a user */ @Delete('block/:userId') @HttpCode(HttpStatus.OK) async unblockUser( @Param('userId') userId: string, @Request() req: RequestWithUser, ): Promise { await this.dmsService.unblockUser(req.user.id, userId); return { success: true, message: 'User unblocked successfully', }; } /** * GET /dms/blocked * Get blocked users list */ @Get('blocked/list') @HttpCode(HttpStatus.OK) async getBlockedUsers(@Request() req: RequestWithUser): Promise { const blocked = await this.dmsService.getBlockedUsers(req.user.id); return { success: true, data: blocked, }; } }