From 4edb33b27860c1832463f14e0d5ca586ff373942 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 14 Apr 2025 13:23:40 +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/auth.js | 146 +++++++++++++++++--------------- backend/routes/chat.js | 87 +++++++++++-------- frontend/src/services/wallet.js | 3 - frontend/src/views/HomeView.vue | 51 +++++++---- 4 files changed, 160 insertions(+), 127 deletions(-) diff --git a/backend/routes/auth.js b/backend/routes/auth.js index cdbe87f..62c4f1a 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -256,7 +256,7 @@ router.post('/telegram/verify', async (req, res) => { logger.error('[telegram/verify] Error regenerating session:', err); return res.status(500).json({ success: false, - error: 'Session regeneration failed' + error: 'Session error' }); } @@ -285,8 +285,8 @@ router.post('/telegram/verify', async (req, res) => { }); }); - // Связываем гостевые сообщения только если это новый пользователь - if (verificationResult.isNewUser && guestId) { + // Связываем гостевые сообщения только один раз - исправлено дублирование + if (guestId) { // Создаем объект сессии для совместимости с другими методами аутентификации const session = { guestId: guestId, @@ -298,12 +298,7 @@ router.post('/telegram/verify', async (req, res) => { } }; const linkResults = await linkGuestMessagesAfterAuth(session, verificationResult.userId); - logger.info(`[telegram/verify] Guest messages linking results for new user:`, linkResults); - } - // Если пользователь не новый, но есть гостевой ID, все равно связываем сообщения - else if (!verificationResult.isNewUser && guestId) { - const linkResults = await linkGuestMessagesAfterAuth(req.session, verificationResult.userId); - logger.info(`[telegram/verify] Guest messages linking results for existing user:`, linkResults); + logger.info(`[telegram/verify] Guest messages linking results:`, linkResults); } return res.json({ @@ -1168,88 +1163,94 @@ router.get('/email/auth-status/:token', async (req, res) => { // Маршрут для проверки кода email router.post('/email/verify-code', async (req, res) => { try { - const { code } = req.body; - const pendingEmail = req.session.pendingEmail; + const { email, code } = req.body; - logger.info(`[email/verify-code] Verifying code for email: ${pendingEmail}`); - logger.info(`[email/verify-code] Guest context: guestId=${req.session.guestId}, previousGuestId=${req.session.previousGuestId}`); - - if (!pendingEmail) { - logger.warn('[email/verify-code] No pending email found in session'); + if (!email || !code) { return res.status(400).json({ success: false, - error: 'Нет ожидающей верификации электронной почты' + error: 'Email и код подтверждения обязательны' }); } - if (!code) { - logger.warn('[email/verify-code] No verification code provided'); - return res.status(400).json({ - success: false, - error: 'Код подтверждения не указан' - }); + logger.info(`[email/verify-code] Verifying code for email: ${email}`); + + // Сохраняем гостевой ID до проверки + const guestId = req.session.guestId; + const previousGuestId = req.session.previousGuestId; + + logger.info(`[email/verify-code] Guest context: guestId=${guestId}, previousGuestId=${previousGuestId}`); + + // Проверяем существование пользователя с таким email + const userResult = await db.query( + `SELECT u.id FROM users u + JOIN user_identities ui ON u.id = ui.user_id + WHERE ui.provider = $1 AND ui.provider_id = $2`, + ['email', email.toLowerCase()] + ); + + let userId; + let isNewUser = false; + + if (userResult.rows.length > 0) { + // Пользователь уже существует + userId = userResult.rows[0].id; + logger.info(`[email/verify-code] Found existing user with ID ${userId}`); + } else if (req.session.tempUserId) { + // Используем временный ID пользователя + userId = req.session.tempUserId; + logger.info(`[email/verify-code] Using tempUserId ${userId}`); + } else { + // Создаем нового пользователя + const newUserResult = await db.query( + 'INSERT INTO users (created_at, role) VALUES (NOW(), $1) RETURNING id', + ['user'] + ); + userId = newUserResult.rows[0].id; + isNewUser = true; + logger.info(`[email/verify-code] Created new user with ID ${userId} for email ${email}`); } // Проверяем код верификации - const verificationResult = await verifyEmailCode(code, pendingEmail); + const verification = await verificationService.verifyCode( + code.toUpperCase(), + 'email', + email.toLowerCase() + ); - if (!verificationResult.success) { - logger.warn(`[email/verify-code] Invalid verification code for email ${pendingEmail}`); + if (!verification.success) { + logger.warn(`[email/verify-code] Invalid verification code for ${email}: ${verification.error}`); return res.status(400).json({ success: false, - error: verificationResult.error || 'Неверный код подтверждения' + error: verification.error }); } - // Используем существующего пользователя, если он уже аутентифицирован - let userId; - if (req.session.authenticated && req.session.userId) { - userId = req.session.userId; - logger.info(`[email/verify-code] Using existing authenticated user ID ${userId}`); - } else { - // Проверяем, существует ли пользователь с таким email - const existingUser = await db.query( - `SELECT u.id FROM users u - JOIN user_identities ui ON u.id = ui.user_id - WHERE ui.provider = 'email' AND ui.provider_id = $1`, - [pendingEmail] - ); - - if (existingUser.rows.length > 0) { - userId = existingUser.rows[0].id; - logger.info(`[email/verify-code] Found existing user with ID ${userId} for email ${pendingEmail}`); - } else { - // Создаем нового пользователя только если нет существующей аутентификации - const newUser = await db.query( - 'INSERT INTO users (created_at) VALUES (NOW()) RETURNING id' - ); - userId = newUser.rows[0].id; - logger.info(`[email/verify-code] Created new user with ID ${userId} for email ${pendingEmail}`); - } + logger.info(`[email/verify-code] Verification successful for email ${email}, user ${userId}`); + + // Сохраняем email как identity + await saveUserIdentity(userId, 'email', email.toLowerCase(), true); + logger.info(`[email/verify-code] Saved email identity ${email} for user ${userId}`); + + // Если есть гостевой ID, сохраняем его + if (guestId) { + await saveUserIdentity(userId, 'guest', guestId, true); + logger.info(`[email/verify-code] Saved guest ID ${guestId} for user ${userId}`); } - // Сохраняем email как идентификатор пользователя - await saveUserIdentity(userId, 'email', pendingEmail, true); - logger.info(`[email/verify-code] Saved email identity ${pendingEmail} for user ${userId}`); - - // Если есть гостевые ID, сохраняем их - if (req.session.guestId) { - await saveUserIdentity(userId, 'guest', req.session.guestId, true); - logger.info(`[email/verify-code] Saved guest ID ${req.session.guestId} for user ${userId}`); + if (previousGuestId && previousGuestId !== guestId) { + await saveUserIdentity(userId, 'guest', previousGuestId, true); + logger.info(`[email/verify-code] Saved previous guest ID ${previousGuestId} for user ${userId}`); } - if (req.session.previousGuestId && req.session.previousGuestId !== req.session.guestId) { - await saveUserIdentity(userId, 'guest', req.session.previousGuestId, true); - logger.info(`[email/verify-code] Saved previous guest ID ${req.session.previousGuestId} for user ${userId}`); - } - - // Обновляем сессию + // Устанавливаем данные сессии req.session.authenticated = true; req.session.userId = userId; - req.session.email = pendingEmail; req.session.authType = 'email'; - delete req.session.pendingEmail; + req.session.email = email.toLowerCase(); + + // Удаляем временный ID delete req.session.tempUserId; + delete req.session.pendingEmail; // Сохраняем сессию перед связыванием сообщений await new Promise((resolve, reject) => { @@ -1264,14 +1265,19 @@ router.post('/email/verify-code', async (req, res) => { }); }); + // Сначала сохраняем сессию с актуальным гостевым ID + if (guestId) { + req.session.guestId = guestId; + } + // Связываем гостевые сообщения с пользователем const linkResults = await linkGuestMessagesAfterAuth(req.session, userId); logger.info(`[email/verify-code] Guest messages linking results:`, linkResults); - return res.json({ + return res.json({ success: true, userId, - email: pendingEmail, + email: email.toLowerCase(), authenticated: true }); diff --git a/backend/routes/chat.js b/backend/routes/chat.js index 23515ff..a03416f 100644 --- a/backend/routes/chat.js +++ b/backend/routes/chat.js @@ -41,38 +41,16 @@ async function processGuestMessages(userId, guestId) { const conversation = newConversationResult.rows[0]; console.log('Created new conversation for guest messages:', conversation); + // Отслеживаем успешные сохранения сообщений + const savedMessageIds = []; + // Обрабатываем каждое гостевое сообщение 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, channel, created_at) - VALUES - ($1, $2, $3, $4, $5, $6) - RETURNING *`, - [ - conversation.id, - guestMessage.content, - 'user', - 'user', - 'web', - guestMessage.created_at - ] - ); - - console.log(`Saved user message with ID ${userMessageResult.rows[0].id}`); - - // Получаем ответ от ИИ только для сообщений пользователя (не AI) - if (!guestMessage.is_ai) { - 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( + try { + // Сохраняем сообщение пользователя + const userMessageResult = await db.query( `INSERT INTO messages (conversation_id, content, sender_type, role, channel, created_at) VALUES @@ -80,25 +58,60 @@ async function processGuestMessages(userId, guestId) { RETURNING *`, [ conversation.id, - aiResponse, - 'assistant', - 'assistant', + guestMessage.content, + 'user', + 'user', 'web', - new Date() + guestMessage.created_at ] ); - console.log(`Saved AI response with ID ${aiMessageResult.rows[0].id}`); + console.log(`Saved user message with ID ${userMessageResult.rows[0].id}`); + savedMessageIds.push(guestMessage.id); + + // Получаем ответ от ИИ только для сообщений пользователя (не AI) + if (!guestMessage.is_ai) { + 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, channel, created_at) + VALUES + ($1, $2, $3, $4, $5, $6) + RETURNING *`, + [ + conversation.id, + aiResponse, + 'assistant', + 'assistant', + 'web', + new Date() + ] + ); + + console.log(`Saved AI response with ID ${aiMessageResult.rows[0].id}`); + } + } catch (error) { + console.error(`Error processing guest message ${guestMessage.id}:`, error); + // Продолжаем с другими сообщениями в случае ошибки } } - // Удаляем обработанные гостевые сообщения - await db.query('DELETE FROM guest_messages WHERE guest_id = $1', [guestId]); - console.log(`Deleted processed guest messages for guest ID ${guestId}`); + // Удаляем только успешно обработанные гостевые сообщения + if (savedMessageIds.length > 0) { + await db.query('DELETE FROM guest_messages WHERE id = ANY($1)', [savedMessageIds]); + console.log(`Deleted ${savedMessageIds.length} processed guest messages for guest ID ${guestId}`); + } else { + console.log('No guest messages were successfully processed, skipping deletion'); + } return { success: true, - message: `Processed ${guestMessages.length} guest messages`, + message: `Processed ${savedMessageIds.length} of ${guestMessages.length} guest messages`, conversationId: conversation.id }; } catch (error) { diff --git a/frontend/src/services/wallet.js b/frontend/src/services/wallet.js index b1f24d2..2097423 100644 --- a/frontend/src/services/wallet.js +++ b/frontend/src/services/wallet.js @@ -77,9 +77,6 @@ export async function connectWithWallet() { localStorage.setItem('userId', verificationResponse.data.userId); localStorage.setItem('address', verificationResponse.data.address); localStorage.setItem('isAdmin', verificationResponse.data.isAdmin); - - // Перезагружаем страницу для обновления состояния - window.location.reload(); } return verificationResponse.data; diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index 0987424..90f047b 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -529,11 +529,8 @@ const formatIdentityProvider = (provider) => { // ===================================================================== /** - * Загружает историю сообщений - * @param {Object} options - Параметры загрузки - * @param {boolean} options.silent - Не показывать индикатор загрузки - * @param {boolean} options.initial - Первоначальная загрузка - * @param {string} options.authType - Тип аутентификации + * Загружает сообщения пользователя из истории + * @param {Object} options - Опции загрузки */ const loadMessages = async (options = {}) => { const { silent = false, initial = false, authType = null } = options; @@ -547,16 +544,32 @@ const loadMessages = async (options = {}) => { console.log(`Загрузка истории сообщений${authType ? ` после ${authType} аутентификации` : ''}...`); - // Если это загрузка после аутентификации, ждем завершения привязки гостевых сообщений - if (authType && messageLoading.value.isLinkingGuest) { - await new Promise(resolve => { - const checkInterval = setInterval(() => { - if (!messageLoading.value.isLinkingGuest) { + // Если это загрузка после аутентификации, немного ждем для завершения привязки сообщений + if (authType) { + console.log(`Ожидание завершения привязки гостевых сообщений после ${authType} аутентификации...`); + + // Задержка для гарантии, что сервер успеет обработать сессию + await new Promise(resolve => setTimeout(resolve, 1000)); + + // Дополнительно проверяем, что процесс связывания сообщений завершился + if (messageLoading.value.isLinkingGuest) { + await new Promise(resolve => { + const checkInterval = setInterval(() => { + if (!messageLoading.value.isLinkingGuest) { + clearInterval(checkInterval); + resolve(); + } + }, 100); + + // Таймаут на всякий случай + setTimeout(() => { clearInterval(checkInterval); resolve(); - } - }, 100); - }); + }, 5000); + }); + } + + console.log('Привязка сообщений завершена, загружаем историю...'); } // Проверяем сессию перед загрузкой @@ -568,10 +581,14 @@ const loadMessages = async (options = {}) => { messageLoading.value.isLoadingMore = false; messageLoading.value.isInProgress = false; isLoading.value = false; - return; - } - } catch (error) { - console.warn('Ошибка проверки сессии, продолжаем загрузку:', error); + return; + } + } catch (error) { + console.error('Ошибка при проверке сессии:', error); + messageLoading.value.isLoadingMore = false; + messageLoading.value.isInProgress = false; + isLoading.value = false; + return; } }