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

This commit is contained in:
2025-03-28 21:31:46 +03:00
parent e1906835df
commit 37e32de390
4 changed files with 438 additions and 196 deletions

View File

@@ -69,59 +69,99 @@ async function getAIResponse(message, language = 'ru') {
// Функция для обработки гостевых сообщений после аутентификации // Функция для обработки гостевых сообщений после аутентификации
async function processGuestMessages(userId, guestId) { async function processGuestMessages(userId, guestId) {
try { 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( const guestMessagesResult = await db.query(
`SELECT m.id, m.content, m.conversation_id, m.metadata, m.created_at 'SELECT * FROM guest_messages WHERE guest_id = $1 ORDER BY created_at ASC',
FROM messages m
WHERE m.metadata->>'guest_id' = $1
ORDER BY m.created_at ASC`,
[guestId] [guestId]
); );
console.log(`Found ${guestMessages.rows.length} guest messages to process`); if (guestMessagesResult.rows.length === 0) {
console.log('No guest messages found');
// Обновляем user_id для всех бесед с гостевыми сообщениями return { success: true, message: 'No guest messages found' };
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}`);
} }
console.log(`Successfully processed all guest messages for user ${userId}`); const guestMessages = guestMessagesResult.rows;
return true; 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) { } catch (error) {
console.error('Error processing guest messages:', 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) => { 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 { try {
const { message, language } = req.body; console.log('Processing message:', { message, conversationId, language, userId: req.session.userId });
const userId = req.session.userId; const userId = req.session.userId;
// Используем методы из aiAssistant вместо прямого обращения к vectorStore let conversation;
const similarDocs = await aiAssistant.findSimilarDocuments(message);
const aiResponse = await aiAssistant.getResponse(message, language);
// Создаем новую беседу или получаем существующую // Если указан ID диалога, проверяем его существование и принадлежность пользователю
const conversationResult = await db.query( if (conversationId) {
`INSERT INTO conversations (user_id, created_at) const conversationResult = await db.query(
VALUES ($1, NOW()) 'SELECT * FROM conversations WHERE id = $1 AND user_id = $2',
RETURNING id`, [conversationId, userId]
[userId] );
);
const conversationId = conversationResult.rows[0].id; 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);
}
// Сохраняем сообщение пользователя // Сохраняем сообщение пользователя
await db.query( console.log('Saving user message');
const userMessageResult = await db.query(
`INSERT INTO messages `INSERT INTO messages
(conversation_id, sender_type, content, channel, metadata, created_at) (conversation_id, content, sender_type, role, tokens_used, channel, created_at)
VALUES ($1, 'user', $2, 'chat', $3, NOW())`, VALUES
[ ($1, $2, $3, $4, $5, $6, $7)
conversationId, RETURNING *`,
message, [conversation.id, message, 'user', 'user', 0, 'web', new Date()]
JSON.stringify({ language: language || 'ru' })
]
); );
// Сохраняем ответ AI // Получаем ответ от ИИ
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 `INSERT INTO messages
(conversation_id, sender_type, content, channel, metadata, created_at) (conversation_id, content, sender_type, role, tokens_used, channel, created_at)
VALUES ($1, 'assistant', $2, 'chat', $3, NOW())`, VALUES
[ ($1, $2, $3, $4, $5, $6, $7)
conversationId, RETURNING *`,
aiResponse, [conversation.id, aiResponse, 'assistant', 'assistant', 0, 'web', new Date()]
JSON.stringify({ language: language || 'ru' })
]
); );
res.json({ const response = {
success: true, success: true,
message: aiResponse userMessage: userMessageResult.rows[0],
}); aiMessage: aiMessageResult.rows[0],
conversation
};
res.json(response);
} catch (error) { } catch (error) {
logger.error('Error processing message:', error); console.error('Error processing message:', error);
res.status(500).json({ error: 'Internal server error' }); res.status(500).json({ error: 'Error processing message' });
} }
}); });
@@ -232,35 +299,79 @@ router.get('/history', async (req, res) => {
id: req.sessionID, id: req.sessionID,
userId: req.session.userId, userId: req.session.userId,
address: req.session.address, 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 limit = parseInt(req.query.limit) || 50;
const offset = parseInt(req.query.offset) || 0; const offset = parseInt(req.query.offset) || 0;
// Получаем сообщения с пагинацией let messages = [];
const result = await db.query( let total = 0;
`SELECT
m.id, // Если пользователь аутентифицирован, получаем его сообщения
m.content, if (req.session.authenticated && req.session.userId) {
m.sender_type as role, const countResult = await db.query(
m.created_at, `SELECT COUNT(*) as total FROM messages m
c.user_id JOIN conversations c ON m.conversation_id = c.id
FROM messages m WHERE c.user_id = $1`,
JOIN conversations c ON m.conversation_id = c.id [req.session.userId]
WHERE c.user_id = $1 );
ORDER BY m.created_at DESC total = parseInt(countResult.rows[0].total) || 0;
LIMIT $2 OFFSET $3`,
[req.session.userId, limit, offset] 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({ return res.json({
success: true, success: true,
messages: result.rows.reverse() messages: messages,
total: total
}); });
} catch (error) { } 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)', 'SELECT EXISTS(SELECT 1 FROM guest_messages WHERE guest_id = $1)',
[guestId] [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); console.log('No guest messages found for guestId:', guestId);
return res.json({ success: true, message: 'No guest messages to link' }); return res.json({ success: true, message: 'No guest messages to link' });
} }
// Связываем сообщения try {
console.log('Calling link_guest_messages function'); // Обрабатываем гостевые сообщения для получения ответов от AI
await db.query('SELECT link_guest_messages($1, $2)', [userId, guestId]); console.log('Processing guest messages to get AI responses');
const result = await processGuestMessages(userId, guestId);
console.log('Guest messages processed:', result);
// Очищаем guestId из сессии после связывания // Очищаем guestId из сессии после связывания
delete req.session.guestId; req.session.guestId = null;
await req.session.save();
console.log('Messages linked successfully'); console.log('Messages linked and processed successfully');
res.json({ success: true }); 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) { } catch (error) {
console.error('Error linking guest messages:', 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; module.exports = router;

View File

@@ -2,6 +2,7 @@ const { ChatOllama } = require('@langchain/ollama');
const { HNSWLib } = require('@langchain/community/vectorstores/hnswlib'); const { HNSWLib } = require('@langchain/community/vectorstores/hnswlib');
const { OpenAIEmbeddings } = require('@langchain/openai'); const { OpenAIEmbeddings } = require('@langchain/openai');
const logger = require('../utils/logger'); const logger = require('../utils/logger');
const fetch = require('node-fetch');
class AIAssistant { class AIAssistant {
constructor() { constructor() {
@@ -18,7 +19,10 @@ class AIAssistant {
return new ChatOllama({ return new ChatOllama({
baseUrl: this.baseUrl, baseUrl: this.baseUrl,
model: this.defaultModel, 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') { async getResponse(message, language = 'auto') {
try { try {
console.log('getResponse called with:', { message, language });
// Определяем язык, если не указан явно // Определяем язык, если не указан явно
const detectedLanguage = language === 'auto' const detectedLanguage = language === 'auto'
? this.detectLanguage(message) ? this.detectLanguage(message)
: language; : language;
const chat = this.createChat(detectedLanguage); console.log('Detected language:', detectedLanguage);
// Сначала пробуем прямой API запрос
try { 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); const response = await chat.invoke(message);
console.log('ChatOllama response:', response);
return response.content; return response.content;
} catch (error) { } catch (error) {
logger.error('Error using ChatOllama:', error); console.error('Error using ChatOllama:', error);
throw error;
// Пробуем альтернативный метод через прямой API
return await this.fallbackRequest(message, detectedLanguage);
} }
} catch (error) { } catch (error) {
logger.error('Error in getResponse:', error); console.error('Error in getResponse:', error);
return "Извините, я не смог обработать ваш запрос. Пожалуйста, попробуйте позже."; return "Извините, я не смог обработать ваш запрос. Пожалуйста, попробуйте позже.";
} }
} }
@@ -57,12 +74,13 @@ class AIAssistant {
// Альтернативный метод запроса через прямой API // Альтернативный метод запроса через прямой API
async fallbackRequest(message, language) { async fallbackRequest(message, language) {
try { try {
logger.info('Using fallback request method'); console.log('Using fallback request method with:', { message, language });
const systemPrompt = language === 'ru' const systemPrompt = language === 'ru'
? 'Вы - полезный ассистент. Отвечайте на русском языке.' ? 'Вы - полезный ассистент. Отвечайте на русском языке.'
: 'You are a helpful assistant. Respond in English.'; : 'You are a helpful assistant. Respond in English.';
console.log('Sending request to Ollama API...');
const response = await fetch(`${this.baseUrl}/api/generate`, { const response = await fetch(`${this.baseUrl}/api/generate`, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
@@ -70,14 +88,23 @@ class AIAssistant {
model: this.defaultModel, model: this.defaultModel,
prompt: message, prompt: message,
system: systemPrompt, 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(); const data = await response.json();
console.log('Ollama API response:', data);
return data.response; return data.response;
} catch (error) { } catch (error) {
logger.error('Error in fallback request:', error); console.error('Error in fallback request:', error);
throw error; throw error;
} }
} }

View File

@@ -160,11 +160,10 @@ input, textarea {
flex: 1; flex: 1;
display: flex; display: flex;
flex-direction: column; 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; transition: margin 0.3s ease;
max-width: 1200px; max-width: 1200px;
margin: 0 auto;
margin-left: 110px;
padding: 0 20px; padding: 0 20px;
height: 100vh; height: 100vh;
position: relative; position: relative;
@@ -172,16 +171,16 @@ input, textarea {
} }
.sidebar-expanded ~ .main-content { .sidebar-expanded ~ .main-content {
margin-left: 325px; margin-left: 190px; /* 40px + 110px (sidebar) + 40px (button) */
} }
/* Стили для адаптации основного содержимого при скрытии правой панели */ /* Стили для адаптации основного содержимого при скрытии правой панели */
.main-content.no-right-sidebar { .main-content.no-right-sidebar {
margin-right: 0; margin-right: 190px; /* 40px + 110px (sidebar) + 40px (button) */
} }
.main-content:not(.no-right-sidebar) { .main-content:not(.no-right-sidebar) {
margin-right: 300px; margin-right: 190px; /* 40px + 110px (sidebar) + 40px (button) */
} }
/* Стили для контейнера чата */ /* Стили для контейнера чата */
@@ -960,7 +959,7 @@ input, textarea {
.header { .header {
padding: 20px 0; padding: 20px 0;
margin-bottom: 40px; /* Увеличиваем отступ после заголовка */ margin-bottom: 24px; /* Уменьшенный отступ после заголовка */
width: 100%; width: 100%;
} }
@@ -994,7 +993,8 @@ input, textarea {
/* Стили для основного контента */ /* Стили для основного контента */
.content-container { .content-container {
padding: 20px 15px; padding: 20px 15px;
margin-right: 300px; /* Учитываем ширину правой панели */ margin-right: 40px; /* Одинаковый отступ справа */
margin-left: 40px; /* Одинаковый отступ слева */
} }
.footer { .footer {

View File

@@ -49,7 +49,7 @@
<div class="chat-container"> <div class="chat-container">
<div class="chat-messages" ref="messagesContainer"> <div class="chat-messages" ref="messagesContainer">
<div v-for="message in messages" :key="message.id" <div v-for="message in messages" :key="message.id"
:class="['message', message.role === 'assistant' ? 'ai-message' : 'user-message']"> :class="['message', message.sender_type === 'assistant' || message.role === 'assistant' ? 'ai-message' : 'user-message']">
<div class="message-content" v-html="formatMessage(message.content)"></div> <div class="message-content" v-html="formatMessage(message.content)"></div>
<div class="message-time">{{ formatTime(message.timestamp || message.created_at) }}</div> <div class="message-time">{{ formatTime(message.timestamp || message.created_at) }}</div>
</div> </div>
@@ -213,6 +213,7 @@ const auth = useAuth();
const isAuthenticated = computed(() => auth.isAuthenticated.value); const isAuthenticated = computed(() => auth.isAuthenticated.value);
const isConnecting = ref(false); const isConnecting = ref(false);
const messages = ref([]); const messages = ref([]);
const hasUserSentMessage = ref(localStorage.getItem('hasUserSentMessage') === 'true');
const newMessage = ref(''); const newMessage = ref('');
const isLoading = ref(false); const isLoading = ref(false);
const messagesContainer = ref(null); const messagesContainer = ref(null);
@@ -432,15 +433,18 @@ const handleMessage = async (text) => {
if (!isAuthenticated.value) { if (!isAuthenticated.value) {
// Сохраняем в таблицу guest_messages // Сохраняем в таблицу guest_messages
console.log('Sending guest message:', messageContent);
const response = await api.post('/api/chat/guest-message', { const response = await api.post('/api/chat/guest-message', {
message: messageContent, message: messageContent,
language: userLanguage.value language: userLanguage.value
}); });
if (response.data.success) { if (response.data.success) {
console.log('Guest message sent:', response.data);
const userMessage = { const userMessage = {
id: response.data.messageId, id: response.data.messageId,
content: messageContent, content: messageContent,
sender_type: 'user',
role: 'user', role: 'user',
timestamp: new Date().toISOString() timestamp: new Date().toISOString()
}; };
@@ -450,57 +454,65 @@ const handleMessage = async (text) => {
messages.value.push({ messages.value.push({
id: Date.now() + 1, id: Date.now() + 1,
content: 'Для получения ответа от ассистента, пожалуйста, авторизуйтесь одним из способов в правой панели.', content: 'Для получения ответа от ассистента, пожалуйста, авторизуйтесь одним из способов в правой панели.',
sender_type: 'assistant',
role: 'assistant', role: 'assistant',
timestamp: new Date().toISOString() timestamp: new Date().toISOString()
}); });
// Устанавливаем флаг отправки сообщения // Прокручиваем к последнему сообщению
if (!hasUserSentMessage.value) { await nextTick();
hasUserSentMessage.value = true; scrollToBottom();
localStorage.setItem('hasUserSentMessage', 'true');
}
} else { } else {
throw new Error(response.data.error || 'Ошибка при отправке сообщения'); throw new Error(response.data.error || 'Ошибка при отправке сообщения');
} }
} else { } else {
// Для авторизованного пользователя сохраняем в messages // Отправляем сообщение аутентифицированного пользователя
console.log('Sending authenticated message:', messageContent);
const response = await api.post('/api/chat/message', { const response = await api.post('/api/chat/message', {
message: messageContent, message: messageContent,
language: userLanguage.value language: userLanguage.value
}); });
if (response.data.success) { if (response.data.success) {
const message = { console.log('Authenticated message sent:', response.data);
id: response.data.messageId, // Добавляем сообщение пользователя
content: messageContent, messages.value.push({
id: response.data.userMessage.id,
content: response.data.userMessage.content,
sender_type: 'user',
role: 'user', role: 'user',
timestamp: new Date().toISOString(), timestamp: response.data.userMessage.created_at
hasResponse: true });
};
messages.value.push(message);
const aiMessage = { // Добавляем ответ ассистента
id: response.data.aiMessageId, messages.value.push({
content: response.data.message, id: response.data.aiMessage.id,
content: response.data.aiMessage.content,
sender_type: 'assistant',
role: 'assistant', role: 'assistant',
timestamp: new Date().toISOString() timestamp: response.data.aiMessage.created_at
}; });
messages.value.push(aiMessage);
// Прокручиваем к последнему сообщению
await nextTick();
scrollToBottom();
} else { } else {
throw new Error(response.data.error || 'Ошибка при отправке сообщения'); throw new Error(response.data.error || 'Ошибка при отправке сообщения');
} }
} }
await nextTick();
scrollToBottom();
} catch (error) { } catch (error) {
console.error('Error sending message:', error); console.error('Error sending message:', error);
messages.value.push({ messages.value.push({
id: Date.now(), id: Date.now() + 1,
content: error.message || 'Произошла ошибка при отправке сообщения. Пожалуйста, попробуйте еще раз.', content: 'Произошла ошибка при отправке сообщения. Пожалуйста, попробуйте позже.',
sender_type: 'assistant',
role: 'assistant', role: 'assistant',
timestamp: new Date().toISOString() timestamp: new Date().toISOString()
}); });
// Прокручиваем к последнему сообщению
await nextTick();
scrollToBottom();
} finally { } finally {
isLoading.value = false; isLoading.value = false;
} }
@@ -631,10 +643,12 @@ const scrollToBottom = () => {
// Загрузка сообщений // Загрузка сообщений
const loadMoreMessages = async () => { const loadMoreMessages = async () => {
if (!isAuthenticated.value) return;
try { try {
isLoadingMore.value = true; isLoadingMore.value = true;
console.log('Fetching chat history...');
// Всегда запрашиваем историю, так как на сервере проверяется наличие
// userId или guestId в сессии и возвращаются соответствующие сообщения
const response = await api.get('/api/chat/history', { const response = await api.get('/api/chat/history', {
params: { params: {
limit: limit.value, limit: limit.value,
@@ -642,18 +656,39 @@ const loadMoreMessages = async () => {
} }
}); });
if (response.data.success) { console.log('Chat history response:', response.data);
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
}));
messages.value = [...messages.value, ...newMessages]; if (response.data.success) {
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
};
});
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; hasMoreMessages.value = response.data.total > messages.value.length;
offset.value += newMessages.length; offset.value += newMessages.length;
// Прокручиваем к последнему сообщению
await nextTick();
scrollToBottom();
} }
} catch (error) { } catch (error) {
console.error('Error loading chat history:', error); console.error('Error loading chat history:', error);
@@ -663,23 +698,18 @@ const loadMoreMessages = async () => {
}; };
// Загружаем сообщения при изменении аутентификации // Загружаем сообщения при изменении аутентификации
watch(() => isAuthenticated.value, async (newValue) => { watch(() => isAuthenticated.value, async (newValue, oldValue) => {
if (newValue) { // Если пользователь только что авторизовался
messages.value = []; if (newValue && !oldValue) {
offset.value = 0;
hasMoreMessages.value = true;
try { try {
// Сначала загружаем историю из messages
await loadMoreMessages();
// Связываем гостевые сообщения (копируем из guest_messages в messages) // Связываем гостевые сообщения (копируем из guest_messages в messages)
await api.post('/api/chat/link-guest-messages'); await api.post('/api/chat/link-guest-messages');
console.log('Guest messages linked to authenticated user'); console.log('Guest messages linked to authenticated user');
// Перезагружаем сообщения, чтобы получить все, включая перенесенные // Перезагружаем все сообщения
messages.value = []; messages.value = [];
offset.value = 0; offset.value = 0;
hasMoreMessages.value = true;
await loadMoreMessages(); await loadMoreMessages();
await nextTick(); await nextTick();
@@ -687,10 +717,12 @@ watch(() => isAuthenticated.value, async (newValue) => {
} catch (linkError) { } catch (linkError) {
console.error('Error linking guest messages:', linkError); console.error('Error linking guest messages:', linkError);
} }
} else { } else if (!newValue && oldValue) {
// Если пользователь вышел из системы, загружаем только гостевые сообщения
messages.value = []; messages.value = [];
offset.value = 0; offset.value = 0;
hasMoreMessages.value = true; hasMoreMessages.value = true;
await loadMoreMessages(); // Загрузит гостевые сообщения, если они есть
} }
}); });
@@ -751,8 +783,14 @@ const disconnectWallet = async () => {
auth.telegramId = null; auth.telegramId = null;
auth.email = null; auth.email = null;
// Перезагружаем страницу для сброса состояния // Загружаем только гостевые сообщения после выхода
window.location.reload(); messages.value = [];
offset.value = 0;
hasMoreMessages.value = true;
await loadMoreMessages();
// НЕ перезагружаем страницу, чтобы не потерять историю сообщений
// window.location.reload();
} catch (error) { } catch (error) {
console.error('Error disconnecting wallet:', error); console.error('Error disconnecting wallet:', error);
} }
@@ -792,13 +830,32 @@ const formatMessage = (text) => {
return DOMPurify.sanitize(rawHtml); 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 // Загружаем состояние правой панели из localStorage
const savedSidebarState = localStorage.getItem('showWalletSidebar'); const savedSidebarState = localStorage.getItem('showWalletSidebar');
if (savedSidebarState !== null) { if (savedSidebarState !== null) {
showWalletSidebar.value = savedSidebarState === 'true'; showWalletSidebar.value = savedSidebarState === 'true';
} else { } else {
// По умолчанию правая панель скрыта // По умолчанию правая панель скрыта
showWalletSidebar.value = false; showWalletSidebar.value = false;
} }
@@ -807,6 +864,7 @@ onMounted(() => {
if (messagesContainer.value) { if (messagesContainer.value) {
messagesContainer.value.addEventListener('scroll', handleScroll); messagesContainer.value.addEventListener('scroll', handleScroll);
} }
console.log('Auth state on mount:', { console.log('Auth state on mount:', {
isAuthenticated: auth.isAuthenticated.value, isAuthenticated: auth.isAuthenticated.value,
authType: auth.authType.value, authType: auth.authType.value,
@@ -814,9 +872,12 @@ onMounted(() => {
}); });
// Проверяем статус авторизации // Проверяем статус авторизации
auth.checkAuth(); await auth.checkAuth();
// Обновляем баланс при монтировании и изменении аутентификации // Проверяем наличие гостевых сообщений и инициализируем сессию
await checkGuestMessages();
// Обновляем баланс при монтировании если авторизован
if (auth.isAuthenticated.value) { if (auth.isAuthenticated.value) {
updateBalances(); updateBalances();
} }