Описание изменений
This commit is contained in:
@@ -47,6 +47,7 @@
|
|||||||
"pg": "^8.10.0",
|
"pg": "^8.10.0",
|
||||||
"session-file-store": "^1.5.0",
|
"session-file-store": "^1.5.0",
|
||||||
"siwe": "^2.1.4",
|
"siwe": "^2.1.4",
|
||||||
|
"telegraf": "^4.16.3",
|
||||||
"winston": "^3.17.0"
|
"winston": "^3.17.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ const { sendEmail } = 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 { initEmailAuth, verifyEmailCode } = require('../services/emailBot');
|
||||||
|
const { getBot } = require('../services/telegramBot');
|
||||||
|
|
||||||
// Создайте лимитер для попыток аутентификации
|
// Создайте лимитер для попыток аутентификации
|
||||||
const authLimiter = rateLimit({
|
const authLimiter = rateLimit({
|
||||||
@@ -34,24 +37,32 @@ router.get('/nonce', async (req, res) => {
|
|||||||
// Генерируем случайный nonce
|
// Генерируем случайный nonce
|
||||||
const nonce = crypto.randomBytes(16).toString('hex');
|
const nonce = crypto.randomBytes(16).toString('hex');
|
||||||
|
|
||||||
// Удаляем старые nonce для этого адреса
|
// Проверяем, существует ли уже nonce для этого адреса
|
||||||
await db.query(`
|
const existingNonce = await db.query(
|
||||||
DELETE FROM nonces
|
'SELECT id FROM nonces WHERE identity_value = $1',
|
||||||
WHERE identity_value = $1
|
[address.toLowerCase()]
|
||||||
`, [address.toLowerCase()]);
|
);
|
||||||
|
|
||||||
// Сохраняем новый nonce
|
if (existingNonce.rows.length > 0) {
|
||||||
await db.query(`
|
// Обновляем существующий nonce
|
||||||
INSERT INTO nonces (identity_value, nonce, expires_at)
|
await db.query(
|
||||||
VALUES ($1, $2, NOW() + INTERVAL '15 minutes')
|
'UPDATE nonces SET nonce = $1, expires_at = NOW() + INTERVAL \'15 minutes\' WHERE identity_value = $2',
|
||||||
`, [address.toLowerCase(), nonce]);
|
[nonce, address.toLowerCase()]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Создаем новый 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}`);
|
console.log(`Nonce ${nonce} сохранен для адреса ${address}`);
|
||||||
|
|
||||||
return res.json({ nonce });
|
res.json({ nonce });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error generating nonce:', error);
|
console.error('Error generating nonce:', error);
|
||||||
return res.status(500).json({ error: 'Internal server error' });
|
res.status(500).json({ error: 'Failed to generate nonce' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -60,77 +71,85 @@ const ERC20_ABI = [
|
|||||||
"function balanceOf(address owner) view returns (uint256)"
|
"function balanceOf(address owner) view returns (uint256)"
|
||||||
];
|
];
|
||||||
|
|
||||||
// Проверка подписи и аутентификация
|
// Верификация подписи и создание сессии
|
||||||
router.post('/verify', async (req, res) => {
|
router.post('/verify', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { address, signature, message } = req.body;
|
const { address, message, signature } = req.body;
|
||||||
console.log('Received verification request:', { address, message });
|
|
||||||
console.log('Signature:', signature);
|
|
||||||
|
|
||||||
// Проверяем подпись через SIWE
|
// Проверяем подпись
|
||||||
const siwe = new SiweMessage(message);
|
const isValid = await authService.verifySignature(message, signature, address);
|
||||||
console.log('Created SIWE message object');
|
if (!isValid) {
|
||||||
|
return res.status(401).json({ success: false, error: 'Invalid signature' });
|
||||||
const fields = await siwe.verify({ signature });
|
|
||||||
console.log('SIWE validation result:', fields);
|
|
||||||
|
|
||||||
if (!fields || !fields.success) {
|
|
||||||
console.log('SIWE validation failed');
|
|
||||||
return res.status(401).json({ error: 'Invalid signature' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем nonce
|
// Проверяем nonce
|
||||||
const nonceResult = await db.query(
|
const nonceResult = await db.query('SELECT nonce FROM nonces WHERE identity_value = $1', [address.toLowerCase()]);
|
||||||
`SELECT nonce FROM nonces
|
if (nonceResult.rows.length === 0 || nonceResult.rows[0].nonce !== message.match(/Nonce: ([^\n]+)/)[1]) {
|
||||||
WHERE identity_value = $1
|
return res.status(401).json({ success: false, error: 'Invalid nonce' });
|
||||||
AND expires_at > NOW()`,
|
|
||||||
[address.toLowerCase()]
|
|
||||||
);
|
|
||||||
console.log('Nonce check result:', nonceResult.rows);
|
|
||||||
|
|
||||||
if (!nonceResult.rows.length) {
|
|
||||||
console.log('Invalid or expired nonce');
|
|
||||||
return res.status(401).json({ error: 'Invalid or expired nonce' });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем соответствие nonce
|
// Находим или создаем пользователя
|
||||||
if (nonceResult.rows[0].nonce !== fields.data.nonce) {
|
let userId, isAdmin;
|
||||||
console.log('Nonce mismatch');
|
|
||||||
return res.status(401).json({ error: 'Invalid nonce' });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Получаем или создаем пользователя
|
// Ищем пользователя по адресу в таблице user_identities
|
||||||
const userResult = await db.query(
|
const userResult = await db.query(`
|
||||||
`INSERT INTO users (address, created_at, updated_at)
|
SELECT u.* FROM users u
|
||||||
VALUES ($1, NOW(), NOW())
|
JOIN user_identities ui ON u.id = ui.user_id
|
||||||
ON CONFLICT (address) DO UPDATE
|
WHERE ui.provider = 'wallet' AND ui.provider_id = $1
|
||||||
SET updated_at = NOW()
|
`, [address.toLowerCase()]);
|
||||||
RETURNING id, role = 'admin' as is_admin`,
|
|
||||||
[address]
|
if (userResult.rows.length > 0) {
|
||||||
|
// Пользователь найден
|
||||||
|
userId = userResult.rows[0].id;
|
||||||
|
isAdmin = userResult.rows[0].role === 'admin';
|
||||||
|
const address = userResult.rows[0].address;
|
||||||
|
} else {
|
||||||
|
// Создаем нового пользователя
|
||||||
|
const newUserResult = await db.query(
|
||||||
|
'INSERT INTO users (role) VALUES ($1) RETURNING id',
|
||||||
|
['user']
|
||||||
);
|
);
|
||||||
|
|
||||||
const userId = userResult.rows[0].id;
|
userId = newUserResult.rows[0].id;
|
||||||
const isAdmin = false; // Будет обновлено в createSession
|
isAdmin = false;
|
||||||
|
|
||||||
// Используем централизованный сервис
|
// Добавляем идентификатор кошелька
|
||||||
await authService.createSession(req, {
|
await db.query(
|
||||||
|
'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)',
|
||||||
|
[userId, 'wallet', address.toLowerCase()]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Обновляем сессию
|
||||||
|
req.session.userId = userId;
|
||||||
|
req.session.authenticated = true;
|
||||||
|
req.session.authType = 'wallet';
|
||||||
|
req.session.isAdmin = isAdmin;
|
||||||
|
req.session.address = address.toLowerCase();
|
||||||
|
|
||||||
|
// Сохраняем сессию
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
req.session.save((err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Возвращаем успешный ответ
|
||||||
|
return res.json({
|
||||||
|
success: true,
|
||||||
userId,
|
userId,
|
||||||
address,
|
address,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
authType: 'wallet',
|
authenticated: true
|
||||||
guestId: req.session.guestId
|
|
||||||
});
|
});
|
||||||
|
|
||||||
res.json({
|
|
||||||
authenticated: true,
|
|
||||||
userId,
|
|
||||||
address,
|
|
||||||
isAdmin,
|
|
||||||
authType: 'wallet'
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error:', error);
|
console.error('Error in /verify:', error);
|
||||||
res.status(500).json({ error: 'Internal server error' });
|
res.status(500).json({ success: false, error: 'Server error' });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -488,54 +507,106 @@ router.post('/link-identity', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Проверка аутентификации
|
// Проверка статуса аутентификации
|
||||||
router.get('/check', (req, res) => {
|
router.get('/check', async (req, res) => {
|
||||||
try {
|
|
||||||
console.log('Сессия при проверке:', req.session);
|
console.log('Сессия при проверке:', req.session);
|
||||||
|
|
||||||
if (req.session && req.session.authenticated) {
|
let telegramId = null;
|
||||||
return res.json({
|
|
||||||
authenticated: true,
|
if (req.session.userId && req.session.authType === 'telegram') {
|
||||||
userId: req.session.userId,
|
// Проверяем, есть ли telegramId в сессии
|
||||||
address: req.session.address,
|
if (req.session.telegramId) {
|
||||||
isAdmin: req.session.isAdmin,
|
telegramId = req.session.telegramId;
|
||||||
authType: req.session.authType || 'wallet'
|
console.log('Telegram ID from session:', telegramId);
|
||||||
});
|
|
||||||
} else {
|
// Проверяем, есть ли запись в базе данных
|
||||||
return res.json({ authenticated: false });
|
try {
|
||||||
|
const result = await db.query(
|
||||||
|
'SELECT provider_id FROM user_identities WHERE user_id = $1 AND provider = $2',
|
||||||
|
[req.session.userId, 'telegram']
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('Telegram ID query result:', result.rows);
|
||||||
|
|
||||||
|
if (result.rows.length === 0) {
|
||||||
|
// Если нет, добавляем запись
|
||||||
|
try {
|
||||||
|
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 NOTHING',
|
||||||
|
[req.session.userId, 'telegram', telegramId]
|
||||||
|
);
|
||||||
|
console.log('Added Telegram ID to database:', telegramId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding Telegram ID to database:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка при проверке аутентификации:', error);
|
console.error('Error checking Telegram ID in database:', error);
|
||||||
return res.status(500).json({ error: 'Internal server error' });
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// Если нет, ищем в базе данных
|
||||||
|
try {
|
||||||
|
const result = await db.query(
|
||||||
|
'SELECT provider_id FROM user_identities WHERE user_id = $1 AND provider = $2',
|
||||||
|
[req.session.userId, 'telegram']
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log('Telegram ID query result:', result.rows);
|
||||||
|
|
||||||
|
if (result.rows.length > 0) {
|
||||||
|
telegramId = result.rows[0].provider_id;
|
||||||
|
console.log('Telegram ID from database:', telegramId);
|
||||||
|
|
||||||
|
// Сохраняем в сессию для будущих запросов
|
||||||
|
req.session.telegramId = telegramId;
|
||||||
|
} else {
|
||||||
|
// Если нет в базе данных, используем фиксированное значение
|
||||||
|
telegramId = 'Telegram User';
|
||||||
|
console.log('Using fixed Telegram ID:', telegramId);
|
||||||
|
|
||||||
|
// Сохраняем в сессию для будущих запросов
|
||||||
|
req.session.telegramId = telegramId;
|
||||||
|
|
||||||
|
// Добавляем запись в базу данных
|
||||||
|
try {
|
||||||
|
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 NOTHING',
|
||||||
|
[req.session.userId, 'telegram', telegramId]
|
||||||
|
);
|
||||||
|
console.log('Added Telegram ID to database:', telegramId);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding Telegram ID to database:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching Telegram ID:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = {
|
||||||
|
authenticated: !!req.session.authenticated,
|
||||||
|
userId: req.session.userId,
|
||||||
|
isAdmin: !!req.session.isAdmin,
|
||||||
|
authType: req.session.authType,
|
||||||
|
address: req.session.address,
|
||||||
|
telegramId: telegramId
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('Auth check response:', response);
|
||||||
|
|
||||||
|
res.json(response);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Выход из системы
|
// Выход из системы
|
||||||
router.post('/logout', async (req, res) => {
|
router.post('/logout', (req, res) => {
|
||||||
try {
|
req.session.destroy((err) => {
|
||||||
// Сохраняем ID сессии до уничтожения
|
if (err) {
|
||||||
const sessionID = req.sessionID;
|
console.error('Error destroying session:', err);
|
||||||
|
return res.status(500).json({ error: 'Failed to logout' });
|
||||||
// Уничтожаем сессию
|
|
||||||
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 });
|
res.json({ success: true });
|
||||||
} catch (error) {
|
});
|
||||||
console.error('Ошибка при выходе из системы:', error);
|
|
||||||
res.status(500).json({ error: 'Ошибка сервера' });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Маршрут для авторизации через Telegram
|
// Маршрут для авторизации через Telegram
|
||||||
@@ -626,12 +697,16 @@ async function checkTokenBalance(address) {
|
|||||||
|
|
||||||
// Маршрут для верификации Telegram
|
// Маршрут для верификации Telegram
|
||||||
router.post('/telegram/verify', async (req, res) => {
|
router.post('/telegram/verify', async (req, res) => {
|
||||||
|
console.log('Telegram verification request body:', req.body);
|
||||||
|
|
||||||
const { code } = req.body;
|
const { code } = req.body;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const telegramBot = require('../services/telegramBot');
|
const telegramBot = getBot();
|
||||||
const result = await telegramBot.verifyCode(code);
|
const result = await telegramBot.verifyCode(code);
|
||||||
|
|
||||||
|
console.log('Telegram verification result:', result);
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
// Проверяем, что у нас есть telegramId
|
// Проверяем, что у нас есть telegramId
|
||||||
if (!result.telegramId) {
|
if (!result.telegramId) {
|
||||||
@@ -678,6 +753,18 @@ router.post('/telegram/verify', async (req, res) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('Telegram ID saved in session:', req.session.telegramId);
|
||||||
|
|
||||||
|
// Сохраняем идентификатор Telegram в базе данных
|
||||||
|
try {
|
||||||
|
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, 'telegram', result.telegramId]
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving Telegram ID to database:', error);
|
||||||
|
}
|
||||||
|
|
||||||
return res.json({
|
return res.json({
|
||||||
success: true,
|
success: true,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
@@ -1130,4 +1217,64 @@ router.post('/clear-session', async (req, res) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Инициализация Telegram аутентификации
|
||||||
|
router.post('/telegram/init', async (req, res) => {
|
||||||
|
try {
|
||||||
|
// Проверяем, есть ли уже привязанный Telegram
|
||||||
|
if (req.session?.userId) {
|
||||||
|
const existingTelegram = await db.query(
|
||||||
|
`SELECT provider_id
|
||||||
|
FROM user_identities
|
||||||
|
WHERE user_id = $1
|
||||||
|
AND provider = 'telegram'`,
|
||||||
|
[req.session.userId]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existingTelegram.rows.length > 0) {
|
||||||
|
return res.status(400).json({
|
||||||
|
error: 'Telegram already linked to this account'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const { verificationCode, botLink } = await initTelegramAuth(req.session);
|
||||||
|
res.json({ verificationCode, botLink });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error initializing Telegram auth:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to initialize Telegram auth' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Инициализация Email аутентификации
|
||||||
|
router.post('/email/init', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { email } = req.body;
|
||||||
|
if (!email) {
|
||||||
|
return res.status(400).json({ error: 'Email is required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
await initEmailAuth(email);
|
||||||
|
res.json({ success: true });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error initializing email auth:', error);
|
||||||
|
res.status(500).json({ error: 'Failed to send verification code' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Проверка кода подтверждения email
|
||||||
|
router.post('/email/verify', requireAuth, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { code } = req.body;
|
||||||
|
if (!code) {
|
||||||
|
return res.status(400).json({ error: 'Verification code is required' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await verifyEmailCode(code, req.session.userId);
|
||||||
|
res.json(result);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error verifying email code:', error);
|
||||||
|
res.status(400).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
@@ -10,7 +10,7 @@ const authRouter = require('./routes/auth');
|
|||||||
const identitiesRouter = require('./routes/identities');
|
const identitiesRouter = require('./routes/identities');
|
||||||
const { pool } = require('./db');
|
const { pool } = require('./db');
|
||||||
const helmet = require('helmet');
|
const helmet = require('helmet');
|
||||||
const TelegramBotService = require('./services/telegramBot');
|
const { getBot, stopBot } = require('./services/telegramBot');
|
||||||
const pgSession = require('connect-pg-simple')(session);
|
const pgSession = require('connect-pg-simple')(session);
|
||||||
const authService = require('./services/auth-service');
|
const authService = require('./services/auth-service');
|
||||||
const logger = require('./utils/logger');
|
const logger = require('./utils/logger');
|
||||||
@@ -26,10 +26,31 @@ async function initServices() {
|
|||||||
try {
|
try {
|
||||||
console.log('Инициализация сервисов...');
|
console.log('Инициализация сервисов...');
|
||||||
|
|
||||||
if (process.env.TELEGRAM_BOT_TOKEN) {
|
// Останавливаем предыдущий экземпляр бота
|
||||||
const telegramBot = new TelegramBotService(process.env.TELEGRAM_BOT_TOKEN);
|
await stopBot();
|
||||||
global.telegramBot = telegramBot; // Сохраняем экземпляр глобально
|
|
||||||
console.log('Telegram бот инициализирован');
|
// Добавляем обработку ошибок при запуске бота
|
||||||
|
try {
|
||||||
|
await getBot(); // getBot теперь асинхронный и сам запускает бота
|
||||||
|
console.log('Telegram bot started');
|
||||||
|
|
||||||
|
// Добавляем graceful shutdown
|
||||||
|
process.once('SIGINT', async () => {
|
||||||
|
await stopBot();
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
process.once('SIGTERM', async () => {
|
||||||
|
await stopBot();
|
||||||
|
process.exit(0);
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === 409) {
|
||||||
|
logger.warn('Another instance of Telegram bot is running. This is normal during development with nodemon');
|
||||||
|
// Просто логируем ошибку и продолжаем работу
|
||||||
|
// Бот будет запущен при следующем перезапуске
|
||||||
|
} else {
|
||||||
|
logger.error('Error launching Telegram bot:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Все сервисы успешно инициализированы');
|
console.log('Все сервисы успешно инициализированы');
|
||||||
|
|||||||
@@ -44,39 +44,41 @@ class AuthService {
|
|||||||
*/
|
*/
|
||||||
async findOrCreateUser(address) {
|
async findOrCreateUser(address) {
|
||||||
try {
|
try {
|
||||||
const existingUser = await db.query(
|
// Нормализуем адрес
|
||||||
`SELECT u.id
|
address = ethers.getAddress(address);
|
||||||
FROM users u
|
|
||||||
JOIN user_identities ui ON u.id = ui.user_id
|
|
||||||
WHERE ui.provider = 'wallet'
|
|
||||||
AND LOWER(ui.provider_id) = LOWER($1)`,
|
|
||||||
[address]
|
|
||||||
);
|
|
||||||
|
|
||||||
if (existingUser.rows.length > 0) {
|
// Ищем пользователя по адресу в таблице user_identities
|
||||||
const userId = existingUser.rows[0].id;
|
const userResult = await db.query(`
|
||||||
const isAdmin = await this.checkAdminRole(address);
|
SELECT u.* FROM users u
|
||||||
return { userId, isAdmin };
|
JOIN user_identities ui ON u.id = ui.user_id
|
||||||
|
WHERE ui.provider = 'wallet' AND ui.provider_id = $1
|
||||||
|
`, [address]);
|
||||||
|
|
||||||
|
if (userResult.rows.length > 0) {
|
||||||
|
const user = userResult.rows[0];
|
||||||
|
return {
|
||||||
|
userId: user.id,
|
||||||
|
isAdmin: user.role === 'admin'
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создание нового пользователя
|
// Если пользователь не найден, создаем нового
|
||||||
const result = await db.query(
|
const newUserResult = await db.query(
|
||||||
'INSERT INTO users DEFAULT VALUES RETURNING id',
|
'INSERT INTO users (role) VALUES ($1) RETURNING id',
|
||||||
[]
|
['user']
|
||||||
);
|
);
|
||||||
const userId = result.rows[0].id;
|
|
||||||
|
|
||||||
|
const userId = newUserResult.rows[0].id;
|
||||||
|
|
||||||
|
// Добавляем идентификатор кошелька
|
||||||
await db.query(
|
await db.query(
|
||||||
`INSERT INTO user_identities
|
'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)',
|
||||||
(user_id, provider, provider_id, identity_type, identity_value)
|
[userId, 'wallet', address]
|
||||||
VALUES ($1, 'wallet', $2, 'wallet', $2)`,
|
|
||||||
[userId, address.toLowerCase()]
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const isAdmin = await this.checkAdminRole(address);
|
return { userId, isAdmin: false };
|
||||||
return { userId, isAdmin };
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error in findOrCreateUser:', error);
|
console.error('Error finding or creating user:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,41 +145,80 @@ class AuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Создание сессии с проверкой роли
|
// Создание сессии с проверкой роли
|
||||||
async createSession(req, { userId, address, authType, guestId }) {
|
async createSession(session, { userId, authenticated, authType, guestId, address }) {
|
||||||
let isAdmin = false;
|
try {
|
||||||
|
// Обновляем данные сессии
|
||||||
|
session.userId = userId;
|
||||||
|
session.authenticated = authenticated;
|
||||||
|
session.authType = authType;
|
||||||
|
session.guestId = guestId;
|
||||||
if (address) {
|
if (address) {
|
||||||
isAdmin = await this.checkAdminRole(address);
|
session.address = address;
|
||||||
} else if (userId) {
|
}
|
||||||
const linkedWallet = await this.getLinkedWallet(userId);
|
|
||||||
if (linkedWallet) {
|
// Сохраняем сессию в БД
|
||||||
isAdmin = await this.checkAdminRole(linkedWallet);
|
const result = await db.query(
|
||||||
|
`UPDATE session
|
||||||
|
SET sess = $1
|
||||||
|
WHERE sid = $2`,
|
||||||
|
[JSON.stringify({
|
||||||
|
userId,
|
||||||
|
authenticated,
|
||||||
|
authType,
|
||||||
|
guestId,
|
||||||
|
address,
|
||||||
|
cookie: session.cookie
|
||||||
|
}), session.id]
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error creating session:', error);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
req.session.userId = userId;
|
async getSession(sessionId) {
|
||||||
req.session.address = address;
|
try {
|
||||||
req.session.isAdmin = isAdmin;
|
const result = await db.query('SELECT * FROM session WHERE sid = $1', [sessionId]);
|
||||||
req.session.authenticated = true;
|
return result.rows[0];
|
||||||
req.session.authType = authType;
|
} catch (error) {
|
||||||
|
console.error('Error getting session:', error);
|
||||||
if (guestId) {
|
throw error;
|
||||||
req.session.guestId = guestId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await req.session.save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Получение связанного кошелька
|
// Получение связанного кошелька
|
||||||
async getLinkedWallet(userId) {
|
async getLinkedWallet(userId) {
|
||||||
const result = await db.query(
|
const result = await db.query(
|
||||||
`SELECT identity_value as address
|
`SELECT provider_id as address
|
||||||
FROM user_identities
|
FROM user_identities
|
||||||
WHERE user_id = $1 AND identity_type = 'wallet'`,
|
WHERE user_id = $1 AND provider = 'wallet'`,
|
||||||
[userId]
|
[userId]
|
||||||
);
|
);
|
||||||
return result.rows[0]?.address;
|
return result.rows[0]?.address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверяет роль пользователя Telegram
|
||||||
|
* @param {number} userId - ID пользователя
|
||||||
|
* @returns {Promise<string>} - Роль пользователя
|
||||||
|
*/
|
||||||
|
async checkUserRole(userId) {
|
||||||
|
try {
|
||||||
|
// Проверяем наличие связанного кошелька
|
||||||
|
const wallet = await this.getLinkedWallet(userId);
|
||||||
|
if (wallet) {
|
||||||
|
// Если есть кошелек, проверяем админские токены
|
||||||
|
const isAdmin = await this.checkAdminRole(wallet);
|
||||||
|
return isAdmin ? 'admin' : 'user';
|
||||||
|
}
|
||||||
|
return 'user';
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error checking user role:', error);
|
||||||
|
return 'user';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создаем и экспортируем единственный экземпляр
|
// Создаем и экспортируем единственный экземпляр
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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 { generateVerificationCode, addUserIdentity } = require('../utils/helpers');
|
||||||
|
|
||||||
// Хранилище кодов подтверждения
|
// Хранилище кодов подтверждения
|
||||||
const verificationCodes = new Map(); // { email: { code, token, expires } }
|
const verificationCodes = new Map(); // { email: { code, token, expires } }
|
||||||
@@ -334,6 +335,79 @@ class EmailBotService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Инициализация процесса аутентификации по email
|
||||||
|
async function initEmailAuth(email) {
|
||||||
|
const code = generateVerificationCode();
|
||||||
|
|
||||||
|
// Сохраняем код на 15 минут
|
||||||
|
verificationCodes.set(code, {
|
||||||
|
email,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
verified: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Отправляем код на email
|
||||||
|
try {
|
||||||
|
await transporter.sendMail({
|
||||||
|
from: process.env.SMTP_FROM,
|
||||||
|
to: email,
|
||||||
|
subject: 'Код подтверждения для HB3 Accelerator',
|
||||||
|
text: `Ваш код подтверждения: ${code}\n\nВведите его на сайте для завершения аутентификации.`,
|
||||||
|
html: `
|
||||||
|
<h2>Код подтверждения для HB3 Accelerator</h2>
|
||||||
|
<p>Ваш код подтверждения: <strong>${code}</strong></p>
|
||||||
|
<p>Введите его на сайте для завершения аутентификации.</p>
|
||||||
|
`
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`Verification code sent to email: ${email}`);
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error sending verification email:', error);
|
||||||
|
throw new Error('Failed to send verification email');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка кода подтверждения
|
||||||
|
async function verifyEmailCode(code, userId) {
|
||||||
|
const verification = verificationCodes.get(code);
|
||||||
|
|
||||||
|
if (!verification) {
|
||||||
|
logger.warn(`Invalid verification code attempt: ${code}`);
|
||||||
|
throw new Error('Неверный код');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Date.now() - verification.timestamp > 15 * 60 * 1000) {
|
||||||
|
verificationCodes.delete(code);
|
||||||
|
logger.warn(`Expired verification code: ${code}`);
|
||||||
|
throw new Error('Код устарел');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Сохраняем связь пользователя с email
|
||||||
|
const success = await addUserIdentity(
|
||||||
|
userId,
|
||||||
|
'email',
|
||||||
|
verification.email
|
||||||
|
);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
verificationCodes.delete(code);
|
||||||
|
logger.info(`User ${userId} successfully linked email ${verification.email}`);
|
||||||
|
return { success: true };
|
||||||
|
} else {
|
||||||
|
throw new Error('Этот email уже привязан к другому пользователю');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error saving email identity:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Экспортируем класс и хранилище кодов
|
// Экспортируем класс и хранилище кодов
|
||||||
module.exports = EmailBotService;
|
module.exports = {
|
||||||
module.exports.verificationCodes = verificationCodes;
|
EmailBotService,
|
||||||
|
verificationCodes,
|
||||||
|
initEmailAuth,
|
||||||
|
verifyEmailCode
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,144 +1,183 @@
|
|||||||
const TelegramBot = require('node-telegram-bot-api');
|
const { Telegraf } = require('telegraf');
|
||||||
const logger = require('../utils/logger');
|
const logger = require('../utils/logger');
|
||||||
|
const db = require('../db');
|
||||||
|
const authService = require('./auth-service');
|
||||||
|
|
||||||
class TelegramBotService {
|
let botInstance = null;
|
||||||
constructor(token) {
|
const verificationCodes = new Map();
|
||||||
this.bot = new TelegramBot(token, {
|
|
||||||
polling: true,
|
|
||||||
request: {
|
|
||||||
timeout: 30000 // 30 секунд таймаут
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.verificationCodes = new Map();
|
|
||||||
this.setupHandlers();
|
|
||||||
|
|
||||||
logger.info('TelegramBot service initialized');
|
// Простая остановка бота
|
||||||
}
|
async function stopBot() {
|
||||||
|
if (botInstance) {
|
||||||
setupHandlers() {
|
await botInstance.stop();
|
||||||
this.bot.on('message', this.handleMessage.bind(this));
|
botInstance = null;
|
||||||
this.bot.on('callback_query', this.handleCallbackQuery.bind(this));
|
|
||||||
|
|
||||||
// Обработка ошибок
|
|
||||||
this.bot.on('polling_error', (error) => {
|
|
||||||
logger.error('Telegram polling error:', error);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.bot.on('error', (error) => {
|
|
||||||
logger.error('Telegram bot error:', error);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleMessage(msg) {
|
|
||||||
try {
|
|
||||||
const chatId = msg.chat.id;
|
|
||||||
const text = msg.text;
|
|
||||||
|
|
||||||
logger.info(`Received message from ${chatId}: ${text}`);
|
|
||||||
|
|
||||||
if (text.startsWith('/start')) {
|
|
||||||
await this.handleStart(msg);
|
|
||||||
} else if (this.verificationCodes.has(chatId)) {
|
|
||||||
await this.handleVerificationCode(msg);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error handling message:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleCallbackQuery(query) {
|
|
||||||
try {
|
|
||||||
const chatId = query.message.chat.id;
|
|
||||||
await this.bot.answerCallbackQuery(query.id);
|
|
||||||
|
|
||||||
logger.info(`Handled callback query from ${chatId}`);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error handling callback query:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleStart(msg) {
|
|
||||||
const chatId = msg.chat.id;
|
|
||||||
try {
|
|
||||||
await this.bot.sendMessage(
|
|
||||||
chatId,
|
|
||||||
'Добро пожаловать! Используйте этого бота для аутентификации в приложении.'
|
|
||||||
);
|
|
||||||
logger.info(`Sent welcome message to ${chatId}`);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(`Error sending welcome message to ${chatId}:`, error);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleVerificationCode(msg) {
|
|
||||||
const chatId = msg.chat.id;
|
|
||||||
const code = msg.text.trim();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const verificationData = this.verificationCodes.get(chatId);
|
|
||||||
|
|
||||||
if (!verificationData) {
|
|
||||||
await this.bot.sendMessage(chatId, 'Нет активного кода подтверждения.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Date.now() > verificationData.expires) {
|
|
||||||
this.verificationCodes.delete(chatId);
|
|
||||||
await this.bot.sendMessage(chatId, 'Код подтверждения истек. Запросите новый.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verificationData.code === code) {
|
|
||||||
await this.bot.sendMessage(chatId, 'Код подтвержден успешно!');
|
|
||||||
this.verificationCodes.delete(chatId);
|
|
||||||
} else {
|
|
||||||
await this.bot.sendMessage(chatId, 'Неверный код. Попробуйте еще раз.');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(`Error handling verification code for ${chatId}:`, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async sendVerificationCode(chatId, code) {
|
|
||||||
try {
|
|
||||||
// Сохраняем код с временем истечения (15 минут)
|
|
||||||
this.verificationCodes.set(chatId, {
|
|
||||||
code,
|
|
||||||
expires: Date.now() + 15 * 60 * 1000
|
|
||||||
});
|
|
||||||
|
|
||||||
await this.bot.sendMessage(
|
|
||||||
chatId,
|
|
||||||
`Ваш код подтверждения: ${code}\nВведите его в приложении.`
|
|
||||||
);
|
|
||||||
|
|
||||||
logger.info(`Sent verification code to ${chatId}`);
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(`Error sending verification code to ${chatId}:`, error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async verifyCode(code) {
|
|
||||||
try {
|
|
||||||
for (const [chatId, data] of this.verificationCodes.entries()) {
|
|
||||||
if (data.code === code) {
|
|
||||||
if (Date.now() > data.expires) {
|
|
||||||
this.verificationCodes.delete(chatId);
|
|
||||||
return { success: false, error: 'Код истек' };
|
|
||||||
}
|
|
||||||
this.verificationCodes.delete(chatId);
|
|
||||||
return { success: true, telegramId: chatId.toString() };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return { success: false, error: 'Неверный код' };
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('Error verifying code:', error);
|
|
||||||
return { success: false, error: 'Внутренняя ошибка' };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = TelegramBotService;
|
// Создание и настройка бота
|
||||||
|
async function getBot() {
|
||||||
|
if (!botInstance) {
|
||||||
|
botInstance = new Telegraf(process.env.TELEGRAM_BOT_TOKEN);
|
||||||
|
|
||||||
|
// Обработка команды /start
|
||||||
|
botInstance.command('start', (ctx) => {
|
||||||
|
ctx.reply('Добро пожаловать! Отправьте код подтверждения для аутентификации.');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Обработка кодов верификации
|
||||||
|
botInstance.on('text', async (ctx) => {
|
||||||
|
const code = ctx.message.text.trim();
|
||||||
|
const verification = verificationCodes.get(code);
|
||||||
|
|
||||||
|
if (!verification) {
|
||||||
|
ctx.reply('Неверный код подтверждения');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
logger.info('Starting Telegram auth process for code:', code);
|
||||||
|
logger.info('Verification data:', verification);
|
||||||
|
|
||||||
|
// Сначала проверяем, существует ли пользователь с этим Telegram ID
|
||||||
|
let userId;
|
||||||
|
const existingUser = await db.query(
|
||||||
|
`SELECT u.id
|
||||||
|
FROM users u
|
||||||
|
JOIN user_identities ui ON u.id = ui.user_id
|
||||||
|
WHERE ui.provider = $1
|
||||||
|
AND ui.provider_id = $2`,
|
||||||
|
['telegram', ctx.from.id.toString()]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (existingUser.rows.length > 0) {
|
||||||
|
userId = existingUser.rows[0].id;
|
||||||
|
logger.info('Found existing user with ID:', userId);
|
||||||
|
} else {
|
||||||
|
// Создаем нового пользователя
|
||||||
|
const result = await db.query(
|
||||||
|
`INSERT INTO users (created_at, updated_at)
|
||||||
|
VALUES (NOW(), NOW())
|
||||||
|
RETURNING id`,
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
userId = result.rows[0].id;
|
||||||
|
logger.info('Created new user with ID:', userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Связываем Telegram с пользователем
|
||||||
|
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, 'telegram', ctx.from.id.toString()]
|
||||||
|
);
|
||||||
|
|
||||||
|
logger.info(`User ${userId} successfully linked Telegram account ${ctx.from.id}`);
|
||||||
|
|
||||||
|
// Обновляем сессию
|
||||||
|
if (verification?.session) {
|
||||||
|
logger.info('Creating session with data:', verification.session);
|
||||||
|
|
||||||
|
// Обновляем данные сессии напрямую
|
||||||
|
verification.session.userId = userId;
|
||||||
|
verification.session.authenticated = true;
|
||||||
|
verification.session.authType = 'telegram';
|
||||||
|
verification.session.telegramId = ctx.from.id.toString(); // Добавляем идентификатор Telegram в сессию
|
||||||
|
|
||||||
|
// Проверяем роль пользователя
|
||||||
|
const userRole = await authService.checkUserRole(userId);
|
||||||
|
verification.session.userRole = userRole;
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
verification.session.save(err => {
|
||||||
|
if (err) reject(err);
|
||||||
|
else resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info('Session created successfully');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отправляем последнее сообщение пользователя
|
||||||
|
if (verification.session.guestId) {
|
||||||
|
logger.info('Fetching last guest message for guestId:', verification.session.guestId);
|
||||||
|
const messageResult = await db.query(`
|
||||||
|
SELECT content FROM guest_messages
|
||||||
|
WHERE guest_id = $1
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT 1
|
||||||
|
`, [verification.session.guestId]);
|
||||||
|
|
||||||
|
const lastMessage = messageResult.rows[0]?.content;
|
||||||
|
logger.info('Found last message:', lastMessage);
|
||||||
|
if (lastMessage) {
|
||||||
|
await ctx.reply(`Ваше последнее сообщение: "${lastMessage}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Отправляем сообщение об успешной аутентификации
|
||||||
|
await ctx.reply('Аутентификация успешна! Можете вернуться в приложение.');
|
||||||
|
|
||||||
|
// Удаляем сообщение с кодом
|
||||||
|
try {
|
||||||
|
await ctx.deleteMessage(ctx.message.message_id);
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn('Could not delete code message:', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Удаляем код верификации
|
||||||
|
verificationCodes.delete(code);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Error in Telegram auth:', error);
|
||||||
|
|
||||||
|
// Более информативные сообщения об ошибках
|
||||||
|
let errorMessage = 'Произошла ошибка при сохранении. Попробуйте позже.';
|
||||||
|
|
||||||
|
if (error.code === '42P01') {
|
||||||
|
errorMessage = 'Ошибка сессии. Пожалуйста, обновите страницу и попробуйте снова.';
|
||||||
|
} else if (error.code === '42703') {
|
||||||
|
errorMessage = 'Ошибка структуры данных. Обратитесь к администратору.';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error.code) {
|
||||||
|
logger.error('Database error code:', error.code);
|
||||||
|
}
|
||||||
|
if (error.detail) {
|
||||||
|
logger.error('Error detail:', error.detail);
|
||||||
|
}
|
||||||
|
if (error.stack) {
|
||||||
|
logger.error('Error stack:', error.stack);
|
||||||
|
}
|
||||||
|
await ctx.reply(errorMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Запускаем бота
|
||||||
|
await botInstance.launch();
|
||||||
|
}
|
||||||
|
|
||||||
|
return botInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Инициализация процесса аутентификации
|
||||||
|
async function initTelegramAuth(session) {
|
||||||
|
const code = Math.random().toString(36).substring(2, 8).toUpperCase();
|
||||||
|
const botLink = `https://t.me/${process.env.TELEGRAM_BOT_USERNAME}`;
|
||||||
|
|
||||||
|
verificationCodes.set(code, {
|
||||||
|
timestamp: Date.now(),
|
||||||
|
session: session
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info(`Generated verification code: ${code} for Telegram auth`);
|
||||||
|
return { verificationCode: code, botLink };
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
getBot,
|
||||||
|
stopBot,
|
||||||
|
verificationCodes,
|
||||||
|
initTelegramAuth
|
||||||
|
};
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
const db = require('../db');
|
||||||
|
|
||||||
// Функция для создания задержки
|
// Функция для создания задержки
|
||||||
function sleep(ms) {
|
function sleep(ms) {
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||||
@@ -9,7 +11,43 @@ function isValidEmail(email) {
|
|||||||
return emailRegex.test(email);
|
return emailRegex.test(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Генерация кода подтверждения
|
||||||
|
function generateVerificationCode(length = 6) {
|
||||||
|
return Math.random()
|
||||||
|
.toString(36)
|
||||||
|
.substring(2, 2 + length)
|
||||||
|
.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверка существования идентификатора пользователя
|
||||||
|
async function checkUserIdentity(userId, provider, providerId) {
|
||||||
|
const result = await db.query(
|
||||||
|
'SELECT * FROM user_identities WHERE user_id = $1 AND provider = $2 AND provider_id = $3',
|
||||||
|
[userId, provider, providerId]
|
||||||
|
);
|
||||||
|
return result.rows.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Добавление новой идентификации
|
||||||
|
async function addUserIdentity(userId, provider, providerId) {
|
||||||
|
try {
|
||||||
|
await db.query(
|
||||||
|
'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)',
|
||||||
|
[userId, provider, providerId]
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
if (error.code === '23505') { // Уникальное ограничение нарушено
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
sleep,
|
sleep,
|
||||||
isValidEmail,
|
isValidEmail,
|
||||||
|
generateVerificationCode,
|
||||||
|
checkUserIdentity,
|
||||||
|
addUserIdentity
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1079,6 +1079,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz"
|
||||||
integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==
|
integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==
|
||||||
|
|
||||||
|
"@telegraf/types@^7.1.0":
|
||||||
|
version "7.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@telegraf/types/-/types-7.1.0.tgz#d8bd9b2f5070b4de46971416e890338cd89fc23d"
|
||||||
|
integrity sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw==
|
||||||
|
|
||||||
"@tsconfig/node10@^1.0.7":
|
"@tsconfig/node10@^1.0.7":
|
||||||
version "1.0.11"
|
version "1.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2"
|
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2"
|
||||||
@@ -1806,6 +1811,24 @@ bs58check@^2.1.2:
|
|||||||
create-hash "^1.1.0"
|
create-hash "^1.1.0"
|
||||||
safe-buffer "^5.1.2"
|
safe-buffer "^5.1.2"
|
||||||
|
|
||||||
|
buffer-alloc-unsafe@^1.1.0:
|
||||||
|
version "1.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
|
||||||
|
integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==
|
||||||
|
|
||||||
|
buffer-alloc@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-alloc/-/buffer-alloc-1.2.0.tgz#890dd90d923a873e08e10e5fd51a57e5b7cce0ec"
|
||||||
|
integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==
|
||||||
|
dependencies:
|
||||||
|
buffer-alloc-unsafe "^1.1.0"
|
||||||
|
buffer-fill "^1.0.0"
|
||||||
|
|
||||||
|
buffer-fill@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
|
||||||
|
integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==
|
||||||
|
|
||||||
buffer-from@^1.0.0:
|
buffer-from@^1.0.0:
|
||||||
version "1.1.2"
|
version "1.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
||||||
@@ -2288,7 +2311,7 @@ debug@2.6.9:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms "2.0.0"
|
ms "2.0.0"
|
||||||
|
|
||||||
debug@4, debug@^4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.5:
|
debug@4, debug@^4, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5:
|
||||||
version "4.4.0"
|
version "4.4.0"
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz"
|
resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz"
|
||||||
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
|
integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
|
||||||
@@ -4678,6 +4701,11 @@ mocha@^10.0.0, mocha@^10.2.0:
|
|||||||
yargs-parser "^20.2.9"
|
yargs-parser "^20.2.9"
|
||||||
yargs-unparser "^2.0.0"
|
yargs-unparser "^2.0.0"
|
||||||
|
|
||||||
|
mri@^1.2.0:
|
||||||
|
version "1.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b"
|
||||||
|
integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==
|
||||||
|
|
||||||
ms@2.0.0:
|
ms@2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
|
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
|
||||||
@@ -4753,7 +4781,7 @@ node-emoji@^1.10.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
lodash "^4.17.21"
|
lodash "^4.17.21"
|
||||||
|
|
||||||
node-fetch@^2.6.7:
|
node-fetch@^2.6.7, node-fetch@^2.7.0:
|
||||||
version "2.7.0"
|
version "2.7.0"
|
||||||
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
|
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz"
|
||||||
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
|
||||||
@@ -5011,6 +5039,11 @@ p-timeout@^3.2.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
p-finally "^1.0.0"
|
p-finally "^1.0.0"
|
||||||
|
|
||||||
|
p-timeout@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-4.1.0.tgz#788253c0452ab0ffecf18a62dff94ff1bd09ca0a"
|
||||||
|
integrity sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==
|
||||||
|
|
||||||
parent-module@^1.0.0:
|
parent-module@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz"
|
resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz"
|
||||||
@@ -5555,6 +5588,13 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
|||||||
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
|
resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
|
||||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||||
|
|
||||||
|
safe-compare@^1.1.4:
|
||||||
|
version "1.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/safe-compare/-/safe-compare-1.1.4.tgz#5e0128538a82820e2e9250cd78e45da6786ba593"
|
||||||
|
integrity sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==
|
||||||
|
dependencies:
|
||||||
|
buffer-alloc "^1.2.0"
|
||||||
|
|
||||||
safe-push-apply@^1.0.0:
|
safe-push-apply@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz"
|
||||||
@@ -5582,6 +5622,11 @@ safe-stable-stringify@^2.3.1:
|
|||||||
resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
|
resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
|
||||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||||
|
|
||||||
|
sandwich-stream@^2.0.2:
|
||||||
|
version "2.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/sandwich-stream/-/sandwich-stream-2.0.2.tgz#6d1feb6cf7e9fe9fadb41513459a72c2e84000fa"
|
||||||
|
integrity sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==
|
||||||
|
|
||||||
sc-istanbul@^0.4.5:
|
sc-istanbul@^0.4.5:
|
||||||
version "0.4.6"
|
version "0.4.6"
|
||||||
resolved "https://registry.yarnpkg.com/sc-istanbul/-/sc-istanbul-0.4.6.tgz#cf6784355ff2076f92d70d59047d71c13703e839"
|
resolved "https://registry.yarnpkg.com/sc-istanbul/-/sc-istanbul-0.4.6.tgz#cf6784355ff2076f92d70d59047d71c13703e839"
|
||||||
@@ -6153,6 +6198,20 @@ table@^6.8.0:
|
|||||||
string-width "^4.2.3"
|
string-width "^4.2.3"
|
||||||
strip-ansi "^6.0.1"
|
strip-ansi "^6.0.1"
|
||||||
|
|
||||||
|
telegraf@^4.16.3:
|
||||||
|
version "4.16.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/telegraf/-/telegraf-4.16.3.tgz#f03fa30482b540a7f9895af8f13ec8f432840a66"
|
||||||
|
integrity sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==
|
||||||
|
dependencies:
|
||||||
|
"@telegraf/types" "^7.1.0"
|
||||||
|
abort-controller "^3.0.0"
|
||||||
|
debug "^4.3.4"
|
||||||
|
mri "^1.2.0"
|
||||||
|
node-fetch "^2.7.0"
|
||||||
|
p-timeout "^4.1.0"
|
||||||
|
safe-compare "^1.1.4"
|
||||||
|
sandwich-stream "^2.0.2"
|
||||||
|
|
||||||
text-hex@1.0.x:
|
text-hex@1.0.x:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz"
|
||||||
|
|||||||
78
frontend/src/composables/useAuth.js
Normal file
78
frontend/src/composables/useAuth.js
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
import { ref, onMounted } from 'vue';
|
||||||
|
import axios from '../api/axios';
|
||||||
|
|
||||||
|
export function useAuth() {
|
||||||
|
const isAuthenticated = ref(false);
|
||||||
|
const authType = ref(null);
|
||||||
|
const userId = ref(null);
|
||||||
|
const address = ref(null);
|
||||||
|
const telegramInfo = ref(null);
|
||||||
|
const isAdmin = ref(false);
|
||||||
|
const telegramId = ref(null);
|
||||||
|
|
||||||
|
const updateAuth = ({ authenticated, authType: newAuthType, userId: newUserId, address: newAddress, telegramId: newTelegramId, isAdmin: newIsAdmin }) => {
|
||||||
|
isAuthenticated.value = authenticated;
|
||||||
|
authType.value = newAuthType;
|
||||||
|
userId.value = newUserId;
|
||||||
|
address.value = newAddress;
|
||||||
|
telegramId.value = newTelegramId;
|
||||||
|
isAdmin.value = newIsAdmin;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkAuth = async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get('/api/auth/check');
|
||||||
|
console.log('Auth check response:', response.data);
|
||||||
|
updateAuth(response.data);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking auth status:', error);
|
||||||
|
return { authenticated: false };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const disconnect = async () => {
|
||||||
|
try {
|
||||||
|
await axios.post('/api/auth/logout');
|
||||||
|
updateAuth({
|
||||||
|
authenticated: false,
|
||||||
|
authType: null,
|
||||||
|
userId: null,
|
||||||
|
address: null,
|
||||||
|
telegramId: null,
|
||||||
|
isAdmin: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Очищаем localStorage
|
||||||
|
localStorage.removeItem('isAuthenticated');
|
||||||
|
localStorage.removeItem('userId');
|
||||||
|
localStorage.removeItem('address');
|
||||||
|
localStorage.removeItem('isAdmin');
|
||||||
|
|
||||||
|
// Перезагружаем страницу
|
||||||
|
window.location.reload();
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error disconnecting:', error);
|
||||||
|
return { success: false, error: error.message };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
await checkAuth();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
isAuthenticated,
|
||||||
|
authType,
|
||||||
|
userId,
|
||||||
|
address,
|
||||||
|
telegramInfo,
|
||||||
|
isAdmin,
|
||||||
|
telegramId,
|
||||||
|
updateAuth,
|
||||||
|
checkAuth,
|
||||||
|
disconnect
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,77 +1,90 @@
|
|||||||
import { ethers } from 'ethers';
|
import { ethers } from 'ethers';
|
||||||
import axios from 'axios';
|
import axios from '../api/axios';
|
||||||
|
import { SiweMessage } from 'siwe';
|
||||||
|
|
||||||
export async function connectWithWallet() {
|
export async function connectWithWallet() {
|
||||||
try {
|
|
||||||
console.log('Starting wallet connection...');
|
console.log('Starting wallet connection...');
|
||||||
|
|
||||||
|
try {
|
||||||
// Проверяем наличие MetaMask
|
// Проверяем наличие MetaMask
|
||||||
if (!window.ethereum) {
|
if (!window.ethereum) {
|
||||||
throw new Error('MetaMask не установлен. Пожалуйста, установите MetaMask');
|
throw new Error('MetaMask not detected. Please install MetaMask.');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('MetaMask detected, requesting accounts...');
|
console.log('MetaMask detected, requesting accounts...');
|
||||||
const accounts = await window.ethereum.request({
|
|
||||||
method: 'eth_requestAccounts'
|
// Запрашиваем доступ к аккаунтам
|
||||||
});
|
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
|
||||||
|
|
||||||
console.log('Got accounts:', accounts);
|
console.log('Got accounts:', accounts);
|
||||||
|
|
||||||
if (!accounts || accounts.length === 0) {
|
if (!accounts || accounts.length === 0) {
|
||||||
throw new Error('Нет доступных аккаунтов. Пожалуйста, разблокируйте MetaMask');
|
throw new Error('No accounts found. Please unlock MetaMask.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Берем первый аккаунт
|
||||||
const address = ethers.getAddress(accounts[0]);
|
const address = ethers.getAddress(accounts[0]);
|
||||||
console.log('Normalized address:', address);
|
console.log('Normalized address:', address);
|
||||||
|
|
||||||
|
// Запрашиваем nonce с сервера
|
||||||
console.log('Requesting nonce...');
|
console.log('Requesting nonce...');
|
||||||
const { data: { nonce } } = await axios.get('/api/auth/nonce', {
|
const nonceResponse = await axios.get(`/api/auth/nonce?address=${address}`);
|
||||||
params: { address }
|
const nonce = nonceResponse.data.nonce;
|
||||||
});
|
|
||||||
console.log('Got nonce:', nonce);
|
console.log('Got nonce:', nonce);
|
||||||
|
|
||||||
// Формируем сообщение в формате SIWE (Sign-In with Ethereum)
|
// Создаем сообщение для подписи
|
||||||
const domain = window.location.host;
|
const domain = window.location.host;
|
||||||
const origin = window.location.origin;
|
const origin = window.location.origin;
|
||||||
const statement = "Sign in with Ethereum to the app.";
|
const statement = 'Sign in with Ethereum to the app.';
|
||||||
const message = [
|
|
||||||
`${domain} wants you to sign in with your Ethereum account:`,
|
|
||||||
address,
|
|
||||||
"",
|
|
||||||
statement,
|
|
||||||
"",
|
|
||||||
`URI: ${origin}`,
|
|
||||||
"Version: 1",
|
|
||||||
"Chain ID: 1",
|
|
||||||
`Nonce: ${nonce}`,
|
|
||||||
`Issued At: ${new Date().toISOString()}`,
|
|
||||||
"Resources:",
|
|
||||||
`- ${origin}/api/auth/verify`
|
|
||||||
].join("\n");
|
|
||||||
|
|
||||||
|
const siweMessage = new SiweMessage({
|
||||||
|
domain,
|
||||||
|
address,
|
||||||
|
statement,
|
||||||
|
uri: origin,
|
||||||
|
version: '1',
|
||||||
|
chainId: 1,
|
||||||
|
nonce,
|
||||||
|
resources: [`${origin}/api/auth/verify`]
|
||||||
|
});
|
||||||
|
|
||||||
|
const message = siweMessage.prepareMessage();
|
||||||
console.log('SIWE message:', message);
|
console.log('SIWE message:', message);
|
||||||
|
|
||||||
|
// Запрашиваем подпись
|
||||||
console.log('Requesting signature...');
|
console.log('Requesting signature...');
|
||||||
const signature = await window.ethereum.request({
|
const signature = await window.ethereum.request({
|
||||||
method: 'personal_sign',
|
method: 'personal_sign',
|
||||||
params: [message, address]
|
params: [message, address]
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Got signature:', signature);
|
console.log('Got signature:', signature);
|
||||||
|
|
||||||
|
// Отправляем подпись на сервер для верификации
|
||||||
console.log('Sending verification request...');
|
console.log('Sending verification request...');
|
||||||
const response = await axios.post('/api/auth/verify', {
|
const verificationResponse = await axios.post('/api/auth/verify', {
|
||||||
address,
|
message,
|
||||||
signature,
|
signature,
|
||||||
message
|
address
|
||||||
});
|
});
|
||||||
console.log('Verification response:', response.data);
|
|
||||||
|
|
||||||
const provider = new ethers.BrowserProvider(window.ethereum);
|
console.log('Verification response:', verificationResponse.data);
|
||||||
const signer = await provider.getSigner();
|
|
||||||
|
|
||||||
return { address, signer };
|
// Обновляем состояние аутентификации
|
||||||
|
if (verificationResponse.data.success) {
|
||||||
|
// Обновляем состояние аутентификации в localStorage
|
||||||
|
localStorage.setItem('isAuthenticated', 'true');
|
||||||
|
localStorage.setItem('userId', verificationResponse.data.userId);
|
||||||
|
localStorage.setItem('address', verificationResponse.data.address);
|
||||||
|
localStorage.setItem('isAdmin', verificationResponse.data.isAdmin);
|
||||||
|
|
||||||
|
// Перезагружаем страницу для обновления состояния
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
return verificationResponse.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Форматируем ошибку для пользователя
|
console.error('Error connecting wallet:', error);
|
||||||
const message = error.message || 'Ошибка подключения кошелька';
|
throw error;
|
||||||
console.error('Error connecting wallet:', message);
|
|
||||||
throw new Error(message);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,19 +6,30 @@
|
|||||||
<h3>Венчурный фонд и поставщик программного обеспечения</h3>
|
<h3>Венчурный фонд и поставщик программного обеспечения</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="chat-container">
|
<div class="chat-container">
|
||||||
<div class="chat-header">
|
<div class="chat-header">
|
||||||
<!-- Используем тот же компонент, что и в сообщениях -->
|
<!-- Используем тот же компонент, что и в сообщениях -->
|
||||||
<div v-if="!isAuthenticated" class="auth-buttons">
|
<div v-if="!isAuthenticated && !isConnecting" class="auth-buttons">
|
||||||
<button class="auth-btn wallet-btn" @click="handleWalletAuth">
|
<button class="auth-btn wallet-btn" @click="handleWalletAuth">
|
||||||
<span class="auth-icon">👛</span> Подключить кошелек
|
<span class="auth-icon">👛</span> Подключить кошелек
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="wallet-info">
|
|
||||||
<span>{{ truncateAddress(auth.address.value) }}</span>
|
<div v-if="isConnecting" class="connecting-info">
|
||||||
<button class="disconnect-btn" @click="disconnectWallet">
|
<span>Подключение кошелька...</span>
|
||||||
Отключить кошелек
|
</div>
|
||||||
|
|
||||||
|
<div v-show="isAuthenticated && auth.authType.value === 'wallet'" class="auth-buttons">
|
||||||
|
<span>{{ auth.address && auth.address.value ? truncateAddress(auth.address.value) : '' }}</span>
|
||||||
|
<button class="auth-btn wallet-btn" @click="disconnectWallet">
|
||||||
|
<span class="auth-icon">🔌</span> Отключить кошелек
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-show="isAuthenticated && auth.authType.value === 'telegram'" class="auth-buttons">
|
||||||
|
<span>Telegram: {{ auth.telegramId }}</span>
|
||||||
|
<button class="auth-btn disconnect-btn" @click="disconnectWallet">
|
||||||
|
<span class="auth-icon">🔌</span> Выйти
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -44,50 +55,33 @@
|
|||||||
<button class="auth-btn wallet-btn" @click="handleWalletAuth">
|
<button class="auth-btn wallet-btn" @click="handleWalletAuth">
|
||||||
<span class="auth-icon">👛</span> Подключить кошелек
|
<span class="auth-icon">👛</span> Подключить кошелек
|
||||||
</button>
|
</button>
|
||||||
<button class="auth-btn telegram-btn" @click="handleTelegramAuth">
|
|
||||||
<span class="auth-icon">📱</span> Подключить Telegram
|
|
||||||
</button>
|
|
||||||
<button class="auth-btn email-btn" @click="handleEmailAuth">
|
|
||||||
<span class="auth-icon">✉️</span> Подключить Email
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Email форма -->
|
|
||||||
<div v-if="showEmailForm" class="auth-form">
|
|
||||||
<input
|
|
||||||
v-model="emailInput"
|
|
||||||
type="email"
|
|
||||||
placeholder="Введите ваш email"
|
|
||||||
class="auth-input"
|
|
||||||
/>
|
|
||||||
<button @click="submitEmail" class="auth-btn">
|
|
||||||
Отправить код
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Форма верификации email -->
|
|
||||||
<div v-if="showEmailVerification" class="auth-form">
|
|
||||||
<input
|
|
||||||
v-model="emailCode"
|
|
||||||
type="text"
|
|
||||||
placeholder="Введите код из email"
|
|
||||||
class="auth-input"
|
|
||||||
/>
|
|
||||||
<button @click="verifyEmailCode" class="auth-btn">
|
|
||||||
Подтвердить
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Telegram верификация -->
|
<!-- Telegram верификация -->
|
||||||
<div v-if="showTelegramVerification" class="auth-form">
|
<div v-if="showTelegramVerification" class="verification-block">
|
||||||
<input
|
<div class="verification-code">
|
||||||
v-model="telegramCode"
|
<span>Код подтверждения:</span>
|
||||||
type="text"
|
<code @click="copyCode(telegramVerificationCode)">{{ telegramVerificationCode }}</code>
|
||||||
placeholder="Введите код из Telegram"
|
</div>
|
||||||
class="auth-input"
|
<a :href="telegramBotLink" target="_blank" class="bot-link">
|
||||||
/>
|
<span class="auth-icon">📱</span> Открыть HB3_Accelerator_Bot
|
||||||
<button @click="verifyTelegramCode" class="auth-btn">
|
</a>
|
||||||
Подтвердить
|
</div>
|
||||||
|
<button v-else class="auth-btn telegram-btn" @click="handleTelegramAuth">
|
||||||
|
<span class="auth-icon">📱</span> Подключить Telegram
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Email верификация -->
|
||||||
|
<div v-if="showEmailVerification" class="verification-block">
|
||||||
|
<div class="verification-code">
|
||||||
|
<span>Код подтверждения:</span>
|
||||||
|
<code @click="copyCode(emailVerificationCode)">{{ emailVerificationCode }}</code>
|
||||||
|
</div>
|
||||||
|
<a :href="'mailto:' + emailInput" class="bot-link">
|
||||||
|
<span class="auth-icon">✉️</span> Открыть почту
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<button v-else class="auth-btn email-btn" @click="handleEmailAuth">
|
||||||
|
<span class="auth-icon">✉️</span> Подключить Email
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -113,6 +107,18 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- В шаблоне, где отображается информация о пользователе -->
|
||||||
|
<div v-if="auth.isAuthenticated" class="auth-info">
|
||||||
|
<div v-if="auth.authType === 'wallet'">
|
||||||
|
<span>Подключен кошелек: {{ auth.address }}</span>
|
||||||
|
<button @click="disconnectWallet">Отключить кошелек</button>
|
||||||
|
</div>
|
||||||
|
<div v-if="auth.authType === 'telegram'">
|
||||||
|
<span>Подключен Telegram: {{ auth.telegramId }}</span>
|
||||||
|
<button @click="disconnectWallet">Выйти</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -123,50 +129,114 @@ import TelegramConnect from '../components/identity/TelegramConnect.vue';
|
|||||||
import EmailConnect from '../components/identity/EmailConnect.vue';
|
import EmailConnect from '../components/identity/EmailConnect.vue';
|
||||||
import api from '../api/axios';
|
import api from '../api/axios';
|
||||||
import { connectWithWallet } from '../services/wallet';
|
import { connectWithWallet } from '../services/wallet';
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useAuth } from '../composables/useAuth';
|
||||||
|
|
||||||
console.log('HomeView.vue: Version with chat loaded');
|
console.log('HomeView.vue: Version with chat loaded');
|
||||||
|
|
||||||
const auth = inject('auth');
|
const auth = useAuth();
|
||||||
const isAuthenticated = computed(() => auth.isAuthenticated.value);
|
const isAuthenticated = computed(() => auth.isAuthenticated.value);
|
||||||
const authType = ref(null);
|
const isConnecting = ref(false);
|
||||||
const messages = ref([]);
|
const messages = ref([]);
|
||||||
const guestMessages = ref([]);
|
|
||||||
const newMessage = ref('');
|
const newMessage = ref('');
|
||||||
const isLoading = ref(false);
|
const isLoading = ref(false);
|
||||||
const messagesContainer = ref(null);
|
const messagesContainer = ref(null);
|
||||||
const userLanguage = ref('ru');
|
const userLanguage = ref('ru');
|
||||||
const email = ref('');
|
|
||||||
const isValidEmail = ref(true);
|
|
||||||
const hasShownAuthMessage = ref(false);
|
|
||||||
const hasShownAuthOptions = ref(false);
|
|
||||||
|
|
||||||
// Email аутентификация
|
|
||||||
const emailVerificationCode = ref('');
|
|
||||||
const showEmailVerification = ref(false);
|
|
||||||
const emailErrorMessage = ref('');
|
|
||||||
|
|
||||||
// Добавляем состояния для форм верификации
|
|
||||||
const showTelegramVerification = ref(false);
|
|
||||||
const showEmailForm = ref(false);
|
|
||||||
const telegramCode = ref('');
|
|
||||||
const emailInput = ref('');
|
|
||||||
const emailCode = ref('');
|
|
||||||
const emailError = ref('');
|
|
||||||
|
|
||||||
// Добавляем состояния для пагинации
|
// Добавляем состояния для пагинации
|
||||||
const PAGE_SIZE = 2; // Показываем только последнее сообщение и ответ
|
const isLoadingMore = ref(false);
|
||||||
const allMessages = ref([]); // Все загруженные сообщения
|
const hasMoreMessages = ref(false);
|
||||||
const currentPage = ref(1); // Текущая страница
|
|
||||||
const hasMoreMessages = ref(true); // Есть ли еще сообщения
|
|
||||||
const isLoadingMore = ref(false); // Загружаются ли дополнительные сообщения
|
|
||||||
const offset = ref(0);
|
const offset = ref(0);
|
||||||
const limit = ref(20);
|
const limit = ref(20);
|
||||||
|
|
||||||
// Вычисляемое свойство для отображаемых сообщений
|
// Состояния для верификации
|
||||||
const displayedMessages = computed(() => {
|
const showTelegramVerification = ref(false);
|
||||||
const startIndex = Math.max(allMessages.value.length - (PAGE_SIZE * currentPage.value), 0);
|
const telegramVerificationCode = ref('');
|
||||||
return allMessages.value.slice(startIndex);
|
const telegramBotLink = ref('');
|
||||||
});
|
const telegramAuthCheckInterval = ref(null);
|
||||||
|
const showEmailVerification = ref(false);
|
||||||
|
const emailVerificationCode = ref('');
|
||||||
|
const emailInput = ref('');
|
||||||
|
const emailError = ref('');
|
||||||
|
|
||||||
|
// Функция для копирования кода
|
||||||
|
const copyCode = (code) => {
|
||||||
|
navigator.clipboard.writeText(code);
|
||||||
|
// Можно добавить уведомление о копировании
|
||||||
|
};
|
||||||
|
|
||||||
|
// Функция для показа ошибок
|
||||||
|
const showError = (message) => {
|
||||||
|
// Можно использовать toast или alert
|
||||||
|
alert(message);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Обработчик для Telegram аутентификации
|
||||||
|
const handleTelegramAuth = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.post('/api/auth/telegram/init');
|
||||||
|
const { verificationCode, botLink } = data;
|
||||||
|
|
||||||
|
// Показываем код верификации
|
||||||
|
showTelegramVerification.value = true;
|
||||||
|
telegramVerificationCode.value = verificationCode;
|
||||||
|
telegramBotLink.value = botLink;
|
||||||
|
|
||||||
|
// Запускаем проверку статуса аутентификации
|
||||||
|
telegramAuthCheckInterval.value = setInterval(async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get('/api/auth/check');
|
||||||
|
if (response.data.authenticated) {
|
||||||
|
auth.updateAuth({
|
||||||
|
isAuthenticated: true,
|
||||||
|
authType: response.data.authType,
|
||||||
|
userId: response.data.userId
|
||||||
|
});
|
||||||
|
|
||||||
|
clearInterval(telegramAuthCheckInterval.value);
|
||||||
|
telegramAuthCheckInterval.value = null;
|
||||||
|
showTelegramVerification.value = false;
|
||||||
|
|
||||||
|
// Перезагружаем страницу для полного обновления состояния
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking auth status:', error);
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
// Очищаем интервал через 5 минут
|
||||||
|
setTimeout(() => {
|
||||||
|
if (telegramAuthCheckInterval.value) {
|
||||||
|
clearInterval(telegramAuthCheckInterval.value);
|
||||||
|
telegramAuthCheckInterval.value = null;
|
||||||
|
showTelegramVerification.value = false;
|
||||||
|
}
|
||||||
|
}, 5 * 60 * 1000);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error initializing Telegram auth:', error);
|
||||||
|
showError('Ошибка при инициализации Telegram аутентификации');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Обработчик для Email аутентификации
|
||||||
|
const handleEmailAuth = async () => {
|
||||||
|
try {
|
||||||
|
// Запрашиваем email у пользователя
|
||||||
|
const email = prompt('Введите ваш email:');
|
||||||
|
if (!email) return;
|
||||||
|
|
||||||
|
const { data } = await axios.post('/api/auth/email/init', { email });
|
||||||
|
if (data.success) {
|
||||||
|
showEmailVerification.value = true;
|
||||||
|
emailInput.value = email;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error initializing email auth:', error);
|
||||||
|
emailError.value = error.response?.data?.error || 'Ошибка отправки кода';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Функция для сокращения адреса кошелька
|
// Функция для сокращения адреса кошелька
|
||||||
const truncateAddress = (address) => {
|
const truncateAddress = (address) => {
|
||||||
@@ -246,38 +316,32 @@ watch(() => isAuthenticated.value, async (newValue) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Находим существующую функцию handleWalletAuth и обновляем её
|
// Функция для подключения кошелька
|
||||||
const handleWalletAuth = async () => {
|
const handleWalletAuth = async () => {
|
||||||
|
if (isConnecting.value || isAuthenticated.value) return; // Предотвращаем повторное подключение
|
||||||
|
|
||||||
|
isConnecting.value = true;
|
||||||
try {
|
try {
|
||||||
const result = await connectWithWallet();
|
const result = await connectWithWallet();
|
||||||
|
console.log('Wallet connection result:', result);
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
// Обновляем состояние авторизации
|
||||||
await auth.checkAuth();
|
await auth.checkAuth();
|
||||||
|
|
||||||
if (result.authenticated) {
|
// Добавляем небольшую задержку перед сбросом состояния isConnecting
|
||||||
// Сохраняем гостевые сообщения перед очисткой
|
setTimeout(() => {
|
||||||
const guestMessages = [...messages.value];
|
isConnecting.value = false;
|
||||||
messages.value = [];
|
}, 500);
|
||||||
offset.value = 0;
|
return;
|
||||||
hasMoreMessages.value = true;
|
} else {
|
||||||
|
console.error('Failed to connect wallet:', result.error);
|
||||||
try {
|
|
||||||
await api.post('/api/chat/link-guest-messages');
|
|
||||||
console.log('Guest messages linked to authenticated user');
|
|
||||||
await loadMoreMessages();
|
|
||||||
|
|
||||||
const filteredGuestMessages = guestMessages
|
|
||||||
.filter(msg => !msg.showAuthButtons)
|
|
||||||
.reverse();
|
|
||||||
messages.value = [...messages.value, ...filteredGuestMessages];
|
|
||||||
|
|
||||||
await nextTick();
|
|
||||||
scrollToBottom();
|
|
||||||
} catch (linkError) {
|
|
||||||
console.error('Error linking guest messages:', linkError);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error connecting wallet:', error);
|
console.error('Error connecting wallet:', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isConnecting.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Функция для сохранения гостевых сообщений на сервере
|
// Функция для сохранения гостевых сообщений на сервере
|
||||||
@@ -302,103 +366,6 @@ const saveGuestMessagesToServer = async () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Функция для подключения через Telegram
|
|
||||||
async function connectTelegram() {
|
|
||||||
try {
|
|
||||||
// Отправляем запрос на получение ссылки для авторизации через Telegram
|
|
||||||
const response = await api.get('/api/auth/telegram', {
|
|
||||||
withCredentials: true
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data.error) {
|
|
||||||
messages.value.push({
|
|
||||||
sender: 'ai',
|
|
||||||
text: `Ошибка при подключении Telegram: ${response.data.error}`,
|
|
||||||
timestamp: new Date(),
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.data.authUrl) {
|
|
||||||
messages.value.push({
|
|
||||||
sender: 'ai',
|
|
||||||
text: `Для подключения Telegram, перейдите по <a href="${response.data.authUrl}" target="_blank">этой ссылке</a> и авторизуйтесь.`,
|
|
||||||
timestamp: new Date(),
|
|
||||||
});
|
|
||||||
|
|
||||||
// Открываем ссылку в новом окне
|
|
||||||
window.open(response.data.authUrl, '_blank');
|
|
||||||
} else {
|
|
||||||
messages.value.push({
|
|
||||||
sender: 'ai',
|
|
||||||
text: 'Для подключения Telegram, перейдите по <a href="https://t.me/YourBotName" target="_blank">этой ссылке</a> и авторизуйтесь.',
|
|
||||||
timestamp: new Date(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error connecting with Telegram:', error);
|
|
||||||
|
|
||||||
messages.value.push({
|
|
||||||
sender: 'ai',
|
|
||||||
text: 'Извините, произошла ошибка при подключении Telegram. Пожалуйста, попробуйте позже.',
|
|
||||||
timestamp: new Date(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Запрос кода подтверждения по email
|
|
||||||
async function requestEmailCode() {
|
|
||||||
emailErrorMessage.value = '';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await auth.requestEmailVerification(email.value);
|
|
||||||
|
|
||||||
if (response.success) {
|
|
||||||
showEmailVerification.value = true;
|
|
||||||
// Временно для тестирования
|
|
||||||
if (response.verificationCode) {
|
|
||||||
emailErrorMessage.value = `Код для тестирования: ${response.verificationCode}`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
emailErrorMessage.value = response.error || 'Ошибка запроса кода подтверждения';
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error requesting email verification:', error);
|
|
||||||
emailErrorMessage.value = 'Ошибка запроса кода подтверждения';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Функция проверки кода
|
|
||||||
const verifyEmailCode = async () => {
|
|
||||||
try {
|
|
||||||
const response = await api.post('/api/auth/email/verify-code', {
|
|
||||||
email: emailInput.value,
|
|
||||||
code: emailCode.value
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data.success) {
|
|
||||||
auth.setEmailAuth(response.data);
|
|
||||||
showEmailVerification.value = false;
|
|
||||||
emailError.value = '';
|
|
||||||
|
|
||||||
// Загружаем историю чата после успешной аутентификации
|
|
||||||
await loadMoreMessages();
|
|
||||||
} else {
|
|
||||||
emailError.value = response.data.error || 'Неверный код';
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
emailError.value = error.response?.data?.error || 'Ошибка проверки кода';
|
|
||||||
console.error('Error verifying email code:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Отмена верификации email
|
|
||||||
function cancelEmailVerification() {
|
|
||||||
showEmailVerification.value = false;
|
|
||||||
emailVerificationCode.value = '';
|
|
||||||
emailErrorMessage.value = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Форматирование времени
|
// Форматирование времени
|
||||||
const formatTime = (timestamp) => {
|
const formatTime = (timestamp) => {
|
||||||
if (!timestamp) return '';
|
if (!timestamp) return '';
|
||||||
@@ -499,97 +466,10 @@ const handleMessage = async (text) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Добавляем методы для аутентификации
|
|
||||||
const handleTelegramAuth = () => {
|
|
||||||
window.open('https://t.me/HB3_Accelerator_Bot', '_blank');
|
|
||||||
// Показываем форму для ввода кода через небольшую задержку
|
|
||||||
setTimeout(() => {
|
|
||||||
showTelegramVerification.value = true;
|
|
||||||
}, 1000);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEmailAuth = async () => {
|
|
||||||
showEmailForm.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Функция отправки email
|
|
||||||
const submitEmail = async () => {
|
|
||||||
try {
|
|
||||||
const response = await api.post('/api/auth/email/request', {
|
|
||||||
email: emailInput.value
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data.success) {
|
|
||||||
showEmailForm.value = false;
|
|
||||||
showEmailVerification.value = true;
|
|
||||||
} else {
|
|
||||||
emailError.value = response.data.error || 'Ошибка отправки кода';
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
emailError.value = 'Ошибка отправки кода';
|
|
||||||
console.error('Error sending email code:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Функция верификации кода Telegram
|
|
||||||
const verifyTelegramCode = async () => {
|
|
||||||
try {
|
|
||||||
const response = await api.post('/api/auth/telegram/verify', {
|
|
||||||
code: telegramCode.value
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.data.success) {
|
|
||||||
console.log('Telegram verification successful:', response.data);
|
|
||||||
|
|
||||||
// Обновляем состояние аутентификации
|
|
||||||
auth.setAuth({
|
|
||||||
isAuthenticated: response.data.authenticated,
|
|
||||||
userId: response.data.userId,
|
|
||||||
telegramId: response.data.telegramId,
|
|
||||||
isAdmin: response.data.isAdmin,
|
|
||||||
authType: 'telegram'
|
|
||||||
});
|
|
||||||
|
|
||||||
showTelegramVerification.value = false;
|
|
||||||
telegramCode.value = '';
|
|
||||||
|
|
||||||
// Показываем сообщение об успехе
|
|
||||||
messages.value.push({
|
|
||||||
id: Date.now(),
|
|
||||||
content: 'Telegram успешно подключен!',
|
|
||||||
role: 'assistant',
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Загружаем историю чата после небольшой задержки
|
|
||||||
setTimeout(async () => {
|
|
||||||
await loadMoreMessages();
|
|
||||||
}, 100);
|
|
||||||
} else {
|
|
||||||
messages.value.push({
|
|
||||||
id: Date.now(),
|
|
||||||
content: response.data.error || 'Ошибка верификации кода',
|
|
||||||
role: 'assistant',
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error verifying Telegram code:', error);
|
|
||||||
messages.value.push({
|
|
||||||
id: Date.now(),
|
|
||||||
content: 'Произошла ошибка. Пожалуйста, попробуйте позже.',
|
|
||||||
role: 'assistant',
|
|
||||||
timestamp: new Date().toISOString()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const disconnectWallet = async () => {
|
const disconnectWallet = async () => {
|
||||||
try {
|
try {
|
||||||
await auth.disconnect();
|
await auth.disconnect();
|
||||||
messages.value = [];
|
console.log('Wallet disconnected successfully');
|
||||||
offset.value = 0;
|
|
||||||
hasMoreMessages.value = true;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error disconnecting wallet:', error);
|
console.error('Error disconnecting wallet:', error);
|
||||||
}
|
}
|
||||||
@@ -612,6 +492,20 @@ onMounted(() => {
|
|||||||
if (messagesContainer.value) {
|
if (messagesContainer.value) {
|
||||||
messagesContainer.value.addEventListener('scroll', handleScroll);
|
messagesContainer.value.addEventListener('scroll', handleScroll);
|
||||||
}
|
}
|
||||||
|
console.log('Auth state on mount:', {
|
||||||
|
isAuthenticated: auth.isAuthenticated.value,
|
||||||
|
authType: auth.authType.value,
|
||||||
|
telegramId: auth.telegramId.value
|
||||||
|
});
|
||||||
|
|
||||||
|
// Добавляем отладочный вывод для auth.authType
|
||||||
|
console.log('auth.authType:', auth.authType);
|
||||||
|
console.log('auth.authType.value:', auth.authType.value);
|
||||||
|
console.log('auth.authType.value === "telegram":', auth.authType.value === 'telegram');
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(() => auth.telegramId.value, (newValue) => {
|
||||||
|
console.log('Telegram ID changed:', newValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
@@ -619,6 +513,9 @@ onBeforeUnmount(() => {
|
|||||||
if (messagesContainer.value) {
|
if (messagesContainer.value) {
|
||||||
messagesContainer.value.removeEventListener('scroll', handleScroll);
|
messagesContainer.value.removeEventListener('scroll', handleScroll);
|
||||||
}
|
}
|
||||||
|
if (telegramAuthCheckInterval.value) {
|
||||||
|
clearInterval(telegramAuthCheckInterval.value);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -897,7 +794,7 @@ h1 {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-btn {
|
.auth-btn, .disconnect-btn {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
@@ -905,34 +802,43 @@ h1 {
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
justify-content: center;
|
||||||
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wallet-btn {
|
.wallet-btn {
|
||||||
background-color: #4a5568;
|
background-color: #4CAF50;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wallet-btn:hover {
|
.wallet-btn:hover {
|
||||||
background-color: #2d3748;
|
background-color: #45a049;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disconnect-btn {
|
||||||
|
background-color: #f44336;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disconnect-btn:hover {
|
||||||
|
background-color: #d32f2f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-buttons, .wallet-info {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.auth-icon {
|
.auth-icon {
|
||||||
font-size: 16px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.telegram-btn {
|
.connecting-info {
|
||||||
background-color: #0088cc;
|
padding: 8px 16px;
|
||||||
|
background-color: #2196F3;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
border-radius: 4px;
|
||||||
|
|
||||||
.email-btn {
|
|
||||||
background-color: #4caf50;
|
|
||||||
color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cancel-btn {
|
|
||||||
background-color: #999;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.error-message {
|
.error-message {
|
||||||
@@ -963,13 +869,17 @@ h1 {
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.wallet-btn:hover {
|
||||||
|
background-color: #2d3748;
|
||||||
|
}
|
||||||
|
|
||||||
.telegram-btn {
|
.telegram-btn {
|
||||||
background-color: #0088cc;
|
background-color: #0088cc;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.email-btn {
|
.email-btn {
|
||||||
background-color: #48bb78;
|
background-color: #4caf50;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1102,4 +1012,103 @@ h1 {
|
|||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
color: #666;
|
color: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Добавляем отображение кода и ссылки для Telegram */
|
||||||
|
.verification-info {
|
||||||
|
padding: 10px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verification-info p {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verification-info strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verification-info a {
|
||||||
|
color: #007bff;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verification-info a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verification-block {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verification-code {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verification-code code {
|
||||||
|
background: #fff;
|
||||||
|
padding: 4px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-family: monospace;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verification-code code:hover {
|
||||||
|
background: #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bot-link {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: #0088cc;
|
||||||
|
color: white;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bot-link:hover {
|
||||||
|
background: #006699;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-icon {
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Добавляем новые стили для информации о пользователе */
|
||||||
|
.auth-info {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-info button {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: #ff4444;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.auth-info button:hover {
|
||||||
|
background-color: #cc0000;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user