ваше сообщение коммита

This commit is contained in:
2025-04-28 22:54:47 +03:00
parent 5e062c8d9b
commit 3734bea350
13 changed files with 2746 additions and 2122 deletions

View File

@@ -1,14 +1,20 @@
const express = require('express');
const router = express.Router();
const multer = require('multer');
const aiAssistant = require('../services/ai-assistant');
const db = require('../db');
const logger = require('../utils/logger');
const { requireAuth } = require('../middleware/auth');
const crypto = require('crypto');
// Настройка multer для обработки файлов в памяти
const storage = multer.memoryStorage();
const upload = multer({ storage: storage });
// Функция для обработки гостевых сообщений после аутентификации
async function processGuestMessages(userId, guestId) {
try {
console.log(`Processing guest messages for user ${userId} with guest ID ${guestId}`);
logger.info(`Processing guest messages for user ${userId} with guest ID ${guestId}`);
// Проверяем, обрабатывались ли уже эти сообщения
const mappingCheck = await db.query(
@@ -18,7 +24,7 @@ async function processGuestMessages(userId, guestId) {
// Если сообщения уже обработаны, пропускаем
if (mappingCheck.rows.length > 0 && mappingCheck.rows[0].processed) {
console.log(`Guest messages for guest ID ${guestId} were already processed.`);
logger.info(`Guest messages for guest ID ${guestId} were already processed.`);
return { success: true, message: 'Guest messages already processed' };
}
@@ -28,35 +34,38 @@ async function processGuestMessages(userId, guestId) {
'INSERT INTO guest_user_mapping (user_id, guest_id) VALUES ($1, $2) ON CONFLICT (guest_id) DO UPDATE SET user_id = $1',
[userId, guestId]
);
console.log(`Created mapping for guest ID ${guestId} to user ${userId}`);
logger.info(`Created mapping for guest ID ${guestId} to user ${userId}`);
}
// Получаем все гостевые сообщения
// Получаем все гостевые сообщения со всеми новыми полями
const guestMessagesResult = await db.query(
'SELECT * FROM guest_messages WHERE guest_id = $1 ORDER BY created_at ASC',
`SELECT
id, guest_id, content, language, is_ai, created_at,
attachment_filename, attachment_mimetype, attachment_size, attachment_data
FROM guest_messages WHERE guest_id = $1 ORDER BY created_at ASC`,
[guestId]
);
if (guestMessagesResult.rows.length === 0) {
console.log('No guest messages found');
// Помечаем как обработанные, даже если сообщений нет
await db.query('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [
guestId,
]);
logger.info(`No guest messages found for guest ID ${guestId}`);
const checkResult = await db.query('SELECT 1 FROM guest_user_mapping WHERE guest_id = $1', [guestId]);
if (checkResult.rows.length > 0) {
await db.query('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [guestId]);
logger.info(`Marked guest mapping as processed (no messages found) for guest ID ${guestId}`);
} else {
logger.warn(`Attempted to mark non-existent guest mapping as processed for guest ID ${guestId}`);
}
return { success: true, message: 'No guest messages found' };
}
const guestMessages = guestMessagesResult.rows;
console.log(`Found ${guestMessages.length} guest messages`);
logger.info(`Found ${guestMessages.length} guest messages for guest ID ${guestId}`);
// Создаем новый диалог для этих сообщений
const firstMessage = guestMessages[0];
const title =
firstMessage.content.length > 30
? `${firstMessage.content.substring(0, 30)}...`
: firstMessage.content;
const title = firstMessage.content
? (firstMessage.content.length > 30 ? `${firstMessage.content.substring(0, 30)}...` : firstMessage.content)
: (firstMessage.attachment_filename ? `Файл: ${firstMessage.attachment_filename}` : 'Новый диалог');
const newConversationResult = await db.query(
'INSERT INTO conversations (user_id, title) VALUES ($1, $2) RETURNING *',
@@ -64,74 +73,79 @@ async function processGuestMessages(userId, guestId) {
);
const conversation = newConversationResult.rows[0];
console.log('Created new conversation for guest messages:', conversation);
logger.info(`Created new conversation ${conversation.id} for guest messages`);
// Отслеживаем успешные сохранения сообщений
const savedMessageIds = [];
// Обрабатываем каждое гостевое сообщение
for (const guestMessage of guestMessages) {
console.log(`Processing guest message ID ${guestMessage.id}: ${guestMessage.content}`);
logger.info(`Processing guest message ID ${guestMessage.id}: ${guestMessage.content || guestMessage.attachment_filename || '(empty)'}`);
try {
// Сохраняем сообщение пользователя
// Сохраняем сообщение пользователя в таблицу messages, включая данные файла
const userMessageResult = await db.query(
`INSERT INTO messages
(conversation_id, content, sender_type, role, channel, created_at, user_id)
VALUES
($1, $2, $3, $4, $5, $6, $7)
`INSERT INTO messages
(conversation_id, content, sender_type, role, channel, created_at, user_id,
attachment_filename, attachment_mimetype, attachment_size, attachment_data)
VALUES
($1, $2, 'user', 'user', 'web', $3, $4,
$5, $6, $7, $8)
RETURNING *`,
[
conversation.id,
guestMessage.content,
'user',
'user',
'web',
guestMessage.content, // Текст (может быть NULL)
guestMessage.created_at,
userId, // Добавляем userId в сообщение для прямой связи
userId,
guestMessage.attachment_filename, // Метаданные и данные файла
guestMessage.attachment_mimetype,
guestMessage.attachment_size,
guestMessage.attachment_data // BYTEA
]
);
console.log(`Saved user message with ID ${userMessageResult.rows[0].id}`);
const savedUserMessage = userMessageResult.rows[0];
logger.info(`Saved user message with ID ${savedUserMessage.id}`);
savedMessageIds.push(guestMessage.id);
// Получаем ответ от ИИ только для сообщений пользователя (не AI)
if (!guestMessage.is_ai) {
console.log('Getting AI response for:', guestMessage.content);
// Получаем ответ от ИИ только для текстовых сообщений
if (!guestMessage.is_ai && guestMessage.content) {
logger.info('Getting AI response for:', guestMessage.content);
const language = guestMessage.language || 'auto';
const aiResponse = await aiAssistant.getResponse(guestMessage.content, language);
console.log('AI response received:', aiResponse);
// Предполагаем, что aiAssistant.getResponse принимает только текст
const aiResponseContent = await aiAssistant.getResponse(guestMessage.content, language);
logger.info('AI response received' + (aiResponseContent ? '' : ' (empty)'), 'for conversation', conversation.id);
// Сохраняем ответ от ИИ
const aiMessageResult = await db.query(
`INSERT INTO messages
(conversation_id, content, sender_type, role, channel, created_at, user_id)
VALUES
($1, $2, $3, $4, $5, $6, $7)
RETURNING *`,
[
conversation.id,
aiResponse,
'assistant',
'assistant',
'web',
new Date(),
userId, // Добавляем userId в сообщение для прямой связи
]
);
console.log(`Saved AI response with ID ${aiMessageResult.rows[0].id}`);
if (aiResponseContent) {
// Сохраняем ответ от ИИ (у него нет вложений)
const aiMessageResult = await db.query(
`INSERT INTO messages
(conversation_id, content, sender_type, role, channel, created_at, user_id)
VALUES
($1, $2, 'assistant', 'assistant', 'web', $3, $4)
RETURNING *`,
[
conversation.id,
aiResponseContent,
new Date(),
userId
]
);
logger.info(`Saved AI response with ID ${aiMessageResult.rows[0].id}`);
}
} else {
logger.info(`Skipping AI response for guest message ID ${guestMessage.id} (is_ai: ${guestMessage.is_ai}, hasContent: ${!!guestMessage.content})`);
}
} catch (error) {
console.error(`Error processing guest message ${guestMessage.id}:`, error);
logger.error(`Error processing guest message ${guestMessage.id}: ${error.message}`, { stack: error.stack });
// Продолжаем с другими сообщениями в случае ошибки
}
}
// Удаляем только успешно обработанные гостевые сообщения
if (savedMessageIds.length > 0) {
await db.query('DELETE FROM guest_messages WHERE id = ANY($1)', [savedMessageIds]);
console.log(
await db.query('DELETE FROM guest_messages WHERE id = ANY($1::int[])', [savedMessageIds]);
logger.info(
`Deleted ${savedMessageIds.length} processed guest messages for guest ID ${guestId}`
);
@@ -139,8 +153,12 @@ async function processGuestMessages(userId, guestId) {
await db.query('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [
guestId,
]);
logger.info(`Marked guest mapping as processed for guest ID ${guestId}`);
} else {
console.log('No guest messages were successfully processed, skipping deletion');
logger.warn(`No guest messages were successfully processed, skipping deletion for guest ID ${guestId}`);
// Если не было успешных, все равно пометим как обработанные, чтобы не пытаться снова
await db.query('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [guestId]);
logger.info(`Marked guest mapping as processed (no successful messages) for guest ID ${guestId}`);
}
return {
@@ -149,130 +167,244 @@ async function processGuestMessages(userId, guestId) {
conversationId: conversation.id,
};
} catch (error) {
console.error('Error processing guest messages:', error);
throw error;
logger.error(`Error in processGuestMessages for guest ID ${guestId}: ${error.message}`, { stack: error.stack });
// Не пробрасываем ошибку дальше, чтобы не прерывать основной поток, но логируем ее
return { success: false, error: 'Internal error during guest message processing' };
}
}
// Обработчик для гостевых сообщений
router.post('/guest-message', async (req, res) => {
try {
const { content, language, guestId: requestGuestId } = req.body;
router.post('/guest-message', upload.array('attachments'), async (req, res) => {
// Логируем полученные данные
logger.info('Received /guest-message request');
logger.debug('Request Body:', req.body);
logger.debug('Request Files:', req.files); // Файлы будут здесь
if (!content) {
return res.status(400).json({ success: false, error: 'Content is required' });
try {
// Извлекаем данные из req.body (текстовые поля)
const { message, language, guestId: requestGuestId } = req.body;
const files = req.files; // Файлы извлекаем из req.files
const file = files && files.length > 0 ? files[0] : null; // Берем только первый файл
// Валидация: должно быть либо сообщение, либо файл
if (!message && !file) {
logger.warn('Guest message attempt without content or file.', { guestId: requestGuestId });
return res.status(400).json({ success: false, error: 'Требуется текст сообщения или файл.' });
}
// Запрещаем и текст, и файл одновременно (согласно новым требованиям)
if (message && file) {
logger.warn('Guest message attempt with both text and file.', { guestId: requestGuestId });
return res.status(400).json({ success: false, error: 'Нельзя отправить текст и файл одновременно.' });
}
// Используем гостевой ID из запроса или из сессии, или генерируем новый
const guestId = requestGuestId || req.session.guestId || crypto.randomBytes(16).toString('hex');
// Сохраняем ID гостя в сессии
req.session.guestId = guestId;
await req.session.save();
// Сохраняем/обновляем ID гостя в сессии
if (req.session.guestId !== guestId) {
req.session.guestId = guestId;
}
console.log('Saving guest message:', { guestId, content });
// Подготавливаем данные для вставки
const messageContent = message || ''; // Текст или ПУСТАЯ СТРОКА, если есть файл
const attachmentFilename = file ? file.originalname : null;
const attachmentMimetype = file ? file.mimetype : null;
const attachmentSize = file ? file.size : null;
const attachmentData = file ? file.buffer : null; // Сам буфер файла
// Сохраняем сообщение пользователя
logger.info('Saving guest message:', {
guestId,
message: messageContent,
file: attachmentFilename,
mimetype: attachmentMimetype,
size: attachmentSize
});
// Сохраняем сообщение пользователя с текстом или файлом
const result = await db.query(
'INSERT INTO guest_messages (guest_id, content, language, is_ai) VALUES ($1, $2, $3, false) RETURNING id',
[guestId, content, language || 'auto']
`INSERT INTO guest_messages
(guest_id, content, language, is_ai,
attachment_filename, attachment_mimetype, attachment_size, attachment_data)
VALUES ($1, $2, $3, false, $4, $5, $6, $7) RETURNING id`,
[
guestId,
messageContent, // Текст сообщения или NULL
language || 'auto',
attachmentFilename,
attachmentMimetype,
attachmentSize,
attachmentData // BYTEA данные файла или NULL
]
);
console.log('Guest message saved:', result.rows[0]);
const savedMessageId = result.rows[0].id;
logger.info('Guest message saved with ID:', savedMessageId);
// Сохраняем сессию после успешной операции с БД
try {
await new Promise((resolve, reject) => {
req.session.save((err) => {
if (err) return reject(err);
resolve();
});
});
logger.info('Session saved after guest message');
} catch (sessionError) {
logger.error('Error saving session after guest message:', sessionError);
// Не прерываем ответ пользователю из-за ошибки сессии
}
res.json({
success: true,
messageId: result.rows[0].id,
messageId: savedMessageId, // Возвращаем ID сохраненного сообщения
guestId: guestId // Возвращаем использованный guestId
});
} catch (error) {
console.error('Error saving guest message:', error);
res.status(500).json({ success: false, error: 'Internal server error' });
logger.error('Error saving guest message:', error);
res.status(500).json({ success: false, error: 'Ошибка сохранения гостевого сообщения' });
}
});
// Маршрут для обычных сообщений (для аутентифицированных пользователей)
router.post('/message', requireAuth, async (req, res) => {
const { message, conversationId, language = 'auto' } = req.body;
// Обработчик для сообщений аутентифицированных пользователей
router.post('/message', requireAuth, upload.array('attachments'), async (req, res) => {
logger.info('Received /message request');
logger.debug('Request Body:', req.body);
logger.debug('Request Files:', req.files);
if (!message) {
return res.status(400).json({ error: 'Message is required' });
const userId = req.session.userId;
const { message, language, conversationId: convIdFromRequest } = req.body;
const files = req.files;
const file = files && files.length > 0 ? files[0] : null;
// Валидация: должно быть либо сообщение, либо файл
if (!message && !file) {
logger.warn('Authenticated message attempt without content or file.', { userId });
return res.status(400).json({ success: false, error: 'Требуется текст сообщения или файл.' });
}
// Запрещаем и текст, и файл одновременно
if (message && file) {
logger.warn('Authenticated message attempt with both text and file.', { userId });
return res.status(400).json({ success: false, error: 'Нельзя отправить текст и файл одновременно.' });
}
let conversationId = convIdFromRequest;
let conversation = null;
try {
console.log('Processing message:', {
message,
conversationId,
language,
userId: req.session.userId,
});
const userId = req.session.userId;
let conversation;
// Если указан ID диалога, проверяем его существование и принадлежность пользователю
// Найти или создать диалог
if (conversationId) {
const conversationResult = await db.query(
const convResult = await db.query(
'SELECT * FROM conversations WHERE id = $1 AND user_id = $2',
[conversationId, userId]
);
if (conversationResult.rows.length === 0) {
return res.status(404).json({ error: 'Conversation not found or access denied' });
if (convResult.rows.length === 0) {
logger.warn('Conversation not found or access denied', { conversationId, userId });
return res.status(404).json({ success: false, error: 'Диалог не найден или доступ запрещен' });
}
conversation = conversationResult.rows[0];
console.log('Using existing conversation:', conversation);
conversation = convResult.rows[0];
} else {
// Создаем новый диалог
const title = message.length > 30 ? `${message.substring(0, 30)}...` : message;
// Создаем новый диалог, если ID не предоставлен
const title = message
? (message.length > 50 ? `${message.substring(0, 50)}...` : message)
: (file ? `Файл: ${file.originalname}` : 'Новый диалог');
const newConversationResult = await db.query(
const newConvResult = await db.query(
'INSERT INTO conversations (user_id, title) VALUES ($1, $2) RETURNING *',
[userId, title]
);
conversation = newConversationResult.rows[0];
console.log('Created new conversation:', conversation);
conversation = newConvResult.rows[0];
conversationId = conversation.id;
logger.info('Created new conversation', { conversationId, userId });
}
// Подготавливаем данные для вставки сообщения пользователя
const messageContent = message || ''; // Текст или ПУСТАЯ СТРОКА, если есть файл
const attachmentFilename = file ? file.originalname : null;
const attachmentMimetype = file ? file.mimetype : null;
const attachmentSize = file ? file.size : null;
const attachmentData = file ? file.buffer : null;
// Сохраняем сообщение пользователя
console.log('Saving user message');
const userMessageResult = await db.query(
`INSERT INTO messages
(conversation_id, content, sender_type, role, tokens_used, channel, created_at)
VALUES
($1, $2, $3, $4, $5, $6, $7)
`INSERT INTO messages
(conversation_id, user_id, content, sender_type, role, channel,
attachment_filename, attachment_mimetype, attachment_size, attachment_data)
VALUES ($1, $2, $3, 'user', 'user', 'web', $4, $5, $6, $7)
RETURNING *`,
[conversation.id, message, 'user', 'user', 0, 'web', new Date()]
[
conversationId,
userId,
messageContent,
attachmentFilename,
attachmentMimetype,
attachmentSize,
attachmentData
]
);
const userMessage = userMessageResult.rows[0];
logger.info('User message saved', { messageId: userMessage.id, conversationId });
// Получаем ответ от ИИ
console.log('Getting AI response');
const aiResponse = await aiAssistant.getResponse(message, language);
console.log('AI response received:', aiResponse);
// Получаем ответ от ИИ, только если это было текстовое сообщение
let aiMessage = null;
if (messageContent) { // Только для текстовых сообщений
try {
const detectedLanguage = language === 'auto' ? aiAssistant.detectLanguage(messageContent) : language;
logger.info('Getting AI response for:', messageContent);
const aiResponseContent = await aiAssistant.getResponse(messageContent, detectedLanguage);
logger.info('AI response received' + (aiResponseContent ? '' : ' (empty)'), { conversationId });
// Сохраняем ответ от ИИ
console.log('Saving AI response');
const aiMessageResult = await db.query(
`INSERT INTO messages
(conversation_id, content, sender_type, role, tokens_used, channel, created_at)
VALUES
($1, $2, $3, $4, $5, $6, $7)
RETURNING *`,
[conversation.id, aiResponse, 'assistant', 'assistant', 0, 'web', new Date()]
);
if (aiResponseContent) {
const aiMessageResult = await db.query(
`INSERT INTO messages
(conversation_id, user_id, content, sender_type, role, channel)
VALUES ($1, $2, $3, 'assistant', 'assistant', 'web')
RETURNING *`,
[conversationId, userId, aiResponseContent]
);
aiMessage = aiMessageResult.rows[0];
logger.info('AI response saved', { messageId: aiMessage.id, conversationId });
}
} catch (aiError) {
logger.error('Error getting or saving AI response:', aiError);
// Не прерываем основной ответ, но логируем ошибку
}
}
const response = {
success: true,
userMessage: userMessageResult.rows[0],
aiMessage: aiMessageResult.rows[0],
conversation,
// Форматируем ответ для фронтенда
const formatMessageForFrontend = (msg) => {
if (!msg) return null;
const formatted = {
id: msg.id,
conversation_id: msg.conversation_id,
user_id: msg.user_id,
content: msg.content,
sender_type: msg.sender_type,
role: msg.role,
channel: msg.channel,
created_at: msg.created_at,
attachments: null // Инициализируем как null
};
// Добавляем информацию о файле, если она есть
if (msg.attachment_filename) {
formatted.attachments = [{
originalname: msg.attachment_filename,
mimetype: msg.attachment_mimetype,
size: msg.attachment_size,
// НЕ передаем attachment_data обратно в ответе на POST
}];
}
return formatted;
};
res.json(response);
res.json({
success: true,
conversationId: conversationId,
userMessage: formatMessageForFrontend(userMessage),
aiMessage: formatMessageForFrontend(aiMessage),
});
} catch (error) {
console.error('Error processing message:', error);
res.status(500).json({ error: 'Error processing message' });
logger.error('Error processing authenticated message:', error);
res.status(500).json({ success: false, error: 'Ошибка обработки сообщения' });
}
});
@@ -292,79 +424,120 @@ router.get('/models', async (req, res) => {
});
// Получение истории сообщений
router.get('/history', async (req, res) => {
router.get('/history', requireAuth, async (req, res) => {
const userId = req.session.userId;
// Параметры пагинации
const limit = parseInt(req.query.limit, 10) || 30;
const offset = parseInt(req.query.offset, 10) || 0;
// Флаг для запроса только количества
const countOnly = req.query.count_only === 'true';
// Опциональный ID диалога
const conversationId = req.query.conversation_id;
try {
console.log('Session in history route:', {
id: req.sessionID,
userId: req.session.userId,
address: req.session.address,
authenticated: req.session.authenticated,
guestId: req.session.guestId,
// Если нужен только подсчет
if (countOnly) {
let countQuery = 'SELECT COUNT(*) FROM messages WHERE user_id = $1';
let countParams = [userId];
if (conversationId) {
countQuery += ' AND conversation_id = $2';
countParams.push(conversationId);
}
const countResult = await db.query(countQuery, countParams);
const totalCount = parseInt(countResult.rows[0].count, 10);
return res.json({ success: true, count: totalCount });
}
// Формируем основной запрос
let query = `
SELECT
id,
conversation_id,
user_id,
content,
sender_type,
role,
channel,
created_at,
attachment_filename,
attachment_mimetype,
attachment_size,
attachment_data -- Выбираем и данные файла
FROM messages
WHERE user_id = $1
`;
const params = [userId];
// Добавляем фильтр по диалогу, если нужно
if (conversationId) {
query += ' AND conversation_id = $2';
params.push(conversationId);
}
// Добавляем сортировку и пагинацию
query += ' ORDER BY created_at ASC LIMIT $' + (params.length + 1) + ' OFFSET $' + (params.length + 2);
params.push(limit);
params.push(offset);
logger.debug('Executing history query:', { query, params });
const result = await db.query(query, params);
// Обрабатываем результаты для фронтенда
const messages = result.rows.map(msg => {
const formatted = {
id: msg.id,
conversation_id: msg.conversation_id,
user_id: msg.user_id,
content: msg.content,
sender_type: msg.sender_type,
role: msg.role,
channel: msg.channel,
created_at: msg.created_at,
attachments: null // Инициализируем
};
// Если есть данные файла, добавляем их в attachments
if (msg.attachment_data) {
formatted.attachments = [{
originalname: msg.attachment_filename,
mimetype: msg.attachment_mimetype,
size: msg.attachment_size,
// Кодируем Buffer в Base64 для передачи на фронтенд
data_base64: msg.attachment_data.toString('base64')
}];
}
// Не забываем удалить поле attachment_data из итогового объекта,
// так как оно уже обработано и не нужно в сыром виде на фронте
// (хотя map и так создает новый объект, это для ясности)
delete formatted.attachment_data;
return formatted;
});
const limit = parseInt(req.query.limit) || 50;
const offset = parseInt(req.query.offset) || 0;
// Если пользователь аутентифицирован и у него есть гостевые сообщения,
// автоматически связываем их перед получением истории
if (req.session.authenticated && req.session.userId && req.session.guestId) {
try {
console.log('Automatically linking guest messages before fetching history');
await processGuestMessages(req.session.userId, req.session.guestId);
// Очищаем guestId из сессии после связывания
req.session.guestId = null;
await req.session.save();
console.log('Guest messages automatically linked');
} catch (linkError) {
console.error('Error auto-linking guest messages:', linkError);
// Продолжаем выполнение, даже если связывание не удалось
}
// Получаем общее количество сообщений для пагинации (если не запрашивали только количество)
let totalCountQuery = 'SELECT COUNT(*) FROM messages WHERE user_id = $1';
let totalCountParams = [userId];
if (conversationId) {
totalCountQuery += ' AND conversation_id = $2';
totalCountParams.push(conversationId);
}
const totalCountResult = await db.query(totalCountQuery, totalCountParams);
const totalMessages = parseInt(totalCountResult.rows[0].count, 10);
let messages = [];
let total = 0;
logger.info(`Returning message history for user ${userId}`, { count: messages.length, offset, limit, total: totalMessages });
// Если пользователь аутентифицирован, получаем его сообщения
if (req.session.authenticated && req.session.userId) {
const countResult = await db.query(
`SELECT COUNT(*) as total FROM messages m
JOIN conversations c ON m.conversation_id = c.id
WHERE c.user_id = $1`,
[req.session.userId]
);
total = parseInt(countResult.rows[0].total) || 0;
const result = await db.query(
`SELECT
m.id,
m.content,
m.sender_type,
m.role,
m.created_at,
c.user_id,
c.id as conversation_id
FROM messages m
JOIN conversations c ON m.conversation_id = c.id
WHERE c.user_id = $1
ORDER BY m.created_at ASC
LIMIT $2 OFFSET $3`,
[req.session.userId, limit, offset]
);
messages = result.rows;
console.log(`Found ${messages.length} messages for authenticated user`);
}
return res.json({
res.json({
success: true,
messages: messages,
total: total,
offset: offset,
limit: limit,
total: totalMessages
});
} catch (error) {
logger.error('Error getting chat history:', error);
return res.status(500).json({ error: 'Internal server error' });
logger.error(`Error fetching message history for user ${userId}: ${error.message}`, { stack: error.stack });
res.status(500).json({ success: false, error: 'Ошибка получения истории сообщений' });
}
});