ваше сообщение коммита
This commit is contained in:
@@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS verification_codes (
|
||||
code VARCHAR(6) NOT NULL,
|
||||
provider VARCHAR(50) NOT NULL, -- 'telegram', 'email'
|
||||
provider_id VARCHAR(255) NOT NULL, -- telegram_id или email
|
||||
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
|
||||
user_id INTEGER NULL REFERENCES users(id) ON DELETE CASCADE, -- Может быть NULL
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMP NOT NULL,
|
||||
used BOOLEAN DEFAULT FALSE
|
||||
12
backend/db/migrations/013_update_verification_codes.sql
Normal file
12
backend/db/migrations/013_update_verification_codes.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
-- Изменяем ограничение для поля user_id в таблице verification_codes
|
||||
ALTER TABLE verification_codes
|
||||
ALTER COLUMN user_id DROP NOT NULL;
|
||||
|
||||
-- Обновляем комментарий в информационной схеме
|
||||
COMMENT ON COLUMN verification_codes.user_id IS 'ID пользователя (может быть NULL для временных кодов)';
|
||||
|
||||
-- Логирование для отслеживания выполнения миграции
|
||||
DO $$
|
||||
BEGIN
|
||||
RAISE NOTICE 'Migration 012: Updated verification_codes table to allow NULL values for user_id';
|
||||
END $$;
|
||||
@@ -17,6 +17,7 @@ const { initTelegramAuth } = require('../services/telegramBot');
|
||||
const emailAuth = require('../services/emailAuth');
|
||||
const verificationService = require('../services/verification-service');
|
||||
const { processGuestMessages } = require('./chat'); // Импортируем функцию обработки гостевых сообщений
|
||||
const nonceStore = {};
|
||||
|
||||
// Создайте лимитер для попыток аутентификации
|
||||
const authLimiter = rateLimit({
|
||||
@@ -221,138 +222,89 @@ router.post('/verify', async (req, res) => {
|
||||
// Аутентификация через Telegram
|
||||
router.post('/telegram/verify', async (req, res) => {
|
||||
try {
|
||||
const { code } = req.body;
|
||||
const { telegramId, verificationCode } = req.body;
|
||||
|
||||
logger.info(`[telegram/verify] Verifying code: ${code}`);
|
||||
|
||||
// Проверяем код верификации
|
||||
const verificationResult = await verifyTelegramCode(code);
|
||||
|
||||
if (!verificationResult.success) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Неверный код подтверждения'
|
||||
if (!telegramId || !verificationCode) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Missing required fields'
|
||||
});
|
||||
}
|
||||
|
||||
const { telegramId, authData } = verificationResult;
|
||||
logger.info(`[telegram/verify] Code verified successfully for telegramId: ${telegramId}`);
|
||||
|
||||
// Сохраняем гостевые ID до проверки
|
||||
const guestId = req.session.guestId;
|
||||
|
||||
let userId;
|
||||
|
||||
// Если пользователь уже аутентифицирован, добавляем Telegram к существующему аккаунту
|
||||
if (req.session.authenticated && req.session.userId) {
|
||||
userId = req.session.userId;
|
||||
|
||||
// Проверяем не связан ли Telegram с другим аккаунтом
|
||||
const existingTelegram = await db.query(`
|
||||
SELECT user_id FROM user_identities
|
||||
WHERE provider = 'telegram' AND provider_id = $1
|
||||
`, [telegramId]);
|
||||
|
||||
if (existingTelegram.rows.length > 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: 'Этот Telegram аккаунт уже связан с другим пользователем'
|
||||
});
|
||||
}
|
||||
|
||||
// Добавляем Telegram к существующему пользователю
|
||||
await saveUserIdentity(userId, 'telegram', telegramId, true);
|
||||
logger.info(`[telegram/verify] Added Telegram identity ${telegramId} to existing user ${userId}`);
|
||||
|
||||
} else {
|
||||
// Ищем существующего пользователя по Telegram ID
|
||||
const existingUser = await db.query(`
|
||||
SELECT u.* FROM users u
|
||||
JOIN user_identities ui ON u.id = ui.user_id
|
||||
WHERE ui.provider = 'telegram' AND ui.provider_id = $1
|
||||
`, [telegramId]);
|
||||
|
||||
if (existingUser.rows.length > 0) {
|
||||
// Используем существующего пользователя
|
||||
userId = existingUser.rows[0].id;
|
||||
logger.info(`[telegram/verify] Found existing user with ID ${userId} for telegramId ${telegramId}`);
|
||||
} else {
|
||||
// Создаем нового пользователя
|
||||
const newUser = await db.query(
|
||||
'INSERT INTO users (role) VALUES ($1) RETURNING id',
|
||||
['user']
|
||||
);
|
||||
userId = newUser.rows[0].id;
|
||||
|
||||
// Добавляем Telegram идентификатор
|
||||
await saveUserIdentity(userId, 'telegram', telegramId, true);
|
||||
logger.info(`[telegram/verify] Created new user with ID ${userId} for telegramId ${telegramId}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Если есть гостевые сообщения, переносим их
|
||||
if (guestId && !req.session.processedGuestIds?.includes(guestId)) {
|
||||
await processGuestMessages(userId, guestId);
|
||||
// Сохраняем обработанный guestId чтобы избежать повторной обработки
|
||||
if (!req.session.processedGuestIds) {
|
||||
req.session.processedGuestIds = [];
|
||||
}
|
||||
req.session.processedGuestIds.push(guestId);
|
||||
logger.info(`[telegram/verify] Processed guest messages for user ${userId} with guest ID ${guestId}`);
|
||||
}
|
||||
logger.info(`[telegram/verify] Verifying Telegram auth for ID: ${telegramId}`);
|
||||
|
||||
// Сохраняем гостевой ID из текущей сессии
|
||||
const guestId = req.session.guestId;
|
||||
|
||||
// Создаем новую сессию
|
||||
// Передаем сессию в метод верификации
|
||||
const verificationResult = await authService.verifyTelegramAuth(
|
||||
telegramId,
|
||||
verificationCode,
|
||||
req.session
|
||||
);
|
||||
|
||||
if (!verificationResult.success) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
error: verificationResult.error || 'Verification failed'
|
||||
});
|
||||
}
|
||||
|
||||
// Создаем новую сессию для этого telegramId
|
||||
req.session.regenerate(async (err) => {
|
||||
if (err) {
|
||||
logger.error('Error regenerating session:', err);
|
||||
return res.status(500).json({ success: false, error: 'Server error' });
|
||||
logger.error('[telegram/verify] Error regenerating session:', err);
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: 'Session regeneration failed'
|
||||
});
|
||||
}
|
||||
|
||||
// Устанавливаем данные новой сессии
|
||||
req.session.authenticated = true;
|
||||
req.session.userId = userId;
|
||||
|
||||
// Устанавливаем данные в новой сессии
|
||||
req.session.userId = verificationResult.userId;
|
||||
req.session.telegramId = telegramId;
|
||||
req.session.authType = 'telegram';
|
||||
req.session.authenticated = true;
|
||||
req.session.role = verificationResult.role;
|
||||
|
||||
// Сохраняем список обработанных гостевых ID
|
||||
if (req.session.processedGuestIds?.length > 0) {
|
||||
req.session.processedGuestIds = [...req.session.processedGuestIds];
|
||||
// Восстанавливаем гостевой ID, если он был
|
||||
if (guestId) {
|
||||
req.session.guestId = guestId;
|
||||
}
|
||||
|
||||
// Удаляем временные данные
|
||||
delete req.session.tempUserId;
|
||||
delete req.session.guestId;
|
||||
delete req.session.pendingTelegramId;
|
||||
|
||||
if (authData.first_name) {
|
||||
req.session.telegramFirstName = authData.first_name;
|
||||
}
|
||||
if (authData.username) {
|
||||
req.session.telegramUsername = authData.username;
|
||||
}
|
||||
|
||||
|
||||
// Сохраняем сессию
|
||||
req.session.save((err) => {
|
||||
if (err) {
|
||||
logger.error('Error saving session:', err);
|
||||
return res.status(500).json({ success: false, error: 'Server error' });
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
userId,
|
||||
telegramId,
|
||||
authenticated: true,
|
||||
authType: 'telegram',
|
||||
username: authData.username,
|
||||
firstName: authData.first_name
|
||||
await new Promise((resolve, reject) => {
|
||||
req.session.save(err => {
|
||||
if (err) {
|
||||
logger.error('[telegram/verify] Error saving session:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
logger.info(`[telegram/verify] Session saved for user ${verificationResult.userId}`);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Связываем гостевые сообщения только если это новый пользователь
|
||||
if (verificationResult.isNewUser && guestId) {
|
||||
const linkResults = await authService.linkGuestMessagesAfterAuth(verificationResult.userId, guestId);
|
||||
logger.info(`[telegram/verify] Guest messages linking results for new user:`, linkResults);
|
||||
}
|
||||
|
||||
return res.json({
|
||||
success: true,
|
||||
userId: verificationResult.userId,
|
||||
role: verificationResult.role,
|
||||
telegramId,
|
||||
isNewUser: verificationResult.isNewUser
|
||||
});
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('[telegram/verify] Error:', error);
|
||||
res.status(500).json({ success: false, error: 'Server error' });
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: 'Internal server error'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -729,29 +681,46 @@ router.get('/check', async (req, res) => {
|
||||
});
|
||||
|
||||
// Выход из системы
|
||||
router.post('/logout', (req, res) => {
|
||||
router.post('/logout', async (req, res) => {
|
||||
try {
|
||||
// Сохраняем важные данные перед уничтожением сессии
|
||||
const guestId = req.session.guestId;
|
||||
|
||||
// Полностью уничтожаем сессию
|
||||
req.session.destroy((err) => {
|
||||
if (err) {
|
||||
logger.error('Error destroying session:', err);
|
||||
return res.status(500).json({ success: false, error: 'Server error' });
|
||||
}
|
||||
|
||||
// Очищаем куки сессии
|
||||
res.clearCookie('connect.sid');
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
guestId // Возвращаем guestId для фронтенда
|
||||
// Очищаем все идентификаторы сессии
|
||||
req.session.authenticated = false;
|
||||
req.session.userId = null;
|
||||
req.session.address = null;
|
||||
req.session.telegramId = null;
|
||||
req.session.email = null;
|
||||
req.session.isAdmin = false;
|
||||
req.session.guestId = null;
|
||||
req.session.previousGuestId = null;
|
||||
req.session.processedGuestIds = [];
|
||||
req.session.pendingEmail = null;
|
||||
req.session.authType = null;
|
||||
|
||||
// Сохраняем изменения в сессии
|
||||
await new Promise((resolve, reject) => {
|
||||
req.session.save(err => {
|
||||
if (err) {
|
||||
logger.error('[logout] Error saving session:', err);
|
||||
reject(err);
|
||||
} else {
|
||||
logger.info('[logout] Session cleared successfully');
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Уничтожаем сессию полностью
|
||||
req.session.destroy((err) => {
|
||||
if (err) {
|
||||
logger.error('[logout] Error destroying session:', err);
|
||||
return res.status(500).json({ success: false, error: 'Error during logout' });
|
||||
}
|
||||
res.clearCookie('connect.sid');
|
||||
res.json({ success: true, message: 'Logged out successfully' });
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Error in logout:', error);
|
||||
res.status(500).json({ success: false, error: 'Server error' });
|
||||
logger.error('[logout] Error:', error);
|
||||
res.status(500).json({ success: false, error: 'Internal server error during logout' });
|
||||
}
|
||||
});
|
||||
|
||||
@@ -773,30 +742,20 @@ router.get('/telegram', (req, res) => {
|
||||
// Маршрут для получения кода подтверждения Telegram
|
||||
router.get('/telegram/code', authLimiter, async (req, res) => {
|
||||
try {
|
||||
// Создаем или получаем ID пользователя
|
||||
let userId;
|
||||
// Создаем код через сервис телеграм авторизации
|
||||
const authData = await initTelegramAuth(req.session);
|
||||
|
||||
if (req.session.authenticated && req.session.userId) {
|
||||
userId = req.session.userId;
|
||||
} else {
|
||||
const userResult = await db.query(
|
||||
'INSERT INTO users (created_at) VALUES (NOW()) RETURNING id'
|
||||
);
|
||||
userId = userResult.rows[0].id;
|
||||
req.session.tempUserId = userId;
|
||||
if (!authData.verificationCode) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
error: 'Failed to generate verification code'
|
||||
});
|
||||
}
|
||||
|
||||
// Создаем код через сервис верификации
|
||||
const code = await verificationService.createVerificationCode(
|
||||
'telegram',
|
||||
req.session.guestId || 'temp',
|
||||
userId
|
||||
);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Отправьте этот код боту @' + process.env.TELEGRAM_BOT_USERNAME,
|
||||
code,
|
||||
code: authData.verificationCode,
|
||||
botUsername: process.env.TELEGRAM_BOT_USERNAME || 'YourDAppBot'
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -393,60 +393,63 @@ class AuthService {
|
||||
/**
|
||||
* Проверка Telegram аутентификации
|
||||
*/
|
||||
async verifyTelegramAuth(telegramId, verificationCode) {
|
||||
async verifyTelegramAuth(telegramId, verificationCode, session) {
|
||||
try {
|
||||
logger.info(`Verifying Telegram auth for ID: ${telegramId} with code: ${verificationCode}`);
|
||||
logger.info(`[verifyTelegramAuth] Starting for telegramId: ${telegramId}`);
|
||||
|
||||
// Находим или создаем пользователя
|
||||
const userResult = await db.query(
|
||||
`SELECT u.* FROM users u
|
||||
// Проверяем, существует ли уже пользователь с таким Telegram ID
|
||||
const existingUserResult = await db.query(
|
||||
`SELECT u.*, ui.provider, ui.provider_id
|
||||
FROM users u
|
||||
JOIN user_identities ui ON u.id = ui.user_id
|
||||
WHERE ui.provider = 'telegram' AND ui.provider_id = $1`,
|
||||
[telegramId]
|
||||
);
|
||||
|
||||
|
||||
let userId;
|
||||
if (userResult.rows.length > 0) {
|
||||
userId = userResult.rows[0].id;
|
||||
logger.info(`Found existing user ${userId} for Telegram ID ${telegramId}`);
|
||||
let isNewUser = false;
|
||||
|
||||
// Если пользователь существует с таким telegramId, используем его
|
||||
if (existingUserResult.rows.length > 0) {
|
||||
const existingUser = existingUserResult.rows[0];
|
||||
userId = existingUser.id;
|
||||
logger.info(`[verifyTelegramAuth] Found existing user ${userId} for Telegram ID ${telegramId}`);
|
||||
} else {
|
||||
// Создаем нового пользователя с ролью user
|
||||
// Создаем нового пользователя для нового telegramId
|
||||
const newUserResult = await db.query(
|
||||
'INSERT INTO users (role) VALUES ($1) RETURNING id',
|
||||
['user']
|
||||
);
|
||||
userId = newUserResult.rows[0].id;
|
||||
isNewUser = true;
|
||||
|
||||
// Добавляем Telegram идентификатор
|
||||
await db.query(
|
||||
'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)',
|
||||
[userId, 'telegram', telegramId]
|
||||
);
|
||||
logger.info(`Created new user ${userId} for Telegram ID ${telegramId}`);
|
||||
|
||||
logger.info(`[verifyTelegramAuth] Created new user ${userId} for Telegram ID ${telegramId}`);
|
||||
}
|
||||
|
||||
// Проверяем наличие кошелька и определяем роль
|
||||
const wallet = await this.getLinkedWallet(userId);
|
||||
let role = 'user'; // Базовая роль для доступа к чату
|
||||
|
||||
if (wallet) {
|
||||
// Если есть кошелек, проверяем баланс токенов
|
||||
const isAdmin = await this.checkAdminRole(wallet);
|
||||
role = isAdmin ? 'admin' : 'user';
|
||||
logger.info(`User ${userId} has wallet ${wallet}, role set to ${role}`);
|
||||
} else {
|
||||
logger.info(`User ${userId} has no wallet, using basic user role`);
|
||||
|
||||
// Если есть гостевой ID в сессии, сохраняем его для нового пользователя
|
||||
if (session.guestId && isNewUser) {
|
||||
await db.query(
|
||||
'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING',
|
||||
[userId, 'guest', session.guestId]
|
||||
);
|
||||
logger.info(`[verifyTelegramAuth] Saved guest ID ${session.guestId} for user ${userId}`);
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
success: true,
|
||||
userId,
|
||||
role,
|
||||
role: 'user',
|
||||
telegramId,
|
||||
wallet: wallet || null
|
||||
isNewUser
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Error in Telegram auth verification:', error);
|
||||
logger.error('[verifyTelegramAuth] Error:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -574,68 +577,104 @@ class AuthService {
|
||||
return { success: true, message: 'No guest ID to process' };
|
||||
}
|
||||
|
||||
// Получаем все гостевые сообщения для этого ID
|
||||
const guestMessagesResult = await db.query(
|
||||
'SELECT * FROM guest_messages WHERE guest_id = $1 ORDER BY created_at ASC',
|
||||
// Проверяем, не привязаны ли уже эти гостевые сообщения к другому пользователю
|
||||
const existingMessagesCheck = await db.query(
|
||||
`SELECT DISTINCT user_id
|
||||
FROM messages
|
||||
WHERE guest_message_id IN (
|
||||
SELECT id FROM guest_messages WHERE guest_id = $1
|
||||
)`,
|
||||
[currentGuestId]
|
||||
);
|
||||
|
||||
if (guestMessagesResult.rows.length === 0) {
|
||||
logger.info(`[linkGuestMessagesAfterAuth] No messages found for guest ID ${currentGuestId}`);
|
||||
return { success: true, message: 'No messages found' };
|
||||
}
|
||||
|
||||
const guestMessages = guestMessagesResult.rows;
|
||||
logger.info(`[linkGuestMessagesAfterAuth] Found ${guestMessages.length} messages for guest ID ${currentGuestId}`);
|
||||
|
||||
// Создаем одну беседу для всех сообщений этого гостевого ID
|
||||
const firstMessage = guestMessages[0];
|
||||
const title = firstMessage.content.length > 30
|
||||
? `${firstMessage.content.substring(0, 30)}...`
|
||||
: firstMessage.content;
|
||||
|
||||
const newConversationResult = await db.query(
|
||||
'INSERT INTO conversations (user_id, title) VALUES ($1, $2) RETURNING *',
|
||||
[userId, title]
|
||||
);
|
||||
|
||||
const conversation = newConversationResult.rows[0];
|
||||
logger.info(`[linkGuestMessagesAfterAuth] Created conversation ${conversation.id} for user ${userId}`);
|
||||
|
||||
// Переносим все сообщения в новую беседу
|
||||
for (const guestMessage of guestMessages) {
|
||||
await db.query(
|
||||
`INSERT INTO messages
|
||||
(conversation_id, content, sender_type, role, channel, guest_message_id, created_at)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6, $7)`,
|
||||
[
|
||||
conversation.id,
|
||||
guestMessage.content,
|
||||
guestMessage.is_ai ? 'assistant' : 'user',
|
||||
guestMessage.is_ai ? 'assistant' : 'user',
|
||||
'web',
|
||||
guestMessage.id,
|
||||
guestMessage.created_at
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Удаляем гостевой идентификатор после успешной привязки
|
||||
await db.query(
|
||||
'DELETE FROM user_identities WHERE user_id = $1 AND identity_type = $2 AND identity_value = $3',
|
||||
[userId, 'guest', currentGuestId]
|
||||
);
|
||||
logger.info(`[linkGuestMessagesAfterAuth] Deleted guest identity ${currentGuestId}`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
conversationId: conversation.id,
|
||||
message: `Processed ${guestMessages.length} guest messages`,
|
||||
success: true
|
||||
if (existingMessagesCheck.rows.length > 0) {
|
||||
const existingUserId = existingMessagesCheck.rows[0].user_id;
|
||||
if (existingUserId !== userId) {
|
||||
logger.warn(`[linkGuestMessagesAfterAuth] Guest messages for ${currentGuestId} are already linked to user ${existingUserId}`);
|
||||
return {
|
||||
success: false,
|
||||
error: 'Guest messages are already linked to another user'
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Блокируем таблицу guest_messages для атомарной операции
|
||||
await db.query('BEGIN');
|
||||
|
||||
try {
|
||||
// Получаем все гостевые сообщения для этого ID
|
||||
const guestMessagesResult = await db.query(
|
||||
'SELECT * FROM guest_messages WHERE guest_id = $1 ORDER BY created_at ASC FOR UPDATE',
|
||||
[currentGuestId]
|
||||
);
|
||||
|
||||
if (guestMessagesResult.rows.length === 0) {
|
||||
await db.query('COMMIT');
|
||||
logger.info(`[linkGuestMessagesAfterAuth] No messages found for guest ID ${currentGuestId}`);
|
||||
return { success: true, message: 'No messages found' };
|
||||
}
|
||||
|
||||
const guestMessages = guestMessagesResult.rows;
|
||||
logger.info(`[linkGuestMessagesAfterAuth] Found ${guestMessages.length} messages for guest ID ${currentGuestId}`);
|
||||
|
||||
// Создаем одну беседу для всех сообщений этого гостевого ID
|
||||
const firstMessage = guestMessages[0];
|
||||
const title = firstMessage.content.length > 30
|
||||
? `${firstMessage.content.substring(0, 30)}...`
|
||||
: firstMessage.content;
|
||||
|
||||
const newConversationResult = await db.query(
|
||||
'INSERT INTO conversations (user_id, title) VALUES ($1, $2) RETURNING *',
|
||||
[userId, title]
|
||||
);
|
||||
|
||||
const conversation = newConversationResult.rows[0];
|
||||
logger.info(`[linkGuestMessagesAfterAuth] Created conversation ${conversation.id} for user ${userId}`);
|
||||
|
||||
// Переносим все сообщения в новую беседу
|
||||
for (const guestMessage of guestMessages) {
|
||||
await db.query(
|
||||
`INSERT INTO messages
|
||||
(conversation_id, content, sender_type, role, channel, guest_message_id, created_at, user_id)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5, $6, $7, $8)`,
|
||||
[
|
||||
conversation.id,
|
||||
guestMessage.content,
|
||||
guestMessage.is_ai ? 'assistant' : 'user',
|
||||
guestMessage.is_ai ? 'assistant' : 'user',
|
||||
'web',
|
||||
guestMessage.id,
|
||||
guestMessage.created_at,
|
||||
userId
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Удаляем обработанные гостевые сообщения
|
||||
await db.query('DELETE FROM guest_messages WHERE guest_id = $1', [currentGuestId]);
|
||||
|
||||
// Удаляем гостевой идентификатор
|
||||
await db.query(
|
||||
'DELETE FROM user_identities WHERE user_id = $1 AND provider = $2 AND provider_id = $3',
|
||||
[userId, 'guest', currentGuestId]
|
||||
);
|
||||
|
||||
await db.query('COMMIT');
|
||||
logger.info(`[linkGuestMessagesAfterAuth] Successfully processed guest ID ${currentGuestId}`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
result: {
|
||||
conversationId: conversation.id,
|
||||
message: `Processed ${guestMessages.length} guest messages`,
|
||||
success: true
|
||||
}
|
||||
};
|
||||
} catch (error) {
|
||||
await db.query('ROLLBACK');
|
||||
throw error;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('[linkGuestMessagesAfterAuth] Error:', error);
|
||||
throw error;
|
||||
|
||||
@@ -3,6 +3,7 @@ const logger = require('../utils/logger');
|
||||
const db = require('../db');
|
||||
const authService = require('./auth-service');
|
||||
const verificationService = require('./verification-service');
|
||||
const crypto = require('crypto');
|
||||
|
||||
let botInstance = null;
|
||||
|
||||
@@ -38,7 +39,7 @@ async function getBot() {
|
||||
|
||||
const verification = codeResult.rows[0];
|
||||
const providerId = verification.provider_id;
|
||||
let userId = verification.user_id;
|
||||
let userId;
|
||||
|
||||
// Отмечаем код как использованный
|
||||
await db.query(
|
||||
@@ -57,40 +58,37 @@ async function getBot() {
|
||||
);
|
||||
|
||||
if (existingTelegramUser.rows.length > 0) {
|
||||
// Если пользователь с таким Telegram ID уже существует,
|
||||
// используем его ID вместо создания нового связывания
|
||||
const existingUserId = existingTelegramUser.rows[0].user_id;
|
||||
// Если пользователь с таким Telegram ID уже существует, используем его
|
||||
userId = existingTelegramUser.rows[0].user_id;
|
||||
logger.info(`Using existing user ${userId} for Telegram account ${ctx.from.id}`);
|
||||
} else {
|
||||
// Создаем нового пользователя, если нет существующего с этим Telegram ID
|
||||
const userResult = await db.query(
|
||||
'INSERT INTO users (created_at, role) VALUES (NOW(), $1) RETURNING id',
|
||||
['user']
|
||||
);
|
||||
userId = userResult.rows[0].id;
|
||||
|
||||
// Связываем гостевой ID с существующим пользователем, если его еще нет
|
||||
const guestIdentity = await db.query(
|
||||
`SELECT * FROM user_identities
|
||||
WHERE user_id = $1 AND provider = 'guest' AND provider_id = $2`,
|
||||
[existingUserId, providerId]
|
||||
// Связываем Telegram с новым пользователем
|
||||
await db.query(
|
||||
`INSERT INTO user_identities
|
||||
(user_id, provider, provider_id, created_at)
|
||||
VALUES ($1, $2, $3, NOW())`,
|
||||
[userId, 'telegram', ctx.from.id.toString()]
|
||||
);
|
||||
|
||||
if (guestIdentity.rows.length === 0 && providerId) {
|
||||
// Если был гостевой ID, связываем его с новым пользователем
|
||||
if (providerId) {
|
||||
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`,
|
||||
[existingUserId, 'guest', providerId]
|
||||
ON CONFLICT (provider, provider_id) DO NOTHING`,
|
||||
[userId, 'guest', providerId]
|
||||
);
|
||||
}
|
||||
|
||||
userId = existingUserId;
|
||||
logger.info(`Using existing user ${userId} for Telegram account ${ctx.from.id}`);
|
||||
} else {
|
||||
// Связываем 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}`);
|
||||
logger.info(`Created new user ${userId} with Telegram account ${ctx.from.id}`);
|
||||
}
|
||||
|
||||
// Обновляем сессию в базе данных
|
||||
@@ -149,58 +147,19 @@ async function stopBot() {
|
||||
// Инициализация процесса аутентификации
|
||||
async function initTelegramAuth(session) {
|
||||
try {
|
||||
// Создаем или получаем ID пользователя
|
||||
let userId;
|
||||
// Используем временный идентификатор для создания кода верификации
|
||||
// Реальный пользователь будет создан или найден при проверке кода через бота
|
||||
const tempId = crypto.randomBytes(16).toString('hex');
|
||||
|
||||
if (session.authenticated && session.userId) {
|
||||
// Если пользователь уже аутентифицирован, используем его ID
|
||||
userId = session.userId;
|
||||
} else if (session.guestId) {
|
||||
// Проверяем, есть ли уже пользователь с этим guestId
|
||||
const existingUser = await db.query(
|
||||
`SELECT u.id
|
||||
FROM users u
|
||||
JOIN user_identities ui ON u.id = ui.user_id
|
||||
WHERE ui.provider = 'guest' AND ui.provider_id = $1`,
|
||||
[session.guestId]
|
||||
);
|
||||
|
||||
if (existingUser.rows.length > 0) {
|
||||
// Используем существующего пользователя
|
||||
userId = existingUser.rows[0].id;
|
||||
} else {
|
||||
// Создаем нового пользователя
|
||||
const userResult = await db.query(
|
||||
'INSERT INTO users (created_at) VALUES (NOW()) RETURNING id'
|
||||
);
|
||||
userId = userResult.rows[0].id;
|
||||
|
||||
// Связываем гостевой ID с пользователем
|
||||
await db.query(
|
||||
`INSERT INTO user_identities
|
||||
(user_id, provider, provider_id, created_at)
|
||||
VALUES ($1, $2, $3, NOW())`,
|
||||
[userId, 'guest', session.guestId]
|
||||
);
|
||||
}
|
||||
|
||||
session.tempUserId = userId;
|
||||
} else {
|
||||
// Создаем нового пользователя без гостевого ID
|
||||
const userResult = await db.query(
|
||||
'INSERT INTO users (created_at) VALUES (NOW()) RETURNING id'
|
||||
);
|
||||
userId = userResult.rows[0].id;
|
||||
session.tempUserId = userId;
|
||||
}
|
||||
|
||||
// Создаем код через сервис верификации
|
||||
// Создаем код через сервис верификации с временным идентификатором
|
||||
const code = await verificationService.createVerificationCode(
|
||||
'telegram',
|
||||
session.guestId || 'temp',
|
||||
userId
|
||||
session.guestId || tempId,
|
||||
null // Не привязываем к конкретному userId на этом этапе
|
||||
);
|
||||
|
||||
logger.info(`[initTelegramAuth] Created verification code for guestId: ${session.guestId || tempId}`);
|
||||
|
||||
return {
|
||||
verificationCode: code,
|
||||
botLink: `https://t.me/${process.env.TELEGRAM_BOT_USERNAME}`
|
||||
|
||||
@@ -20,14 +20,24 @@ class VerificationService {
|
||||
const expiresAt = new Date(Date.now() + this.expirationMinutes * 60 * 1000);
|
||||
|
||||
try {
|
||||
logger.info(`Creating verification code for ${provider}:${providerId}, userId: ${userId}`);
|
||||
logger.info(`Creating verification code for ${provider}:${providerId}, userId: ${userId || 'null'}`);
|
||||
|
||||
await db.query(
|
||||
`INSERT INTO verification_codes
|
||||
(code, provider, provider_id, user_id, expires_at)
|
||||
VALUES ($1, $2, $3, $4, $5)`,
|
||||
[code, provider, providerId, userId, expiresAt]
|
||||
);
|
||||
// Если userId не указан, добавляем запись без ссылки на пользователя
|
||||
if (userId === null || userId === undefined) {
|
||||
await db.query(
|
||||
`INSERT INTO verification_codes
|
||||
(code, provider, provider_id, expires_at)
|
||||
VALUES ($1, $2, $3, $4)`,
|
||||
[code, provider, providerId, expiresAt]
|
||||
);
|
||||
} else {
|
||||
await db.query(
|
||||
`INSERT INTO verification_codes
|
||||
(code, provider, provider_id, user_id, expires_at)
|
||||
VALUES ($1, $2, $3, $4, $5)`,
|
||||
[code, provider, providerId, userId, expiresAt]
|
||||
);
|
||||
}
|
||||
|
||||
logger.info(`Verification code created successfully for ${provider}:${providerId}`);
|
||||
return code;
|
||||
|
||||
77
backend/update_verification_table.js
Normal file
77
backend/update_verification_table.js
Normal file
@@ -0,0 +1,77 @@
|
||||
// Скрипт для обновления таблицы verification_codes
|
||||
const { Pool } = require('pg');
|
||||
require('dotenv').config();
|
||||
|
||||
// Создаем подключение к базе данных
|
||||
const pool = new Pool({
|
||||
host: process.env.POSTGRES_HOST || 'localhost',
|
||||
port: process.env.POSTGRES_PORT || 5432,
|
||||
database: process.env.POSTGRES_DB || 'dapp_business',
|
||||
user: process.env.POSTGRES_USER || 'postgres',
|
||||
password: process.env.POSTGRES_PASSWORD || 'postgres',
|
||||
});
|
||||
|
||||
async function updateVerificationTable() {
|
||||
try {
|
||||
console.log('Начинаем обновление таблицы verification_codes...');
|
||||
|
||||
// Проверяем, существует ли таблица
|
||||
const checkTableResult = await pool.query(`
|
||||
SELECT EXISTS (
|
||||
SELECT FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'verification_codes'
|
||||
);
|
||||
`);
|
||||
|
||||
const tableExists = checkTableResult.rows[0].exists;
|
||||
|
||||
if (!tableExists) {
|
||||
console.log('Таблица verification_codes не существует. Пропускаем обновление.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Проверяем, разрешает ли уже колонка null значения
|
||||
const checkColumnResult = await pool.query(`
|
||||
SELECT is_nullable
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = 'verification_codes'
|
||||
AND column_name = 'user_id';
|
||||
`);
|
||||
|
||||
if (checkColumnResult.rows.length > 0 && checkColumnResult.rows[0].is_nullable === 'YES') {
|
||||
console.log('Колонка user_id уже разрешает NULL значения. Пропускаем обновление.');
|
||||
return;
|
||||
}
|
||||
|
||||
// Начинаем транзакцию
|
||||
await pool.query('BEGIN');
|
||||
|
||||
// Изменяем ограничение для поля user_id
|
||||
await pool.query(`
|
||||
ALTER TABLE verification_codes
|
||||
ALTER COLUMN user_id DROP NOT NULL;
|
||||
`);
|
||||
|
||||
// Добавляем комментарий к колонке
|
||||
await pool.query(`
|
||||
COMMENT ON COLUMN verification_codes.user_id IS 'ID пользователя (может быть NULL для временных кодов)';
|
||||
`);
|
||||
|
||||
// Фиксируем транзакцию
|
||||
await pool.query('COMMIT');
|
||||
|
||||
console.log('Таблица verification_codes успешно обновлена!');
|
||||
} catch (error) {
|
||||
// Откатываем транзакцию в случае ошибки
|
||||
await pool.query('ROLLBACK');
|
||||
console.error('Ошибка при обновлении таблицы verification_codes:', error);
|
||||
} finally {
|
||||
// Закрываем соединение с базой данных
|
||||
await pool.end();
|
||||
}
|
||||
}
|
||||
|
||||
// Выполняем обновление
|
||||
updateVerificationTable();
|
||||
Reference in New Issue
Block a user