288 lines
9.9 KiB
TypeScript
288 lines
9.9 KiB
TypeScript
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;
|