From d80e73543dd93ad41445de5fbf4503dd2c7be5aa Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 28 Mar 2025 21:31:46 +0300 Subject: [PATCH] =?UTF-8?q?=D0=B2=D0=B0=D1=88=D0=B5=20=D1=81=D0=BE=D0=BE?= =?UTF-8?q?=D0=B1=D1=89=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=BC=D0=BC?= =?UTF-8?q?=D0=B8=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/routes/chat.js | 398 +++++++++++++++++++--------- backend/services/ai-assistant.js | 49 +++- frontend/src/assets/styles/home.css | 16 +- frontend/src/views/HomeView.vue | 171 ++++++++---- 4 files changed, 438 insertions(+), 196 deletions(-) diff --git a/backend/routes/chat.js b/backend/routes/chat.js index 8e738ea..d6badf4 100644 --- a/backend/routes/chat.js +++ b/backend/routes/chat.js @@ -69,59 +69,99 @@ async function getAIResponse(message, language = 'ru') { // Функция для обработки гостевых сообщений после аутентификации async function processGuestMessages(userId, guestId) { try { - console.log(`Starting to process guest messages for user ${userId} with guestId ${guestId}`); - + console.log(`Processing guest messages for user ${userId} with guest ID ${guestId}`); + // Получаем все гостевые сообщения - const guestMessages = await db.query( - `SELECT m.id, m.content, m.conversation_id, m.metadata, m.created_at - FROM messages m - WHERE m.metadata->>'guest_id' = $1 - ORDER BY m.created_at ASC`, + const guestMessagesResult = await db.query( + 'SELECT * FROM guest_messages WHERE guest_id = $1 ORDER BY created_at ASC', [guestId] ); - - console.log(`Found ${guestMessages.rows.length} guest messages to process`); - - // Обновляем user_id для всех бесед с гостевыми сообщениями - await db.query( - `UPDATE conversations c - SET user_id = $1 - WHERE id IN ( - SELECT DISTINCT conversation_id - FROM messages m - WHERE m.metadata->>'guest_id' = $2 - )`, - [userId, guestId] - ); - - // Обрабатываем каждое гостевое сообщение - for (const msg of guestMessages.rows) { - console.log(`Processing guest message ${msg.id}: ${msg.content}`); - - // Получаем язык из метаданных - const metadata = typeof msg.metadata === 'string' ? JSON.parse(msg.metadata) : msg.metadata; - const language = metadata?.language || 'ru'; - - // Получаем ответ от AI - console.log(`Getting AI response for message ${msg.id} in ${language}`); - const aiResponse = await aiAssistant.getResponse(msg.content, language); - - // Сохраняем ответ AI в ту же беседу - await db.query( - `INSERT INTO messages - (conversation_id, sender_type, content, channel, created_at) - VALUES ($1, 'assistant', $2, 'chat', NOW())`, - [msg.conversation_id, aiResponse] - ); - - console.log(`Saved AI response for message ${msg.id}`); + + if (guestMessagesResult.rows.length === 0) { + console.log('No guest messages found'); + return { success: true, message: 'No guest messages found' }; } - - console.log(`Successfully processed all guest messages for user ${userId}`); - return true; + + const guestMessages = guestMessagesResult.rows; + console.log(`Found ${guestMessages.length} guest messages`); + + // Создаем новый диалог для этих сообщений + const firstMessage = guestMessages[0]; + const title = firstMessage.content.length > 30 + ? `${firstMessage.content.substring(0, 30)}...` + : firstMessage.content; + + const newConversationResult = await db.query( + 'INSERT INTO conversations (user_id, title) VALUES ($1, $2) RETURNING *', + [userId, title] + ); + + const conversation = newConversationResult.rows[0]; + console.log('Created new conversation for guest messages:', conversation); + + // Обрабатываем каждое гостевое сообщение + for (const guestMessage of guestMessages) { + console.log(`Processing guest message ID ${guestMessage.id}: ${guestMessage.content}`); + + // Сохраняем сообщение пользователя + const userMessageResult = await db.query( + `INSERT INTO messages + (conversation_id, content, sender_type, role, guest_message_id, channel, created_at) + VALUES + ($1, $2, $3, $4, $5, $6, $7) + RETURNING *`, + [ + conversation.id, + guestMessage.content, + 'user', + 'user', + guestMessage.id, + 'web', + guestMessage.created_at + ] + ); + + console.log(`Saved user message with ID ${userMessageResult.rows[0].id}`); + + // Получаем ответ от ИИ + console.log('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); + + // Сохраняем ответ от ИИ + const aiMessageResult = await db.query( + `INSERT INTO messages + (conversation_id, content, sender_type, role, guest_message_id, channel, created_at) + VALUES + ($1, $2, $3, $4, $5, $6, $7) + RETURNING *`, + [ + conversation.id, + aiResponse, + 'assistant', + 'assistant', + guestMessage.id, + 'web', + new Date() + ] + ); + + console.log(`Saved AI response with ID ${aiMessageResult.rows[0].id}`); + } + + // Удаляем гостевые сообщения, так как они уже обработаны + await db.query('DELETE FROM guest_messages WHERE guest_id = $1', [guestId]); + console.log('Deleted processed guest messages'); + + return { + success: true, + message: `Processed ${guestMessages.length} guest messages`, + conversationId: conversation.id + }; } catch (error) { console.error('Error processing guest messages:', error); - return false; + throw error; } } @@ -157,55 +197,82 @@ router.post('/guest-message', async (req, res) => { // Маршрут для обычных сообщений (для аутентифицированных пользователей) router.post('/message', requireAuth, async (req, res) => { + const { message, conversationId, language = 'auto' } = req.body; + + if (!message) { + return res.status(400).json({ error: 'Message is required' }); + } + try { - const { message, language } = req.body; + console.log('Processing message:', { message, conversationId, language, userId: req.session.userId }); const userId = req.session.userId; - - // Используем методы из aiAssistant вместо прямого обращения к vectorStore - const similarDocs = await aiAssistant.findSimilarDocuments(message); - const aiResponse = await aiAssistant.getResponse(message, language); - - // Создаем новую беседу или получаем существующую - const conversationResult = await db.query( - `INSERT INTO conversations (user_id, created_at) - VALUES ($1, NOW()) - RETURNING id`, - [userId] + + let conversation; + + // Если указан ID диалога, проверяем его существование и принадлежность пользователю + if (conversationId) { + const conversationResult = 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' }); + } + + conversation = conversationResult.rows[0]; + console.log('Using existing conversation:', conversation); + } else { + // Создаем новый диалог + const title = message.length > 30 ? `${message.substring(0, 30)}...` : message; + + const newConversationResult = 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); + } + + // Сохраняем сообщение пользователя + 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) + RETURNING *`, + [conversation.id, message, 'user', 'user', 0, 'web', new Date()] ); - const conversationId = conversationResult.rows[0].id; - - // Сохраняем сообщение пользователя - await db.query( + // Получаем ответ от ИИ + console.log('Getting AI response'); + const aiResponse = await aiAssistant.getResponse(message, language); + console.log('AI response received:', aiResponse); + + // Сохраняем ответ от ИИ + console.log('Saving AI response'); + const aiMessageResult = await db.query( `INSERT INTO messages - (conversation_id, sender_type, content, channel, metadata, created_at) - VALUES ($1, 'user', $2, 'chat', $3, NOW())`, - [ - conversationId, - message, - JSON.stringify({ language: language || 'ru' }) - ] + (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()] ); - - // Сохраняем ответ AI - await db.query( - `INSERT INTO messages - (conversation_id, sender_type, content, channel, metadata, created_at) - VALUES ($1, 'assistant', $2, 'chat', $3, NOW())`, - [ - conversationId, - aiResponse, - JSON.stringify({ language: language || 'ru' }) - ] - ); - - res.json({ + + const response = { success: true, - message: aiResponse - }); + userMessage: userMessageResult.rows[0], + aiMessage: aiMessageResult.rows[0], + conversation + }; + + res.json(response); } catch (error) { - logger.error('Error processing message:', error); - res.status(500).json({ error: 'Internal server error' }); + console.error('Error processing message:', error); + res.status(500).json({ error: 'Error processing message' }); } }); @@ -232,35 +299,79 @@ router.get('/history', async (req, res) => { id: req.sessionID, userId: req.session.userId, address: req.session.address, - authenticated: req.session.authenticated + authenticated: req.session.authenticated, + guestId: req.session.guestId }); - if (!req.session.authenticated || !req.session.userId) { - return res.status(401).json({ error: 'Unauthorized' }); - } - const limit = parseInt(req.query.limit) || 50; const offset = parseInt(req.query.offset) || 0; - // Получаем сообщения с пагинацией - const result = await db.query( - `SELECT - m.id, - m.content, - m.sender_type as role, - m.created_at, - c.user_id - FROM messages m - JOIN conversations c ON m.conversation_id = c.id - WHERE c.user_id = $1 - ORDER BY m.created_at DESC - LIMIT $2 OFFSET $3`, - [req.session.userId, limit, offset] - ); + let messages = []; + let total = 0; + + // Если пользователь аутентифицирован, получаем его сообщения + 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`); + } + // Если есть guestId, получаем гостевые сообщения + else if (req.session.guestId) { + const countResult = await db.query( + `SELECT COUNT(*) as total FROM guest_messages + WHERE guest_id = $1`, + [req.session.guestId] + ); + total = parseInt(countResult.rows[0].total) || 0; + + const result = await db.query( + `SELECT + id, + content, + 'user' as sender_type, + 'user' as role, + created_at, + guest_id as user_id, + NULL as conversation_id + FROM guest_messages + WHERE guest_id = $1 + ORDER BY created_at ASC + LIMIT $2 OFFSET $3`, + [req.session.guestId, limit, offset] + ); + + messages = result.rows; + console.log(`Found ${messages.length} guest messages`); + } return res.json({ success: true, - messages: result.rows.reverse() + messages: messages, + total: total }); } catch (error) { @@ -316,30 +427,49 @@ router.post('/link-guest-messages', requireAuth, async (req, res) => { } // Проверяем наличие гостевых сообщений - const guestMessages = await db.query( + const guestMessagesCheck = await db.query( 'SELECT EXISTS(SELECT 1 FROM guest_messages WHERE guest_id = $1)', [guestId] ); - console.log('Guest messages check:', guestMessages.rows[0]); + console.log('Guest messages check:', guestMessagesCheck.rows[0]); - if (!guestMessages.rows[0].exists) { + if (!guestMessagesCheck.rows[0].exists) { console.log('No guest messages found for guestId:', guestId); return res.json({ success: true, message: 'No guest messages to link' }); } - // Связываем сообщения - console.log('Calling link_guest_messages function'); - await db.query('SELECT link_guest_messages($1, $2)', [userId, guestId]); - - // Очищаем guestId из сессии после связывания - delete req.session.guestId; - - console.log('Messages linked successfully'); - res.json({ success: true }); + try { + // Обрабатываем гостевые сообщения для получения ответов от AI + console.log('Processing guest messages to get AI responses'); + const result = await processGuestMessages(userId, guestId); + console.log('Guest messages processed:', result); + + // Очищаем guestId из сессии после связывания + req.session.guestId = null; + await req.session.save(); + + console.log('Messages linked and processed successfully'); + return res.json({ + success: true, + message: 'Guest messages linked and processed', + result + }); + } catch (processError) { + console.error('Error processing guest messages:', processError); + return res.status(500).json({ + success: false, + error: 'Error processing guest messages', + details: processError.message + }); + } } catch (error) { console.error('Error linking guest messages:', error); - res.status(500).json({ success: false, error: 'Internal server error' }); + return res.status(500).json({ + success: false, + error: 'Internal server error', + details: error.message + }); } }); @@ -395,4 +525,28 @@ router.delete('/message/:id', requireAuth, async (req, res) => { } }); +// Маршрут для проверки и инициализации сессии +router.get('/check-session', async (req, res) => { + try { + // Если у пользователя нет guestId, создаем его + if (!req.session.guestId) { + req.session.guestId = crypto.randomBytes(16).toString('hex'); + await req.session.save(); + console.log('Created new guestId:', req.session.guestId); + } + + res.json({ + success: true, + guestId: req.session.guestId, + isAuthenticated: req.session.authenticated || false + }); + } catch (error) { + console.error('Error checking session:', error); + res.status(500).json({ + success: false, + error: 'Internal server error' + }); + } +}); + module.exports = router; diff --git a/backend/services/ai-assistant.js b/backend/services/ai-assistant.js index 96e9ec6..4a54ab1 100644 --- a/backend/services/ai-assistant.js +++ b/backend/services/ai-assistant.js @@ -2,6 +2,7 @@ const { ChatOllama } = require('@langchain/ollama'); const { HNSWLib } = require('@langchain/community/vectorstores/hnswlib'); const { OpenAIEmbeddings } = require('@langchain/openai'); const logger = require('../utils/logger'); +const fetch = require('node-fetch'); class AIAssistant { constructor() { @@ -18,7 +19,10 @@ class AIAssistant { return new ChatOllama({ baseUrl: this.baseUrl, model: this.defaultModel, - system: systemPrompt + system: systemPrompt, + temperature: 0.7, + maxTokens: 1000, + timeout: 30000 // 30 секунд таймаут }); } @@ -31,25 +35,38 @@ class AIAssistant { // Основной метод для получения ответа async getResponse(message, language = 'auto') { try { + console.log('getResponse called with:', { message, language }); + // Определяем язык, если не указан явно const detectedLanguage = language === 'auto' ? this.detectLanguage(message) : language; - const chat = this.createChat(detectedLanguage); + console.log('Detected language:', detectedLanguage); + // Сначала пробуем прямой API запрос try { - // Пробуем получить ответ через ChatOllama + console.log('Trying direct API request...'); + const response = await this.fallbackRequest(message, detectedLanguage); + console.log('Direct API response received:', response); + return response; + } catch (error) { + console.error('Error in direct API request:', error); + } + + // Если прямой запрос не удался, пробуем через ChatOllama + const chat = this.createChat(detectedLanguage); + try { + console.log('Sending request to ChatOllama...'); const response = await chat.invoke(message); + console.log('ChatOllama response:', response); return response.content; } catch (error) { - logger.error('Error using ChatOllama:', error); - - // Пробуем альтернативный метод через прямой API - return await this.fallbackRequest(message, detectedLanguage); + console.error('Error using ChatOllama:', error); + throw error; } } catch (error) { - logger.error('Error in getResponse:', error); + console.error('Error in getResponse:', error); return "Извините, я не смог обработать ваш запрос. Пожалуйста, попробуйте позже."; } } @@ -57,12 +74,13 @@ class AIAssistant { // Альтернативный метод запроса через прямой API async fallbackRequest(message, language) { try { - logger.info('Using fallback request method'); + console.log('Using fallback request method with:', { message, language }); const systemPrompt = language === 'ru' ? 'Вы - полезный ассистент. Отвечайте на русском языке.' : 'You are a helpful assistant. Respond in English.'; + console.log('Sending request to Ollama API...'); const response = await fetch(`${this.baseUrl}/api/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, @@ -70,14 +88,23 @@ class AIAssistant { model: this.defaultModel, prompt: message, system: systemPrompt, - stream: false + stream: false, + options: { + temperature: 0.7, + num_predict: 1000 + } }), }); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + console.log('Ollama API response:', data); return data.response; } catch (error) { - logger.error('Error in fallback request:', error); + console.error('Error in fallback request:', error); throw error; } } diff --git a/frontend/src/assets/styles/home.css b/frontend/src/assets/styles/home.css index 8e4ab42..6e3a20d 100644 --- a/frontend/src/assets/styles/home.css +++ b/frontend/src/assets/styles/home.css @@ -160,11 +160,10 @@ input, textarea { flex: 1; display: flex; flex-direction: column; - margin-left: 110px; + margin-left: 190px; /* 40px + 110px (sidebar) + 40px (button) */ + margin-right: 190px; /* 40px + 110px (sidebar) + 40px (button) */ transition: margin 0.3s ease; max-width: 1200px; - margin: 0 auto; - margin-left: 110px; padding: 0 20px; height: 100vh; position: relative; @@ -172,16 +171,16 @@ input, textarea { } .sidebar-expanded ~ .main-content { - margin-left: 325px; + margin-left: 190px; /* 40px + 110px (sidebar) + 40px (button) */ } /* Стили для адаптации основного содержимого при скрытии правой панели */ .main-content.no-right-sidebar { - margin-right: 0; + margin-right: 190px; /* 40px + 110px (sidebar) + 40px (button) */ } .main-content:not(.no-right-sidebar) { - margin-right: 300px; + margin-right: 190px; /* 40px + 110px (sidebar) + 40px (button) */ } /* Стили для контейнера чата */ @@ -960,7 +959,7 @@ input, textarea { .header { padding: 20px 0; - margin-bottom: 40px; /* Увеличиваем отступ после заголовка */ + margin-bottom: 24px; /* Уменьшенный отступ после заголовка */ width: 100%; } @@ -994,7 +993,8 @@ input, textarea { /* Стили для основного контента */ .content-container { padding: 20px 15px; - margin-right: 300px; /* Учитываем ширину правой панели */ + margin-right: 40px; /* Одинаковый отступ справа */ + margin-left: 40px; /* Одинаковый отступ слева */ } .footer { diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index c85e334..a11aaa0 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -49,7 +49,7 @@
+ :class="['message', message.sender_type === 'assistant' || message.role === 'assistant' ? 'ai-message' : 'user-message']">
{{ formatTime(message.timestamp || message.created_at) }}
@@ -213,6 +213,7 @@ const auth = useAuth(); const isAuthenticated = computed(() => auth.isAuthenticated.value); const isConnecting = ref(false); const messages = ref([]); +const hasUserSentMessage = ref(localStorage.getItem('hasUserSentMessage') === 'true'); const newMessage = ref(''); const isLoading = ref(false); const messagesContainer = ref(null); @@ -432,15 +433,18 @@ const handleMessage = async (text) => { if (!isAuthenticated.value) { // Сохраняем в таблицу guest_messages + console.log('Sending guest message:', messageContent); const response = await api.post('/api/chat/guest-message', { message: messageContent, language: userLanguage.value }); if (response.data.success) { + console.log('Guest message sent:', response.data); const userMessage = { id: response.data.messageId, content: messageContent, + sender_type: 'user', role: 'user', timestamp: new Date().toISOString() }; @@ -450,57 +454,65 @@ const handleMessage = async (text) => { messages.value.push({ id: Date.now() + 1, content: 'Для получения ответа от ассистента, пожалуйста, авторизуйтесь одним из способов в правой панели.', + sender_type: 'assistant', role: 'assistant', timestamp: new Date().toISOString() }); - - // Устанавливаем флаг отправки сообщения - if (!hasUserSentMessage.value) { - hasUserSentMessage.value = true; - localStorage.setItem('hasUserSentMessage', 'true'); - } + + // Прокручиваем к последнему сообщению + await nextTick(); + scrollToBottom(); } else { throw new Error(response.data.error || 'Ошибка при отправке сообщения'); } } else { - // Для авторизованного пользователя сохраняем в messages + // Отправляем сообщение аутентифицированного пользователя + console.log('Sending authenticated message:', messageContent); const response = await api.post('/api/chat/message', { message: messageContent, language: userLanguage.value }); if (response.data.success) { - const message = { - id: response.data.messageId, - content: messageContent, + console.log('Authenticated message sent:', response.data); + // Добавляем сообщение пользователя + messages.value.push({ + id: response.data.userMessage.id, + content: response.data.userMessage.content, + sender_type: 'user', role: 'user', - timestamp: new Date().toISOString(), - hasResponse: true - }; - messages.value.push(message); + timestamp: response.data.userMessage.created_at + }); - const aiMessage = { - id: response.data.aiMessageId, - content: response.data.message, + // Добавляем ответ ассистента + messages.value.push({ + id: response.data.aiMessage.id, + content: response.data.aiMessage.content, + sender_type: 'assistant', role: 'assistant', - timestamp: new Date().toISOString() - }; - messages.value.push(aiMessage); + timestamp: response.data.aiMessage.created_at + }); + + // Прокручиваем к последнему сообщению + await nextTick(); + scrollToBottom(); } else { throw new Error(response.data.error || 'Ошибка при отправке сообщения'); } } - - await nextTick(); - scrollToBottom(); } catch (error) { console.error('Error sending message:', error); messages.value.push({ - id: Date.now(), - content: error.message || 'Произошла ошибка при отправке сообщения. Пожалуйста, попробуйте еще раз.', + id: Date.now() + 1, + content: 'Произошла ошибка при отправке сообщения. Пожалуйста, попробуйте позже.', + sender_type: 'assistant', role: 'assistant', timestamp: new Date().toISOString() }); + + // Прокручиваем к последнему сообщению + await nextTick(); + scrollToBottom(); } finally { isLoading.value = false; } @@ -631,10 +643,12 @@ const scrollToBottom = () => { // Загрузка сообщений const loadMoreMessages = async () => { - if (!isAuthenticated.value) return; - try { isLoadingMore.value = true; + console.log('Fetching chat history...'); + + // Всегда запрашиваем историю, так как на сервере проверяется наличие + // userId или guestId в сессии и возвращаются соответствующие сообщения const response = await api.get('/api/chat/history', { params: { limit: limit.value, @@ -642,18 +656,39 @@ const loadMoreMessages = async () => { } }); + console.log('Chat history response:', response.data); + if (response.data.success) { - const newMessages = response.data.messages.map(msg => ({ - id: msg.id, - content: msg.content, - role: msg.role || (msg.sender_type === 'assistant' ? 'assistant' : 'user'), - timestamp: msg.created_at, - showAuthOptions: false - })); + const newMessages = response.data.messages.map(msg => { + console.log('Processing message:', msg); + return { + id: msg.id, + content: msg.content, + sender_type: msg.sender_type || (msg.role === 'assistant' ? 'assistant' : 'user'), + role: msg.role || (msg.sender_type === 'assistant' ? 'assistant' : 'user'), + timestamp: msg.created_at, + showAuthOptions: false + }; + }); - messages.value = [...messages.value, ...newMessages]; + console.log('Processed messages:', newMessages); + + // Объединяем сообщения и сортируем их по timestamp + const allMessages = [...messages.value, ...newMessages]; + allMessages.sort((a, b) => { + const timeA = new Date(a.timestamp || a.created_at).getTime(); + const timeB = new Date(b.timestamp || b.created_at).getTime(); + return timeA - timeB; + }); + + messages.value = allMessages; + console.log('Updated messages array:', messages.value); hasMoreMessages.value = response.data.total > messages.value.length; offset.value += newMessages.length; + + // Прокручиваем к последнему сообщению + await nextTick(); + scrollToBottom(); } } catch (error) { console.error('Error loading chat history:', error); @@ -663,23 +698,18 @@ const loadMoreMessages = async () => { }; // Загружаем сообщения при изменении аутентификации -watch(() => isAuthenticated.value, async (newValue) => { - if (newValue) { - messages.value = []; - offset.value = 0; - hasMoreMessages.value = true; - +watch(() => isAuthenticated.value, async (newValue, oldValue) => { + // Если пользователь только что авторизовался + if (newValue && !oldValue) { try { - // Сначала загружаем историю из messages - await loadMoreMessages(); - // Связываем гостевые сообщения (копируем из guest_messages в messages) await api.post('/api/chat/link-guest-messages'); console.log('Guest messages linked to authenticated user'); - // Перезагружаем сообщения, чтобы получить все, включая перенесенные + // Перезагружаем все сообщения messages.value = []; offset.value = 0; + hasMoreMessages.value = true; await loadMoreMessages(); await nextTick(); @@ -687,10 +717,12 @@ watch(() => isAuthenticated.value, async (newValue) => { } catch (linkError) { console.error('Error linking guest messages:', linkError); } - } else { + } else if (!newValue && oldValue) { + // Если пользователь вышел из системы, загружаем только гостевые сообщения messages.value = []; offset.value = 0; hasMoreMessages.value = true; + await loadMoreMessages(); // Загрузит гостевые сообщения, если они есть } }); @@ -751,8 +783,14 @@ const disconnectWallet = async () => { auth.telegramId = null; auth.email = null; - // Перезагружаем страницу для сброса состояния - window.location.reload(); + // Загружаем только гостевые сообщения после выхода + messages.value = []; + offset.value = 0; + hasMoreMessages.value = true; + await loadMoreMessages(); + + // НЕ перезагружаем страницу, чтобы не потерять историю сообщений + // window.location.reload(); } catch (error) { console.error('Error disconnecting wallet:', error); } @@ -792,13 +830,32 @@ const formatMessage = (text) => { return DOMPurify.sanitize(rawHtml); }; -// Инициализация состояния правой панели при загрузке -onMounted(() => { +// Функция для проверки наличия гостевых сообщений +const checkGuestMessages = async () => { + try { + const response = await api.get('/api/chat/check-session'); + console.log('Session check response:', response.data); + + // После инициализации сессии загружаем сообщения + if (!isAuthenticated.value) { + // Если пользователь не авторизован, попробуем загрузить гостевые сообщения + await loadMoreMessages(); + } + + return response.data; + } catch (error) { + console.error('Error checking guest messages:', error); + return { success: false }; + } +}; + +// Инициализация состояния при загрузке +onMounted(async () => { // Загружаем состояние правой панели из localStorage const savedSidebarState = localStorage.getItem('showWalletSidebar'); if (savedSidebarState !== null) { showWalletSidebar.value = savedSidebarState === 'true'; - } else { + } else { // По умолчанию правая панель скрыта showWalletSidebar.value = false; } @@ -807,6 +864,7 @@ onMounted(() => { if (messagesContainer.value) { messagesContainer.value.addEventListener('scroll', handleScroll); } + console.log('Auth state on mount:', { isAuthenticated: auth.isAuthenticated.value, authType: auth.authType.value, @@ -814,9 +872,12 @@ onMounted(() => { }); // Проверяем статус авторизации - auth.checkAuth(); - - // Обновляем баланс при монтировании и изменении аутентификации + await auth.checkAuth(); + + // Проверяем наличие гостевых сообщений и инициализируем сессию + await checkGuestMessages(); + + // Обновляем баланс при монтировании если авторизован if (auth.isAuthenticated.value) { updateBalances(); }