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

This commit is contained in:
2025-08-04 14:16:05 +03:00
parent e30d672c52
commit df37507bbe
22 changed files with 421 additions and 273 deletions

View File

@@ -103,10 +103,11 @@ class AuthService {
const userId = newUserResult.rows[0].id;
// Добавляем идентификатор кошелька (всегда в нижнем регистре)
await db.getQuery()(
'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)',
[userId, 'wallet', normalizedAddress]
);
await encryptedDb.saveData('user_identities', {
user_id: userId,
provider: 'wallet',
provider_id: normalizedAddress
});
// Проверяем, есть ли у пользователя роль админа
const isAdmin = await checkAdminRole(normalizedAddress);
@@ -400,11 +401,11 @@ class AuthService {
// Если в сессии нет авторизованного пользователя, проверяем существующие идентификаторы
// Проверяем, существует ли уже пользователь с таким Telegram ID
const existingUserResult = await db.getQuery()(
`SELECT u.*, ui.provider, ui.provider_id
`SELECT u.*, decrypt_text(ui.provider_encrypted, $2) as provider, decrypt_text(ui.provider_id_encrypted, $2) as 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]
WHERE ui.provider_encrypted = encrypt_text('telegram', $2) AND ui.provider_id_encrypted = encrypt_text($1, $2)`,
[telegramId, encryptionKey]
);
// Если пользователь существует с таким telegramId, используем его
@@ -423,10 +424,11 @@ class AuthService {
isNewUser = true;
// Добавляем Telegram идентификатор
await db.getQuery()(
'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)',
[userId, 'telegram', telegramId]
);
await encryptedDb.saveData('user_identities', {
user_id: userId,
provider: 'telegram',
provider_id: telegramId
});
logger.info(
`[verifyTelegramAuth] Created new user ${userId} for Telegram ID ${telegramId}`
@@ -582,10 +584,34 @@ class AuthService {
async getUserIdentities(userId) {
try {
const identities = await encryptedDb.getData('user_identities', { user_id: userId }, null, 'created_at DESC');
return identities;
// Данные уже расшифрованы encryptedDb, просто переименовываем поля
const formattedIdentities = identities.map(identity => ({
id: identity.id,
user_id: identity.user_id,
created_at: identity.created_at,
provider: identity.provider, // Уже расшифровано
provider_id: identity.provider_id // Уже расшифровано
}));
return formattedIdentities;
} catch (error) {
logger.error('[getUserIdentities] Error:', error);
throw error;
// Если произошла ошибка расшифровки, попробуем получить данные напрямую
try {
logger.info(`[AuthService] Trying to get unencrypted data for user ${userId}`);
const { rows } = await db.getQuery()(
'SELECT id, user_id, created_at, provider_encrypted as provider, provider_id_encrypted as provider_id FROM user_identities WHERE user_id = $1 ORDER BY created_at DESC',
[userId]
);
logger.info(`[AuthService] Found ${rows.length} unencrypted identities for user ${userId}`);
return rows;
} catch (fallbackError) {
logger.error(`[AuthService] Fallback error getting identities for user ${userId}:`, fallbackError);
throw fallbackError;
}
}
}
@@ -667,11 +693,11 @@ class AuthService {
}
// Добавляем новый идентификатор для пользователя
await db.getQuery()(
`INSERT INTO user_identities (user_id, provider, provider_id)
VALUES ($1, $2, $3)`,
[userId, provider, normalizedProviderId]
);
await encryptedDb.saveData('user_identities', {
user_id: userId,
provider: provider,
provider_id: normalizedProviderId
});
// Проверяем и обновляем роль администратора, если это идентификатор кошелька
let isAdmin = false;

View File

@@ -15,7 +15,7 @@ const path = require('path');
const fs = require('fs');
const { ethers } = require('ethers');
const logger = require('../utils/logger');
const { getRpcUrlByNetworkId } = require('./rpcProviderService');
const { getRpcUrlByChainId } = require('./rpcProviderService');
/**
* Сервис для управления DLE v2 (Digital Legal Entity)
@@ -49,10 +49,15 @@ class DLEV2Service {
fs.copyFileSync(paramsFile, tempParamsFile);
logger.info(`Файл параметров скопирован успешно`);
// Получаем rpc_url из базы по выбранной сети
const rpcUrl = await getRpcUrlByNetworkId(deployParams.network);
// Определяем сеть для деплоя (берем первую из выбранных сетей)
const chainId = deployParams.supportedChainIds && deployParams.supportedChainIds.length > 0
? deployParams.supportedChainIds[0]
: 1; // По умолчанию Ethereum
// Получаем rpc_url из базы по chain_id
const rpcUrl = await getRpcUrlByChainId(chainId);
if (!rpcUrl) {
throw new Error(`RPC URL для сети ${deployParams.network} не найден в базе данных`);
throw new Error(`RPC URL для сети с chain_id ${chainId} не найден в базе данных`);
}
if (!dleParams.privateKey) {
throw new Error('Приватный ключ для деплоя не передан');
@@ -62,8 +67,8 @@ class DLEV2Service {
const result = await this.runDeployScript(paramsFile, {
rpcUrl,
privateKey: dleParams.privateKey,
networkId: deployParams.network,
envNetworkKey: deployParams.network.toUpperCase()
networkId: chainId.toString(),
envNetworkKey: chainId.toString().toUpperCase()
});
// Очищаем временные файлы
@@ -94,61 +99,73 @@ class DLEV2Service {
throw new Error('Местонахождение DLE обязательно');
}
if (!params.partners || !Array.isArray(params.partners)) {
if (!params.initialPartners || !Array.isArray(params.initialPartners)) {
throw new Error('Партнеры должны быть массивом');
}
if (!params.amounts || !Array.isArray(params.amounts)) {
if (!params.initialAmounts || !Array.isArray(params.initialAmounts)) {
throw new Error('Суммы должны быть массивом');
}
if (params.partners.length !== params.amounts.length) {
if (params.initialPartners.length !== params.initialAmounts.length) {
throw new Error('Количество партнеров должно соответствовать количеству сумм распределения');
}
if (params.partners.length === 0) {
if (params.initialPartners.length === 0) {
throw new Error('Должен быть указан хотя бы один партнер');
}
if (params.quorumPercentage > 100) {
throw new Error('Процент кворума не может превышать 100%');
if (params.quorumPercentage > 100 || params.quorumPercentage < 1) {
throw new Error('Процент кворума должен быть от 1% до 100%');
}
// Проверяем адреса партнеров
for (let i = 0; i < params.partners.length; i++) {
if (!ethers.isAddress(params.partners[i])) {
throw new Error(`Неверный адрес партнера ${i + 1}: ${params.partners[i]}`);
for (let i = 0; i < params.initialPartners.length; i++) {
if (!ethers.isAddress(params.initialPartners[i])) {
throw new Error(`Неверный адрес партнера ${i + 1}: ${params.initialPartners[i]}`);
}
}
// Проверяем, что выбраны сети
if (!params.supportedChainIds || !Array.isArray(params.supportedChainIds) || params.supportedChainIds.length === 0) {
throw new Error('Должна быть выбрана хотя бы одна сеть для деплоя');
}
}
/**
* Подготавливает параметры для деплоя
* @param {Object} params - Параметры DLE
* @returns {Object} - Подготовленные параметры
* @param {Object} params - Параметры DLE из формы
* @returns {Object} - Подготовленные параметры для скрипта деплоя
*/
prepareDeployParams(params) {
// Создаем копию объекта, чтобы не изменять исходный
const deployParams = { ...params };
// Преобразуем суммы из строк или чисел в BigNumber, если нужно
deployParams.amounts = params.amounts.map(amount => {
if (typeof amount === 'string' && !amount.startsWith('0x')) {
return ethers.parseEther(amount).toString();
}
return amount.toString();
});
if (deployParams.initialAmounts && Array.isArray(deployParams.initialAmounts)) {
deployParams.initialAmounts = deployParams.initialAmounts.map(amount => {
if (typeof amount === 'string' && !amount.startsWith('0x')) {
return ethers.parseEther(amount).toString();
}
return amount.toString();
});
}
// Преобразуем параметры голосования
deployParams.votingDelay = params.votingDelay || 1;
deployParams.votingPeriod = params.votingPeriod || 45818; // ~1 неделя
deployParams.proposalThreshold = params.proposalThreshold || ethers.parseEther("100000").toString();
deployParams.quorumPercentage = params.quorumPercentage || 4;
deployParams.minTimelockDelay = params.minTimelockDelay || 2;
// Убеждаемся, что okvedCodes - это массив
if (!Array.isArray(deployParams.okvedCodes)) {
deployParams.okvedCodes = [];
}
// Убеждаемся, что isicCodes - это массив
if (!Array.isArray(deployParams.isicCodes)) {
deployParams.isicCodes = [];
// Убеждаемся, что supportedChainIds - это массив
if (!Array.isArray(deployParams.supportedChainIds)) {
deployParams.supportedChainIds = [1]; // По умолчанию Ethereum
}
// Устанавливаем currentChainId как первую выбранную сеть
if (deployParams.supportedChainIds.length > 0) {
deployParams.currentChainId = deployParams.supportedChainIds[0];
} else {
deployParams.currentChainId = 1; // По умолчанию Ethereum
}
return deployParams;
@@ -244,20 +261,18 @@ class DLEV2Service {
extractDeployResult(stdout) {
// Ищем строки с адресами в выводе
const dleAddressMatch = stdout.match(/DLE v2 задеплоен по адресу: (0x[a-fA-F0-9]{40})/);
const timelockAddressMatch = stdout.match(/Таймлок создан по адресу: (0x[a-fA-F0-9]{40})/);
if (dleAddressMatch && timelockAddressMatch) {
if (dleAddressMatch) {
return {
success: true,
data: {
dleAddress: dleAddressMatch[1],
timelockAddress: timelockAddressMatch[1],
version: 'v2'
}
};
}
throw new Error('Не удалось извлечь адреса из вывода скрипта');
throw new Error('Не удалось извлечь адрес DLE из вывода скрипта');
}
/**

View File

@@ -77,9 +77,9 @@ class EncryptedDataService {
const originalName = col.column_name.replace('_encrypted', '');
// console.log(`🔓 Расшифровываем поле ${col.column_name} -> ${originalName}`);
if (col.data_type === 'jsonb') {
return `decrypt_json(${col.column_name}, $1) as "${originalName}"`;
return `CASE WHEN ${col.column_name} IS NULL OR ${col.column_name} = '' THEN NULL ELSE decrypt_json(${col.column_name}, $1) END as "${originalName}"`;
} else {
return `decrypt_text(${col.column_name}, $1) as "${originalName}"`;
return `CASE WHEN ${col.column_name} IS NULL OR ${col.column_name} = '' THEN NULL ELSE decrypt_text(${col.column_name}, $1) END as "${originalName}"`;
}
} else if (!col.column_name.includes('_encrypted')) {
// Проверяем, есть ли зашифрованная версия этой колонки
@@ -181,6 +181,13 @@ class EncryptedDataService {
return rows;
} catch (error) {
// console.error(`❌ Ошибка получения данных из ${tableName}:`, error);
// Если ошибка связана с расшифровкой, попробуем получить данные без расшифровки
if (error.message && error.message.includes('invalid base64')) {
console.log(`⚠️ Ошибка расшифровки в ${tableName}, пытаемся получить данные без расшифровки`);
return await this.executeUnencryptedQuery(tableName, conditions, limit, orderBy);
}
throw error;
}
}
@@ -230,17 +237,18 @@ class EncryptedDataService {
// Проверяем, что значение не пустое перед шифрованием
if (value === null || value === undefined || (typeof value === 'string' && value.trim() === '')) {
// Пропускаем пустые значения
// console.log(`⚠️ Пропускаем пустое зашифрованное поле ${key}`);
console.log(`⚠️ Пропускаем пустое зашифрованное поле ${key}`);
continue;
}
const currentParamIndex = paramIndex++;
filteredData[key] = value; // Добавляем в отфильтрованные данные
// console.log(`✅ Добавили зашифрованное поле ${key} в filteredData`);
console.log(`✅ Добавили зашифрованное поле ${key} = "${value}" в filteredData`);
if (encryptedColumn.data_type === 'jsonb') {
encryptedData[`${key}_encrypted`] = `encrypt_json($${currentParamIndex}, ${hasEncryptedFields ? '$1::text' : 'NULL'})`;
} else {
encryptedData[`${key}_encrypted`] = `encrypt_text($${currentParamIndex}, ${hasEncryptedFields ? '$1::text' : 'NULL'})`;
}
console.log(`🔐 Будем шифровать ${key} -> ${key}_encrypted`);
} else if (unencryptedColumn) {
// Если есть незашифрованная колонка, сохраняем как есть
// Проверяем, что значение не пустое перед сохранением (кроме role и sender_type)
@@ -297,6 +305,10 @@ class EncryptedDataService {
const query = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders}) RETURNING *`;
const params = hasEncryptedFields ? [this.encryptionKey, ...Object.values(filteredData)] : [...Object.values(filteredData)];
console.log(`🔍 Выполняем INSERT запрос:`, query);
console.log(`🔍 Параметры:`, params);
console.log(`🔍 Ключ шифрования:`, this.encryptionKey ? 'установлен' : 'не установлен');
const { rows } = await db.getQuery()(query, params);
return rows[0];
}

View File

@@ -151,10 +151,34 @@ class IdentityService {
try {
const identities = await encryptedDb.getData('user_identities', { user_id: userId });
logger.info(`[IdentityService] Found ${identities.length} identities for user ${userId}`);
return identities;
// Данные уже расшифрованы encryptedDb, просто переименовываем поля
const formattedIdentities = identities.map(identity => ({
id: identity.id,
user_id: identity.user_id,
created_at: identity.created_at,
provider: identity.provider, // Уже расшифровано
provider_id: identity.provider_id // Уже расшифровано
}));
return formattedIdentities;
} catch (error) {
logger.error(`[IdentityService] Error getting identities for user ${userId}:`, error);
return [];
// Если произошла ошибка расшифровки, попробуем получить данные напрямую
try {
logger.info(`[IdentityService] Trying to get unencrypted data for user ${userId}`);
const { rows } = await db.getQuery()(
'SELECT id, user_id, created_at, provider_encrypted as provider, provider_id_encrypted as provider_id FROM user_identities WHERE user_id = $1',
[userId]
);
logger.info(`[IdentityService] Found ${rows.length} unencrypted identities for user ${userId}`);
return rows;
} catch (fallbackError) {
logger.error(`[IdentityService] Fallback error getting identities for user ${userId}:`, fallbackError);
return [];
}
}
}

View File

@@ -62,4 +62,9 @@ async function getRpcUrlByNetworkId(networkId) {
return providers[0]?.rpc_url || null;
}
module.exports = { getAllRpcProviders, saveAllRpcProviders, upsertRpcProvider, deleteRpcProvider, getRpcUrlByNetworkId };
async function getRpcUrlByChainId(chainId) {
const providers = await encryptedDb.getData('rpc_providers', { chain_id: chainId }, 1);
return providers[0]?.rpc_url || null;
}
module.exports = { getAllRpcProviders, saveAllRpcProviders, upsertRpcProvider, deleteRpcProvider, getRpcUrlByNetworkId, getRpcUrlByChainId };