import express from 'express'; import { authRequired } from '../../Application/Services/AuthMiddleware'; import { container } from '../../Application/Services/DIContainer'; import { ErrorResponseService } from '../../Application/Services/ErrorResponseService'; import { ValidationMiddleware } from '../../Application/Services/ValidationMiddleware'; import { logAuth, logError, logRequest, logWarning } from '../../Application/Services/Logger'; const chatRouter = express.Router(); // Get user's chats chatRouter.get('/user-chats', authRequired, async (req, res) => { try { const userId = (req as any).user.userId; const includeArchived = req.query.includeArchived === 'true'; logRequest('Get user chats endpoint accessed', req, res, { userId, includeArchived }); const chats = await container.getUserChatsQueryHandler.execute({ userId, includeArchived }); logRequest('User chats retrieved successfully', req, res, { userId, chatCount: chats.length }); res.json(chats); } catch (error) { logError('Get user chats endpoint error', error as Error, req, res); return ErrorResponseService.sendInternalServerError(res); } }); // Get chat history chatRouter.get('/history/:chatId', authRequired, ValidationMiddleware.validateUUIDFormat(['chatId']), async (req, res) => { try { const userId = (req as any).user.userId; const chatId = req.params.chatId; logRequest('Get chat history endpoint accessed', req, res, { userId, chatId }); const history = await container.getChatHistoryQueryHandler.execute({ chatId, userId }); if (!history) { logWarning('Chat history not found or unauthorized', { userId, chatId }, req, res); return ErrorResponseService.sendNotFound(res, 'Chat not found or unauthorized'); } logRequest('Chat history retrieved successfully', req, res, { userId, chatId, messageCount: history.messages.length, isArchived: history.isArchived }); res.json(history); } catch (error) { logError('Get chat history endpoint error', error as Error, req, res); return ErrorResponseService.sendInternalServerError(res); } }); // Create new chat (direct/group) chatRouter.post('/create', authRequired, ValidationMiddleware.combine([ ValidationMiddleware.validateRequiredFields(['type', 'userIds']), ValidationMiddleware.validateAllowedValues({ type: ['direct', 'group'] }), ValidationMiddleware.validateNonEmptyArrays(['userIds']) ]), async (req, res) => { try { const userId = (req as any).user.userId; const { type, name, userIds } = req.body; logRequest('Create chat endpoint accessed', req, res, { userId, type, targetUserCount: userIds?.length || 0 }); if (type === 'group' && !name?.trim()) { return ErrorResponseService.sendBadRequest(res, 'Group name is required'); } const chat = await container.createChatCommandHandler.execute({ type, name: name?.trim(), createdBy: userId, userIds }); if (!chat) { return ErrorResponseService.sendBadRequest(res, 'Failed to create chat'); } logRequest('Chat created successfully', req, res, { userId, chatId: chat.id, chatType: chat.type }); res.json({ id: chat.id, type: chat.type, name: chat.name, users: chat.users, messages: chat.messages }); } catch (error) { logError('Create chat endpoint error', error as Error, req, res); if (error instanceof Error) { if (error.message.includes('Premium subscription required')) { return ErrorResponseService.sendForbidden(res, 'Premium subscription required to create groups'); } if (error.message.includes('not found')) { return ErrorResponseService.sendNotFound(res, 'One or more users not found'); } } return ErrorResponseService.sendInternalServerError(res); } }); // Send message (REST endpoint - mainly for testing, real messaging is via WebSocket) chatRouter.post('/message', authRequired, ValidationMiddleware.combine([ ValidationMiddleware.validateRequiredFields(['chatId', 'message']), ValidationMiddleware.validateUUIDFormat(['chatId']), ValidationMiddleware.validateStringLength({ message: { min: 1, max: 2000 } }) ]), async (req, res) => { try { const userId = (req as any).user.userId; const { chatId, message } = req.body; logRequest('Send message endpoint accessed', req, res, { userId, chatId, messageLength: message?.length || 0 }); const sentMessage = await container.sendMessageCommandHandler.execute({ chatId, userId, message }); if (!sentMessage) { return ErrorResponseService.sendBadRequest(res, 'Failed to send message'); } logRequest('Message sent successfully', req, res, { userId, chatId, messageId: sentMessage.id }); res.json(sentMessage); } catch (error) { logError('Send message endpoint error', error as Error, req, res); if (error instanceof Error) { if (error.message.includes('Chat not found')) { return ErrorResponseService.sendNotFound(res, 'Chat not found'); } if (error.message.includes('not a member')) { return ErrorResponseService.sendForbidden(res, 'Not authorized to send messages to this chat'); } if (error.message.includes('non-empty string')) { return ErrorResponseService.sendBadRequest(res, 'Message must be a non-empty string'); } } return ErrorResponseService.sendInternalServerError(res); } }); // Archive chat manually chatRouter.post('/archive/:chatId', authRequired, ValidationMiddleware.validateUUIDFormat(['chatId']), async (req, res) => { try { const userId = (req as any).user.userId; const chatId = req.params.chatId; logRequest('Archive chat endpoint accessed', req, res, { userId, chatId }); // Check if user has access to this chat const chat = await container.chatRepository.findById(chatId); if (!chat) { return ErrorResponseService.sendNotFound(res, 'Chat not found'); } if (!chat.users.includes(userId)) { return ErrorResponseService.sendForbidden(res, 'Not authorized to archive this chat'); } const success = await container.archiveChatCommandHandler.execute({ chatId }); if (!success) { return ErrorResponseService.sendBadRequest(res, 'Failed to archive chat'); } logRequest('Chat archived successfully', req, res, { userId, chatId }); res.json({ success: true, message: 'Chat archived successfully' }); } catch (error) { logError('Archive chat endpoint error', error as Error, req, res); return ErrorResponseService.sendInternalServerError(res); } }); // Restore chat from archive chatRouter.post('/restore/:chatId', authRequired, ValidationMiddleware.validateUUIDFormat(['chatId']), async (req, res) => { try { const userId = (req as any).user.userId; const chatId = req.params.chatId; logRequest('Restore chat endpoint accessed', req, res, { userId, chatId }); // Check if user has access to this archived chat const archive = await container.chatArchiveRepository.findByChatId(chatId); const userArchive = archive.find((a: any) => a.participants.includes(userId)); if (!userArchive) { return ErrorResponseService.sendNotFound(res, 'Archived chat not found or unauthorized'); } const success = await container.restoreChatCommandHandler.execute({ chatId }); if (!success) { return ErrorResponseService.sendBadRequest(res, 'Failed to restore chat (game chats cannot be restored)'); } logRequest('Chat restored successfully', req, res, { userId, chatId }); res.json({ success: true, message: 'Chat restored successfully' }); } catch (error) { logError('Restore chat endpoint error', error as Error, req, res); return ErrorResponseService.sendInternalServerError(res); } }); // Get archived chats for a game chatRouter.get('/archived/game/:gameId', authRequired, ValidationMiddleware.validateUUIDFormat(['gameId']), async (req, res) => { try { const userId = (req as any).user.userId; const gameId = req.params.gameId; logRequest('Get archived game chats endpoint accessed', req, res, { userId, gameId }); const archivedChats = await container.getArchivedChatsQueryHandler.execute({ userId, gameId }); logRequest('Archived game chats retrieved successfully', req, res, { userId, gameId, chatCount: archivedChats.length }); res.json(archivedChats); } catch (error) { logError('Get archived game chats endpoint error', error as Error, req, res); return ErrorResponseService.sendInternalServerError(res); } }); export default chatRouter;