feat: Implement real-time messaging module with REST API endpoints and WebSocket gateway.
This commit is contained in:
218
src/messages/messages.controller.ts
Normal file
218
src/messages/messages.controller.ts
Normal file
@@ -0,0 +1,218 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Patch,
|
||||
Body,
|
||||
Param,
|
||||
Query,
|
||||
UseGuards,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiTags,
|
||||
ApiOperation,
|
||||
ApiResponse,
|
||||
ApiBearerAuth,
|
||||
ApiQuery,
|
||||
ApiParam,
|
||||
} from '@nestjs/swagger';
|
||||
import { MessagesService } from './messages.service';
|
||||
import { CreateMessageDto, StartConversationDto } from './dto';
|
||||
import { JwtAuthGuard } from '../auth/guards';
|
||||
import { CurrentUser } from '../auth/decorators';
|
||||
import { UserRole } from '@prisma/client';
|
||||
|
||||
@ApiTags('Messages')
|
||||
@Controller('messages')
|
||||
@UseGuards(JwtAuthGuard)
|
||||
@ApiBearerAuth('JWT-auth')
|
||||
export class MessagesController {
|
||||
constructor(private readonly messagesService: MessagesService) {}
|
||||
|
||||
// ==========================================
|
||||
// GET CONVERSATIONS LIST
|
||||
// ==========================================
|
||||
@Get('conversations')
|
||||
@ApiOperation({ summary: 'Get all conversations for the current user' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'List of conversations',
|
||||
schema: {
|
||||
example: [
|
||||
{
|
||||
id: 'conv-uuid',
|
||||
lastMessageAt: '2024-01-01T00:00:00.000Z',
|
||||
lastMessageText: 'Hello!',
|
||||
unreadCount: 2,
|
||||
otherParty: {
|
||||
id: 'agent-uuid',
|
||||
name: 'John Doe',
|
||||
avatar: 'https://...',
|
||||
headline: 'Real Estate Agent',
|
||||
isOnline: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
async getConversations(
|
||||
@CurrentUser('id') userId: string,
|
||||
@CurrentUser('role') role: UserRole,
|
||||
) {
|
||||
return this.messagesService.getConversations(userId, role);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// START OR GET CONVERSATION
|
||||
// ==========================================
|
||||
@Post('conversations')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: 'Start or get existing conversation with an agent' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Conversation created or retrieved',
|
||||
})
|
||||
@ApiResponse({
|
||||
status: 403,
|
||||
description: 'Connection not accepted',
|
||||
})
|
||||
async startConversation(
|
||||
@CurrentUser('id') userId: string,
|
||||
@Body() dto: StartConversationDto,
|
||||
) {
|
||||
return this.messagesService.getOrCreateConversation(userId, dto.agentProfileId);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// GET CONVERSATION DETAILS
|
||||
// ==========================================
|
||||
@Get('conversations/:id')
|
||||
@ApiOperation({ summary: 'Get a specific conversation' })
|
||||
@ApiParam({ name: 'id', description: 'Conversation ID' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Conversation details',
|
||||
})
|
||||
@ApiResponse({
|
||||
status: 404,
|
||||
description: 'Conversation not found',
|
||||
})
|
||||
async getConversation(
|
||||
@Param('id') conversationId: string,
|
||||
@CurrentUser('id') userId: string,
|
||||
) {
|
||||
return this.messagesService.getConversationById(conversationId, userId);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// GET MESSAGES FOR CONVERSATION
|
||||
// ==========================================
|
||||
@Get('conversations/:id/messages')
|
||||
@ApiOperation({ summary: 'Get messages for a conversation' })
|
||||
@ApiParam({ name: 'id', description: 'Conversation ID' })
|
||||
@ApiQuery({ name: 'page', required: false, type: Number })
|
||||
@ApiQuery({ name: 'limit', required: false, type: Number })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'List of messages with pagination',
|
||||
schema: {
|
||||
example: {
|
||||
messages: [
|
||||
{
|
||||
id: 'msg-uuid',
|
||||
content: 'Hello!',
|
||||
messageType: 'TEXT',
|
||||
status: 'READ',
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
sender: {
|
||||
id: 'user-uuid',
|
||||
role: 'USER',
|
||||
userProfile: {
|
||||
firstName: 'Jane',
|
||||
lastName: 'Doe',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
pagination: {
|
||||
page: 1,
|
||||
limit: 50,
|
||||
total: 100,
|
||||
pages: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
async getMessages(
|
||||
@Param('id') conversationId: string,
|
||||
@CurrentUser('id') userId: string,
|
||||
@Query('page') page?: string,
|
||||
@Query('limit') limit?: string,
|
||||
) {
|
||||
return this.messagesService.getMessages(
|
||||
conversationId,
|
||||
userId,
|
||||
page ? parseInt(page, 10) : 1,
|
||||
limit ? parseInt(limit, 10) : 50,
|
||||
);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// SEND MESSAGE (REST FALLBACK)
|
||||
// ==========================================
|
||||
@Post('conversations/:id/messages')
|
||||
@HttpCode(HttpStatus.CREATED)
|
||||
@ApiOperation({ summary: 'Send a message (REST fallback - prefer WebSocket)' })
|
||||
@ApiParam({ name: 'id', description: 'Conversation ID' })
|
||||
@ApiResponse({
|
||||
status: 201,
|
||||
description: 'Message created',
|
||||
})
|
||||
async sendMessage(
|
||||
@Param('id') conversationId: string,
|
||||
@CurrentUser('id') userId: string,
|
||||
@Body() dto: CreateMessageDto,
|
||||
) {
|
||||
return this.messagesService.createMessage(conversationId, userId, dto);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// MARK MESSAGES AS READ
|
||||
// ==========================================
|
||||
@Patch('conversations/:id/read')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: 'Mark all messages in conversation as read' })
|
||||
@ApiParam({ name: 'id', description: 'Conversation ID' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Messages marked as read',
|
||||
})
|
||||
async markAsRead(
|
||||
@Param('id') conversationId: string,
|
||||
@CurrentUser('id') userId: string,
|
||||
) {
|
||||
return this.messagesService.markMessagesAsRead(conversationId, userId);
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// GET UNREAD COUNT
|
||||
// ==========================================
|
||||
@Get('unread-count')
|
||||
@ApiOperation({ summary: 'Get total unread message count' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Unread count',
|
||||
schema: {
|
||||
example: { unreadCount: 5 },
|
||||
},
|
||||
})
|
||||
async getUnreadCount(
|
||||
@CurrentUser('id') userId: string,
|
||||
@CurrentUser('role') role: UserRole,
|
||||
) {
|
||||
const unreadCount = await this.messagesService.getTotalUnreadCount(userId, role);
|
||||
return { unreadCount };
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user