From e7a0aacb60990352d6efbf8129dedbc741ab6c71 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 24 Feb 2025 18:06:05 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D0=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/.gitignore | 5 +- backend/contracts/MyContract.sol | 5 + backend/migrations/init.sql | 10 +- backend/routes/api.js | 773 ++++++++++++++++-- .../MQ7gY14YfnCv8X0bQKUNjZjITzdTWz58.json | 2 +- backend/yarn.lock | 48 +- frontend/index.html | 11 + frontend/package.json | 11 +- frontend/src/App.vue | 162 +++- frontend/src/artifacts/MyContract.json | 146 ++++ frontend/src/components/AI/Chats.vue | 238 ++++++ frontend/src/components/AI/Users.vue | 281 +++++++ frontend/src/components/AI/VectorStore.vue | 254 ++++++ frontend/src/components/AIAssistant.vue | 240 ------ frontend/src/components/Contract/Deploy.vue | 162 ++++ frontend/src/components/Contract/Manage.vue | 771 +++++++++++++++++ .../src/components/ContractInteraction.vue | 685 ---------------- frontend/src/components/DataTables.vue | 163 ---- frontend/src/components/ServerControl.vue | 54 -- frontend/src/components/Sidebar/Sidebar.vue | 180 ++++ .../src/components/Sidebar/SidebarItem.vue | 73 ++ frontend/src/components/WalletConnection.vue | 243 ++++++ frontend/src/main.js | 2 + frontend/src/router/index.js | 63 ++ frontend/yarn.lock | 20 +- 25 files changed, 3299 insertions(+), 1303 deletions(-) create mode 100644 frontend/src/artifacts/MyContract.json create mode 100644 frontend/src/components/AI/Chats.vue create mode 100644 frontend/src/components/AI/Users.vue create mode 100644 frontend/src/components/AI/VectorStore.vue delete mode 100644 frontend/src/components/AIAssistant.vue create mode 100644 frontend/src/components/Contract/Deploy.vue create mode 100644 frontend/src/components/Contract/Manage.vue delete mode 100644 frontend/src/components/ContractInteraction.vue delete mode 100644 frontend/src/components/DataTables.vue delete mode 100644 frontend/src/components/ServerControl.vue create mode 100644 frontend/src/components/Sidebar/Sidebar.vue create mode 100644 frontend/src/components/Sidebar/SidebarItem.vue create mode 100644 frontend/src/components/WalletConnection.vue create mode 100644 frontend/src/router/index.js diff --git a/backend/.gitignore b/backend/.gitignore index a53cd4f..7132e9f 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -26,4 +26,7 @@ yarn-error.log* # Coverage directory used by tools like istanbul coverage/ -coverage.json \ No newline at end of file +coverage.json + +# Sessions directory +sessions/ \ No newline at end of file diff --git a/backend/contracts/MyContract.sol b/backend/contracts/MyContract.sol index e50c519..bfd5520 100644 --- a/backend/contracts/MyContract.sol +++ b/backend/contracts/MyContract.sol @@ -5,6 +5,11 @@ import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract MyContract is Ownable, ReentrancyGuard { + // Явно объявляем функцию owner + function owner() public view override returns (address) { + return super.owner(); + } + uint256 public price; event Purchase(address buyer, uint256 amount); diff --git a/backend/migrations/init.sql b/backend/migrations/init.sql index 9341fb4..850a29a 100644 --- a/backend/migrations/init.sql +++ b/backend/migrations/init.sql @@ -12,7 +12,7 @@ CREATE TABLE IF NOT EXISTS users ( DROP TABLE IF EXISTS documents; CREATE TABLE documents ( id bigserial PRIMARY KEY, - content text, + content text NOT NULL, metadata jsonb, embedding vector(4096) ); @@ -24,7 +24,8 @@ CREATE TABLE IF NOT EXISTS chat_history ( message TEXT NOT NULL, response TEXT NOT NULL, context_docs INTEGER[], - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP + created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, + is_approved BOOLEAN DEFAULT false ); -- Даем права пользователю @@ -37,4 +38,7 @@ UPDATE users SET address = LOWER(address); -- Удаляем дубликаты DELETE FROM users a USING users b WHERE a.id > b.id -AND LOWER(a.address) = LOWER(b.address); \ No newline at end of file +AND LOWER(a.address) = LOWER(b.address); + +ALTER TABLE chat_history +ADD COLUMN IF NOT EXISTS is_approved BOOLEAN DEFAULT false; \ No newline at end of file diff --git a/backend/routes/api.js b/backend/routes/api.js index 4a25f35..bd8e055 100644 --- a/backend/routes/api.js +++ b/backend/routes/api.js @@ -7,6 +7,9 @@ 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'); require('dotenv').config(); const pool = new Pool({ @@ -16,30 +19,117 @@ const pool = new Pool({ const chat = new ChatOllama({ model: 'mistral', - baseUrl: 'http://localhost:11434' + baseUrl: 'http://localhost:11434', + temperature: 0.7, + format: 'json' }); const embeddings = new OllamaEmbeddings({ model: 'mistral', - baseUrl: 'http://localhost:11434' + 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() { - vectorStore = await PGVectorStore.initialize( - embeddings, - { - postgresConnectionOptions: { - connectionString: process.env.DATABASE_URL + 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 + })) }, - tableName: 'documents' + 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) => { @@ -59,16 +149,38 @@ function requireAuth(req, res, next) { 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; - // ... верификация подписи ... - - // Сохраняем в сессию - req.session.authenticated = true; - req.session.siwe = message; - req.session.userAddress = message.address; + // Обновляем данные сессии + Object.assign(req.session, { + authenticated: true, + siwe: message, + userAddress: message.address, + cookie: { + maxAge: 7 * 24 * 60 * 60 * 1000 + } + }); // Ждем сохранения await new Promise((resolve) => { @@ -81,7 +193,20 @@ router.post('/verify', async (req, res) => { address: req.session.userAddress }); - res.json({ ok: true }); + // Проверяем права админа сразу после входа + 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 }); @@ -89,13 +214,13 @@ router.post('/verify', async (req, res) => { }); // Создаем шаблон промпта для RAG -const TEMPLATE = `Вы - ассистент в DApp приложении. -Используйте этот контекст для ответа на вопрос: -{context} +const TEMPLATE = `Вы - ассистент в DApp приложении. Используйте следующий контекст для ответа: -Вопрос: {question} +Контекст: {context} +Вопрос пользователя: {question} -Ответ должен быть полезным, точным и основанным на предоставленном контексте.`; +Отвечайте кратко и по существу, основываясь на предоставленном контексте. Если контекст пустой или не релевантный, +используйте свои базовые знания о DApp и блокчейне.`; const prompt = PromptTemplate.fromTemplate(TEMPLATE); @@ -103,106 +228,145 @@ const prompt = PromptTemplate.fromTemplate(TEMPLATE); const chain = RunnableSequence.from([ { context: async (input) => { - const results = await vectorStore.similaritySearch(input.question); - return results.map(doc => doc.content).join('\n\n'); + 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.question + 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 { - if (req.session.siwe.address.toLowerCase() !== req.body.userAddress.toLowerCase()) { - return res.status(401).json({ - error: 'Address mismatch' - }); - } - - if (!vectorStore) { - throw new Error('Vector store not initialized'); - } - const { message, userAddress } = req.body; - console.log('Session in chat:', req.session); - console.log('User address:', userAddress); + const { message } = req.body; + const userAddress = req.session.siwe.address; // Получаем или создаем пользователя - let user = await pool.query( - 'INSERT INTO users (address) VALUES (LOWER($1)) ON CONFLICT (address) DO UPDATE SET address = LOWER($1) RETURNING id', + let userResult = await pool.query( + 'SELECT id FROM users WHERE LOWER(address) = LOWER($1)', [userAddress] ); - const userId = user.rows[0].id; + + 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; - // Получаем релевантные документы для контекста - const results = await vectorStore.similaritySearch(message); - console.log('Found documents:', results); - const contextIds = results.map(doc => doc.id); - - const response = await chain.invoke({ + // Создаем входные данные для chain + const input = { + message: message, question: message - }); + }; - // Сохраняем историю - await pool.query( - 'INSERT INTO chat_history (user_id, message, response, context_docs) VALUES ($1, $2, $3, $4)', - [userId, message, response, contextIds] - ); + // Проверяем эмбеддинги перед использованием + 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; + } + } - res.json({ response: response }); + 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); - if (error.code === 'unsupported_country_region_territory') { - return res.status(503).json({ - error: 'Сервис временно недоступен в вашем регионе' - }); - } - res.status(500).json({ error: 'Ошибка сервера' }); + res.status(500).json({ + error: error.message, + details: error.stack + }); } }); // Получение истории чата router.get('/chat/history', requireAuth, async (req, res) => { try { - const userAddress = req.session.siwe.address; - console.log('Запрос истории чата для:', userAddress); + setCorsHeaders(res); - // Получаем ID пользователя - const userResult = await pool.query( - 'SELECT id FROM users WHERE LOWER(address) = LOWER($1) ORDER BY created_at ASC LIMIT 1', - [userAddress] - ); - console.log('Найден пользователь:', userResult.rows); - - if (userResult.rows.length === 0) { - return res.status(404).json({ error: 'User not found' }); - } - - const userId = userResult.rows[0].id; - - // Получаем историю чата - const history = await pool.query( - `SELECT - ch.id, - LOWER(u.address) as address, - ch.message, - ch.response, - ch.created_at + 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 ch.user_id = $1 - ORDER BY created_at DESC`, - [userId] + WHERE LOWER(u.address) = LOWER($1) + ORDER BY ch.created_at DESC`, + [userAddress] ); - console.log('История чата:', history.rows); - - res.json({ - history: history.rows - }); + + res.json({ history: result.rows }); } catch (error) { console.error('Ошибка получения истории:', error); - res.status(500).json({ error: 'Ошибка сервера' }); + res.status(500).json({ error: 'Server error' }); } }); @@ -221,4 +385,433 @@ router.get('/users', requireAuth, async (req, res) => { } }); +// Проверка на админа +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('Таблицы успешно инициализированы'); + } catch (error) { + console.error('Ошибка инициализации таблиц:', error); + } +} + +// Вызываем инициализацию при старте +initializeTables(); + module.exports = router; \ No newline at end of file diff --git a/backend/sessions/MQ7gY14YfnCv8X0bQKUNjZjITzdTWz58.json b/backend/sessions/MQ7gY14YfnCv8X0bQKUNjZjITzdTWz58.json index 2777518..be1bed4 100644 --- a/backend/sessions/MQ7gY14YfnCv8X0bQKUNjZjITzdTWz58.json +++ b/backend/sessions/MQ7gY14YfnCv8X0bQKUNjZjITzdTWz58.json @@ -1 +1 @@ -{"cookie":{"originalMaxAge":2591999999,"expires":"2025-03-23T16:06:19.831Z","secure":false,"httpOnly":true,"domain":"127.0.0.1","path":"/","sameSite":"lax"},"nonce":null,"__lastAccess":1740153979832,"siwe":{"domain":"127.0.0.1:5173","address":"0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B","statement":"Sign in with Ethereum to access DApp features and AI Assistant","uri":"http://127.0.0.1:5173","version":"1","nonce":"u7P3wT2kyPmGb4Z0I","issuedAt":"2025-02-21T16:05:49.561Z","chainId":11155111,"resources":["http://127.0.0.1:5173/api/chat","http://127.0.0.1:5173/api/contract"]},"authenticated":true} \ No newline at end of file +{"cookie":{"originalMaxAge":2591999999,"expires":"2025-03-24T09:38:02.826Z","secure":false,"httpOnly":true,"domain":"127.0.0.1","path":"/","sameSite":"lax"},"nonce":null,"__lastAccess":1740217082827,"siwe":{"domain":"127.0.0.1:5173","address":"0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B","statement":"Sign in with Ethereum to access DApp features and AI Assistant","uri":"http://127.0.0.1:5173","version":"1","nonce":"3ZIxF8sn0dDIbwWZz","issuedAt":"2025-02-22T09:37:59.804Z","chainId":11155111,"resources":["http://127.0.0.1:5173/api/chat","http://127.0.0.1:5173/api/contract"]},"authenticated":true} \ No newline at end of file diff --git a/backend/yarn.lock b/backend/yarn.lock index cebe1a7..4c12f1d 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -195,9 +195,9 @@ integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== "@langchain/community@^0.3.31": - version "0.3.31" - resolved "https://registry.yarnpkg.com/@langchain/community/-/community-0.3.31.tgz#6c7a52091a4f26fd044ce04b059dec083d1ab0d2" - integrity sha512-VsqZBQdJqJ8LeBjmVAAujpGQaSYjSXItb8uyWzoq/+fQkpq7UjWGnKWDkY65kBYPNDf6ShwjUdGiLm2yuyefzQ== + version "0.3.32" + resolved "https://registry.yarnpkg.com/@langchain/community/-/community-0.3.32.tgz#aa415ebfd51f10610f3abaa4127dae197f29cdc7" + integrity sha512-5AvGyjIFheXdBUSiIWNwc40rI8fXYiHV0UA3ncbBVu5fTwWur+mAQvl2ZsgyxBBKm4VuoCcuh6U6I7b1kiOYBQ== dependencies: "@langchain/openai" ">=0.2.0 <0.5.0" binary-extensions "^2.2.0" @@ -612,9 +612,9 @@ form-data "^4.0.0" "@types/node@*": - version "22.13.4" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.4.tgz#3fe454d77cd4a2d73c214008b3e331bfaaf5038a" - integrity sha512-ywP2X0DYtX3y08eFVx5fNIw7/uIv8hYUKgXoK8oayJlLnKcRfEYCxWMVE1XagUdVtCJlZT1AU4LXEABW+L1Peg== + version "22.13.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.13.5.tgz#23add1d71acddab2c6a4d31db89c0f98d330b511" + integrity sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg== dependencies: undici-types "~6.20.0" @@ -940,7 +940,7 @@ bytes@3.1.2: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== -call-bind-apply-helpers@^1.0.1: +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== @@ -1270,7 +1270,7 @@ es-errors@^1.3.0: resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== -es-object-atoms@^1.0.0: +es-object-atoms@^1.0.0, es-object-atoms@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== @@ -1584,22 +1584,22 @@ get-func-name@^2.0.0, get-func-name@^2.0.1, get-func-name@^2.0.2: integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== get-intrinsic@^1.2.5, get-intrinsic@^1.2.6: - version "1.2.7" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.7.tgz#dcfcb33d3272e15f445d15124bc0a216189b9044" - integrity sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA== + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== dependencies: - call-bind-apply-helpers "^1.0.1" + call-bind-apply-helpers "^1.0.2" es-define-property "^1.0.1" es-errors "^1.3.0" - es-object-atoms "^1.0.0" + es-object-atoms "^1.1.1" function-bind "^1.1.2" - get-proto "^1.0.0" + get-proto "^1.0.1" gopd "^1.2.0" has-symbols "^1.1.0" hasown "^2.0.2" math-intrinsics "^1.1.0" -get-proto@^1.0.0: +get-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== @@ -2210,9 +2210,9 @@ once@^1.3.0: wrappy "1" openai@^4.77.0, openai@^4.85.2: - version "4.85.2" - resolved "https://registry.yarnpkg.com/openai/-/openai-4.85.2.tgz#370702c56cd61c17c44c8c02c6a48229d5299903" - integrity sha512-ZQg3Q+K4A6M9dLFh5W36paZkZBQO+VbxMNJ1gUSyHsGiEWuXahdn02ermqNV68LhWQxdJQaWUFRAYpW/suTPWQ== + version "4.85.4" + resolved "https://registry.yarnpkg.com/openai/-/openai-4.85.4.tgz#fdf9d3228967b87221a112993501fa33a98f5d18" + integrity sha512-Nki51PBSu+Aryo7WKbdXvfm0X/iKkQS2fq3O0Uqb/O3b4exOZFid2te1BZ52bbO5UwxQZ5eeHJDCTqtrJLPw0w== dependencies: "@types/node" "^18.11.18" "@types/node-fetch" "^2.6.4" @@ -2800,9 +2800,9 @@ supports-color@^8.1.1: has-flag "^4.0.0" tinyglobby@^0.2.6: - version "0.2.11" - resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.11.tgz#9182cff655a0e272aad850d1a84c5e8e0f700426" - integrity sha512-32TmKeeKUahv0Go8WmQgiEp9Y21NuxjwjqiRC1nrUB51YacfSwuB44xgXD+HdIppmMRgjQNPdrHyA6vIybYZ+g== + version "0.2.12" + resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.12.tgz#ac941a42e0c5773bd0b5d08f32de82e74a1a61b5" + integrity sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww== dependencies: fdir "^6.4.3" picomatch "^4.0.2" @@ -3083,9 +3083,9 @@ yocto-queue@^0.1.0: integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== zod-to-json-schema@^3.22.3, zod-to-json-schema@^3.22.5, zod-to-json-schema@^3.24.1: - version "3.24.2" - resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.2.tgz#0e24e4a963ab34cf4211ef5227e342c0c6eddb79" - integrity sha512-pNUqrcSxuuB3/+jBbU8qKUbTbDqYUaG1vf5cXFjbhGgoUuA1amO/y4Q8lzfOhHU8HNPK6VFJ18lBDKj3OHyDsg== + version "3.24.3" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.3.tgz#5958ba111d681f8d01c5b6b647425c9b8a6059e7" + integrity sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A== zod@^3.22.3, zod@^3.22.4, zod@^3.24.1: version "3.24.2" diff --git a/frontend/index.html b/frontend/index.html index b6c12ba..c79cf37 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,8 +2,19 @@ + DApp for Business +
diff --git a/frontend/package.json b/frontend/package.json index b579992..13f52f5 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "frontend", - "version": "1.0.0", + "version": "0.0.0", "private": true, "type": "module", "scripts": { @@ -9,11 +9,12 @@ "preview": "vite preview" }, "dependencies": { - "ethers": "^6.13.5", - "vue": "^3.4.21" + "ethers": "^6.11.1", + "vue": "^3.4.15", + "vue-router": "^4.2.5" }, "devDependencies": { - "@vitejs/plugin-vue": "^5.0.4", - "vite": "^5.1.7" + "@vitejs/plugin-vue": "^5.0.3", + "vite": "^5.0.11" } } diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 7123ab3..982fd68 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,53 +1,149 @@ \ No newline at end of file diff --git a/frontend/src/artifacts/MyContract.json b/frontend/src/artifacts/MyContract.json new file mode 100644 index 0000000..1b9184e --- /dev/null +++ b/frontend/src/artifacts/MyContract.json @@ -0,0 +1,146 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "MyContract", + "sourceName": "contracts/MyContract.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "buyer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Purchase", + "type": "event" + }, + { + "inputs": [], + "name": "getPrice", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "price", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "purchase", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newPrice", + "type": "uint256" + } + ], + "name": "setPrice", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b5061002d61002261004760201b60201c565b61004f60201b60201c565b60018081905550662386f26fc10000600281905550610113565b600033905090565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b610a49806101226000396000f3fe60806040526004361061007b5760003560e01c806398d5fdca1161004e57806398d5fdca14610102578063a035b1fe1461012d578063efef39a114610158578063f2fde38b146101745761007b565b80633ccfd60b14610080578063715018a6146100975780638da5cb5b146100ae57806391b7f5ed146100d9575b600080fd5b34801561008c57600080fd5b5061009561019d565b005b3480156100a357600080fd5b506100ac61026b565b005b3480156100ba57600080fd5b506100c361027f565b6040516100d091906105da565b60405180910390f35b3480156100e557600080fd5b5061010060048036038101906100fb9190610630565b61028e565b005b34801561010e57600080fd5b506101176102a0565b604051610124919061066c565b60405180910390f35b34801561013957600080fd5b506101426102aa565b60405161014f919061066c565b60405180910390f35b610172600480360381019061016d9190610630565b6102b0565b005b34801561018057600080fd5b5061019b600480360381019061019691906106b3565b61034b565b005b6101a56103ce565b6101ad61044c565b60006101b761027f565b73ffffffffffffffffffffffffffffffffffffffff16476040516101da90610711565b60006040518083038185875af1925050503d8060008114610217576040519150601f19603f3d011682016040523d82523d6000602084013e61021c565b606091505b5050905080610260576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161025790610783565b60405180910390fd5b5061026961049b565b565b6102736103ce565b61027d60006104a4565b565b6000610289610568565b905090565b6102966103ce565b8060028190555050565b6000600254905090565b60025481565b6102b861044c565b806002546102c691906107d2565b3414610307576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102fe90610860565b60405180910390fd5b7f2499a5330ab0979cc612135e7883ebc3cd5c9f7a8508f042540c34723348f6323382604051610338929190610880565b60405180910390a161034861049b565b50565b6103536103ce565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036103c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103b99061091b565b60405180910390fd5b6103cb816104a4565b50565b6103d6610591565b73ffffffffffffffffffffffffffffffffffffffff166103f461027f565b73ffffffffffffffffffffffffffffffffffffffff161461044a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044190610987565b60405180910390fd5b565b600260015403610491576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610488906109f3565b60405180910390fd5b6002600181905550565b60018081905550565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006105c482610599565b9050919050565b6105d4816105b9565b82525050565b60006020820190506105ef60008301846105cb565b92915050565b600080fd5b6000819050919050565b61060d816105fa565b811461061857600080fd5b50565b60008135905061062a81610604565b92915050565b600060208284031215610646576106456105f5565b5b60006106548482850161061b565b91505092915050565b610666816105fa565b82525050565b6000602082019050610681600083018461065d565b92915050565b610690816105b9565b811461069b57600080fd5b50565b6000813590506106ad81610687565b92915050565b6000602082840312156106c9576106c86105f5565b5b60006106d78482850161069e565b91505092915050565b600081905092915050565b50565b60006106fb6000836106e0565b9150610706826106eb565b600082019050919050565b600061071c826106ee565b9150819050919050565b600082825260208201905092915050565b7f5472616e73666572206661696c65640000000000000000000000000000000000600082015250565b600061076d600f83610726565b915061077882610737565b602082019050919050565b6000602082019050818103600083015261079c81610760565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006107dd826105fa565b91506107e8836105fa565b92508282026107f6816105fa565b9150828204841483151761080d5761080c6107a3565b5b5092915050565b7f496e636f7272656374207061796d656e7420616d6f756e740000000000000000600082015250565b600061084a601883610726565b915061085582610814565b602082019050919050565b600060208201905081810360008301526108798161083d565b9050919050565b600060408201905061089560008301856105cb565b6108a2602083018461065d565b9392505050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000610905602683610726565b9150610910826108a9565b604082019050919050565b60006020820190508181036000830152610934816108f8565b9050919050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b6000610971602083610726565b915061097c8261093b565b602082019050919050565b600060208201905081810360008301526109a081610964565b9050919050565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00600082015250565b60006109dd601f83610726565b91506109e8826109a7565b602082019050919050565b60006020820190508181036000830152610a0c816109d0565b905091905056fea2646970667358221220a54935f39e1e67e3add3d5d8d075494bf021eef805964418d23e0b7ee9a2fc4b64736f6c63430008130033", + "deployedBytecode": "0x60806040526004361061007b5760003560e01c806398d5fdca1161004e57806398d5fdca14610102578063a035b1fe1461012d578063efef39a114610158578063f2fde38b146101745761007b565b80633ccfd60b14610080578063715018a6146100975780638da5cb5b146100ae57806391b7f5ed146100d9575b600080fd5b34801561008c57600080fd5b5061009561019d565b005b3480156100a357600080fd5b506100ac61026b565b005b3480156100ba57600080fd5b506100c361027f565b6040516100d091906105da565b60405180910390f35b3480156100e557600080fd5b5061010060048036038101906100fb9190610630565b61028e565b005b34801561010e57600080fd5b506101176102a0565b604051610124919061066c565b60405180910390f35b34801561013957600080fd5b506101426102aa565b60405161014f919061066c565b60405180910390f35b610172600480360381019061016d9190610630565b6102b0565b005b34801561018057600080fd5b5061019b600480360381019061019691906106b3565b61034b565b005b6101a56103ce565b6101ad61044c565b60006101b761027f565b73ffffffffffffffffffffffffffffffffffffffff16476040516101da90610711565b60006040518083038185875af1925050503d8060008114610217576040519150601f19603f3d011682016040523d82523d6000602084013e61021c565b606091505b5050905080610260576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161025790610783565b60405180910390fd5b5061026961049b565b565b6102736103ce565b61027d60006104a4565b565b6000610289610568565b905090565b6102966103ce565b8060028190555050565b6000600254905090565b60025481565b6102b861044c565b806002546102c691906107d2565b3414610307576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102fe90610860565b60405180910390fd5b7f2499a5330ab0979cc612135e7883ebc3cd5c9f7a8508f042540c34723348f6323382604051610338929190610880565b60405180910390a161034861049b565b50565b6103536103ce565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036103c2576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103b99061091b565b60405180910390fd5b6103cb816104a4565b50565b6103d6610591565b73ffffffffffffffffffffffffffffffffffffffff166103f461027f565b73ffffffffffffffffffffffffffffffffffffffff161461044a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161044190610987565b60405180910390fd5b565b600260015403610491576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610488906109f3565b60405180910390fd5b6002600181905550565b60018081905550565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050816000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508173ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35050565b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600033905090565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006105c482610599565b9050919050565b6105d4816105b9565b82525050565b60006020820190506105ef60008301846105cb565b92915050565b600080fd5b6000819050919050565b61060d816105fa565b811461061857600080fd5b50565b60008135905061062a81610604565b92915050565b600060208284031215610646576106456105f5565b5b60006106548482850161061b565b91505092915050565b610666816105fa565b82525050565b6000602082019050610681600083018461065d565b92915050565b610690816105b9565b811461069b57600080fd5b50565b6000813590506106ad81610687565b92915050565b6000602082840312156106c9576106c86105f5565b5b60006106d78482850161069e565b91505092915050565b600081905092915050565b50565b60006106fb6000836106e0565b9150610706826106eb565b600082019050919050565b600061071c826106ee565b9150819050919050565b600082825260208201905092915050565b7f5472616e73666572206661696c65640000000000000000000000000000000000600082015250565b600061076d600f83610726565b915061077882610737565b602082019050919050565b6000602082019050818103600083015261079c81610760565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006107dd826105fa565b91506107e8836105fa565b92508282026107f6816105fa565b9150828204841483151761080d5761080c6107a3565b5b5092915050565b7f496e636f7272656374207061796d656e7420616d6f756e740000000000000000600082015250565b600061084a601883610726565b915061085582610814565b602082019050919050565b600060208201905081810360008301526108798161083d565b9050919050565b600060408201905061089560008301856105cb565b6108a2602083018461065d565b9392505050565b7f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000610905602683610726565b9150610910826108a9565b604082019050919050565b60006020820190508181036000830152610934816108f8565b9050919050565b7f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572600082015250565b6000610971602083610726565b915061097c8261093b565b602082019050919050565b600060208201905081810360008301526109a081610964565b9050919050565b7f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00600082015250565b60006109dd601f83610726565b91506109e8826109a7565b602082019050919050565b60006020820190508181036000830152610a0c816109d0565b905091905056fea2646970667358221220a54935f39e1e67e3add3d5d8d075494bf021eef805964418d23e0b7ee9a2fc4b64736f6c63430008130033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/frontend/src/components/AI/Chats.vue b/frontend/src/components/AI/Chats.vue new file mode 100644 index 0000000..a04727a --- /dev/null +++ b/frontend/src/components/AI/Chats.vue @@ -0,0 +1,238 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/AI/Users.vue b/frontend/src/components/AI/Users.vue new file mode 100644 index 0000000..c2ac89b --- /dev/null +++ b/frontend/src/components/AI/Users.vue @@ -0,0 +1,281 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/AI/VectorStore.vue b/frontend/src/components/AI/VectorStore.vue new file mode 100644 index 0000000..d546343 --- /dev/null +++ b/frontend/src/components/AI/VectorStore.vue @@ -0,0 +1,254 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/AIAssistant.vue b/frontend/src/components/AIAssistant.vue deleted file mode 100644 index a98cd99..0000000 --- a/frontend/src/components/AIAssistant.vue +++ /dev/null @@ -1,240 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/Contract/Deploy.vue b/frontend/src/components/Contract/Deploy.vue new file mode 100644 index 0000000..83e41cc --- /dev/null +++ b/frontend/src/components/Contract/Deploy.vue @@ -0,0 +1,162 @@ + + + + + diff --git a/frontend/src/components/Contract/Manage.vue b/frontend/src/components/Contract/Manage.vue new file mode 100644 index 0000000..f5af633 --- /dev/null +++ b/frontend/src/components/Contract/Manage.vue @@ -0,0 +1,771 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/components/ContractInteraction.vue b/frontend/src/components/ContractInteraction.vue deleted file mode 100644 index 304b3a3..0000000 --- a/frontend/src/components/ContractInteraction.vue +++ /dev/null @@ -1,685 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/DataTables.vue b/frontend/src/components/DataTables.vue deleted file mode 100644 index f354fa7..0000000 --- a/frontend/src/components/DataTables.vue +++ /dev/null @@ -1,163 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/ServerControl.vue b/frontend/src/components/ServerControl.vue deleted file mode 100644 index 285b548..0000000 --- a/frontend/src/components/ServerControl.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/Sidebar/Sidebar.vue b/frontend/src/components/Sidebar/Sidebar.vue new file mode 100644 index 0000000..52e7758 --- /dev/null +++ b/frontend/src/components/Sidebar/Sidebar.vue @@ -0,0 +1,180 @@ + + + + + + diff --git a/frontend/src/components/Sidebar/SidebarItem.vue b/frontend/src/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..dbccfa3 --- /dev/null +++ b/frontend/src/components/Sidebar/SidebarItem.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/frontend/src/components/WalletConnection.vue b/frontend/src/components/WalletConnection.vue new file mode 100644 index 0000000..d1a75b9 --- /dev/null +++ b/frontend/src/components/WalletConnection.vue @@ -0,0 +1,243 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/main.js b/frontend/src/main.js index b3a4235..372d1b6 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,6 +1,8 @@ import { createApp } from 'vue' +import router from './router/index.js' import App from './App.vue' // Создаем и монтируем приложение Vue const app = createApp(App) +app.use(router) app.mount('#app') \ No newline at end of file diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js new file mode 100644 index 0000000..6dd6a41 --- /dev/null +++ b/frontend/src/router/index.js @@ -0,0 +1,63 @@ +import { createRouter, createWebHistory } from 'vue-router' +import Chats from '../components/AI/Chats.vue' +import Users from '../components/AI/Users.vue' +import VectorStore from '../components/AI/VectorStore.vue' +import Deploy from '../components/Contract/Deploy.vue' +import Manage from '../components/Contract/Manage.vue' + +// Защита маршрутов для админа +const requireAdmin = async (to, from, next) => { + try { + const response = await fetch('http://127.0.0.1:3000/api/admin/check', { + credentials: 'include' + }) + if (response.ok) { + const { isAdmin } = await response.json() + if (isAdmin) { + next() + return + } + } + next('/') + } catch (error) { + console.error('Ошибка проверки прав:', error) + next('/') + } +} + +const routes = [ + { + path: '/', + redirect: '/ai/chats' + }, + { + path: '/ai/chats', + name: 'chats', + component: Chats + }, + { + path: '/ai/users', + beforeEnter: requireAdmin, + component: Users + }, + { + path: '/ai/vectorstore', + beforeEnter: requireAdmin, + component: VectorStore + }, + { + path: '/contract/deploy', + beforeEnter: requireAdmin, + component: Deploy + }, + { + path: '/contract/manage', + beforeEnter: requireAdmin, + component: Manage + } +] + +export default createRouter({ + history: createWebHistory(), + routes +}) \ No newline at end of file diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 7ad2a7c..6d7ce05 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -271,7 +271,7 @@ dependencies: undici-types "~6.19.2" -"@vitejs/plugin-vue@^5.0.4": +"@vitejs/plugin-vue@^5.0.3": version "5.2.1" resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz#d1491f678ee3af899f7ae57d9c21dc52a65c7133" integrity sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ== @@ -318,6 +318,11 @@ "@vue/compiler-dom" "3.5.13" "@vue/shared" "3.5.13" +"@vue/devtools-api@^6.6.4": + version "6.6.4" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz#cbe97fe0162b365edc1dba80e173f90492535343" + integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g== + "@vue/reactivity@3.5.13": version "3.5.13" resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.13.tgz#b41ff2bb865e093899a22219f5b25f97b6fe155f" @@ -405,7 +410,7 @@ estree-walker@^2.0.2: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== -ethers@^6.13.5: +ethers@^6.11.1: version "6.13.5" resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.5.tgz#8c1d6ac988ac08abc3c1d8fabbd4b8b602851ac4" integrity sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ== @@ -492,7 +497,7 @@ undici-types@~6.19.2: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== -vite@^5.1.7: +vite@^5.0.11: version "5.4.14" resolved "https://registry.yarnpkg.com/vite/-/vite-5.4.14.tgz#ff8255edb02134df180dcfca1916c37a6abe8408" integrity sha512-EK5cY7Q1D8JNhSaPKVK4pwBFvaTmZxEnoKXLG/U9gmdDcihQGNzFlgIvaxezFR4glP1LsuiedwMBqCXH3wZccA== @@ -503,7 +508,14 @@ vite@^5.1.7: optionalDependencies: fsevents "~2.3.3" -vue@^3.4.21: +vue-router@^4.2.5: + version "4.5.0" + resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.5.0.tgz#58fc5fe374e10b6018f910328f756c3dae081f14" + integrity sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w== + dependencies: + "@vue/devtools-api" "^6.6.4" + +vue@^3.4.15: version "3.5.13" resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.13.tgz#9f760a1a982b09c0c04a867903fc339c9f29ec0a" integrity sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==