855 lines
27 KiB
JavaScript
855 lines
27 KiB
JavaScript
const express = require('express');
|
||
const router = express.Router();
|
||
const { ChatOllama } = require('@langchain/ollama');
|
||
const { PGVectorStore } = require('@langchain/community/vectorstores/pgvector');
|
||
const { OllamaEmbeddings } = require('@langchain/ollama');
|
||
const { RunnableSequence } = require('@langchain/core/runnables');
|
||
const { StringOutputParser } = require('@langchain/core/output_parsers');
|
||
const { PromptTemplate } = require('@langchain/core/prompts');
|
||
const { Pool } = require('pg');
|
||
const { ethers } = require('ethers');
|
||
const contractABI = require('../artifacts/contracts/MyContract.sol/MyContract.json').abi;
|
||
const crypto = require('crypto');
|
||
const TelegramBotService = require('../services/telegramBot');
|
||
require('dotenv').config();
|
||
|
||
const pool = new Pool({
|
||
connectionString: process.env.DATABASE_URL,
|
||
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false
|
||
});
|
||
|
||
const chat = new ChatOllama({
|
||
model: 'mistral',
|
||
baseUrl: 'http://localhost:11434',
|
||
temperature: 0.7,
|
||
format: 'json'
|
||
});
|
||
|
||
const embeddings = new OllamaEmbeddings({
|
||
model: 'mistral',
|
||
baseUrl: 'http://localhost:11434',
|
||
requestOptions: {
|
||
headers: {
|
||
'Content-Type': 'application/json'
|
||
}
|
||
},
|
||
dimensions: 4096,
|
||
stripNewLines: true,
|
||
maxConcurrency: 1,
|
||
maxRetries: 3,
|
||
timeout: 10000
|
||
});
|
||
|
||
let vectorStore;
|
||
let contract;
|
||
|
||
async function initVectorStore() {
|
||
try {
|
||
console.log('Начинаем инициализацию векторного хранилища...');
|
||
vectorStore = await PGVectorStore.initialize(
|
||
embeddings,
|
||
{
|
||
postgresConnectionOptions: {
|
||
connectionString: process.env.DATABASE_URL
|
||
},
|
||
tableName: 'documents',
|
||
columns: {
|
||
idColumnName: 'id',
|
||
vectorColumnName: 'embedding',
|
||
contentColumnName: 'content',
|
||
metadataColumnName: 'metadata',
|
||
}
|
||
}
|
||
);
|
||
console.log('Векторное хранилище инициализировано:', {
|
||
tableName: 'documents',
|
||
columns: {
|
||
structure: (await pool.query(`
|
||
SELECT
|
||
column_name,
|
||
data_type,
|
||
is_nullable,
|
||
column_default
|
||
FROM information_schema.columns
|
||
WHERE table_name = 'documents'
|
||
ORDER BY ordinal_position
|
||
`)).rows.map(row => ({
|
||
name: row.column_name,
|
||
type: row.data_type,
|
||
nullable: row.is_nullable,
|
||
default: row.column_default
|
||
}))
|
||
},
|
||
config: {
|
||
tableName: vectorStore.tableName,
|
||
columns: vectorStore.columns,
|
||
client: vectorStore.client ? 'Connected' : 'Not connected',
|
||
embeddings: vectorStore.embeddings ? 'Initialized' : 'Not initialized'
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.error('Ошибка инициализации векторного хранилища:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
async function initContract() {
|
||
try {
|
||
const provider = new ethers.JsonRpcProvider(process.env.ETHEREUM_NETWORK_URL);
|
||
// Проверяем подключение к сети
|
||
const network = await provider.getNetwork();
|
||
console.log('Подключены к сети:', network.chainId);
|
||
|
||
contract = new ethers.Contract(
|
||
process.env.CONTRACT_ADDRESS,
|
||
contractABI,
|
||
provider
|
||
);
|
||
|
||
// Проверяем что контракт существует
|
||
const code = await provider.getCode(process.env.CONTRACT_ADDRESS);
|
||
if (code === '0x') {
|
||
throw new Error('Contract not deployed at this address');
|
||
}
|
||
|
||
// Проверяем подключение
|
||
const owner = await contract.owner();
|
||
console.log('Владелец контракта:', owner);
|
||
console.log('Контракт инициализирован:', process.env.CONTRACT_ADDRESS);
|
||
} catch (error) {
|
||
console.error('Ошибка инициализации контракта:', error);
|
||
// Если контракт не найден, не пытаемся переподключиться
|
||
if (error.message.includes('not deployed')) {
|
||
console.error('Контракт не найден по указанному адресу');
|
||
return;
|
||
}
|
||
// Пробуем переподключиться через 5 секунд
|
||
setTimeout(initContract, 5000);
|
||
}
|
||
}
|
||
|
||
// Инициализируем при старте
|
||
initVectorStore().catch(console.error);
|
||
initContract().catch(console.error);
|
||
|
||
// Проверяем подключение к БД при старте
|
||
pool.connect((err, client, release) => {
|
||
if (err) {
|
||
console.error('Ошибка подключения к PostgreSQL:', err);
|
||
} else {
|
||
console.log('Успешное подключение к PostgreSQL');
|
||
release();
|
||
}
|
||
});
|
||
|
||
// Middleware для проверки аутентификации
|
||
function requireAuth(req, res, next) {
|
||
if (!req.session?.siwe?.address) {
|
||
return res.status(401).json({ error: 'Not authenticated' });
|
||
}
|
||
next();
|
||
}
|
||
|
||
// Генерация случайного nonce
|
||
function generateNonce() {
|
||
return crypto.randomBytes(16).toString('base64').replace(/[^a-zA-Z0-9]/g, '');
|
||
}
|
||
|
||
// Получение nonce для подписи
|
||
router.get('/nonce', (req, res) => {
|
||
try {
|
||
setCorsHeaders(res);
|
||
const nonce = generateNonce();
|
||
console.log('Сгенерирован новый nonce:', nonce);
|
||
res.json({ nonce });
|
||
} catch (error) {
|
||
console.error('Ошибка генерации nonce:', error);
|
||
res.status(500).json({ error: 'Server error' });
|
||
}
|
||
});
|
||
|
||
// Верификация подписи
|
||
router.post('/verify', async (req, res) => {
|
||
try {
|
||
const { message, signature } = req.body;
|
||
|
||
// Обновляем данные сессии
|
||
Object.assign(req.session, {
|
||
authenticated: true,
|
||
siwe: message,
|
||
userAddress: message.address,
|
||
cookie: {
|
||
maxAge: 7 * 24 * 60 * 60 * 1000
|
||
}
|
||
});
|
||
|
||
// Ждем сохранения
|
||
await new Promise((resolve) => {
|
||
req.session.save(resolve);
|
||
});
|
||
|
||
console.log('Session saved:', {
|
||
id: req.sessionID,
|
||
authenticated: req.session.authenticated,
|
||
address: req.session.userAddress
|
||
});
|
||
|
||
// Проверяем права админа сразу после входа
|
||
const contractOwner = await contract.owner();
|
||
const isAdmin = message.address.toLowerCase() === contractOwner.toLowerCase();
|
||
|
||
console.log('Проверка прав после входа:', {
|
||
userAddress: message.address,
|
||
contractOwner,
|
||
isAdmin
|
||
});
|
||
|
||
res.json({
|
||
ok: true,
|
||
isAdmin
|
||
});
|
||
} catch (error) {
|
||
console.error('Verify error:', error);
|
||
res.status(400).json({ error: error.message });
|
||
}
|
||
});
|
||
|
||
// Создаем шаблон промпта для RAG
|
||
const TEMPLATE = `Вы - ассистент в DApp приложении. Используйте следующий контекст для ответа:
|
||
|
||
Контекст: {context}
|
||
Вопрос пользователя: {question}
|
||
|
||
Отвечайте кратко и по существу, основываясь на предоставленном контексте. Если контекст пустой или не релевантный,
|
||
используйте свои базовые знания о DApp и блокчейне.`;
|
||
|
||
const prompt = PromptTemplate.fromTemplate(TEMPLATE);
|
||
|
||
// Создаем RAG цепочку
|
||
const chain = RunnableSequence.from([
|
||
{
|
||
context: async (input) => {
|
||
try {
|
||
const results = await vectorStore.similaritySearch(
|
||
input.question,
|
||
1,
|
||
{ type: 'approved_chat' }
|
||
);
|
||
if (!results.length) return '';
|
||
return results
|
||
.filter(doc => doc.pageContent)
|
||
.map(doc => doc.pageContent)
|
||
.join('\n\n');
|
||
} catch (error) {
|
||
console.error('Ошибка поиска контекста:', error);
|
||
return '';
|
||
}
|
||
},
|
||
question: (input) => input.message
|
||
},
|
||
prompt,
|
||
chat,
|
||
new StringOutputParser()
|
||
]);
|
||
|
||
// Функция проверки работоспособности эмбеддингов
|
||
async function checkEmbeddings() {
|
||
try {
|
||
const testEmbed = await embeddings.embedQuery('test');
|
||
console.log('Эмбеддинги работают, размерность:', testEmbed.length);
|
||
if (testEmbed.length !== 4096) {
|
||
throw new Error(`Неверная размерность: ${testEmbed.length}, ожидалось: 4096`);
|
||
}
|
||
return true;
|
||
} catch (error) {
|
||
console.error('Ошибка эмбеддингов:', error);
|
||
return false;
|
||
}
|
||
}
|
||
|
||
router.post('/chat', requireAuth, async (req, res) => {
|
||
try {
|
||
const { message } = req.body;
|
||
const userAddress = req.session.siwe.address;
|
||
|
||
// Получаем или создаем пользователя
|
||
let userResult = await pool.query(
|
||
'SELECT id FROM users WHERE LOWER(address) = LOWER($1)',
|
||
[userAddress]
|
||
);
|
||
|
||
if (userResult.rows.length === 0) {
|
||
userResult = await pool.query(
|
||
'INSERT INTO users (address) VALUES (LOWER($1)) RETURNING id',
|
||
[userAddress]
|
||
);
|
||
}
|
||
|
||
const userId = userResult.rows[0].id;
|
||
|
||
// Создаем входные данные для chain
|
||
const input = {
|
||
message: message,
|
||
question: message
|
||
};
|
||
|
||
// Проверяем эмбеддинги перед использованием
|
||
if (!await checkEmbeddings()) {
|
||
console.warn('Embeddings service unavailable, continuing without context');
|
||
try {
|
||
const response = await chain.invoke(input);
|
||
|
||
// Сохраняем в базу без контекста
|
||
await pool.query(
|
||
'INSERT INTO chat_history (user_id, message, response) VALUES ($1, $2, $3)',
|
||
[userId, message, response]
|
||
);
|
||
|
||
return res.json({ response });
|
||
} catch (error) {
|
||
console.error('Ошибка генерации ответа:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
const response = await chain.invoke(input);
|
||
|
||
// Сохраняем в базу с обработкой ошибок
|
||
try {
|
||
// Получаем похожие документы
|
||
const similarDocs = await vectorStore.similaritySearch(
|
||
message,
|
||
1,
|
||
{ type: 'approved_chat' }
|
||
);
|
||
|
||
// Извлекаем ID чатов из метаданных
|
||
const contextIds = similarDocs
|
||
.map(doc => doc.metadata?.chatId)
|
||
.filter(id => typeof id === 'number');
|
||
|
||
await pool.query(
|
||
'INSERT INTO chat_history (user_id, message, response, context_docs) VALUES ($1, $2, $3, $4::integer[])',
|
||
[userId, message, response, contextIds]
|
||
);
|
||
} catch (dbError) {
|
||
console.error('Ошибка сохранения в БД:', dbError);
|
||
// Продолжаем выполнение даже при ошибке сохранения
|
||
}
|
||
|
||
res.json({ response });
|
||
} catch (error) {
|
||
console.error('Ошибка чата:', error);
|
||
res.status(500).json({
|
||
error: error.message,
|
||
details: error.stack
|
||
});
|
||
}
|
||
});
|
||
|
||
// Получение истории чата
|
||
router.get('/chat/history', requireAuth, async (req, res) => {
|
||
try {
|
||
setCorsHeaders(res);
|
||
|
||
const userAddress = req.session.siwe.address;
|
||
|
||
// Получаем историю чата пользователя
|
||
const result = await pool.query(
|
||
`SELECT ch.*
|
||
FROM chat_history ch
|
||
JOIN users u ON ch.user_id = u.id
|
||
WHERE LOWER(u.address) = LOWER($1)
|
||
ORDER BY ch.created_at DESC`,
|
||
[userAddress]
|
||
);
|
||
|
||
res.json({ history: result.rows });
|
||
} catch (error) {
|
||
console.error('Ошибка получения истории:', error);
|
||
res.status(500).json({ error: 'Server error' });
|
||
}
|
||
});
|
||
|
||
// Получение списка пользователей
|
||
router.get('/users', requireAuth, async (req, res) => {
|
||
try {
|
||
console.log('Запрос списка пользователей');
|
||
const users = await pool.query(
|
||
'SELECT id, LOWER(address) as address, created_at FROM users ORDER BY created_at DESC'
|
||
);
|
||
console.log('Найдено пользователей:', users.rows);
|
||
res.json({ users: users.rows });
|
||
} catch (error) {
|
||
console.error('Ошибка получения пользователей:', error);
|
||
res.status(500).json({ error: 'Ошибка сервера' });
|
||
}
|
||
});
|
||
|
||
// Проверка на админа
|
||
router.get('/admin/check', requireAuth, async (req, res) => {
|
||
try {
|
||
if (!contract) {
|
||
await initContract();
|
||
if (!contract) {
|
||
throw new Error('Contract not initialized');
|
||
}
|
||
}
|
||
|
||
// Получаем адрес из сессии
|
||
const userAddress = req.session.siwe.address;
|
||
console.log('Проверка админа, адрес из сессии:', userAddress);
|
||
|
||
const contractOwner = await contract.owner();
|
||
console.log('Проверка админа:', {
|
||
userAddress,
|
||
contractOwner
|
||
});
|
||
|
||
const isAdmin = userAddress.toLowerCase() === contractOwner.toLowerCase();
|
||
console.log('Результат проверки админа:', isAdmin);
|
||
|
||
res.json({ isAdmin });
|
||
} catch (error) {
|
||
console.error('Ошибка проверки админа:', error);
|
||
res.status(500).json({
|
||
error: 'Server error',
|
||
details: error.message,
|
||
code: error.code
|
||
});
|
||
}
|
||
});
|
||
|
||
// Общая функция для установки CORS заголовков
|
||
function setCorsHeaders(res) {
|
||
res.header('Access-Control-Allow-Origin', 'http://127.0.0.1:5173');
|
||
res.header('Access-Control-Allow-Credentials', 'true');
|
||
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
|
||
res.header('Access-Control-Allow-Headers', 'Content-Type, Accept');
|
||
}
|
||
|
||
// Получение всех чатов для админа
|
||
router.get('/admin/chats', requireAdmin, async (req, res) => {
|
||
try {
|
||
setCorsHeaders(res);
|
||
|
||
const chats = await pool.query(`
|
||
SELECT
|
||
ch.id,
|
||
LOWER(u.address) as address,
|
||
ch.message,
|
||
ch.response,
|
||
ch.created_at,
|
||
ch.context_docs,
|
||
EXISTS (
|
||
SELECT 1 FROM documents d
|
||
WHERE d.metadata->>'chatId' = ch.id::text
|
||
AND d.metadata->>'type' = 'approved_chat'
|
||
) as is_approved
|
||
FROM chat_history ch
|
||
JOIN users u ON ch.user_id = u.id
|
||
ORDER BY ch.created_at DESC
|
||
`);
|
||
|
||
console.log('Получено чатов:', chats.rows.length);
|
||
if (chats.rows.length > 0) {
|
||
console.log('Пример чата:', {
|
||
id: chats.rows[0].id,
|
||
address: chats.rows[0].address,
|
||
is_approved: chats.rows[0].is_approved
|
||
});
|
||
}
|
||
|
||
res.json({ chats: chats.rows });
|
||
} catch (error) {
|
||
console.error('Ошибка получения чатов:', error);
|
||
res.status(500).json({
|
||
error: 'Server error',
|
||
details: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
// Одобрение чата для обучения
|
||
router.post('/admin/approve', requireAuth, async (req, res) => {
|
||
try {
|
||
const userAddress = req.session.siwe.address;
|
||
const contractOwner = await contract.owner();
|
||
|
||
if (userAddress.toLowerCase() !== contractOwner.toLowerCase()) {
|
||
return res.status(403).json({ error: 'Not authorized' });
|
||
}
|
||
|
||
const { chatId } = req.body;
|
||
|
||
// Обновляем статус в базе
|
||
await pool.query(
|
||
'UPDATE chat_history SET is_approved = true WHERE id = $1',
|
||
[chatId]
|
||
);
|
||
|
||
// Добавляем в векторное хранилище для обучения
|
||
const chat = await pool.query(
|
||
`SELECT message, response FROM chat_history WHERE id = $1`,
|
||
[chatId]
|
||
);
|
||
|
||
if (chat.rows.length > 0) {
|
||
const { message, response } = chat.rows[0];
|
||
console.log('Добавляем в векторное хранилище:', {
|
||
message: message.substring(0, 50) + '...',
|
||
response: response.substring(0, 50) + '...',
|
||
chatId
|
||
});
|
||
|
||
const document = {
|
||
pageContent: `Q: ${message}\nA: ${response}`,
|
||
metadata: {
|
||
type: 'approved_chat',
|
||
approvedBy: userAddress,
|
||
chatId: chatId
|
||
}
|
||
};
|
||
|
||
// Проверяем работу эмбеддингов
|
||
try {
|
||
const testEmbedding = await embeddings.embedQuery('test');
|
||
console.log('Эмбеддинги работают, размерность:', testEmbedding.length);
|
||
} catch (error) {
|
||
console.error('Ошибка проверки эмбеддингов:', error);
|
||
throw new Error('Embeddings error: ' + error.message);
|
||
}
|
||
|
||
console.log('Документ для добавления:', {
|
||
pageContent: document.pageContent.substring(0, 100) + '...',
|
||
metadata: document.metadata,
|
||
vectorStore: {
|
||
tableName: vectorStore.tableName,
|
||
columns: vectorStore.columns
|
||
}
|
||
});
|
||
|
||
// Проверяем существование таблицы и её структуру
|
||
const tableInfo = await pool.query(`
|
||
SELECT EXISTS (
|
||
SELECT FROM information_schema.tables
|
||
WHERE table_name = 'documents'
|
||
);
|
||
`);
|
||
console.log('Таблица documents существует:', tableInfo.rows[0].exists);
|
||
|
||
if (tableInfo.rows[0].exists) {
|
||
const columns = await pool.query(`
|
||
SELECT column_name, data_type
|
||
FROM information_schema.columns
|
||
WHERE table_name = 'documents'
|
||
ORDER BY ordinal_position;
|
||
`);
|
||
console.log('Структура таблицы documents:',
|
||
columns.rows.map(row => `${row.column_name} (${row.data_type})`)
|
||
);
|
||
}
|
||
|
||
await vectorStore.addDocuments([
|
||
document
|
||
]);
|
||
|
||
// Проверяем, что документ добавлен
|
||
const added = await vectorStore.similaritySearch(
|
||
document.pageContent,
|
||
1,
|
||
{ chatId: chatId }
|
||
);
|
||
console.log('Проверка добавления документа:', {
|
||
found: added.length > 0,
|
||
document: added[0]?.pageContent.substring(0, 100) + '...'
|
||
});
|
||
|
||
console.log('Успешно добавлено в векторное хранилище');
|
||
}
|
||
|
||
res.json({ success: true });
|
||
} catch (error) {
|
||
console.error('Ошибка одобрения:', error);
|
||
res.status(500).json({
|
||
error: 'Server error',
|
||
details: error.message,
|
||
code: error.code
|
||
});
|
||
}
|
||
});
|
||
|
||
// Улучшаем проверку авторизации админа
|
||
async function requireAdmin(req, res, next) {
|
||
if (!req.session?.siwe?.address) {
|
||
return res.status(401).json({
|
||
error: 'Not authenticated',
|
||
details: 'Please sign in first'
|
||
});
|
||
}
|
||
|
||
try {
|
||
// Получаем адреса
|
||
const userAddress = req.session.siwe.address;
|
||
const contractOwner = await contract.owner();
|
||
|
||
console.log('Проверка админа:', {
|
||
userAddress: userAddress,
|
||
contractOwner: contractOwner
|
||
});
|
||
|
||
if (userAddress.toLowerCase() !== contractOwner.toLowerCase()) {
|
||
return res.status(403).json({
|
||
error: 'Not authorized',
|
||
details: 'Only contract owner can access this endpoint'
|
||
});
|
||
}
|
||
|
||
next();
|
||
} catch (error) {
|
||
console.error('Ошибка проверки админа:', error);
|
||
return res.status(500).json({
|
||
error: 'Server error',
|
||
details: error.message
|
||
});
|
||
}
|
||
}
|
||
|
||
// Получение векторного хранилища для админа
|
||
router.get('/admin/vectors', requireAdmin, async (req, res) => {
|
||
try {
|
||
setCorsHeaders(res);
|
||
|
||
// Добавляем колонку created_at если её нет
|
||
await pool.query(`
|
||
ALTER TABLE documents
|
||
ADD COLUMN IF NOT EXISTS created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||
`);
|
||
console.log('Проверена/добавлена колонка created_at');
|
||
|
||
// Проверяем структуру таблицы
|
||
const tableInfo = await pool.query(`
|
||
SELECT column_name, data_type
|
||
FROM information_schema.columns
|
||
WHERE table_name = 'documents'
|
||
`);
|
||
console.log('Структура таблицы documents:', tableInfo.rows);
|
||
|
||
// Получаем все документы из векторного хранилища
|
||
const documents = await pool.query(`
|
||
SELECT
|
||
d.id,
|
||
d.content,
|
||
d.metadata,
|
||
length(d.embedding::text) as embedding_size,
|
||
COALESCE(d.created_at, CURRENT_TIMESTAMP) as created_at,
|
||
CASE
|
||
WHEN d.metadata->>'type' = 'approved_chat' THEN true
|
||
ELSE false
|
||
END as is_approved
|
||
FROM documents d
|
||
ORDER BY d.created_at DESC NULLS LAST
|
||
`);
|
||
|
||
// Форматируем ответ
|
||
const vectors = documents.rows.map(doc => ({
|
||
id: doc.id,
|
||
content: doc.content,
|
||
metadata: doc.metadata,
|
||
embedding_size: doc.embedding ? 4096 : 0, // Фиксированный размер для mistral
|
||
created: doc.created_at,
|
||
is_approved: doc.is_approved
|
||
}));
|
||
|
||
console.log('Получено векторов:', vectors.length);
|
||
console.log('Пример вектора:', vectors[0]);
|
||
res.json({ vectors });
|
||
} catch (error) {
|
||
console.error('Ошибка получения векторов:', error);
|
||
console.error('Детали ошибки:', {
|
||
code: error.code,
|
||
detail: error.detail,
|
||
hint: error.hint,
|
||
position: error.position
|
||
});
|
||
res.status(500).json({
|
||
error: 'Server error',
|
||
details: error.message,
|
||
code: error.code
|
||
});
|
||
}
|
||
});
|
||
|
||
// Обработка CORS preflight запросов для админских роутов
|
||
router.options('/admin/*', (req, res) => {
|
||
setCorsHeaders(res);
|
||
res.sendStatus(200);
|
||
});
|
||
|
||
// Очистка кэша и данных
|
||
router.post('/admin/clear-cache', requireAdmin, async (req, res) => {
|
||
try {
|
||
setCorsHeaders(res);
|
||
|
||
// Очищаем таблицы
|
||
await pool.query('TRUNCATE TABLE documents CASCADE');
|
||
await pool.query('TRUNCATE TABLE chat_history CASCADE');
|
||
await pool.query('TRUNCATE TABLE users CASCADE');
|
||
|
||
// Сбрасываем автоинкремент
|
||
await pool.query('ALTER SEQUENCE documents_id_seq RESTART WITH 1');
|
||
await pool.query('ALTER SEQUENCE chat_history_id_seq RESTART WITH 1');
|
||
await pool.query('ALTER SEQUENCE users_id_seq RESTART WITH 1');
|
||
|
||
// Реинициализируем векторное хранилище
|
||
await initVectorStore();
|
||
|
||
console.log('Кэш и данные очищены');
|
||
res.json({ success: true });
|
||
} catch (error) {
|
||
console.error('Ошибка очистки кэша:', error);
|
||
res.status(500).json({
|
||
error: 'Server error',
|
||
details: error.message
|
||
});
|
||
}
|
||
});
|
||
|
||
// Выход из системы
|
||
router.post('/signout', requireAuth, async (req, res) => {
|
||
try {
|
||
setCorsHeaders(res);
|
||
|
||
// Уничтожаем сессию
|
||
req.session.destroy((err) => {
|
||
if (err) {
|
||
console.error('Ошибка при удалении сессии:', err);
|
||
return res.status(500).json({ error: 'Failed to destroy session' });
|
||
}
|
||
|
||
console.log('Сессия успешно завершена');
|
||
res.json({ success: true });
|
||
});
|
||
} catch (error) {
|
||
console.error('Ошибка выхода:', error);
|
||
res.status(500).json({ error: 'Server error' });
|
||
}
|
||
});
|
||
|
||
// Проверка сессии
|
||
router.get('/session', (req, res) => {
|
||
try {
|
||
setCorsHeaders(res);
|
||
|
||
if (req.session?.authenticated && req.session?.siwe?.address) {
|
||
res.json({
|
||
authenticated: true,
|
||
address: req.session.siwe.address
|
||
});
|
||
} else {
|
||
res.json({
|
||
authenticated: false
|
||
});
|
||
}
|
||
} catch (error) {
|
||
console.error('Ошибка проверки сессии:', error);
|
||
res.status(500).json({ error: 'Server error' });
|
||
}
|
||
});
|
||
|
||
// Создание нового пользователя
|
||
router.post('/users', async (req, res) => {
|
||
try {
|
||
setCorsHeaders(res);
|
||
|
||
const { address } = req.body;
|
||
|
||
// Проверяем существование пользователя
|
||
const existingUser = await pool.query(
|
||
'SELECT * FROM users WHERE address = $1',
|
||
[address.toLowerCase()]
|
||
);
|
||
|
||
if (existingUser.rows.length > 0) {
|
||
return res.json({ user: existingUser.rows[0] });
|
||
}
|
||
|
||
// Создаем нового пользователя
|
||
const result = await pool.query(
|
||
'INSERT INTO users (address) VALUES ($1) RETURNING *',
|
||
[address.toLowerCase()]
|
||
);
|
||
|
||
res.json({ user: result.rows[0] });
|
||
} catch (error) {
|
||
console.error('Ошибка создания пользователя:', error);
|
||
res.status(500).json({ error: 'Server error' });
|
||
}
|
||
});
|
||
|
||
// Создание необходимых таблиц при старте
|
||
async function initializeTables() {
|
||
try {
|
||
await pool.query(`
|
||
CREATE TABLE IF NOT EXISTS users (
|
||
id SERIAL PRIMARY KEY,
|
||
address VARCHAR(42) NOT NULL UNIQUE,
|
||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
|
||
CREATE TABLE IF NOT EXISTS chat_history (
|
||
id SERIAL PRIMARY KEY,
|
||
user_id INTEGER REFERENCES users(id),
|
||
message TEXT,
|
||
response TEXT,
|
||
is_user BOOLEAN DEFAULT true,
|
||
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP
|
||
);
|
||
`);
|
||
console.log('Таблицы успешно инициализированы');
|
||
|
||
// Инициализируем vectorStore
|
||
vectorStore = await PGVectorStore.initialize(
|
||
embeddings,
|
||
{
|
||
postgresConnectionOptions: {
|
||
connectionString: process.env.DATABASE_URL
|
||
},
|
||
tableName: 'documents',
|
||
columns: {
|
||
idColumnName: 'id',
|
||
vectorColumnName: 'embedding',
|
||
contentColumnName: 'content',
|
||
metadataColumnName: 'metadata'
|
||
}
|
||
}
|
||
);
|
||
|
||
console.log('Векторное хранилище инициализировано:', {
|
||
tableName: 'documents',
|
||
columns: vectorStore.columns,
|
||
config: {
|
||
tableName: vectorStore.tableName,
|
||
columns: vectorStore.columns,
|
||
client: vectorStore.client ? 'Connected' : 'Not Connected',
|
||
embeddings: vectorStore.embeddings ? 'Initialized' : 'Not Initialized'
|
||
}
|
||
});
|
||
|
||
// Создаем экземпляр TelegramBotService только после инициализации vectorStore
|
||
if (vectorStore) {
|
||
const telegramBot = new TelegramBotService(
|
||
process.env.TELEGRAM_BOT_TOKEN,
|
||
vectorStore
|
||
);
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('Ошибка при инициализации:', error);
|
||
}
|
||
}
|
||
|
||
// Вызываем инициализацию при старте
|
||
initializeTables();
|
||
|
||
module.exports = router;
|