diff --git a/backend/controllers/conversationController.js b/backend/controllers/conversationController.js index 4355bc1..2eb7c1f 100644 --- a/backend/controllers/conversationController.js +++ b/backend/controllers/conversationController.js @@ -289,10 +289,90 @@ const deleteMessage = async (req, res, next) => { } }; +// @desc Редактировать сообщение +// @route PUT /api/conversations/:conversationId/messages/:messageId +// @access Private +const editMessage = async (req, res, next) => { + const { conversationId, messageId } = req.params; + const { text } = req.body; + const currentUserId = req.user._id; + + if (!text || text.trim() === '') { + const error = new Error('Текст сообщения не может быть пустым.'); + error.statusCode = 400; + return next(error); + } + + try { + const conversation = await Conversation.findById(conversationId); + if (!conversation) { + const error = new Error('Диалог не найден.'); + error.statusCode = 404; + return next(error); + } + + if (!conversation.participants.includes(currentUserId)) { + const error = new Error('Вы не являетесь участником этого диалога.'); + error.statusCode = 403; + return next(error); + } + + const message = await Message.findById(messageId); + if (!message) { + const error = new Error('Сообщение не найдено.'); + error.statusCode = 404; + return next(error); + } + + if (!message.sender.equals(currentUserId)) { + const error = new Error('Вы можете редактировать только свои сообщения.'); + error.statusCode = 403; + return next(error); + } + + // Ограничение по времени на редактирование (например, 24 часа) + // const twentyFourHours = 24 * 60 * 60 * 1000; + // if (new Date() - new Date(message.createdAt) > twentyFourHours) { + // const error = new Error('Сообщение не может быть отредактировано по истечении 24 часов.'); + // error.statusCode = 403; + // return next(error); + // } + + message.text = text.trim(); + message.isEdited = true; + message.editedAt = new Date(); + + const updatedMessage = await message.save(); + + // Загружаем данные отправителя для ответа клиенту + await updatedMessage.populate('sender', 'name photos _id'); + + + // Эмитим событие редактирования сообщения в комнату диалога + const io = req.app.get('io'); + if (io) { + io.to(conversationId).emit('messageEdited', { + message: updatedMessage.toObject(), // Отправляем обновленное сообщение целиком + conversationId, + }); + console.log(`[CONV_CTRL] Emitted messageEdited to room ${conversationId} for message ${messageId}`); + } else { + console.warn('[CONV_CTRL] Socket.io instance (io) not found on req.app. Could not emit messageEdited.'); + } + + res.status(200).json(updatedMessage); + + } catch (error) { + console.error(`[CONV_CTRL] Ошибка при редактировании сообщения ${messageId} из диалога ${conversationId}:`, error.message); + next(error); + } +}; + module.exports = { createOrGetConversation, getUserConversations, getMessagesForConversation, - deleteMessage, // <-- Добавляем новый метод + deleteMessage, + editMessage, // <-- Добавляем новый метод }; \ No newline at end of file diff --git a/backend/models/Message.js b/backend/models/Message.js index 65b2557..997eebd 100644 --- a/backend/models/Message.js +++ b/backend/models/Message.js @@ -26,6 +26,13 @@ const messageSchema = new mongoose.Schema( type: mongoose.Schema.Types.ObjectId, ref: 'User' }], + isEdited: { + type: Boolean, + default: false, + }, + editedAt: { + type: Date, + }, // Или просто флаг для 1-на-1 чата // isRead: { // type: Boolean, diff --git a/backend/routes/conversationRoutes.js b/backend/routes/conversationRoutes.js index 9a27f0b..8d89230 100644 --- a/backend/routes/conversationRoutes.js +++ b/backend/routes/conversationRoutes.js @@ -5,7 +5,8 @@ const { createOrGetConversation, getUserConversations, getMessagesForConversation, - deleteMessage // Импортируем новый контроллер + deleteMessage, // Импортируем новый контроллер + editMessage // Импортируем контроллер редактирования } = require('../controllers/conversationController'); const { protect } = require('../middleware/authMiddleware'); @@ -23,4 +24,9 @@ router.route('/:conversationId/messages') router.route('/:conversationId/messages/:messageId') .delete(deleteMessage); // DELETE /api/conversations/:conversationId/messages/:messageId (удалить сообщение) +// Маршрут для редактирования сообщения +router.route('/:conversationId/messages/:messageId') + .put(editMessage); // PUT /api/conversations/:conversationId/messages/:messageId (редактировать сообщение) + + module.exports = router; \ No newline at end of file diff --git a/src/services/api.js b/src/services/api.js index 4cd4390..a0a211b 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -103,5 +103,8 @@ export default { }, deleteMessage(conversationId, messageId) { return apiClient.delete(`/conversations/${conversationId}/messages/${messageId}`); + }, + editMessage(conversationId, messageId, newText) { + return apiClient.put(`/conversations/${conversationId}/messages/${messageId}`, { text: newText }); } }; \ No newline at end of file diff --git a/src/views/ChatView.vue b/src/views/ChatView.vue index e5a731e..273a839 100644 --- a/src/views/ChatView.vue +++ b/src/views/ChatView.vue @@ -33,6 +33,7 @@

{{ item.data.text }}

{{ formatMessageTimestamp(item.data.createdAt) }} + (изменено) (отправка...) @@ -41,12 +42,18 @@ - + @@ -57,6 +64,10 @@ class="context-menu" :style="{ top: contextMenu.y + 'px', left: contextMenu.x + 'px' }">