Files
DLE/backend/routes/settings.js

490 lines
19 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
* All rights reserved.
*
* This software is proprietary and confidential.
* Unauthorized copying, modification, or distribution is prohibited.
*
* For licensing inquiries: info@hb3-accelerator.com
* Website: https://hb3-accelerator.com
* GitHub: https://github.com/HB3-ACCELERATOR
*/
const express = require('express');
const router = express.Router();
const { requireAdmin } = require('../middleware/auth');
const logger = require('../utils/logger');
const { ethers } = require('ethers');
const rpcProviderService = require('../services/rpcProviderService');
const authTokenService = require('../services/authTokenService');
const aiProviderSettingsService = require('../services/aiProviderSettingsService');
const aiAssistant = require('../services/ai-assistant');
const dns = require('node:dns').promises;
const aiAssistantSettingsService = require('../services/aiAssistantSettingsService');
const aiAssistantRulesService = require('../services/aiAssistantRulesService');
const telegramBot = require('../services/telegramBot');
const EmailBotService = require('../services/emailBot');
const emailBotService = new EmailBotService();
const dbSettingsService = require('../services/dbSettingsService');
// Логируем версию ethers для отладки
logger.info(`Ethers version: ${ethers.version || 'unknown'}`);
// Получение RPC настроек
router.get('/rpc', async (req, res, next) => {
try {
let isAdmin = false;
// Проверяем, авторизован ли пользователь и является ли он админом
if (req.session && req.session.authenticated) {
if (req.session.address) {
const authService = require('../services/auth-service');
isAdmin = await authService.checkAdminTokens(req.session.address);
} else {
isAdmin = req.session.isAdmin || false;
}
}
const rpcConfigs = await rpcProviderService.getAllRpcProviders();
if (isAdmin) {
// Для админов возвращаем полные данные
res.json({ success: true, data: rpcConfigs });
} else {
// Для обычных пользователей и гостей возвращаем ограниченные данные для ОТОБРАЖЕНИЯ,
// но с полными данными для функциональности (тестирование RPC)
const limitedConfigs = rpcConfigs.map(config => ({
network_id: config.network_id,
rpc_url: config.rpc_url, // Передаем реальный URL для функциональности
rpc_url_display: 'Скрыто', // Для отображения в UI
chain_id: config.chain_id,
_isLimited: true
}));
res.json({ success: true, data: limitedConfigs });
}
} catch (error) {
logger.error('Ошибка при получении RPC настроек:', error);
next(error);
}
});
// Добавление/обновление одного или нескольких RPC
router.post('/rpc', requireAdmin, async (req, res, next) => {
try {
// Если пришёл массив rpcConfigs — bulk-режим
if (Array.isArray(req.body.rpcConfigs)) {
const rpcConfigs = req.body.rpcConfigs;
if (!rpcConfigs.length) {
return res.status(400).json({ success: false, error: 'rpcConfigs не может быть пустым массивом' });
}
await rpcProviderService.saveAllRpcProviders(rpcConfigs);
return res.json({ success: true, message: 'RPC провайдеры успешно сохранены (bulk)' });
}
// Иначе — одиночный режим (старый)
const { networkId, rpcUrl, chainId } = req.body;
if (!networkId || !rpcUrl) {
return res.status(400).json({ success: false, error: 'networkId и rpcUrl обязательны' });
}
await rpcProviderService.upsertRpcProvider({ networkId, rpcUrl, chainId });
res.json({ success: true, message: 'RPC провайдер сохранён' });
} catch (error) {
logger.error('Ошибка при сохранении RPC:', error);
next(error);
}
});
// Удаление одного RPC
router.delete('/rpc/:networkId', requireAdmin, async (req, res, next) => {
try {
const { networkId } = req.params;
await rpcProviderService.deleteRpcProvider(networkId);
res.json({ success: true, message: 'RPC провайдер удалён' });
} catch (error) {
logger.error('Ошибка при удалении RPC:', error);
next(error);
}
});
// Получение токенов для аутентификации
router.get('/auth-tokens', async (req, res, next) => {
try {
const authTokens = await authTokenService.getAllAuthTokens();
// Возвращаем полные данные для всех пользователей (включая гостевых)
res.json({ success: true, data: authTokens });
} catch (error) {
logger.error('Ошибка при получении токенов аутентификации:', error);
next(error);
}
});
// Сохранение токенов для аутентификации
router.post('/auth-tokens', requireAdmin, async (req, res, next) => {
try {
const { authTokens } = req.body;
if (!Array.isArray(authTokens)) {
return res.status(400).json({ success: false, error: 'Неверный формат данных' });
}
await authTokenService.saveAllAuthTokens(authTokens);
res.json({ success: true, message: 'Токены аутентификации успешно сохранены' });
} catch (error) {
logger.error('Ошибка при сохранении токенов аутентификации:', error);
next(error);
}
});
// Добавление/обновление одного токена
router.post('/auth-token', requireAdmin, async (req, res, next) => {
try {
const { name, address, network, minBalance } = req.body;
if (!name || !address || !network) {
return res.status(400).json({ success: false, error: 'name, address и network обязательны' });
}
await authTokenService.upsertAuthToken({ name, address, network, minBalance });
res.json({ success: true, message: 'Токен аутентификации сохранён' });
} catch (error) {
logger.error('Ошибка при сохранении токена аутентификации:', error);
next(error);
}
});
// Удаление одного токена
router.delete('/auth-token/:address/:network', requireAdmin, async (req, res, next) => {
try {
const { address, network } = req.params;
await authTokenService.deleteAuthToken(address, network);
res.json({ success: true, message: 'Токен аутентификации удалён' });
} catch (error) {
logger.error('Ошибка при удалении токена аутентификации:', error);
next(error);
}
});
// Тестирование RPC соединения
router.post('/rpc-test', async (req, res, next) => {
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)
let provider;
if (rpcUrl.startsWith('ws://') || rpcUrl.startsWith('wss://')) {
provider = new ethers.WebSocketProvider(rpcUrl);
} else {
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 || 'Неизвестная ошибка сервера'
});
}
});
// Получить настройки AI-провайдера
router.get('/ai-settings/:provider', async (req, res, next) => {
try {
let isAdmin = false;
// Проверяем, авторизован ли пользователь и является ли он админом
if (req.session && req.session.authenticated) {
if (req.session.address) {
const authService = require('../services/auth-service');
isAdmin = await authService.checkAdminTokens(req.session.address);
} else {
isAdmin = req.session.isAdmin || false;
}
}
if (isAdmin) {
const { provider } = req.params;
const settings = await aiProviderSettingsService.getProviderSettings(provider);
res.json({ success: true, settings });
} else {
// Для обычных пользователей и гостей возвращаем пустые настройки
res.json({ success: true, settings: null });
}
} catch (error) {
logger.error('Ошибка при получении AI-настроек:', error);
next(error);
}
});
// Сохранить/обновить настройки AI-провайдера
router.put('/ai-settings/:provider', requireAdmin, async (req, res, next) => {
try {
const { provider } = req.params;
const { api_key, base_url, selected_model, embedding_model } = req.body;
const updated = await aiProviderSettingsService.upsertProviderSettings({ provider, api_key, base_url, selected_model, embedding_model });
res.json({ success: true, settings: updated });
} catch (error) {
logger.error('Ошибка при сохранении AI-настроек:', error);
next(error);
}
});
// Удалить настройки AI-провайдера
router.delete('/ai-settings/:provider', requireAdmin, async (req, res, next) => {
try {
const { provider } = req.params;
await aiProviderSettingsService.deleteProviderSettings(provider);
res.json({ success: true });
} catch (error) {
logger.error('Ошибка при удалении AI-настроек:', error);
next(error);
}
});
// Получить список моделей для провайдера
router.get('/ai-settings/:provider/models', requireAdmin, async (req, res, next) => {
try {
const { provider } = req.params;
const settings = await aiProviderSettingsService.getProviderSettings(provider);
let models = [];
if (provider === 'ollama') {
models = await aiAssistant.getAvailableModels();
} else {
models = await aiProviderSettingsService.getProviderModels(provider, settings || {});
}
res.json({ success: true, models });
} catch (error) {
logger.error('Ошибка при получении моделей AI:', error);
res.status(500).json({ success: false, error: error.message });
}
});
// Проверить валидность ключа (verify)
router.post('/ai-settings/:provider/verify', requireAdmin, async (req, res, next) => {
try {
const { provider } = req.params;
const { api_key, base_url } = req.body;
const result = await aiProviderSettingsService.verifyProviderKey(provider, { api_key, base_url });
if (result.success) {
res.json({ success: true });
} else {
res.status(400).json({ success: false, error: result.error });
}
} catch (error) {
logger.error('Ошибка при проверке AI-ключа:', error);
res.status(500).json({ success: false, error: error.message });
}
});
router.get('/ai-assistant', requireAdmin, async (req, res, next) => {
try {
const settings = await aiAssistantSettingsService.getSettings();
res.json({ success: true, settings });
} catch (error) {
next(error);
}
});
router.put('/ai-assistant', requireAdmin, async (req, res, next) => {
try {
let { selected_rag_tables, ...rest } = req.body;
// Приведение к массиву чисел
if (typeof selected_rag_tables === 'string') {
try {
selected_rag_tables = JSON.parse(selected_rag_tables);
} catch {
selected_rag_tables = [Number(selected_rag_tables)];
}
}
if (!Array.isArray(selected_rag_tables)) {
selected_rag_tables = [Number(selected_rag_tables)];
}
selected_rag_tables = selected_rag_tables.map(Number);
const updated = await aiAssistantSettingsService.upsertSettings({
...rest,
selected_rag_tables,
updated_by: req.session.userId || null
});
res.json({ success: true, settings: updated });
} catch (error) {
next(error);
}
});
// Получить все наборы правил
router.get('/ai-assistant-rules', requireAdmin, async (req, res, next) => {
try {
const rules = await aiAssistantRulesService.getAllRules();
res.json({ success: true, rules });
} catch (error) {
next(error);
}
});
// Получить набор правил по id
router.get('/ai-assistant-rules/:id', requireAdmin, async (req, res, next) => {
try {
const rule = await aiAssistantRulesService.getRuleById(req.params.id);
res.json({ success: true, rule });
} catch (error) {
next(error);
}
});
// Создать набор правил
router.post('/ai-assistant-rules', requireAdmin, async (req, res, next) => {
try {
const created = await aiAssistantRulesService.createRule(req.body);
res.json({ success: true, rule: created });
} catch (error) {
next(error);
}
});
// Обновить набор правил
router.put('/ai-assistant-rules/:id', requireAdmin, async (req, res, next) => {
try {
const updated = await aiAssistantRulesService.updateRule(req.params.id, req.body);
res.json({ success: true, rule: updated });
} catch (error) {
next(error);
}
});
// Удалить набор правил
router.delete('/ai-assistant-rules/:id', requireAdmin, async (req, res, next) => {
try {
await aiAssistantRulesService.deleteRule(req.params.id);
res.json({ success: true });
} catch (error) {
next(error);
}
});
// Получить текущие настройки Email (для страницы Email)
router.get('/email-settings', requireAdmin, async (req, res) => {
try {
const settings = await emailBotService.getSettingsFromDb();
res.json({ success: true, settings });
} catch (error) {
res.status(404).json({ success: false, error: error.message });
}
});
// Получить список всех email (для ассистента)
router.get('/email-settings/list', requireAdmin, async (req, res) => {
try {
const emails = await emailBotService.getAllEmailSettings();
res.json({ success: true, items: emails });
} catch (error) {
res.status(404).json({ success: false, error: error.message });
}
});
// Получить текущие настройки Telegram-бота (для страницы Telegram)
router.get('/telegram-settings', requireAdmin, async (req, res, next) => {
try {
const settings = await telegramBot.getTelegramSettings();
res.json({ success: true, settings });
} catch (error) {
res.status(404).json({ success: false, error: error.message });
}
});
// Получить список всех Telegram-ботов (для ассистента)
router.get('/telegram-settings/list', requireAdmin, async (req, res, next) => {
try {
const bots = await telegramBot.getAllBots();
res.json({ success: true, items: bots });
} catch (error) {
res.status(404).json({ success: false, error: error.message });
}
});
// Получение списка моделей для выбранного AI-провайдера
router.get('/ai-provider-models', requireAdmin, async (req, res, next) => {
try {
const provider = req.query.provider;
if (!provider) return res.status(400).json({ error: 'provider is required' });
const settings = await aiProviderSettingsService.getProviderSettings(provider);
if (!settings) return res.status(404).json({ error: 'Provider not found' });
const models = await aiProviderSettingsService.getProviderModels(provider, {
api_key: settings.api_key,
base_url: settings.base_url,
});
res.json({ models });
} catch (error) {
next(error);
}
});
// Получить настройки базы данных
router.get('/db-settings', async (req, res) => {
try {
const settings = await dbSettingsService.getSettings();
res.json({ success: true, settings });
} catch (error) {
res.status(404).json({ success: false, error: error.message });
}
});
// Обновить настройки базы данных
router.put('/db-settings', requireAdmin, async (req, res) => {
try {
const { db_host, db_port, db_name, db_user, db_password } = req.body;
const updated = await dbSettingsService.upsertSettings({ db_host, db_port, db_name, db_user, db_password });
res.json({ success: true, settings: updated });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Получить все LLM-модели
router.get('/llm-models', requireAdmin, async (req, res) => {
try {
const models = await aiProviderSettingsService.getAllLLMModels();
res.json({ success: true, models });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
// Получить все embedding-модели
router.get('/embedding-models', requireAdmin, async (req, res) => {
try {
const models = await aiProviderSettingsService.getAllEmbeddingModels();
res.json({ success: true, models });
} catch (error) {
res.status(500).json({ success: false, error: error.message });
}
});
module.exports = router;