ваше сообщение коммита
This commit is contained in:
191
backend/routes/dle.js
Normal file
191
backend/routes/dle.js
Normal file
@@ -0,0 +1,191 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const dleService = require('../services/dleService');
|
||||
const logger = require('../utils/logger');
|
||||
const auth = require('../middleware/auth');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
/**
|
||||
* @route POST /api/dle
|
||||
* @desc Создать новое DLE (Digital Legal Entity)
|
||||
* @access Private (только для авторизованных пользователей с ролью admin)
|
||||
*/
|
||||
router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const dleParams = req.body;
|
||||
logger.info('Получен запрос на создание DLE:', dleParams);
|
||||
|
||||
// Если параметр partners не был передан явно, используем адрес авторизованного пользователя
|
||||
if (!dleParams.partners || dleParams.partners.length === 0) {
|
||||
// Проверяем, есть ли в сессии адрес кошелька пользователя
|
||||
if (!req.user || !req.user.walletAddress) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Не указан адрес кошелька пользователя или партнеров для распределения токенов'
|
||||
});
|
||||
}
|
||||
|
||||
// Используем адрес авторизованного пользователя
|
||||
dleParams.partners = [req.user.address || req.user.walletAddress];
|
||||
|
||||
// Если суммы не указаны, используем значение по умолчанию (100% токенов)
|
||||
if (!dleParams.amounts || dleParams.amounts.length === 0) {
|
||||
dleParams.amounts = ['1000000'];
|
||||
}
|
||||
}
|
||||
|
||||
const result = await dleService.createDLE(dleParams);
|
||||
|
||||
res.status(201).json({
|
||||
success: true,
|
||||
message: 'DLE успешно создано',
|
||||
data: result
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при создании DLE:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || 'Произошла ошибка при создании DLE',
|
||||
error: process.env.NODE_ENV === 'development' ? error.stack : undefined
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @route GET /api/dle
|
||||
* @desc Получить список всех DLE
|
||||
* @access Private (только для авторизованных пользователей)
|
||||
*/
|
||||
router.get('/', auth.requireAuth, async (req, res) => {
|
||||
try {
|
||||
const dles = await dleService.getAllDLEs();
|
||||
res.json({
|
||||
success: true,
|
||||
data: dles
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при получении списка DLE:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || 'Произошла ошибка при получении списка DLE',
|
||||
error: process.env.NODE_ENV === 'development' ? error.stack : undefined
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @route GET /api/dle/settings
|
||||
* @desc Получить настройки для деплоя DLE по умолчанию
|
||||
* @access Private (только для авторизованных пользователей)
|
||||
*/
|
||||
router.get('/settings', auth.requireAuth, (req, res) => {
|
||||
// Возвращаем настройки по умолчанию, которые будут использоваться
|
||||
// при заполнении формы на фронтенде
|
||||
res.json({
|
||||
success: true,
|
||||
data: {
|
||||
votingDelay: 1, // 1 блок задержки перед началом голосования
|
||||
votingPeriod: 45818, // ~1 неделя в блоках (при 13 секундах на блок)
|
||||
proposalThreshold: '100000', // 100,000 токенов
|
||||
quorumPercentage: 4, // 4% от общего количества токенов
|
||||
minTimelockDelay: 2 // 2 дня
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* @route DELETE /api/dle/:tokenAddress
|
||||
* @desc Удалить DLE по адресу токена
|
||||
* @access Private (только для авторизованных пользователей с ролью admin)
|
||||
*/
|
||||
router.delete('/:tokenAddress', auth.requireAuth, auth.requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { tokenAddress } = req.params;
|
||||
logger.info(`Получен запрос на удаление DLE с адресом токена: ${tokenAddress}`);
|
||||
|
||||
// Проверяем существование DLE в директории contracts-data/dles
|
||||
const dlesDir = path.join(__dirname, '../contracts-data/dles');
|
||||
const files = fs.readdirSync(dlesDir);
|
||||
|
||||
let fileToDelete = null;
|
||||
|
||||
// Находим файл, содержащий указанный адрес токена
|
||||
for (const file of files) {
|
||||
const filePath = path.join(dlesDir, file);
|
||||
if (fs.statSync(filePath).isFile() && file.endsWith('.json')) {
|
||||
try {
|
||||
const dleData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
if (dleData.tokenAddress && dleData.tokenAddress.toLowerCase() === tokenAddress.toLowerCase()) {
|
||||
fileToDelete = filePath;
|
||||
break;
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(`Ошибка при чтении файла ${file}:`, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!fileToDelete) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: `DLE с адресом токена ${tokenAddress} не найдено`
|
||||
});
|
||||
}
|
||||
|
||||
// Удаляем файл
|
||||
fs.unlinkSync(fileToDelete);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `DLE с адресом токена ${tokenAddress} успешно удалено`
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при удалении DLE:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || 'Произошла ошибка при удалении DLE',
|
||||
error: process.env.NODE_ENV === 'development' ? error.stack : undefined
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @route DELETE /api/dle/empty/:fileName
|
||||
* @desc Удалить пустое DLE по имени файла
|
||||
* @access Private (только для авторизованных пользователей с ролью admin)
|
||||
*/
|
||||
router.delete('/empty/:fileName', auth.requireAuth, auth.requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { fileName } = req.params;
|
||||
logger.info(`Получен запрос на удаление пустого DLE с именем файла: ${fileName}`);
|
||||
|
||||
// Проверяем существование файла в директории contracts-data/dles
|
||||
const dlesDir = path.join(__dirname, '../contracts-data/dles');
|
||||
const filePath = path.join(dlesDir, fileName);
|
||||
|
||||
if (!fs.existsSync(filePath)) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: `Файл ${fileName} не найден`
|
||||
});
|
||||
}
|
||||
|
||||
// Удаляем файл
|
||||
fs.unlinkSync(filePath);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `Пустое DLE с именем файла ${fileName} успешно удалено`
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при удалении пустого DLE:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: error.message || 'Произошла ошибка при удалении пустого DLE',
|
||||
error: process.env.NODE_ENV === 'development' ? error.stack : undefined
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -60,11 +60,11 @@
|
||||
const nominatimUrl = `https://nominatim.openstreetmap.org/search?${queryParams.toString()}`;
|
||||
|
||||
if (logger && typeof logger.info === 'function') {
|
||||
logger.info(`[Geocoding] Proxying request to Nominatim: ${nominatimUrl}`);
|
||||
logger.info(`[Geocoding] Proxying request to Nominatim: ${nominatimUrl}`);
|
||||
} else {
|
||||
console.log(`[Geocoding] Proxying request to Nominatim: ${nominatimUrl}`);
|
||||
}
|
||||
|
||||
|
||||
const nominatimResponse = await axios.get(nominatimUrl);
|
||||
|
||||
res.json(nominatimResponse.data);
|
||||
|
||||
162
backend/routes/settings.js
Normal file
162
backend/routes/settings.js
Normal file
@@ -0,0 +1,162 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { requireAdmin } = require('../middleware/auth');
|
||||
const logger = require('../utils/logger');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { ethers } = require('ethers');
|
||||
|
||||
// Логируем версию ethers для отладки
|
||||
logger.info(`Ethers version: ${ethers.version || 'unknown'}`);
|
||||
|
||||
// Путь к файлу с настройками
|
||||
const RPC_CONFIG_PATH = path.join(__dirname, '../config/rpc-settings.json');
|
||||
const AUTH_TOKENS_PATH = path.join(__dirname, '../config/auth-tokens.json');
|
||||
|
||||
// Вспомогательная функция для чтения настроек из файла
|
||||
const readSettingsFile = (filePath, defaultValue = []) => {
|
||||
try {
|
||||
if (fs.existsSync(filePath)) {
|
||||
const data = fs.readFileSync(filePath, 'utf8');
|
||||
return JSON.parse(data);
|
||||
}
|
||||
return defaultValue;
|
||||
} catch (error) {
|
||||
logger.error(`Ошибка при чтении файла настроек ${filePath}:`, error);
|
||||
return defaultValue;
|
||||
}
|
||||
};
|
||||
|
||||
// Вспомогательная функция для записи настроек в файл
|
||||
const writeSettingsFile = async (filePath, data) => {
|
||||
try {
|
||||
// Создаем директорию, если не существует
|
||||
const dirname = path.dirname(filePath);
|
||||
if (!fs.existsSync(dirname)) {
|
||||
fs.mkdirSync(dirname, { recursive: true });
|
||||
}
|
||||
|
||||
await fs.promises.writeFile(filePath, JSON.stringify(data, null, 2), 'utf8');
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error(`Ошибка при записи файла настроек ${filePath}:`, error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Получение RPC настроек
|
||||
router.get('/rpc', requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const rpcConfigs = readSettingsFile(RPC_CONFIG_PATH);
|
||||
res.json({ success: true, data: rpcConfigs });
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при получении RPC настроек:', error);
|
||||
res.status(500).json({ success: false, error: 'Ошибка сервера при получении настроек RPC' });
|
||||
}
|
||||
});
|
||||
|
||||
// Сохранение RPC настроек
|
||||
router.post('/rpc', requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { rpcConfigs } = req.body;
|
||||
|
||||
if (!Array.isArray(rpcConfigs)) {
|
||||
return res.status(400).json({ success: false, error: 'Неверный формат данных' });
|
||||
}
|
||||
|
||||
const success = await writeSettingsFile(RPC_CONFIG_PATH, rpcConfigs);
|
||||
|
||||
if (success) {
|
||||
res.json({ success: true, message: 'RPC настройки успешно сохранены' });
|
||||
} else {
|
||||
res.status(500).json({ success: false, error: 'Ошибка при сохранении RPC настроек' });
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при сохранении RPC настроек:', error);
|
||||
res.status(500).json({ success: false, error: 'Ошибка сервера при сохранении настроек RPC' });
|
||||
}
|
||||
});
|
||||
|
||||
// Получение токенов для аутентификации
|
||||
router.get('/auth-tokens', requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const authTokens = readSettingsFile(AUTH_TOKENS_PATH);
|
||||
res.json({ success: true, data: authTokens });
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при получении токенов аутентификации:', error);
|
||||
res.status(500).json({ success: false, error: 'Ошибка сервера при получении токенов аутентификации' });
|
||||
}
|
||||
});
|
||||
|
||||
// Сохранение токенов для аутентификации
|
||||
router.post('/auth-tokens', requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { authTokens } = req.body;
|
||||
|
||||
if (!Array.isArray(authTokens)) {
|
||||
return res.status(400).json({ success: false, error: 'Неверный формат данных' });
|
||||
}
|
||||
|
||||
const success = await writeSettingsFile(AUTH_TOKENS_PATH, authTokens);
|
||||
|
||||
if (success) {
|
||||
res.json({ success: true, message: 'Токены аутентификации успешно сохранены' });
|
||||
} else {
|
||||
res.status(500).json({ success: false, error: 'Ошибка при сохранении токенов аутентификации' });
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Ошибка при сохранении токенов аутентификации:', error);
|
||||
res.status(500).json({ success: false, error: 'Ошибка сервера при сохранении токенов аутентификации' });
|
||||
}
|
||||
});
|
||||
|
||||
// Тестирование RPC соединения
|
||||
router.post('/rpc-test', requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { rpcUrl, networkId } = req.body;
|
||||
|
||||
if (!rpcUrl || !networkId) {
|
||||
return res.status(400).json({ success: false, error: 'Необходимо указать URL и ID сети' });
|
||||
}
|
||||
|
||||
logger.info(`Тестирование RPC для ${networkId}: ${rpcUrl}`);
|
||||
|
||||
try {
|
||||
// Пробуем создать провайдера и получить номер последнего блока (обновлено для ethers v6)
|
||||
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
||||
|
||||
// Устанавливаем таймаут для соединения
|
||||
const timeoutPromise = new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Таймаут соединения')), 10000)
|
||||
);
|
||||
|
||||
// Пробуем получить номер последнего блока с таймаутом
|
||||
const blockNumber = await Promise.race([
|
||||
provider.getBlockNumber(),
|
||||
timeoutPromise
|
||||
]);
|
||||
|
||||
logger.info(`Успешное тестирование RPC для ${networkId}: ${rpcUrl}, номер блока: ${blockNumber}`);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `Успешное соединение с ${networkId}`,
|
||||
blockNumber
|
||||
});
|
||||
} catch (providerError) {
|
||||
logger.error(`Ошибка провайдера при тестировании RPC для ${networkId}: ${providerError.message}`);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: providerError.message || 'Не удалось подключиться к RPC провайдеру'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`Неожиданная ошибка при тестировании RPC: ${error.message}`);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
error: error.message || 'Неизвестная ошибка сервера'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user