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

This commit is contained in:
2025-03-27 17:22:45 +03:00
parent 5fb81c7558
commit 831059d501
12 changed files with 784 additions and 457 deletions

View File

@@ -17,6 +17,7 @@ const usersRoutes = require('./routes/users');
const identitiesRoutes = require('./routes/identities'); const identitiesRoutes = require('./routes/identities');
const chatRoutes = require('./routes/chat'); const chatRoutes = require('./routes/chat');
const adminRoutes = require('./routes/admin'); const adminRoutes = require('./routes/admin');
const tokensRouter = require('./routes/tokens');
const app = express(); const app = express();
@@ -125,6 +126,7 @@ app.use('/api/users', usersRoutes);
app.use('/api/identities', identitiesRoutes); app.use('/api/identities', identitiesRoutes);
app.use('/api/chat', chatRoutes); app.use('/api/chat', chatRoutes);
app.use('/api/admin', adminRoutes); app.use('/api/admin', adminRoutes);
app.use('/api/tokens', tokensRouter);
const nonceStore = new Map(); // или любая другая реализация хранилища nonce const nonceStore = new Map(); // или любая другая реализация хранилища nonce

View File

@@ -9,13 +9,11 @@ const { checkRole, requireAuth } = require('../middleware/auth');
const { pool } = require('../db'); const { pool } = require('../db');
const authService = require('../services/auth-service'); const authService = require('../services/auth-service');
const { SiweMessage } = require('siwe'); const { SiweMessage } = require('siwe');
const { EmailBotService } = require('../services/emailBot'); const emailBot = require('../services/emailBot');
const { verificationCodes } = require('../services/telegramBot'); const { verificationCodes } = require('../services/telegramBot');
const { checkTokensAndUpdateRole } = require('../services/auth-service'); const { checkTokensAndUpdateRole } = require('../services/auth-service');
const { ethers } = require('ethers'); const { ethers } = require('ethers');
const { initTelegramAuth } = require('../services/telegramBot'); const { initTelegramAuth } = require('../services/telegramBot');
const { initEmailAuth, verifyEmailCode } = require('../services/emailBot');
const { getBot } = require('../services/telegramBot');
const emailAuth = require('../services/emailAuth'); const emailAuth = require('../services/emailAuth');
const verificationService = require('../services/verification-service'); const verificationService = require('../services/verification-service');
@@ -281,7 +279,7 @@ router.post('/email/request', authLimiter, async (req, res) => {
}); });
// Отправляем email с кодом подтверждения // Отправляем 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); const result = await emailBot.sendVerificationCode(email, req.session.tempUserId || req.session.userId);
if (result.success) { if (result.success) {
@@ -349,40 +347,24 @@ router.post('/email/verify', async (req, res) => {
// Добавляем email в базу данных // Добавляем email в базу данных
await db.query( await db.query(
`INSERT INTO user_identities `INSERT INTO user_identities
(user_id, provider, provider_id, created_at) (user_id, provider, provider_id)
VALUES ($1, $2, $3, NOW()) VALUES ($1, $2, $3)
ON CONFLICT (provider, provider_id) ON CONFLICT (provider, provider_id)
DO UPDATE SET user_id = $1`, DO UPDATE SET user_id = $1`,
[userId, 'email', email.toLowerCase()] [userId, 'email', email.toLowerCase()]
); );
// Связываем гостевой ID с пользователем, если его еще нет // Проверяем наличие кошелька и определяем роль
if (req.session.guestId) { const wallet = await authService.getLinkedWallet(userId);
const guestIdentity = await db.query( let role = 'user'; // Базовая роль для доступа к чату
`SELECT * FROM user_identities
WHERE user_id = $1 AND provider = 'guest' AND provider_id = $2`,
[userId, req.session.guestId]
);
if (guestIdentity.rows.length === 0) { if (wallet) {
await db.query( // Если есть кошелек, проверяем баланс токенов
`INSERT INTO user_identities const isAdmin = await authService.checkAdminRole(wallet);
(user_id, provider, provider_id, created_at) role = isAdmin ? 'admin' : 'user';
VALUES ($1, $2, $3, NOW()) logger.info(`User ${userId} has wallet ${wallet}, role set to ${role}`);
ON CONFLICT (provider, provider_id) DO UPDATE SET user_id = $1`, } else {
[userId, 'guest', req.session.guestId] logger.info(`User ${userId} has no wallet, using basic user role`);
);
}
}
// Связываем все гостевые сообщения с пользователем
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}`);
}
} }
// Устанавливаем аутентификацию пользователя // Устанавливаем аутентификацию пользователя
@@ -390,6 +372,11 @@ router.post('/email/verify', async (req, res) => {
req.session.userId = userId; req.session.userId = userId;
req.session.email = email.toLowerCase(); req.session.email = email.toLowerCase();
req.session.authType = 'email'; req.session.authType = 'email';
req.session.role = role;
if (wallet) {
req.session.address = wallet;
}
// Очищаем временные данные // Очищаем временные данные
delete req.session.pendingEmail; delete req.session.pendingEmail;
@@ -402,7 +389,7 @@ router.post('/email/verify', async (req, res) => {
logger.error('Error saving session:', err); logger.error('Error saving session:', err);
reject(err); reject(err);
} else { } else {
logger.info('Session saved successfully'); logger.info(`Session saved successfully for user ${userId} with role ${role}`);
resolve(); resolve();
} }
}); });
@@ -412,6 +399,8 @@ router.post('/email/verify', async (req, res) => {
success: true, success: true,
userId, userId,
email: email.toLowerCase(), email: email.toLowerCase(),
role,
wallet: wallet || null,
message: 'Аутентификация успешна' message: 'Аутентификация успешна'
}); });
@@ -620,61 +609,72 @@ router.get('/telegram', (req, res) => {
res.json({ authUrl }); res.json({ authUrl });
}); });
// Маршрут для верификации Telegram // Обработка верификации Telegram
router.post('/telegram/verify', async (req, res) => { router.post('/telegram/verify', async (req, res) => {
try { try {
const { code } = req.body; const { telegramId, verificationCode } = req.body;
// Проверяем код через сервис верификации if (!telegramId || !verificationCode) {
const result = await verificationService.verifyCode(code, 'telegram', req.session.guestId || 'temp'); return res.status(400).json({
success: false,
error: 'Missing required fields'
});
}
// Проверяем верификацию через сервис
const result = await authService.verifyTelegramAuth(telegramId, verificationCode);
if (!result.success) { if (!result.success) {
return res.status(400).json({ return res.status(400).json({
success: false, success: false,
error: result.error || 'Неверный код подтверждения' error: 'Invalid verification'
}); });
} }
const userId = result.userId; // Обновляем сессию
const telegramId = result.providerId; req.session.userId = result.userId;
// Добавляем 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]
);
// Устанавливаем аутентификацию пользователя
req.session.authenticated = true; req.session.authenticated = true;
req.session.userId = userId;
req.session.telegramId = telegramId;
req.session.authType = 'telegram'; 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) { if (req.session.tempUserId) {
delete req.session.tempUserId; delete req.session.tempUserId;
} }
// Если есть подключенный кошелек, проверяем баланс токенов // Сохраняем сессию
if (req.session.address) { await new Promise((resolve, reject) => {
const isAdmin = await checkTokenBalance(req.session.address); req.session.save(err => {
req.session.isAdmin = isAdmin; if (err) {
} logger.error('Error saving session:', err);
reject(err);
} else {
logger.info(`Session saved successfully for user ${result.userId}`);
resolve();
}
});
});
return res.json({ return res.json({
success: true, success: true,
userId, userId: result.userId,
telegramId, role: result.role,
isAdmin: req.session.isAdmin || false, telegramId: result.telegramId,
authenticated: true wallet: result.wallet,
message: 'Authentication successful'
}); });
} catch (error) { } catch (error) {
logger.error('Error in telegram verification:', error); logger.error('Error in Telegram verification:', error);
res.status(500).json({ error: 'Internal server error' }); res.status(500).json({
success: false,
error: 'Internal server error'
});
} }
}); });
@@ -1212,24 +1212,65 @@ router.post('/email/init', async (req, res) => {
try { try {
const { email } = req.body; const { email } = req.body;
if (!email) { if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
return res.status(400).json({ return res.status(400).json({
success: false, success: false,
error: 'Email не указан' error: 'Некорректный формат email'
}); });
} }
const result = await emailAuth.initEmailAuth(req.session, 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({ return res.json({
success: true, success: true,
message: 'Код верификации отправлен на указанный email' message: 'Код верификации отправлен на email'
}); });
} catch (error) { } catch (error) {
logger.error('Error initializing email auth:', error); logger.error('Error in email auth initialization:', error);
return res.status(500).json({ res.status(500).json({
success: false, success: false,
error: error.message || 'Ошибка при инициализации email аутентификации' error: 'Внутренняя ошибка сервера'
}); });
} }
}); });
@@ -1251,13 +1292,14 @@ router.post('/email/verify', requireAuth, async (req, res) => {
}); });
// Проверка кода верификации email // Проверка кода верификации email
router.get('/check-email-verification', async (req, res) => { router.all('/check-email-verification', async (req, res) => {
try { try {
const { code } = req.query; // Получаем код из query параметров (GET) или тела запроса (POST)
const code = req.method === 'POST' ? req.body.code : req.query.code;
if (!code) { if (!code) {
return res.status(400).json({ return res.status(400).json({
verified: false, success: false,
message: 'Код верификации не предоставлен' message: 'Код верификации не предоставлен'
}); });
} }
@@ -1266,7 +1308,11 @@ router.get('/check-email-verification', async (req, res) => {
const result = await emailAuth.checkEmailVerification(code, req.session); const result = await emailAuth.checkEmailVerification(code, req.session);
if (!result.verified) { 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({ return res.json({
verified: true, success: true,
userId: result.userId, userId: result.userId,
email: result.email email: result.email
}); });
} catch (error) { } catch (error) {
logger.error('Error checking email verification:', error); logger.error('Error checking email verification:', error);
return res.status(500).json({ return res.status(500).json({
verified: false, success: false,
message: 'Ошибка при проверке кода верификации' message: 'Ошибка при проверке кода верификации'
}); });
} }

31
backend/routes/tokens.js Normal file
View File

@@ -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;

View File

@@ -2,7 +2,7 @@ require('dotenv').config();
const express = require('express'); const express = require('express');
const cors = require('cors'); const cors = require('cors');
const { ethers } = require('ethers'); const { ethers } = require('ethers');
const EmailBotService = require('./services/emailBot'); const emailBot = require('./services/emailBot');
const session = require('express-session'); const session = require('express-session');
const { app, nonceStore } = require('./app'); const { app, nonceStore } = require('./app');
const usersRouter = require('./routes/users'); const usersRouter = require('./routes/users');

View File

@@ -145,6 +145,61 @@ class AuthService {
return false; return false;
} }
/**
* Получение балансов токенов для адреса
* @param {string} address - Адрес кошелька
* @returns {Promise<Object>} - Объект с балансами токенов
*/
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 }) { async createSession(session, { userId, authenticated, authType, guestId, address }) {
try { try {
@@ -209,12 +264,18 @@ class AuthService {
try { try {
// Проверяем наличие связанного кошелька // Проверяем наличие связанного кошелька
const wallet = await this.getLinkedWallet(userId); const wallet = await this.getLinkedWallet(userId);
if (wallet) {
// Если есть кошелек, проверяем админские токены // Если кошелек не привязан, пользователь получает роль user
const isAdmin = await this.checkAdminRole(wallet); // с базовым доступом к чату и истории сообщений
return isAdmin ? 'admin' : '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) { } catch (error) {
logger.error('Error checking user role:', error); logger.error('Error checking user role:', error);
return 'user'; return 'user';
@@ -244,16 +305,92 @@ class AuthService {
return { verified: false }; 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 { return {
verified: true, verified: true,
userId, userId,
email email,
role,
wallet: wallet || null
}; };
} catch (error) { } catch (error) {
logger.error('Error checking email verification:', error); logger.error('Error checking email verification:', error);
return { verified: false }; 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;
}
}
} }
// Создаем и экспортируем единственный экземпляр // Создаем и экспортируем единственный экземпляр

View File

@@ -1,209 +1,106 @@
const { pool } = require('../db');
const verificationService = require('./verification-service');
const logger = require('../utils/logger'); const logger = require('../utils/logger');
const emailBot = require('./emailBot');
const db = require('../db'); const db = require('../db');
const authService = require('./auth-service'); const authService = require('./auth-service');
const verificationService = require('./verification-service');
const { EmailBotService } = require('./emailBot');
// Инициализация процесса аутентификации по email class EmailAuth {
async function initEmailAuth(session, email) { constructor() {
try { this.emailBot = emailBot;
if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) { }
throw new Error('Некорректный формат email');
}
// Сохраняем email в сессии для последующей верификации async initEmailAuth(session, email) {
session.pendingEmail = email.toLowerCase(); try {
if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) {
// Создаем или получаем ID пользователя throw new Error('Некорректный формат email');
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]
);
}
} }
} 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) { // Создаем или получаем ID пользователя
// Используем существующего пользователя с guestId let userId;
userId = guestUserResult.rows[0].id;
if (session.authenticated && session.userId) {
userId = session.userId;
} else { } else {
// Создаем нового пользователя
const userResult = await db.query( 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; userId = userResult.rows[0].id;
session.tempUserId = userId;
// Связываем гостевой 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]
);
}
} }
} else {
// Создаем нового пользователя без гостевого ID // Сохраняем email в сессии
const userResult = await db.query( session.pendingEmail = email.toLowerCase();
'INSERT INTO users (created_at) VALUES (NOW()) RETURNING id'
// Создаем код через сервис верификации
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 checkEmailVerification(code, session) {
async function checkEmailVerification(code, session) { try {
try { if (!code) {
if (!session?.pendingEmail) { return { verified: false, message: 'Код верификации не предоставлен' };
return { verified: false, message: "Email не найден в сессии" }; }
}
// Проверяем код через сервис верификации if (!session.pendingEmail) {
const result = await verificationService.verifyCode(code, 'email', session.pendingEmail); return { verified: false, message: 'Email не найден в сессии' };
}
if (!result.success) { // Проверяем код через сервис верификации
return { verified: false, message: result.error || "Неверный код верификации" }; const result = await verificationService.verifyCode(code, 'email', session.pendingEmail);
}
const userId = result.userId; if (!result.success) {
// Используем сообщение об ошибке из сервиса верификации
return { verified: false, message: result.error || 'Неверный код верификации' };
}
// Проверяем, существует ли пользователь const userId = result.userId || session.tempUserId;
const userResult = await db.query( const email = session.pendingEmail;
'SELECT * FROM users WHERE id = $1',
[userId]
);
if (userResult.rows.length === 0) { // Добавляем email в базу данных
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 с пользователем
await db.query( await db.query(
`INSERT INTO user_identities `INSERT INTO user_identities
(user_id, provider, provider_id, created_at) (user_id, provider, provider_id)
VALUES ($1, $2, $3, NOW()) VALUES ($1, $2, $3)
ON CONFLICT (provider, provider_id) DO UPDATE SET user_id = $1`, ON CONFLICT (provider, provider_id)
[userId, 'email', session.pendingEmail] 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( delete session.pendingEmail;
`INSERT INTO user_identities if (session.tempUserId) {
(user_id, provider, provider_id, created_at) delete session.tempUserId;
VALUES ($1, $2, $3, NOW())
ON CONFLICT (provider, provider_id) DO UPDATE SET user_id = $1`,
[userId, 'guest', session.guestId]
);
} }
// Связываем гостевые сообщения с пользователем return {
try { verified: true,
const messagesExist = await db.query( userId,
'SELECT EXISTS(SELECT 1 FROM guest_messages WHERE guest_id = $1) as exists', email: email.toLowerCase()
[session.guestId] };
); } catch (error) {
logger.error('Error checking email verification:', error);
if (messagesExist.rows[0].exists) { return { verified: false, message: 'Ошибка при проверке кода верификации' };
await db.query('SELECT link_guest_messages($1, $2)', [userId, session.guestId]);
}
} catch (linkError) {
logger.error(`Error linking messages: ${linkError}`);
}
} }
return {
verified: true,
userId,
email: session.pendingEmail
};
} catch (error) {
logger.error('Error in Email verification:', error);
return { verified: false, message: "Ошибка при проверке кода" };
} }
} }
module.exports = { // Создаем и экспортируем единственный экземпляр
initEmailAuth, const emailAuth = new EmailAuth();
checkEmailVerification module.exports = emailAuth;
};

