From 831059d5017afd90341148735765a6542b6c71d0 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 27 Mar 2025 17:22:45 +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/app.js | 2 + backend/routes/auth.js | 224 +++++++++++-------- backend/routes/tokens.js | 31 +++ backend/server.js | 2 +- backend/services/auth-service.js | 149 ++++++++++++- backend/services/emailAuth.js | 265 +++++++---------------- backend/services/emailBot.js | 169 ++++----------- backend/services/index.js | 14 +- backend/services/verification-service.js | 55 ++++- frontend/src/assets/styles/home.css | 75 +++++++ frontend/src/services/tokens.js | 41 ++++ frontend/src/views/HomeView.vue | 214 ++++++++++++++---- 12 files changed, 784 insertions(+), 457 deletions(-) create mode 100644 backend/routes/tokens.js create mode 100644 frontend/src/services/tokens.js diff --git a/backend/app.js b/backend/app.js index 2c7820f..9a4fb4e 100644 --- a/backend/app.js +++ b/backend/app.js @@ -17,6 +17,7 @@ const usersRoutes = require('./routes/users'); const identitiesRoutes = require('./routes/identities'); const chatRoutes = require('./routes/chat'); const adminRoutes = require('./routes/admin'); +const tokensRouter = require('./routes/tokens'); const app = express(); @@ -125,6 +126,7 @@ app.use('/api/users', usersRoutes); app.use('/api/identities', identitiesRoutes); app.use('/api/chat', chatRoutes); app.use('/api/admin', adminRoutes); +app.use('/api/tokens', tokensRouter); const nonceStore = new Map(); // или любая другая реализация хранилища nonce diff --git a/backend/routes/auth.js b/backend/routes/auth.js index 529d044..32952f2 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -9,13 +9,11 @@ const { checkRole, requireAuth } = require('../middleware/auth'); const { pool } = require('../db'); const authService = require('../services/auth-service'); const { SiweMessage } = require('siwe'); -const { EmailBotService } = require('../services/emailBot'); +const emailBot = require('../services/emailBot'); const { verificationCodes } = require('../services/telegramBot'); const { checkTokensAndUpdateRole } = require('../services/auth-service'); const { ethers } = require('ethers'); const { initTelegramAuth } = require('../services/telegramBot'); -const { initEmailAuth, verifyEmailCode } = require('../services/emailBot'); -const { getBot } = require('../services/telegramBot'); const emailAuth = require('../services/emailAuth'); const verificationService = require('../services/verification-service'); @@ -281,7 +279,7 @@ router.post('/email/request', authLimiter, async (req, res) => { }); // Отправляем email с кодом подтверждения - const emailBot = new EmailBotService(process.env.EMAIL_USER, process.env.EMAIL_PASSWORD); + const emailBot = new emailBot(process.env.EMAIL_USER, process.env.EMAIL_PASSWORD); const result = await emailBot.sendVerificationCode(email, req.session.tempUserId || req.session.userId); if (result.success) { @@ -349,40 +347,24 @@ router.post('/email/verify', async (req, res) => { // Добавляем email в базу данных await db.query( `INSERT INTO user_identities - (user_id, provider, provider_id, created_at) - VALUES ($1, $2, $3, NOW()) + (user_id, provider, provider_id) + VALUES ($1, $2, $3) ON CONFLICT (provider, provider_id) DO UPDATE SET user_id = $1`, [userId, 'email', email.toLowerCase()] ); - // Связываем гостевой ID с пользователем, если его еще нет - if (req.session.guestId) { - const guestIdentity = await db.query( - `SELECT * FROM user_identities - WHERE user_id = $1 AND provider = 'guest' AND provider_id = $2`, - [userId, req.session.guestId] - ); - - if (guestIdentity.rows.length === 0) { - await db.query( - `INSERT INTO user_identities - (user_id, provider, provider_id, created_at) - VALUES ($1, $2, $3, NOW()) - ON CONFLICT (provider, provider_id) DO UPDATE SET user_id = $1`, - [userId, 'guest', req.session.guestId] - ); - } - } + // Проверяем наличие кошелька и определяем роль + const wallet = await authService.getLinkedWallet(userId); + let role = 'user'; // Базовая роль для доступа к чату - // Связываем все гостевые сообщения с пользователем - if (req.session.guestId) { - try { - await db.query('SELECT link_guest_messages($1, $2)', [userId, req.session.guestId]); - logger.info(`Messages linked successfully for user ${userId} and guest ${req.session.guestId}`); - } catch (linkError) { - logger.error(`Error linking messages: ${linkError.message}`); - } + if (wallet) { + // Если есть кошелек, проверяем баланс токенов + const isAdmin = await authService.checkAdminRole(wallet); + role = isAdmin ? 'admin' : 'user'; + logger.info(`User ${userId} has wallet ${wallet}, role set to ${role}`); + } else { + logger.info(`User ${userId} has no wallet, using basic user role`); } // Устанавливаем аутентификацию пользователя @@ -390,6 +372,11 @@ router.post('/email/verify', async (req, res) => { req.session.userId = userId; req.session.email = email.toLowerCase(); req.session.authType = 'email'; + req.session.role = role; + + if (wallet) { + req.session.address = wallet; + } // Очищаем временные данные delete req.session.pendingEmail; @@ -402,7 +389,7 @@ router.post('/email/verify', async (req, res) => { logger.error('Error saving session:', err); reject(err); } else { - logger.info('Session saved successfully'); + logger.info(`Session saved successfully for user ${userId} with role ${role}`); resolve(); } }); @@ -412,6 +399,8 @@ router.post('/email/verify', async (req, res) => { success: true, userId, email: email.toLowerCase(), + role, + wallet: wallet || null, message: 'Аутентификация успешна' }); @@ -620,61 +609,72 @@ router.get('/telegram', (req, res) => { res.json({ authUrl }); }); -// Маршрут для верификации Telegram +// Обработка верификации Telegram router.post('/telegram/verify', async (req, res) => { try { - const { code } = req.body; + const { telegramId, verificationCode } = req.body; - // Проверяем код через сервис верификации - const result = await verificationService.verifyCode(code, 'telegram', req.session.guestId || 'temp'); - - if (!result.success) { - return res.status(400).json({ - success: false, - error: result.error || 'Неверный код подтверждения' + if (!telegramId || !verificationCode) { + return res.status(400).json({ + success: false, + error: 'Missing required fields' }); } + + // Проверяем верификацию через сервис + const result = await authService.verifyTelegramAuth(telegramId, verificationCode); - const userId = result.userId; - const telegramId = result.providerId; - - // Добавляем Telegram идентификатор в базу данных - await db.query( - 'INSERT INTO user_identities (user_id, identity_type, identity_value, verified, created_at) ' + - 'VALUES ($1, $2, $3, true, NOW()) ' + - 'ON CONFLICT (identity_type, identity_value) ' + - 'DO UPDATE SET user_id = $1, verified = true', - [userId, 'telegram', telegramId] - ); - - // Устанавливаем аутентификацию пользователя + if (!result.success) { + return res.status(400).json({ + success: false, + error: 'Invalid verification' + }); + } + + // Обновляем сессию + req.session.userId = result.userId; req.session.authenticated = true; - req.session.userId = userId; - req.session.telegramId = telegramId; req.session.authType = 'telegram'; + req.session.telegramId = result.telegramId; + req.session.role = result.role; - // Если был временный ID, удаляем его + if (result.wallet) { + req.session.address = result.wallet; + } + + // Удаляем временные данные if (req.session.tempUserId) { delete req.session.tempUserId; } - // Если есть подключенный кошелек, проверяем баланс токенов - if (req.session.address) { - const isAdmin = await checkTokenBalance(req.session.address); - req.session.isAdmin = isAdmin; - } + // Сохраняем сессию + await new Promise((resolve, reject) => { + req.session.save(err => { + if (err) { + logger.error('Error saving session:', err); + reject(err); + } else { + logger.info(`Session saved successfully for user ${result.userId}`); + resolve(); + } + }); + }); return res.json({ success: true, - userId, - telegramId, - isAdmin: req.session.isAdmin || false, - authenticated: true + userId: result.userId, + role: result.role, + telegramId: result.telegramId, + wallet: result.wallet, + message: 'Authentication successful' }); } catch (error) { - logger.error('Error in telegram verification:', error); - res.status(500).json({ error: 'Internal server error' }); + logger.error('Error in Telegram verification:', error); + res.status(500).json({ + success: false, + error: 'Internal server error' + }); } }); @@ -1212,24 +1212,65 @@ router.post('/email/init', async (req, res) => { try { const { email } = req.body; - if (!email) { - return res.status(400).json({ - success: false, - error: 'Email не указан' + if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) { + return res.status(400).json({ + success: false, + error: 'Некорректный формат email' }); } - - const result = await emailAuth.initEmailAuth(req.session, email); - - return res.json({ - success: true, - message: 'Код верификации отправлен на указанный email' + + // Создаем или получаем ID пользователя + let userId; + if (req.session.authenticated && req.session.userId) { + userId = req.session.userId; + } else { + const userResult = await db.query( + 'INSERT INTO users (role) VALUES ($1) RETURNING id', + ['user'] + ); + userId = userResult.rows[0].id; + req.session.tempUserId = userId; + } + + // Сохраняем email в сессии + req.session.pendingEmail = email.toLowerCase(); + + // Генерируем код верификации + const code = await verificationService.createVerificationCode('email', email.toLowerCase(), userId); + + // Инициализируем верификацию через email бот + const result = await emailBot.initEmailVerification(email, userId, code); + + if (!result.success) { + return res.status(500).json({ + success: false, + error: 'Ошибка при отправке кода верификации' + }); + } + + // Сохраняем сессию + await new Promise((resolve, reject) => { + req.session.save(err => { + if (err) { + logger.error('Error saving session:', err); + reject(err); + } else { + logger.info(`Session saved successfully with pendingEmail: ${email}`); + resolve(); + } + }); }); + + return res.json({ + success: true, + message: 'Код верификации отправлен на email' + }); + } catch (error) { - logger.error('Error initializing email auth:', error); - return res.status(500).json({ - success: false, - error: error.message || 'Ошибка при инициализации email аутентификации' + logger.error('Error in email auth initialization:', error); + res.status(500).json({ + success: false, + error: 'Внутренняя ошибка сервера' }); } }); @@ -1251,13 +1292,14 @@ router.post('/email/verify', requireAuth, async (req, res) => { }); // Проверка кода верификации email -router.get('/check-email-verification', async (req, res) => { +router.all('/check-email-verification', async (req, res) => { try { - const { code } = req.query; + // Получаем код из query параметров (GET) или тела запроса (POST) + const code = req.method === 'POST' ? req.body.code : req.query.code; if (!code) { return res.status(400).json({ - verified: false, + success: false, message: 'Код верификации не предоставлен' }); } @@ -1266,7 +1308,11 @@ router.get('/check-email-verification', async (req, res) => { const result = await emailAuth.checkEmailVerification(code, req.session); if (!result.verified) { - return res.json(result); + // Преобразуем ответ для совместимости с фронтендом + return res.json({ + success: false, + message: result.message + }); } // Код верный, обновляем сессию @@ -1300,14 +1346,14 @@ router.get('/check-email-verification', async (req, res) => { }); return res.json({ - verified: true, + success: true, userId: result.userId, email: result.email }); } catch (error) { logger.error('Error checking email verification:', error); return res.status(500).json({ - verified: false, + success: false, message: 'Ошибка при проверке кода верификации' }); } diff --git a/backend/routes/tokens.js b/backend/routes/tokens.js new file mode 100644 index 0000000..72b621b --- /dev/null +++ b/backend/routes/tokens.js @@ -0,0 +1,31 @@ +const express = require('express'); +const router = express.Router(); +const { requireAuth } = require('../middleware/auth'); +const authService = require('../services/auth-service'); +const logger = require('../utils/logger'); + +// Получение балансов токенов +router.get('/balances', requireAuth, async (req, res) => { + try { + const { address } = req.session; + + if (!address) { + return res.status(400).json({ + error: 'No wallet address in session' + }); + } + + logger.info(`Fetching token balances for address: ${address}`); + const balances = await authService.getTokenBalances(address); + + logger.info(`Token balances fetched for ${address}:`, balances); + res.json(balances); + } catch (error) { + logger.error('Error fetching token balances:', error); + res.status(500).json({ + error: 'Failed to fetch token balances' + }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/backend/server.js b/backend/server.js index 98344a4..52ef5a6 100644 --- a/backend/server.js +++ b/backend/server.js @@ -2,7 +2,7 @@ require('dotenv').config(); const express = require('express'); const cors = require('cors'); const { ethers } = require('ethers'); -const EmailBotService = require('./services/emailBot'); +const emailBot = require('./services/emailBot'); const session = require('express-session'); const { app, nonceStore } = require('./app'); const usersRouter = require('./routes/users'); diff --git a/backend/services/auth-service.js b/backend/services/auth-service.js index 8f900bc..029f6e1 100644 --- a/backend/services/auth-service.js +++ b/backend/services/auth-service.js @@ -145,6 +145,61 @@ class AuthService { return false; } + /** + * Получение балансов токенов для адреса + * @param {string} address - Адрес кошелька + * @returns {Promise} - Объект с балансами токенов + */ + async getTokenBalances(address) { + if (!address) { + logger.error('No address provided for getTokenBalances'); + return { + eth: '0', + bsc: '0', + arbitrum: '0', + polygon: '0' + }; + } + + const balances = {}; + + for (const contract of ADMIN_CONTRACTS) { + try { + const provider = this.providers[contract.network]; + if (!provider) { + logger.error(`No provider for network ${contract.network}`); + balances[contract.network] = '0'; + continue; + } + + const tokenContract = new ethers.Contract( + contract.address, + ERC20_ABI, + provider + ); + + const balance = await tokenContract.balanceOf(address); + const formattedBalance = ethers.formatUnits(balance, 18); + + logger.info(`Token balance for ${address} on ${contract.network}:`, { + contract: contract.address, + balance: formattedBalance + }); + + balances[contract.network] = formattedBalance; + } catch (error) { + logger.error(`Error getting balance for ${contract.network}:`, { + address, + contract: contract.address, + error: error.message + }); + balances[contract.network] = '0'; + } + } + + return balances; + } + // Создание сессии с проверкой роли async createSession(session, { userId, authenticated, authType, guestId, address }) { try { @@ -209,12 +264,18 @@ class AuthService { try { // Проверяем наличие связанного кошелька const wallet = await this.getLinkedWallet(userId); - if (wallet) { - // Если есть кошелек, проверяем админские токены - const isAdmin = await this.checkAdminRole(wallet); - return isAdmin ? 'admin' : 'user'; + + // Если кошелек не привязан, пользователь получает роль user + // с базовым доступом к чату и истории сообщений + if (!wallet) { + logger.info(`No wallet linked for user ${userId}, assigning basic user role`); + return 'user'; } - return 'user'; + + // Если есть кошелек, проверяем админские токены + const isAdmin = await this.checkAdminRole(wallet); + logger.info(`Role check for user ${userId} with wallet ${wallet}: ${isAdmin ? 'admin' : 'user'}`); + return isAdmin ? 'admin' : 'user'; } catch (error) { logger.error('Error checking user role:', error); return 'user'; @@ -243,17 +304,93 @@ class AuthService { if (userResult.rows.length === 0) { return { verified: false }; } + + // Проверяем наличие кошелька и определяем роль + const wallet = await this.getLinkedWallet(userId); + let role = 'user'; // Базовая роль для доступа к чату + + if (wallet) { + // Если есть кошелек, проверяем баланс токенов + const isAdmin = await this.checkAdminRole(wallet); + role = isAdmin ? 'admin' : 'user'; + logger.info(`User ${userId} has wallet ${wallet}, role set to ${role}`); + } else { + logger.info(`User ${userId} has no wallet, using basic user role`); + } return { verified: true, userId, - email + email, + role, + wallet: wallet || null }; } catch (error) { logger.error('Error checking email verification:', error); return { verified: false }; } } + + /** + * Проверка Telegram аутентификации + */ + async verifyTelegramAuth(telegramId, verificationCode) { + try { + logger.info(`Verifying Telegram auth for ID: ${telegramId} with code: ${verificationCode}`); + + // Находим или создаем пользователя + const userResult = await db.query( + `SELECT u.* FROM users u + JOIN user_identities ui ON u.id = ui.user_id + WHERE ui.provider = 'telegram' AND ui.provider_id = $1`, + [telegramId] + ); + + let userId; + if (userResult.rows.length > 0) { + userId = userResult.rows[0].id; + logger.info(`Found existing user ${userId} for Telegram ID ${telegramId}`); + } else { + // Создаем нового пользователя с ролью user + const newUserResult = await db.query( + 'INSERT INTO users (role) VALUES ($1) RETURNING id', + ['user'] + ); + userId = newUserResult.rows[0].id; + + // Добавляем Telegram идентификатор + await db.query( + 'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)', + [userId, 'telegram', telegramId] + ); + logger.info(`Created new user ${userId} for Telegram ID ${telegramId}`); + } + + // Проверяем наличие кошелька и определяем роль + const wallet = await this.getLinkedWallet(userId); + let role = 'user'; // Базовая роль для доступа к чату + + if (wallet) { + // Если есть кошелек, проверяем баланс токенов + const isAdmin = await this.checkAdminRole(wallet); + role = isAdmin ? 'admin' : 'user'; + logger.info(`User ${userId} has wallet ${wallet}, role set to ${role}`); + } else { + logger.info(`User ${userId} has no wallet, using basic user role`); + } + + return { + success: true, + userId, + role, + telegramId, + wallet: wallet || null + }; + } catch (error) { + logger.error('Error in Telegram auth verification:', error); + throw error; + } + } } // Создаем и экспортируем единственный экземпляр diff --git a/backend/services/emailAuth.js b/backend/services/emailAuth.js index db529b5..2baa407 100644 --- a/backend/services/emailAuth.js +++ b/backend/services/emailAuth.js @@ -1,209 +1,106 @@ +const { pool } = require('../db'); +const verificationService = require('./verification-service'); const logger = require('../utils/logger'); +const emailBot = require('./emailBot'); const db = require('../db'); const authService = require('./auth-service'); -const verificationService = require('./verification-service'); -const { EmailBotService } = require('./emailBot'); -// Инициализация процесса аутентификации по email -async function initEmailAuth(session, email) { - try { - if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) { - throw new Error('Некорректный формат email'); - } - - // Сохраняем email в сессии для последующей верификации - session.pendingEmail = email.toLowerCase(); - - // Создаем или получаем ID пользователя - let userId; - - // Проверяем, существует ли пользователь с этим email - const existingEmailUser = await db.query( - `SELECT ui.user_id - FROM user_identities ui - WHERE ui.provider = 'email' AND ui.provider_id = $1`, - [email.toLowerCase()] - ); - - if (existingEmailUser.rows.length > 0) { - // Используем существующего пользователя - userId = existingEmailUser.rows[0].user_id; - logger.info(`Using existing user ${userId} for email ${email}`); - - // Связываем гостевой ID с существующим пользователем, если еще нет - if (session.guestId) { - const guestIdentity = await db.query( - `SELECT * FROM user_identities - WHERE user_id = $1 AND provider = 'guest' AND provider_id = $2`, - [userId, session.guestId] - ); - - if (guestIdentity.rows.length === 0) { - await db.query( - `INSERT INTO user_identities - (user_id, provider, provider_id, created_at) - VALUES ($1, $2, $3, NOW()) - ON CONFLICT (provider, provider_id) DO UPDATE SET user_id = $1`, - [userId, 'guest', session.guestId] - ); - } +class EmailAuth { + constructor() { + this.emailBot = emailBot; + } + + async initEmailAuth(session, email) { + try { + if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) { + throw new Error('Некорректный формат email'); } - } else if (session.authenticated && session.userId) { - // Если пользователь уже аутентифицирован, используем его ID - userId = session.userId; - } else if (session.guestId) { - // Проверяем, есть ли пользователь с текущим guestId - const guestUserResult = await db.query( - `SELECT u.id - FROM users u - JOIN user_identities ui ON u.id = ui.user_id - WHERE ui.provider = 'guest' AND ui.provider_id = $1`, - [session.guestId] - ); - if (guestUserResult.rows.length > 0) { - // Используем существующего пользователя с guestId - userId = guestUserResult.rows[0].id; + // Создаем или получаем ID пользователя + let userId; + + if (session.authenticated && session.userId) { + userId = session.userId; } else { - // Создаем нового пользователя const userResult = await db.query( - 'INSERT INTO users (created_at) VALUES (NOW()) RETURNING id' + 'INSERT INTO users (role) VALUES ($1) RETURNING id', + ['user'] ); userId = userResult.rows[0].id; - - // Связываем гостевой ID с новым пользователем - if (session.guestId) { - await db.query( - `INSERT INTO user_identities - (user_id, provider, provider_id, created_at) - VALUES ($1, $2, $3, NOW())`, - [userId, 'guest', session.guestId] - ); - } + session.tempUserId = userId; } - } else { - // Создаем нового пользователя без гостевого ID - const userResult = await db.query( - 'INSERT INTO users (created_at) VALUES (NOW()) RETURNING id' + + // Сохраняем email в сессии + session.pendingEmail = email.toLowerCase(); + + // Создаем код через сервис верификации + const verificationCode = await verificationService.createVerificationCode( + 'email', + email.toLowerCase(), + userId ); - userId = userResult.rows[0].id; + + // Отправляем код на email + await this.emailBot.sendVerificationCode(email, verificationCode); + + logger.info(`Generated verification code for Email auth for ${email} and sent to user's email`); + + return { success: true, verificationCode }; + } catch (error) { + logger.error('Error in email auth initialization:', error); + throw error; } - - session.tempUserId = userId; - - // Создаем код через сервис верификации - const code = await verificationService.createVerificationCode( - 'email', - email.toLowerCase(), - userId - ); - - // Создаем экземпляр EmailBotService для отправки кода - const emailService = new EmailBotService( - process.env.EMAIL_USER, - process.env.EMAIL_PASSWORD - ); - - // Отправляем код на email пользователя - await emailService.sendVerificationCode(email.toLowerCase(), userId); - - logger.info(`Generated verification code for Email auth for ${email} and sent to user's email`); - return { success: true }; - } catch (error) { - logger.error('Error initializing email auth:', error); - throw error; } -} -// Проверка кода верификации -async function checkEmailVerification(code, session) { - try { - if (!session?.pendingEmail) { - return { verified: false, message: "Email не найден в сессии" }; - } + async checkEmailVerification(code, session) { + try { + if (!code) { + return { verified: false, message: 'Код верификации не предоставлен' }; + } - // Проверяем код через сервис верификации - const result = await verificationService.verifyCode(code, 'email', session.pendingEmail); - - if (!result.success) { - return { verified: false, message: result.error || "Неверный код верификации" }; - } - - const userId = result.userId; - - // Проверяем, существует ли пользователь - const userResult = await db.query( - 'SELECT * FROM users WHERE id = $1', - [userId] - ); - - if (userResult.rows.length === 0) { - return { verified: false, message: "Пользователь не найден" }; - } - - // Проверяем, есть ли у пользователя связанный email - const emailIdentity = await db.query( - `SELECT * FROM user_identities - WHERE user_id = $1 AND provider = 'email' AND provider_id = $2`, - [userId, session.pendingEmail] - ); - - if (emailIdentity.rows.length === 0) { - // Связываем Email с пользователем + if (!session.pendingEmail) { + return { verified: false, message: 'Email не найден в сессии' }; + } + + // Проверяем код через сервис верификации + const result = await verificationService.verifyCode(code, 'email', session.pendingEmail); + + if (!result.success) { + // Используем сообщение об ошибке из сервиса верификации + return { verified: false, message: result.error || 'Неверный код верификации' }; + } + + const userId = result.userId || session.tempUserId; + const email = session.pendingEmail; + + // Добавляем email в базу данных await db.query( `INSERT INTO user_identities - (user_id, provider, provider_id, created_at) - VALUES ($1, $2, $3, NOW()) - ON CONFLICT (provider, provider_id) DO UPDATE SET user_id = $1`, - [userId, 'email', session.pendingEmail] + (user_id, provider, provider_id) + VALUES ($1, $2, $3) + ON CONFLICT (provider, provider_id) + DO UPDATE SET user_id = $1`, + [userId, 'email', email.toLowerCase()] ); - } - - // Связываем гостевой ID с пользователем, если его еще нет - if (session.guestId) { - const guestIdentity = await db.query( - `SELECT * FROM user_identities - WHERE user_id = $1 AND provider = 'guest' AND provider_id = $2`, - [userId, session.guestId] - ); - - if (guestIdentity.rows.length === 0) { - await db.query( - `INSERT INTO user_identities - (user_id, provider, provider_id, created_at) - VALUES ($1, $2, $3, NOW()) - ON CONFLICT (provider, provider_id) DO UPDATE SET user_id = $1`, - [userId, 'guest', session.guestId] - ); - } - - // Связываем гостевые сообщения с пользователем - try { - const messagesExist = await db.query( - 'SELECT EXISTS(SELECT 1 FROM guest_messages WHERE guest_id = $1) as exists', - [session.guestId] - ); - if (messagesExist.rows[0].exists) { - await db.query('SELECT link_guest_messages($1, $2)', [userId, session.guestId]); - } - } catch (linkError) { - logger.error(`Error linking messages: ${linkError}`); + // Очищаем временные данные + delete session.pendingEmail; + if (session.tempUserId) { + delete session.tempUserId; } + + return { + verified: true, + userId, + email: email.toLowerCase() + }; + } catch (error) { + logger.error('Error checking email verification:', error); + return { verified: false, message: 'Ошибка при проверке кода верификации' }; } - - return { - verified: true, - userId, - email: session.pendingEmail - }; - } catch (error) { - logger.error('Error in Email verification:', error); - return { verified: false, message: "Ошибка при проверке кода" }; } } -module.exports = { - initEmailAuth, - checkEmailVerification -}; \ No newline at end of file +// Создаем и экспортируем единственный экземпляр +const emailAuth = new EmailAuth(); +module.exports = emailAuth; \ No newline at end of file diff --git a/backend/services/emailBot.js b/backend/services/emailBot.js index 24b0b22..9c73cd6 100644 --- a/backend/services/emailBot.js +++ b/backend/services/emailBot.js @@ -5,17 +5,19 @@ const simpleParser = require('mailparser').simpleParser; const { processMessage } = require('./ai-assistant'); const { inspect } = require('util'); const logger = require('../utils/logger'); -const verificationService = require('./verification-service'); // Конфигурация для отправки писем const transporter = nodemailer.createTransport({ - host: process.env.EMAIL_SMTP_HOST, - port: process.env.EMAIL_SMTP_PORT, - secure: process.env.EMAIL_SMTP_PORT === '465', + host: process.env.EMAIL_SMTP_HOST || 'smtp.hostland.ru', + port: process.env.EMAIL_SMTP_PORT || 465, + secure: true, auth: { user: process.env.EMAIL_USER, pass: process.env.EMAIL_PASSWORD, }, + pool: true, + maxConnections: 3, + maxMessages: 5, tls: { rejectUnauthorized: false } @@ -30,26 +32,22 @@ const imapConfig = { tls: true, tlsOptions: { rejectUnauthorized: false }, keepalive: { - interval: 10000, // Проверка соединения каждые 10 секунд - idleInterval: 300000, // Сброс соединения через 5 минут простоя - forceNoop: true // Принудительная отправка NOOP для поддержания соединения + interval: 10000, + idleInterval: 300000, + forceNoop: true } }; class EmailBotService { - constructor(user, password) { - this.user = user; - this.password = password; + constructor() { this.transporter = transporter; this.imap = new Imap(imapConfig); this.initialize(); - this.listenForReplies(); } initialize() { this.imap.once('error', (err) => { logger.error(`IMAP connection error: ${err.message}`); - // Пробуем переподключиться через 1 минуту при ошибке setTimeout(() => { try { if (this.imap.state !== 'connected') { @@ -63,59 +61,52 @@ class EmailBotService { }); } - async sendVerificationCode(toEmail, userId) { + // Метод для инициализации email верификации + async initEmailVerification(email, userId, code) { try { - // Создаем код через сервис верификации - const code = await verificationService.createVerificationCode( - 'email', - toEmail.toLowerCase(), - userId - ); + // Отправляем код на email + await this.sendVerificationCode(email, code); - // Отправляем письмо с кодом + return { success: true }; + } catch (error) { + logger.error('Error initializing email verification:', error); + throw error; + } + } + + // Отправка кода верификации + async sendVerificationCode(email, code) { + try { const mailOptions = { - from: this.user, - to: toEmail, - subject: 'Код подтверждения для DApp for Business', - text: `Ваш код подтверждения: ${code}\n\nДля завершения аутентификации, пожалуйста, введите этот код на сайте.\n\nКод действителен в течение 15 минут.`, + from: process.env.EMAIL_USER, + to: email, + subject: 'Код подтверждения', + text: `Ваш код подтверждения: ${code}\n\nКод действителен в течение 15 минут.`, html: ` -
-

