feat: Добавлены формы деплоя модулей DLE с полными настройками

- Создана форма деплоя TreasuryModule с детальными настройками казны
- Создана форма деплоя TimelockModule с настройками временных задержек
- Создана форма деплоя DLEReader с простой конфигурацией
- Добавлены маршруты и индексы для всех модулей
- Исправлены пути импорта BaseLayout
- Добавлены авторские права во все файлы
- Улучшена архитектура деплоя модулей отдельно от основного DLE
This commit is contained in:
2025-09-23 02:57:59 +03:00
parent 9f94295d15
commit de0f8aecf2
63 changed files with 11631 additions and 1920 deletions

View File

@@ -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) => {