View File

@@ -5,17 +5,19 @@ const simpleParser = require('mailparser').simpleParser;
const { processMessage } = require('./ai-assistant'); const { processMessage } = require('./ai-assistant');
const { inspect } = require('util'); const { inspect } = require('util');
const logger = require('../utils/logger'); const logger = require('../utils/logger');
const verificationService = require('./verification-service');
// Конфигурация для отправки писем // Конфигурация для отправки писем
const transporter = nodemailer.createTransport({ const transporter = nodemailer.createTransport({
host: process.env.EMAIL_SMTP_HOST, host: process.env.EMAIL_SMTP_HOST || 'smtp.hostland.ru',
port: process.env.EMAIL_SMTP_PORT, port: process.env.EMAIL_SMTP_PORT || 465,
secure: process.env.EMAIL_SMTP_PORT === '465', secure: true,
auth: { auth: {
user: process.env.EMAIL_USER, user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASSWORD, pass: process.env.EMAIL_PASSWORD,
}, },
pool: true,
maxConnections: 3,
maxMessages: 5,
tls: { tls: {
rejectUnauthorized: false rejectUnauthorized: false
} }
@@ -30,26 +32,22 @@ const imapConfig = {
tls: true, tls: true,
tlsOptions: { rejectUnauthorized: false }, tlsOptions: { rejectUnauthorized: false },
keepalive: { keepalive: {
interval: 10000, // Проверка соединения каждые 10 секунд interval: 10000,
idleInterval: 300000, // Сброс соединения через 5 минут простоя idleInterval: 300000,
forceNoop: true // Принудительная отправка NOOP для поддержания соединения forceNoop: true
} }
}; };
class EmailBotService { class EmailBotService {
constructor(user, password) { constructor() {
this.user = user;
this.password = password;
this.transporter = transporter; this.transporter = transporter;
this.imap = new Imap(imapConfig); this.imap = new Imap(imapConfig);
this.initialize(); this.initialize();
this.listenForReplies();
} }
initialize() { initialize() {
this.imap.once('error', (err) => { this.imap.once('error', (err) => {
logger.error(`IMAP connection error: ${err.message}`); logger.error(`IMAP connection error: ${err.message}`);
// Пробуем переподключиться через 1 минуту при ошибке
setTimeout(() => { setTimeout(() => {
try { try {
if (this.imap.state !== 'connected') { if (this.imap.state !== 'connected') {
@@ -63,59 +61,52 @@ class EmailBotService {
}); });
} }
async sendVerificationCode(toEmail, userId) { // Метод для инициализации email верификации
async initEmailVerification(email, userId, code) {
try { try {
// Создаем код через сервис верификации // Отправляем код на email
const code = await verificationService.createVerificationCode( await this.sendVerificationCode(email, code);
'email',
toEmail.toLowerCase(),
userId
);
// Отправляем письмо с кодом return { success: true };
} catch (error) {
logger.error('Error initializing email verification:', error);
throw error;
}
}
// Отправка кода верификации
async sendVerificationCode(email, code) {
try {
const mailOptions = { const mailOptions = {
from: this.user, from: process.env.EMAIL_USER,
to: toEmail, to: email,
subject: 'Код подтверждения для DApp for Business', subject: 'Код подтверждения',
text: `Ваш код подтверждения: ${code}\n\nДля завершения аутентификации, пожалуйста, введите этот код на сайте.\n\nКод действителен в течение 15 минут.`, text: `Ваш код подтверждения: ${code}\n\nКод действителен в течение 15 минут.`,
html: ` html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px; border: 1px solid #ddd; border-radius: 5px;"> <div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #333;">Код подтверждения для DApp for Business</h2> <h2 style="color: #333;">Код подтверждения</h2>
<p>Ваш код подтверждения:</p> <p style="font-size: 16px; color: #666;">Ваш код подтверждения:</p>
<div style="font-size: 24px; font-weight: bold; padding: 15px; background-color: #f5f5f5; border-radius: 5px; text-align: center; margin: 20px 0;"> <div style="background-color: #f5f5f5; padding: 15px; border-radius: 5px; text-align: center; margin: 20px 0;">
${code} <span style="font-size: 24px; font-weight: bold; color: #333;">${code}</span>
</div> </div>
<p>Для завершения аутентификации, пожалуйста, введите этот код в форме на сайте.</p> <p style="font-size: 14px; color: #999;">Код действителен в течение 15 минут.</p>
<p>Код действителен в течение 15 минут.</p>
<hr style="margin: 20px 0; border: none; border-top: 1px solid #ddd;">
<p style="font-size: 12px; color: #777;">Это автоматическое сообщение, пожалуйста, не отвечайте на него.</p>
</div> </div>
` `
}; };
const info = await this.transporter.sendMail(mailOptions); await this.transporter.sendMail(mailOptions);
logger.info(`Email sent: ${info.messageId}`); logger.info(`Verification code sent to ${email}`);
return { success: true, code };
} catch (error) { } catch (error) {
logger.error(`Error sending email: ${error}`); logger.error('Error sending verification code:', error);
return { success: false, error: error.message }; throw error;
} }
} }
listenForReplies() {
// Запускаем проверку почты каждые 60 секунд
setInterval(() => {
this.checkEmails();
}, 60000);
}
checkEmails() { checkEmails() {
try { try {
// Добавляем обработчики ошибок // Добавляем обработчики ошибок
this.imap.once('error', (err) => { this.imap.once('error', (err) => {
logger.error(`IMAP connection error during check: ${err.message}`); logger.error(`IMAP connection error during check: ${err.message}`);
// Пытаемся закрыть соединение при ошибке
try { try {
this.imap.end(); this.imap.end();
} catch (e) { } catch (e) {
@@ -145,7 +136,6 @@ class EmailBotService {
return; return;
} }
// Защищаемся от пустых результатов
try { try {
const f = this.imap.fetch(results, { bodies: '' }); const f = this.imap.fetch(results, { bodies: '' });
@@ -156,23 +146,6 @@ class EmailBotService {
logger.error(`Error parsing message: ${err}`); logger.error(`Error parsing message: ${err}`);
return; 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(); this.imap.connect();
} catch (error) { } catch (error) {
logger.error(`Global error checking emails: ${error.message}`); logger.error(`Global error checking emails: ${error.message}`);
// Обеспечиваем корректное завершение IMAP сессии
try { try {
this.imap.end(); this.imap.end();
} catch (e) { } catch (e) {
@@ -212,74 +184,25 @@ class EmailBotService {
} }
} }
// Метод для обработки письма с помощью Ollama // Метод для отправки email
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());
}
// Оставляем существующий метод для отправки электронных писем
async sendEmail(to, subject, text) { async sendEmail(to, subject, text) {
try { try {
const mailOptions = { const mailOptions = {
from: this.user, from: process.env.EMAIL_USER,
to, to,
subject, subject,
text text
}; };
const info = await this.transporter.sendMail(mailOptions); await this.transporter.sendMail(mailOptions);
logger.info(`Email sent: ${info.messageId}`); logger.info(`Email sent to ${to}`);
return true; return true;
} catch (error) { } catch (error) {
logger.error(`Error sending email: ${error}`); logger.error('Error sending email:', error);
return false; throw error;
} }
} }
} }
module.exports = { // Экспортируем singleton instance
EmailBotService, module.exports = new EmailBotService();
transporter
};

View File

@@ -1,5 +1,7 @@
const { initTelegramBot } = require('./telegram-service'); 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 { const {
initializeVectorStore, initializeVectorStore,
getVectorStore, getVectorStore,
@@ -14,9 +16,9 @@ module.exports = {
initTelegramBot, initTelegramBot,
// Email // Email
initEmailBot, emailBot,
sendEmail, sendEmail: emailBot.sendEmail,
checkEmails, checkEmails: emailBot.checkEmails,
// Vector Store // Vector Store
initializeVectorStore, initializeVectorStore,
@@ -28,5 +30,7 @@ module.exports = {
processMessage, processMessage,
getUserInfo, getUserInfo,
getConversationHistory, getConversationHistory,
// ... другие экспорты
telegramBot,
aiAssistant
}; };

View File

@@ -9,7 +9,9 @@ class VerificationService {
// Генерация кода // Генерация кода
generateCode() { 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); const expiresAt = new Date(Date.now() + this.expirationMinutes * 60 * 1000);
try { try {
logger.info(`Creating verification code for ${provider}:${providerId}, userId: ${userId}`);
await db.query( await db.query(
`INSERT INTO verification_codes `INSERT INTO verification_codes
(code, provider, provider_id, user_id, expires_at) (code, provider, provider_id, user_id, expires_at)
@@ -25,9 +29,15 @@ class VerificationService {
[code, provider, providerId, userId, expiresAt] [code, provider, providerId, userId, expiresAt]
); );
logger.info(`Verification code created successfully for ${provider}:${providerId}`);
return code; return code;
} catch (error) { } catch (error) {
logger.error('Error creating verification code:', error); logger.error('Error creating verification code:', {
error: error.message,
provider,
providerId,
userId
});
throw error; throw error;
} }
} }
@@ -35,6 +45,28 @@ class VerificationService {
// Проверка кода // Проверка кода
async verifyCode(code, provider, providerId) { async verifyCode(code, provider, providerId) {
try { 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( const result = await db.query(
`SELECT * FROM verification_codes `SELECT * FROM verification_codes
WHERE code = $1 WHERE code = $1
@@ -42,10 +74,11 @@ class VerificationService {
AND provider_id = $3 AND provider_id = $3
AND used = false AND used = false
AND expires_at > NOW()`, AND expires_at > NOW()`,
[code, provider, providerId] [normalizedCode, provider, providerId]
); );
if (result.rows.length === 0) { if (result.rows.length === 0) {
logger.warn(`Invalid or expired code for ${provider}:${providerId}. Input: ${normalizedCode}`);
return { success: false, error: 'Неверный или истекший код' }; return { success: false, error: 'Неверный или истекший код' };
} }
@@ -57,13 +90,19 @@ class VerificationService {
[verification.id] [verification.id]
); );
logger.info(`Code verified successfully for ${provider}:${providerId}`);
return { return {
success: true, success: true,
userId: verification.user_id, userId: verification.user_id,
providerId: verification.provider_id providerId: verification.provider_id
}; };
} catch (error) { } catch (error) {
logger.error('Error verifying code:', error); logger.error('Error verifying code:', {
error: error.message,
code,
provider,
providerId
});
throw error; throw error;
} }
} }
@@ -71,13 +110,15 @@ class VerificationService {
// Очистка истекших кодов // Очистка истекших кодов
async cleanupExpiredCodes() { async cleanupExpiredCodes() {
try { try {
await db.query( const result = await db.query(
'DELETE FROM verification_codes WHERE expires_at <= NOW()' 'DELETE FROM verification_codes WHERE expires_at <= NOW() RETURNING id'
); );
logger.info(`Cleaned up ${result.rowCount} expired verification codes`);
} catch (error) { } catch (error) {
logger.error('Error cleaning up expired codes:', error); logger.error('Error cleaning up expired codes:', error);
} }
} }
} }
module.exports = new VerificationService(); const verificationService = new VerificationService();
module.exports = verificationService;

View File

@@ -376,3 +376,78 @@
.user-info-value { .user-info-value {
font-weight: bold; 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;
}

View File

@@ -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'
};
}
};

View File

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