ваше сообщение коммита
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"_format": "hh-sol-dbg-1",
|
"_format": "hh-sol-dbg-1",
|
||||||
"buildInfo": "../../../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
|
"buildInfo": "../../../../build-info/5f658ec7c83a39083e0b58539865c835.json"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"_format": "hh-sol-dbg-1",
|
"_format": "hh-sol-dbg-1",
|
||||||
"buildInfo": "../../../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
|
"buildInfo": "../../../../build-info/5f658ec7c83a39083e0b58539865c835.json"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"_format": "hh-sol-dbg-1",
|
"_format": "hh-sol-dbg-1",
|
||||||
"buildInfo": "../../../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
|
"buildInfo": "../../../../build-info/5f658ec7c83a39083e0b58539865c835.json"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"_format": "hh-sol-dbg-1",
|
"_format": "hh-sol-dbg-1",
|
||||||
"buildInfo": "../../../../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
|
"buildInfo": "../../../../../build-info/5f658ec7c83a39083e0b58539865c835.json"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"_format": "hh-sol-dbg-1",
|
"_format": "hh-sol-dbg-1",
|
||||||
"buildInfo": "../../../../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
|
"buildInfo": "../../../../../build-info/5f658ec7c83a39083e0b58539865c835.json"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"_format": "hh-sol-dbg-1",
|
"_format": "hh-sol-dbg-1",
|
||||||
"buildInfo": "../../../../../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
|
"buildInfo": "../../../../../../build-info/5f658ec7c83a39083e0b58539865c835.json"
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"_format": "hh-sol-dbg-1",
|
"_format": "hh-sol-dbg-1",
|
||||||
"buildInfo": "../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
|
"buildInfo": "../../build-info/5f658ec7c83a39083e0b58539865c835.json"
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
22
backend/cache/solidity-files-cache.json
vendored
22
backend/cache/solidity-files-cache.json
vendored
@@ -42,8 +42,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"/home/alex/Digital_Legal_Entity(DLE)/backend/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol": {
|
"/home/alex/Digital_Legal_Entity(DLE)/backend/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol": {
|
||||||
"lastModificationDate": 1751738715692,
|
"lastModificationDate": 1753876422727,
|
||||||
"contentHash": "227a6eb2225701c12d9c959b758b6333",
|
"contentHash": "59dfce11284f2636db261df9b6a18f81",
|
||||||
"sourceName": "@openzeppelin/contracts/token/ERC20/ERC20.sol",
|
"sourceName": "@openzeppelin/contracts/token/ERC20/ERC20.sol",
|
||||||
"solcConfig": {
|
"solcConfig": {
|
||||||
"version": "0.8.20",
|
"version": "0.8.20",
|
||||||
@@ -158,8 +158,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"/home/alex/Digital_Legal_Entity(DLE)/backend/node_modules/@openzeppelin/contracts/interfaces/draft-IERC6093.sol": {
|
"/home/alex/Digital_Legal_Entity(DLE)/backend/node_modules/@openzeppelin/contracts/interfaces/draft-IERC6093.sol": {
|
||||||
"lastModificationDate": 1751738715692,
|
"lastModificationDate": 1753876422645,
|
||||||
"contentHash": "267d92fe4de67b1bdb3302c08f387dbf",
|
"contentHash": "5041977bbe908de2e6ed0270447f79ad",
|
||||||
"sourceName": "@openzeppelin/contracts/interfaces/draft-IERC6093.sol",
|
"sourceName": "@openzeppelin/contracts/interfaces/draft-IERC6093.sol",
|
||||||
"solcConfig": {
|
"solcConfig": {
|
||||||
"version": "0.8.20",
|
"version": "0.8.20",
|
||||||
@@ -188,7 +188,7 @@
|
|||||||
},
|
},
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"versionPragmas": [
|
"versionPragmas": [
|
||||||
"^0.8.20"
|
">=0.8.4"
|
||||||
],
|
],
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
"IERC1155Errors",
|
"IERC1155Errors",
|
||||||
@@ -197,8 +197,8 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"/home/alex/Digital_Legal_Entity(DLE)/backend/node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
|
"/home/alex/Digital_Legal_Entity(DLE)/backend/node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
|
||||||
"lastModificationDate": 1751738715692,
|
"lastModificationDate": 1753876422727,
|
||||||
"contentHash": "794db3115001aa372c79326fcfd44b1f",
|
"contentHash": "513778b30d2750f5d2b9b19bbcf748a5",
|
||||||
"sourceName": "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol",
|
"sourceName": "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol",
|
||||||
"solcConfig": {
|
"solcConfig": {
|
||||||
"version": "0.8.20",
|
"version": "0.8.20",
|
||||||
@@ -229,15 +229,15 @@
|
|||||||
"../IERC20.sol"
|
"../IERC20.sol"
|
||||||
],
|
],
|
||||||
"versionPragmas": [
|
"versionPragmas": [
|
||||||
"^0.8.20"
|
">=0.6.2"
|
||||||
],
|
],
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
"IERC20Metadata"
|
"IERC20Metadata"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"/home/alex/Digital_Legal_Entity(DLE)/backend/node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
|
"/home/alex/Digital_Legal_Entity(DLE)/backend/node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
|
||||||
"lastModificationDate": 1751738715692,
|
"lastModificationDate": 1753876422727,
|
||||||
"contentHash": "8f19f64d2adadf448840908bbaf431c8",
|
"contentHash": "9261adf6457863de3e9892f51317ec89",
|
||||||
"sourceName": "@openzeppelin/contracts/token/ERC20/IERC20.sol",
|
"sourceName": "@openzeppelin/contracts/token/ERC20/IERC20.sol",
|
||||||
"solcConfig": {
|
"solcConfig": {
|
||||||
"version": "0.8.20",
|
"version": "0.8.20",
|
||||||
@@ -266,7 +266,7 @@
|
|||||||
},
|
},
|
||||||
"imports": [],
|
"imports": [],
|
||||||
"versionPragmas": [
|
"versionPragmas": [
|
||||||
"^0.8.20"
|
">=0.4.16"
|
||||||
],
|
],
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
"IERC20"
|
"IERC20"
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ const logger = require('../utils/logger');
|
|||||||
const auth = require('../middleware/auth');
|
const auth = require('../middleware/auth');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const ethers = require('ethers'); // Added ethers for private key validation
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route POST /api/dle-v2
|
* @route POST /api/dle-v2
|
||||||
@@ -28,8 +29,8 @@ router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res, next) =>
|
|||||||
const dleParams = req.body;
|
const dleParams = req.body;
|
||||||
logger.info('Получен запрос на создание DLE v2:', dleParams);
|
logger.info('Получен запрос на создание DLE v2:', dleParams);
|
||||||
|
|
||||||
// Если параметр partners не был передан явно, используем адрес авторизованного пользователя
|
// Если параметр initialPartners не был передан явно, используем адрес авторизованного пользователя
|
||||||
if (!dleParams.partners || dleParams.partners.length === 0) {
|
if (!dleParams.initialPartners || dleParams.initialPartners.length === 0) {
|
||||||
// Проверяем, есть ли в сессии адрес кошелька пользователя
|
// Проверяем, есть ли в сессии адрес кошелька пользователя
|
||||||
if (!req.user || !req.user.walletAddress) {
|
if (!req.user || !req.user.walletAddress) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
@@ -39,11 +40,11 @@ router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res, next) =>
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Используем адрес авторизованного пользователя
|
// Используем адрес авторизованного пользователя
|
||||||
dleParams.partners = [req.user.address || req.user.walletAddress];
|
dleParams.initialPartners = [req.user.address || req.user.walletAddress];
|
||||||
|
|
||||||
// Если суммы не указаны, используем значение по умолчанию (100% токенов)
|
// Если суммы не указаны, используем значение по умолчанию (100% токенов)
|
||||||
if (!dleParams.amounts || dleParams.amounts.length === 0) {
|
if (!dleParams.initialAmounts || dleParams.initialAmounts.length === 0) {
|
||||||
dleParams.amounts = ['1000000'];
|
dleParams.initialAmounts = ['1000000000000000000000000']; // 1,000,000 токенов
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,23 +92,40 @@ router.get('/', auth.requireAuth, async (req, res, next) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @route GET /api/dle-v2/defaults
|
* @route GET /api/dle-v2/default-params
|
||||||
* @desc Получить настройки по умолчанию для DLE v2
|
* @desc Получить параметры по умолчанию для создания DLE v2
|
||||||
* @access Private (только для авторизованных пользователей)
|
* @access Private
|
||||||
*/
|
*/
|
||||||
router.get('/defaults', auth.requireAuth, async (req, res, next) => {
|
router.get('/default-params', auth.requireAuth, async (req, res, next) => {
|
||||||
// Возвращаем настройки по умолчанию, которые будут использоваться
|
try {
|
||||||
// при заполнении формы на фронтенде
|
const defaultParams = {
|
||||||
|
name: '',
|
||||||
|
symbol: '',
|
||||||
|
location: '',
|
||||||
|
coordinates: '',
|
||||||
|
jurisdiction: 1,
|
||||||
|
oktmo: 45000000000,
|
||||||
|
okvedCodes: [],
|
||||||
|
kpp: 770101001,
|
||||||
|
quorumPercentage: 51,
|
||||||
|
initialPartners: [],
|
||||||
|
initialAmounts: [],
|
||||||
|
supportedChainIds: [1, 137, 56, 42161],
|
||||||
|
currentChainId: 1
|
||||||
|
};
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: defaultParams
|
||||||
votingDelay: 1, // 1 блок задержки перед началом голосования
|
|
||||||
votingPeriod: 45818, // ~1 неделя в блоках (при 13 секундах на блок)
|
|
||||||
proposalThreshold: '100000', // 100,000 токенов
|
|
||||||
quorumPercentage: 4, // 4% от общего количества токенов
|
|
||||||
minTimelockDelay: 2 // 2 дня
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Ошибка при получении параметров по умолчанию:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: error.message || 'Произошла ошибка при получении параметров по умолчанию'
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -170,4 +188,74 @@ router.delete('/:dleAddress', auth.requireAuth, auth.requireAdmin, async (req, r
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @route POST /api/dle-v2/validate-private-key
|
||||||
|
* @desc Валидировать приватный ключ и получить адрес кошелька
|
||||||
|
* @access Private
|
||||||
|
*/
|
||||||
|
router.post('/validate-private-key', auth.requireAuth, async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const { privateKey } = req.body;
|
||||||
|
|
||||||
|
if (!privateKey) {
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Приватный ключ не передан'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Логируем входящий ключ (только для отладки)
|
||||||
|
logger.info('Получен приватный ключ для валидации:', privateKey);
|
||||||
|
logger.info('Длина входящего ключа:', privateKey.length);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Очищаем ключ от префикса 0x если есть
|
||||||
|
const cleanKey = privateKey.startsWith('0x') ? privateKey.slice(2) : privateKey;
|
||||||
|
|
||||||
|
// Логируем очищенный ключ (только для отладки)
|
||||||
|
logger.info('Очищенный ключ:', cleanKey);
|
||||||
|
logger.info('Длина очищенного ключа:', cleanKey.length);
|
||||||
|
|
||||||
|
// Проверяем длину и формат (64 символа в hex)
|
||||||
|
if (cleanKey.length !== 64 || !/^[a-fA-F0-9]+$/.test(cleanKey)) {
|
||||||
|
logger.error('Некорректный формат ключа. Длина:', cleanKey.length, 'Формат:', /^[a-fA-F0-9]+$/.test(cleanKey));
|
||||||
|
return res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Некорректный формат приватного ключа'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Генерируем адрес из приватного ключа
|
||||||
|
const wallet = new ethers.Wallet('0x' + cleanKey);
|
||||||
|
const address = wallet.address;
|
||||||
|
|
||||||
|
// Логируем сгенерированный адрес
|
||||||
|
logger.info('Сгенерированный адрес из приватного ключа:', address);
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
isValid: true,
|
||||||
|
address: address,
|
||||||
|
error: null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Ошибка при генерации адреса из приватного ключа:', error);
|
||||||
|
res.status(400).json({
|
||||||
|
success: false,
|
||||||
|
message: 'Некорректный приватный ключ'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Ошибка при валидации приватного ключа:', error);
|
||||||
|
res.status(500).json({
|
||||||
|
success: false,
|
||||||
|
message: error.message || 'Произошла ошибка при валидации приватного ключа'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
||||||
@@ -213,7 +213,7 @@ router.get('/', requireAuth, async (req, res, next) => {
|
|||||||
// --- Формируем ответ ---
|
// --- Формируем ответ ---
|
||||||
const contacts = users.map(u => ({
|
const contacts = users.map(u => ({
|
||||||
id: u.id,
|
id: u.id,
|
||||||
name: [u.first_name, u.last_name].filter(Boolean).join(' ') || null,
|
name: null, // Имена теперь только в зашифрованных колонках
|
||||||
email: u.email || null,
|
email: u.email || null,
|
||||||
telegram: u.telegram || null,
|
telegram: u.telegram || null,
|
||||||
wallet: u.wallet || null,
|
wallet: u.wallet || null,
|
||||||
@@ -418,22 +418,37 @@ router.get('/:id', async (req, res, next) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Получаем ключ шифрования
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
let encryptionKey = 'default-key';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const keyPath = path.join(__dirname, '../ssl/keys/full_db_encryption.key');
|
||||||
|
if (fs.existsSync(keyPath)) {
|
||||||
|
encryptionKey = fs.readFileSync(keyPath, 'utf8').trim();
|
||||||
|
}
|
||||||
|
} catch (keyError) {
|
||||||
|
console.error('Error reading encryption key:', keyError);
|
||||||
|
}
|
||||||
|
|
||||||
const query = db.getQuery();
|
const query = db.getQuery();
|
||||||
// Получаем пользователя
|
// Получаем пользователя
|
||||||
const userResult = await query('SELECT id, decrypt_text(first_name_encrypted, $2) as first_name, decrypt_text(last_name_encrypted, $2) as last_name, created_at, preferred_language, is_blocked FROM users WHERE id = $1', [userId, encryptionKey]);
|
const userResult = await query('SELECT id, created_at, preferred_language, is_blocked FROM users WHERE id = $1', [userId]);
|
||||||
if (userResult.rows.length === 0) {
|
if (userResult.rows.length === 0) {
|
||||||
return res.status(404).json({ error: 'User not found' });
|
return res.status(404).json({ error: 'User not found' });
|
||||||
}
|
}
|
||||||
const user = userResult.rows[0];
|
const user = userResult.rows[0];
|
||||||
|
|
||||||
// Получаем идентификаторы
|
// Получаем идентификаторы
|
||||||
const identitiesResult = await query('SELECT decrypt_text(provider_encrypted, $2) as provider, decrypt_text(provider_id_encrypted, $2) as provider_id FROM user_identities WHERE user_id = $1', [userId, encryptionKey]);
|
const identitiesResult = await query('SELECT CASE WHEN provider_encrypted IS NULL OR provider_encrypted = \'\' THEN NULL ELSE decrypt_text(provider_encrypted, $2) END as provider, CASE WHEN provider_id_encrypted IS NULL OR provider_id_encrypted = \'\' THEN NULL ELSE decrypt_text(provider_id_encrypted, $2) END as provider_id FROM user_identities WHERE user_id = $1', [userId, encryptionKey]);
|
||||||
const identityMap = {};
|
const identityMap = {};
|
||||||
for (const id of identitiesResult.rows) {
|
for (const id of identitiesResult.rows) {
|
||||||
identityMap[id.provider] = id.provider_id;
|
identityMap[id.provider] = id.provider_id;
|
||||||
}
|
}
|
||||||
res.json({
|
res.json({
|
||||||
id: user.id,
|
id: user.id,
|
||||||
name: [user.first_name, user.last_name].filter(Boolean).join(' ') || null,
|
name: null, // Пока не используем имена
|
||||||
email: identityMap.email || null,
|
email: identityMap.email || null,
|
||||||
telegram: identityMap.telegram || null,
|
telegram: identityMap.telegram || null,
|
||||||
wallet: identityMap.wallet || null,
|
wallet: identityMap.wallet || null,
|
||||||
@@ -545,9 +560,9 @@ router.post('/import', requireAuth, async (req, res) => {
|
|||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
for (const idn of identities) {
|
for (const idn of identities) {
|
||||||
// Проверяем, есть ли уже такой идентификатор у пользователя
|
// Проверяем, есть ли уже такой идентификатор у пользователя
|
||||||
const exists = await dbq('SELECT 1 FROM user_identities WHERE user_id = $1 AND provider = $2 AND provider_id = $3', [userId, idn.provider, idn.provider_id]);
|
const exists = await dbq('SELECT 1 FROM user_identities WHERE user_id = $1 AND provider_encrypted = encrypt_text($2, $4) AND provider_id_encrypted = encrypt_text($3, $4)', [userId, idn.provider, idn.provider_id, encryptionKey]);
|
||||||
if (!exists.rows.length) {
|
if (!exists.rows.length) {
|
||||||
await dbq('INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING', [userId, idn.provider, idn.provider_id]);
|
await dbq('INSERT INTO user_identities (user_id, provider_encrypted, provider_id_encrypted) VALUES ($1, encrypt_text($2, $4), encrypt_text($3, $4)) ON CONFLICT DO NOTHING', [userId, idn.provider, idn.provider_id, encryptionKey]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -34,70 +34,59 @@ async function main() {
|
|||||||
|
|
||||||
const DLE = await ethers.getContractFactory("DLE");
|
const DLE = await ethers.getContractFactory("DLE");
|
||||||
|
|
||||||
// Преобразуем параметры голосования
|
// Создаем структуру DLEConfig
|
||||||
const votingDelay = deployParams.votingDelay || 1;
|
const dleConfig = {
|
||||||
const votingPeriod = deployParams.votingPeriod || 45818; // ~1 неделя
|
name: deployParams.name,
|
||||||
const proposalThreshold = deployParams.proposalThreshold || ethers.parseEther("100000");
|
symbol: deployParams.symbol,
|
||||||
const quorumPercentage = deployParams.quorumPercentage || 4;
|
location: deployParams.location,
|
||||||
const minTimelockDelay = (deployParams.minTimelockDelay || 2) * 24 * 60 * 60; // дни в секунды
|
coordinates: deployParams.coordinates || "0,0",
|
||||||
|
jurisdiction: deployParams.jurisdiction || 1,
|
||||||
|
oktmo: deployParams.oktmo || 45000000000,
|
||||||
|
okvedCodes: deployParams.okvedCodes || [],
|
||||||
|
kpp: deployParams.kpp || 770101001,
|
||||||
|
quorumPercentage: deployParams.quorumPercentage || 51,
|
||||||
|
initialPartners: deployParams.initialPartners,
|
||||||
|
initialAmounts: deployParams.initialAmounts,
|
||||||
|
supportedChainIds: deployParams.supportedChainIds || [1, 137, 56, 42161] // Ethereum, Polygon, BSC, Arbitrum
|
||||||
|
};
|
||||||
|
|
||||||
const dle = await DLE.deploy(
|
const currentChainId = deployParams.currentChainId || 1; // По умолчанию Ethereum
|
||||||
deployParams.name,
|
|
||||||
deployParams.symbol,
|
const dle = await DLE.deploy(dleConfig, currentChainId);
|
||||||
deployParams.location,
|
|
||||||
deployParams.isicCodes || [],
|
|
||||||
votingDelay,
|
|
||||||
votingPeriod,
|
|
||||||
proposalThreshold,
|
|
||||||
quorumPercentage,
|
|
||||||
minTimelockDelay
|
|
||||||
);
|
|
||||||
|
|
||||||
await dle.waitForDeployment();
|
await dle.waitForDeployment();
|
||||||
const dleAddress = await dle.getAddress();
|
const dleAddress = await dle.getAddress();
|
||||||
console.log(`DLE v2 задеплоен по адресу: ${dleAddress}`);
|
console.log(`DLE v2 задеплоен по адресу: ${dleAddress}`);
|
||||||
|
|
||||||
// 2. Получаем адрес таймлока
|
// 2. Получаем информацию о DLE
|
||||||
const timelockAddress = await dle.getTimelockAddress();
|
|
||||||
console.log(`Таймлок создан по адресу: ${timelockAddress}`);
|
|
||||||
|
|
||||||
// 3. Распределяем начальные токены
|
|
||||||
console.log("\n3. Распределение начальных токенов...");
|
|
||||||
const distributeTx = await dle.distributeInitialTokens(
|
|
||||||
deployParams.partners,
|
|
||||||
deployParams.amounts
|
|
||||||
);
|
|
||||||
await distributeTx.wait();
|
|
||||||
console.log(`Токены распределены между партнерами`);
|
|
||||||
|
|
||||||
// 4. Получаем информацию о DLE
|
|
||||||
const dleInfo = await dle.getDLEInfo();
|
const dleInfo = await dle.getDLEInfo();
|
||||||
console.log("\n4. Информация о DLE:");
|
console.log("\n2. Информация о DLE:");
|
||||||
console.log(`Название: ${dleInfo.name}`);
|
console.log(`Название: ${dleInfo.name}`);
|
||||||
console.log(`Символ: ${dleInfo.symbol}`);
|
console.log(`Символ: ${dleInfo.symbol}`);
|
||||||
console.log(`Местонахождение: ${dleInfo.location}`);
|
console.log(`Местонахождение: ${dleInfo.location}`);
|
||||||
console.log(`Коды деятельности: ${dleInfo.isicCodes.join(', ')}`);
|
console.log(`Коды деятельности: ${dleInfo.okvedCodes.join(', ')}`);
|
||||||
console.log(`Дата создания: ${new Date(dleInfo.creationTimestamp * 1000).toISOString()}`);
|
console.log(`Дата создания: ${new Date(dleInfo.creationTimestamp * 1000).toISOString()}`);
|
||||||
|
|
||||||
// 5. Сохраняем информацию о созданном DLE
|
// 3. Сохраняем информацию о созданном DLE
|
||||||
console.log("\n5. Сохранение информации о DLE v2...");
|
console.log("\n3. Сохранение информации о DLE v2...");
|
||||||
const dleData = {
|
const dleData = {
|
||||||
name: deployParams.name,
|
name: deployParams.name,
|
||||||
symbol: deployParams.symbol,
|
symbol: deployParams.symbol,
|
||||||
location: deployParams.location,
|
location: deployParams.location,
|
||||||
isicCodes: deployParams.isicCodes || [],
|
coordinates: deployParams.coordinates || "0,0",
|
||||||
|
jurisdiction: deployParams.jurisdiction || 1,
|
||||||
|
oktmo: deployParams.oktmo || 45000000000,
|
||||||
|
okvedCodes: deployParams.isicCodes || [],
|
||||||
|
kpp: deployParams.kpp || 770101001,
|
||||||
dleAddress: dleAddress,
|
dleAddress: dleAddress,
|
||||||
timelockAddress: timelockAddress,
|
creationBlock: (await dle.provider.getBlockNumber()),
|
||||||
creationBlock: (await distributeTx.provider.getBlockNumber()),
|
creationTimestamp: (await dle.provider.getBlock()).timestamp,
|
||||||
creationTimestamp: (await distributeTx.provider.getBlock()).timestamp,
|
|
||||||
deployedManually: true,
|
deployedManually: true,
|
||||||
version: "v2",
|
version: "v2",
|
||||||
governanceSettings: {
|
governanceSettings: {
|
||||||
votingDelay: votingDelay,
|
quorumPercentage: deployParams.quorumPercentage || 51,
|
||||||
votingPeriod: votingPeriod,
|
supportedChainIds: deployParams.supportedChainIds || [1, 137, 56, 42161],
|
||||||
proposalThreshold: proposalThreshold.toString(),
|
currentChainId: currentChainId
|
||||||
quorumPercentage: quorumPercentage,
|
|
||||||
minTimelockDelay: deployParams.minTimelockDelay || 2
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -105,13 +94,11 @@ async function main() {
|
|||||||
|
|
||||||
console.log("\nDLE v2 успешно создан!");
|
console.log("\nDLE v2 успешно создан!");
|
||||||
console.log(`Адрес DLE: ${dleAddress}`);
|
console.log(`Адрес DLE: ${dleAddress}`);
|
||||||
console.log(`Адрес таймлока: ${timelockAddress}`);
|
|
||||||
console.log(`Версия: v2 (единый контракт)`);
|
console.log(`Версия: v2 (единый контракт)`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
dleAddress: dleAddress,
|
dleAddress: dleAddress,
|
||||||
timelockAddress: timelockAddress,
|
|
||||||
data: dleData
|
data: dleData
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "рога и копыта",
|
|
||||||
"symbol": "roga",
|
|
||||||
"location": "325000, 中国, 瓯海区",
|
|
||||||
"isicCodes": [
|
|
||||||
"9000"
|
|
||||||
],
|
|
||||||
"partners": [
|
|
||||||
"0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b",
|
|
||||||
"0x15a4ed4759e5762174b300a4cf51cc17ad967f4d"
|
|
||||||
],
|
|
||||||
"amounts": [
|
|
||||||
"1000000000000000000",
|
|
||||||
"10000000000000000000000"
|
|
||||||
],
|
|
||||||
"network": "sepolia",
|
|
||||||
"minTimelockDelay": 5,
|
|
||||||
"votingDelay": 46523,
|
|
||||||
"votingPeriod": 6646,
|
|
||||||
"proposalThreshold": "0",
|
|
||||||
"quorumPercentage": 51,
|
|
||||||
"privateKey": "7de38b2ada1d23581342f106c8587ce26068797b3bc06656e24b9dcd1810c7b1"
|
|
||||||
}
|
|
||||||
@@ -103,10 +103,11 @@ class AuthService {
|
|||||||
const userId = newUserResult.rows[0].id;
|
const userId = newUserResult.rows[0].id;
|
||||||
|
|
||||||
// Добавляем идентификатор кошелька (всегда в нижнем регистре)
|
// Добавляем идентификатор кошелька (всегда в нижнем регистре)
|
||||||
await db.getQuery()(
|
await encryptedDb.saveData('user_identities', {
|
||||||
'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)',
|
user_id: userId,
|
||||||
[userId, 'wallet', normalizedAddress]
|
provider: 'wallet',
|
||||||
);
|
provider_id: normalizedAddress
|
||||||
|
});
|
||||||
|
|
||||||
// Проверяем, есть ли у пользователя роль админа
|
// Проверяем, есть ли у пользователя роль админа
|
||||||
const isAdmin = await checkAdminRole(normalizedAddress);
|
const isAdmin = await checkAdminRole(normalizedAddress);
|
||||||
@@ -400,11 +401,11 @@ class AuthService {
|
|||||||
// Если в сессии нет авторизованного пользователя, проверяем существующие идентификаторы
|
// Если в сессии нет авторизованного пользователя, проверяем существующие идентификаторы
|
||||||
// Проверяем, существует ли уже пользователь с таким Telegram ID
|
// Проверяем, существует ли уже пользователь с таким Telegram ID
|
||||||
const existingUserResult = await db.getQuery()(
|
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
|
FROM users u
|
||||||
JOIN user_identities ui ON u.id = ui.user_id
|
JOIN user_identities ui ON u.id = ui.user_id
|
||||||
WHERE ui.provider = 'telegram' AND ui.provider_id = $1`,
|
WHERE ui.provider_encrypted = encrypt_text('telegram', $2) AND ui.provider_id_encrypted = encrypt_text($1, $2)`,
|
||||||
[telegramId]
|
[telegramId, encryptionKey]
|
||||||
);
|
);
|
||||||
|
|
||||||
// Если пользователь существует с таким telegramId, используем его
|
// Если пользователь существует с таким telegramId, используем его
|
||||||
@@ -423,10 +424,11 @@ class AuthService {
|
|||||||
isNewUser = true;
|
isNewUser = true;
|
||||||
|
|
||||||
// Добавляем Telegram идентификатор
|
// Добавляем Telegram идентификатор
|
||||||
await db.getQuery()(
|
await encryptedDb.saveData('user_identities', {
|
||||||
'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)',
|
user_id: userId,
|
||||||
[userId, 'telegram', telegramId]
|
provider: 'telegram',
|
||||||
);
|
provider_id: telegramId
|
||||||
|
});
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`[verifyTelegramAuth] Created new user ${userId} for Telegram ID ${telegramId}`
|
`[verifyTelegramAuth] Created new user ${userId} for Telegram ID ${telegramId}`
|
||||||
@@ -582,10 +584,34 @@ class AuthService {
|
|||||||
async getUserIdentities(userId) {
|
async getUserIdentities(userId) {
|
||||||
try {
|
try {
|
||||||
const identities = await encryptedDb.getData('user_identities', { user_id: userId }, null, 'created_at DESC');
|
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) {
|
} catch (error) {
|
||||||
logger.error('[getUserIdentities] Error:', 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()(
|
await encryptedDb.saveData('user_identities', {
|
||||||
`INSERT INTO user_identities (user_id, provider, provider_id)
|
user_id: userId,
|
||||||
VALUES ($1, $2, $3)`,
|
provider: provider,
|
||||||
[userId, provider, normalizedProviderId]
|
provider_id: normalizedProviderId
|
||||||
);
|
});
|
||||||
|
|
||||||
// Проверяем и обновляем роль администратора, если это идентификатор кошелька
|
// Проверяем и обновляем роль администратора, если это идентификатор кошелька
|
||||||
let isAdmin = false;
|
let isAdmin = false;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ const path = require('path');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const { ethers } = require('ethers');
|
const { ethers } = require('ethers');
|
||||||
const logger = require('../utils/logger');
|
const logger = require('../utils/logger');
|
||||||
const { getRpcUrlByNetworkId } = require('./rpcProviderService');
|
const { getRpcUrlByChainId } = require('./rpcProviderService');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Сервис для управления DLE v2 (Digital Legal Entity)
|
* Сервис для управления DLE v2 (Digital Legal Entity)
|
||||||
@@ -49,10 +49,15 @@ class DLEV2Service {
|
|||||||
fs.copyFileSync(paramsFile, tempParamsFile);
|
fs.copyFileSync(paramsFile, tempParamsFile);
|
||||||
logger.info(`Файл параметров скопирован успешно`);
|
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) {
|
if (!rpcUrl) {
|
||||||
throw new Error(`RPC URL для сети ${deployParams.network} не найден в базе данных`);
|
throw new Error(`RPC URL для сети с chain_id ${chainId} не найден в базе данных`);
|
||||||
}
|
}
|
||||||
if (!dleParams.privateKey) {
|
if (!dleParams.privateKey) {
|
||||||
throw new Error('Приватный ключ для деплоя не передан');
|
throw new Error('Приватный ключ для деплоя не передан');
|
||||||
@@ -62,8 +67,8 @@ class DLEV2Service {
|
|||||||
const result = await this.runDeployScript(paramsFile, {
|
const result = await this.runDeployScript(paramsFile, {
|
||||||
rpcUrl,
|
rpcUrl,
|
||||||
privateKey: dleParams.privateKey,
|
privateKey: dleParams.privateKey,
|
||||||
networkId: deployParams.network,
|
networkId: chainId.toString(),
|
||||||
envNetworkKey: deployParams.network.toUpperCase()
|
envNetworkKey: chainId.toString().toUpperCase()
|
||||||
});
|
});
|
||||||
|
|
||||||
// Очищаем временные файлы
|
// Очищаем временные файлы
|
||||||
@@ -94,61 +99,73 @@ class DLEV2Service {
|
|||||||
throw new Error('Местонахождение DLE обязательно');
|
throw new Error('Местонахождение DLE обязательно');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params.partners || !Array.isArray(params.partners)) {
|
if (!params.initialPartners || !Array.isArray(params.initialPartners)) {
|
||||||
throw new Error('Партнеры должны быть массивом');
|
throw new Error('Партнеры должны быть массивом');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!params.amounts || !Array.isArray(params.amounts)) {
|
if (!params.initialAmounts || !Array.isArray(params.initialAmounts)) {
|
||||||
throw new Error('Суммы должны быть массивом');
|
throw new Error('Суммы должны быть массивом');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.partners.length !== params.amounts.length) {
|
if (params.initialPartners.length !== params.initialAmounts.length) {
|
||||||
throw new Error('Количество партнеров должно соответствовать количеству сумм распределения');
|
throw new Error('Количество партнеров должно соответствовать количеству сумм распределения');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.partners.length === 0) {
|
if (params.initialPartners.length === 0) {
|
||||||
throw new Error('Должен быть указан хотя бы один партнер');
|
throw new Error('Должен быть указан хотя бы один партнер');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (params.quorumPercentage > 100) {
|
if (params.quorumPercentage > 100 || params.quorumPercentage < 1) {
|
||||||
throw new Error('Процент кворума не может превышать 100%');
|
throw new Error('Процент кворума должен быть от 1% до 100%');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем адреса партнеров
|
// Проверяем адреса партнеров
|
||||||
for (let i = 0; i < params.partners.length; i++) {
|
for (let i = 0; i < params.initialPartners.length; i++) {
|
||||||
if (!ethers.isAddress(params.partners[i])) {
|
if (!ethers.isAddress(params.initialPartners[i])) {
|
||||||
throw new Error(`Неверный адрес партнера ${i + 1}: ${params.partners[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
|
* @param {Object} params - Параметры DLE из формы
|
||||||
* @returns {Object} - Подготовленные параметры
|
* @returns {Object} - Подготовленные параметры для скрипта деплоя
|
||||||
*/
|
*/
|
||||||
prepareDeployParams(params) {
|
prepareDeployParams(params) {
|
||||||
// Создаем копию объекта, чтобы не изменять исходный
|
// Создаем копию объекта, чтобы не изменять исходный
|
||||||
const deployParams = { ...params };
|
const deployParams = { ...params };
|
||||||
|
|
||||||
// Преобразуем суммы из строк или чисел в BigNumber, если нужно
|
// Преобразуем суммы из строк или чисел в BigNumber, если нужно
|
||||||
deployParams.amounts = params.amounts.map(amount => {
|
if (deployParams.initialAmounts && Array.isArray(deployParams.initialAmounts)) {
|
||||||
|
deployParams.initialAmounts = deployParams.initialAmounts.map(amount => {
|
||||||
if (typeof amount === 'string' && !amount.startsWith('0x')) {
|
if (typeof amount === 'string' && !amount.startsWith('0x')) {
|
||||||
return ethers.parseEther(amount).toString();
|
return ethers.parseEther(amount).toString();
|
||||||
}
|
}
|
||||||
return amount.toString();
|
return amount.toString();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Преобразуем параметры голосования
|
// Убеждаемся, что okvedCodes - это массив
|
||||||
deployParams.votingDelay = params.votingDelay || 1;
|
if (!Array.isArray(deployParams.okvedCodes)) {
|
||||||
deployParams.votingPeriod = params.votingPeriod || 45818; // ~1 неделя
|
deployParams.okvedCodes = [];
|
||||||
deployParams.proposalThreshold = params.proposalThreshold || ethers.parseEther("100000").toString();
|
}
|
||||||
deployParams.quorumPercentage = params.quorumPercentage || 4;
|
|
||||||
deployParams.minTimelockDelay = params.minTimelockDelay || 2;
|
|
||||||
|
|
||||||
// Убеждаемся, что isicCodes - это массив
|
// Убеждаемся, что supportedChainIds - это массив
|
||||||
if (!Array.isArray(deployParams.isicCodes)) {
|
if (!Array.isArray(deployParams.supportedChainIds)) {
|
||||||
deployParams.isicCodes = [];
|
deployParams.supportedChainIds = [1]; // По умолчанию Ethereum
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливаем currentChainId как первую выбранную сеть
|
||||||
|
if (deployParams.supportedChainIds.length > 0) {
|
||||||
|
deployParams.currentChainId = deployParams.supportedChainIds[0];
|
||||||
|
} else {
|
||||||
|
deployParams.currentChainId = 1; // По умолчанию Ethereum
|
||||||
}
|
}
|
||||||
|
|
||||||
return deployParams;
|
return deployParams;
|
||||||
@@ -244,20 +261,18 @@ class DLEV2Service {
|
|||||||
extractDeployResult(stdout) {
|
extractDeployResult(stdout) {
|
||||||
// Ищем строки с адресами в выводе
|
// Ищем строки с адресами в выводе
|
||||||
const dleAddressMatch = stdout.match(/DLE v2 задеплоен по адресу: (0x[a-fA-F0-9]{40})/);
|
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 {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
dleAddress: dleAddressMatch[1],
|
dleAddress: dleAddressMatch[1],
|
||||||
timelockAddress: timelockAddressMatch[1],
|
|
||||||
version: 'v2'
|
version: 'v2'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error('Не удалось извлечь адреса из вывода скрипта');
|
throw new Error('Не удалось извлечь адрес DLE из вывода скрипта');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -77,9 +77,9 @@ class EncryptedDataService {
|
|||||||
const originalName = col.column_name.replace('_encrypted', '');
|
const originalName = col.column_name.replace('_encrypted', '');
|
||||||
// console.log(`🔓 Расшифровываем поле ${col.column_name} -> ${originalName}`);
|
// console.log(`🔓 Расшифровываем поле ${col.column_name} -> ${originalName}`);
|
||||||
if (col.data_type === 'jsonb') {
|
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 {
|
} 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')) {
|
} else if (!col.column_name.includes('_encrypted')) {
|
||||||
// Проверяем, есть ли зашифрованная версия этой колонки
|
// Проверяем, есть ли зашифрованная версия этой колонки
|
||||||
@@ -181,6 +181,13 @@ class EncryptedDataService {
|
|||||||
return rows;
|
return rows;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error(`❌ Ошибка получения данных из ${tableName}:`, 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;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -230,17 +237,18 @@ class EncryptedDataService {
|
|||||||
// Проверяем, что значение не пустое перед шифрованием
|
// Проверяем, что значение не пустое перед шифрованием
|
||||||
if (value === null || value === undefined || (typeof value === 'string' && value.trim() === '')) {
|
if (value === null || value === undefined || (typeof value === 'string' && value.trim() === '')) {
|
||||||
// Пропускаем пустые значения
|
// Пропускаем пустые значения
|
||||||
// console.log(`⚠️ Пропускаем пустое зашифрованное поле ${key}`);
|
console.log(`⚠️ Пропускаем пустое зашифрованное поле ${key}`);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const currentParamIndex = paramIndex++;
|
const currentParamIndex = paramIndex++;
|
||||||
filteredData[key] = value; // Добавляем в отфильтрованные данные
|
filteredData[key] = value; // Добавляем в отфильтрованные данные
|
||||||
// console.log(`✅ Добавили зашифрованное поле ${key} в filteredData`);
|
console.log(`✅ Добавили зашифрованное поле ${key} = "${value}" в filteredData`);
|
||||||
if (encryptedColumn.data_type === 'jsonb') {
|
if (encryptedColumn.data_type === 'jsonb') {
|
||||||
encryptedData[`${key}_encrypted`] = `encrypt_json($${currentParamIndex}, ${hasEncryptedFields ? '$1::text' : 'NULL'})`;
|
encryptedData[`${key}_encrypted`] = `encrypt_json($${currentParamIndex}, ${hasEncryptedFields ? '$1::text' : 'NULL'})`;
|
||||||
} else {
|
} else {
|
||||||
encryptedData[`${key}_encrypted`] = `encrypt_text($${currentParamIndex}, ${hasEncryptedFields ? '$1::text' : 'NULL'})`;
|
encryptedData[`${key}_encrypted`] = `encrypt_text($${currentParamIndex}, ${hasEncryptedFields ? '$1::text' : 'NULL'})`;
|
||||||
}
|
}
|
||||||
|
console.log(`🔐 Будем шифровать ${key} -> ${key}_encrypted`);
|
||||||
} else if (unencryptedColumn) {
|
} else if (unencryptedColumn) {
|
||||||
// Если есть незашифрованная колонка, сохраняем как есть
|
// Если есть незашифрованная колонка, сохраняем как есть
|
||||||
// Проверяем, что значение не пустое перед сохранением (кроме role и sender_type)
|
// Проверяем, что значение не пустое перед сохранением (кроме role и sender_type)
|
||||||
@@ -297,6 +305,10 @@ class EncryptedDataService {
|
|||||||
const query = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders}) RETURNING *`;
|
const query = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders}) RETURNING *`;
|
||||||
const params = hasEncryptedFields ? [this.encryptionKey, ...Object.values(filteredData)] : [...Object.values(filteredData)];
|
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);
|
const { rows } = await db.getQuery()(query, params);
|
||||||
return rows[0];
|
return rows[0];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,12 +151,36 @@ class IdentityService {
|
|||||||
try {
|
try {
|
||||||
const identities = await encryptedDb.getData('user_identities', { user_id: userId });
|
const identities = await encryptedDb.getData('user_identities', { user_id: userId });
|
||||||
logger.info(`[IdentityService] Found ${identities.length} identities for user ${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) {
|
} catch (error) {
|
||||||
logger.error(`[IdentityService] Error getting identities for user ${userId}:`, error);
|
logger.error(`[IdentityService] Error getting identities for user ${userId}:`, error);
|
||||||
|
|
||||||
|
// Если произошла ошибка расшифровки, попробуем получить данные напрямую
|
||||||
|
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 [];
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Получает идентификаторы пользователя по типу провайдера
|
* Получает идентификаторы пользователя по типу провайдера
|
||||||
|
|||||||
@@ -62,4 +62,9 @@ async function getRpcUrlByNetworkId(networkId) {
|
|||||||
return providers[0]?.rpc_url || null;
|
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 };
|
||||||
@@ -56,25 +56,14 @@ async function checkUserIdentity(userId, provider, providerId) {
|
|||||||
|
|
||||||
// Добавление новой идентификации
|
// Добавление новой идентификации
|
||||||
async function addUserIdentity(userId, provider, providerId) {
|
async function addUserIdentity(userId, provider, providerId) {
|
||||||
// Получаем ключ шифрования
|
const encryptedDb = require('../services/encryptedDatabaseService');
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
let encryptionKey = 'default-key';
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const keyPath = path.join(__dirname, '../ssl/keys/full_db_encryption.key');
|
await encryptedDb.saveData('user_identities', {
|
||||||
if (fs.existsSync(keyPath)) {
|
user_id: userId,
|
||||||
encryptionKey = fs.readFileSync(keyPath, 'utf8').trim();
|
provider: provider,
|
||||||
}
|
provider_id: providerId
|
||||||
} catch (keyError) {
|
});
|
||||||
console.error('Error reading encryption key:', keyError);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await db.getQuery()(
|
|
||||||
'INSERT INTO user_identities (user_id, provider_encrypted, provider_id_encrypted) VALUES ($1, encrypt_text($2, $4), encrypt_text($3, $4))',
|
|
||||||
[userId, provider, providerId, encryptionKey]
|
|
||||||
);
|
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === '23505') {
|
if (error.code === '23505') {
|
||||||
|
|||||||
@@ -10,76 +10,63 @@
|
|||||||
* GitHub: https://github.com/HB3-ACCELERATOR
|
* GitHub: https://github.com/HB3-ACCELERATOR
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import api from '@/api/axios';
|
// Сервис для работы с DLE v2
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
/**
|
|
||||||
* Сервис для работы с DLE v2 (Digital Legal Entity)
|
|
||||||
* Современный подход с единым контрактом
|
|
||||||
*/
|
|
||||||
class DLEV2Service {
|
|
||||||
/**
|
/**
|
||||||
* Создает новое DLE v2
|
* Создает новое DLE v2
|
||||||
* @param {Object} dleParams - Параметры DLE
|
* @param {Object} dleParams - Параметры DLE
|
||||||
* @returns {Promise<Object>} - Результат создания
|
* @returns {Promise<Object>} - Результат создания
|
||||||
*/
|
*/
|
||||||
async createDLE(dleParams) {
|
export const createDLE = async (dleParams) => {
|
||||||
try {
|
try {
|
||||||
const response = await api.post('/dle-v2', dleParams);
|
const response = await axios.post('/api/dle-v2', dleParams);
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error('Ошибка при создании DLE v2:', error);
|
console.error('Ошибка при создании DLE:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Получает список всех DLE v2
|
* Получает список всех DLE v2
|
||||||
* @returns {Promise<Array>} - Список DLE v2
|
* @returns {Promise<Object>} - Список DLE
|
||||||
*/
|
*/
|
||||||
async getAllDLEs() {
|
export const getAllDLEs = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await api.get('/dle-v2');
|
const response = await axios.get('/api/dle-v2');
|
||||||
return response.data.data || [];
|
|
||||||
} catch (error) {
|
|
||||||
// console.error('Ошибка при получении списка DLE v2:', error);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Получает настройки по умолчанию для DLE v2
|
|
||||||
* @returns {Promise<Object>} - Настройки по умолчанию
|
|
||||||
*/
|
|
||||||
async getDefaults() {
|
|
||||||
try {
|
|
||||||
const response = await api.get('/dle-v2/defaults');
|
|
||||||
return response.data.data;
|
|
||||||
} catch (error) {
|
|
||||||
// console.error('Ошибка при получении настроек по умолчанию DLE v2:', error);
|
|
||||||
return {
|
|
||||||
votingDelay: 1,
|
|
||||||
votingPeriod: 45818,
|
|
||||||
proposalThreshold: '100000',
|
|
||||||
quorumPercentage: 4,
|
|
||||||
minTimelockDelay: 2
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Удаляет DLE v2 по адресу
|
|
||||||
* @param {string} dleAddress - Адрес DLE
|
|
||||||
* @returns {Promise<Object>} - Результат удаления
|
|
||||||
*/
|
|
||||||
async deleteDLE(dleAddress) {
|
|
||||||
try {
|
|
||||||
const response = await api.delete(`/dle-v2/${dleAddress}`);
|
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// console.error('Ошибка при удалении DLE v2:', error);
|
console.error('Ошибка при получении списка DLE:', error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
export default new DLEV2Service();
|
/**
|
||||||
|
* Получает информацию о конкретном DLE v2
|
||||||
|
* @param {string} dleAddress - Адрес DLE
|
||||||
|
* @returns {Promise<Object>} - Информация о DLE
|
||||||
|
*/
|
||||||
|
export const getDLEInfo = async (dleAddress) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`/api/dle-v2/${dleAddress}`);
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка при получении информации о DLE:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Получает параметры по умолчанию для создания DLE v2
|
||||||
|
* @returns {Promise<Object>} - Параметры по умолчанию
|
||||||
|
*/
|
||||||
|
export const getDefaultParams = async () => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get('/api/dle-v2/default-params');
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка при получении параметров по умолчанию:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -569,6 +569,9 @@
|
|||||||
v-model="unifiedPrivateKey"
|
v-model="unifiedPrivateKey"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder="Введите приватный ключ (0x... или без префикса)"
|
placeholder="Введите приватный ключ (0x... или без префикса)"
|
||||||
|
@input="() => { console.log('Input event triggered'); validatePrivateKey('unified'); }"
|
||||||
|
@focus="() => console.log('Input field focused')"
|
||||||
|
@blur="() => console.log('Input field blurred')"
|
||||||
>
|
>
|
||||||
<span class="input-icon" @click="showUnifiedKey = !showUnifiedKey">
|
<span class="input-icon" @click="showUnifiedKey = !showUnifiedKey">
|
||||||
<i :class="showUnifiedKey ? 'fas fa-eye-slash' : 'fas fa-eye'"></i>
|
<i :class="showUnifiedKey ? 'fas fa-eye-slash' : 'fas fa-eye'"></i>
|
||||||
@@ -1951,15 +1954,18 @@ const toggleKeyVisibility = (chainId) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Валидация приватного ключа с дебаунсом
|
// Валидация приватного ключа с дебаунсом
|
||||||
const validatePrivateKey = (chainId) => {
|
const validatePrivateKey = async (chainId) => {
|
||||||
|
console.log('Функция validatePrivateKey вызвана для chainId:', chainId);
|
||||||
|
|
||||||
// Очищаем предыдущий таймер
|
// Очищаем предыдущий таймер
|
||||||
if (validatePrivateKey.timeout) {
|
if (validatePrivateKey.timeout) {
|
||||||
clearTimeout(validatePrivateKey.timeout);
|
clearTimeout(validatePrivateKey.timeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Устанавливаем новый таймер для дебаунса
|
// Устанавливаем новый таймер для дебаунса
|
||||||
validatePrivateKey.timeout = setTimeout(() => {
|
validatePrivateKey.timeout = setTimeout(async () => {
|
||||||
const key = chainId === 'unified' ? unifiedPrivateKey.value : privateKeys[chainId];
|
const key = chainId === 'unified' ? unifiedPrivateKey.value : privateKeys[chainId];
|
||||||
|
console.log('Ключ для валидации:', key);
|
||||||
|
|
||||||
if (!key) {
|
if (!key) {
|
||||||
keyValidation[chainId] = null;
|
keyValidation[chainId] = null;
|
||||||
@@ -1967,26 +1973,33 @@ const validatePrivateKey = (chainId) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Простая валидация длины и формата
|
// Логируем отправляемый ключ (только для отладки)
|
||||||
const cleanKey = key.startsWith('0x') ? key.slice(2) : key;
|
console.log('Отправляем приватный ключ для валидации:', key);
|
||||||
|
console.log('Длина ключа:', key.length);
|
||||||
|
console.log('Полный ключ:', key);
|
||||||
|
|
||||||
if (cleanKey.length === 64 && /^[a-fA-F0-9]+$/.test(cleanKey)) {
|
// Отправляем запрос на бэкенд для валидации
|
||||||
// Генерируем адрес из приватного ключа (упрощенная версия)
|
const response = await axios.post('/api/dle-v2/validate-private-key', {
|
||||||
const address = '0x' + cleanKey.substring(0, 40);
|
privateKey: key
|
||||||
|
});
|
||||||
|
|
||||||
keyValidation[chainId] = {
|
console.log('Ответ от сервера:', response.data);
|
||||||
isValid: true,
|
|
||||||
address: address,
|
if (response.data.success) {
|
||||||
error: null
|
keyValidation[chainId] = response.data.data;
|
||||||
};
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Некорректный формат ключа');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
keyValidation[chainId] = {
|
keyValidation[chainId] = {
|
||||||
isValid: false,
|
isValid: false,
|
||||||
address: null,
|
address: null,
|
||||||
error: 'Некорректный приватный ключ'
|
error: response.data.message
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка валидации приватного ключа:', error);
|
||||||
|
keyValidation[chainId] = {
|
||||||
|
isValid: false,
|
||||||
|
address: null,
|
||||||
|
error: error.response?.data?.message || 'Ошибка валидации приватного ключа'
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, 300); // Задержка 300мс
|
}, 300); // Задержка 300мс
|
||||||
@@ -2107,6 +2120,9 @@ watch([() => dleSettings.name, () => dleSettings.tokenSymbol, selectedNetworks],
|
|||||||
|
|
||||||
// Инициализация
|
// Инициализация
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
console.log('🚀 DleDeployFormView компонент загружен - ТЕСТ ОБНОВЛЕНИЯ');
|
||||||
|
alert('Компонент загружен - проверьте консоль');
|
||||||
|
|
||||||
// Загружаем список стран
|
// Загружаем список стран
|
||||||
loadCountries();
|
loadCountries();
|
||||||
|
|
||||||
@@ -2209,7 +2225,10 @@ const deploySmartContracts = async () => {
|
|||||||
supportedChainIds: dleSettings.selectedNetworks || [],
|
supportedChainIds: dleSettings.selectedNetworks || [],
|
||||||
|
|
||||||
// Текущая цепочка (будет установлена при деплое)
|
// Текущая цепочка (будет установлена при деплое)
|
||||||
currentChainId: dleSettings.selectedNetworks[0] || 1
|
currentChainId: dleSettings.selectedNetworks[0] || 1,
|
||||||
|
|
||||||
|
// Приватный ключ для деплоя
|
||||||
|
privateKey: unifiedPrivateKey.value
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('Данные для деплоя DLE:', deployData);
|
console.log('Данные для деплоя DLE:', deployData);
|
||||||
@@ -2247,6 +2266,9 @@ const isFormValid = computed(() => {
|
|||||||
dleSettings.governanceQuorum > 0 &&
|
dleSettings.governanceQuorum > 0 &&
|
||||||
dleSettings.governanceQuorum <= 100 &&
|
dleSettings.governanceQuorum <= 100 &&
|
||||||
dleSettings.selectedNetworks.length > 0 &&
|
dleSettings.selectedNetworks.length > 0 &&
|
||||||
|
// Проверка приватного ключа
|
||||||
|
unifiedPrivateKey.value &&
|
||||||
|
keyValidation.unified?.isValid &&
|
||||||
// Валидация координат
|
// Валидация координат
|
||||||
validateCoordinates(dleSettings.coordinates)
|
validateCoordinates(dleSettings.coordinates)
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user