Код подтверждения для DApp for Business

-

Ваш код подтверждения:

-
- ${code} +
+

Код подтверждения

+

Ваш код подтверждения:

+
+ ${code}
-

Для завершения аутентификации, пожалуйста, введите этот код в форме на сайте.

-

Код действителен в течение 15 минут.

-
-

Это автоматическое сообщение, пожалуйста, не отвечайте на него.

+

Код действителен в течение 15 минут.

` }; - const info = await this.transporter.sendMail(mailOptions); - logger.info(`Email sent: ${info.messageId}`); - - return { success: true, code }; + await this.transporter.sendMail(mailOptions); + logger.info(`Verification code sent to ${email}`); } catch (error) { - logger.error(`Error sending email: ${error}`); - return { success: false, error: error.message }; + logger.error('Error sending verification code:', error); + throw error; } } - listenForReplies() { - // Запускаем проверку почты каждые 60 секунд - setInterval(() => { - this.checkEmails(); - }, 60000); - } - checkEmails() { try { // Добавляем обработчики ошибок this.imap.once('error', (err) => { logger.error(`IMAP connection error during check: ${err.message}`); - // Пытаемся закрыть соединение при ошибке try { this.imap.end(); } catch (e) { @@ -145,7 +136,6 @@ class EmailBotService { return; } - // Защищаемся от пустых результатов try { const f = this.imap.fetch(results, { bodies: '' }); @@ -156,23 +146,6 @@ class EmailBotService { logger.error(`Error parsing message: ${err}`); return; } - - // Обработка входящего письма для Ollama - try { - // Проверяем, что это действительно письмо (защита от ошибок) - if (parsed && parsed.text && parsed.from && parsed.from.value && - parsed.from.value.length > 0 && parsed.from.value[0].address) { - - const fromEmail = parsed.from.value[0].address.toLowerCase(); - const subject = parsed.subject || ''; - const text = parsed.text || ''; - - // Передаем письмо в Ollama для обработки - await this.processOllamaEmail(fromEmail, subject, text); - } - } catch (e) { - logger.error(`Error processing email for Ollama: ${e.message}`); - } }); }); }); @@ -203,7 +176,6 @@ class EmailBotService { this.imap.connect(); } catch (error) { logger.error(`Global error checking emails: ${error.message}`); - // Обеспечиваем корректное завершение IMAP сессии try { this.imap.end(); } catch (e) { @@ -212,74 +184,25 @@ class EmailBotService { } } - // Метод для обработки письма с помощью Ollama - async processOllamaEmail(fromEmail, subject, text) { - try { - // Проверяем, есть ли текст для обработки - if (!text || text.trim() === '') { - logger.info(`Empty message from ${fromEmail}, skipping Ollama processing`); - return; - } - - logger.info(`Processing message from ${fromEmail} for Ollama`); - - // Получаем ответ от Ollama - const response = await processMessage(text); - - if (response) { - // Отправляем ответ обратно пользователю - await this.transporter.sendMail({ - from: this.user, - to: fromEmail, - subject: `Re: ${subject}`, - text: response - }); - - logger.info(`Ollama response sent to ${fromEmail}`); - } - } catch (error) { - logger.error(`Error in Ollama email processing: ${error}`); - - // Отправляем сообщение об ошибке пользователю - try { - await this.transporter.sendMail({ - from: this.user, - to: fromEmail, - subject: 'Error processing your request', - text: 'Sorry, we encountered an error processing your message. Please try again later.' - }); - } catch (e) { - logger.error(`Error sending error notification: ${e}`); - } - } - } - - // Метод для проверки кода без IMAP - async verifyCode(email, code) { - return await verificationService.verifyCode(code, 'email', email.toLowerCase()); - } - - // Оставляем существующий метод для отправки электронных писем + // Метод для отправки email async sendEmail(to, subject, text) { try { const mailOptions = { - from: this.user, + from: process.env.EMAIL_USER, to, subject, text }; - const info = await this.transporter.sendMail(mailOptions); - logger.info(`Email sent: ${info.messageId}`); + await this.transporter.sendMail(mailOptions); + logger.info(`Email sent to ${to}`); return true; } catch (error) { - logger.error(`Error sending email: ${error}`); - return false; + logger.error('Error sending email:', error); + throw error; } } } -module.exports = { - EmailBotService, - transporter -}; +// Экспортируем singleton instance +module.exports = new EmailBotService(); diff --git a/backend/services/index.js b/backend/services/index.js index a490dec..985a9b9 100644 --- a/backend/services/index.js +++ b/backend/services/index.js @@ -1,5 +1,7 @@ const { initTelegramBot } = require('./telegram-service'); -const { initEmailBot, sendEmail, checkEmails } = require('./emailBot'); +const emailBot = require('./emailBot'); +const telegramBot = require('./telegramBot'); +const aiAssistant = require('./ai-assistant'); const { initializeVectorStore, getVectorStore, @@ -14,9 +16,9 @@ module.exports = { initTelegramBot, // Email - initEmailBot, - sendEmail, - checkEmails, + emailBot, + sendEmail: emailBot.sendEmail, + checkEmails: emailBot.checkEmails, // Vector Store initializeVectorStore, @@ -28,5 +30,7 @@ module.exports = { processMessage, getUserInfo, getConversationHistory, - // ... другие экспорты + + telegramBot, + aiAssistant }; diff --git a/backend/services/verification-service.js b/backend/services/verification-service.js index 2ac90be..9715367 100644 --- a/backend/services/verification-service.js +++ b/backend/services/verification-service.js @@ -9,7 +9,9 @@ class VerificationService { // Генерация кода generateCode() { - return Math.random().toString(36).substring(2, 2 + this.codeLength).toUpperCase(); + const code = Math.random().toString(36).substring(2, 2 + this.codeLength).toUpperCase(); + logger.info(`Generated verification code: ${code}`); + return code; } // Создание кода верификации @@ -18,6 +20,8 @@ class VerificationService { const expiresAt = new Date(Date.now() + this.expirationMinutes * 60 * 1000); try { + logger.info(`Creating verification code for ${provider}:${providerId}, userId: ${userId}`); + await db.query( `INSERT INTO verification_codes (code, provider, provider_id, user_id, expires_at) @@ -25,9 +29,15 @@ class VerificationService { [code, provider, providerId, userId, expiresAt] ); + logger.info(`Verification code created successfully for ${provider}:${providerId}`); return code; } catch (error) { - logger.error('Error creating verification code:', error); + logger.error('Error creating verification code:', { + error: error.message, + provider, + providerId, + userId + }); throw error; } } @@ -35,6 +45,28 @@ class VerificationService { // Проверка кода async verifyCode(code, provider, providerId) { try { + logger.info(`Verifying code for ${provider}:${providerId}`); + + // Преобразуем код в верхний регистр для сравнения + const normalizedCode = code.toUpperCase(); + logger.info(`Normalized code: ${normalizedCode}`); + + // Проверим, есть ли такой код в базе (для отладки) + const checkResult = await db.query( + `SELECT code FROM verification_codes + WHERE provider = $1 + AND provider_id = $2 + AND used = false + AND expires_at > NOW()`, + [provider, providerId] + ); + + if (checkResult.rows.length > 0) { + logger.info(`Found codes for ${provider}:${providerId}: ${JSON.stringify(checkResult.rows.map(r => r.code))}`); + } else { + logger.warn(`No active codes found for ${provider}:${providerId}`); + } + const result = await db.query( `SELECT * FROM verification_codes WHERE code = $1 @@ -42,10 +74,11 @@ class VerificationService { AND provider_id = $3 AND used = false AND expires_at > NOW()`, - [code, provider, providerId] + [normalizedCode, provider, providerId] ); if (result.rows.length === 0) { + logger.warn(`Invalid or expired code for ${provider}:${providerId}. Input: ${normalizedCode}`); return { success: false, error: 'Неверный или истекший код' }; } @@ -57,13 +90,19 @@ class VerificationService { [verification.id] ); + logger.info(`Code verified successfully for ${provider}:${providerId}`); return { success: true, userId: verification.user_id, providerId: verification.provider_id }; } catch (error) { - logger.error('Error verifying code:', error); + logger.error('Error verifying code:', { + error: error.message, + code, + provider, + providerId + }); throw error; } } @@ -71,13 +110,15 @@ class VerificationService { // Очистка истекших кодов async cleanupExpiredCodes() { try { - await db.query( - 'DELETE FROM verification_codes WHERE expires_at <= NOW()' + const result = await db.query( + 'DELETE FROM verification_codes WHERE expires_at <= NOW() RETURNING id' ); + logger.info(`Cleaned up ${result.rowCount} expired verification codes`); } catch (error) { logger.error('Error cleaning up expired codes:', error); } } } -module.exports = new VerificationService(); \ No newline at end of file +const verificationService = new VerificationService(); +module.exports = verificationService; \ No newline at end of file diff --git a/frontend/src/assets/styles/home.css b/frontend/src/assets/styles/home.css index 63936ff..1972133 100644 --- a/frontend/src/assets/styles/home.css +++ b/frontend/src/assets/styles/home.css @@ -376,3 +376,78 @@ .user-info-value { font-weight: bold; } + +/* Стили для форм верификации Email */ +.email-form { + margin-top: 20px; + background-color: #fff; + padding: 15px; + border-radius: 8px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05); +} + +.email-input-container { + display: flex; + margin-top: 10px; + margin-bottom: 10px; +} + +.email-input { + flex-grow: 1; + padding: 10px; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 14px; +} + +.email-input-error { + border-color: #e74c3c; +} + +.send-email-btn { + padding: 0 15px; + background-color: #4CAF50; + color: white; + border: none; + border-radius: 4px; + margin-left: 10px; + cursor: pointer; + white-space: nowrap; +} + +.send-email-btn:hover:not(:disabled) { + background-color: #3e8e41; +} + +.send-email-btn:disabled { + background-color: #cccccc; + cursor: not-allowed; +} + +.email-format-error { + color: #e74c3c; + font-size: 12px; + margin-top: 5px; +} + +.form-actions { + display: flex; + flex-direction: column; + align-items: start; + margin-top: 10px; +} + +.cancel-btn { + background-color: #f8f8f8; + color: #333; + border: 1px solid #ddd; + border-radius: 4px; + padding: 8px 15px; + font-size: 14px; + cursor: pointer; + margin-bottom: 10px; +} + +.cancel-btn:hover { + background-color: #e8e8e8; +} diff --git a/frontend/src/services/tokens.js b/frontend/src/services/tokens.js new file mode 100644 index 0000000..c60d3b1 --- /dev/null +++ b/frontend/src/services/tokens.js @@ -0,0 +1,41 @@ +import api from '../api/axios'; + +// Адреса смарт-контрактов токенов HB3A +export const TOKEN_CONTRACTS = { + eth: { + address: "0xd95a45fc46a7300e6022885afec3d618d7d3f27c", + symbol: "HB3A", + network: "Ethereum" + }, + bsc: { + address: "0x1d47f12ffA279BFE59Ab16d56fBb10d89AECdD5D", + symbol: "HB3A", + network: "BSC" + }, + arbitrum: { + address: "0xdce769b847a0a697239777d0b1c7dd33b6012ba0", + symbol: "HB3A", + network: "Arbitrum" + }, + polygon: { + address: "0x351f59de4fedbdf7601f5592b93db3b9330c1c1d", + symbol: "HB3A", + network: "Polygon" + } +}; + +// Получение балансов токенов +export const fetchTokenBalances = async () => { + try { + const response = await api.get('/api/tokens/balances'); + return response.data; + } catch (error) { + console.error('Error fetching token balances:', error); + return { + eth: '0', + bsc: '0', + arbitrum: '0', + polygon: '0' + }; + } +}; \ No newline at end of file diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index 9fd6531..56026d5 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -77,11 +77,11 @@ -
+
-
@@ -111,7 +111,10 @@ {{ isEmailSending ? 'Отправка...' : 'Отправить код' }}
- +
+ + +
@@ -141,23 +144,23 @@

Баланс:

ETH: - {{ isAuthenticated ? '1500000' : '0' }} - HB3A + {{ tokenBalances.eth }} + {{ TOKEN_CONTRACTS.eth.symbol }}
ARB: - {{ isAuthenticated ? '500000' : '0' }} - HB3A + {{ tokenBalances.arbitrum }} + {{ TOKEN_CONTRACTS.arbitrum.symbol }}
POL: - {{ isAuthenticated ? '500000' : '0' }} - HB3A + {{ tokenBalances.polygon }} + {{ TOKEN_CONTRACTS.polygon.symbol }}
BNB: - 0 - HB3A + {{ tokenBalances.bsc }} + {{ TOKEN_CONTRACTS.bsc.symbol }}
@@ -194,6 +197,7 @@ import api from '../api/axios'; import DOMPurify from 'dompurify'; import { marked } from 'marked'; import '../assets/styles/home.css'; +import { fetchTokenBalances, TOKEN_CONTRACTS } from '../services/tokens'; console.log('HomeView.vue: Version with chat loaded'); @@ -242,6 +246,14 @@ const showSuccessMessage = ref(false); const showSidebar = ref(false); const currentPage = ref('home'); +// Добавляем состояние для балансов +const tokenBalances = ref({ + eth: '0', + bsc: '0', + arbitrum: '0', + polygon: '0' +}); + // Функция для управления сайдбаром const toggleSidebar = () => { showSidebar.value = !showSidebar.value; @@ -268,7 +280,7 @@ const clearEmailError = () => { emailError.value = ''; }; -// Функция для обработки Email аутентификации +// Обработчик для Email аутентификации const handleEmailAuth = async () => { try { // Показываем форму для ввода email @@ -285,9 +297,18 @@ const handleEmailAuth = async () => { } }; +// Функция для отмены email авторизации +const cancelEmailAuth = () => { + showEmailForm.value = false; + showEmailVerificationInput.value = false; + emailError.value = ''; + emailFormatError.value = false; +}; + // Функция для отправки запроса на верификацию email const sendEmailVerification = async () => { try { + // Очищаем сообщения об ошибках emailFormatError.value = false; emailError.value = ''; @@ -300,21 +321,30 @@ const sendEmailVerification = async () => { isEmailSending.value = true; // Отправляем запрос на сервер для инициализации email аутентификации - const response = await axios.post('/api/auth/email/init', { email: emailInput.value }); - - if (response.data.success) { - // Скрываем форму ввода email - showEmailForm.value = false; - // Показываем форму для ввода кода - showEmailVerificationInput.value = true; - // Скрываем старую форму кода верификации - showEmailVerification.value = false; - // Сохраняем email - emailVerificationEmail.value = emailInput.value; - // Очищаем поле для ввода кода - emailVerificationCode.value = ''; - } else { - emailError.value = response.data.error || 'Ошибка инициализации аутентификации по email'; + try { + const response = await axios.post('/api/auth/email/init', { email: emailInput.value }); + + if (response.data.success) { + // Скрываем форму ввода email + showEmailForm.value = false; + // Показываем форму для ввода кода + showEmailVerificationInput.value = true; + // Скрываем старую форму кода верификации + showEmailVerification.value = false; + // Сохраняем email + emailVerificationEmail.value = emailInput.value; + // Очищаем поле для ввода кода + emailVerificationCode.value = ''; + } else { + emailError.value = response.data.error || 'Ошибка инициализации аутентификации по email'; + } + } catch (error) { + console.error('Error sending email verification:', error); + if (error.response && error.response.data && error.response.data.error) { + emailError.value = error.response.data.error; + } else { + emailError.value = 'Ошибка при отправке кода. Пожалуйста, проверьте правильность email или попробуйте позже.'; + } } } catch (error) { emailError.value = 'Ошибка при запросе кода подтверждения'; @@ -338,8 +368,11 @@ const verifyEmailCode = async () => { // Показываем индикатор процесса верификации isVerifying.value = true; + // Преобразуем код в верхний регистр перед отправкой + const code = emailVerificationCode.value.toUpperCase(); + const response = await axios.post('/api/auth/check-email-verification', { - code: emailVerificationCode.value + code: code }); if (response.data.success) { @@ -360,10 +393,27 @@ const verifyEmailCode = async () => { // Обновляем состояние аутентификации await auth.checkAuth(); - // Перезагружаем страницу для обновления UI через 1 секунду - setTimeout(() => { - window.location.reload(); - }, 1000); + // Загружаем историю сообщений + messages.value = []; + offset.value = 0; + hasMoreMessages.value = true; + await loadMoreMessages(); + + // Связываем гостевые сообщения + try { + await api.post('/api/chat/link-guest-messages'); + console.log('Guest messages linked to authenticated user'); + + // Перезагружаем сообщения после связывания + messages.value = []; + offset.value = 0; + await loadMoreMessages(); + } catch (linkError) { + console.error('Error linking guest messages:', linkError); + } + + // Обновляем баланс токенов + await updateBalances(); } else { emailError.value = response.data.message || 'Неверный код верификации'; } @@ -390,19 +440,47 @@ const handleTelegramAuth = async () => { telegramAuthCheckInterval.value = setInterval(async () => { try { const response = await axios.get('/api/auth/check'); + console.log('Проверка авторизации:', response.data); + if (response.data.authenticated) { + // Обновляем состояние аутентификации auth.updateAuth({ isAuthenticated: true, authType: response.data.authType, - userId: response.data.userId + userId: response.data.userId, + telegramId: response.data.telegramId }); + console.log('Telegram authentication successful:', response.data); + + // Обновляем все данные пользователя + await auth.checkAuth(); + + // Загружаем историю сообщений + messages.value = []; + offset.value = 0; + hasMoreMessages.value = true; + await loadMoreMessages(); + + // Связываем гостевые сообщения + try { + await api.post('/api/chat/link-guest-messages'); + console.log('Guest messages linked to authenticated user'); + + // Перезагружаем сообщения после связывания + messages.value = []; + offset.value = 0; + await loadMoreMessages(); + } catch (linkError) { + console.error('Error linking guest messages:', linkError); + } + + // Обновляем баланс токенов + await updateBalances(); + clearInterval(telegramAuthCheckInterval.value); telegramAuthCheckInterval.value = null; showTelegramVerification.value = false; - - // Перезагружаем страницу для полного обновления состояния - window.location.reload(); } } catch (error) { console.error('Error checking auth status:', error); @@ -504,7 +582,7 @@ watch(() => isAuthenticated.value, async (newValue) => { // Функция для подключения кошелька const handleWalletAuth = async () => { - if (isConnecting.value || isAuthenticated.value) return; // Предотвращаем повторное подключение + if (isConnecting.value || isAuthenticated.value) return; isConnecting.value = true; try { @@ -515,6 +593,25 @@ const handleWalletAuth = async () => { // Обновляем состояние авторизации await auth.checkAuth(); + // Загружаем историю сообщений + messages.value = []; + offset.value = 0; + hasMoreMessages.value = true; + await loadMoreMessages(); + + // Связываем гостевые сообщения + try { + await api.post('/api/chat/link-guest-messages'); + console.log('Guest messages linked to authenticated user'); + + // Перезагружаем сообщения после связывания + messages.value = []; + offset.value = 0; + await loadMoreMessages(); + } catch (linkError) { + console.error('Error linking guest messages:', linkError); + } + // Добавляем небольшую задержку перед сбросом состояния isConnecting setTimeout(() => { isConnecting.value = false; @@ -602,18 +699,21 @@ const handleMessage = async (text) => { id: response.data.messageId, content: messageContent, role: 'user', - timestamp: new Date().toISOString(), - showAuthButtons: false + timestamp: new Date().toISOString() }; messages.value.push(userMessage); + // Показываем сообщение с просьбой авторизоваться messages.value.push({ id: Date.now() + 1, - content: 'Для получения ответа от ассистента, пожалуйста, авторизуйтесь одним из способов:', + content: 'Для получения ответа от ассистента, пожалуйста, авторизуйтесь одним из способов в правой панели.', role: 'assistant', - timestamp: new Date().toISOString(), - showAuthButtons: true + timestamp: new Date().toISOString() }); + + // НЕ показываем форму email автоматически и НЕ устанавливаем showEmailAlternatives + // showEmailForm.value = true; + // showEmailAlternatives.value = true; } } else { // Для авторизованного пользователя сохраняем в messages @@ -669,6 +769,18 @@ const handleScroll = async () => { } }; +// Функция получения балансов +const updateBalances = async () => { + if (auth.isAuthenticated.value && auth.address?.value) { + try { + const balances = await fetchTokenBalances(); + tokenBalances.value = balances; + } catch (error) { + console.error('Error updating balances:', error); + } + } +}; + onMounted(() => { // Добавляем слушатель прокрутки if (messagesContainer.value) { @@ -682,6 +794,24 @@ onMounted(() => { // Проверяем статус авторизации auth.checkAuth(); + + // Обновляем баланс при монтировании и изменении аутентификации + if (auth.isAuthenticated.value) { + updateBalances(); + } +}); + +watch(() => auth.isAuthenticated.value, async (newValue) => { + if (newValue) { + await updateBalances(); + } else { + tokenBalances.value = { + eth: '0', + bsc: '0', + arbitrum: '0', + polygon: '0' + }; + } }); onBeforeUnmount(() => {