Files
DLE/backend/services/identity-service.js

304 lines
12 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const db = require('../db');
const logger = require('../utils/logger');
/**
* Сервис для работы с идентификаторами пользователей
*/
class IdentityService {
/**
* Сохраняет идентификатор пользователя в базу данных
* @param {number} userId - ID пользователя
* @param {string} provider - Тип идентификатора (wallet, email, telegram, guest)
* @param {string} providerId - Значение идентификатора
* @param {boolean} verified - Флаг верификации идентификатора
* @returns {Promise<object>} - Результат операции
*/
async saveIdentity(userId, provider, providerId, verified = true) {
try {
if (!userId || !provider || !providerId) {
logger.warn(`[IdentityService] Missing required parameters: userId=${userId}, provider=${provider}, providerId=${providerId}`);
return {
success: false,
error: 'Missing required parameters'
};
}
logger.info(`[IdentityService] Saving identity for user ${userId}: ${provider}:${providerId}`);
// Проверяем, существует ли уже такой идентификатор
const existingResult = await db.query(
`SELECT user_id FROM user_identities WHERE provider = $1 AND provider_id = $2`,
[provider, providerId]
);
if (existingResult.rows.length > 0) {
const existingUserId = existingResult.rows[0].user_id;
// Если идентификатор уже принадлежит этому пользователю, ничего не делаем
if (existingUserId === userId) {
logger.info(`[IdentityService] Identity ${provider}:${providerId} already exists for user ${userId}`);
} else {
// Если идентификатор принадлежит другому пользователю, логируем это
logger.warn(`[IdentityService] Identity ${provider}:${providerId} already belongs to user ${existingUserId}, not user ${userId}`);
return {
success: false,
error: `Identity already belongs to another user (${existingUserId})`
};
}
} else {
// Создаем новую запись
await db.query(
`INSERT INTO user_identities (user_id, provider, provider_id)
VALUES ($1, $2, $3)`,
[userId, provider, providerId]
);
logger.info(`[IdentityService] Created new identity ${provider}:${providerId} for user ${userId}`);
}
return { success: true };
} catch (error) {
logger.error(`[IdentityService] Error saving identity ${provider}:${providerId} for user ${userId}:`, error);
return { success: false, error: error.message };
}
}
/**
* Получает все идентификаторы пользователя
* @param {number} userId - ID пользователя
* @returns {Promise<Array>} - Массив идентификаторов
*/
async getUserIdentities(userId) {
try {
if (!userId) {
logger.warn('[IdentityService] Missing userId parameter');
return [];
}
const result = await db.query(
`SELECT provider, provider_id FROM user_identities WHERE user_id = $1`,
[userId]
);
logger.info(`[IdentityService] Found ${result.rows.length} identities for user ${userId}`);
return result.rows;
} catch (error) {
logger.error(`[IdentityService] Error getting identities for user ${userId}:`, error);
return [];
}
}
/**
* Получает все идентификаторы пользователя определенного типа
* @param {number} userId - ID пользователя
* @param {string} provider - Тип идентификатора
* @returns {Promise<Array>} - Массив идентификаторов
*/
async getUserIdentitiesByProvider(userId, provider) {
try {
if (!userId || !provider) {
logger.warn(`[IdentityService] Missing parameters: userId=${userId}, provider=${provider}`);
return [];
}
const result = await db.query(
`SELECT provider_id FROM user_identities WHERE user_id = $1 AND provider = $2`,
[userId, provider]
);
logger.info(`[IdentityService] Found ${result.rows.length} ${provider} identities for user ${userId}`);
return result.rows.map(row => row.provider_id);
} catch (error) {
logger.error(`[IdentityService] Error getting ${provider} identities for user ${userId}:`, error);
return [];
}
}
/**
* Находит пользователя по идентификатору
* @param {string} provider - Тип идентификатора
* @param {string} providerId - Значение идентификатора
* @returns {Promise<object|null>} - Информация о пользователе или null
*/
async findUserByIdentity(provider, providerId) {
try {
if (!provider || !providerId) {
logger.warn(`[IdentityService] Missing parameters: provider=${provider}, providerId=${providerId}`);
return null;
}
const result = await db.query(
`SELECT u.id, u.role FROM users u
JOIN user_identities ui ON u.id = ui.user_id
WHERE ui.provider = $1 AND ui.provider_id = $2`,
[provider, providerId]
);
if (result.rows.length === 0) {
logger.info(`[IdentityService] No user found with identity ${provider}:${providerId}`);
return null;
}
logger.info(`[IdentityService] Found user ${result.rows[0].id} with identity ${provider}:${providerId}`);
return result.rows[0];
} catch (error) {
logger.error(`[IdentityService] Error finding user by identity ${provider}:${providerId}:`, error);
return null;
}
}
/**
* Сохраняет идентификаторы из сессии для пользователя
* @param {object} session - Объект сессии
* @param {number} userId - ID пользователя
* @returns {Promise<object>} - Результат операции
*/
async saveIdentitiesFromSession(session, userId) {
try {
if (!session || !userId) {
logger.warn(`[IdentityService] Missing parameters: session=${!!session}, userId=${userId}`);
return { success: false, error: 'Missing required parameters' };
}
const results = [];
// Сохраняем все постоянные идентификаторы из сессии
if (session.email) {
const emailResult = await this.saveIdentity(userId, 'email', session.email.toLowerCase(), true);
results.push({ type: 'email', result: emailResult });
}
if (session.address) {
const walletResult = await this.saveIdentity(userId, 'wallet', session.address.toLowerCase(), true);
results.push({ type: 'wallet', result: walletResult });
}
if (session.telegramId) {
const telegramResult = await this.saveIdentity(userId, 'telegram', session.telegramId, true);
results.push({ type: 'telegram', result: telegramResult });
}
// Сохраняем гостевые идентификаторы
if (session.guestId) {
const guestResult = await this.saveIdentity(userId, 'guest', session.guestId, true);
results.push({ type: 'guest', result: guestResult });
}
if (session.previousGuestId && session.previousGuestId !== session.guestId) {
const prevGuestResult = await this.saveIdentity(userId, 'guest', session.previousGuestId, true);
results.push({ type: 'previousGuest', result: prevGuestResult });
}
logger.info(`[IdentityService] Saved ${results.length} identities from session for user ${userId}`);
return { success: true, results };
} catch (error) {
logger.error(`[IdentityService] Error saving identities from session for user ${userId}:`, error);
return { success: false, error: error.message };
}
}
/**
* Мигрирует все идентификаторы и сообщения от одного пользователя к другому
* @param {number} fromUserId - ID исходного пользователя
* @param {number} toUserId - ID целевого пользователя
* @returns {Promise<object>} - Результат операции
*/
async migrateUserData(fromUserId, toUserId) {
try {
if (!fromUserId || !toUserId) {
logger.warn(`[IdentityService] Missing parameters: fromUserId=${fromUserId}, toUserId=${toUserId}`);
return { success: false, error: 'Missing required parameters' };
}
// Начинаем транзакцию
const client = await db.pool.connect();
try {
await client.query('BEGIN');
// Получаем все идентификаторы исходного пользователя
const identitiesResult = await client.query(
`SELECT provider, provider_id FROM user_identities WHERE user_id = $1`,
[fromUserId]
);
// Переносим каждый идентификатор
for (const identity of identitiesResult.rows) {
await client.query(
`UPDATE user_identities
SET user_id = $1
WHERE user_id = $2 AND provider = $3 AND provider_id = $4`,
[toUserId, fromUserId, identity.provider, identity.provider_id]
);
}
// Переносим все сообщения
await client.query(
`UPDATE messages
SET user_id = $1
WHERE user_id = $2`,
[toUserId, fromUserId]
);
// Переносим все диалоги
await client.query(
`UPDATE conversations
SET user_id = $1
WHERE user_id = $2`,
[toUserId, fromUserId]
);
// Удаляем исходного пользователя
await client.query(
`DELETE FROM users WHERE id = $1`,
[fromUserId]
);
await client.query('COMMIT');
logger.info(`[IdentityService] Successfully migrated data from user ${fromUserId} to user ${toUserId}`);
return {
success: true,
migratedIdentities: identitiesResult.rows.length
};
} catch (error) {
await client.query('ROLLBACK');
throw error;
} finally {
client.release();
}
} catch (error) {
logger.error(`[IdentityService] Error migrating data from user ${fromUserId} to user ${toUserId}:`, error);
return { success: false, error: error.message };
}
}
/**
* Находит всех пользователей с похожими идентификаторами
* @param {object} identities - Объект с идентификаторами
* @returns {Promise<Array>} - Массив ID пользователей
*/
async findRelatedUsers(identities) {
try {
const userIds = new Set();
for (const [provider, providerId] of Object.entries(identities)) {
if (!providerId) continue;
const result = await db.query(
`SELECT DISTINCT user_id
FROM user_identities
WHERE provider = $1 AND provider_id = $2`,
[provider, providerId]
);
result.rows.forEach(row => userIds.add(row.user_id));
}
return Array.from(userIds);
} catch (error) {
logger.error(`[IdentityService] Error finding related users:`, error);
return [];
}
}
}
module.exports = new IdentityService();