ваше сообщение коммита
This commit is contained in:
@@ -1,41 +0,0 @@
|
||||
PORT=8000
|
||||
NODE_ENV=development
|
||||
SESSION_SECRET=your_session_secret
|
||||
|
||||
# RPC URLs
|
||||
RPC_URL_ETH=https://your-ethereum-rpc-url
|
||||
RPC_URL_POLYGON=https://your-polygon-rpc-url
|
||||
RPC_URL_BSC=https://your-bsc-rpc-url
|
||||
RPC_URL_ARBITRUM=https://your-arbitrum-rpc-url
|
||||
RPC_URL=https://your-default-rpc-url
|
||||
ETHEREUM_NETWORK_URL=https://your-ethereum-network-url
|
||||
PRIVATE_KEY=your_private_key_here
|
||||
ETHERSCAN_API_KEY=your_etherscan_api_key
|
||||
|
||||
# Database
|
||||
DATABASE_URL=postgresql://dapp_user:dapp_password@postgres:5432/dapp_db
|
||||
DB_HOST=postgres
|
||||
DB_PORT=5432
|
||||
DB_NAME=dapp_db
|
||||
DB_USER=dapp_user
|
||||
DB_PASSWORD=dapp_password
|
||||
|
||||
# Email Configuration
|
||||
EMAIL_USER=your_email@example.com
|
||||
EMAIL_PASSWORD=your_email_password
|
||||
EMAIL_SMTP_HOST=smtp.example.com
|
||||
EMAIL_SMTP_PORT=465
|
||||
EMAIL_IMAP_HOST=imap.example.com
|
||||
EMAIL_IMAP_PORT=993
|
||||
|
||||
# Ollama AI Configuration
|
||||
OLLAMA_BASE_URL=http://ollama:11434
|
||||
OLLAMA_EMBEDDINGS_MODEL=qwen2.5:7b
|
||||
OLLAMA_MODEL=qwen2.5:7b
|
||||
|
||||
# Telegram Bot
|
||||
TELEGRAM_BOT_TOKEN=your_telegram_bot_token
|
||||
TELEGRAM_BOT_USERNAME=your_bot_username
|
||||
|
||||
# Frontend URL
|
||||
FRONTEND_URL=http://localhost:5173
|
||||
@@ -14,6 +14,7 @@ const path = require('path');
|
||||
const messagesRoutes = require('./routes/messages');
|
||||
const ragRoutes = require('./routes/rag'); // Новый роут для RAG-ассистента
|
||||
const monitoringRoutes = require('./routes/monitoring');
|
||||
const pagesRoutes = require('./routes/pages'); // Добавляем импорт роутера страниц
|
||||
|
||||
// Проверка и создание директорий для хранения данных контрактов
|
||||
const ensureDirectoriesExist = () => {
|
||||
@@ -186,6 +187,7 @@ app.use('/api/messages', messagesRoutes);
|
||||
app.use('/api/identities', identitiesRoutes);
|
||||
app.use('/api/rag', ragRoutes); // Подключаем роут
|
||||
app.use('/api/monitoring', monitoringRoutes);
|
||||
app.use('/api/pages', pagesRoutes); // Подключаем роутер страниц
|
||||
|
||||
const nonceStore = new Map(); // или любая другая реализация хранилища nonce
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const { Pool } = require('pg');
|
||||
require('dotenv').config();
|
||||
const axios = require('axios');
|
||||
|
||||
// Выводим настройки подключения (без пароля)
|
||||
console.log('Настройки подключения к базе данных:');
|
||||
@@ -105,5 +106,44 @@ async function saveGuestMessageToDatabase(message, language, guestId) {
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForOllamaModel(modelName) {
|
||||
const ollamaUrl = process.env.OLLAMA_BASE_URL || 'http://ollama:11434';
|
||||
while (true) {
|
||||
try {
|
||||
const res = await axios.get(`${ollamaUrl}/api/tags`);
|
||||
const models = res.data.models.map(m => m.name);
|
||||
if (models.includes(modelName)) {
|
||||
return true;
|
||||
}
|
||||
console.log(`[seedAIAssistantSettings] Ожидание загрузки модели ${modelName}...`);
|
||||
} catch (e) {
|
||||
console.log('[seedAIAssistantSettings] Ollama недоступна, ожидание...');
|
||||
}
|
||||
await new Promise(r => setTimeout(r, 5000));
|
||||
}
|
||||
}
|
||||
|
||||
async function seedAIAssistantSettings() {
|
||||
const modelName = process.env.OLLAMA_MODEL || 'qwen2.5:7b';
|
||||
await waitForOllamaModel(modelName);
|
||||
const res = await pool.query('SELECT COUNT(*) FROM ai_assistant_settings');
|
||||
if (parseInt(res.rows[0].count, 10) === 0) {
|
||||
await pool.query(`
|
||||
INSERT INTO ai_assistant_settings (system_prompt, selected_rag_tables, languages, model, rules, updated_by)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
`, [
|
||||
'Ты — ИИ-ассистент для бизнеса. Отвечай кратко и по делу.',
|
||||
[],
|
||||
['ru'],
|
||||
modelName,
|
||||
JSON.stringify({}),
|
||||
1
|
||||
]);
|
||||
console.log('[seedAIAssistantSettings] ai_assistant_settings: инициализировано дефолтными значениями');
|
||||
} else {
|
||||
console.log('[seedAIAssistantSettings] ai_assistant_settings: уже инициализировано, пропускаю');
|
||||
}
|
||||
}
|
||||
|
||||
// Экспортируем функции для работы с базой данных
|
||||
module.exports = { query, getQuery, pool, getPool, setPoolChangeCallback, initDbPool };
|
||||
module.exports = { query, getQuery, pool, getPool, setPoolChangeCallback, initDbPool, seedAIAssistantSettings };
|
||||
|
||||
@@ -11,5 +11,5 @@ CREATE TABLE IF NOT EXISTS db_settings (
|
||||
|
||||
-- Для простоты предполагаем, что настройки всегда одни (id=1)
|
||||
INSERT INTO db_settings (db_host, db_port, db_name, db_user, db_password)
|
||||
VALUES ('localhost', 5432, 'dapp_db', 'dapp_user', 'dapp_password')
|
||||
VALUES ('postgres', 5432, 'dapp_db', 'dapp_user', 'dapp_password')
|
||||
ON CONFLICT DO NOTHING;
|
||||
@@ -6,7 +6,7 @@ CREATE TABLE IF NOT EXISTS ai_assistant_settings (
|
||||
model TEXT,
|
||||
rules JSONB,
|
||||
updated_at TIMESTAMP DEFAULT NOW(),
|
||||
updated_by INTEGER,
|
||||
updated_by INTEGER
|
||||
);
|
||||
|
||||
-- Вставить дефолтную строку (глобальные настройки)
|
||||
|
||||
@@ -9,6 +9,7 @@ const crypto = require('crypto');
|
||||
const aiAssistantSettingsService = require('../services/aiAssistantSettingsService');
|
||||
const aiAssistantRulesService = require('../services/aiAssistantRulesService');
|
||||
const { isUserBlocked } = require('../utils/userUtils');
|
||||
const { broadcastChatMessage } = require('../wsHub');
|
||||
|
||||
// Настройка multer для обработки файлов в памяти
|
||||
const storage = multer.memoryStorage();
|
||||
@@ -460,7 +461,7 @@ router.post('/message', requireAuth, upload.array('attachments'), async (req, re
|
||||
logger.info(`[RAG] Запуск поиска по RAG: tableId=${ragTableId}, вопрос="${messageContent}", threshold=${threshold}`);
|
||||
const ragResult = await ragAnswer({ tableId: ragTableId, userQuestion: messageContent, threshold });
|
||||
logger.info(`[RAG] Результат поиска по RAG:`, ragResult);
|
||||
if (ragResult && ragResult.answer && ragResult.score && ragResult.score > threshold) {
|
||||
if (ragResult && ragResult.answer && typeof ragResult.score === 'number' && Math.abs(ragResult.score) <= threshold) {
|
||||
logger.info(`[RAG] Найден confident-ответ (score=${ragResult.score}), отправляем ответ из базы.`);
|
||||
// Прямой ответ из RAG
|
||||
const aiMessageResult = await db.getQuery()(
|
||||
@@ -471,6 +472,8 @@ router.post('/message', requireAuth, upload.array('attachments'), async (req, re
|
||||
[conversationId, userId, ragResult.answer]
|
||||
);
|
||||
aiMessage = aiMessageResult.rows[0];
|
||||
// Пушим новое сообщение через WebSocket
|
||||
broadcastChatMessage(aiMessage);
|
||||
} else if (ragResult) {
|
||||
logger.info(`[RAG] Нет confident-ответа (score=${ragResult.score}), переходим к генерации через LLM.`);
|
||||
// Генерация через LLM с подстановкой значений из RAG
|
||||
@@ -502,6 +505,8 @@ router.post('/message', requireAuth, upload.array('attachments'), async (req, re
|
||||
[conversationId, userId, llmResponse]
|
||||
);
|
||||
aiMessage = aiMessageResult.rows[0];
|
||||
// Пушим новое сообщение через WebSocket
|
||||
broadcastChatMessage(aiMessage);
|
||||
} else {
|
||||
logger.info(`[RAG] Нет ни одного результата, прошедшего порог (${threshold}).`);
|
||||
}
|
||||
|
||||
136
backend/routes/pages.js
Normal file
136
backend/routes/pages.js
Normal file
@@ -0,0 +1,136 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const db = require('../db');
|
||||
|
||||
const FIELDS_TO_EXCLUDE = ['image', 'tags'];
|
||||
|
||||
// Проверка и создание таблицы для пользователя-админа
|
||||
async function ensureUserPagesTable(userId, fields) {
|
||||
fields = fields.filter(f => !FIELDS_TO_EXCLUDE.includes(f));
|
||||
const tableName = `pages_user_${userId}`;
|
||||
// Проверяем, есть ли таблица
|
||||
const existsRes = await db.getQuery()(
|
||||
`SELECT to_regclass($1) as exists`, [tableName]
|
||||
);
|
||||
if (!existsRes.rows[0].exists) {
|
||||
// Формируем SQL для создания таблицы с нужными полями
|
||||
let columns = [
|
||||
'id SERIAL PRIMARY KEY',
|
||||
'created_at TIMESTAMP DEFAULT NOW()',
|
||||
'updated_at TIMESTAMP DEFAULT NOW()'
|
||||
];
|
||||
for (const field of fields) {
|
||||
columns.push(`"${field}" TEXT`);
|
||||
}
|
||||
const sql = `CREATE TABLE ${tableName} (${columns.join(', ')})`;
|
||||
await db.getQuery()(sql);
|
||||
} else {
|
||||
// Проверяем, есть ли все нужные столбцы, и добавляем недостающие
|
||||
const colRes = await db.getQuery()(
|
||||
`SELECT column_name FROM information_schema.columns WHERE table_name = $1`, [tableName]
|
||||
);
|
||||
const existingCols = colRes.rows.map(r => r.column_name);
|
||||
for (const field of fields) {
|
||||
if (!existingCols.includes(field)) {
|
||||
await db.getQuery()(
|
||||
`ALTER TABLE ${tableName} ADD COLUMN "${field}" TEXT`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
return tableName;
|
||||
}
|
||||
|
||||
// Создать страницу (только для админа)
|
||||
router.post('/', async (req, res) => {
|
||||
if (!req.user || !req.user.isAdmin) {
|
||||
return res.status(403).json({ error: 'Only admin can create pages' });
|
||||
}
|
||||
const userId = req.user.id;
|
||||
const fields = Object.keys(req.body).filter(f => !FIELDS_TO_EXCLUDE.includes(f));
|
||||
const filteredBody = {};
|
||||
fields.forEach(f => { filteredBody[f] = req.body[f]; });
|
||||
const tableName = await ensureUserPagesTable(userId, fields);
|
||||
|
||||
// Формируем SQL для вставки данных
|
||||
const colNames = fields.map(f => `"${f}"`).join(', ');
|
||||
const values = Object.values(filteredBody);
|
||||
const placeholders = values.map((_, i) => `$${i + 1}`).join(', ');
|
||||
const sql = `INSERT INTO ${tableName} (${colNames}) VALUES (${placeholders}) RETURNING *`;
|
||||
const { rows } = await db.getQuery()(sql, values);
|
||||
res.json(rows[0]);
|
||||
});
|
||||
|
||||
// Получить все страницы пользователя-админа
|
||||
router.get('/', async (req, res) => {
|
||||
if (!req.user || !req.user.isAdmin) {
|
||||
return res.status(403).json({ error: 'Only admin can view pages' });
|
||||
}
|
||||
const userId = req.user.id;
|
||||
const tableName = `pages_user_${userId}`;
|
||||
// Проверяем, есть ли таблица
|
||||
const existsRes = await db.getQuery()(
|
||||
`SELECT to_regclass($1) as exists`, [tableName]
|
||||
);
|
||||
if (!existsRes.rows[0].exists) return res.json([]);
|
||||
const { rows } = await db.getQuery()(`SELECT * FROM ${tableName} ORDER BY created_at DESC`);
|
||||
res.json(rows);
|
||||
});
|
||||
|
||||
// Получить одну страницу по id
|
||||
router.get('/:id', async (req, res) => {
|
||||
if (!req.user || !req.user.isAdmin) {
|
||||
return res.status(403).json({ error: 'Only admin can view pages' });
|
||||
}
|
||||
const userId = req.user.id;
|
||||
const tableName = `pages_user_${userId}`;
|
||||
const existsRes = await db.getQuery()(
|
||||
`SELECT to_regclass($1) as exists`, [tableName]
|
||||
);
|
||||
if (!existsRes.rows[0].exists) return res.status(404).json({ error: 'Page table not found' });
|
||||
const { rows } = await db.getQuery()(`SELECT * FROM ${tableName} WHERE id = $1`, [req.params.id]);
|
||||
if (!rows.length) return res.status(404).json({ error: 'Page not found' });
|
||||
res.json(rows[0]);
|
||||
});
|
||||
|
||||
// Редактировать страницу по id
|
||||
router.patch('/:id', async (req, res) => {
|
||||
if (!req.user || !req.user.isAdmin) {
|
||||
return res.status(403).json({ error: 'Only admin can edit pages' });
|
||||
}
|
||||
const userId = req.user.id;
|
||||
const tableName = `pages_user_${userId}`;
|
||||
const existsRes = await db.getQuery()(
|
||||
`SELECT to_regclass($1) as exists`, [tableName]
|
||||
);
|
||||
if (!existsRes.rows[0].exists) return res.status(404).json({ error: 'Page table not found' });
|
||||
const fields = Object.keys(req.body).filter(f => !FIELDS_TO_EXCLUDE.includes(f));
|
||||
if (!fields.length) return res.status(400).json({ error: 'No fields to update' });
|
||||
const filteredBody = {};
|
||||
fields.forEach(f => { filteredBody[f] = req.body[f]; });
|
||||
const setClause = fields.map((f, i) => `"${f}" = $${i + 1}`).join(', ');
|
||||
const values = Object.values(filteredBody);
|
||||
values.push(req.params.id);
|
||||
const sql = `UPDATE ${tableName} SET ${setClause}, updated_at = NOW() WHERE id = $${fields.length + 1} RETURNING *`;
|
||||
const { rows } = await db.getQuery()(sql, values);
|
||||
if (!rows.length) return res.status(404).json({ error: 'Page not found' });
|
||||
res.json(rows[0]);
|
||||
});
|
||||
|
||||
// Удалить страницу по id
|
||||
router.delete('/:id', async (req, res) => {
|
||||
if (!req.user || !req.user.isAdmin) {
|
||||
return res.status(403).json({ error: 'Only admin can delete pages' });
|
||||
}
|
||||
const userId = req.user.id;
|
||||
const tableName = `pages_user_${userId}`;
|
||||
const existsRes = await db.getQuery()(
|
||||
`SELECT to_regclass($1) as exists`, [tableName]
|
||||
);
|
||||
if (!existsRes.rows[0].exists) return res.status(404).json({ error: 'Page table not found' });
|
||||
const { rows } = await db.getQuery()(`DELETE FROM ${tableName} WHERE id = $1 RETURNING *`, [req.params.id]);
|
||||
if (!rows.length) return res.status(404).json({ error: 'Page not found' });
|
||||
res.json(rows[0]);
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -5,7 +5,7 @@ const { initWSS } = require('./wsHub');
|
||||
const logger = require('./utils/logger');
|
||||
const { getBot } = require('./services/telegramBot');
|
||||
const EmailBotService = require('./services/emailBot');
|
||||
const { initDbPool } = require('./db');
|
||||
const { initDbPool, seedAIAssistantSettings } = require('./db');
|
||||
|
||||
const PORT = process.env.PORT || 8000;
|
||||
|
||||
@@ -47,6 +47,7 @@ initWSS(server);
|
||||
|
||||
async function startServer() {
|
||||
await initDbPool(); // Дождаться пересоздания пула!
|
||||
await seedAIAssistantSettings(); // Инициализация ассистента после загрузки модели Ollama
|
||||
await initServices(); // Только теперь запускать сервисы
|
||||
console.log(`Server is running on port ${PORT}`);
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@ class EmailBotService {
|
||||
if (ragTableId) {
|
||||
// Сначала ищем ответ через RAG
|
||||
const ragResult = await ragAnswer({ tableId: ragTableId, userQuestion: text });
|
||||
if (ragResult && ragResult.answer) {
|
||||
if (ragResult && ragResult.answer && typeof ragResult.score === 'number' && Math.abs(ragResult.score) <= 0.3) {
|
||||
aiResponse = ragResult.answer;
|
||||
} else {
|
||||
aiResponse = await generateLLMResponse({
|
||||
|
||||
@@ -359,7 +359,7 @@ async function getBot() {
|
||||
if (ragTableId) {
|
||||
// Сначала ищем ответ через RAG
|
||||
const ragResult = await ragAnswer({ tableId: ragTableId, userQuestion: content });
|
||||
if (ragResult && ragResult.answer) {
|
||||
if (ragResult && ragResult.answer && typeof ragResult.score === 'number' && Math.abs(ragResult.score) <= 0.3) {
|
||||
aiResponse = ragResult.answer;
|
||||
} else {
|
||||
aiResponse = await generateLLMResponse({
|
||||
|
||||
@@ -27,4 +27,12 @@ function broadcastMessagesUpdate() {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { initWSS, broadcastContactsUpdate, broadcastMessagesUpdate };
|
||||
function broadcastChatMessage(message) {
|
||||
for (const ws of wsClients) {
|
||||
if (ws.readyState === WebSocket.OPEN) {
|
||||
ws.send(JSON.stringify({ type: 'chat-message', message }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { initWSS, broadcastContactsUpdate, broadcastMessagesUpdate, broadcastChatMessage };
|
||||
Reference in New Issue
Block a user