const express = require('express'); const router = express.Router(); const { ethers } = require('ethers'); const crypto = require('crypto'); const db = require('../db'); const logger = require('../utils/logger'); const helmet = require('helmet'); const rateLimit = require('express-rate-limit'); const { checkRole, requireAuth } = require('../middleware/auth'); const { pool } = require('../db'); const { verifySignature, checkAccess, findOrCreateUser } = require('../utils/auth'); const authService = require('../services/auth-service'); const { SiweMessage } = require('siwe'); const { sendEmail } = require('../services/emailBot'); // Создайте лимитер для попыток аутентификации const authLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 минут max: 20, // Увеличьте лимит с 5 до 20 standardHeaders: true, legacyHeaders: false, message: { error: 'Слишком много попыток аутентификации. Попробуйте позже.' }, }); // Получение nonce для аутентификации router.get('/nonce', async (req, res) => { try { const { address } = req.query; if (!address) { return res.status(400).json({ error: 'Address is required' }); } // Генерируем случайный nonce const nonce = crypto.randomBytes(16).toString('hex'); // Удаляем старые nonce для этого адреса await db.query(` DELETE FROM nonces WHERE identity_value = $1 `, [address.toLowerCase()]); // Сохраняем новый nonce await db.query(` INSERT INTO nonces (identity_value, nonce, expires_at) VALUES ($1, $2, NOW() + INTERVAL '15 minutes') `, [address.toLowerCase(), nonce]); console.log(`Nonce ${nonce} сохранен для адреса ${address}`); return res.json({ nonce }); } catch (error) { console.error('Error generating nonce:', error); return res.status(500).json({ error: 'Internal server error' }); } }); // Функция для проверки роли пользователя async function checkUserRole(address, req) { try { const lowerCaseAddress = address.toLowerCase(); // Проверяем наличие токена доступа в базе данных const result = await db.query( 'SELECT role FROM access_tokens WHERE LOWER(wallet_address) = $1 AND expires_at > NOW()', [lowerCaseAddress] ); if (result.rows.length > 0) { // Если есть активный токен, проверяем роль const role = result.rows[0].role; return role === 'ADMIN'; } // Если нет токена, проверяем адрес администратора из переменных окружения const adminAddresses = (process.env.ADMIN_ADDRESSES || '') .split(',') .map((a) => a.toLowerCase()); return adminAddresses.includes(lowerCaseAddress); } catch (error) { console.error('Ошибка при проверке роли пользователя:', error); return false; } } // Проверка подписи и аутентификация router.post('/verify', async (req, res) => { try { const { address, signature, message } = req.body; console.log('Verify request: address=' + address + ', signature=' + signature.substring(0, 10) + '...'); // Получаем nonce из базы данных const nonceResult = await db.query(` SELECT nonce FROM nonces WHERE identity_value = $1 AND expires_at > NOW() AND used = false `, [address.toLowerCase()]); if (nonceResult.rows.length === 0) { console.error(`No valid nonce found for address ${address}`); return res.status(401).json({ error: 'Invalid or expired nonce' }); } const nonce = nonceResult.rows[0].nonce; console.log(`Found nonce ${nonce} for address ${address}`); // Проверяем подпись const isValid = await verifySignature(nonce, signature, address); console.log('Signature verification result:', isValid); if (!isValid) { return res.status(401).json({ error: 'Invalid signature' }); } // Помечаем nonce как использованный await db.query(` UPDATE nonces SET used = true WHERE identity_value = $1 `, [address.toLowerCase()]); // Находим или создаем пользователя console.log('Finding or creating user for address:', address); const { userId, isAdmin } = await findOrCreateUser(address); console.log('User found/created:', { userId, isAdmin }); // Устанавливаем пользователя в сессии req.session.userId = userId; req.session.address = address; req.session.isAdmin = isAdmin; req.session.authenticated = true; // Сохраняем сессию ПЕРЕД отправкой ответа await new Promise((resolve, reject) => { req.session.save(err => { if (err) { console.error('Error saving session:', err); reject(err); } else { console.log('Session saved successfully'); resolve(); } }); }); // Добавляем задержку для гарантии сохранения сессии (временное решение) await new Promise(resolve => setTimeout(resolve, 100)); console.log('Authentication successful for user:', { userId, address, isAdmin }); console.log('Session after save:', req.session); return res.json({ authenticated: true, userId, address, isAdmin, authType: 'wallet' }); } catch (error) { console.error('Error verifying signature:', error); return res.status(500).json({ error: 'Internal server error' }); } }); // Аутентификация через Telegram router.post('/telegram', async (req, res) => { try { const { telegramId, authData } = req.body; // Здесь должна быть проверка данных от Telegram // Получаем или создаем пользователя let userId = await authService.getUserIdByIdentity('telegram', telegramId); if (!userId) { // Создаем нового пользователя const userResult = await db.query( 'INSERT INTO users (created_at, role_id) VALUES (NOW(), (SELECT id FROM roles WHERE name = $1)) RETURNING id', ['user'] ); userId = userResult.rows[0].id; // Добавляем идентификатор Telegram await db.query( 'INSERT INTO user_identities (user_id, identity_type, identity_value, created_at) VALUES ($1, $2, $3, NOW())', [userId, 'telegram', telegramId] ); } // Проверяем связанные аккаунты const identitiesResult = await db.query(` SELECT identity_type, identity_value FROM user_identities ui WHERE user_id = $1 `, [userId]); const identities = identitiesResult.rows; // Если есть связанный кошелек, проверяем токены if (identities.wallet) { await authService.checkTokensAndUpdateRole(identities.wallet); } // Получаем текущую роль const isAdmin = await authService.isAdmin(userId); // Устанавливаем сессию req.session.userId = userId; req.session.telegramId = telegramId; req.session.authType = 'telegram'; req.session.authenticated = true; res.json({ authenticated: true, userId, isAdmin, authType: 'telegram', identities }); } catch (error) { logger.error(`Telegram auth error: ${error.message}`); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Маршрут для запроса кода подтверждения по email router.post('/email/request', async (req, res) => { try { const { email } = req.body; if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) { return res.status(400).json({ error: 'Invalid email format' }); } // Генерируем уникальный токен const token = crypto.randomBytes(20).toString('hex'); // Создаем или получаем ID пользователя let userId; if (req.session.authenticated && req.session.userId) { // Если пользователь уже аутентифицирован, используем его ID userId = req.session.userId; } else { // Создаем временного пользователя const userResult = await db.query( 'INSERT INTO users (created_at) VALUES (NOW()) RETURNING id' ); userId = userResult.rows[0].id; // Сохраняем ID в сессии как временный req.session.tempUserId = userId; } // Сохраняем токен в базе данных await db.query(` INSERT INTO email_auth_tokens (user_id, token, created_at, expires_at) VALUES ($1, $2, NOW(), NOW() + INTERVAL '15 minutes') `, [userId, token]); // Отправляем email с кодом подтверждения через emailBot const EmailBotService = require('../services/emailBot'); const emailBot = new EmailBotService(process.env.EMAIL_USER, process.env.EMAIL_PASSWORD); // Используем новый метод sendVerificationCode вместо sendEmail const result = await emailBot.sendVerificationCode(email, token); if (result.success) { // Сохраняем email в сессии для последующей верификации req.session.pendingEmail = email; // Сохраняем сессию await new Promise((resolve, reject) => { req.session.save(err => { if (err) reject(err); else resolve(); }); }); return res.json({ success: true, message: 'Verification code sent to your email', verificationCode: result.code // НЕ ОСТАВЛЯЙТЕ В PRODUCTION - только для отладки }); } else { return res.status(500).json({ error: 'Error sending email' }); } } catch (error) { console.error('Error requesting email verification:', error); res.status(500).json({ error: 'Internal server error' }); } }); // Маршрут для верификации email router.post('/email/verify', async (req, res) => { try { const { code } = req.body; const verificationData = req.session.emailVerificationData; // Проверяем, что код существует и не истек if (!verificationData || verificationData.code !== code || Date.now() > verificationData.expires) { return res.status(400).json({ success: false, error: 'Неверный или истекший код подтверждения' }); } const email = verificationData.email; // Ищем или создаем пользователя с этим email const result = await db.query( 'SELECT * FROM find_or_create_user_by_identity($1, $2)', ['email', email] ); const userId = result.rows[0].user_id; const isNew = result.rows[0].is_new; // Проверяем, есть ли у пользователя связанный кошелек const walletResult = await db.query(` SELECT identity_value FROM user_identities ui WHERE ui.user_id = $1 AND ui.identity_type = 'wallet' `, [userId]); const hasWallet = walletResult.rows.length > 0; let walletAddress = null; let isAdmin = false; // Если есть кошелек, проверяем наличие токенов if (hasWallet) { walletAddress = walletResult.rows[0].identity_value; const userResult = await db.query('SELECT is_admin FROM users WHERE id = $1', [userId]); isAdmin = userResult.rows[0].is_admin; } // Устанавливаем сессию req.session.authenticated = true; req.session.userId = userId; req.session.authType = 'email'; req.session.email = email; req.session.isAdmin = isAdmin; if (walletAddress) { req.session.address = walletAddress; } // Сохраняем сессию await new Promise((resolve, reject) => { req.session.save(err => { if (err) reject(err); else resolve(); }); }); // Очищаем данные верификации delete req.session.emailVerificationData; res.json({ success: true, authenticated: true, userId, email, isAdmin, hasWallet, walletAddress, isNew }); } catch (error) { logger.error(`Error in email verification: ${error.message}`); res.status(500).json({ success: false, error: 'Внутренняя ошибка сервера' }); } }); // Аутентификация через Email router.post('/email', async (req, res) => { try { const { email, verificationCode } = req.body; // Здесь должна быть проверка кода подтверждения // Получаем или создаем пользователя let userId = await authService.getUserIdByIdentity('email', email); if (!userId) { // Создаем нового пользователя const userResult = await db.query( 'INSERT INTO users (created_at, role_id) VALUES (NOW(), (SELECT id FROM roles WHERE name = $1)) RETURNING id', ['user'] ); userId = userResult.rows[0].id; // Добавляем идентификатор Email await db.query( 'INSERT INTO user_identities (user_id, identity_type, identity_value, created_at) VALUES ($1, $2, $3, NOW())', [userId, 'email', email] ); } // Получаем связанные идентификаторы const identitiesResult = await db.query(` SELECT identity_type, identity_value FROM user_identities ui WHERE ui.user_id = $1 `, [userId]); const identities = identitiesResult.rows; // Формируем объект с идентификаторами по типам const identitiesMap = {}; for (const identity of identities) { identitiesMap[identity.identity_type] = identity.identity_value; } // Проверяем, есть ли связанный кошелек let isAdmin = false; if (identitiesMap.wallet) { // Если есть связанный кошелек, проверяем токены const walletAddress = identitiesMap.wallet; isAdmin = await authService.checkAdminTokens(walletAddress); // Обновляем статус администратора в БД, если необходимо await db.query('UPDATE users SET is_admin = $1 WHERE id = $2', [isAdmin, userId]); } else { // Если нет связанного кошелька, проверяем текущий статус администратора isAdmin = await authService.isAdmin(userId); } // Устанавливаем сессию req.session.userId = userId; req.session.email = email; req.session.authType = 'email'; req.session.authenticated = true; req.session.isAdmin = isAdmin; if (identitiesMap.wallet) { req.session.address = identitiesMap.wallet; } // Сохраняем сессию перед отправкой ответа await new Promise((resolve, reject) => { req.session.save(err => { if (err) { console.error('Error saving session:', err); reject(err); } else { console.log('Session saved successfully'); resolve(); } }); }); res.json({ authenticated: true, userId, isAdmin, authType: 'email', identities: identitiesMap }); } catch (error) { logger.error(`Email auth error: ${error.message}`); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Связывание аккаунтов router.post('/link-identity', async (req, res) => { try { if (!req.session || !req.session.userId) { return res.status(401).json({ error: 'Требуется аутентификация' }); } const { identityType, identityValue } = req.body; // Проверяем, не привязан ли уже этот идентификатор к другому пользователю const existingUserId = await authService.getUserIdByIdentity(identityType, identityValue); if (existingUserId && existingUserId !== req.session.userId) { return res.status(400).json({ error: 'Этот идентификатор уже привязан к другому аккаунту' }); } // Добавляем новый идентификатор if (!existingUserId) { await db.query( 'INSERT INTO user_identities (user_id, identity_type, identity_value, created_at) VALUES ($1, $2, $3, NOW())', [req.session.userId, identityType, identityValue] ); } // Если добавлен кошелек, проверяем токены if (identityType === 'wallet') { await authService.checkTokensAndUpdateRole(identityValue); } // Получаем все идентификаторы пользователя const identitiesResult = await db.query(` SELECT identity_type, identity_value FROM user_identities WHERE user_id = $1 `, [req.session.userId]); const identities = identitiesResult.rows; // Получаем текущую роль const isAdmin = await authService.isAdmin(req.session.userId); res.json({ success: true, identities, isAdmin }); } catch (error) { logger.error(`Link identity error: ${error.message}`); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Проверка аутентификации router.get('/check', (req, res) => { try { console.log('Сессия при проверке:', req.session); if (req.session && req.session.authenticated) { return res.json({ authenticated: true, userId: req.session.userId, address: req.session.address, isAdmin: req.session.isAdmin, authType: req.session.authType || 'wallet' }); } else { return res.json({ authenticated: false }); } } catch (error) { console.error('Ошибка при проверке аутентификации:', error); return res.status(500).json({ error: 'Internal server error' }); } }); // Выход из системы router.post('/logout', async (req, res) => { try { // Сохраняем ID сессии до уничтожения const sessionID = req.sessionID; // Уничтожаем сессию req.session.destroy(); // Удаляем сессию из базы данных try { const { pool } = require('../db'); await pool.query('DELETE FROM session WHERE sid = $1', [sessionID]); console.log(`Сессия ${sessionID} удалена из базы данных`); } catch (dbError) { console.error('Ошибка при удалении сессии из базы данных:', dbError); } // Очищаем куки res.clearCookie('connect.sid'); res.json({ success: true }); } catch (error) { console.error('Ошибка при выходе из системы:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Маршрут для авторизации через Telegram router.get('/telegram', (req, res) => { // Генерируем случайный токен для авторизации const token = crypto.randomBytes(32).toString('hex'); // Сохраняем токен в сессии req.session.telegramToken = token; // Создаем URL для авторизации через Telegram const botName = process.env.TELEGRAM_BOT_NAME || 'YourBotName'; const authUrl = `https://t.me/${botName}?start=${token}`; res.json({ authUrl }); }); // Маршрут для получения кода подтверждения Telegram router.get('/telegram/code', async (req, res) => { try { // Генерируем код подтверждения (6 символов) const verificationCode = Math.random().toString(36).substring(2, 8).toUpperCase(); // Сохраняем код в сессии req.session.telegramVerificationData = { code: verificationCode, expires: Date.now() + 10 * 60 * 1000 // 10 минут }; res.json({ success: true, message: 'Отправьте этот код боту @' + process.env.TELEGRAM_BOT_USERNAME, code: verificationCode, botUsername: process.env.TELEGRAM_BOT_USERNAME || 'YourDAppBot' }); } catch (error) { logger.error(`Error in telegram code request: ${error.message}`); res.status(500).json({ success: false, error: 'Внутренняя ошибка сервера' }); } }); // Маршрут для верификации Telegram router.post('/telegram/verify', async (req, res) => { try { const { telegramId, code } = req.body; const verificationData = req.session.telegramVerificationData; // Проверяем, что код существует и не истек if (!verificationData || verificationData.code !== code || Date.now() > verificationData.expires) { return res.status(400).json({ success: false, error: 'Неверный или истекший код подтверждения' }); } // Ищем или создаем пользователя с этим Telegram ID const result = await db.query( 'SELECT * FROM find_or_create_user_by_identity($1, $2)', ['telegram', telegramId] ); const userId = result.rows[0].user_id; const isNew = result.rows[0].is_new; // Проверяем, есть ли у пользователя связанный кошелек const walletResult = await db.query(` SELECT identity_value FROM user_identities ui WHERE ui.user_id = $1 AND ui.identity_type = 'wallet' `, [userId]); const hasWallet = walletResult.rows.length > 0; let walletAddress = null; let isAdmin = false; // Если есть кошелек, проверяем наличие токенов if (hasWallet) { walletAddress = walletResult.rows[0].identity_value; const userResult = await db.query('SELECT is_admin FROM users WHERE id = $1', [userId]); isAdmin = userResult.rows[0].is_admin; } // Устанавливаем сессию req.session.authenticated = true; req.session.userId = userId; req.session.authType = 'telegram'; req.session.telegramId = telegramId; req.session.isAdmin = isAdmin; if (walletAddress) { req.session.address = walletAddress; } // Сохраняем сессию await new Promise((resolve, reject) => { req.session.save(err => { if (err) reject(err); else resolve(); }); }); // Очищаем данные верификации delete req.session.telegramVerificationData; res.json({ success: true, authenticated: true, userId, telegramId, isAdmin, hasWallet, walletAddress, isNew }); } catch (error) { logger.error(`Error in telegram verification: ${error.message}`); res.status(500).json({ success: false, error: 'Внутренняя ошибка сервера' }); } }); // Маршрут для связывания разных идентификаторов router.post('/link-identity', requireAuth, async (req, res) => { try { const { type, value } = req.body; const userId = req.session.userId; // Проверяем валидность типа if (!['wallet', 'email', 'telegram'].includes(type)) { return res.status(400).json({ success: false, error: 'Неподдерживаемый тип идентификатора' }); } // Проверяем, не связан ли идентификатор с другим пользователем const existingResult = await db.query(` SELECT ui.user_id FROM user_identities ui WHERE ui.identity_type = $1 AND ui.identity_value = $2 `, [type, value]); if (existingResult.rows.length > 0 && existingResult.rows[0].user_id !== userId) { return res.status(400).json({ success: false, error: 'Этот идентификатор уже связан с другим аккаунтом' }); } // Добавляем или обновляем идентификатор await db.query(` INSERT INTO user_identities (user_id, identity_type, identity_value, created_at, verified) VALUES ($1, $2, $3, NOW(), true) ON CONFLICT (identity_type, identity_value) DO UPDATE SET verified = true `, [userId, type, value]); // Если связываем кошелек, обновляем также поле address в таблице users if (type === 'wallet') { await db.query('UPDATE users SET address = $1 WHERE id = $2', [value, userId]); // Проверяем наличие токенов для статуса админа const isAdmin = await authService.checkAdminTokens(value); if (isAdmin) { await db.query('UPDATE users SET is_admin = true WHERE id = $1', [userId]); req.session.isAdmin = true; } req.session.address = value; } // Если связываем email, обновляем сессию if (type === 'email') { req.session.email = value; } // Если связываем telegram, обновляем сессию if (type === 'telegram') { req.session.telegramId = value; } // Сохраняем сессию await new Promise((resolve, reject) => { req.session.save(err => { if (err) reject(err); else resolve(); }); }); res.json({ success: true, message: `Идентификатор успешно связан с вашим аккаунтом`, isAdmin: req.session.isAdmin }); } catch (error) { logger.error(`Error linking identity: ${error.message}`); res.status(500).json({ success: false, error: 'Внутренняя ошибка сервера' }); } }); // Добавляем маршрут для проверки прав доступа router.get('/check-access', requireAuth, (req, res) => { try { // Получаем информацию о пользователе const userData = { address: req.session.address, isAdmin: req.session.isAdmin || false, roles: req.session.roles || [], authenticated: true, }; // Проверяем доступ к различным разделам const access = { dashboard: true, // Все аутентифицированные пользователи имеют доступ к панели управления admin: userData.isAdmin, // Только администраторы имеют доступ к админке contracts: userData.roles.includes('CONTRACT_MANAGER') || userData.isAdmin, users: userData.roles.includes('USER_MANAGER') || userData.isAdmin, }; res.json({ user: userData, access: access, }); } catch (error) { console.error('Ошибка при проверке прав доступа:', error); res.status(500).json({ error: 'Internal server error' }); } }); // Обновление сессии router.post('/refresh-session', async (req, res) => { try { const { address } = req.body; if (req.session && req.session.authenticated) { console.log('Обновление сессии для пользователя:', req.session.userId); // Обновляем время жизни сессии req.session.cookie.maxAge = 30 * 24 * 60 * 60 * 1000; // 30 дней // Сохраняем обновленную сессию await new Promise((resolve, reject) => { req.session.save(err => { if (err) { console.error('Ошибка при сохранении сессии:', err); reject(err); } else { console.log('Сессия успешно обновлена'); resolve(); } }); }); return res.json({ success: true }); } else if (address) { // Если сессия не аутентифицирована, но есть адрес try { const { pool } = require('../db'); const result = await pool.query('SELECT * FROM users WHERE address = $1', [address]); if (result.rows.length > 0) { const user = result.rows[0]; // Обновляем сессию req.session.authenticated = true; req.session.userId = user.id; req.session.address = address; req.session.isAdmin = user.is_admin; req.session.authType = 'wallet'; // Сохраняем обновленную сессию await new Promise((resolve, reject) => { req.session.save(err => { if (err) { console.error('Ошибка при сохранении сессии:', err); reject(err); } else { console.log('Сессия успешно обновлена'); resolve(); } }); }); return res.json({ success: true }); } } catch (error) { console.error('Ошибка при проверке пользователя:', error); } } // Если не удалось обновить сессию, возвращаем успех=false, но не ошибку return res.json({ success: false }); } catch (error) { console.error('Ошибка при обновлении сессии:', error); res.status(500).json({ error: 'Ошибка сервера' }); } }); // Маршрут для обновления статуса администратора router.post('/update-admin-status', async (req, res) => { try { const { address, isAdmin } = req.body; if (!address) { return res.status(400).json({ error: 'Address is required' }); } console.log(`Запрос на обновление статуса администратора для адреса ${address} на ${isAdmin}`); // Проверяем, существует ли пользователь const userResult = await db.query('SELECT * FROM users WHERE address = $1', [ address.toLowerCase(), ]); if (userResult.rows.length === 0) { // Если пользователь не найден, создаем его await db.query('INSERT INTO users (address, is_admin, created_at) VALUES ($1, $2, NOW())', [ address.toLowerCase(), isAdmin, ]); console.log( `Создан новый пользователь с адресом ${address} и статусом администратора ${isAdmin}` ); } else { // Если пользователь найден, обновляем его статус await db.query('UPDATE users SET is_admin = $1 WHERE address = $2', [ isAdmin, address.toLowerCase(), ]); console.log( `Создан новый пользователь с адресом ${address} и статусом администратора ${isAdmin}` ); } res.json({ success: true }); } catch (error) { console.error('Ошибка при обновлении статуса администратора:', error); res.status(500).json({ error: 'Internal server error' }); } }); // Маршрут для создания токена авторизации через Email router.post('/email/auth-token', async (req, res) => { try { const { email } = req.body; if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) { return res.status(400).json({ success: false, error: 'Неверный формат email' }); } // Генерируем уникальный токен const token = crypto.randomBytes(20).toString('hex'); // Получаем ID пользователя из сессии или создаем нового гостевого пользователя let userId; if (req.session.authenticated && req.session.userId) { // Если пользователь уже аутентифицирован, используем его ID userId = req.session.userId; } else { // Создаем временного пользователя const userResult = await db.query( 'INSERT INTO users (created_at) VALUES (NOW()) RETURNING id' ); userId = userResult.rows[0].id; // Сохраняем ID в сессии как временный req.session.tempUserId = userId; } // Сохраняем токен в базе данных await db.query(` INSERT INTO email_auth_tokens (user_id, token, created_at, expires_at) VALUES ($1, $2, NOW(), NOW() + INTERVAL '15 minutes') `, [userId, token]); // Отправляем email с кодом подтверждения через emailBot const emailBot = require('../services/emailBot'); const emailService = new emailBot(process.env.EMAIL_USER, process.env.EMAIL_PASSWORD); const sendResult = await emailService.sendVerificationCode(email, token); if (sendResult.success) { res.json({ success: true, message: 'Код подтверждения отправлен на ваш email' }); } else { res.status(500).json({ success: false, error: 'Ошибка отправки email' }); } } catch (error) { logger.error(`Error creating Email auth token: ${error.message}`); res.status(500).json({ success: false, error: 'Ошибка сервера' }); } }); // Маршрут для проверки статуса аутентификации через Email router.get('/email/auth-status/:token', async (req, res) => { try { const { token } = req.params; // Проверяем статус токена const tokenResult = await db.query(` SELECT user_id, used FROM email_auth_tokens WHERE token = $1 AND expires_at > NOW() `, [token]); if (tokenResult.rows.length === 0) { return res.json({ success: false, error: 'Токен не найден или истек' }); } const userId = tokenResult.rows[0].user_id; const isAuthenticated = tokenResult.rows[0].used; if (isAuthenticated) { // Токен использован, email подключен // Получаем email пользователя const emailResult = await db.query(` SELECT ui.identity_value FROM user_identities ui WHERE ui.user_id = $1 AND ui.identity_type = 'email' `, [userId]); if (emailResult.rows.length > 0) { // Устанавливаем полную аутентификацию в сессии req.session.authenticated = true; req.session.userId = userId; req.session.email = emailResult.rows[0].identity_value; req.session.authType = 'email'; // Если был временный ID, удаляем его if (req.session.tempUserId) { delete req.session.tempUserId; } // Сохраняем сессию await new Promise((resolve, reject) => { req.session.save(err => { if (err) reject(err); else resolve(); }); }); } } res.json({ success: true, authenticated: isAuthenticated }); } catch (error) { logger.error(`Error checking Email auth status: ${error.message}`); res.status(500).json({ success: false, error: 'Ошибка сервера' }); } }); // Маршрут для прямой проверки кода, введенного пользователем router.post('/email/verify-code', async (req, res) => { try { const { email, code } = req.body; if (!email || !code) { return res.status(400).json({ success: false, error: 'Email и код обязательны' }); } const EmailBotService = require('../services/emailBot'); const emailBot = new EmailBotService(process.env.EMAIL_USER, process.env.EMAIL_PASSWORD); // Проверяем код из хранилища const verificationData = emailBot.verificationCodes.get(email.toLowerCase()); if (!verificationData) { return res.status(400).json({ success: false, error: 'Код подтверждения не найден' }); } if (Date.now() > verificationData.expires) { emailBot.verificationCodes.delete(email.toLowerCase()); return res.status(400).json({ success: false, error: 'Срок действия кода истек' }); } if (verificationData.code !== code) { return res.status(400).json({ success: false, error: 'Неверный код подтверждения' }); } // Код верный, завершаем аутентификацию const token = verificationData.token; // Получаем информацию о токене const tokenResult = await db.query( 'SELECT user_id FROM email_auth_tokens WHERE token = $1', [token] ); if (tokenResult.rows.length === 0) { return res.status(500).json({ success: false, error: 'Токен не найден' }); } const userId = tokenResult.rows[0].user_id; // Добавляем email в базу данных 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, 'email', email.toLowerCase()] ); // Отмечаем токен как использованный await db.query( 'UPDATE email_auth_tokens SET used = true WHERE token = $1', [token] ); // Устанавливаем аутентификацию пользователя req.session.authenticated = true; req.session.userId = userId; req.session.email = email.toLowerCase(); req.session.authType = 'email'; // Если был временный ID, удаляем его if (req.session.tempUserId) { delete req.session.tempUserId; } // Сохраняем сессию await new Promise((resolve, reject) => { req.session.save(err => { if (err) reject(err); else resolve(); }); }); // Удаляем код из хранилища emailBot.verificationCodes.delete(email.toLowerCase()); return res.json({ success: true, userId, email: email.toLowerCase(), message: 'Аутентификация успешна' }); } catch (error) { logger.error(`Error verifying email code: ${error.message}`); return res.status(500).json({ success: false, error: 'Ошибка сервера' }); } }); module.exports = router;