feat: Добавлены формы деплоя модулей DLE с полными настройками
- Создана форма деплоя TreasuryModule с детальными настройками казны - Создана форма деплоя TimelockModule с настройками временных задержек - Создана форма деплоя DLEReader с простой конфигурацией - Добавлены маршруты и индексы для всех модулей - Исправлены пути импорта BaseLayout - Добавлены авторские права во все файлы - Улучшена архитектура деплоя модулей отдельно от основного DLE
This commit is contained in:
@@ -18,10 +18,36 @@ const auth = require('../middleware/auth');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const ethers = require('ethers'); // Added ethers for private key validation
|
||||
const deploymentTracker = require('../utils/deploymentTracker');
|
||||
const create2 = require('../utils/create2');
|
||||
const verificationStore = require('../services/verificationStore');
|
||||
const etherscanV2 = require('../services/etherscanV2VerificationService');
|
||||
|
||||
/**
|
||||
* Асинхронная функция для выполнения деплоя в фоне
|
||||
*/
|
||||
async function executeDeploymentInBackground(deploymentId, dleParams) {
|
||||
try {
|
||||
// Отправляем уведомление о начале
|
||||
deploymentTracker.updateDeployment(deploymentId, {
|
||||
status: 'in_progress',
|
||||
stage: 'initializing'
|
||||
});
|
||||
|
||||
deploymentTracker.addLog(deploymentId, '🚀 Начинаем деплой DLE контракта и модулей', 'info');
|
||||
|
||||
// Выполняем деплой с передачей deploymentId для WebSocket обновлений
|
||||
const result = await dleV2Service.createDLE(dleParams, deploymentId);
|
||||
|
||||
// Завершаем успешно
|
||||
deploymentTracker.completeDeployment(deploymentId, result.data);
|
||||
|
||||
} catch (error) {
|
||||
// Завершаем с ошибкой
|
||||
deploymentTracker.failDeployment(deploymentId, error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @route POST /api/dle-v2
|
||||
* @desc Создать новое DLE v2 (Digital Legal Entity)
|
||||
@@ -30,7 +56,7 @@ const etherscanV2 = require('../services/etherscanV2VerificationService');
|
||||
router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res, next) => {
|
||||
try {
|
||||
const dleParams = req.body;
|
||||
logger.info('Получен запрос на создание DLE v2:', dleParams);
|
||||
logger.info('🔥 Получен запрос на асинхронный деплой DLE v2');
|
||||
|
||||
// Если параметр initialPartners не был передан явно, используем адрес авторизованного пользователя
|
||||
if (!dleParams.initialPartners || dleParams.initialPartners.length === 0) {
|
||||
@@ -51,22 +77,26 @@ router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res, next) =>
|
||||
}
|
||||
}
|
||||
|
||||
// Создаем DLE v2
|
||||
const result = await dleV2Service.createDLE(dleParams);
|
||||
// Создаем запись о деплое
|
||||
const deploymentId = deploymentTracker.createDeployment(dleParams);
|
||||
|
||||
logger.info('DLE v2 успешно создано:', result);
|
||||
// Запускаем деплой в фоне (без await!)
|
||||
executeDeploymentInBackground(deploymentId, dleParams);
|
||||
|
||||
logger.info(`📤 Деплой запущен асинхронно: ${deploymentId}`);
|
||||
|
||||
// Сразу возвращаем ответ с ID деплоя
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'DLE v2 успешно создано',
|
||||
data: result.data
|
||||
message: 'Деплой запущен в фоновом режиме',
|
||||
deploymentId: deploymentId
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при создании DLE v2:', error);
|
||||
logger.error('❌ Ошибка при запуске асинхронного деплоя:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || 'Произошла ошибка при создании DLE v2'
|
||||
message: error.message || 'Произошла ошибка при запуске деплоя'
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -94,46 +124,6 @@ router.get('/', async (req, res, next) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @route POST /api/dle-v2/manual-card
|
||||
* @desc Ручное сохранение карточки DLE по адресу (если деплой уже был)
|
||||
* @access Private (admin)
|
||||
*/
|
||||
router.post('/manual-card', auth.requireAuth, auth.requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { dleAddress, name, symbol, location, coordinates, jurisdiction, oktmo, okvedCodes, kpp, quorumPercentage, initialPartners, initialAmounts, supportedChainIds, networks } = req.body || {};
|
||||
if (!dleAddress) {
|
||||
return res.status(400).json({ success: false, message: 'dleAddress обязателен' });
|
||||
}
|
||||
const data = {
|
||||
name: name || '',
|
||||
symbol: symbol || '',
|
||||
location: location || '',
|
||||
coordinates: coordinates || '',
|
||||
jurisdiction: jurisdiction ?? 1,
|
||||
oktmo: oktmo ?? null,
|
||||
okvedCodes: Array.isArray(okvedCodes) ? okvedCodes : [],
|
||||
kpp: kpp ?? null,
|
||||
quorumPercentage: quorumPercentage ?? 51,
|
||||
initialPartners: Array.isArray(initialPartners) ? initialPartners : [],
|
||||
initialAmounts: Array.isArray(initialAmounts) ? initialAmounts : [],
|
||||
governanceSettings: {
|
||||
quorumPercentage: quorumPercentage ?? 51,
|
||||
supportedChainIds: Array.isArray(supportedChainIds) ? supportedChainIds : [],
|
||||
currentChainId: Array.isArray(supportedChainIds) && supportedChainIds.length ? supportedChainIds[0] : 1
|
||||
},
|
||||
dleAddress,
|
||||
version: 'v2',
|
||||
networks: Array.isArray(networks) ? networks : [],
|
||||
createdAt: new Date().toISOString()
|
||||
};
|
||||
const savedPath = dleV2Service.saveDLEData(data);
|
||||
return res.json({ success: true, data: { file: savedPath } });
|
||||
} catch (e) {
|
||||
logger.error('manual-card error', e);
|
||||
return res.status(500).json({ success: false, message: e.message });
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @route GET /api/dle-v2/default-params
|
||||
@@ -342,35 +332,130 @@ router.post('/validate-private-key', async (req, res, next) => {
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @route GET /api/dle-v2/deployment-status/:deploymentId
|
||||
* @desc Получить статус деплоя
|
||||
* @access Private
|
||||
*/
|
||||
router.get('/deployment-status/:deploymentId', auth.requireAuth, auth.requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { deploymentId } = req.params;
|
||||
|
||||
const deployment = deploymentTracker.getDeployment(deploymentId);
|
||||
|
||||
if (!deployment) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Деплой не найден'
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
id: deployment.id,
|
||||
status: deployment.status,
|
||||
stage: deployment.stage,
|
||||
progress: deployment.progress,
|
||||
networks: deployment.networks,
|
||||
startedAt: deployment.startedAt,
|
||||
updatedAt: deployment.updatedAt,
|
||||
logs: deployment.logs.slice(-50), // Последние 50 логов
|
||||
error: deployment.error
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при получении статуса деплоя:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || 'Произошла ошибка при получении статуса'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @route GET /api/dle-v2/deployment-result/:deploymentId
|
||||
* @desc Получить результат завершенного деплоя
|
||||
* @access Private
|
||||
*/
|
||||
router.get('/deployment-result/:deploymentId', auth.requireAuth, auth.requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { deploymentId } = req.params;
|
||||
|
||||
const deployment = deploymentTracker.getDeployment(deploymentId);
|
||||
|
||||
if (!deployment) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: 'Деплой не найден'
|
||||
});
|
||||
}
|
||||
|
||||
if (deployment.status !== 'completed') {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: `Деплой не завершен. Текущий статус: ${deployment.status}`,
|
||||
status: deployment.status
|
||||
});
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
result: deployment.result,
|
||||
completedAt: deployment.completedAt,
|
||||
duration: deployment.completedAt ? deployment.completedAt - deployment.startedAt : null
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при получении результата деплоя:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || 'Произошла ошибка при получении результата'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @route GET /api/dle-v2/deployment-stats
|
||||
* @desc Получить статистику деплоев
|
||||
* @access Private
|
||||
*/
|
||||
router.get('/deployment-stats', auth.requireAuth, auth.requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const stats = deploymentTracker.getStats();
|
||||
const activeDeployments = deploymentTracker.getActiveDeployments();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
stats,
|
||||
activeDeployments: activeDeployments.map(d => ({
|
||||
id: d.id,
|
||||
stage: d.stage,
|
||||
progress: d.progress,
|
||||
startedAt: d.startedAt
|
||||
}))
|
||||
}
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при получении статистики деплоев:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || 'Произошла ошибка при получении статистики'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
/**
|
||||
* Дополнительные маршруты (подключаются из app.js)
|
||||
*/
|
||||
|
||||
// Предсказание адресов по выбранным сетям с использованием CREATE2
|
||||
router.post('/predict-addresses', auth.requireAuth, auth.requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { name, symbol, selectedNetworks } = req.body || {};
|
||||
if (!selectedNetworks || !Array.isArray(selectedNetworks) || selectedNetworks.length === 0) {
|
||||
return res.status(400).json({ success: false, message: 'Не переданы сети' });
|
||||
}
|
||||
|
||||
// Используем служебные секреты для фабрики и SALT
|
||||
// Factory больше не используется - адреса DLE теперь вычисляются через CREATE с выровненным nonce
|
||||
const result = {};
|
||||
for (const chainId of selectedNetworks) {
|
||||
// Адрес DLE будет одинаковым во всех сетях благодаря выравниванию nonce
|
||||
// Вычисляется в deploy-multichain.js во время деплоя
|
||||
result[chainId] = 'Вычисляется во время деплоя';
|
||||
}
|
||||
|
||||
return res.json({ success: true, data: result });
|
||||
} catch (e) {
|
||||
logger.error('predict-addresses error', e);
|
||||
return res.status(500).json({ success: false, message: 'Ошибка расчета адресов' });
|
||||
}
|
||||
});
|
||||
|
||||
// Сохранить GUID верификации (если нужно отдельным вызовом)
|
||||
router.post('/verify/save-guid', auth.requireAuth, auth.requireAdmin, async (req, res) => {
|
||||
|
||||
Reference in New Issue
Block a user