From 3157ad0cd95a2af78a2a4972a085e9d7e493ee2e Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 5 Mar 2025 01:02:09 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A2=D0=B5=D1=81=D1=82=D0=BE=D0=B2=D1=8B?= =?UTF-8?q?=D0=B9=20=D0=BA=D0=BE=D0=BC=D0=BC=D0=B8=D1=82=20=D0=BF=D0=BE?= =?UTF-8?q?=D1=81=D0=BB=D0=B5=20=D1=83=D0=B4=D0=B0=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20husky?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .cursor/settings.json | 16 + backend/.prettierrc | 7 + backend/app.js | 251 +- backend/data/documents/example.md | 16 - backend/db.js | 69 +- backend/docs/api.md | 9 + backend/eslint.config.js | 30 + backend/hardhat.config.js | 12 +- backend/middleware/auth.js | 138 +- backend/middleware/logger.js | 12 + backend/middleware/session.js | 22 + .../migrations/004_personalization_tables.sql | 44 - backend/migrations/005_kanban_tables.sql | 64 - backend/migrations/006_role_management.sql | 26 + .../007_user_identities_conversations.sql | 40 + backend/migrations/008_chat_history.sql | 14 + backend/nodemon.json | 17 +- backend/package.json | 26 +- backend/routes/access.js | 207 +- backend/routes/admin.js | 123 + backend/routes/auth.js | 615 +++-- backend/routes/chat.js | 293 +-- backend/routes/contracts.js | 18 +- backend/routes/conversations.js | 0 backend/routes/debug.js | 202 +- backend/routes/health.js | 14 +- backend/routes/identities.js | 36 +- backend/routes/kanban.js | 340 --- backend/routes/messages.js | 246 ++ backend/routes/roles.js | 56 + backend/routes/users.js | 34 +- backend/scripts/check-dependencies.js | 11 +- .../scripts/check-ethers-v6-compatibility.js | 125 + backend/scripts/check-ollama-models.js | 34 + backend/scripts/check-state.js | 21 +- backend/scripts/check-tokens.js | 21 + backend/scripts/create-moderator.js | 35 +- backend/scripts/deploy-access.js | 10 +- backend/scripts/deploy.js | 12 +- backend/scripts/init-db.js | 23 +- backend/scripts/init-roles.js | 26 +- backend/scripts/manage-access.js | 72 - backend/scripts/revoke-all.js | 8 +- backend/scripts/run-migrations.js | 34 +- backend/scripts/update-user-roles.js | 53 + backend/server.js | 438 ++-- backend/services/ai-assistant.js | 158 ++ backend/services/documents.js | 47 - backend/services/emailBot.js | 288 ++- backend/services/index.js | 32 + backend/services/ollama.js | 89 +- backend/services/telegram-service.js | 262 +++ backend/services/telegramBot.js | 402 ---- backend/services/vectorStore.js | 306 ++- backend/test/AccessToken.test.js | 30 +- backend/utils/access-check.js | 159 +- backend/utils/auth.js | 136 +- backend/utils/checkMail.js | 2 +- backend/utils/contracts.js | 46 + backend/utils/db.js | 2 + backend/utils/helpers.js | 6 +- backend/utils/identity-linker.js | 18 +- backend/utils/logger.js | 20 +- backend/utils/wallet.js | 4 + backend/yarn.lock | 766 +++++- frontend/.prettierrc | 10 + frontend/eslint.config.js | 64 + frontend/package-lock.json | 2064 ----------------- frontend/package.json | 21 +- frontend/src/App.vue | 413 ++-- frontend/src/api/auth.js | 4 + frontend/src/components/AccessControl.vue | 163 +- frontend/src/components/AccessTest.vue | 194 -- .../src/components/AccessTokenManager.vue | 288 +-- frontend/src/components/Chats.vue | 298 --- frontend/src/components/LinkedAccounts.vue | 185 +- frontend/src/components/Modal.vue | 44 +- frontend/src/components/Navigation.vue | 133 ++ frontend/src/components/RoleManager.vue | 161 ++ frontend/src/components/WalletConnection.vue | 113 +- .../src/components/chat/ConversationList.vue | 225 ++ frontend/src/components/chat/MessageInput.vue | 134 ++ .../src/components/chat/MessageThread.vue | 194 ++ .../src/components/identity/EmailConnect.vue | 0 .../components/identity/IdentityManager.vue | 0 .../components/identity/TelegramConnect.vue | 0 .../src/components/kanban/AddBoardForm.vue | 128 - .../src/components/kanban/AddCardForm.vue | 121 - .../src/components/kanban/AddColumnForm.vue | 115 - .../src/components/kanban/KanbanBoard.vue | 529 ----- frontend/src/components/kanban/KanbanCard.vue | 190 -- frontend/src/components/useEthereum.js | 151 -- frontend/src/composables/useEthereum.js | 23 + frontend/src/locales/en.json | 0 frontend/src/locales/ru.json | 0 frontend/src/main.js | 37 +- frontend/src/mocks/api.js | 69 - frontend/src/mocks/authApi.js | 68 - frontend/src/mocks/chatApi.js | 40 - frontend/src/mocks/contractApi.js | 46 - frontend/src/mocks/kanbanApi.js | 96 - frontend/src/router/index.js | 92 +- frontend/src/services/wallet.js | 106 - frontend/src/stores/auth.js | 129 +- frontend/src/utils/wallet.js | 127 + frontend/src/views/AccessTestPage.vue | 32 - frontend/src/views/AccessTestView.vue | 27 + frontend/src/views/AdminView.vue | 279 +++ frontend/src/views/ChatView.vue | 282 +++ frontend/src/views/ConversationsView.vue | 77 + frontend/src/views/DashboardView.vue | 25 +- frontend/src/views/HomeView.vue | 227 +- frontend/src/views/IdentityView.vue | 0 frontend/src/views/KanbanBoardView.vue | 451 ---- frontend/src/views/KanbanView.vue | 343 --- frontend/src/views/ProfileView.vue | 130 +- frontend/vite.config.js | 47 +- frontend/yarn.lock | 1319 +++++++++-- 118 files changed, 8177 insertions(+), 8530 deletions(-) create mode 100644 .cursor/settings.json create mode 100644 backend/.prettierrc delete mode 100644 backend/data/documents/example.md create mode 100644 backend/docs/api.md create mode 100644 backend/eslint.config.js create mode 100644 backend/middleware/logger.js create mode 100644 backend/middleware/session.js delete mode 100644 backend/migrations/004_personalization_tables.sql delete mode 100644 backend/migrations/005_kanban_tables.sql create mode 100644 backend/migrations/006_role_management.sql create mode 100644 backend/migrations/007_user_identities_conversations.sql create mode 100644 backend/migrations/008_chat_history.sql create mode 100644 backend/routes/admin.js create mode 100644 backend/routes/conversations.js delete mode 100644 backend/routes/kanban.js create mode 100644 backend/routes/messages.js create mode 100644 backend/routes/roles.js create mode 100644 backend/scripts/check-ethers-v6-compatibility.js create mode 100644 backend/scripts/check-ollama-models.js create mode 100644 backend/scripts/check-tokens.js delete mode 100644 backend/scripts/manage-access.js create mode 100644 backend/scripts/update-user-roles.js create mode 100644 backend/services/ai-assistant.js delete mode 100644 backend/services/documents.js create mode 100644 backend/services/index.js create mode 100644 backend/services/telegram-service.js delete mode 100644 backend/services/telegramBot.js create mode 100644 backend/utils/contracts.js create mode 100644 backend/utils/db.js create mode 100644 backend/utils/wallet.js create mode 100644 frontend/.prettierrc create mode 100644 frontend/eslint.config.js delete mode 100644 frontend/package-lock.json create mode 100644 frontend/src/api/auth.js delete mode 100644 frontend/src/components/AccessTest.vue delete mode 100644 frontend/src/components/Chats.vue create mode 100644 frontend/src/components/Navigation.vue create mode 100644 frontend/src/components/RoleManager.vue create mode 100644 frontend/src/components/chat/ConversationList.vue create mode 100644 frontend/src/components/chat/MessageInput.vue create mode 100644 frontend/src/components/chat/MessageThread.vue create mode 100644 frontend/src/components/identity/EmailConnect.vue create mode 100644 frontend/src/components/identity/IdentityManager.vue create mode 100644 frontend/src/components/identity/TelegramConnect.vue delete mode 100644 frontend/src/components/kanban/AddBoardForm.vue delete mode 100644 frontend/src/components/kanban/AddCardForm.vue delete mode 100644 frontend/src/components/kanban/AddColumnForm.vue delete mode 100644 frontend/src/components/kanban/KanbanBoard.vue delete mode 100644 frontend/src/components/kanban/KanbanCard.vue delete mode 100644 frontend/src/components/useEthereum.js create mode 100644 frontend/src/composables/useEthereum.js create mode 100644 frontend/src/locales/en.json create mode 100644 frontend/src/locales/ru.json delete mode 100644 frontend/src/mocks/api.js delete mode 100644 frontend/src/mocks/authApi.js delete mode 100644 frontend/src/mocks/chatApi.js delete mode 100644 frontend/src/mocks/contractApi.js delete mode 100644 frontend/src/mocks/kanbanApi.js delete mode 100644 frontend/src/services/wallet.js create mode 100644 frontend/src/utils/wallet.js delete mode 100644 frontend/src/views/AccessTestPage.vue create mode 100644 frontend/src/views/AccessTestView.vue create mode 100644 frontend/src/views/AdminView.vue create mode 100644 frontend/src/views/ChatView.vue create mode 100644 frontend/src/views/ConversationsView.vue create mode 100644 frontend/src/views/IdentityView.vue delete mode 100644 frontend/src/views/KanbanBoardView.vue delete mode 100644 frontend/src/views/KanbanView.vue diff --git a/.cursor/settings.json b/.cursor/settings.json new file mode 100644 index 0000000..875d087 --- /dev/null +++ b/.cursor/settings.json @@ -0,0 +1,16 @@ +{ + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": true + }, + "[javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[vue]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[json]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + } + } \ No newline at end of file diff --git a/backend/.prettierrc b/backend/.prettierrc new file mode 100644 index 0000000..bc5ca63 --- /dev/null +++ b/backend/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 100 +} \ No newline at end of file diff --git a/backend/app.js b/backend/app.js index f637789..69aef8a 100644 --- a/backend/app.js +++ b/backend/app.js @@ -1,7 +1,7 @@ const express = require('express'); const cors = require('cors'); const session = require('express-session'); -const { verifySignature } = require('./utils/auth'); +const { verifySignature, findOrCreateUser } = require('./utils/auth'); const pgSession = require('connect-pg-simple')(session); const { requireRole } = require('./middleware/auth'); const crypto = require('crypto'); @@ -9,6 +9,13 @@ const path = require('path'); const fs = require('fs'); const { router: authRouter } = require('./routes/auth'); const { pool } = require('./db'); +const FileStore = require('session-file-store')(session); +const helmet = require('helmet'); +const sessionMiddleware = require('./middleware/session'); +const chatRouter = require('./routes/chat'); +const usersRoutes = require('./routes/users'); +const contractsRoutes = require('./routes/contracts'); +const rolesRoutes = require('./routes/roles'); const app = express(); @@ -20,74 +27,88 @@ function generateNonce() { // Парсинг JSON - должен быть до всех роутов app.use(express.json()); -// Настройка CORS - должна быть первой после парсинга JSON -app.use(cors({ - origin: ['http://127.0.0.1:5173', 'http://localhost:5173'], - credentials: true, - methods: ['GET', 'POST', 'OPTIONS', 'DELETE', 'PUT', 'HEAD'], - allowedHeaders: [ - 'Content-Type', - 'X-Wallet-Address', - 'X-Wallet-Signature', - 'Cookie', - 'Authorization' - ], - exposedHeaders: ['Set-Cookie'] -})); +// Настройка CORS +app.use( + cors({ + origin: function(origin, callback) { + // Разрешаем запросы с любого источника в режиме разработки + const allowedOrigins = ['http://localhost:3000', 'http://127.0.0.1:5173', 'http://localhost:5173']; + if (!origin || allowedOrigins.indexOf(origin) !== -1) { + callback(null, true); + } else { + callback(new Error('Not allowed by CORS')); + } + }, + credentials: true, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization', 'X-Auth-Nonce'], + }) +); // Настройка сессий -app.use(session({ - store: new pgSession({ - pool: pool, - tableName: 'session', - createTableIfMissing: true - }), - secret: process.env.SESSION_SECRET || 'your-secret-key', - resave: false, - saveUninitialized: true, - cookie: { - httpOnly: true, - secure: false, - sameSite: 'lax', - maxAge: 24 * 60 * 60 * 1000 // 24 часа - } -})); +app.use( + session({ + store: new pgSession({ + pool: pool, + tableName: 'sessions', + createTableIfMissing: false, + }), + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: true, + name: 'dapp.sid', + cookie: { + secure: false, + httpOnly: true, + maxAge: 30 * 24 * 60 * 60 * 1000, + sameSite: 'lax', + }, + }) +); -// Middleware для логирования сессий -app.use((req, res, next) => { - // Восстанавливаем сессию из store если есть sessionID - if (req.sessionID && !req.session.authenticated) { - req.sessionStore.get(req.sessionID, (err, session) => { - if (err) { - console.error('Session restore error:', err); - } else if (session) { - req.session.authenticated = session.authenticated; - req.session.address = session.address; - req.session.lastSignature = session.lastSignature; - } - next(); - }); - } else { - next(); - } -}); +// Middleware для безопасности +app.use( + helmet({ + contentSecurityPolicy: false, // Отключаем CSP для разработки + }) +); // Middleware для логирования app.use((req, res, next) => { console.log(`${req.method} ${req.path}`, { headers: req.headers, body: req.body, - session: req.session + session: req.session, }); next(); }); +// Middleware для сохранения сессии после каждого запроса +app.use((req, res, next) => { + const originalEnd = res.end; + + res.end = function () { + if (req.session && req.session.save) { + req.session.save((err) => { + if (err) { + console.error('Ошибка при сохранении сессии:', err); + } + originalEnd.apply(res, arguments); + }); + } else { + originalEnd.apply(res, arguments); + } + }; + + next(); +}); + // Middleware для проверки авторизации const requireAuth = (req, res, next) => { console.log('Auth check:', { session: req.session, authenticated: req.session.authenticated, - address: req.session.address + address: req.session.address, }); if (!req.session.authenticated || !req.session.address) { @@ -96,9 +117,68 @@ const requireAuth = (req, res, next) => { next(); }; +// Миддлвар для обновления дополнительных полей в таблице sessions +app.use(async (req, res, next) => { + try { + // Если есть адрес, но нет userId, найдем или создадим пользователя + if (req.session && req.session.authenticated && req.session.address && !req.session.userId) { + try { + const user = await findOrCreateUser(req.session.address, 'wallet'); + req.session.userId = user.id; + req.session.role = user.role; + req.session.isAdmin = user.is_admin; + + // Стандартизируем данные сессии + standardizeSessionData(req); + + // Сохраняем обновленную сессию + await new Promise((resolve, reject) => { + req.session.save(err => { + if (err) reject(err); + else resolve(); + }); + }); + } catch (err) { + console.error('Ошибка при обновлении userId в сессии:', err); + } + } + + // Обновляем поля в таблице sessions + if (req.session && (req.session.userId || req.session.address)) { + db.query( + 'UPDATE sessions SET last_activity = NOW(), user_id = $1, auth_channel = $2, language = $3 WHERE sid = $4', + [ + req.session.userId || null, + req.session.authChannel || 'web', + req.session.language || 'en', + req.sessionID + ] + ).catch(err => console.error('Error updating session data:', err)); + } + } catch (err) { + console.error('Ошибка в middleware сессий:', err); + } + + next(); +}); + +// Функция для стандартизации данных сессии +function standardizeSessionData(req) { + if (req.session.authenticated) { + // Убедимся, что все необходимые поля присутствуют + req.session.authType = req.session.authType || 'wallet'; + req.session.role = req.session.role || 'USER'; + req.session.isAdmin = !!req.session.isAdmin; + req.session.authChannel = req.session.authChannel || 'web'; + req.session.language = req.session.language || 'en'; + } +} + // API роуты const apiRouter = express.Router(); +// Удалите или закомментируйте этот блок кода +/* apiRouter.post('/refresh-session', async (req, res) => { try { const { address, signature } = req.body; @@ -143,17 +223,18 @@ apiRouter.post('/refresh-session', async (req, res) => { res.status(500).json({ error: 'Internal server error' }); } }); +*/ apiRouter.get('/session', (req, res) => { console.log('Session check:', { session: req.session, authenticated: req.session.authenticated, - address: req.session.address + address: req.session.address, }); - + res.json({ authenticated: !!req.session.authenticated, - address: req.session.address || null + address: req.session.address || null, }); }); @@ -180,12 +261,8 @@ apiRouter.get('/admin/check', async (req, res) => { const ethers = require('ethers'); const provider = new ethers.JsonRpcProvider(process.env.ETHEREUM_NETWORK_URL); const contractABI = require('./artifacts/contracts/MyContract.sol/MyContract.json').abi; - const contract = new ethers.Contract( - process.env.CONTRACT_ADDRESS, - contractABI, - provider - ); - + const contract = new ethers.Contract(process.env.CONTRACT_ADDRESS, contractABI, provider); + const contractOwner = await contract.owner(); const isAdmin = req.session.address.toLowerCase() === contractOwner.toLowerCase(); @@ -216,48 +293,51 @@ apiRouter.post('/verify', async (req, res) => { try { console.log('Получен запрос на верификацию в app.js:', req.body); const { message, signature } = req.body; - + if (!message || !signature) { - console.error('Отсутствуют необходимые поля:', { message: !!message, signature: !!signature }); + console.error('Отсутствуют необходимые поля:', { + message: !!message, + signature: !!signature, + }); return res.status(400).json({ success: false, error: 'Missing required fields' }); } - + // Проверяем, что message содержит адрес if (!message.address) { console.error('Отсутствует адрес в сообщении'); return res.status(400).json({ success: false, error: 'Missing address in message' }); } - + // Получаем адрес из сообщения напрямую const address = message.address; console.log('Адрес из сообщения:', address); - + // Устанавливаем сессию без проверки подписи req.session.authenticated = true; req.session.address = address; req.session.lastSignature = signature; - + // Сохраняем сессию req.session.save((err) => { if (err) { console.error('Ошибка при сохранении сессии:', err); - return res.status(500).json({ - success: false, - error: 'Session save failed', - message: err.message + return res.status(500).json({ + success: false, + error: 'Session save failed', + message: err.message, }); } - + console.log('Сессия сохранена успешно:', req.sessionID); return res.json({ success: true, address, isAdmin: true }); }); } catch (error) { console.error('Подробная ошибка при верификации:', error.stack); console.error('Verification error:', error); - res.status(500).json({ - success: false, - error: 'Verification failed', - message: error.message || 'Unknown error' + res.status(500).json({ + success: false, + error: 'Verification failed', + message: error.message || 'Unknown error', }); } }); @@ -267,27 +347,34 @@ app.use('/api', apiRouter); // Подключаем маршруты аутентификации app.use('/api/auth', authRouter); +app.use('/api/users', usersRoutes); +app.use('/api/contracts', contractsRoutes); +app.use('/api/chat', chatRouter); +app.use('/api/roles', rolesRoutes); apiRouter.get('/nonce', (req, res) => { const nonce = generateNonce(); console.log('Generated new nonce:', nonce); - + // Сохраняем nonce в сессии if (!req.session.nonces) { req.session.nonces = []; } req.session.nonces.push(nonce); - + // Ограничиваем количество nonce в сессии if (req.session.nonces.length > 5) { req.session.nonces.shift(); } - + console.log('Nonces in session:', req.session.nonces); - + res.json({ nonce }); }); +// Добавьте после настройки сессий +app.use(sessionMiddleware); + // Обработка ошибок сессий app.use((err, req, res, next) => { if (err.code === 'ENOENT' && err.message.includes('sessions')) { @@ -311,4 +398,8 @@ app.use((err, req, res, next) => { res.status(500).json({ error: 'Something broke!' }); }); -module.exports = { app }; \ No newline at end of file +// Подключаем маршруты для отладки +const debugRouter = require('./routes/debug'); +app.use('/api/debug', debugRouter); + +module.exports = { app }; diff --git a/backend/data/documents/example.md b/backend/data/documents/example.md deleted file mode 100644 index 7354c2e..0000000 --- a/backend/data/documents/example.md +++ /dev/null @@ -1,16 +0,0 @@ -# Пример документа для RAG - - Это пример документа, который будет использоваться в RAG системе. - - ## Блокчейн - - Блокчейн - это распределенная база данных, которая хранит информацию о всех транзакциях участников системы в виде "цепочки блоков". Каждый блок содержит набор транзакций и ссылку на предыдущий блок. - - ## Смарт-контракты - - Смарт-контракты - это программы, которые автоматически выполняются при соблюдении определенных условий. Они работают на блокчейне и могут автоматизировать выполнение соглашений. - - ## Web3 - - Web3 - это новое поколение интернета, основанное на блокчейне и децентрализованных технологиях. Оно позволяет пользователям контролировать свои данные и взаимодействовать без посредников. - \ No newline at end of file diff --git a/backend/db.js b/backend/db.js index b8dda3c..9e10da7 100644 --- a/backend/db.js +++ b/backend/db.js @@ -4,7 +4,7 @@ require('dotenv').config(); // Создаем пул соединений с базой данных const pool = new Pool({ connectionString: process.env.DATABASE_URL, - ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false + ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, }); // Проверяем подключение к базе данных @@ -12,7 +12,7 @@ pool.query('SELECT NOW()', (err, res) => { if (err) { console.error('Ошибка подключения к базе данных:', err); console.log('Переключение на временное хранилище данных в памяти...'); - + // Если не удалось подключиться к базе данных, используем временное хранилище module.exports = createInMemoryStorage(); } else { @@ -28,53 +28,53 @@ const query = (text, params) => { // Экспортируем функции для работы с базой данных module.exports = { query, - pool + pool, }; // Функция для создания временного хранилища данных в памяти function createInMemoryStorage() { console.log('Используется временное хранилище данных в памяти'); - + const users = []; let userId = 1; - + // Эмуляция функции query для работы с пользователями const inMemoryQuery = async (text, params) => { console.log('SQL query (in-memory):', text, 'Params:', params); - + // Эмуляция запроса SELECT * FROM users WHERE address = $1 if (text.includes('SELECT * FROM users WHERE address = $1')) { const address = params[0]; - const user = users.find(u => u.address === address); + const user = users.find((u) => u.address === address); return { rows: user ? [user] : [] }; } - + // Эмуляция запроса SELECT * FROM users WHERE email = $1 if (text.includes('SELECT * FROM users WHERE email = $1')) { const email = params[0]; - const user = users.find(u => u.email === email); + const user = users.find((u) => u.email === email); return { rows: user ? [user] : [] }; } - + // Эмуляция запроса INSERT INTO users if (text.includes('INSERT INTO users')) { let newUser; - + if (text.includes('address')) { newUser = { id: userId++, address: params[0], created_at: new Date(), is_admin: false }; } else if (text.includes('email')) { newUser = { id: userId++, email: params[0], created_at: new Date(), is_admin: false }; } - + if (newUser) { users.push(newUser); return { rows: [newUser] }; } } - + return { rows: [] }; }; - + return { query: inMemoryQuery, pool: { @@ -89,7 +89,42 @@ function createInMemoryStorage() { } else { return inMemoryQuery(text, params); } - } - } + }, + }, }; -} \ No newline at end of file +} + +// Проверка и создание таблицы session, если она не существует +async function checkSessionTable() { + try { + const result = await pool.query(` + SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = 'session' + ); + `); + + const tableExists = result.rows[0].exists; + + if (!tableExists) { + console.log('Таблица session не существует, создаем...'); + + await pool.query(` + CREATE TABLE "session" ( + "sid" varchar NOT NULL COLLATE "default", + "sess" json NOT NULL, + "expire" timestamp(6) NOT NULL, + CONSTRAINT "session_pkey" PRIMARY KEY ("sid") + ); + CREATE INDEX "IDX_session_expire" ON "session" ("expire"); + `); + + console.log('Таблица session успешно создана'); + } else { + console.log('Таблица session уже существует'); + } + } catch (error) { + console.error('Ошибка при проверке/создании таблицы session:', error); + } +} diff --git a/backend/docs/api.md b/backend/docs/api.md new file mode 100644 index 0000000..c24dc91 --- /dev/null +++ b/backend/docs/api.md @@ -0,0 +1,9 @@ + # API Documentation + +## Authentication + +### POST /api/auth/refresh-session + +Refreshes the user session. + +**Request:** diff --git a/backend/eslint.config.js b/backend/eslint.config.js new file mode 100644 index 0000000..8be5868 --- /dev/null +++ b/backend/eslint.config.js @@ -0,0 +1,30 @@ +import globals from 'globals'; + +export default [ + { + ignores: ['node_modules/**', 'artifacts/**', 'sessions/**', 'logs/**', 'data/**'], + }, + { + files: ['**/*.js'], + languageOptions: { + ecmaVersion: 2022, + sourceType: 'module', + globals: { + ...globals.node, + ...globals.es2021, + describe: 'readonly', + it: 'readonly', + beforeEach: 'readonly', + before: 'readonly', + after: 'readonly', + afterEach: 'readonly', + }, + }, + rules: { + 'no-unused-vars': 'off', + 'no-console': 'off', + 'no-undef': 'error', + 'no-duplicate-imports': 'error', + }, + }, +]; diff --git a/backend/hardhat.config.js b/backend/hardhat.config.js index 3bdc956..8a59a1e 100644 --- a/backend/hardhat.config.js +++ b/backend/hardhat.config.js @@ -1,12 +1,12 @@ -require("@nomicfoundation/hardhat-toolbox"); +require('@nomicfoundation/hardhat-toolbox'); require('dotenv').config(); module.exports = { - solidity: "0.8.20", + solidity: '0.8.20', networks: { sepolia: { url: process.env.ETHEREUM_NETWORK_URL, - accounts: [process.env.PRIVATE_KEY] - } - } -}; \ No newline at end of file + accounts: [process.env.PRIVATE_KEY], + }, + }, +}; diff --git a/backend/middleware/auth.js b/backend/middleware/auth.js index 20d5dc0..0c1d608 100644 --- a/backend/middleware/auth.js +++ b/backend/middleware/auth.js @@ -1,32 +1,130 @@ -const { checkAccess } = require('../utils/access-check'); +const logger = require('../utils/logger'); +const { getUserInfo } = require('../utils/access-check'); + +// Добавьте в начало файла +const isMiddleware = true; // Middleware для проверки роли -const requireRole = (requiredRole) => async (req, res, next) => { +const requireRole = (allowedRoles) => async (req, res, next) => { + if (!req.session || !req.session.authenticated || !req.session.userId) { + return res.status(401).json({ error: 'Требуется аутентификация' }); + } + try { - const address = req.headers['x-wallet-address']; - if (!address) { - return res.status(401).json({ error: 'No wallet address' }); - } - - const { hasAccess, role } = await checkAccess(address); + // Получение информации о пользователе + const userInfo = await getUserInfo(req.session.userId); - if (!hasAccess) { - return res.status(403).json({ error: 'No access token' }); + if (!userInfo) { + return res.status(401).json({ error: 'Пользователь не найден' }); } - - if (requiredRole && role !== requiredRole) { - return res.status(403).json({ error: 'Insufficient permissions' }); + + // Проверка роли + if (!allowedRoles.includes(userInfo.role)) { + return res.status(403).json({ error: 'Недостаточно прав' }); } - - // Добавляем информацию о роли в request - req.userRole = role; + next(); } catch (error) { - console.error('Auth check error:', error); - res.status(500).json({ error: 'Auth check failed' }); + logger.error('Error checking user role:', error); + res.status(500).json({ error: 'Внутренняя ошибка сервера' }); } }; +// Проверка роли пользователя +const checkRole = async (req, res, next) => { + try { + // Если функция вызвана как middleware + const isMiddleware = typeof next === 'function'; + + if (!req.session.authenticated) { + return isMiddleware ? res.status(401).json({ error: 'Не авторизован' }) : false; + } + + // Если роль администратора уже проверена в сессии + if (req.session.isAdmin === true) { + return isMiddleware ? next() : true; + } + + const db = require('../db'); + + // Проверка наличия токенов доступа в смарт-контракте + if (req.session.address) { + const address = req.session.address.toLowerCase(); + + // Проверка в базе данных + const userRole = await db.query( + 'SELECT r.name FROM users u JOIN roles r ON u.role_id = r.id WHERE LOWER(u.address) = $1', + [address] + ); + + if (userRole.rows.length > 0 && userRole.rows[0].name === 'admin') { + req.session.isAdmin = true; + return isMiddleware ? next() : true; + } + + // Проверка токенов в смарт-контракте через сервис + const { ethers } = require('ethers'); + const provider = new ethers.JsonRpcProvider(process.env.PROVIDER_URL); + const accessTokenABI = require('../artifacts/contracts/AccessToken.sol/AccessToken.json').abi; + const accessTokenContract = new ethers.Contract( + process.env.ACCESS_TOKEN_ADDRESS, + accessTokenABI, + provider + ); + + try { + const hasAdminRole = await accessTokenContract.hasRole( + ethers.keccak256(ethers.toUtf8Bytes('ADMIN_ROLE')), + address + ); + + if (hasAdminRole) { + // Обновляем роль в базе данных + await db.query( + 'UPDATE users SET role_id = (SELECT id FROM roles WHERE name = $1) WHERE LOWER(address) = $2', + ['admin', address] + ); + req.session.isAdmin = true; + return isMiddleware ? next() : true; + } + } catch (error) { + console.error('Ошибка при проверке роли в контракте:', error); + } + } + + // Если пользователь не администратор + req.session.isAdmin = false; + return isMiddleware ? res.status(403).json({ error: 'Недостаточно прав' }) : false; + } catch (error) { + console.error('Ошибка при проверке роли:', error); + return isMiddleware ? res.status(500).json({ error: 'Внутренняя ошибка сервера' }) : false; + } +}; + +// Middleware для проверки аутентификации +const requireAuth = (req, res, next) => { + if (!req.session || !req.session.authenticated) { + return res.status(401).json({ error: 'Требуется аутентификация' }); + } + next(); +}; + +// Middleware для проверки прав администратора +const requireAdmin = (req, res, next) => { + if (!req.session || !req.session.authenticated) { + return res.status(401).json({ error: 'Требуется аутентификация' }); + } + + if (!req.session.isAdmin) { + return res.status(403).json({ error: 'Требуются права администратора' }); + } + + next(); +}; + module.exports = { - requireRole -}; \ No newline at end of file + requireRole, + requireAuth, + requireAdmin, + checkRole, +}; diff --git a/backend/middleware/logger.js b/backend/middleware/logger.js new file mode 100644 index 0000000..bcab6a4 --- /dev/null +++ b/backend/middleware/logger.js @@ -0,0 +1,12 @@ +const logger = (req, res, next) => { + const start = Date.now(); + + res.on('finish', () => { + const duration = Date.now() - start; + console.log(`${req.method} ${req.originalUrl} - ${res.statusCode} - ${duration}ms`); + }); + + next(); +}; + +module.exports = logger; diff --git a/backend/middleware/session.js b/backend/middleware/session.js new file mode 100644 index 0000000..a422eec --- /dev/null +++ b/backend/middleware/session.js @@ -0,0 +1,22 @@ +const session = require('express-session'); +const pgSession = require('connect-pg-simple')(session); +const { pool } = require('../db'); + +const sessionMiddleware = session({ + store: new pgSession({ + pool: pool, + tableName: 'session', + createTableIfMissing: true, + }), + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + cookie: { + secure: process.env.NODE_ENV === 'production', // В production должно быть true + httpOnly: true, + maxAge: 24 * 60 * 60 * 1000, // 24 часа + sameSite: 'none', // Для работы между разными доменами + }, +}); + +module.exports = sessionMiddleware; diff --git a/backend/migrations/004_personalization_tables.sql b/backend/migrations/004_personalization_tables.sql deleted file mode 100644 index 94001aa..0000000 --- a/backend/migrations/004_personalization_tables.sql +++ /dev/null @@ -1,44 +0,0 @@ --- Создание таблицы для связи идентификаторов пользователей -CREATE TABLE IF NOT EXISTS user_identities ( - id SERIAL PRIMARY KEY, - user_id INTEGER REFERENCES users(id), - identity_type VARCHAR(20) NOT NULL, -- 'ethereum', 'telegram', 'email' - identity_value VARCHAR(255) NOT NULL, - created_at TIMESTAMP DEFAULT NOW(), - UNIQUE(identity_type, identity_value) -); - --- Создание таблицы для предпочтений пользователей -CREATE TABLE IF NOT EXISTS user_preferences ( - id SERIAL PRIMARY KEY, - user_id INTEGER REFERENCES users(id), - preference_key VARCHAR(50) NOT NULL, - preference_value TEXT, - updated_at TIMESTAMP DEFAULT NOW(), - UNIQUE(user_id, preference_key) -); - --- Создание таблицы для взаимодействий пользователей -CREATE TABLE IF NOT EXISTS user_interactions ( - id SERIAL PRIMARY KEY, - user_id INTEGER REFERENCES users(id), - interaction_type VARCHAR(50) NOT NULL, - interaction_data JSONB, - created_at TIMESTAMP DEFAULT NOW() -); - --- Создание таблицы для тем пользователей -CREATE TABLE IF NOT EXISTS user_topics ( - id SERIAL PRIMARY KEY, - user_id INTEGER REFERENCES users(id), - topic VARCHAR(100) NOT NULL, - relevance_score FLOAT DEFAULT 1.0, - updated_at TIMESTAMP DEFAULT NOW(), - UNIQUE(user_id, topic) -); - --- Индексы для оптимизации запросов -CREATE INDEX IF NOT EXISTS idx_user_identities_user_id ON user_identities(user_id); -CREATE INDEX IF NOT EXISTS idx_user_preferences_user_id ON user_preferences(user_id); -CREATE INDEX IF NOT EXISTS idx_user_interactions_user_id ON user_interactions(user_id); -CREATE INDEX IF NOT EXISTS idx_user_topics_user_id ON user_topics(user_id); \ No newline at end of file diff --git a/backend/migrations/005_kanban_tables.sql b/backend/migrations/005_kanban_tables.sql deleted file mode 100644 index f977b7b..0000000 --- a/backend/migrations/005_kanban_tables.sql +++ /dev/null @@ -1,64 +0,0 @@ --- Таблица для Канбан-досок -CREATE TABLE IF NOT EXISTS kanban_boards ( - id SERIAL PRIMARY KEY, - title VARCHAR(100) NOT NULL, - description TEXT, - owner_id INTEGER REFERENCES users(id), - is_public BOOLEAN DEFAULT false, - created_at TIMESTAMP DEFAULT NOW(), - updated_at TIMESTAMP DEFAULT NOW() -); - --- Таблица для колонок Канбан-доски -CREATE TABLE IF NOT EXISTS kanban_columns ( - id SERIAL PRIMARY KEY, - board_id INTEGER REFERENCES kanban_boards(id) ON DELETE CASCADE, - title VARCHAR(100) NOT NULL, - position INTEGER NOT NULL, - wip_limit INTEGER DEFAULT NULL, -- Лимит задач в работе (Work In Progress) - created_at TIMESTAMP DEFAULT NOW(), - updated_at TIMESTAMP DEFAULT NOW() -); - --- Таблица для карточек (задач) Канбан-доски -CREATE TABLE IF NOT EXISTS kanban_cards ( - id SERIAL PRIMARY KEY, - column_id INTEGER REFERENCES kanban_columns(id) ON DELETE CASCADE, - title VARCHAR(200) NOT NULL, - description TEXT, - position INTEGER NOT NULL, - assigned_to INTEGER REFERENCES users(id), - due_date TIMESTAMP, - labels JSONB DEFAULT '[]', - created_by INTEGER REFERENCES users(id), - created_at TIMESTAMP DEFAULT NOW(), - updated_at TIMESTAMP DEFAULT NOW() -); - --- Таблица для комментариев к карточкам -CREATE TABLE IF NOT EXISTS kanban_comments ( - id SERIAL PRIMARY KEY, - card_id INTEGER REFERENCES kanban_cards(id) ON DELETE CASCADE, - user_id INTEGER REFERENCES users(id), - content TEXT NOT NULL, - created_at TIMESTAMP DEFAULT NOW(), - updated_at TIMESTAMP DEFAULT NOW() -); - --- Таблица для доступа к доскам -CREATE TABLE IF NOT EXISTS kanban_board_access ( - id SERIAL PRIMARY KEY, - board_id INTEGER REFERENCES kanban_boards(id) ON DELETE CASCADE, - user_id INTEGER REFERENCES users(id), - access_level VARCHAR(20) NOT NULL, -- 'read', 'write', 'admin' - created_at TIMESTAMP DEFAULT NOW(), - updated_at TIMESTAMP DEFAULT NOW(), - UNIQUE(board_id, user_id) -); - --- Индексы для оптимизации запросов -CREATE INDEX IF NOT EXISTS idx_kanban_columns_board_id ON kanban_columns(board_id); -CREATE INDEX IF NOT EXISTS idx_kanban_cards_column_id ON kanban_cards(column_id); -CREATE INDEX IF NOT EXISTS idx_kanban_comments_card_id ON kanban_comments(card_id); -CREATE INDEX IF NOT EXISTS idx_kanban_board_access_board_id ON kanban_board_access(board_id); -CREATE INDEX IF NOT EXISTS idx_kanban_board_access_user_id ON kanban_board_access(user_id); \ No newline at end of file diff --git a/backend/migrations/006_role_management.sql b/backend/migrations/006_role_management.sql new file mode 100644 index 0000000..f944950 --- /dev/null +++ b/backend/migrations/006_role_management.sql @@ -0,0 +1,26 @@ +CREATE TABLE IF NOT EXISTS roles ( + id SERIAL PRIMARY KEY, + name VARCHAR(50) NOT NULL UNIQUE, + description TEXT +); + +-- Добавление базовых ролей +INSERT INTO roles (name, description) VALUES + ('admin', 'Администратор с полным доступом к системе'), + ('user', 'Обычный пользователь с базовым доступом') +ON CONFLICT (name) DO NOTHING; + +-- Добавление поля role_id в таблицу users, если оно еще не существует +ALTER TABLE users ADD COLUMN IF NOT EXISTS role_id INTEGER REFERENCES roles(id) DEFAULT 2; + +-- Таблица для отслеживания токенов доступа +CREATE TABLE IF NOT EXISTS access_tokens ( + id SERIAL PRIMARY KEY, + wallet_address VARCHAR(42) NOT NULL, + token_id INTEGER NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(wallet_address, token_id) +); + +-- Индекс для быстрого поиска по адресу кошелька +CREATE INDEX IF NOT EXISTS idx_access_tokens_wallet ON access_tokens(wallet_address); \ No newline at end of file diff --git a/backend/migrations/007_user_identities_conversations.sql b/backend/migrations/007_user_identities_conversations.sql new file mode 100644 index 0000000..94329e2 --- /dev/null +++ b/backend/migrations/007_user_identities_conversations.sql @@ -0,0 +1,40 @@ +-- Таблица идентификаторов пользователей +CREATE TABLE IF NOT EXISTS user_identities ( + id SERIAL PRIMARY KEY, + user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, + identity_type VARCHAR(20) NOT NULL, -- 'wallet', 'telegram', 'email' + identity_value VARCHAR(255) NOT NULL, + verified BOOLEAN DEFAULT FALSE, + verification_token VARCHAR(100), + verification_expires TIMESTAMP, + last_used TIMESTAMP, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(identity_type, identity_value) +); + +-- Таблица диалогов +CREATE TABLE IF NOT EXISTS conversations ( + id SERIAL PRIMARY KEY, + user_id INTEGER REFERENCES users(id), + title VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Таблица сообщений +CREATE TABLE IF NOT EXISTS messages ( + id SERIAL PRIMARY KEY, + conversation_id INTEGER REFERENCES conversations(id), + sender_type VARCHAR(20) NOT NULL, -- 'user', 'ai', 'admin' + sender_id INTEGER, -- ID пользователя или администратора + content TEXT, + channel VARCHAR(20) NOT NULL, -- 'web', 'telegram', 'email' + metadata JSONB, -- Дополнительная информация о сообщении + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Добавление языковых настроек в таблицу пользователей +ALTER TABLE users +ADD COLUMN IF NOT EXISTS language VARCHAR(10) DEFAULT 'en', +ADD COLUMN IF NOT EXISTS last_token_check TIMESTAMP; + diff --git a/backend/migrations/008_chat_history.sql b/backend/migrations/008_chat_history.sql new file mode 100644 index 0000000..d3a4740 --- /dev/null +++ b/backend/migrations/008_chat_history.sql @@ -0,0 +1,14 @@ +-- Создание таблицы для хранения истории диалогов +CREATE TABLE IF NOT EXISTS chat_history ( + id SERIAL PRIMARY KEY, + user_id INTEGER REFERENCES users(id), + channel VARCHAR(20) NOT NULL, -- 'web', 'telegram', 'email' + sender_type VARCHAR(10) NOT NULL, -- 'user', 'ai', 'admin' + content TEXT, + metadata JSONB, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +-- Индексы для быстрого поиска +CREATE INDEX IF NOT EXISTS idx_chat_history_user_id ON chat_history(user_id); +CREATE INDEX IF NOT EXISTS idx_chat_history_channel ON chat_history(channel); \ No newline at end of file diff --git a/backend/nodemon.json b/backend/nodemon.json index 223e986..ab5c2f7 100644 --- a/backend/nodemon.json +++ b/backend/nodemon.json @@ -1,20 +1,9 @@ { "verbose": true, - "ignore": [ - ".git", - "node_modules/**/node_modules", - "sessions", - "data/vector_store" - ], - "watch": [ - "*.js", - "routes/", - "services/", - "utils/", - "middleware/" - ], + "ignore": [".git", "node_modules/**/node_modules", "sessions", "data/vector_store"], + "watch": ["*.js", "routes/", "services/", "utils/", "middleware/"], "env": { "NODE_ENV": "development" }, "ext": "js,json,env" -} \ No newline at end of file +} diff --git a/backend/package.json b/backend/package.json index ea36ca2..e7bb1b4 100644 --- a/backend/package.json +++ b/backend/package.json @@ -12,34 +12,46 @@ "server": "nodemon server.js --signal SIGUSR2", "migrate": "node scripts/run-migrations.js", "prod": "NODE_ENV=production node server.js", - "test": "mocha test/**/*.test.js" + "test": "mocha test/**/*.test.js", + "check-ollama": "node scripts/check-ollama-models.js", + "check-ethers": "node scripts/check-ethers-v6-compatibility.js", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "format": "prettier --write \"**/*.{js,vue,json,md}\"", + "format:check": "prettier --check \"**/*.{js,vue,json,md}\"" }, "dependencies": { - "@langchain/community": "^0.0.32", + "@langchain/community": "^0.3.34", "@langchain/core": "0.3.0", "@langchain/ollama": "^0.2.0", "axios": "^1.6.7", "connect-pg-simple": "^10.0.0", "cors": "^2.8.5", + "cron": "^4.1.0", "csurf": "^1.11.0", "dotenv": "^16.0.3", - "ethers": "^6.7.1", + "ethers": "6.13.5", "express": "^4.18.2", "express-rate-limit": "^7.5.0", "express-session": "^1.17.3", "helmet": "^8.0.0", "hnswlib-node": "^3.0.0", "imap": "^0.8.19", - "langchain": "^0.1.21", + "langchain": "0.0.200", "mailparser": "^3.7.2", - "node-telegram-bot-api": "^0.64.0", - "nodemailer": "^6.9.9", + "node-cron": "^3.0.3", + "node-telegram-bot-api": "^0.66.0", + "nodemailer": "^6.10.0", "pg": "^8.10.0", "session-file-store": "^1.5.0", "siwe": "^2.1.4", "winston": "^3.17.0" }, "devDependencies": { - "nodemon": "^2.0.22" + "eslint": "^9.21.0", + "eslint-config-prettier": "^10.0.2", + "globals": "^16.0.0", + "nodemon": "^3.1.9", + "prettier": "^3.5.3" } } diff --git a/backend/routes/access.js b/backend/routes/access.js index 3ee95dd..d8372c2 100644 --- a/backend/routes/access.js +++ b/backend/routes/access.js @@ -1,39 +1,42 @@ const express = require('express'); const router = express.Router(); const { Pool } = require('pg'); +const { requireAuth, requireAdmin } = require('../middleware/auth'); +const db = require('../db'); +const { ethers } = require('ethers'); // Подключение к БД const pool = new Pool({ connectionString: process.env.DATABASE_URL, - ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false + ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, }); // Проверка доступа router.get('/check', async (req, res) => { const walletAddress = req.headers['x-wallet-address']; - + if (!walletAddress) { return res.status(400).json({ error: 'No wallet address provided' }); } - + try { // Проверяем наличие активного токена для адреса const result = await pool.query( 'SELECT * FROM access_tokens WHERE wallet_address = $1 AND expires_at > NOW()', [walletAddress.toLowerCase()] ); - + if (result.rows.length === 0) { return res.json({ hasAccess: false }); } - + const token = result.rows[0]; - + res.json({ hasAccess: true, tokenId: token.id, role: token.role, - expiresAt: token.expires_at + expiresAt: token.expires_at, }); } catch (error) { console.error('Access check error:', error); @@ -44,15 +47,15 @@ router.get('/check', async (req, res) => { // Проверка прав администратора router.get('/admin-only', async (req, res) => { const walletAddress = req.headers['x-wallet-address']; - + if (!walletAddress) { return res.status(400).json({ error: 'No wallet address provided' }); } - + try { // Временное решение: разрешаем доступ для всех console.log('Admin access requested by:', walletAddress); - + res.json({ success: true }); } catch (error) { console.error('Admin check error:', error); @@ -63,34 +66,34 @@ router.get('/admin-only', async (req, res) => { // Получение списка токенов router.get('/tokens', async (req, res) => { const walletAddress = req.headers['x-wallet-address']; - + if (!walletAddress) { return res.status(400).json({ error: 'No wallet address provided' }); } - + try { // Проверяем права администратора const adminCheck = await pool.query( 'SELECT * FROM access_tokens WHERE wallet_address = $1 AND role = $2 AND expires_at > NOW()', [walletAddress.toLowerCase(), 'ADMIN'] ); - + if (adminCheck.rows.length === 0) { return res.status(403).json({ error: 'Access denied' }); } - + // Получаем список всех токенов - const result = await pool.query( - 'SELECT * FROM access_tokens ORDER BY created_at DESC' + const result = await pool.query('SELECT * FROM access_tokens ORDER BY created_at DESC'); + + res.json( + result.rows.map((token) => ({ + id: token.id, + walletAddress: token.wallet_address, + role: token.role, + createdAt: token.created_at, + expiresAt: token.expires_at, + })) ); - - res.json(result.rows.map(token => ({ - id: token.id, - walletAddress: token.wallet_address, - role: token.role, - createdAt: token.created_at, - expiresAt: token.expires_at - }))); } catch (error) { console.error('Tokens list error:', error); res.status(500).json({ error: error.message }); @@ -100,44 +103,44 @@ router.get('/tokens', async (req, res) => { // Создание токена router.post('/tokens', async (req, res) => { const walletAddress = req.headers['x-wallet-address']; - + if (!walletAddress) { return res.status(400).json({ error: 'No wallet address provided' }); } - + try { // Проверяем права администратора const adminCheck = await pool.query( 'SELECT * FROM access_tokens WHERE wallet_address = $1 AND role = $2 AND expires_at > NOW()', [walletAddress.toLowerCase(), 'ADMIN'] ); - + if (adminCheck.rows.length === 0) { return res.status(403).json({ error: 'Access denied' }); } - + const { walletAddress: targetAddress, role, expiresInDays } = req.body; - + if (!targetAddress || !role || !expiresInDays) { return res.status(400).json({ error: 'Missing required fields' }); } - + // Вычисляем дату истечения const expiresAt = new Date(); expiresAt.setDate(expiresAt.getDate() + parseInt(expiresInDays)); - + // Создаем токен const result = await pool.query( 'INSERT INTO access_tokens (wallet_address, role, expires_at) VALUES ($1, $2, $3) RETURNING *', [targetAddress.toLowerCase(), role, expiresAt] ); - + res.json({ id: result.rows[0].id, walletAddress: result.rows[0].wallet_address, role: result.rows[0].role, createdAt: result.rows[0].created_at, - expiresAt: result.rows[0].expires_at + expiresAt: result.rows[0].expires_at, }); } catch (error) { console.error('Token creation error:', error); @@ -148,30 +151,27 @@ router.post('/tokens', async (req, res) => { // Отзыв токена router.delete('/tokens/:id', async (req, res) => { const walletAddress = req.headers['x-wallet-address']; - + if (!walletAddress) { return res.status(400).json({ error: 'No wallet address provided' }); } - + try { // Проверяем права администратора const adminCheck = await pool.query( 'SELECT * FROM access_tokens WHERE wallet_address = $1 AND role = $2 AND expires_at > NOW()', [walletAddress.toLowerCase(), 'ADMIN'] ); - + if (adminCheck.rows.length === 0) { return res.status(403).json({ error: 'Access denied' }); } - + const { id } = req.params; - + // Удаляем токен - await pool.query( - 'DELETE FROM access_tokens WHERE id = $1', - [id] - ); - + await pool.query('DELETE FROM access_tokens WHERE id = $1', [id]); + res.json({ success: true }); } catch (error) { console.error('Token revocation error:', error); @@ -179,4 +179,123 @@ router.delete('/tokens/:id', async (req, res) => { } }); -module.exports = router; \ No newline at end of file +// Получение информации о роли текущего пользователя +router.get('/role', requireAuth, async (req, res) => { + try { + const address = req.session.address.toLowerCase(); + + const result = await db.query( + 'SELECT r.name as role FROM users u JOIN roles r ON u.role_id = r.id WHERE LOWER(u.address) = $1', + [address] + ); + + if (result.rows.length === 0) { + return res.status(404).json({ error: 'Пользователь не найден' }); + } + + return res.json({ role: result.rows[0].role }); + } catch (error) { + console.error('Ошибка при получении роли:', error); + return res.status(500).json({ error: 'Внутренняя ошибка сервера' }); + } +}); + +// Получение списка всех пользователей (только для администраторов) +router.get('/users', requireAdmin, async (req, res) => { + try { + const result = await db.query( + 'SELECT u.id, u.wallet_address, r.name as role, u.created_at FROM users u JOIN roles r ON u.role_id = r.id' + ); + + return res.json(result.rows); + } catch (error) { + console.error('Ошибка при получении списка пользователей:', error); + return res.status(500).json({ error: 'Внутренняя ошибка сервера' }); + } +}); + +// Изменение роли пользователя (только для администраторов) +router.post('/users/:userId/role', requireAdmin, async (req, res) => { + try { + const { userId } = req.params; + const { role } = req.body; + + if (!role || !['admin', 'user'].includes(role)) { + return res.status(400).json({ error: 'Некорректная роль' }); + } + + await db.query( + 'UPDATE users SET role_id = (SELECT id FROM roles WHERE name = $1) WHERE id = $2', + [role, userId] + ); + + return res.json({ success: true }); + } catch (error) { + console.error('Ошибка при изменении роли пользователя:', error); + return res.status(500).json({ error: 'Внутренняя ошибка сервера' }); + } +}); + +// Получение информации о токенах доступа текущего пользователя +router.get('/tokens', requireAuth, async (req, res) => { + try { + // Логирование для отладки + console.log('GET /api/access/tokens запрос получен'); + console.log('Сессия пользователя:', req.session); + + // Получаем адрес из сессии, а не из заголовков + if (!req.session || !req.session.address) { + return res.status(400).json({ error: 'No wallet address in session' }); + } + + const address = req.session.address.toLowerCase(); + + // Используем правильное имя таблицы и полей + const result = await db.query( + 'SELECT id, wallet_address, role, created_at, expires_at FROM access_tokens WHERE LOWER(wallet_address) = $1', + [address] + ); + + return res.json(result.rows); + } catch (error) { + console.error('Ошибка при получении токенов:', error); + res.status(500).json({ error: 'Ошибка сервера' }); + } +}); + +router.post('/mint', requireAuth, requireAdmin, async (req, res) => { + try { + // Логирование для отладки + console.log('POST /api/access/mint запрос получен'); + console.log('Данные запроса:', req.body); + + const { walletAddress, role, expiresInDays } = req.body; + + if (!walletAddress || !role || !expiresInDays) { + return res.status(400).json({ error: 'Missing required fields' }); + } + + // Вычисляем дату истечения + const expiresAt = new Date(); + expiresAt.setDate(expiresAt.getDate() + parseInt(expiresInDays)); + + // Создаем токен + const result = await pool.query( + 'INSERT INTO access_tokens (wallet_address, role, expires_at) VALUES ($1, $2, $3) RETURNING *', + [walletAddress.toLowerCase(), role, expiresAt] + ); + + res.json({ + id: result.rows[0].id, + walletAddress: result.rows[0].wallet_address, + role: result.rows[0].role, + createdAt: result.rows[0].created_at, + expiresAt: result.rows[0].expires_at, + }); + } catch (error) { + console.error('Ошибка при создании токена:', error); + res.status(500).json({ error: 'Ошибка сервера' }); + } +}); + +module.exports = router; diff --git a/backend/routes/admin.js b/backend/routes/admin.js new file mode 100644 index 0000000..b4f473d --- /dev/null +++ b/backend/routes/admin.js @@ -0,0 +1,123 @@ +const express = require('express'); +const router = express.Router(); +const db = require('../db'); +const { checkIfAdmin } = require('../utils/access-check'); + +// Middleware для проверки прав администратора +const requireAdmin = async (req, res, next) => { + console.log('Проверка прав администратора:', { + session: req.session + ? { + authenticated: req.session.authenticated, + address: req.session.address, + isAdmin: req.session.isAdmin, + } + : null, + headers: { + authorization: req.headers.authorization, + }, + }); + + // Проверка аутентификации через сессию + if (req.session && req.session.authenticated && req.session.isAdmin) { + console.log('Пользователь авторизован как администратор через сессию'); + return next(); + } + + // Проверка через заголовок авторизации + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Bearer ')) { + console.log('Отсутствует заголовок авторизации'); + return res.status(401).json({ error: 'Unauthorized' }); + } + + const address = authHeader.split(' ')[1]; + console.log('Проверка адреса из заголовка:', address); + + try { + // Проверяем напрямую в базе данных + const userResult = await db.query('SELECT is_admin FROM users WHERE address = $1', [ + address.toLowerCase(), + ]); + + if (userResult.rows.length === 0) { + console.log(`Пользователь с адресом ${address} не найден`); + return res.status(404).json({ error: 'User not found' }); + } + + const isAdmin = userResult.rows[0].is_admin; + console.log(`Пользователь с адресом ${address} имеет статус администратора:`, isAdmin); + + if (!isAdmin) { + console.log(`Пользователь с адресом ${address} не является администратором`); + return res.status(403).json({ error: 'Forbidden' }); + } + + // Обновляем сессию + if (req.session) { + req.session.authenticated = true; + req.session.address = address; + req.session.isAdmin = true; + + console.log('Сессия обновлена из middleware:', { + address, + isAdmin: true, + }); + } + + next(); + } catch (error) { + console.error('Ошибка при проверке прав администратора:', error); + return res.status(500).json({ error: 'Internal server error' }); + } +}; + +// Применяем middleware ко всем маршрутам +router.use(requireAdmin); + +// Маршрут для получения списка пользователей +router.get('/users', async (req, res) => { + try { + const result = await db.query('SELECT * FROM users'); + res.json(result.rows); + } catch (error) { + console.error('Ошибка при получении списка пользователей:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Маршрут для получения статистики +router.get('/stats', async (req, res) => { + try { + // Получаем количество пользователей + const usersCount = await db.query('SELECT COUNT(*) FROM users'); + + // Получаем количество досок + const boardsCount = await db.query('SELECT COUNT(*) FROM kanban_boards'); + + // Получаем количество задач + const tasksCount = await db.query('SELECT COUNT(*) FROM kanban_tasks'); + + res.json({ + userCount: parseInt(usersCount.rows[0].count), + boardCount: parseInt(boardsCount.rows[0].count), + taskCount: parseInt(tasksCount.rows[0].count), + }); + } catch (error) { + console.error('Ошибка при получении статистики:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Маршрут для получения логов +router.get('/logs', async (req, res) => { + try { + const result = await db.query('SELECT * FROM logs ORDER BY created_at DESC LIMIT 100'); + res.json(result.rows); + } catch (error) { + console.error('Ошибка при получении логов:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +module.exports = router; diff --git a/backend/routes/auth.js b/backend/routes/auth.js index 795dcf8..a1f132d 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -6,6 +6,10 @@ const db = require('../db'); const logger = require('../utils/logger'); const helmet = require('helmet'); const rateLimit = require('express-rate-limit'); +const { checkIfAdmin } = require('../utils/access-check'); +const { checkRole, requireAuth } = require('../middleware/auth'); +const { pool } = require('../db'); +const { verifySignature, checkAccess, findOrCreateUser } = require('../utils/auth'); // Создайте лимитер для попыток аутентификации const authLimiter = rateLimit({ @@ -13,208 +17,221 @@ const authLimiter = rateLimit({ max: 20, // Увеличьте лимит с 5 до 20 standardHeaders: true, legacyHeaders: false, - message: { error: 'Слишком много попыток аутентификации. Попробуйте позже.' } + message: { error: 'Слишком много попыток аутентификации. Попробуйте позже.' }, }); -// Маршрут для получения nonce для подписи +// Получение nonce для аутентификации router.get('/nonce', async (req, res) => { try { const { address } = req.query; - - // Удалите или закомментируйте эти логи - // console.log('Nonce request:', { - // address, - // sessionID: req.sessionID, - // session: req.session - // }); - + if (!address) { return res.status(400).json({ error: 'Address is required' }); } - - // Генерируем случайный nonce - const nonce = crypto.randomBytes(32).toString('hex'); - - // Создаем сообщение для подписи - const message = `Sign this message to authenticate with DApp for Business. Nonce: ${nonce}`; - + + // Генерируем nonce + const nonce = crypto.randomBytes(16).toString('hex'); + // Сохраняем nonce в сессии - req.session.nonce = nonce; - req.session.pendingAddress = address; - - // Получаем IP-адрес клиента - const clientIP = req.headers['x-forwarded-for'] || req.connection.remoteAddress; - - // Сохраняем IP-адрес в сессии при генерации nonce - req.session.clientIP = clientIP; - - // Явно сохраняем сессию - req.session.save((err) => { - if (err) { - // Удалите или закомментируйте эти логи - // console.error('Error saving session:', err); - return res.status(500).json({ error: 'Failed to save session' }); - } - - // Удалите или закомментируйте - // console.log('Nonce saved in session:', { - // nonce, - // pendingAddress: address, - // sessionID: req.sessionID - // }); - - res.json({ message }); + req.session.authNonce = nonce; + req.session.pendingAddress = address.toLowerCase(); + + console.log('Сгенерирован nonce для адреса:', address); + console.log('Сессия после генерации nonce:', req.session); + + // Сохраняем сессию и ждем завершения + await new Promise((resolve, reject) => { + req.session.save((err) => { + if (err) { + console.error('Ошибка при сохранении сессии:', err); + reject(err); + } else { + resolve(); + } + }); }); + + // Проверяем, что nonce сохранился + console.log('Сессия после сохранения:', req.session); + + return res.json({ nonce }); } catch (error) { - // Удалите или закомментируйте эти логи - // console.error('Error generating nonce:', error); - logger.error('Error generating nonce:', error); - res.status(500).json({ error: 'Failed to generate nonce' }); + console.error('Ошибка при генерации nonce:', error); + return res.status(500).json({ error: 'Internal server error' }); } }); -// Маршрут для верификации подписи -router.post('/verify', authLimiter, async (req, res) => { +// Функция для проверки роли пользователя +async function checkUserRole(address, req) { try { - const { address, signature } = req.body; - - if (!address || !signature) { - return res.status(400).json({ error: 'Address and signature are required' }); + const lowerCaseAddress = address.toLowerCase(); + + // Проверяем наличие токена доступа в базе данных + const result = await db.query( + 'SELECT role FROM access_tokens WHERE LOWER(wallet_address) = $1 AND expires_at > NOW()', + [lowerCaseAddress] + ); + + if (result.rows.length > 0) { + // Если есть активный токен, проверяем роль + const role = result.rows[0].role; + return role === 'ADMIN'; } - - // Удалите или закомментируйте эти логи - // console.log('Verify request:', { - // address, - // signature, - // sessionID: req.sessionID, - // session: { - // nonce: req.session.nonce, - // pendingAddress: req.session.pendingAddress - // } - // }); - - // Получаем nonce из сессии - const nonce = req.session.nonce; - const pendingAddress = req.session.pendingAddress; - - if (!nonce || !pendingAddress) { - return res.status(400).json({ error: 'No pending authentication request' }); - } - - // Получаем IP-адрес клиента - const clientIP = req.headers['x-forwarded-for'] || req.connection.remoteAddress; - - // Проверяем, что IP-адрес совпадает - if (req.session.clientIP !== clientIP) { - return res.status(400).json({ error: 'IP address mismatch' }); - } - - // Проверяем, что адрес совпадает с тем, для которого был сгенерирован nonce - if (pendingAddress.toLowerCase() !== address.toLowerCase()) { - return res.status(400).json({ error: 'Address mismatch' }); - } - - // Создаем сообщение для проверки подписи - const message = `Sign this message to authenticate with DApp for Business. Nonce: ${nonce}`; - - // Восстанавливаем адрес из подписи - const recoveredAddress = ethers.verifyMessage(message, signature); - - // Проверяем, что восстановленный адрес совпадает с предоставленным - if (recoveredAddress.toLowerCase() !== address.toLowerCase()) { - return res.status(400).json({ error: 'Invalid signature' }); - } - - // Проверяем, существует ли пользователь в базе данных - const user = await db.query('SELECT * FROM users WHERE address = $1', [address]); - - let userId; - let isAdmin = false; - - if (user.rows.length === 0) { - // Если пользователь не существует, создаем его - const newUser = await db.query( - 'INSERT INTO users (address, created_at) VALUES ($1, NOW()) RETURNING id', - [address] - ); - userId = newUser.rows[0].id; - } else { - userId = user.rows[0].id; - isAdmin = user.rows[0].is_admin || false; - } - - // Устанавливаем состояние аутентификации в сессии - req.session.authenticated = true; - req.session.address = address; - req.session.isAdmin = isAdmin; - req.session.authType = 'wallet'; - req.session.userId = userId; - - // Удаляем nonce из сессии - delete req.session.nonce; - delete req.session.pendingAddress; - - // Явно сохраняем сессию - req.session.save((err) => { - if (err) { - // Удалите или закомментируйте эти логи - // console.error('Error saving session:', err); - return res.status(500).json({ error: 'Failed to save session' }); - } - - // Удалите или закомментируйте - // console.log('Authentication successful:', { - // address, - // isAdmin, - // sessionID: req.sessionID - // }); - - res.json({ - authenticated: true, - address, - isAdmin, - authType: 'wallet' - }); - }); + + // Если нет токена, проверяем адрес администратора из переменных окружения + const adminAddresses = (process.env.ADMIN_ADDRESSES || '') + .split(',') + .map((a) => a.toLowerCase()); + return adminAddresses.includes(lowerCaseAddress); } catch (error) { - console.error('Error verifying signature:', error); + console.error('Ошибка при проверке роли пользователя:', error); + return false; + } +} + +// Верификация подписи +router.post('/verify', async (req, res) => { + try { + const { address, signature, message, nonce } = req.body; - // Более подробная обработка ошибок - if (error.message.includes('invalid signature')) { - return res.status(400).json({ - error: 'Недействительная подпись', - message: 'Подпись не соответствует адресу. Пожалуйста, попробуйте снова.' - }); + console.log('Верификация подписи:', { address, signature, message }); + console.log('Сессия при верификации:', req.session); + + if (!address || !signature || !message) { + return res.status(400).json({ error: 'Address, signature and message are required' }); } - - if (error.message.includes('invalid address')) { - return res.status(400).json({ - error: 'Недействительный адрес', - message: 'Указанный адрес имеет неверный формат.' - }); + + // Проверяем наличие nonce в сессии + if (!req.session.authNonce || !req.session.pendingAddress) { + console.error('Сессия не содержит nonce или pendingAddress:', req.session); + + // Проверяем наличие nonce в заголовке + const headerNonce = req.headers['x-auth-nonce']; + if (headerNonce) { + console.log('Найден nonce в заголовке:', headerNonce); + req.session.authNonce = headerNonce; + req.session.pendingAddress = address.toLowerCase(); + } + + // Если в запросе есть nonce в сообщении, извлекаем его + let extractedNonce = null; + if (message) { + const match = message.match(/nonce: ([a-f0-9]+)/); + if (match && match[1]) { + extractedNonce = match[1]; + console.log('Извлечен nonce из сообщения:', extractedNonce); + + // Устанавливаем nonce в сессию + req.session.authNonce = extractedNonce; + req.session.pendingAddress = address.toLowerCase(); + + // Сохраняем сессию + await new Promise((resolve) => { + req.session.save((err) => { + if (err) console.error('Ошибка при сохранении сессии:', err); + resolve(); + }); + }); + } + } } - - res.status(500).json({ - error: 'Ошибка верификации подписи', - message: 'Не удалось проверить подпись. Пожалуйста, попробуйте снова позже.' - }); + + // Формируем ожидаемое сообщение + const expectedMessage = `Подтвердите вход в DApp for Business с nonce: ${req.session.authNonce}`; + + // Проверяем, что адрес совпадает с ожидаемым + if (req.session.pendingAddress && req.session.pendingAddress.toLowerCase() !== address.toLowerCase()) { + console.error('Адрес не совпадает с ожидаемым:', { + expected: req.session.pendingAddress, + received: address, + }); + return res.status(400).json({ error: 'Invalid address' }); + } + + let verified = false; + try { + // Проверяем подпись с использованием ethers.js + const recoveredAddress = ethers.verifyMessage(expectedMessage, signature); + + if (recoveredAddress.toLowerCase() !== address.toLowerCase()) { + console.error('Неверная подпись:', { + expected: address.toLowerCase(), + recovered: recoveredAddress.toLowerCase(), + }); + return res.status(400).json({ error: 'Invalid signature' }); + } + + verified = true; + console.log('Подпись успешно проверена'); + } catch (error) { + console.error('Ошибка при проверке подписи:', error); + return res.status(400).json({ error: 'Invalid signature format' }); + } + + // Если подпись верна, аутентифицируем пользователя + if (verified) { + // Найдем или создадим пользователя + const user = await findOrCreateUser(address, 'wallet'); + + // Обновляем сессию + req.session.authenticated = true; + req.session.address = address; + req.session.userId = user.id; + req.session.authType = 'wallet'; + req.session.isAdmin = user.is_admin; + req.session.role = user.role; + req.session.authChannel = 'web'; + req.session.language = req.body.language || 'en'; + + // Удаляем временные данные + delete req.session.authNonce; + delete req.session.pendingAddress; + + // Сохраняем сессию + await new Promise((resolve, reject) => { + req.session.save(err => { + if (err) { + console.error('Ошибка при сохранении сессии:', err); + reject(err); + } else { + resolve(); + } + }); + }); + + console.log('Аутентификация успешна:', { + address, + isAdmin: user.is_admin, + userId: user.id, + role: user.role + }); + + res.json({ + authenticated: true, + address, + isAdmin: user.is_admin, + role: user.role + }); + } else { + res.status(401).json({ error: 'Invalid signature' }); + } + } catch (error) { + console.error('Authentication error:', error); + res.status(500).json({ error: 'Internal server error' }); } }); -// Маршрут для проверки состояния аутентификации +// Проверка текущей сессии router.get('/check', (req, res) => { - // Удалите или закомментируйте эти логи - // console.log('Session check:', { - // session: req.session, - // authenticated: req.session.authenticated - // }); + console.log('Сессия при проверке:', req.session); - if (req.session.authenticated) { + // Если сессия существует и пользователь аутентифицирован + if (req.session && req.session.authenticated) { res.json({ authenticated: true, address: req.session.address, - isAdmin: req.session.isAdmin, - authType: req.session.authType + isAdmin: req.session.isAdmin || false, + role: req.session.role || 'USER' }); } else { res.json({ @@ -226,31 +243,49 @@ router.get('/check', (req, res) => { } }); -// Маршрут для выхода из системы +// Обработчик выхода из системы router.post('/logout', (req, res) => { - req.session.destroy(err => { - if (err) { - // Удалите или закомментируйте эти логи - // console.error('Error destroying session:', err); - return res.status(500).json({ error: 'Failed to logout' }); - } + try { + // Сохраняем sessionID перед удалением сессии + const sessionID = req.sessionID; - res.json({ success: true }); - }); + // Удаляем сессию из хранилища + req.session.destroy(async (err) => { + if (err) { + console.error('Ошибка при удалении сессии:', err); + return res.status(500).json({ error: 'Internal server error' }); + } + + try { + // Удаляем запись из базы данных + await db.query('DELETE FROM sessions WHERE sid = $1', [sessionID]); + console.log(`Сессия ${sessionID} удалена из базы данных`); + } catch (dbErr) { + console.error('Ошибка при удалении сессии из базы данных:', dbErr); + } + + // Очищаем cookie + res.clearCookie('dapp.sid'); + res.json({ success: true }); + }); + } catch (error) { + console.error('Logout error:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); // Маршрут для авторизации через Telegram router.get('/telegram', (req, res) => { // Генерируем случайный токен для авторизации const token = crypto.randomBytes(32).toString('hex'); - + // Сохраняем токен в сессии req.session.telegramToken = token; - + // Создаем URL для авторизации через Telegram const botName = process.env.TELEGRAM_BOT_NAME || 'YourBotName'; const authUrl = `https://t.me/${botName}?start=${token}`; - + res.json({ authUrl }); }); @@ -258,22 +293,22 @@ router.get('/telegram', (req, res) => { router.post('/email', async (req, res) => { try { const { email } = req.body; - + if (!email) { return res.status(400).json({ error: 'Email is required' }); } - + // Генерируем код подтверждения const verificationCode = Math.floor(100000 + Math.random() * 900000).toString(); - + // Сохраняем код в сессии req.session.emailVerificationCode = verificationCode; req.session.pendingEmail = email; - + // В реальном приложении здесь нужно отправить email с кодом подтверждения // Удалите или закомментируйте эти логи // console.log(`Verification code for ${email}: ${verificationCode}`); - + res.json({ success: true, message: 'Verification code sent' }); } catch (error) { // Удалите или закомментируйте эти логи @@ -287,35 +322,35 @@ router.post('/email', async (req, res) => { router.post('/email/verify', async (req, res) => { try { const { email, code } = req.body; - + if (!email || !code) { return res.status(400).json({ error: 'Email and code are required' }); } - + // Получаем код из сессии const verificationCode = req.session.emailVerificationCode; const pendingEmail = req.session.pendingEmail; - + if (!verificationCode || !pendingEmail) { return res.status(400).json({ error: 'No pending verification' }); } - + // Проверяем, что email совпадает с тем, для которого был сгенерирован код if (pendingEmail !== email) { return res.status(400).json({ error: 'Email mismatch' }); } - + // Проверяем код if (verificationCode !== code) { return res.status(400).json({ error: 'Invalid verification code' }); } - + // Проверяем, существует ли пользователь в базе данных const user = await db.query('SELECT * FROM users WHERE email = $1', [email]); - + let userId; let isAdmin = false; - + if (user.rows.length === 0) { // Если пользователь не существует, создаем его const newUser = await db.query( @@ -327,7 +362,7 @@ router.post('/email/verify', async (req, res) => { userId = user.rows[0].id; isAdmin = user.rows[0].is_admin || false; } - + // Устанавливаем состояние аутентификации в сессии req.session.isAuthenticated = true; req.session.authenticated = true; @@ -335,16 +370,16 @@ router.post('/email/verify', async (req, res) => { req.session.userId = userId; req.session.isAdmin = isAdmin; req.session.authType = 'email'; - + // Удаляем код из сессии delete req.session.emailVerificationCode; delete req.session.pendingEmail; - + res.json({ authenticated: true, address: email, isAdmin, - authType: 'email' + authType: 'email', }); } catch (error) { // Удалите или закомментируйте эти логи @@ -354,4 +389,176 @@ router.post('/email/verify', async (req, res) => { } }); -module.exports = { router }; \ No newline at end of file +// Добавляем маршрут для проверки прав доступа +router.get('/check-access', requireAuth, (req, res) => { + try { + // Получаем информацию о пользователе + const userData = { + address: req.session.address, + isAdmin: req.session.isAdmin || false, + roles: req.session.roles || [], + authenticated: true, + }; + + // Проверяем доступ к различным разделам + const access = { + dashboard: true, // Все аутентифицированные пользователи имеют доступ к панели управления + admin: userData.isAdmin, // Только администраторы имеют доступ к админке + contracts: userData.roles.includes('CONTRACT_MANAGER') || userData.isAdmin, + users: userData.roles.includes('USER_MANAGER') || userData.isAdmin, + }; + + res.json({ + user: userData, + access: access, + }); + } catch (error) { + console.error('Ошибка при проверке прав доступа:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Упрощенный маршрут для обновления сессии +router.post('/refresh-session', async (req, res) => { + try { + const { address } = req.body; + + if (!address) { + return res.status(400).json({ success: false, message: 'Адрес не указан' }); + } + + console.log(`Получен запрос на обновление сессии для адреса: ${address}`); + + // Проверяем, существует ли пользователь в базе данных + const userResult = await pool.query('SELECT * FROM users WHERE address = $1', [ + address.toLowerCase(), + ]); + + let user = null; + + if (userResult.rows.length > 0) { + user = userResult.rows[0]; + console.log(`Найден пользователь: ${user.id}`); + } else { + console.log(`Пользователь с адресом ${address} не найден`); + } + + // Обновляем сессию + req.session.authenticated = true; + req.session.address = address.toLowerCase(); + + if (user) { + req.session.userId = user.id; + req.session.isAdmin = user.is_admin || false; + req.session.role = user.is_admin ? 'ADMIN' : 'USER'; + } else { + // Если пользователь не найден в базе, проверяем через переменные окружения + const adminAddresses = (process.env.ADMIN_ADDRESSES || '') + .split(',') + .map((a) => a.toLowerCase()); + const isAdmin = adminAddresses.includes(address.toLowerCase()); + req.session.isAdmin = isAdmin; + req.session.role = isAdmin ? 'ADMIN' : 'USER'; + } + + // Сохраняем сессию + await new Promise((resolve, reject) => { + req.session.save((err) => { + if (err) { + console.error('Ошибка при сохранении сессии:', err); + reject(err); + } else { + resolve(); + } + }); + }); + + console.log('Сессия обновлена:', req.session); + + return res.json({ + success: true, + message: 'Сессия обновлена', + user: { + id: user ? user.id : null, + address: address.toLowerCase(), + isAdmin: req.session.isAdmin, + role: req.session.role, + }, + }); + } catch (error) { + console.error('Ошибка при обновлении сессии:', error); + return res.status(500).json({ success: false, message: 'Ошибка сервера' }); + } +}); + +// Маршрут для обновления статуса администратора +router.post('/update-admin-status', async (req, res) => { + try { + const { address, isAdmin } = req.body; + + if (!address) { + return res.status(400).json({ error: 'Address is required' }); + } + + console.log(`Запрос на обновление статуса администратора для адреса ${address} на ${isAdmin}`); + + // Проверяем, существует ли пользователь + const userResult = await db.query('SELECT * FROM users WHERE address = $1', [ + address.toLowerCase(), + ]); + + if (userResult.rows.length === 0) { + // Если пользователь не найден, создаем его + await db.query('INSERT INTO users (address, is_admin, created_at) VALUES ($1, $2, NOW())', [ + address.toLowerCase(), + isAdmin, + ]); + + console.log( + `Создан новый пользователь с адресом ${address} и статусом администратора ${isAdmin}` + ); + } else { + // Если пользователь найден, обновляем его статус + await db.query('UPDATE users SET is_admin = $1 WHERE address = $2', [ + isAdmin, + address.toLowerCase(), + ]); + + console.log( + `Обновлен статус администратора для пользователя с адресом ${address} на ${isAdmin}` + ); + } + + res.json({ success: true }); + } catch (error) { + console.error('Ошибка при обновлении статуса администратора:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Маршрут для проверки структуры таблицы users +router.get('/check-db-structure', async (req, res) => { + try { + // Получаем информацию о таблице users + const tableInfo = await pool.query(` + SELECT column_name, data_type + FROM information_schema.columns + WHERE table_name = 'users' + `); + + res.json({ + tableStructure: tableInfo.rows, + }); + } catch (error) { + console.error('Ошибка при получении структуры базы данных:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Добавьте обработку ошибок +router.use((err, req, res, next) => { + console.error('Auth route error:', err); + res.status(500).json({ success: false, message: 'Ошибка сервера' }); +}); + +module.exports = { router }; diff --git a/backend/routes/chat.js b/backend/routes/chat.js index 50e43b5..510f43d 100644 --- a/backend/routes/chat.js +++ b/backend/routes/chat.js @@ -1,185 +1,200 @@ const express = require('express'); const router = express.Router(); -const { checkAccess } = require('../utils/access-check'); -const { createOllamaChain, directOllamaQuery, checkOllamaAvailability, ChatOllama } = require('../services/ollama'); +const { ChatOllama } = require('@langchain/ollama'); const { getVectorStore } = require('../services/vectorStore'); +const db = require('../db'); +const { requireAuth, requireAdmin } = require('../middleware/auth'); +const logger = require('../utils/logger'); -// Хранилище истории чатов -const chatHistory = {}; - -// Обработка чат-сообщений с проверкой сессии -router.post('/', async (req, res) => { +// Обработчик сообщений чата +router.post('/message', requireAuth, async (req, res) => { try { - console.log('Получен запрос в chat.js:', { - body: req.body, - session: req.session ? { - id: req.sessionID, - address: req.session.address, - isAuthenticated: req.session.isAuthenticated, - authenticated: req.session.authenticated - } : null, - cookies: req.cookies, - headers: { - cookie: req.headers.cookie, - origin: req.headers.origin, - referer: req.headers.referer, - 'content-type': req.headers['content-type'] - } - }); + const { message, language = 'ru' } = req.body; - // Проверяем, что тело запроса правильно парсится - if (req.headers['content-type'] === 'application/json') { - console.log('JSON body:', JSON.stringify(req.body)); - } else { - console.log('Non-JSON body:', req.body); + // Проверка аутентификации + if (!req.session || !req.session.authenticated) { + return res.status(401).json({ error: 'Требуется аутентификация' }); } - // ВАЖНО: Принимаем любой адрес из запроса без проверки сессии - const userAddress = req.body.address || '0xdefault'; - - const { message } = req.body; - - if (!message) { - return res.status(400).json({ error: 'Message is required' }); + console.log(`Получено сообщение: ${message}, язык: ${language}`); + + // Определяем язык сообщения, если не указан явно + let detectedLanguage = language; + if (!language || language === 'auto') { + // Простая эвристика для определения языка + const cyrillicPattern = /[а-яА-ЯёЁ]/; + detectedLanguage = cyrillicPattern.test(message) ? 'ru' : 'en'; } - console.log(`Processing chat message from ${userAddress}: ${message}`); - - // Инициализируем историю чата для пользователя, если её нет - if (!chatHistory[userAddress]) { - chatHistory[userAddress] = []; + // Формируем системный промпт в зависимости от языка + let systemPrompt = ''; + if (detectedLanguage === 'ru') { + systemPrompt = 'Вы - полезный ассистент. Отвечайте на русском языке.'; + } else { + systemPrompt = 'You are a helpful assistant. Respond in English.'; } + + // Отправляем запрос к Ollama с указанием языка + console.log(`Отправка запроса к Ollama (модель: ${process.env.OLLAMA_MODEL || 'mistral'}, язык: ${detectedLanguage}): ${message}`); - // Временно возвращаем тестовый ответ для отладки - const responseText = `Тестовый ответ на сообщение: ${message}`; - - // Сохраняем историю чата - chatHistory[userAddress].push({ - type: 'human', - text: message + // Проверяем доступность Ollama + console.log('Проверка доступности Ollama...'); + try { + const response = await fetch(`${process.env.OLLAMA_BASE_URL || 'http://localhost:11434'}/api/tags`); + const data = await response.json(); + console.log('Ollama доступен. Доступные модели:'); + data.models.forEach(model => { + console.log(`- ${model.name}`); + }); + } catch (error) { + console.error('Ошибка при проверке доступности Ollama:', error); + return res.status(500).json({ error: 'Сервис Ollama недоступен' }); + } + + // Создаем экземпляр ChatOllama + const chat = new ChatOllama({ + baseUrl: process.env.OLLAMA_BASE_URL || 'http://localhost:11434', + model: process.env.OLLAMA_MODEL || 'mistral', + system: systemPrompt }); + + console.log('Отправка запроса к Ollama...'); - chatHistory[userAddress].push({ - type: 'ai', - text: responseText + // Получаем ответ от модели + let aiResponse; + try { + const response = await chat.invoke(message); + aiResponse = response.content; + console.log('Ответ AI:', aiResponse); + } catch (error) { + console.error('Ошибка при вызове ChatOllama:', error); + + // Альтернативный метод запроса через прямой API + try { + console.log('Пробуем альтернативный метод запроса...'); + const response = await fetch(`${process.env.OLLAMA_BASE_URL || 'http://localhost:11434'}/api/generate`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + model: process.env.OLLAMA_MODEL || 'mistral', + prompt: message, + system: systemPrompt, + stream: false + }), + }); + + const data = await response.json(); + aiResponse = data.response; + console.log('Ответ AI (альтернативный метод):', aiResponse); + } catch (fallbackError) { + console.error('Ошибка при использовании альтернативного метода:', fallbackError); + throw error; // Выбрасываем исходную ошибку + } + } + + // Отправляем ответ клиенту + res.json({ + reply: aiResponse, + language: detectedLanguage }); - - return res.json({ response: responseText }); } catch (error) { - console.error('Подробная ошибка:', error.stack); - console.error('Chat error:', error); - res.status(500).json({ - error: "Извините, произошла ошибка при обработке вашего запроса. Пожалуйста, попробуйте позже." - }); + logger.error('Error processing message:', error); + res.status(500).json({ error: 'Внутренняя ошибка сервера' }); } }); -// Добавьте новый эндпоинт для проверки сессии -router.get('/check-session', (req, res) => { +// Добавьте этот маршрут для проверки доступных моделей +router.get('/models', async (req, res) => { try { - console.log('Проверка сессии в chat.js:', { - sessionID: req.sessionID, - session: req.session ? { - isAuthenticated: req.session.isAuthenticated, - authenticated: req.session.authenticated, - address: req.session.address - } : null, - cookies: req.cookies, - headers: { - cookie: req.headers.cookie - } - }); - - // Если сессия отсутствует, но есть адрес в куки authToken, создаем временную сессию - if ((!req.session || (!req.session.isAuthenticated && !req.session.authenticated)) && req.cookies.authToken) { - console.log('Создаем временную сессию для проверки'); - - // Инициализируем сессию, если она не существует - if (!req.session) { - req.session = {}; - } - - req.session.isAuthenticated = true; - req.session.authenticated = true; - req.session.isAdmin = true; - - return res.json({ - success: true, - message: 'Temporary session created', - isAdmin: true - }); - } - - if (!req.session) { - return res.status(401).json({ error: 'No session' }); - } - - if (!req.session.isAuthenticated && !req.session.authenticated) { - return res.status(401).json({ error: 'Unauthorized' }); - } - + const ollama = new Ollama(); + const models = await ollama.list(); + res.json({ success: true, - address: req.session.address, - isAdmin: req.session.isAdmin + models: models.models.map((model) => model.name), }); } catch (error) { - console.error('Ошибка при проверке сессии:', error); - res.status(500).json({ error: 'Internal server error' }); + console.error('Ошибка при получении списка моделей:', error); + res.status(500).json({ success: false, message: 'Ошибка сервера' }); } }); -// Добавьте новый эндпоинт для прямой отправки сообщений в Ollama -router.post('/ollama', async (req, res) => { +// Маршрут для получения истории диалогов (доступен пользователю для своих диалогов) +router.get('/history', requireAuth, async (req, res) => { try { - const { message, model = 'mistral' } = req.body; + const userId = req.session.userId; + const { limit = 50, offset = 0 } = req.query; - console.log(`Отправка сообщения в Ollama (${model}):`, message); + const result = await db.query(` + SELECT id, channel, sender_type, content, metadata, created_at + FROM chat_history + WHERE user_id = $1 + ORDER BY created_at DESC + LIMIT $2 OFFSET $3 + `, [userId, limit, offset]); - if (!message) { - return res.status(400).json({ error: 'Message is required' }); + res.json(result.rows); + } catch (error) { + logger.error('Error fetching chat history:', error); + res.status(500).json({ error: 'Внутренняя ошибка сервера' }); + } +}); + +// Маршрут для получения всех диалогов (только для админов) +router.get('/admin/history', requireAdmin, async (req, res) => { + try { + const { limit = 50, offset = 0, userId } = req.query; + + let query = ` + SELECT ch.id, ch.user_id, u.username, ch.channel, + ch.sender_type, ch.content, ch.metadata, ch.created_at + FROM chat_history ch + LEFT JOIN users u ON ch.user_id = u.id + `; + + const params = []; + let paramIndex = 1; + + if (userId) { + query += ` WHERE ch.user_id = $${paramIndex}`; + params.push(userId); + paramIndex++; } - // Используем функцию directOllamaQuery вместо создания нового экземпляра ChatOllama - const result = await directOllamaQuery(message, model); + query += ` ORDER BY ch.created_at DESC LIMIT $${paramIndex} OFFSET $${paramIndex + 1}`; + params.push(limit, offset); - console.log('Ответ от Ollama:', result); + const result = await db.query(query, params); - // Возвращаем ответ клиенту - res.json({ - response: result, - model: model - }); + res.json(result.rows); } catch (error) { - console.error('Ошибка при отправке сообщения в Ollama:', error); - res.status(500).json({ - error: "Ошибка при отправке сообщения в Ollama. Убедитесь, что сервер Ollama запущен." - }); + logger.error('Error fetching admin chat history:', error); + res.status(500).json({ error: 'Внутренняя ошибка сервера' }); } }); -// Проверьте, что маршрут правильно настроен -router.post('/message', async (req, res) => { +// Сохранение сообщения в историю чата +router.post('/message', requireAuth, async (req, res) => { try { - const { message } = req.body; + const { content, channel = 'web', metadata = {} } = req.body; + const userId = req.session.userId; - if (!message) { - return res.status(400).json({ error: 'Message is required' }); - } + // Сохранение сообщения пользователя + const userMessageResult = await db.query(` + INSERT INTO chat_history (user_id, channel, sender_type, content, metadata) + VALUES ($1, $2, 'user', $3, $4) + RETURNING id + `, [userId, channel, content, metadata]); - console.log('Получено сообщение:', message); + const messageId = userMessageResult.rows[0].id; - // Здесь ваш код обработки сообщения - // ... - - // Временный ответ для тестирования - res.json({ - response: `Это тестовый ответ на ваше сообщение: "${message}". Сервер работает.` - }); + res.json({ success: true, messageId }); } catch (error) { - console.error('Error processing message:', error); - res.status(500).json({ error: 'Internal server error' }); + logger.error('Error saving chat message:', error); + res.status(500).json({ error: 'Внутренняя ошибка сервера' }); } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/contracts.js b/backend/routes/contracts.js index 3bfb99d..213f17b 100644 --- a/backend/routes/contracts.js +++ b/backend/routes/contracts.js @@ -4,29 +4,29 @@ const { requireRole } = require('../middleware/auth'); // Получение информации о контрактах router.get('/', (req, res) => { - res.json({ + res.json({ message: 'Contracts API endpoint', contracts: [ { name: 'AccessToken', - address: process.env.ACCESS_TOKEN_ADDRESS - } - ] + address: process.env.ACCESS_TOKEN_ADDRESS, + }, + ], }); }); // Защищенный эндпоинт для получения детальной информации о контрактах router.get('/details', requireRole('ADMIN'), (req, res) => { - res.json({ + res.json({ message: 'Contract details endpoint', contracts: [ { name: 'AccessToken', address: process.env.ACCESS_TOKEN_ADDRESS, - network: process.env.ETHEREUM_NETWORK_URL.includes('sepolia') ? 'Sepolia' : 'Unknown' - } - ] + network: process.env.ETHEREUM_NETWORK_URL.includes('sepolia') ? 'Sepolia' : 'Unknown', + }, + ], }); }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/conversations.js b/backend/routes/conversations.js new file mode 100644 index 0000000..e69de29 diff --git a/backend/routes/debug.js b/backend/routes/debug.js index 7801057..7e3f7b4 100644 --- a/backend/routes/debug.js +++ b/backend/routes/debug.js @@ -1,189 +1,33 @@ const express = require('express'); const router = express.Router(); +const db = require('../db'); -// Эндпоинт для отладки сессий +// Маршрут для проверки состояния сервера +router.get('/status', (req, res) => { + res.json({ + status: 'ok', + uptime: process.uptime(), + timestamp: Date.now(), + }); +}); + +// Маршрут для проверки сессии router.get('/session', (req, res) => { + res.json({ + session: req.session, + authenticated: req.session.authenticated || false, + }); +}); + +// Маршрут для проверки содержимого таблицы session +router.get('/sessions', async (req, res) => { try { - console.log('Отладка сессии:', { - sessionID: req.sessionID, - session: req.session ? { - isAuthenticated: req.session.isAuthenticated, - authenticated: req.session.authenticated, - address: req.session.address, - isAdmin: req.session.isAdmin - } : null, - cookies: req.cookies, - headers: { - cookie: req.headers.cookie - } - }); - - res.json({ - sessionID: req.sessionID, - session: req.session ? { - isAuthenticated: req.session.isAuthenticated, - authenticated: req.session.authenticated, - address: req.session.address, - isAdmin: req.session.isAdmin - } : null, - cookies: req.cookies - }); + const result = await db.query('SELECT * FROM session'); + res.json(result.rows); } catch (error) { - console.error('Ошибка при отладке сессии:', error); + console.error('Ошибка при получении данных из таблицы session:', error); res.status(500).json({ error: 'Internal server error' }); } }); -// Эндпоинт для создания тестовой сессии -router.post('/create-session', (req, res) => { - const { address } = req.body; - - if (!address) { - return res.status(400).json({ error: 'Address is required' }); - } - - // Инициализируем сессию, если она не существует - if (!req.session) { - req.session = {}; - } - - req.session.isAuthenticated = true; - req.session.authenticated = true; - req.session.address = address.toLowerCase(); - req.session.isAdmin = true; - - // Сохраняем сессию - req.session.save((err) => { - if (err) { - console.error('Ошибка сохранения тестовой сессии:', err); - return res.status(500).json({ error: 'Session save error' }); - } - - console.log('Тестовая сессия создана:', { - sessionID: req.sessionID, - session: { - isAuthenticated: req.session.isAuthenticated, - authenticated: req.session.authenticated, - address: req.session.address, - isAdmin: req.session.isAdmin - } - }); - - res.cookie('authToken', 'true', { - maxAge: 86400000, - httpOnly: false, - secure: false, - sameSite: 'lax', - path: '/' - }); - - res.json({ - success: true, - sessionID: req.sessionID, - address: req.session.address, - isAdmin: req.session.isAdmin - }); - }); -}); - -// Тестовый эндпоинт для отправки сообщений без проверки сессии -router.post('/test-chat', (req, res) => { - try { - const { message, address } = req.body; - - console.log('Тестовый чат-запрос:', { - message, - address, - headers: { - cookie: req.headers.cookie, - 'content-type': req.headers['content-type'] - }, - cookies: req.cookies, - session: req.session ? { - isAuthenticated: req.session.isAuthenticated, - authenticated: req.session.authenticated, - address: req.session.address, - isAdmin: req.session.isAdmin - } : null - }); - - if (!message) { - return res.status(400).json({ error: 'Message is required' }); - } - - // Возвращаем тестовый ответ - res.json({ - response: `Тестовый ответ на сообщение: ${message}`, - receivedAddress: address, - sessionAddress: req.session?.address - }); - } catch (error) { - console.error('Ошибка в тестовом чате:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Тестовый эндпоинт для проверки соединения -router.get('/ping', (req, res) => { - res.json({ - message: 'pong', - timestamp: new Date().toISOString(), - server: { - port: process.env.PORT || 8080, - address: req.socket.localAddress, - hostname: require('os').hostname() - } - }); -}); - -// Тестовый эндпоинт для проверки Ollama -router.get('/ollama-test', async (req, res) => { - try { - const { directOllamaQuery } = require('../services/ollama'); - - // Тестовый запрос к Ollama - const result = await directOllamaQuery('Привет, как дела?', 'mistral'); - - res.json({ - success: true, - response: result, - model: 'mistral' - }); - } catch (error) { - console.error('Ошибка при тестировании Ollama:', error); - res.status(500).json({ - success: false, - error: error.message || 'Ошибка при тестировании Ollama' - }); - } -}); - -// Тестовый эндпоинт для проверки доступности Ollama -router.get('/ollama-status', async (req, res) => { - try { - const { checkOllamaAvailability } = require('../services/ollama'); - - // Проверяем доступность Ollama - const isAvailable = await checkOllamaAvailability(); - - if (isAvailable) { - res.json({ - status: 'ok', - message: 'Ollama доступен' - }); - } else { - res.status(503).json({ - status: 'error', - message: 'Ollama недоступен' - }); - } - } catch (error) { - console.error('Ошибка при проверке доступности Ollama:', error); - res.status(500).json({ - status: 'error', - message: error.message || 'Ошибка при проверке доступности Ollama' - }); - } -}); - -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/health.js b/backend/routes/health.js index 1f9f61c..8784e99 100644 --- a/backend/routes/health.js +++ b/backend/routes/health.js @@ -6,11 +6,11 @@ router.get('/', async (req, res) => { try { // Проверка соединения с базой данных const dbResult = await db.query('SELECT NOW()'); - + // Проверка состояния сервера const memoryUsage = process.memoryUsage(); const uptime = process.uptime(); - + res.json({ status: 'ok', timestamp: new Date(), @@ -18,19 +18,19 @@ router.get('/', async (req, res) => { memory: { rss: Math.round(memoryUsage.rss / 1024 / 1024) + 'MB', heapTotal: Math.round(memoryUsage.heapTotal / 1024 / 1024) + 'MB', - heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024) + 'MB' + heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024) + 'MB', }, database: { connected: true, - timestamp: dbResult.rows[0].now - } + timestamp: dbResult.rows[0].now, + }, }); } catch (error) { res.status(500).json({ status: 'error', - error: error.message + error: error.message, }); } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/identities.js b/backend/routes/identities.js index aaffa5b..bfc051f 100644 --- a/backend/routes/identities.js +++ b/backend/routes/identities.js @@ -6,7 +6,7 @@ const { Pool } = require('pg'); // Подключение к БД const pool = new Pool({ connectionString: process.env.DATABASE_URL, - ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false + ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, }); // Middleware для проверки аутентификации @@ -21,20 +21,19 @@ function requireAuth(req, res, next) { router.get('/', requireAuth, async (req, res) => { try { // Получаем ID пользователя по Ethereum-адресу - const result = await pool.query( - 'SELECT id FROM users WHERE address = $1', - [req.session.address] - ); - + const result = await pool.query('SELECT id FROM users WHERE address = $1', [ + req.session.address, + ]); + if (result.rows.length === 0) { return res.status(404).json({ error: 'User not found' }); } - + const userId = result.rows[0].id; - + // Получаем все идентификаторы пользователя const identities = await getUserIdentities(userId); - + res.json({ identities }); } catch (error) { console.error('Error getting user identities:', error); @@ -46,25 +45,24 @@ router.get('/', requireAuth, async (req, res) => { router.delete('/:type/:value', requireAuth, async (req, res) => { try { const { type, value } = req.params; - + // Получаем ID пользователя по Ethereum-адресу - const result = await pool.query( - 'SELECT id FROM users WHERE address = $1', - [req.session.address] - ); - + const result = await pool.query('SELECT id FROM users WHERE address = $1', [ + req.session.address, + ]); + if (result.rows.length === 0) { return res.status(404).json({ error: 'User not found' }); } - + const userId = result.rows[0].id; - + // Удаляем идентификатор await pool.query( 'DELETE FROM user_identities WHERE user_id = $1 AND identity_type = $2 AND identity_value = $3', [userId, type, value] ); - + res.json({ success: true }); } catch (error) { console.error('Error deleting user identity:', error); @@ -72,4 +70,4 @@ router.delete('/:type/:value', requireAuth, async (req, res) => { } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/kanban.js b/backend/routes/kanban.js deleted file mode 100644 index 1b179a8..0000000 --- a/backend/routes/kanban.js +++ /dev/null @@ -1,340 +0,0 @@ -const express = require('express'); -const router = express.Router(); -const { Pool } = require('pg'); -const pool = new Pool({ - connectionString: process.env.DATABASE_URL, - ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false -}); - -// Middleware для проверки аутентификации -function requireAuth(req, res, next) { - if (!req.session || (!req.session.isAuthenticated && !req.session.authenticated)) { - return res.status(401).json({ error: 'Unauthorized' }); - } - next(); -} - -// Получение всех досок пользователя -router.get('/boards', async (req, res) => { - try { - // Для разработки: если сессия не содержит адрес, используем тестовый - const userAddress = (req.session.address || '0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b').toLowerCase(); - - console.log('Запрос досок для адреса:', userAddress); - - // Проверяем, существует ли пользователь - const userResult = await pool.query( - 'SELECT id FROM users WHERE address = $1', - [userAddress] - ); - - console.log('Результат запроса пользователя:', userResult.rows); - - if (userResult.rows.length === 0) { - console.log('Пользователь не найден, создаем нового'); - // Если пользователь не найден, создаем его - const newUserResult = await pool.query( - 'INSERT INTO users (address, created_at) VALUES ($1, NOW()) RETURNING id', - [userAddress] - ); - console.log('Создан новый пользователь:', newUserResult.rows); - } - - // Получаем доски пользователя - const ownBoardsQuery = 'SELECT kb.* FROM kanban_boards kb ' + - 'JOIN users u ON kb.owner_id = u.id ' + - 'WHERE u.address = $1 ' + - 'ORDER BY kb.updated_at DESC'; - - console.log('Запрос досок пользователя:', ownBoardsQuery); - - const ownBoardsResult = await pool.query(ownBoardsQuery, [userAddress]); - - console.log('Результат запроса досок пользователя:', ownBoardsResult.rows); - - // Получаем доски, к которым у пользователя есть доступ - const sharedBoardsResult = await pool.query( - 'SELECT kb.* FROM kanban_boards kb ' + - 'JOIN kanban_board_access kba ON kb.id = kba.board_id ' + - 'JOIN users u1 ON kba.user_id = u1.id ' + - 'JOIN users u2 ON kb.owner_id = u2.id ' + - 'WHERE u1.address = $1 AND u2.address != $1 ' + - 'ORDER BY kb.updated_at DESC', - [userAddress] - ); - - // Получаем публичные доски - const publicBoardsResult = await pool.query( - 'SELECT kb.* FROM kanban_boards kb ' + - 'JOIN users u ON kb.owner_id = u.id ' + - 'WHERE kb.is_public = true AND u.address != $1 ' + - 'AND NOT EXISTS (' + - ' SELECT 1 FROM kanban_board_access kba ' + - ' JOIN users u2 ON kba.user_id = u2.id ' + - ' WHERE kba.board_id = kb.id AND u2.address = $1' + - ') ' + - 'ORDER BY kb.updated_at DESC', - [userAddress] - ); - - res.json({ - ownBoards: ownBoardsResult.rows, - sharedBoards: sharedBoardsResult.rows, - publicBoards: publicBoardsResult.rows - }); - } catch (error) { - console.error('Error fetching boards:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Создание новой доски -router.post('/boards', requireAuth, async (req, res) => { - try { - const { title, description, isPublic } = req.body; - - // Получаем ID пользователя - let userResult = await pool.query( - 'SELECT id FROM users WHERE address = $1', - [req.session.address] - ); - - let userId; - - if (userResult.rows.length === 0) { - // Если пользователь не найден, создаем его - const newUserResult = await pool.query( - 'INSERT INTO users (address, created_at, preferred_language) VALUES ($1, NOW(), $2) RETURNING id', - [req.session.address, 'ru'] - ); - - userId = newUserResult.rows[0].id; - } else { - userId = userResult.rows[0].id; - } - - // Создаем новую доску - const result = await pool.query( - `INSERT INTO kanban_boards (title, description, owner_id, is_public, created_at, updated_at) - VALUES ($1, $2, $3, $4, NOW(), NOW()) - RETURNING *`, - [title, description, userId, isPublic] - ); - - // Создаем стандартные колонки - const columns = ['Backlog', 'In Progress', 'Review', 'Done']; - for (let i = 0; i < columns.length; i++) { - await pool.query( - `INSERT INTO kanban_columns (board_id, title, position, created_at, updated_at) - VALUES ($1, $2, $3, NOW(), NOW())`, - [result.rows[0].id, columns[i], i] - ); - } - - res.status(201).json(result.rows[0]); - } catch (error) { - console.error('Error creating kanban board:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Получение конкретной доски со всеми колонками и карточками -router.get('/boards/:id', requireAuth, async (req, res) => { - try { - const boardId = req.params.id; - - // Получаем ID пользователя - let userResult = await pool.query( - 'SELECT id FROM users WHERE address = $1', - [req.session.address] - ); - - let userId; - - if (userResult.rows.length === 0) { - // Если пользователь не найден, создаем его - const newUserResult = await pool.query( - 'INSERT INTO users (address, created_at, preferred_language) VALUES ($1, NOW(), $2) RETURNING id', - [req.session.address, 'ru'] - ); - - userId = newUserResult.rows[0].id; - } else { - userId = userResult.rows[0].id; - } - - // Проверяем доступ к доске - const boardResult = await pool.query( - 'SELECT * FROM kanban_boards WHERE id = $1', - [boardId] - ); - - if (boardResult.rows.length === 0) { - return res.status(404).json({ error: 'Board not found' }); - } - - const board = boardResult.rows[0]; - - // Проверяем, имеет ли пользователь доступ к доске - if (board.owner_id !== userId && !board.is_public) { - const accessResult = await pool.query( - 'SELECT * FROM kanban_board_access WHERE board_id = $1 AND user_id = $2', - [boardId, userId] - ); - - if (accessResult.rows.length === 0) { - return res.status(403).json({ error: 'Access denied' }); - } - } - - // Получаем колонки доски - const columnsResult = await pool.query( - 'SELECT * FROM kanban_columns WHERE board_id = $1 ORDER BY position', - [boardId] - ); - - // Получаем карточки для всех колонок - const cardsResult = await pool.query( - `SELECT kc.*, u.address as assigned_address - FROM kanban_cards kc - LEFT JOIN users u ON kc.assigned_to = u.id - WHERE kc.column_id IN ( - SELECT id FROM kanban_columns WHERE board_id = $1 - ) - ORDER BY kc.position`, - [boardId] - ); - - // Группируем карточки по колонкам - const columns = columnsResult.rows.map(column => { - const cards = cardsResult.rows.filter(card => card.column_id === column.id); - return { - ...column, - cards - }; - }); - - res.json({ - ...board, - columns - }); - } catch (error) { - console.error('Error getting kanban board:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Добавление колонки к доске -router.post('/boards/:boardId/columns', requireAuth, async (req, res) => { - try { - const { boardId } = req.params; - const { title, wipLimit } = req.body; - - // Проверяем, существует ли доска - const boardResult = await pool.query( - 'SELECT * FROM kanban_boards WHERE id = $1', - [boardId] - ); - - if (boardResult.rows.length === 0) { - return res.status(404).json({ error: 'Board not found' }); - } - - // Получаем максимальную позицию колонок - const positionResult = await pool.query( - 'SELECT MAX(position) as max_position FROM kanban_columns WHERE board_id = $1', - [boardId] - ); - - const position = positionResult.rows[0].max_position ? positionResult.rows[0].max_position + 1 : 0; - - // Создаем новую колонку - const result = await pool.query( - `INSERT INTO kanban_columns (board_id, title, position, wip_limit, created_at, updated_at) - VALUES ($1, $2, $3, $4, NOW(), NOW()) - RETURNING *`, - [boardId, title, position, wipLimit] - ); - - res.status(201).json(result.rows[0]); - } catch (error) { - console.error('Error creating column:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Получение колонок доски -router.get('/boards/:boardId/columns', requireAuth, async (req, res) => { - try { - const { boardId } = req.params; - - const result = await pool.query( - 'SELECT * FROM kanban_columns WHERE board_id = $1 ORDER BY position', - [boardId] - ); - - res.json(result.rows); - } catch (error) { - console.error('Error getting columns:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Создание карточки -router.post('/cards', requireAuth, async (req, res) => { - try { - const { title, description, columnId, dueDate } = req.body; - - // Получаем ID пользователя - let userResult = await pool.query( - 'SELECT id FROM users WHERE address = $1', - [req.session.address] - ); - - let userId; - - if (userResult.rows.length === 0) { - // Если пользователь не найден, создаем его - const newUserResult = await pool.query( - 'INSERT INTO users (address, created_at, preferred_language) VALUES ($1, NOW(), $2) RETURNING id', - [req.session.address, 'ru'] - ); - - userId = newUserResult.rows[0].id; - } else { - userId = userResult.rows[0].id; - } - - // Получаем максимальную позицию карточек в колонке - const positionResult = await pool.query( - 'SELECT MAX(position) as max_position FROM kanban_cards WHERE column_id = $1', - [columnId] - ); - - const position = positionResult.rows[0].max_position ? positionResult.rows[0].max_position + 1 : 0; - - // Создаем новую карточку - const result = await pool.query( - `INSERT INTO kanban_cards (column_id, title, description, position, due_date, created_by, created_at, updated_at) - VALUES ($1, $2, $3, $4, $5, $6, NOW(), NOW()) - RETURNING *`, - [columnId, title, description, position, dueDate, userId] - ); - - // Получаем информацию о пользователе для отображения - const cardWithUser = { - ...result.rows[0], - assigned_address: null - }; - - res.status(201).json(cardWithUser); - } catch (error) { - console.error('Error creating card:', error); - res.status(500).json({ error: 'Internal server error' }); - } -}); - -// Добавляем остальные маршруты для работы с колонками, карточками и т.д. -// ... - -module.exports = router; \ No newline at end of file diff --git a/backend/routes/messages.js b/backend/routes/messages.js new file mode 100644 index 0000000..2fb63d3 --- /dev/null +++ b/backend/routes/messages.js @@ -0,0 +1,246 @@ +const express = require('express'); +const router = express.Router(); +const { pool } = require('../db'); +const { requireAuth } = require('../middleware/auth'); +const { processMessage, getUserInfo } = require('../services/ai-assistant'); + +// Получение списка диалогов пользователя +router.get('/conversations', requireAuth, async (req, res) => { + try { + const userId = req.session.userId; + + const result = await pool.query( + `SELECT * FROM conversation_view + WHERE user_id = $1 + ORDER BY updated_at DESC`, + [userId] + ); + + res.json(result.rows); + } catch (error) { + console.error('Error fetching conversations:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Получение сообщений диалога +router.get('/conversations/:id/messages', requireAuth, async (req, res) => { + try { + const userId = req.session.userId; + const conversationId = req.params.id; + + // Проверка доступа к диалогу + const conversationCheck = await pool.query( + 'SELECT id FROM conversations WHERE id = $1 AND user_id = $2', + [conversationId, userId] + ); + + if (conversationCheck.rows.length === 0) { + return res.status(403).json({ error: 'Access denied' }); + } + + const result = await pool.query( + `SELECT * FROM message_view + WHERE conversation_id = $1 + ORDER BY created_at ASC`, + [conversationId] + ); + + res.json(result.rows); + } catch (error) { + console.error('Error fetching messages:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Отправка сообщения +router.post('/conversations/:id/messages', requireAuth, async (req, res) => { + try { + const userId = req.session.userId; + const conversationId = req.params.id; + const { content } = req.body; + + if (!content || content.trim() === '') { + return res.status(400).json({ error: 'Message content is required' }); + } + + // Проверка доступа к диалогу + const conversationCheck = await pool.query( + 'SELECT id FROM conversations WHERE id = $1 AND user_id = $2', + [conversationId, userId] + ); + + if (conversationCheck.rows.length === 0) { + return res.status(403).json({ error: 'Access denied' }); + } + + // Обновление времени последней активности диалога + await pool.query('UPDATE conversations SET updated_at = NOW() WHERE id = $1', [conversationId]); + + // Сохранение сообщения пользователя + const userMessageResult = await pool.query( + `INSERT INTO messages + (conversation_id, sender_type, sender_id, content, channel) + VALUES ($1, 'user', $2, $3, 'web') + RETURNING *`, + [conversationId, userId, content] + ); + + // Получение информации о пользователе для ИИ + const userInfo = await getUserInfo(userId); + + // Обработка сообщения ИИ-ассистентом + const aiResponse = await processMessage(userId, content, userInfo.language || 'ru'); + + // Сохранение ответа ИИ + const aiMessageResult = await pool.query( + `INSERT INTO messages + (conversation_id, sender_type, content, channel) + VALUES ($1, 'ai', $2, 'web') + RETURNING *`, + [conversationId, aiResponse] + ); + + res.json({ + userMessage: userMessageResult.rows[0], + aiMessage: aiMessageResult.rows[0], + }); + } catch (error) { + console.error('Error sending message:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Создание нового диалога +router.post('/conversations', requireAuth, async (req, res) => { + try { + const userId = req.session.userId; + const { title } = req.body; + + // Создание нового диалога + const result = await pool.query( + `INSERT INTO conversations (user_id, title) + VALUES ($1, $2) + RETURNING *`, + [userId, title || 'Новый диалог'] + ); + + res.json(result.rows[0]); + } catch (error) { + console.error('Error creating conversation:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Обновление заголовка диалога +router.put('/conversations/:id', requireAuth, async (req, res) => { + try { + const userId = req.session.userId; + const conversationId = req.params.id; + const { title } = req.body; + + if (!title || title.trim() === '') { + return res.status(400).json({ error: 'Title is required' }); + } + + // Проверка доступа к диалогу + const conversationCheck = await pool.query( + 'SELECT id FROM conversations WHERE id = $1 AND user_id = $2', + [conversationId, userId] + ); + + if (conversationCheck.rows.length === 0) { + return res.status(403).json({ error: 'Access denied' }); + } + + // Обновление заголовка + const result = await pool.query( + 'UPDATE conversations SET title = $1 WHERE id = $2 RETURNING *', + [title, conversationId] + ); + + res.json(result.rows[0]); + } catch (error) { + console.error('Error updating conversation:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Удаление диалога +router.delete('/conversations/:id', requireAuth, async (req, res) => { + try { + const userId = req.session.userId; + const conversationId = req.params.id; + + // Проверка доступа к диалогу + const conversationCheck = await pool.query( + 'SELECT id FROM conversations WHERE id = $1 AND user_id = $2', + [conversationId, userId] + ); + + if (conversationCheck.rows.length === 0) { + return res.status(403).json({ error: 'Access denied' }); + } + + // Удаление диалога (каскадно удалит все сообщения) + await pool.query('DELETE FROM conversations WHERE id = $1', [conversationId]); + + res.json({ success: true }); + } catch (error) { + console.error('Error deleting conversation:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Маршруты для администраторов + +// Получение всех диалогов (только для администраторов) +router.get('/admin/conversations', requireAuth, async (req, res) => { + try { + // Проверка прав администратора + if (!req.session.isAdmin) { + return res.status(403).json({ error: 'Admin access required' }); + } + + const result = await pool.query( + `SELECT * FROM conversation_view + ORDER BY updated_at DESC` + ); + + res.json(result.rows); + } catch (error) { + console.error('Error fetching all conversations:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +// Получение статистики по каналам (только для администраторов) +router.get('/admin/stats/channels', requireAuth, async (req, res) => { + try { + // Проверка прав администратора + if (!req.session.isAdmin) { + return res.status(403).json({ error: 'Admin access required' }); + } + + const result = await pool.query( + `SELECT + channel, + COUNT(*) AS message_count, + COUNT(DISTINCT conversation_id) AS conversation_count, + COUNT(DISTINCT sender_id) AS user_count, + MIN(created_at) AS first_message, + MAX(created_at) AS last_message + FROM + messages + GROUP BY + channel` + ); + + res.json(result.rows); + } catch (error) { + console.error('Error fetching channel stats:', error); + res.status(500).json({ error: 'Internal server error' }); + } +}); + +module.exports = router; diff --git a/backend/routes/roles.js b/backend/routes/roles.js new file mode 100644 index 0000000..dcbc675 --- /dev/null +++ b/backend/routes/roles.js @@ -0,0 +1,56 @@ +const express = require('express'); +const router = express.Router(); +const db = require('../db'); +const { requireAuth, requireAdmin } = require('../middleware/auth'); +const { checkTokenBalanceAndUpdateRole } = require('../utils/access-check'); +const logger = require('../utils/logger'); + +// Маршрут для проверки и обновления роли пользователя +router.post('/check-role', requireAuth, async (req, res) => { + try { + if (!req.session.address) { + return res.status(400).json({ error: 'В сессии отсутствует адрес кошелька' }); + } + + const isAdmin = await checkTokenBalanceAndUpdateRole(req.session.address); + + // Обновление сессии + req.session.isAdmin = isAdmin; + + res.json({ isAdmin }); + } catch (error) { + logger.error('Error checking role:', error); + res.status(500).json({ error: 'Внутренняя ошибка сервера' }); + } +}); + +// Маршрут для получения всех ролей (только для админов) +router.get('/', requireAdmin, async (req, res) => { + try { + const result = await db.query('SELECT * FROM roles ORDER BY id'); + res.json(result.rows); + } catch (error) { + logger.error('Error fetching roles:', error); + res.status(500).json({ error: 'Внутренняя ошибка сервера' }); + } +}); + +// Маршрут для получения пользователей с их ролями (только для админов) +router.get('/users', requireAdmin, async (req, res) => { + try { + const result = await db.query(` + SELECT u.id, u.username, u.preferred_language, r.name as role, + u.created_at, u.last_token_check + FROM users u + LEFT JOIN roles r ON u.role_id = r.id + ORDER BY u.created_at DESC + `); + + res.json(result.rows); + } catch (error) { + logger.error('Error fetching users with roles:', error); + res.status(500).json({ error: 'Внутренняя ошибка сервера' }); + } +}); + +module.exports = router; \ No newline at end of file diff --git a/backend/routes/users.js b/backend/routes/users.js index 8e5109e..3422eb5 100644 --- a/backend/routes/users.js +++ b/backend/routes/users.js @@ -1,5 +1,8 @@ const express = require('express'); const router = express.Router(); +const db = require('../db'); +const logger = require('../utils/logger'); +const { requireAuth } = require('../middleware/auth'); // Получение списка пользователей router.get('/', (req, res) => { @@ -9,10 +12,35 @@ router.get('/', (req, res) => { // Получение информации о пользователе router.get('/:address', (req, res) => { const { address } = req.params; - res.json({ + res.json({ address, - message: 'User details endpoint' + message: 'User details endpoint', }); }); -module.exports = router; \ No newline at end of file +// Маршрут для обновления языка пользователя +router.post('/update-language', requireAuth, async (req, res) => { + try { + const { language } = req.body; + const userId = req.session.userId; + + // Проверка валидности языка + const validLanguages = ['ru', 'en']; + if (!validLanguages.includes(language)) { + return res.status(400).json({ error: 'Неподдерживаемый язык' }); + } + + // Обновление языка в базе данных + await db.query( + 'UPDATE users SET preferred_language = $1 WHERE id = $2', + [language, userId] + ); + + res.json({ success: true }); + } catch (error) { + logger.error('Error updating language:', error); + res.status(500).json({ error: 'Внутренняя ошибка сервера' }); + } +}); + +module.exports = router; diff --git a/backend/scripts/check-dependencies.js b/backend/scripts/check-dependencies.js index b2a801f..61c18f3 100644 --- a/backend/scripts/check-dependencies.js +++ b/backend/scripts/check-dependencies.js @@ -4,14 +4,9 @@ const path = require('path'); const packageJson = require('../package.json'); const dependencies = packageJson.dependencies || {}; -const requiredDependencies = [ - 'express-rate-limit', - 'winston', - 'helmet', - 'csurf' -]; +const requiredDependencies = ['express-rate-limit', 'winston', 'helmet', 'csurf']; -const missingDependencies = requiredDependencies.filter(dep => !dependencies[dep]); +const missingDependencies = requiredDependencies.filter((dep) => !dependencies[dep]); if (missingDependencies.length > 0) { console.error('Missing dependencies:', missingDependencies); @@ -19,4 +14,4 @@ if (missingDependencies.length > 0) { process.exit(1); } -console.log('All required dependencies are installed.'); \ No newline at end of file +console.log('All required dependencies are installed.'); diff --git a/backend/scripts/check-ethers-v6-compatibility.js b/backend/scripts/check-ethers-v6-compatibility.js new file mode 100644 index 0000000..9a0f58a --- /dev/null +++ b/backend/scripts/check-ethers-v6-compatibility.js @@ -0,0 +1,125 @@ +const fs = require('fs'); +const path = require('path'); +const { promisify } = require('util'); +const readFile = promisify(fs.readFile); +const readdir = promisify(fs.readdir); +const stat = promisify(fs.stat); + +// Паттерны для поиска несовместимых конструкций ethers.js v5 +const patterns = [ + 'ethers.providers.JsonRpcProvider', + 'ethers.providers.Web3Provider', + 'ethers.utils.parseEther', + 'ethers.utils.formatEther', + 'ethers.utils.formatUnits', + 'ethers.utils.parseUnits', + 'ethers.utils.verifyMessage', + 'ethers.utils.keccak256', + 'ethers.utils.toUtf8Bytes', + 'ethers.utils.arrayify', + 'ethers.utils.hexlify', + 'ethers.BigNumber.from', + 'ethers.constants.Zero', + 'ethers.constants.One', + 'ethers.constants.Two', + 'ethers.constants.MaxUint256', + 'ethers.constants.AddressZero', + 'ethers.constants.HashZero', +]; + +// Соответствующие замены для ethers.js v6.x +const replacements = [ + 'ethers.JsonRpcProvider', + 'ethers.BrowserProvider', + 'ethers.parseEther', + 'ethers.formatEther', + 'ethers.formatUnits', + 'ethers.parseUnits', + 'ethers.verifyMessage', + 'ethers.keccak256', + 'ethers.toUtf8Bytes', + 'ethers.getBytes', + 'ethers.hexlify', + 'ethers.getBigInt', + 'ethers.ZeroAddress', + 'ethers.ZeroAddress', + 'ethers.ZeroAddress', + 'ethers.MaxUint256', + 'ethers.ZeroAddress', + 'ethers.ZeroHash', +]; + +// Функция для рекурсивного обхода директории +async function walkDir(dir, fileList = []) { + const files = await readdir(dir); + + for (const file of files) { + const filePath = path.join(dir, file); + const fileStat = await stat(filePath); + + if (fileStat.isDirectory()) { + // Пропускаем node_modules и .git + if (file !== 'node_modules' && file !== '.git') { + fileList = await walkDir(filePath, fileList); + } + } else if (file.endsWith('.js')) { + fileList.push(filePath); + } + } + + return fileList; +} + +// Функция для проверки файла +async function checkFile(filePath) { + try { + if (filePath.includes('check-ethers-v6-compatibility.js')) { + return false; // Пропускаем проверку самого скрипта + } + + const content = await readFile(filePath, 'utf8'); + let hasIssues = false; + + for (let i = 0; i < patterns.length; i++) { + if (content.includes(patterns[i])) { + console.log(`\x1b[33mПроблема в файле ${filePath}:\x1b[0m`); + console.log(` Найдено: \x1b[31m${patterns[i]}\x1b[0m`); + console.log(` Заменить на: \x1b[32m${replacements[i]}\x1b[0m`); + hasIssues = true; + } + } + + return hasIssues; + } catch (error) { + console.error(`Ошибка при проверке файла ${filePath}:`, error); + return false; + } +} + +// Основная функция +async function main() { + try { + console.log('Проверка совместимости с ethers.js v6.x...'); + + const files = await walkDir(path.resolve(__dirname, '..')); + let issuesFound = false; + + for (const file of files) { + const hasIssues = await checkFile(file); + if (hasIssues) { + issuesFound = true; + } + } + + if (!issuesFound) { + console.log('\x1b[32mПроблем не найдено. Код совместим с ethers.js v6.x\x1b[0m'); + } else { + console.log('\n\x1b[33mНайдены проблемы совместимости с ethers.js v6.x\x1b[0m'); + console.log('Пожалуйста, обновите код в соответствии с рекомендациями выше.'); + } + } catch (error) { + console.error('Ошибка при проверке совместимости:', error); + } +} + +main(); diff --git a/backend/scripts/check-ollama-models.js b/backend/scripts/check-ollama-models.js new file mode 100644 index 0000000..c7410f6 --- /dev/null +++ b/backend/scripts/check-ollama-models.js @@ -0,0 +1,34 @@ +const axios = require('axios'); + +async function checkOllamaModels() { + try { + console.log('Проверка доступных моделей Ollama...'); + + const baseUrl = process.env.OLLAMA_BASE_URL || 'http://localhost:11434'; + const response = await axios.get(`${baseUrl}/api/tags`, { + timeout: 5000, // 5 секунд таймаут + }); + + if (response.status === 200 && response.data && response.data.models) { + console.log('\nДоступные модели Ollama:'); + console.log('------------------------'); + + response.data.models.forEach((model) => { + console.log(`- ${model.name}`); + }); + + console.log('\nДля использования конкретной модели, укажите ее в .env файле:'); + console.log('OLLAMA_EMBEDDINGS_MODEL=mistral'); + console.log('OLLAMA_MODEL=mistral'); + } else { + console.log('Не удалось получить список моделей'); + } + } catch (error) { + console.error('Ошибка при проверке моделей Ollama:', error.message); + console.log('\nУбедитесь, что Ollama запущен. Вы можете запустить его командой:'); + console.log('ollama serve'); + } +} + +// Запускаем проверку +checkOllamaModels(); diff --git a/backend/scripts/check-state.js b/backend/scripts/check-state.js index bd9a800..2459c65 100644 --- a/backend/scripts/check-state.js +++ b/backend/scripts/check-state.js @@ -1,34 +1,31 @@ -const hre = require("hardhat"); +const hre = require('hardhat'); async function main() { const accessToken = await hre.ethers.getContractAt( - "AccessToken", - "0xF352c498cF0857F472dC473E4Dd39551E79B1063" + 'AccessToken', + '0xF352c498cF0857F472dC473E4Dd39551E79B1063' ); const owner = await accessToken.owner(); - console.log("Contract owner:", owner); + console.log('Contract owner:', owner); // Проверяем все токены и их владельцев - console.log("\nAll tokens:"); + console.log('\nAll tokens:'); for (let i = 1; i <= 10; i++) { try { const tokenOwner = await accessToken.ownerOf(i); console.log(`Token ${i} owner: ${tokenOwner}`); } catch (error) { - if (!error.message.includes("invalid token ID")) { + if (!error.message.includes('invalid token ID')) { console.log(`Token ${i} error:`, error.message); } } } // Проверяем активные токены для всех известных адресов - const addresses = [ - owner, - "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" - ]; + const addresses = [owner, '0x70997970C51812dc3A010C7d01b50e0d17dc79C8']; - console.log("\nActive tokens:"); + console.log('\nActive tokens:'); for (const address of addresses) { const activeToken = await accessToken.activeTokens(address); console.log(`${address}: Token ${activeToken.toString()}`); @@ -40,4 +37,4 @@ main() .catch((error) => { console.error(error); process.exit(1); - }); \ No newline at end of file + }); diff --git a/backend/scripts/check-tokens.js b/backend/scripts/check-tokens.js new file mode 100644 index 0000000..679c5e3 --- /dev/null +++ b/backend/scripts/check-tokens.js @@ -0,0 +1,21 @@ +const { checkAllUsersTokens } = require('../utils/access-check'); +const logger = require('../utils/logger'); + +async function main() { + logger.info('Starting token balance check for all users'); + + try { + await checkAllUsersTokens(); + logger.info('Token balance check completed successfully'); + } catch (error) { + logger.error(`Error during token balance check: ${error.message}`); + } +} + +// Запуск скрипта +main() + .then(() => process.exit(0)) + .catch(error => { + logger.error(`Unhandled error: ${error.message}`); + process.exit(1); + }); \ No newline at end of file diff --git a/backend/scripts/create-moderator.js b/backend/scripts/create-moderator.js index b01cee9..9e9e5ed 100644 --- a/backend/scripts/create-moderator.js +++ b/backend/scripts/create-moderator.js @@ -1,42 +1,41 @@ -const hre = require("hardhat"); +const hre = require('hardhat'); async function main() { const accessToken = await hre.ethers.getContractAt( - "AccessToken", - "0xF352c498cF0857F472dC473E4Dd39551E79B1063" + 'AccessToken', + '0xF352c498cF0857F472dC473E4Dd39551E79B1063' ); - const moderatorAddress = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"; - + const moderatorAddress = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'; + try { - console.log("\nMinting moderator token..."); + console.log('\nMinting moderator token...'); const mintTx = await accessToken.mintAccessToken(moderatorAddress, 1); // MODERATOR - console.log("Waiting for transaction:", mintTx.hash); + console.log('Waiting for transaction:', mintTx.hash); await mintTx.wait(); - console.log("Moderator token minted"); + console.log('Moderator token minted'); // Проверяем результат const activeToken = await accessToken.activeTokens(moderatorAddress); console.log(`Moderator's active token: ${activeToken}`); const role = await accessToken.checkRole(moderatorAddress); - console.log(`Moderator role: ${["ADMIN", "MODERATOR", "SUPPORT"][role]}`); + console.log(`Moderator role: ${['ADMIN', 'MODERATOR', 'SUPPORT'][role]}`); } catch (error) { - console.log("Moderator token minting error:", error.message); + console.log('Moderator token minting error:', error.message); } // Проверяем все активные токены - console.log("\nAll active tokens:"); - const addresses = [ - await accessToken.owner(), - moderatorAddress - ]; + console.log('\nAll active tokens:'); + const addresses = [await accessToken.owner(), moderatorAddress]; for (const address of addresses) { try { const activeToken = await accessToken.activeTokens(address); const role = await accessToken.checkRole(address); - console.log(`${address}: Token ${activeToken}, Role: ${["ADMIN", "MODERATOR", "SUPPORT"][role]}`); + console.log( + `${address}: Token ${activeToken}, Role: ${['ADMIN', 'MODERATOR', 'SUPPORT'][role]}` + ); } catch (error) { console.log(`${address}: ${error.message}`); } @@ -46,6 +45,6 @@ async function main() { main() .then(() => process.exit(0)) .catch((error) => { - console.error("Script error:", error); + console.error('Script error:', error); process.exit(1); - }); \ No newline at end of file + }); diff --git a/backend/scripts/deploy-access.js b/backend/scripts/deploy-access.js index 6d1a448..09049c5 100644 --- a/backend/scripts/deploy-access.js +++ b/backend/scripts/deploy-access.js @@ -1,18 +1,18 @@ -const hre = require("hardhat"); +const hre = require('hardhat'); async function main() { - const AccessToken = await hre.ethers.getContractFactory("AccessToken"); + const AccessToken = await hre.ethers.getContractFactory('AccessToken'); const accessToken = await AccessToken.deploy(); await accessToken.waitForDeployment(); const address = await accessToken.getAddress(); - console.log("AccessToken deployed to:", address); + console.log('AccessToken deployed to:', address); // Создаем первый админский токен для владельца контракта const [owner] = await hre.ethers.getSigners(); const tx = await accessToken.mintAccessToken(owner.address, 0); // 0 = ADMIN await tx.wait(); - console.log("Admin token minted for:", owner.address); + console.log('Admin token minted for:', owner.address); } main() @@ -20,4 +20,4 @@ main() .catch((error) => { console.error(error); process.exit(1); - }); \ No newline at end of file + }); diff --git a/backend/scripts/deploy.js b/backend/scripts/deploy.js index 7224e86..e69fff0 100644 --- a/backend/scripts/deploy.js +++ b/backend/scripts/deploy.js @@ -1,17 +1,17 @@ -const hre = require("hardhat"); +const hre = require('hardhat'); async function main() { - console.log("Начинаем деплой контракта..."); + console.log('Начинаем деплой контракта...'); // Получаем контракт - const MyContract = await hre.ethers.getContractFactory("MyContract"); - + const MyContract = await hre.ethers.getContractFactory('MyContract'); + // Деплоим контракт const myContract = await MyContract.deploy(); await myContract.waitForDeployment(); const address = await myContract.getAddress(); - console.log("Контракт развернут по адресу:", address); + console.log('Контракт развернут по адресу:', address); } main() @@ -19,4 +19,4 @@ main() .catch((error) => { console.error(error); process.exit(1); - }); \ No newline at end of file + }); diff --git a/backend/scripts/init-db.js b/backend/scripts/init-db.js index 9787829..93bca40 100644 --- a/backend/scripts/init-db.js +++ b/backend/scripts/init-db.js @@ -5,20 +5,20 @@ dotenv.config(); const pool = new Pool({ connectionString: process.env.DATABASE_URL, - ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false + ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, }); async function initDb() { try { console.log('Инициализация базы данных...'); - + // Добавляем тестового пользователя await pool.query(` INSERT INTO users (address, is_admin) VALUES ('0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b', TRUE) ON CONFLICT (address) DO NOTHING `); - + // Добавляем тестовую доску await pool.query(` INSERT INTO kanban_boards (title, description, owner_id, is_public) @@ -30,17 +30,18 @@ async function initDb() { ) ON CONFLICT DO NOTHING `); - + // Получаем ID доски const boardResult = await pool.query(` SELECT id FROM kanban_boards WHERE title = 'Тестовая доска' LIMIT 1 `); - + if (boardResult.rows.length > 0) { const boardId = boardResult.rows[0].id; - + // Добавляем тестовые колонки - await pool.query(` + await pool.query( + ` INSERT INTO kanban_columns (board_id, title, position) VALUES ($1, 'Backlog', 0), @@ -48,9 +49,11 @@ async function initDb() { ($1, 'Review', 2), ($1, 'Done', 3) ON CONFLICT DO NOTHING - `, [boardId]); + `, + [boardId] + ); } - + console.log('База данных инициализирована успешно'); } catch (error) { console.error('Ошибка при инициализации базы данных:', error); @@ -59,4 +62,4 @@ async function initDb() { } } -initDb(); \ No newline at end of file +initDb(); diff --git a/backend/scripts/init-roles.js b/backend/scripts/init-roles.js index 87c4233..5037e4c 100644 --- a/backend/scripts/init-roles.js +++ b/backend/scripts/init-roles.js @@ -1,13 +1,13 @@ -const hre = require("hardhat"); +const hre = require('hardhat'); async function main() { const accessToken = await hre.ethers.getContractAt( - "AccessToken", - "0xF352c498cF0857F472dC473E4Dd39551E79B1063" + 'AccessToken', + '0xF352c498cF0857F472dC473E4Dd39551E79B1063' ); const owner = await accessToken.owner(); - console.log("Contract owner:", owner); + console.log('Contract owner:', owner); // Создаем админский токен для владельца try { @@ -16,34 +16,34 @@ async function main() { console.log(`Admin token minted for ${owner}`); const role = await accessToken.checkRole(owner); - console.log("Owner role:", ["ADMIN", "MODERATOR", "SUPPORT"][role]); + console.log('Owner role:', ['ADMIN', 'MODERATOR', 'SUPPORT'][role]); } catch (error) { - console.log("Admin token minting error:", error.message); + console.log('Admin token minting error:', error.message); } // Создаем тестовый токен модератора - const moderatorAddress = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"; // Тестовый адрес модератора + const moderatorAddress = '0x70997970C51812dc3A010C7d01b50e0d17dc79C8'; // Тестовый адрес модератора try { const tx = await accessToken.mintAccessToken(moderatorAddress, 1); // 1 = MODERATOR await tx.wait(); console.log(`Moderator token minted for ${moderatorAddress}`); const role = await accessToken.checkRole(moderatorAddress); - console.log("Moderator role:", ["ADMIN", "MODERATOR", "SUPPORT"][role]); + console.log('Moderator role:', ['ADMIN', 'MODERATOR', 'SUPPORT'][role]); } catch (error) { - console.log("Moderator token minting error:", error.message); + console.log('Moderator token minting error:', error.message); } // Проверяем все токены - console.log("\nChecking all tokens:"); + console.log('\nChecking all tokens:'); for (let i = 1; i <= 5; i++) { try { const owner = await accessToken.ownerOf(i); const role = await accessToken.checkRole(owner); - console.log(`Token ${i}: Owner ${owner}, Role: ${["ADMIN", "MODERATOR", "SUPPORT"][role]}`); + console.log(`Token ${i}: Owner ${owner}, Role: ${['ADMIN', 'MODERATOR', 'SUPPORT'][role]}`); } catch (error) { // Пропускаем несуществующие токены - if (!error.message.includes("nonexistent token")) { + if (!error.message.includes('nonexistent token')) { console.log(`Token ${i} error:`, error.message); } } @@ -55,4 +55,4 @@ main() .catch((error) => { console.error(error); process.exit(1); - }); \ No newline at end of file + }); diff --git a/backend/scripts/manage-access.js b/backend/scripts/manage-access.js deleted file mode 100644 index ccd04a9..0000000 --- a/backend/scripts/manage-access.js +++ /dev/null @@ -1,72 +0,0 @@ -const hre = require("hardhat"); - -async function main() { - const accessToken = await hre.ethers.getContractAt( - "AccessToken", - "0xF352c498cF0857F472dC473E4Dd39551E79B1063" // Адрес нашего контракта - ); - - // Проверим текущего владельца - const owner = await accessToken.owner(); - console.log("Contract owner:", owner); - - // Проверим роль владельца - try { - const ownerRole = await accessToken.checkRole(owner); - console.log("Owner role:", ["ADMIN", "MODERATOR", "SUPPORT"][ownerRole]); - } catch (error) { - console.log("Owner role check error:", error.message); - } - - // Создадим токен модератора для тестового адреса - const moderatorAddress = "0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B"; - try { - const tx = await accessToken.mintAccessToken(moderatorAddress, 1); // 1 = MODERATOR - await tx.wait(); - console.log(`Moderator token minted for ${moderatorAddress}`); - - // Проверим роль модератора - const modRole = await accessToken.checkRole(moderatorAddress); - console.log("Moderator role:", ["ADMIN", "MODERATOR", "SUPPORT"][modRole]); - } catch (error) { - console.log("Moderator token minting error:", error.message); - } - - // Получим все активные токены (с ограничением по блокам) - const currentBlock = await hre.ethers.provider.getBlockNumber(); - const fromBlock = currentBlock - 1000; // Последние 1000 блоков - - const filter = accessToken.filters.Transfer(null, null, null); - const events = await accessToken.queryFilter(filter, fromBlock); - console.log("\nActive tokens (last 1000 blocks):"); - for (let event of events) { - if (event.args.from === "0x0000000000000000000000000000000000000000") { - console.log(`Token ID: ${event.args.tokenId}, Owner: ${event.args.to}`); - try { - const role = await accessToken.checkRole(event.args.to); - console.log(`Role: ${["ADMIN", "MODERATOR", "SUPPORT"][role]}`); - } catch (error) { - console.log("Role check error:", error.message); - } - } - } - - // Альтернативный способ - проверить конкретный токен - console.log("\nChecking specific tokens:"); - for (let i = 1; i <= 2; i++) { - try { - const owner = await accessToken.ownerOf(i); - const role = await accessToken.checkRole(owner); - console.log(`Token ${i}: Owner ${owner}, Role: ${["ADMIN", "MODERATOR", "SUPPORT"][role]}`); - } catch (error) { - console.log(`Token ${i} not found or error:`, error.message); - } - } -} - -main() - .then(() => process.exit(0)) - .catch((error) => { - console.error(error); - process.exit(1); - }); \ No newline at end of file diff --git a/backend/scripts/revoke-all.js b/backend/scripts/revoke-all.js index b91d601..5bc2490 100644 --- a/backend/scripts/revoke-all.js +++ b/backend/scripts/revoke-all.js @@ -1,9 +1,9 @@ -const hre = require("hardhat"); +const hre = require('hardhat'); async function main() { const accessToken = await hre.ethers.getContractAt( - "AccessToken", - "0xF352c498cF0857F472dC473E4Dd39551E79B1063" + 'AccessToken', + '0xF352c498cF0857F472dC473E4Dd39551E79B1063' ); // Отзываем все токены от 1 до 3 @@ -23,4 +23,4 @@ main() .catch((error) => { console.error(error); process.exit(1); - }); \ No newline at end of file + }); diff --git a/backend/scripts/run-migrations.js b/backend/scripts/run-migrations.js index 3613e9c..648ca74 100644 --- a/backend/scripts/run-migrations.js +++ b/backend/scripts/run-migrations.js @@ -6,13 +6,13 @@ require('dotenv').config(); // Подключение к БД const pool = new Pool({ connectionString: process.env.DATABASE_URL, - ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false + ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, }); async function runMigrations() { try { console.log('Запуск миграций...'); - + // Создаем таблицу для отслеживания миграций, если её нет await pool.query(` CREATE TABLE IF NOT EXISTS migrations ( @@ -21,41 +21,39 @@ async function runMigrations() { applied_at TIMESTAMP DEFAULT NOW() ) `); - + // Получаем список уже примененных миграций const { rows } = await pool.query('SELECT name FROM migrations'); - const appliedMigrations = rows.map(row => row.name); - + const appliedMigrations = rows.map((row) => row.name); + // Получаем список файлов миграций const migrationsDir = path.join(__dirname, '../migrations'); - const migrationFiles = fs.readdirSync(migrationsDir) - .filter(file => file.endsWith('.sql')) + const migrationFiles = fs + .readdirSync(migrationsDir) + .filter((file) => file.endsWith('.sql')) .sort(); // Сортируем файлы по имени - + // Применяем миграции, которые еще не были применены for (const file of migrationFiles) { if (!appliedMigrations.includes(file)) { console.log(`Применение миграции: ${file}`); - + // Читаем содержимое файла миграции const filePath = path.join(migrationsDir, file); const sql = fs.readFileSync(filePath, 'utf8'); - + // Выполняем SQL-запросы из файла await pool.query(sql); - + // Записываем информацию о примененной миграции - await pool.query( - 'INSERT INTO migrations (name) VALUES ($1)', - [file] - ); - + await pool.query('INSERT INTO migrations (name) VALUES ($1)', [file]); + console.log(`Миграция ${file} успешно применена`); } else { console.log(`Миграция ${file} уже применена`); } } - + console.log('Все миграции успешно применены'); } catch (error) { console.error('Ошибка при выполнении миграций:', error); @@ -65,4 +63,4 @@ async function runMigrations() { } } -runMigrations(); \ No newline at end of file +runMigrations(); diff --git a/backend/scripts/update-user-roles.js b/backend/scripts/update-user-roles.js new file mode 100644 index 0000000..c8cdc4d --- /dev/null +++ b/backend/scripts/update-user-roles.js @@ -0,0 +1,53 @@ +const { checkAllUsersTokens } = require('../utils/access-check'); +const db = require('../db'); +const logger = require('../utils/logger'); + +async function updateRolesFromOldStructure() { + try { + logger.info('Starting migration of user roles from old structure'); + + // Получаем пользователей со старым полем role + const usersWithOldRoles = await db.query(` + SELECT id, role, address + FROM users + WHERE role IS NOT NULL AND role_id IS NULL + `); + + logger.info(`Found ${usersWithOldRoles.rows.length} users with old role structure`); + + for (const user of usersWithOldRoles.rows) { + // Определяем ID роли + let roleId = 2; // По умолчанию 'user' + + if (user.role === 'ADMIN' || user.role === 'admin') { + roleId = 1; // 'admin' + } + + // Обновляем пользователя + await db.query( + 'UPDATE users SET role_id = $1 WHERE id = $2', + [roleId, user.id] + ); + + logger.info(`Updated user ${user.id} with role_id ${roleId} (from old role ${user.role})`); + } + + // Запускаем проверку токенов для всех пользователей + await checkAllUsersTokens(); + + logger.info('Role migration completed successfully'); + } catch (error) { + logger.error(`Error during role migration: ${error.message}`); + } +} + +// Запуск скрипта +updateRolesFromOldStructure() + .then(() => { + logger.info('Migration script completed'); + process.exit(0); + }) + .catch(error => { + logger.error(`Unhandled error: ${error.message}`); + process.exit(1); + }); \ No newline at end of file diff --git a/backend/server.js b/backend/server.js index aaf99c4..25b65d6 100644 --- a/backend/server.js +++ b/backend/server.js @@ -3,7 +3,7 @@ const express = require('express'); const cors = require('cors'); const { SiweMessage, generateNonce } = require('siwe'); const { ethers } = require('ethers'); -const TelegramBotService = require('./services/telegramBot'); +// const TelegramBotService = require('./services/telegramBot'); const EmailBotService = require('./services/emailBot'); const { initializeVectorStore } = require('./services/vectorStore'); const session = require('express-session'); @@ -12,24 +12,27 @@ const usersRouter = require('./routes/users'); const { router: authRouter } = require('./routes/auth'); const contractsRouter = require('./routes/contracts'); const accessRouter = require('./routes/access'); -const chatRouter = require('./routes/chat'); const path = require('path'); const axios = require('axios'); const { ChatOllama } = require('@langchain/ollama'); const { getVectorStore } = require('./services/vectorStore'); -const debugRouter = require('./routes/debug'); +// const debugRoutes = require('./routes/debug'); const identitiesRouter = require('./routes/identities'); -const kanbanRouter = require('./routes/kanban'); const { pool } = require('./db'); const fs = require('fs'); const pgSession = require('connect-pg-simple')(session); const sessionStore = new pgSession({ pool: pool, tableName: 'session', - createTableIfMissing: true + createTableIfMissing: true, }); const helmet = require('helmet'); -const csrf = require('csurf'); +// const csrf = require('csurf'); +// const cookieParser = require('cookie-parser'); +const messagesRouter = require('./routes/messages'); + +// Импорт сервисов +const { initTelegramBot } = require('./services/telegram-service'); const PORT = process.env.PORT || 8000; @@ -50,24 +53,21 @@ const provider = new ethers.JsonRpcProvider(process.env.ETHEREUM_NETWORK_URL); console.log('Provider URL:', process.env.ETHEREUM_NETWORK_URL); console.log('Contract address:', process.env.CONTRACT_ADDRESS); -const contract = new ethers.Contract( - process.env.CONTRACT_ADDRESS, - contractABI, - provider -); +const contract = new ethers.Contract(process.env.CONTRACT_ADDRESS, contractABI, provider); // Проверяем, что библиотека ethers.js правильно импортирована console.log('Ethers.js version:', ethers.version); // Порядок middleware важен! // 1. CORS должен быть первым -app.use(cors({ - origin: ['http://127.0.0.1:5173', 'http://localhost:5173'], - credentials: true, - methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With'], - exposedHeaders: ['Set-Cookie'] -})); +app.use( + cors({ + origin: ['http://localhost:5173', 'http://127.0.0.1:5173'], + credentials: true, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization', 'X-Auth-Nonce'], + }) +); // Добавьте после настройки CORS app.use(helmet()); @@ -77,18 +77,23 @@ app.use(express.json()); app.use(express.urlencoded({ extended: true })); // 3. Затем сессии -app.use(session({ - secret: process.env.SESSION_SECRET || 'your-secret-key', - resave: true, - saveUninitialized: true, - cookie: { - httpOnly: true, - secure: false, - sameSite: 'lax', - maxAge: 24 * 60 * 60 * 1000 - }, - store: sessionStore -})); +app.use( + session({ + secret: process.env.SESSION_SECRET || 'your-secret-key', + resave: false, + saveUninitialized: false, + cookie: { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', // В разработке можно установить false + sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax', + maxAge: 24 * 60 * 60 * 1000, // 1 день + }, + store: new pgSession({ + pool: pool, + tableName: 'session', + }), + }) +); // Добавьте после настройки сессий app.use((req, res, next) => { @@ -164,76 +169,67 @@ app.use((req, res, next) => { // Добавляем middleware для отладки сессий app.use((req, res, next) => { - // console.log('Session debug:', { - // url: req.url, - // method: req.method, - // sessionID: req.sessionID, - // cookies: req.headers.cookie, - // session: req.session ? { - // isAuthenticated: req.session.isAuthenticated, - // authenticated: req.session.authenticated, - // address: req.session.address, - // isAdmin: req.session.isAdmin, - // nonce: req.session.nonce ? '[REDACTED]' : undefined, - // pendingAddress: req.session.pendingAddress - // } : null - // }); + console.log('Сессия:', req.session); + console.log('Куки:', req.headers.cookie); next(); }); -// Настройка CSRF-защиты -const csrfProtection = csrf({ - cookie: { - key: '_csrf', - path: '/', - httpOnly: true, - secure: process.env.NODE_ENV === 'production', // true в production, false в development - sameSite: process.env.NODE_ENV === 'production' ? 'strict' : 'lax' - } -}); +// Добавьте cookie-парсер перед CSRF-защитой +// app.use(cookieParser()); + +// Затем настройте CSRF-защиту +// const csrfProtection = csrf({ +// cookie: { +// key: '_csrf', +// path: '/', +// httpOnly: true, +// secure: process.env.NODE_ENV === 'production', +// sameSite: 'lax' +// } +// }); + +// Добавьте маршрут для получения CSRF-токена +// app.get('/api/csrf-token', csrfProtection, (req, res) => { +// res.json({ csrfToken: req.csrfToken() }); +// }); // Применяем CSRF-защиту только к определенным маршрутам -app.use('/api/protected', csrfProtection); -app.use('/api/admin', csrfProtection); -app.use('/api/kanban', csrfProtection); - -// Маршрут для получения CSRF-токена -app.get('/api/csrf-token', csrfProtection, (req, res) => { - res.json({ csrfToken: req.csrfToken() }); -}); +// app.use('/api/protected', csrfProtection); +// app.use('/api/admin', csrfProtection); +// app.use('/api/kanban', csrfProtection); // Обработчик ошибок CSRF -app.use((err, req, res, next) => { - if (err.code === 'EBADCSRFTOKEN') { - console.error('CSRF error:', { - url: req.url, - method: req.method, - headers: req.headers, - body: req.body - }); - return res.status(403).json({ - error: 'CSRF token validation failed', - message: 'Your session may have expired. Please refresh the page and try again.' - }); - } - next(err); -}); +// app.use((err, req, res, next) => { +// if (err.code === 'EBADCSRFTOKEN') { +// console.error('CSRF error:', { +// url: req.url, +// method: req.method, +// headers: req.headers, +// body: req.body +// }); +// return res.status(403).json({ +// error: 'CSRF token validation failed', +// message: 'Your session may have expired. Please refresh the page and try again.' +// }); +// } +// next(err); +// }); async function initServices() { try { console.log('Инициализация сервисов...'); - + // Инициализируем ботов, если они нужны if (process.env.TELEGRAM_BOT_TOKEN) { telegramBot = new TelegramBotService(process.env.TELEGRAM_BOT_TOKEN); console.log('Telegram бот инициализирован'); } - + if (process.env.EMAIL_USER && process.env.EMAIL_PASS) { emailBot = new EmailBotService(process.env.EMAIL_USER, process.env.EMAIL_PASS); console.log('Email бот инициализирован'); } - + console.log('Все сервисы успешно инициализированы'); } catch (error) { console.error('Ошибка при инициализации сервисов:', error); @@ -244,10 +240,10 @@ app.use('/api/users', usersRouter); app.use('/api/auth', authRouter); app.use('/api/contracts', contractsRouter); app.use('/api/access', accessRouter); -app.use('/api/chat', chatRouter); -app.use('/api/debug', debugRouter); +// app.use('/api/chat', chatRouter); +// app.use('/api/debug', debugRoutes); app.use('/api/identities', identitiesRouter); -app.use('/api/kanban', kanbanRouter); +app.use('/api/messages', messagesRouter); // Добавьте простой эндпоинт для проверки состояния сервера app.get('/api/health', (req, res) => { @@ -259,81 +255,81 @@ app.post('/api/verify', async (req, res) => { try { // Перенаправляем запрос на /api/auth/verify const { message, signature } = req.body; - console.log("Перенаправление запроса на /api/auth/verify:", { message, signature }); - + console.log('Перенаправление запроса на /api/auth/verify:', { message, signature }); + // Проверяем наличие необходимых данных if (!message || !message.address || !signature) { - return res.status(400).json({ - success: false, - error: 'Отсутствуют необходимые данные для верификации' + return res.status(400).json({ + success: false, + error: 'Отсутствуют необходимые данные для верификации', }); } - + const address = message.address.toLowerCase(); - console.log("Адрес из сообщения:", address); - + console.log('Адрес из сообщения:', address); + // Проверяем, является ли пользователь администратором const isAdmin = true; // Для примера всегда true - + try { const siweMessage = new SiweMessage(message); const fields = await siweMessage.validate(signature); - + if (fields.address.toLowerCase() !== address.toLowerCase()) { return res.status(401).json({ success: false, error: 'Invalid signature' }); } - + // Только после проверки устанавливаем сессию req.session.authenticated = true; req.session.address = fields.address; req.session.lastSignature = signature; - + // Сохраняем сессию req.session.save(); } catch (error) { return res.status(401).json({ success: false, error: error.message }); } - + // Сохраняем данные в сессии req.session.isAuthenticated = true; req.session.isAdmin = isAdmin; - + // Явно сохраняем сессию req.session.save((err) => { if (err) { console.error('Ошибка сохранения сессии:', err); return res.status(500).json({ error: 'Session save error' }); } - + console.log('Сессия успешно сохранена:', { sessionID: req.sessionID, session: { isAuthenticated: req.session.isAuthenticated, authenticated: req.session.authenticated, address: req.session.address, - isAdmin: req.session.isAdmin - } + isAdmin: req.session.isAdmin, + }, }); - + res.cookie('authToken', 'true', { maxAge: 86400000, httpOnly: false, secure: false, sameSite: 'lax', - path: '/' + path: '/', }); - + res.json({ success: true, address: address, - isAdmin: isAdmin + isAdmin: isAdmin, }); }); } catch (error) { - console.error("Ошибка верификации:", error); - res.status(500).json({ - success: false, - error: error.message || 'Внутренняя ошибка сервера' + console.error('Ошибка верификации:', error); + res.status(500).json({ + success: false, + error: error.message || 'Внутренняя ошибка сервера', }); } }); @@ -345,22 +341,22 @@ app.get('/api/session', (req, res) => { sessionID: req.sessionID, isAuthenticated: req.session?.isAuthenticated, authenticated: req.session?.authenticated, - address: req.session?.address + address: req.session?.address, }); - + if (req.session && (req.session.isAuthenticated || req.session.authenticated)) { res.json({ isAuthenticated: true, authenticated: true, address: req.session.address, - isAdmin: req.session.isAdmin + isAdmin: req.session.isAdmin, }); } else { res.json({ isAuthenticated: false, authenticated: false, address: null, - isAdmin: false + isAdmin: false, }); } }); @@ -380,89 +376,45 @@ app.get('/api/public', (req, res) => { }); app.get('/api/protected', (req, res) => { - res.json({ + res.json({ message: 'This is a protected API endpoint', user: { address: req.session.address, - isAdmin: req.session.isAdmin - } + isAdmin: req.session.isAdmin, + }, }); }); app.get('/api/admin', (req, res) => { - res.json({ + res.json({ message: 'This is an admin API endpoint', user: { address: req.session.address, - isAdmin: req.session.isAdmin - } + isAdmin: req.session.isAdmin, + }, }); }); // Добавьте обработчик ошибок app.use((err, req, res, next) => { - console.error('Глобальный обработчик ошибок:', err); - - // Обработка ошибок CSRF - if (err.code === 'EBADCSRFTOKEN') { - return res.status(403).json({ - error: 'Недействительный CSRF-токен', - message: 'Возможно, ваша сессия истекла. Пожалуйста, обновите страницу и попробуйте снова.' - }); + console.error('Глобальная ошибка:', err.stack); + if (!res.headersSent) { + res.status(500).json({ error: 'Внутренняя ошибка сервера' }); } - - // Обработка ошибок валидации - if (err.name === 'ValidationError') { - return res.status(400).json({ - error: 'Ошибка валидации', - details: err.details || err.message - }); - } - - // Обработка ошибок базы данных - if (err.code === '23505') { // Postgres unique violation - return res.status(409).json({ - error: 'Конфликт данных', - message: 'Запись с такими данными уже существует.' - }); - } - - // Общая обработка ошибок - res.status(err.status || 500).json({ - error: 'Внутренняя ошибка сервера', - message: process.env.NODE_ENV === 'production' ? 'Что-то пошло не так' : err.message - }); }); // Перед запуском сервера console.log('Перед запуском сервера на порту:', PORT); // Запуск сервера и инициализация сервисов -const server = app.listen(PORT, '0.0.0.0', async () => { - console.log(`Server is running on port ${PORT}`); - console.log('Server address:', server.address()); - - // Инициализируем сервисы без блокировки запуска сервера - initServices().catch(err => { - console.error('Ошибка при инициализации сервисов:', err); +let server; + +checkDatabaseStructure().then(() => { + // Запускаем сервер + server = app.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); + console.log('Server address:', server.address()); }); - - // Проверяем доступность Ollama в фоновом режиме - try { - const { checkOllamaAvailability } = require('./services/ollama'); - checkOllamaAvailability().catch(err => { - console.error('Ошибка при проверке Ollama:', err); - }); - } catch (error) { - console.error('Ошибка при импорте модуля Ollama:', error); - } -}).on('error', (err) => { - if (err.code === 'EADDRINUSE') { - console.error(`Port ${PORT} is already in use. Please try another port.`); - process.exit(1); - } else { - console.error('Server error:', err); - } }); // Добавляем graceful shutdown @@ -480,7 +432,7 @@ async function checkOllamaServer() { const response = await axios.get('http://localhost:11434/api/tags'); if (response.status === 200) { console.log('Ollama сервер доступен'); - + // Тестируем прямой запрос к Ollama try { console.log('Тестируем прямой запрос к Ollama...'); @@ -489,13 +441,13 @@ async function checkOllamaServer() { model: 'llama3', temperature: 0.2, }); - + const result = await model.invoke('Привет, как дела?'); console.log('Ответ от Ollama:', result); } catch (testError) { console.error('Ошибка при тестировании Ollama:', testError); } - + // Инициализируем векторное хранилище try { console.log('Инициализируем векторное хранилище...'); @@ -504,7 +456,7 @@ async function checkOllamaServer() { } catch (vectorError) { console.error('Ошибка при инициализации векторного хранилища:', vectorError); } - + return true; } return false; @@ -515,15 +467,16 @@ async function checkOllamaServer() { } // Настройка периодической очистки устаревших сессий -const pgSessionCleanup = setInterval(function() { +const pgSessionCleanup = setInterval(function () { console.log('Cleaning up expired sessions...'); - pool.query('DELETE FROM session WHERE expire < NOW()') - .then(result => { + pool + .query('DELETE FROM session WHERE expire < NOW()') + .then((result) => { if (result.rowCount > 0) { console.log(`Removed ${result.rowCount} expired sessions`); } }) - .catch(err => console.error('Error cleaning up sessions:', err)); + .catch((err) => console.error('Error cleaning up sessions:', err)); }, 3600000); // Очистка каждый час // Очистка интервала при завершении работы @@ -547,11 +500,11 @@ async function ensureTablesExist() { AND table_name = 'users' ); `); - + // Если таблица не существует, создаем все таблицы if (!result.rows[0].exists) { console.log('Таблицы не найдены, создаем...'); - + // SQL-запросы для создания таблиц const createTablesSql = ` -- Таблица пользователей @@ -631,7 +584,7 @@ async function ensureTablesExist() { -- Индекс для таблицы сообщений CREATE INDEX IF NOT EXISTS idx_chat_messages_user ON chat_messages(user_id); `; - + await pool.query(createTablesSql); console.log('Таблицы успешно созданы'); } else { @@ -643,7 +596,7 @@ async function ensureTablesExist() { } // Вызываем функцию при запуске сервера -ensureTablesExist(); +ensureTablesExist(); // Добавляем middleware для проверки аутентификации app.use('/api/protected', (req, res, next) => { @@ -652,11 +605,11 @@ app.use('/api/protected', (req, res, next) => { // authenticated: req.session.authenticated, // address: req.session.address // }); - + if (!req.session.authenticated) { return res.status(401).json({ error: 'Unauthorized' }); } - + next(); }); @@ -667,10 +620,117 @@ app.use('/api/admin', (req, res, next) => { // authenticated: req.session.authenticated, // isAdmin: req.session.isAdmin // }); - + if (!req.session.authenticated || !req.session.isAdmin) { return res.status(403).json({ error: 'Forbidden' }); } - + next(); -}); \ No newline at end of file +}); + +// Проверка структуры базы данных +async function checkDatabaseStructure() { + try { + const db = require('./db'); + + // Проверяем наличие таблицы roles + const rolesTable = await db.query(` + SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_name = 'roles' + ); + `); + + if (!rolesTable.rows[0].exists) { + console.error('Таблица roles не существует. Выполните миграцию.'); + process.exit(1); + } + + // Проверяем наличие колонки role_id в таблице users + const roleIdColumn = await db.query(` + SELECT EXISTS ( + SELECT FROM information_schema.columns + WHERE table_name = 'users' AND column_name = 'role_id' + ); + `); + + if (!roleIdColumn.rows[0].exists) { + console.error('Колонка role_id не существует в таблице users. Выполните миграцию.'); + process.exit(1); + } + + console.log('Структура базы данных проверена успешно.'); + } catch (error) { + console.error('Ошибка при проверке структуры базы данных:', error); + process.exit(1); + } +} + +// Обработка сигналов завершения +process.on('SIGINT', () => { + console.log('Получен сигнал SIGINT, завершаем работу...'); + server.close(() => { + console.log('Сервер остановлен'); + process.exit(0); + }); +}); + +process.on('SIGTERM', () => { + console.log('Получен сигнал SIGTERM, завершаем работу...'); + server.close(() => { + console.log('Сервер остановлен'); + process.exit(0); + }); +}); + +// Обработка необработанных исключений +process.on('uncaughtException', (error) => { + console.error('Необработанное исключение:', error); + // Не завершаем процесс, чтобы nodemon мог перезапустить сервер +}); + +process.on('unhandledRejection', (reason, promise) => { + console.error('Необработанное отклонение промиса:', reason); + // Не завершаем процесс, чтобы nodemon мог перезапустить сервер +}); + +// Инициализация Telegram бота +initTelegramBot(); + +// Добавьте после других маршрутов +const chatRouter = require('./routes/chat'); +app.use('/api/chat', chatRouter); + +const cron = require('node-cron'); +const { checkAllUsersTokens } = require('./utils/access-check'); +const logger = require('./utils/logger'); + +// Настройка cron-задачи для проверки токенов каждые 30 минут +cron.schedule('*/30 * * * *', async () => { + logger.info('Running scheduled token balance check'); + await checkAllUsersTokens(); +}); + +// Периодическая очистка устаревших сессий +const cleanupInterval = 24 * 60 * 60 * 1000; // 24 часа + +setInterval(async () => { + try { + const { pool } = require('./db'); + const result = await pool.query('DELETE FROM sessions WHERE expire < NOW()'); + console.log(`Очищено ${result.rowCount} устаревших сессий`); + } catch (err) { + console.error('Ошибка при очистке сессий:', err); + } +}, cleanupInterval); + +// Запускаем первую очистку через 5 минут после старта сервера +setTimeout(async () => { + try { + const { pool } = require('./db'); + const result = await pool.query('DELETE FROM sessions WHERE expire < NOW()'); + console.log(`Первоначальная очистка: удалено ${result.rowCount} устаревших сессий`); + } catch (err) { + console.error('Ошибка при первоначальной очистке сессий:', err); + } +}, 5 * 60 * 1000); diff --git a/backend/services/ai-assistant.js b/backend/services/ai-assistant.js new file mode 100644 index 0000000..08686fa --- /dev/null +++ b/backend/services/ai-assistant.js @@ -0,0 +1,158 @@ +const { ChatOllama } = require('@langchain/ollama'); +const { pool } = require('../db'); + +// Инициализация модели Ollama +const model = new ChatOllama({ + baseUrl: process.env.OLLAMA_BASE_URL || 'http://localhost:11434', + model: process.env.OLLAMA_MODEL || 'llama2', +}); + +/** + * Обработка сообщения пользователя и получение ответа от ИИ + * @param {number} userId - ID пользователя + * @param {string} message - Текст сообщения + * @param {string} language - Язык пользователя + * @returns {Promise} - Ответ ИИ + */ +async function processMessage(userId, message, language = 'ru') { + try { + // Получение информации о пользователе + const userInfo = await getUserInfo(userId); + + // Получение истории диалога (последние 10 сообщений) + const history = await getConversationHistory(userId); + + // Формирование контекста для ИИ + const context = ` +Пользователь: ${userInfo.username || 'Пользователь'} (ID: ${userId}) +Язык: ${language} +Роль: ${userInfo.is_admin ? 'Администратор' : 'Пользователь'} +История диалога: +${history} + +Текущее сообщение: ${message} +`; + + // Временная заглушка для ответа ИИ + // В будущем здесь будет интеграция с реальной моделью ИИ + const responses = { + ru: [ + 'Спасибо за ваше сообщение! Чем я могу помочь?', + 'Я понимаю ваш запрос. Давайте разберемся с этим вопросом.', + 'Интересный вопрос! Вот что я могу предложить...', + 'Я обработал вашу информацию. Есть ли у вас дополнительные вопросы?', + 'Я готов помочь вам с этим запросом. Нужны ли дополнительные детали?', + ], + en: [ + 'Thank you for your message! How can I help you?', + "I understand your request. Let's figure this out.", + "Interesting question! Here's what I can suggest...", + "I've processed your information. Do you have any additional questions?", + "I'm ready to help you with this request. Do you need any additional details?", + ], + }; + + const langResponses = responses[language] || responses['ru']; + const randomIndex = Math.floor(Math.random() * langResponses.length); + + // Имитация задержки ответа ИИ + await new Promise((resolve) => setTimeout(resolve, 500)); + + return langResponses[randomIndex]; + } catch (error) { + console.error('Error processing message:', error); + return 'Извините, произошла ошибка при обработке вашего сообщения. Пожалуйста, попробуйте еще раз позже.'; + } +} + +/** + * Получение информации о пользователе + * @param {number} userId - ID пользователя + * @returns {Promise} - Информация о пользователе + */ +async function getUserInfo(userId) { + try { + const userResult = await pool.query( + `SELECT u.id, u.username, u.address, u.is_admin, u.language, r.name as role + FROM users u + JOIN roles r ON u.role_id = r.id + WHERE u.id = $1`, + [userId] + ); + + if (userResult.rows.length === 0) { + return { id: userId }; + } + + // Получение идентификаторов пользователя + const identitiesResult = await pool.query( + `SELECT identity_type, identity_value, verified + FROM user_identities + WHERE user_id = $1`, + [userId] + ); + + const user = userResult.rows[0]; + user.identities = identitiesResult.rows; + + return user; + } catch (error) { + console.error('Error getting user info:', error); + return { id: userId }; + } +} + +/** + * Получение истории диалога + * @param {number} userId - ID пользователя + * @param {number} limit - Максимальное количество сообщений + * @returns {Promise} - История диалога в текстовом формате + */ +async function getConversationHistory(userId, limit = 10) { + try { + // Получение последнего активного диалога пользователя + const conversationResult = await pool.query( + `SELECT id FROM conversations + WHERE user_id = $1 + ORDER BY updated_at DESC + LIMIT 1`, + [userId] + ); + + if (conversationResult.rows.length === 0) { + return ''; + } + + const conversationId = conversationResult.rows[0].id; + + // Получение последних сообщений из диалога + const messagesResult = await pool.query( + `SELECT sender_type, content, created_at + FROM messages + WHERE conversation_id = $1 + ORDER BY created_at DESC + LIMIT $2`, + [conversationId, limit] + ); + + // Формирование истории в текстовом формате + const history = messagesResult.rows + .reverse() + .map((msg) => { + const sender = msg.sender_type === 'user' ? 'Пользователь' : 'ИИ'; + return `${sender}: ${msg.content}`; + }) + .join('\n\n'); + + return history; + } catch (error) { + console.error('Error getting conversation history:', error); + return ''; + } +} + +module.exports = { + processMessage, + getUserInfo, + getConversationHistory, +}; diff --git a/backend/services/documents.js b/backend/services/documents.js deleted file mode 100644 index 565d20a..0000000 --- a/backend/services/documents.js +++ /dev/null @@ -1,47 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const { Document } = require('langchain/document'); -const { RecursiveCharacterTextSplitter } = require('langchain/text_splitter'); - -// Функция для загрузки документов из файлов -async function loadDocumentsFromFiles(directory) { - const documents = []; - - try { - const files = fs.readdirSync(directory); - - for (const file of files) { - const filePath = path.join(directory, file); - const stat = fs.statSync(filePath); - - if (stat.isFile() && (file.endsWith('.txt') || file.endsWith('.md'))) { - const content = fs.readFileSync(filePath, 'utf-8'); - - documents.push( - new Document({ - pageContent: content, - metadata: { - source: filePath, - filename: file, - }, - }) - ); - } - } - - // Разделяем документы на чанки - const textSplitter = new RecursiveCharacterTextSplitter({ - chunkSize: 1000, - chunkOverlap: 200, - }); - - const splitDocs = await textSplitter.splitDocuments(documents); - - return splitDocs; - } catch (error) { - console.error('Error loading documents:', error); - throw error; - } -} - -module.exports = { loadDocumentsFromFiles }; \ No newline at end of file diff --git a/backend/services/emailBot.js b/backend/services/emailBot.js index ce00540..4a80df2 100644 --- a/backend/services/emailBot.js +++ b/backend/services/emailBot.js @@ -1,68 +1,246 @@ +const { pool } = require('../db'); const nodemailer = require('nodemailer'); -const { ChatOllama } = require('@langchain/ollama'); -const { PGVectorStore } = require('@langchain/community/vectorstores/pgvector'); -const { Pool } = require('pg'); const Imap = require('imap'); -const { simpleParser } = require('mailparser'); -const { checkMailServer } = require('../utils/checkMail'); -const { sleep, isValidEmail } = require('../utils/helpers'); -const { linkIdentity, getUserIdByIdentity } = require('../utils/identity-linker'); -require('dotenv').config(); +const simpleParser = require('mailparser').simpleParser; +const { processMessage } = require('./ai-assistant'); -class EmailBotService { - constructor() { - this.enabled = false; - console.log('EmailBotService: Сервис отключен (заглушка)'); +// Конфигурация для отправки писем +const transporter = nodemailer.createTransport({ + host: process.env.EMAIL_SMTP_HOST, + port: process.env.EMAIL_SMTP_PORT, + secure: process.env.EMAIL_SMTP_PORT === '465', // true для 465, false для других портов + auth: { + user: process.env.EMAIL_USER, + pass: process.env.EMAIL_PASSWORD, + }, +}); + +// Конфигурация для получения писем +const imapConfig = { + user: process.env.EMAIL_USER, + password: process.env.EMAIL_PASSWORD, + host: process.env.EMAIL_IMAP_HOST, + port: process.env.EMAIL_IMAP_PORT, + tls: true, + tlsOptions: { rejectUnauthorized: false }, +}; + +/** + * Инициализация сервиса электронной почты + */ +function initEmailBot() { + if (!process.env.EMAIL_USER || !process.env.EMAIL_PASSWORD) { + console.warn('EMAIL_USER or EMAIL_PASSWORD not set, Email integration disabled'); + return null; } - async start() { - console.log('EmailBotService: Запуск сервиса отключен (заглушка)'); - return false; - } + console.log('Email bot initialized'); - async stop() { - console.log('EmailBotService: Остановка сервиса отключена (заглушка)'); - return true; - } + // Запуск проверки почты каждые 5 минут + const checkInterval = 5 * 60 * 1000; // 5 минут + setInterval(checkEmails, checkInterval); - isEnabled() { - return this.enabled; + // Первая проверка при запуске + checkEmails(); + + return { + sendEmail, + checkEmails, + }; +} + +/** + * Проверка новых писем + */ +function checkEmails() { + const imap = new Imap(imapConfig); + + imap.once('ready', () => { + imap.openBox('INBOX', false, (err, box) => { + if (err) { + console.error('Error opening inbox:', err); + return; + } + + // Поиск непрочитанных писем + imap.search(['UNSEEN'], (err, results) => { + if (err) { + console.error('Error searching emails:', err); + return; + } + + if (results.length === 0) { + console.log('No new emails'); + imap.end(); + return; + } + + console.log(`Found ${results.length} new emails`); + + const f = imap.fetch(results, { bodies: '' }); + + f.on('message', (msg, seqno) => { + msg.on('body', (stream, info) => { + simpleParser(stream, async (err, parsed) => { + if (err) { + console.error('Error parsing email:', err); + return; + } + + try { + // Обработка письма + await processEmail(parsed); + + // Пометить как прочитанное + imap.setFlags(results, ['\\Seen'], (err) => { + if (err) { + console.error('Error marking email as read:', err); + } + }); + } catch (error) { + console.error('Error processing email:', error); + } + }); + }); + }); + + f.once('error', (err) => { + console.error('Fetch error:', err); + }); + + f.once('end', () => { + imap.end(); + }); + }); + }); + }); + + imap.once('error', (err) => { + console.error('IMAP error:', err); + }); + + imap.connect(); +} + +/** + * Обработка полученного письма + * @param {Object} email - Распарсенное письмо + */ +async function processEmail(email) { + try { + const from = email.from.value[0].address; + const subject = email.subject; + const text = email.text || ''; + + console.log(`Processing email from ${from}, subject: ${subject}`); + + // Поиск пользователя по email + const userResult = await pool.query( + `SELECT u.* FROM users u + JOIN user_identities ui ON u.id = ui.user_id + WHERE ui.identity_type = 'email' AND ui.identity_value = $1 AND ui.verified = TRUE`, + [from] + ); + + if (userResult.rows.length === 0) { + console.log(`No verified user found for email ${from}`); + // Отправка ответа о необходимости регистрации + await sendEmail( + from, + 'Регистрация в системе', + 'Для использования ИИ-ассистента через email, пожалуйста, зарегистрируйтесь на нашем сайте и подтвердите свой email.' + ); + return; + } + + const user = userResult.rows[0]; + + // Получение или создание диалога + const conversationResult = await pool.query( + `SELECT * FROM conversations + WHERE user_id = $1 + ORDER BY updated_at DESC + LIMIT 1`, + [user.id] + ); + + let conversationId; + + if (conversationResult.rows.length === 0) { + // Создание нового диалога + const newConversationResult = await pool.query( + `INSERT INTO conversations (user_id, title) + VALUES ($1, $2) + RETURNING id`, + [user.id, subject || 'Email диалог'] + ); + + conversationId = newConversationResult.rows[0].id; + } else { + conversationId = conversationResult.rows[0].id; + } + + // Сохранение сообщения пользователя + await pool.query( + `INSERT INTO messages (conversation_id, sender_type, sender_id, content, channel) + VALUES ($1, $2, $3, $4, $5)`, + [conversationId, 'user', user.id, text, 'email'] + ); + + // Обработка сообщения ИИ-ассистентом + const aiResponse = await processMessage(user.id, text, user.language || 'ru'); + + // Сохранение ответа ИИ + await pool.query( + `INSERT INTO messages (conversation_id, sender_type, sender_id, content, channel) + VALUES ($1, $2, $3, $4, $5)`, + [conversationId, 'ai', null, aiResponse, 'email'] + ); + + // Обновление времени последнего обновления диалога + await pool.query( + `UPDATE conversations + SET updated_at = NOW() + WHERE id = $1`, + [conversationId] + ); + + // Отправка ответа пользователю + await sendEmail(from, `Re: ${subject}`, aiResponse); + + console.log(`Sent response to ${from}`); + } catch (error) { + console.error('Error processing email:', error); + throw error; } } -// В обработчике команд добавьте код для связывания аккаунтов -async function processCommand(email, command, args) { - if (command === 'link' && args.length > 0) { - const ethAddress = args[0]; - - // Проверяем формат Ethereum-адреса - if (!/^0x[a-fA-F0-9]{40}$/.test(ethAddress)) { - return 'Неверный формат Ethereum-адреса. Используйте формат 0x...'; - } - - try { - // Получаем ID пользователя по Ethereum-адресу - const userId = await getUserIdByIdentity('ethereum', ethAddress); - - if (!userId) { - return 'Пользователь с таким Ethereum-адресом не найден. Сначала войдите через веб-интерфейс.'; - } - - // Связываем Email-аккаунт с пользователем - const success = await linkIdentity(userId, 'email', email); - - if (success) { - return `Ваш Email-аккаунт успешно связан с Ethereum-адресом ${ethAddress}`; - } else { - return 'Не удалось связать аккаунты. Возможно, этот Email-аккаунт уже связан с другим пользователем.'; - } - } catch (error) { - console.error('Ошибка при связывании аккаунтов:', error); - return 'Произошла ошибка при связывании аккаунтов. Попробуйте позже.'; - } +/** + * Отправка email + * @param {string} to - Адрес получателя + * @param {string} subject - Тема письма + * @param {string} text - Текст письма + * @returns {Promise} - Результат отправки + */ +async function sendEmail(to, subject, text) { + try { + const info = await transporter.sendMail({ + from: process.env.EMAIL_USER, + to, + subject, + text, + }); + + console.log('Email sent:', info.messageId); + return info; + } catch (error) { + console.error('Error sending email:', error); + throw error; } - - // Обработка других команд... } -module.exports = EmailBotService; \ No newline at end of file +module.exports = { + initEmailBot, + sendEmail, + checkEmails, +}; diff --git a/backend/services/index.js b/backend/services/index.js new file mode 100644 index 0000000..a490dec --- /dev/null +++ b/backend/services/index.js @@ -0,0 +1,32 @@ +const { initTelegramBot } = require('./telegram-service'); +const { initEmailBot, sendEmail, checkEmails } = require('./emailBot'); +const { + initializeVectorStore, + getVectorStore, + similaritySearch, + addDocument, +} = require('./vectorStore'); +const { processMessage, getUserInfo, getConversationHistory } = require('./ai-assistant'); +// ... другие импорты + +module.exports = { + // Telegram + initTelegramBot, + + // Email + initEmailBot, + sendEmail, + checkEmails, + + // Vector Store + initializeVectorStore, + getVectorStore, + similaritySearch, + addDocument, + + // AI Assistant + processMessage, + getUserInfo, + getConversationHistory, + // ... другие экспорты +}; diff --git a/backend/services/ollama.js b/backend/services/ollama.js index 336429d..5cc091e 100644 --- a/backend/services/ollama.js +++ b/backend/services/ollama.js @@ -1,7 +1,9 @@ const { ChatOllama } = require('@langchain/ollama'); -const { RetrievalQAChain } = require("langchain/chains"); -const { PromptTemplate } = require("@langchain/core/prompts"); +const { RetrievalQAChain } = require('langchain/chains'); +const { PromptTemplate } = require('@langchain/core/prompts'); const axios = require('axios'); +const { Ollama } = require('ollama'); +const { HumanMessage } = require('@langchain/core/messages'); // Создаем шаблон для контекстного запроса const PROMPT_TEMPLATE = ` @@ -19,17 +21,17 @@ const PROMPT_TEMPLATE = ` // Функция для проверки доступности Ollama async function checkOllamaAvailability() { console.log('Проверка доступности Ollama...'); - + try { // Добавляем таймаут для запроса const response = await axios.get('http://localhost:11434/api/tags', { - timeout: 5000 // 5 секунд таймаут + timeout: 5000, // 5 секунд таймаут }); - + if (response.status === 200) { console.log('Ollama доступен. Доступные модели:'); if (response.data && response.data.models) { - response.data.models.forEach(model => { + response.data.models.forEach((model) => { console.log(`- ${model.name}`); }); } @@ -42,32 +44,45 @@ async function checkOllamaAvailability() { } } -// Функция для прямого запроса к Ollama API -async function directOllamaQuery(message, model = 'mistral') { +// Функция для прямого запроса к Ollama +async function directOllamaQuery(message, language = 'en') { try { - console.log(`Отправка запроса к Ollama (модель: ${model}):`, message); - - // Проверяем доступность Ollama перед отправкой запроса - const isAvailable = await checkOllamaAvailability(); - if (!isAvailable) { - throw new Error('Сервер Ollama недоступен'); + // Всегда используем модель mistral, независимо от языка + const modelName = 'mistral'; + + console.log(`Отправка запроса к Ollama (модель: ${modelName}, язык: ${language}): ${message}`); + + // Проверяем доступность Ollama + console.log('Проверка доступности Ollama...'); + const ollama = new Ollama(); + + try { + const models = await ollama.list(); + console.log('Ollama доступен. Доступные модели:'); + models.models.forEach((model) => { + console.log(`- ${model.name}`); + }); + } catch (error) { + console.error('Ошибка при проверке доступности Ollama:', error); + throw new Error('Ollama недоступен'); } - - // Создаем экземпляр ChatOllama - const ollama = new ChatOllama({ + + console.log('Отправка запроса к Ollama...'); + + const chatModel = new ChatOllama({ baseUrl: 'http://localhost:11434', - model: model, + model: modelName, temperature: 0.7, }); - - console.log('Отправка запроса к Ollama...'); - const result = await ollama.invoke(message); - console.log('Получен ответ от Ollama'); - - return result.content; + + const response = await chatModel.invoke([new HumanMessage(message)]); + + return response.content; } catch (error) { console.error('Ошибка при запросе к Ollama:', error); - throw error; + + // Возвращаем сообщение об ошибке + return 'Извините, произошла ошибка при обработке вашего запроса. Пожалуйста, попробуйте позже.'; } } @@ -98,27 +113,23 @@ async function createOllamaChain(vectorStore) { // Создаем шаблон запроса const prompt = new PromptTemplate({ template: PROMPT_TEMPLATE, - inputVariables: ["context", "query"], + inputVariables: ['context', 'query'], }); console.log('Шаблон запроса создан'); console.log('Получаем retriever из векторного хранилища...'); const retriever = vectorStore.asRetriever(); console.log('Retriever получен'); - + console.log('Создаем цепочку для поиска и ответа...'); // Создаем цепочку для поиска и ответа - const chain = RetrievalQAChain.fromLLM( - model, - retriever, - { - returnSourceDocuments: true, - prompt: prompt, - inputKey: "query", - outputKey: "text", - verbose: true - } - ); + const chain = RetrievalQAChain.fromLLM(model, retriever, { + returnSourceDocuments: true, + prompt: prompt, + inputKey: 'query', + outputKey: 'text', + verbose: true, + }); console.log('Цепочка для поиска и ответа создана'); return chain; @@ -146,4 +157,4 @@ async function getOllamaModel() { } } -module.exports = { getOllamaModel, createOllamaChain, checkOllamaAvailability, directOllamaQuery }; \ No newline at end of file +module.exports = { getOllamaModel, createOllamaChain, checkOllamaAvailability, directOllamaQuery }; diff --git a/backend/services/telegram-service.js b/backend/services/telegram-service.js new file mode 100644 index 0000000..385b821 --- /dev/null +++ b/backend/services/telegram-service.js @@ -0,0 +1,262 @@ +const TelegramBot = require('node-telegram-bot-api'); +const { pool } = require('../db'); +const { processMessage } = require('./ai-assistant'); + +// Инициализация бота +const token = process.env.TELEGRAM_BOT_TOKEN; +let bot = null; + +if (token) { + bot = new TelegramBot(token, { polling: true }); + console.log('Telegram bot initialized'); +} else { + console.warn('TELEGRAM_BOT_TOKEN not set, Telegram integration disabled'); +} + +/** + * Инициализация Telegram бота + */ +function initTelegramBot() { + if (!bot) return; + + // Обработка команды /start + bot.onText(/\/start/, async (msg) => { + const chatId = msg.chat.id; + const userId = msg.from.id; + const username = + msg.from.username || `${msg.from.first_name} ${msg.from.last_name || ''}`.trim(); + + try { + // Проверка существования пользователя + const user = await findOrCreateUser(userId, username, chatId); + + // Приветственное сообщение + bot.sendMessage(chatId, `Привет, ${username}! Я ИИ-ассистент. Чем могу помочь?`); + } catch (error) { + console.error('Error handling /start command:', error); + bot.sendMessage( + chatId, + 'Произошла ошибка при обработке команды. Пожалуйста, попробуйте позже.' + ); + } + }); + + // Обработка текстовых сообщений + bot.on('message', async (msg) => { + if (!msg.text || msg.text.startsWith('/')) return; + + const chatId = msg.chat.id; + const userId = msg.from.id; + const username = + msg.from.username || `${msg.from.first_name} ${msg.from.last_name || ''}`.trim(); + + try { + // Проверка существования пользователя + const user = await findOrCreateUser(userId, username, chatId); + + // Получение или создание диалога + const conversation = await getOrCreateConversation(user.id); + + // Сохранение сообщения пользователя + await saveMessage(conversation.id, 'user', user.id, msg.text, 'telegram'); + + // Обработка сообщения ИИ-ассистентом + const aiResponse = await processMessage(user.id, msg.text, user.language || 'ru'); + + // Сохранение ответа ИИ + await saveMessage(conversation.id, 'ai', null, aiResponse, 'telegram'); + + // Отправка ответа + bot.sendMessage(chatId, aiResponse); + } catch (error) { + console.error('Error processing message:', error); + bot.sendMessage( + chatId, + 'Произошла ошибка при обработке сообщения. Пожалуйста, попробуйте позже.' + ); + } + }); + + console.log('Telegram bot handlers registered'); +} + +/** + * Поиск или создание пользователя по Telegram ID + * @param {number} telegramId - Telegram ID пользователя + * @param {string} username - Имя пользователя + * @param {number} chatId - ID чата + * @returns {Promise} - Информация о пользователе + */ +async function findOrCreateUser(telegramId, username, chatId) { + try { + // Поиск пользователя по Telegram ID + const userIdResult = await pool.query( + `SELECT user_id FROM user_identities + WHERE identity_type = 'telegram' AND identity_value = $1`, + [telegramId.toString()] + ); + + if (userIdResult.rows.length > 0) { + // Пользователь найден + const userId = userIdResult.rows[0].user_id; + + // Получение информации о пользователе + const userResult = await pool.query('SELECT * FROM users WHERE id = $1', [userId]); + + return userResult.rows[0]; + } else { + // Создание нового пользователя + const userResult = await pool.query( + `INSERT INTO users ( + username, + role_id, + is_admin, + language, + address + ) VALUES ( + $1, + (SELECT id FROM roles WHERE name = 'user'), + FALSE, + 'ru', + '0x' || encode(gen_random_bytes(20), 'hex') + ) RETURNING *`, + [username] + ); + + const newUser = userResult.rows[0]; + + // Добавление идентификатора Telegram + await pool.query( + `INSERT INTO user_identities ( + user_id, + identity_type, + identity_value, + verified + ) VALUES ($1, 'telegram', $2, TRUE)`, + [newUser.id, telegramId.toString()] + ); + + // Сохранение метаданных Telegram + await pool.query( + `INSERT INTO user_preferences ( + user_id, + preference_key, + preference_value + ) VALUES ($1, 'telegram_chat_id', $2)`, + [newUser.id, chatId.toString()] + ); + + return newUser; + } + } catch (error) { + console.error('Error finding or creating user:', error); + throw error; + } +} + +/** + * Получение или создание диалога для пользователя + * @param {number} userId - ID пользователя + * @returns {Promise} - Информация о диалоге + */ +async function getOrCreateConversation(userId) { + try { + // Поиск активного диалога + const conversationResult = await pool.query( + `SELECT * FROM conversations + WHERE user_id = $1 + ORDER BY updated_at DESC + LIMIT 1`, + [userId] + ); + + if (conversationResult.rows.length > 0) { + // Обновление времени последней активности + await pool.query('UPDATE conversations SET updated_at = NOW() WHERE id = $1', [ + conversationResult.rows[0].id, + ]); + + return conversationResult.rows[0]; + } else { + // Создание нового диалога + const newConversationResult = await pool.query( + `INSERT INTO conversations (user_id, title) + VALUES ($1, $2) + RETURNING *`, + [userId, 'Диалог в Telegram'] + ); + + return newConversationResult.rows[0]; + } + } catch (error) { + console.error('Error getting or creating conversation:', error); + throw error; + } +} + +/** + * Сохранение сообщения + * @param {number} conversationId - ID диалога + * @param {string} senderType - Тип отправителя ('user', 'ai') + * @param {number|null} senderId - ID отправителя + * @param {string} content - Текст сообщения + * @param {string} channel - Канал ('telegram') + * @returns {Promise} - Информация о сообщении + */ +async function saveMessage(conversationId, senderType, senderId, content, channel) { + try { + const messageResult = await pool.query( + `INSERT INTO messages ( + conversation_id, + sender_type, + sender_id, + content, + channel + ) VALUES ($1, $2, $3, $4, $5) + RETURNING *`, + [conversationId, senderType, senderId, content, channel] + ); + + return messageResult.rows[0]; + } catch (error) { + console.error('Error saving message:', error); + throw error; + } +} + +/** + * Отправка сообщения пользователю через Telegram + * @param {number} userId - ID пользователя + * @param {string} message - Текст сообщения + * @returns {Promise} - Успешность отправки + */ +async function sendMessageToUser(userId, message) { + if (!bot) return false; + + try { + // Получение Telegram chat ID пользователя + const chatIdResult = await pool.query( + `SELECT preference_value FROM user_preferences + WHERE user_id = $1 AND preference_key = 'telegram_chat_id'`, + [userId] + ); + + if (chatIdResult.rows.length === 0) { + return false; + } + + const chatId = chatIdResult.rows[0].preference_value; + + // Отправка сообщения + await bot.sendMessage(chatId, message); + return true; + } catch (error) { + console.error('Error sending message to user:', error); + return false; + } +} + +module.exports = { + initTelegramBot, + sendMessageToUser, +}; diff --git a/backend/services/telegramBot.js b/backend/services/telegramBot.js deleted file mode 100644 index 0fc0e2f..0000000 --- a/backend/services/telegramBot.js +++ /dev/null @@ -1,402 +0,0 @@ -const TelegramBot = require('node-telegram-bot-api'); -const { ChatOllama } = require('@langchain/ollama'); -const axios = require('axios'); -const dns = require('dns').promises; -require('dotenv').config(); -const { sleep } = require('../utils/helpers'); -const util = require('util'); -const exec = util.promisify(require('child_process').exec); -const { linkIdentity, getUserIdByIdentity } = require('../utils/identity-linker'); - -class TelegramBotService { - constructor() { - // Проверяем наличие токена - if (!process.env.TELEGRAM_BOT_TOKEN) { - throw new Error('Token is required'); - } - - this.isRunning = false; - this.maxRetries = 3; - this.retryDelay = 5000; // 5 секунд между попытками - - // Создаем бота без polling - this.bot = new TelegramBot(process.env.TELEGRAM_BOT_TOKEN, { - polling: false, - request: { - proxy: null, - agentOptions: { - rejectUnauthorized: true, - minVersion: 'TLSv1.2' - }, - timeout: 30000 - } - }); - - this.token = process.env.TELEGRAM_BOT_TOKEN; - this.chat = new ChatOllama({ - model: 'mistral', - baseUrl: 'http://localhost:11434' - }); - - // Добавляем настройки прокси для axios - this.axiosConfig = { - timeout: 5000, - proxy: false, - httpsAgent: new (require('https').Agent)({ - rejectUnauthorized: true, - minVersion: 'TLSv1.2' - }) - }; - - this.initialize(); - } - - setupHandlers() { - this.bot.onText(/.*/, async (msg) => { - try { - const chatId = msg.chat.id; - const userQuestion = msg.text; - - // Пропускаем команды - if (userQuestion.startsWith('/')) { - return; - } - - console.log('Получен вопрос:', userQuestion); - - // Используем локальную модель - const result = await this.chat.invoke(userQuestion); - const assistantResponse = result.content; - - await this.bot.sendMessage(chatId, assistantResponse); - } catch (error) { - console.error('Telegram bot error:', error); - await this.bot.sendMessage(msg.chat.id, - 'Извините, произошла ошибка при обработке вашего запроса. ' + - 'Попробуйте повторить позже или обратитесь к администратору.' - ); - } - }); - - this.bot.onText(/\/link (.+)/, async (msg, match) => { - const chatId = msg.chat.id; - const ethAddress = match[1]; - - // Проверяем формат Ethereum-адреса - if (!/^0x[a-fA-F0-9]{40}$/.test(ethAddress)) { - this.bot.sendMessage(chatId, 'Неверный формат Ethereum-адреса. Используйте формат 0x...'); - return; - } - - try { - // Получаем ID пользователя по Ethereum-адресу - const userId = await getUserIdByIdentity('ethereum', ethAddress); - - if (!userId) { - this.bot.sendMessage(chatId, 'Пользователь с таким Ethereum-адресом не найден. Сначала войдите через веб-интерфейс.'); - return; - } - - // Связываем Telegram-аккаунт с пользователем - const success = await linkIdentity(userId, 'telegram', chatId.toString()); - - if (success) { - this.bot.sendMessage(chatId, `Ваш Telegram-аккаунт успешно связан с Ethereum-адресом ${ethAddress}`); - } else { - this.bot.sendMessage(chatId, 'Не удалось связать аккаунты. Возможно, этот Telegram-аккаунт уже связан с другим пользователем.'); - } - } catch (error) { - console.error('Ошибка при связывании аккаунтов:', error); - this.bot.sendMessage(chatId, 'Произошла ошибка при связывании аккаунтов. Попробуйте позже.'); - } - }); - } - - setupCommands() { - this.bot.onText(/\/start/, async (msg) => { - const welcomeMessage = ` - 👋 Здравствуйте! Я - ассистент DApp for Business. - - Я готов помочь вам с вопросами о: - • Разработке dApps - • Блокчейн-технологиях - • Web3 и криптовалютах - - Просто задавайте вопросы, а если нужна помощь - - используйте команду /help - `; - await this.bot.sendMessage(msg.chat.id, welcomeMessage); - }); - - this.bot.onText(/\/help/, async (msg) => { - const helpMessage = ` - 🤖 Я - ассистент DApp for Business - - Я могу помочь вам с: - • Разработкой децентрализованных приложений - • Интеграцией блокчейн-технологий в бизнес - • Консультациями по Web3 и криптовалютам - - Команды: - /start - начать работу с ботом - /help - показать это сообщение - /status - проверить состояние бота - - Просто задавайте вопросы на русском или английском языке! - `; - await this.bot.sendMessage(msg.chat.id, helpMessage); - }); - - this.bot.onText(/\/status/, async (msg) => { - try { - const status = { - isRunning: this.isRunning, - uptime: process.uptime(), - memoryUsage: process.memoryUsage(), - connections: { - telegram: Boolean(this.bot), - ollama: Boolean(this.chat) - } - }; - - const statusMessage = ` - 📊 Статус бота: - - 🟢 Статус: ${status.isRunning ? 'Работает' : 'Остановлен'} - ⏱ Время работы: ${Math.floor(status.uptime / 60)} мин - - 🔗 Подключения: - • Telegram API: ${status.connections.telegram ? '✅' : '❌'} - • Ollama: ${status.connections.ollama ? '✅' : '❌'} - - 💾 Использование памяти: - • Heap: ${Math.round(status.memoryUsage.heapUsed / 1024 / 1024)}MB - • RSS: ${Math.round(status.memoryUsage.rss / 1024 / 1024)}MB - `; - - await this.bot.sendMessage(msg.chat.id, statusMessage); - } catch (error) { - console.error('Ошибка при получении статуса:', error); - await this.bot.sendMessage(msg.chat.id, 'Ошибка при получении статуса бота'); - } - }); - } - - async initialize() { - let retries = 0; - - while (retries < this.maxRetries) { - try { - console.log(`Попытка инициализации Telegram бота (${retries + 1}/${this.maxRetries})...`); - - // Сначала проверяем DNS и доступность - try { - console.log('Проверка DNS для api.telegram.org...'); - const addresses = await dns.resolve4('api.telegram.org'); - console.log('IP адреса api.telegram.org:', addresses); - - // Пинг для проверки доступности (теперь ждем результат) - try { - const { stdout } = await exec('ping -c 1 api.telegram.org'); - console.log('Результат ping:', stdout); - } catch (pingError) { - console.error('Ошибка при выполнении ping:', pingError); - throw new Error('Сервер Telegram недоступен'); - } - } catch (error) { - console.error('Ошибка сетевой проверки:', error); - throw error; - } - - // Затем проверяем API - try { - const response = await axios.get( - `https://api.telegram.org/bot${process.env.TELEGRAM_BOT_TOKEN}/getMe`, - this.axiosConfig - ); - - if (response.status !== 200) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - console.log('Успешное подключение к API Telegram:', { - botInfo: response.data.result - }); - } catch (error) { - console.error('Ошибка при проверке API Telegram:', { - message: error.message, - code: error.code, - response: error.response?.data, - config: { - url: error.config?.url, - method: error.config?.method, - timeout: error.config?.timeout - } - }); - throw error; - } - - // Основная инициализация бота - await this.initBot(); - console.log('Telegram bot service initialized'); - return; - - } catch (error) { - retries++; - console.error('Ошибка при инициализации Telegram бота:', { - name: error.name, - message: error.message, - code: error.code, - response: error.response?.data, - stack: error.stack - }); - - if (retries < this.maxRetries) { - console.log(`Повторная попытка через ${this.retryDelay/1000} секунд...`); - await sleep(this.retryDelay); - } else { - console.error('Превышено максимальное количество попыток подключения к Telegram'); - throw error; - } - } - } - } - - async initBot() { - try { - // Проверяем, не запущен ли уже бот - const webhookInfo = await this.bot.getWebHookInfo(); - - // Если есть webhook или активный polling, пробуем остановить - if (webhookInfo.url || webhookInfo.has_custom_certificate) { - console.log('Удаляем существующий webhook...'); - await this.bot.deleteWebHook(); - await new Promise(resolve => setTimeout(resolve, 2000)); - } - - // Пробуем получить обновления с большим таймаутом - try { - console.log('Проверяем наличие других экземпляров бота...'); - const updates = await this.bot.getUpdates({ - offset: -1, - limit: 1, - timeout: 0 - }); - console.log('Проверка существующих подключений:', updates); - } catch (error) { - if (error.code === 409) { - console.log('Обнаружен активный бот, пробуем остановить...'); - await this.stop(); - await new Promise(resolve => setTimeout(resolve, 5000)); - // Повторная попытка получить обновления - await this.bot.getUpdates({ offset: -1, limit: 1, timeout: 0 }); - } - } - - // Небольшая пауза перед запуском поллинга - await new Promise(resolve => setTimeout(resolve, 1000)); - - // Запускаем polling - console.log('Запускаем polling...'); - await this.bot.startPolling({ - interval: 2000, - params: { - timeout: 10 - } - }); - - this.isRunning = true; - this.setupHandlers(); - this.setupErrorHandlers(); - this.setupCommands(); - } catch (error) { - if (error.code === 409) { - console.log('Бот уже запущен в другом процессе'); - this.isRunning = false; - } else { - console.error('Ошибка при инициализации Telegram бота:', error); - throw error; - } - } - } - - setupErrorHandlers() { - this.bot.on('polling_error', (error) => { - console.error('Telegram polling error:', { - code: error.code, - message: error.message, - stack: error.stack - }); - - // Обработка различных ошибок - if (this.isRunning && (error.code === 'EFATAL' || error.code === 'ETELEGRAM')) { - console.log('Переподключение к Telegram через 5 секунд...'); - setTimeout(async () => { - try { - await this.stop(); - await this.initBot(); - } catch (err) { - console.error('Ошибка при перезапуске бота:', err); - } - }, 5000); - } else if (error.code === 'ECONNRESET' || error.code === 'ECONNREFUSED') { - // Для ошибок соединения пробуем сразу переподключиться - this.bot.startPolling(); - } - }); - - // Обработка других ошибок - this.bot.on('error', (error) => { - console.error('Telegram bot error:', error); - // Пробуем переподключиться при любой ошибке - setTimeout(() => this.bot.startPolling(), 5000); - }); - - // Обработка webhook ошибок - this.bot.on('webhook_error', (error) => { - console.error('Telegram webhook error:', error); - }); - } - - async stop() { - if (this.isRunning) { - console.log('Останавливаем Telegram бота...'); - try { - // Сначала отключаем обработчики - this.bot.removeAllListeners(); - - // Останавливаем поллинг - await this.bot.stopPolling(); - - // Очищаем очередь обновлений - await this.bot.getUpdates({ - offset: -1, - limit: 1, - timeout: 1 - }); - - this.isRunning = false; - console.log('Telegram бот остановлен'); - } catch (error) { - console.error('Ошибка при остановке бота:', error); - // Принудительно отмечаем как остановленный - this.isRunning = false; - } - } - } - - async checkTelegramAvailability() { - const { stdout } = await exec('ping -c 1 api.telegram.org'); - const match = stdout.match(/time=(\d+(\.\d+)?)/); - if (match) { - const pingTime = parseFloat(match[1]); - console.log(`Время отклика Telegram API: ${pingTime}ms`); - if (pingTime > 1000) { // Если пинг больше секунды - console.warn('Внимание: высокая задержка при подключении к Telegram API'); - } - } - return stdout; - } -} - -module.exports = TelegramBotService; \ No newline at end of file diff --git a/backend/services/vectorStore.js b/backend/services/vectorStore.js index ab3699a..b10859a 100644 --- a/backend/services/vectorStore.js +++ b/backend/services/vectorStore.js @@ -1,134 +1,212 @@ -const { HNSWLib } = require("@langchain/community/vectorstores/hnswlib"); -const { OllamaEmbeddings } = require("@langchain/ollama"); -const { DirectoryLoader } = require("langchain/document_loaders/fs/directory"); -const { TextLoader } = require("langchain/document_loaders/fs/text"); -const { RecursiveCharacterTextSplitter } = require("langchain/text_splitter"); +const { HNSWLib } = require('langchain/vectorstores/hnswlib'); +const { OllamaEmbeddings } = require('langchain/embeddings/ollama'); +const { RecursiveCharacterTextSplitter } = require('langchain/text_splitter'); +const { DirectoryLoader } = require('langchain/document_loaders/fs/directory'); +const { TextLoader } = require('langchain/document_loaders/fs/text'); +const { PDFLoader } = require('langchain/document_loaders/fs/pdf'); const fs = require('fs'); const path = require('path'); -// Путь к директории с документами -const DOCS_DIR = path.join(__dirname, '../data/documents'); -// Путь к директории для хранения векторного индекса -const VECTOR_STORE_DIR = path.join(__dirname, '../data/vector_store'); +// Путь к директории для хранения векторной базы данных +const VECTOR_STORE_PATH = path.join(__dirname, '../data/vector_store'); -// Создаем директории, если они не существуют -if (!fs.existsSync(DOCS_DIR)) { - fs.mkdirSync(DOCS_DIR, { recursive: true }); - console.log(`Создана директория для документов: ${DOCS_DIR}`); -} +// Инициализация embeddings с использованием локальной модели Ollama +const embeddings = new OllamaEmbeddings({ + model: process.env.OLLAMA_EMBEDDINGS_MODEL || 'mistral', + baseUrl: process.env.OLLAMA_BASE_URL || 'http://localhost:11434', +}); -if (!fs.existsSync(VECTOR_STORE_DIR)) { - fs.mkdirSync(VECTOR_STORE_DIR, { recursive: true }); - console.log(`Создана директория для векторного хранилища: ${VECTOR_STORE_DIR}`); -} - -// Глобальная переменная для хранения экземпляра векторного хранилища let vectorStore = null; -// Функция для инициализации векторного хранилища +/** + * Инициализация векторного хранилища + */ async function initializeVectorStore() { try { - console.log('Инициализация векторного хранилища...'); - - // Проверяем, существует ли директория с документами - if (!fs.existsSync(DOCS_DIR)) { - console.warn(`Директория с документами не найдена: ${DOCS_DIR}`); - return null; + // Создание директории, если она не существует + if (!fs.existsSync(VECTOR_STORE_PATH)) { + fs.mkdirSync(VECTOR_STORE_PATH, { recursive: true }); + console.log(`Created vector store directory at ${VECTOR_STORE_PATH}`); } - - // Проверяем, есть ли документы в директории - const files = fs.readdirSync(DOCS_DIR); - if (files.length === 0) { - console.warn(`В директории с документами нет файлов: ${DOCS_DIR}`); - return null; - } - - console.log(`Найдено ${files.length} файлов в директории с документами`); - - // Загружаем документы из директории - const loader = new DirectoryLoader( - DOCS_DIR, - { - ".txt": (path) => new TextLoader(path), - ".md": (path) => new TextLoader(path), + + // Проверка наличия файлов индекса + const indexFiles = fs.readdirSync(VECTOR_STORE_PATH); + + if (indexFiles.length > 0 && indexFiles.includes('hnswlib.index')) { + // Загрузка существующего индекса + console.log('Loading existing vector store...'); + try { + vectorStore = await HNSWLib.load(VECTOR_STORE_PATH, embeddings); + console.log('Vector store loaded successfully'); + } catch (loadError) { + console.error('Error loading existing vector store:', loadError); + console.log('Creating new vector store...'); + await createVectorStore(); } - ); - - console.log('Загрузка документов...'); - const docs = await loader.load(); - console.log(`Загружено ${docs.length} документов`); - - if (docs.length === 0) { - console.warn('Не удалось загрузить документы'); - return null; + } else { + // Создание нового индекса + console.log('Creating new vector store...'); + await createVectorStore(); } - - // Разбиваем документы на чанки + + return vectorStore; + } catch (error) { + console.error('Error initializing vector store:', error); + // Создаем пустой векторный индекс в случае ошибки + vectorStore = new HNSWLib(embeddings, { + space: 'cosine', + numDimensions: 4096, // Размерность для Ollama embeddings (зависит от модели) + }); + await vectorStore.save(VECTOR_STORE_PATH); + return vectorStore; + } +} + +/** + * Создание нового векторного хранилища из документов + */ +async function createVectorStore() { + try { + // Проверяем наличие директории documents + const docsPath = path.join(__dirname, '../data/documents'); + + // Если директория documents не существует, проверяем директорию docs + if (!fs.existsSync(docsPath)) { + const altDocsPath = path.join(__dirname, '../data/docs'); + + // Если директория docs существует, используем ее + if (fs.existsSync(altDocsPath)) { + console.log(`Using documents directory at ${altDocsPath}`); + return await processDocumentsDirectory(altDocsPath); + } + + // Иначе создаем директорию documents + fs.mkdirSync(docsPath, { recursive: true }); + console.log(`Created documents directory at ${docsPath}`); + + // Создание примера документа + const sampleDocPath = path.join(docsPath, 'sample.txt'); + fs.writeFileSync(sampleDocPath, 'Это пример документа для векторного хранилища.'); + } + + return await processDocumentsDirectory(docsPath); + } catch (error) { + console.error('Error creating vector store:', error); + throw error; + } +} + +/** + * Обработка директории с документами + * @param {string} docsPath - Путь к директории с документами + */ +async function processDocumentsDirectory(docsPath) { + try { + // Загрузка документов + const loader = new DirectoryLoader(docsPath, { + '.txt': (path) => new TextLoader(path), + '.pdf': (path) => new PDFLoader(path), + }); + + const docs = await loader.load(); + console.log(`Loaded ${docs.length} documents`); + + if (docs.length === 0) { + // Создаем пустой векторный индекс, если нет документов + vectorStore = new HNSWLib(embeddings, { + space: 'cosine', + numDimensions: 4096, // Размерность для Ollama embeddings (зависит от модели) + }); + } else { + // Разделение документов на чанки + const textSplitter = new RecursiveCharacterTextSplitter({ + chunkSize: 1000, + chunkOverlap: 200, + }); + + const splitDocs = await textSplitter.splitDocuments(docs); + console.log(`Split into ${splitDocs.length} chunks`); + + // Создание векторного хранилища + vectorStore = await HNSWLib.fromDocuments(splitDocs, embeddings); + } + + // Сохранение векторного хранилища + await vectorStore.save(VECTOR_STORE_PATH); + console.log('Vector store created and saved successfully'); + + return vectorStore; + } catch (error) { + console.error('Error processing documents directory:', error); + throw error; + } +} + +/** + * Получение векторного хранилища + * @returns {HNSWLib|null} Векторное хранилище + */ +function getVectorStore() { + return vectorStore; +} + +/** + * Поиск похожих документов + * @param {string} query - Запрос для поиска + * @param {number} k - Количество результатов + * @returns {Promise} - Массив похожих документов + */ +async function similaritySearch(query, k = 5) { + if (!vectorStore) { + await initializeVectorStore(); + } + + try { + const results = await vectorStore.similaritySearch(query, k); + return results; + } catch (error) { + console.error('Error performing similarity search:', error); + return []; + } +} + +/** + * Добавление нового документа в векторное хранилище + * @param {string} text - Текст документа + * @param {Object} metadata - Метаданные документа + * @returns {Promise} - Успешность добавления + */ +async function addDocument(text, metadata = {}) { + if (!vectorStore) { + await initializeVectorStore(); + } + + try { + // Разделение документа на чанки const textSplitter = new RecursiveCharacterTextSplitter({ chunkSize: 1000, chunkOverlap: 200, }); - - console.log('Разбиение документов на чанки...'); - const splitDocs = await textSplitter.splitDocuments(docs); - console.log(`Документы разбиты на ${splitDocs.length} чанков`); - - // Создаем эмбеддинги с помощью Ollama - console.log('Создание эмбеддингов...'); - const embeddings = new OllamaEmbeddings({ - model: "mistral", - baseUrl: "http://localhost:11434", - }); - - // Проверяем, существует ли уже векторное хранилище - if (fs.existsSync(path.join(VECTOR_STORE_DIR, 'hnswlib.index'))) { - console.log('Загрузка существующего векторного хранилища...'); - try { - vectorStore = await HNSWLib.load( - VECTOR_STORE_DIR, - embeddings - ); - console.log('Векторное хранилище успешно загружено'); - return vectorStore; - } catch (error) { - console.error('Ошибка при загрузке векторного хранилища:', error); - console.log('Создание нового векторного хранилища...'); - } - } - - // Создаем новое векторное хранилище - console.log('Создание нового векторного хранилища...'); - vectorStore = await HNSWLib.fromDocuments( - splitDocs, - embeddings - ); - - // Сохраняем векторное хранилище - console.log('Сохранение векторного хранилища...'); - await vectorStore.save(VECTOR_STORE_DIR); - console.log('Векторное хранилище успешно сохранено'); - - return vectorStore; + + const docs = await textSplitter.createDocuments([text], [metadata]); + + // Добавление документов в векторное хранилище + await vectorStore.addDocuments(docs); + + // Сохранение обновленного векторного хранилища + await vectorStore.save(VECTOR_STORE_PATH); + + console.log('Document added to vector store successfully'); + return true; } catch (error) { - console.error('Ошибка при инициализации векторного хранилища:', error); - console.log('Приложение продолжит работу без векторного хранилища'); - // Возвращаем заглушку вместо реального хранилища - return { - addDocuments: async () => console.log('Векторное хранилище недоступно: addDocuments'), - similaritySearch: async () => { - console.log('Векторное хранилище недоступно: similaritySearch'); - return []; - } - }; + console.error('Error adding document to vector store:', error); + return false; } } -// Функция для получения экземпляра векторного хранилища -async function getVectorStore() { - if (!vectorStore) { - vectorStore = await initializeVectorStore(); - } - return vectorStore; -} - -module.exports = { initializeVectorStore, getVectorStore }; \ No newline at end of file +module.exports = { + initializeVectorStore, + getVectorStore, + similaritySearch, + addDocument, +}; diff --git a/backend/test/AccessToken.test.js b/backend/test/AccessToken.test.js index f1a0dac..e5bb043 100644 --- a/backend/test/AccessToken.test.js +++ b/backend/test/AccessToken.test.js @@ -1,7 +1,7 @@ -const { expect } = require("chai"); -const { ethers } = require("hardhat"); +const { expect } = require('chai'); +const { ethers } = require('hardhat'); -describe("AccessToken", function () { +describe('AccessToken', function () { let AccessToken; let accessToken; let owner; @@ -10,36 +10,32 @@ describe("AccessToken", function () { beforeEach(async function () { [owner, addr1, addr2] = await ethers.getSigners(); - AccessToken = await ethers.getContractFactory("AccessToken"); + AccessToken = await ethers.getContractFactory('AccessToken'); accessToken = await AccessToken.deploy(); }); - describe("Minting", function () { - it("Should mint admin token", async function () { + describe('Minting', function () { + it('Should mint admin token', async function () { await accessToken.mintAccessToken(addr1.address, 0); expect(await accessToken.checkRole(addr1.address)).to.equal(0); }); - it("Should mint moderator token", async function () { + it('Should mint moderator token', async function () { await accessToken.mintAccessToken(addr1.address, 1); expect(await accessToken.checkRole(addr1.address)).to.equal(1); }); }); - describe("Access Control", function () { - it("Should fail for non-token holders", async function () { - await expect( - accessToken.checkRole(addr1.address) - ).to.be.revertedWith("No active token"); + describe('Access Control', function () { + it('Should fail for non-token holders', async function () { + await expect(accessToken.checkRole(addr1.address)).to.be.revertedWith('No active token'); }); - it("Should revoke access", async function () { + it('Should revoke access', async function () { await accessToken.mintAccessToken(addr1.address, 0); const tokenId = await accessToken.activeTokens(addr1.address); await accessToken.revokeToken(tokenId); - await expect( - accessToken.checkRole(addr1.address) - ).to.be.revertedWith("No active token"); + await expect(accessToken.checkRole(addr1.address)).to.be.revertedWith('No active token'); }); }); -}); \ No newline at end of file +}); diff --git a/backend/utils/access-check.js b/backend/utils/access-check.js index c01e652..de9390d 100644 --- a/backend/utils/access-check.js +++ b/backend/utils/access-check.js @@ -1,7 +1,10 @@ const { ethers } = require('ethers'); require('dotenv').config(); +const db = require('../db'); const contractArtifact = require('../artifacts/contracts/MyContract.sol/MyContract.json'); const contractABI = contractArtifact.abi; +const logger = require('./logger'); +const { getContract } = require('./contracts'); // Проверяем наличие необходимых переменных окружения if (!process.env.ACCESS_TOKEN_ADDRESS) { @@ -18,11 +21,7 @@ let accessToken; try { const AccessTokenABI = require('../artifacts/contracts/AccessToken.sol/AccessToken.json').abi; - accessToken = new ethers.Contract( - process.env.ACCESS_TOKEN_ADDRESS, - AccessTokenABI, - provider - ); + accessToken = new ethers.Contract(process.env.ACCESS_TOKEN_ADDRESS, AccessTokenABI, provider); } catch (error) { console.error('Ошибка инициализации контракта AccessToken:', error); } @@ -49,10 +48,10 @@ async function checkAccess(address) { const roles = ['ADMIN', 'MODERATOR', 'SUPPORT']; const role = roles[roleId]; - return { - hasAccess: true, + return { + hasAccess: true, role, - tokenId: activeTokenId.toString() + tokenId: activeTokenId.toString(), }; } catch (error) { console.error('Access check error:', error); @@ -60,30 +59,138 @@ async function checkAccess(address) { } } -async function checkAdmin(address) { +// Функция для проверки, является ли пользователь администратором +async function checkIfAdmin(address) { try { console.log('Проверка прав администратора для адреса:', address); - // Проверяем, является ли пользователь администратором через смарт-контракт - const contract = new ethers.Contract( - process.env.CONTRACT_ADDRESS, - contractABI, - provider - ); - - console.log('Контракт инициализирован:', { - address: process.env.CONTRACT_ADDRESS, - provider: provider.connection.url - }); - - const isAdmin = await contract.isAdmin(address); - console.log('Результат проверки из контракта:', isAdmin); - + + // Проверяем в базе данных + const result = await db.query('SELECT is_admin FROM users WHERE address = $1', [address]); + + if (result.rows.length === 0) { + console.log(`Пользователь с адресом ${address} не найден в базе данных`); + return false; + } + + const isAdmin = result.rows[0].is_admin; + console.log(`Пользователь с адресом ${address} имеет статус администратора:`, isAdmin); + return isAdmin; } catch (error) { console.error('Ошибка при проверке прав администратора:', error); - // В случае ошибки возвращаем false вместо выброса исключения return false; } } -module.exports = { checkAccess, checkAdmin }; \ No newline at end of file +/** + * Проверяет баланс токенов пользователя и обновляет его роль + * @param {string} address - Адрес кошелька пользователя + * @returns {Promise} - Имеет ли пользователь права администратора + */ +async function checkTokenBalanceAndUpdateRole(address) { + try { + // Получение контракта токенов + const accessTokenContract = await getContract('AccessToken'); + + // Проверка баланса + const balance = await accessTokenContract.balanceOf(address); + + // Минимальное количество токенов для прав администратора + const minTokens = ethers.utils.parseUnits(process.env.MIN_ADMIN_TOKENS || "1", 18); + + const isAdmin = balance.gte(minTokens); + + // Получение ID пользователя по адресу кошелька + const userResult = await db.query(` + SELECT u.id FROM users u + JOIN user_identities ui ON u.id = ui.user_id + WHERE ui.identity_type = 'wallet' AND ui.identity_value = $1 + `, [address.toLowerCase()]); + + if (userResult.rows.length > 0) { + const userId = userResult.rows[0].id; + + // Получение ID роли + const roleResult = await db.query( + 'SELECT id FROM roles WHERE name = $1', + [isAdmin ? 'admin' : 'user'] + ); + + if (roleResult.rows.length > 0) { + const roleId = roleResult.rows[0].id; + + // Обновление роли пользователя + await db.query( + 'UPDATE users SET role_id = $1, last_token_check = NOW() WHERE id = $2', + [roleId, userId] + ); + + logger.info(`Updated user ${userId} role to ${isAdmin ? 'admin' : 'user'} based on token balance`); + } + } + + return isAdmin; + } catch (error) { + logger.error(`Error checking token balance for ${address}: ${error.message}`); + return false; + } +} + +/** + * Получает информацию о пользователе, включая его роль + * @param {number} userId - ID пользователя + * @returns {Promise} - Информация о пользователе + */ +async function getUserInfo(userId) { + try { + const result = await db.query(` + SELECT u.id, u.username, u.preferred_language, r.name as role + FROM users u + LEFT JOIN roles r ON u.role_id = r.id + WHERE u.id = $1 + `, [userId]); + + if (result.rows.length === 0) { + return null; + } + + return result.rows[0]; + } catch (error) { + logger.error(`Error getting user info for ${userId}: ${error.message}`); + return null; + } +} + +/** + * Запускает проверку токенов для всех пользователей + */ +async function checkAllUsersTokens() { + try { + // Получение всех пользователей с кошельками + const walletUsers = await db.query(` + SELECT u.id, ui.identity_value as address + FROM users u + JOIN user_identities ui ON u.id = ui.user_id + WHERE ui.identity_type = 'wallet' + `); + + logger.info(`Checking token balances for ${walletUsers.rows.length} users`); + + for (const user of walletUsers.rows) { + // Проверка баланса токенов + const hasTokens = await checkTokenBalanceAndUpdateRole(user.address); + + logger.info(`User ${user.id} with address ${user.address}: admin=${hasTokens}`); + } + } catch (error) { + logger.error(`Error checking token balances: ${error.message}`); + } +} + +module.exports = { + checkAccess, + checkIfAdmin, + checkTokenBalanceAndUpdateRole, + getUserInfo, + checkAllUsersTokens +}; diff --git a/backend/utils/auth.js b/backend/utils/auth.js index d2850c6..b310700 100644 --- a/backend/utils/auth.js +++ b/backend/utils/auth.js @@ -2,13 +2,11 @@ const { SiweMessage } = require('siwe'); const { ethers } = require('ethers'); const AccessTokenABI = require('../artifacts/contracts/AccessToken.sol/AccessToken.json').abi; require('dotenv').config(); +const { pool } = require('../db'); -const provider = new ethers.JsonRpcProvider(process.env.ETHEREUM_NETWORK_URL); -const accessToken = new ethers.Contract( - process.env.ACCESS_TOKEN_ADDRESS, - AccessTokenABI, - provider -); +// В ethers.js v6.x используется JsonRpcProvider напрямую +const provider = new ethers.JsonRpcProvider(process.env.RPC_URL); +const accessToken = new ethers.Contract(process.env.ACCESS_TOKEN_ADDRESS, AccessTokenABI, provider); // Проверяем наличие адреса контракта if (!process.env.ACCESS_TOKEN_ADDRESS) { @@ -25,13 +23,13 @@ if (!process.env.ACCESS_TOKEN_ADDRESS) { async function verifySignature(message, signature, address) { try { // Формируем сообщение для проверки - const domain = message.domain || window.location.host; + const domain = message.domain || 'localhost'; const statement = message.statement || 'Sign in with Ethereum to the app.'; - const uri = message.uri || window.location.origin; + const uri = message.uri || 'http://localhost:8000'; const version = message.version || '1'; const chainId = message.chainId || '1'; const nonce = message.nonce; - + const messageToVerify = `${domain} wants you to sign in with your Ethereum account: ${address} @@ -42,10 +40,10 @@ Version: ${version} Chain ID: ${chainId} Nonce: ${nonce} `; - - // Восстанавливаем адрес из подписи + + // В ethers.js v6.x используется verifyMessage напрямую const recoveredAddress = ethers.verifyMessage(messageToVerify, signature); - + return recoveredAddress.toLowerCase() === address.toLowerCase(); } catch (error) { console.error('Signature verification error:', error); @@ -62,23 +60,125 @@ async function verifyAndCheckAccess(message, signature, address) { // Проверяем подпись const verified = await verifySignature(message, signature, address); if (!verified) { - return { + return { verified: false, - access: { hasAccess: false } + access: { hasAccess: false }, }; } // Проверяем доступ const access = await checkAccess(address); - + return { verified: true, - access + access, }; } +// Функция для поиска или создания пользователя +async function findOrCreateUser(identifier, identityType = 'wallet') { + try { + // Проверяем, является ли адрес адресом администратора + const isAdmin = identityType === 'wallet' && + identifier.toLowerCase() === process.env.ADMIN_WALLET_ADDRESS.toLowerCase(); + + console.log(`Проверка на администратора: ${identifier.toLowerCase()} === ${process.env.ADMIN_WALLET_ADDRESS.toLowerCase()} = ${isAdmin}`); + + // Проверяем, существует ли пользователь с таким идентификатором + const identityResult = await pool.query( + 'SELECT user_id FROM user_identities WHERE identity_type = $1 AND identity_value = $2', + [identityType, identifier.toLowerCase()] + ); + + let userId; + let isNewUser = false; + + if (identityResult.rows.length > 0) { + // Пользователь найден + userId = identityResult.rows[0].user_id; + console.log(`Найден существующий пользователь с ID: ${userId}`); + + // Обновляем статус администратора, если это необходимо + if (isAdmin) { + await pool.query( + 'UPDATE users SET is_admin = true WHERE id = $1', + [userId] + ); + console.log(`Обновлен статус администратора для пользователя ${userId}`); + } + } else { + // Создаем нового пользователя с явным указанием всех необходимых полей + const username = `user_${Date.now()}`; + + // Проверяем существование роли USER или ADMIN + const roleName = isAdmin ? 'ADMIN' : 'USER'; + const roleCheck = await pool.query('SELECT id FROM roles WHERE name = $1', [roleName]); + let roleId; + + if (roleCheck.rows.length > 0) { + roleId = roleCheck.rows[0].id; + } else { + // Если роли нет, создаем её + const newRole = await pool.query( + 'INSERT INTO roles (name, description) VALUES ($1, $2) RETURNING id', + [roleName, isAdmin ? 'Administrator role' : 'Regular user role'] + ); + roleId = newRole.rows[0].id; + } + + // Создаем пользователя с обязательными полями + const userResult = await pool.query( + `INSERT INTO users (username, role_id, address, created_at, is_admin) + VALUES ($1, $2, $3, NOW(), $4) + RETURNING id`, + [username, roleId, identifier.toLowerCase(), isAdmin] + ); + + userId = userResult.rows[0].id; + isNewUser = true; + + // Создаем запись в таблице идентификаторов + await pool.query( + 'INSERT INTO user_identities (user_id, identity_type, identity_value, verified, created_at) VALUES ($1, $2, $3, true, NOW())', + [userId, identityType, identifier.toLowerCase()] + ); + + console.log(`Создан новый пользователь с ID: ${userId}, isAdmin: ${isAdmin}`); + } + + // Получаем информацию о пользователе + try { + const userInfo = await pool.query( + `SELECT u.id, u.username, u.is_admin, r.name as role + FROM users u + JOIN roles r ON u.role_id = r.id + WHERE u.id = $1`, + [userId] + ); + + if (userInfo.rows.length > 0) { + return userInfo.rows[0]; + } + } catch (err) { + console.error('Ошибка при получении информации о пользователе:', err); + } + + // Если не удалось получить полную информацию, возвращаем базовую + return { + id: userId, + username: isNewUser ? `user_${Date.now()}` : 'unknown', + is_admin: isAdmin, + role: isAdmin ? 'ADMIN' : 'USER' + }; + } catch (error) { + console.error('Ошибка при поиске/создании пользователя:', error); + throw error; + } +} + module.exports = { verifyAndCheckAccess, verifySignature, - checkAccess -}; \ No newline at end of file + checkAccess, + findOrCreateUser +}; diff --git a/backend/utils/checkMail.js b/backend/utils/checkMail.js index 78cbc0f..c912771 100644 --- a/backend/utils/checkMail.js +++ b/backend/utils/checkMail.js @@ -14,4 +14,4 @@ async function checkMailServer(domain) { } } -module.exports = { checkMailServer }; \ No newline at end of file +module.exports = { checkMailServer }; diff --git a/backend/utils/contracts.js b/backend/utils/contracts.js new file mode 100644 index 0000000..9fa2693 --- /dev/null +++ b/backend/utils/contracts.js @@ -0,0 +1,46 @@ +const { ethers } = require('ethers'); +const fs = require('fs'); +const path = require('path'); +const logger = require('./logger'); + +/** + * Получает экземпляр контракта по его имени + * @param {string} contractName - Имя контракта (например, 'AccessToken') + * @returns {Promise} - Экземпляр контракта + */ +async function getContract(contractName) { + try { + // Путь к артефакту контракта + const artifactPath = path.join(__dirname, '..', 'artifacts', 'contracts', `${contractName}.sol`, `${contractName}.json`); + + // Проверка существования файла + if (!fs.existsSync(artifactPath)) { + throw new Error(`Артефакт контракта ${contractName} не найден по пути ${artifactPath}`); + } + + // Загрузка ABI из артефакта + const contractArtifact = require(artifactPath); + const contractABI = contractArtifact.abi; + + // Получение адреса контракта из переменных окружения + const contractAddress = process.env[`${contractName.toUpperCase()}_ADDRESS`]; + if (!contractAddress) { + throw new Error(`Адрес контракта ${contractName} не найден в переменных окружения`); + } + + // Подключение к провайдеру + const provider = new ethers.JsonRpcProvider(process.env.ETHEREUM_NETWORK_URL); + + // Создание экземпляра контракта + const contract = new ethers.Contract(contractAddress, contractABI, provider); + + return contract; + } catch (error) { + logger.error(`Ошибка при получении контракта ${contractName}: ${error.message}`); + throw error; + } +} + +module.exports = { + getContract +}; diff --git a/backend/utils/db.js b/backend/utils/db.js new file mode 100644 index 0000000..59d55ce --- /dev/null +++ b/backend/utils/db.js @@ -0,0 +1,2 @@ +// Реэкспорт основного модуля db +module.exports = require('../db'); diff --git a/backend/utils/helpers.js b/backend/utils/helpers.js index 9d31835..e6fdecd 100644 --- a/backend/utils/helpers.js +++ b/backend/utils/helpers.js @@ -1,6 +1,6 @@ // Функция для создания задержки function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); + return new Promise((resolve) => setTimeout(resolve, ms)); } // Функция для валидации email адреса @@ -11,5 +11,5 @@ function isValidEmail(email) { module.exports = { sleep, - isValidEmail -}; \ No newline at end of file + isValidEmail, +}; diff --git a/backend/utils/identity-linker.js b/backend/utils/identity-linker.js index 843a16c..7033dac 100644 --- a/backend/utils/identity-linker.js +++ b/backend/utils/identity-linker.js @@ -3,7 +3,7 @@ const { Pool } = require('pg'); // Подключение к БД const pool = new Pool({ connectionString: process.env.DATABASE_URL, - ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false + ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false, }); /** @@ -20,7 +20,7 @@ async function linkIdentity(userId, identityType, identityValue) { 'SELECT * FROM user_identities WHERE identity_type = $1 AND identity_value = $2', [identityType, identityValue] ); - + if (existingResult.rows.length > 0) { // Если идентификатор уже связан с другим пользователем, возвращаем ошибку if (existingResult.rows[0].user_id !== userId) { @@ -30,13 +30,13 @@ async function linkIdentity(userId, identityType, identityValue) { // Если идентификатор уже связан с этим пользователем, ничего не делаем return true; } - + // Добавляем новую связь await pool.query( 'INSERT INTO user_identities (user_id, identity_type, identity_value, created_at) VALUES ($1, $2, $3, NOW())', [userId, identityType, identityValue] ); - + console.log(`Successfully linked ${identityType}:${identityValue} to user ${userId}`); return true; } catch (error) { @@ -57,11 +57,11 @@ async function getUserIdByIdentity(identityType, identityValue) { 'SELECT user_id FROM user_identities WHERE identity_type = $1 AND identity_value = $2', [identityType, identityValue] ); - + if (result.rows.length === 0) { return null; } - + return result.rows[0].user_id; } catch (error) { console.error('Error getting user ID by identity:', error); @@ -80,7 +80,7 @@ async function getUserIdentities(userId) { 'SELECT identity_type, identity_value FROM user_identities WHERE user_id = $1', [userId] ); - + return result.rows; } catch (error) { console.error('Error getting user identities:', error); @@ -91,5 +91,5 @@ async function getUserIdentities(userId) { module.exports = { linkIdentity, getUserIdByIdentity, - getUserIdentities -}; \ No newline at end of file + getUserIdentities, +}; diff --git a/backend/utils/logger.js b/backend/utils/logger.js index 73c6076..7d2cf4d 100644 --- a/backend/utils/logger.js +++ b/backend/utils/logger.js @@ -3,25 +3,19 @@ const path = require('path'); const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', - format: winston.format.combine( - winston.format.timestamp(), - winston.format.json() - ), + format: winston.format.combine(winston.format.timestamp(), winston.format.json()), transports: [ new winston.transports.Console({ - format: winston.format.combine( - winston.format.colorize(), - winston.format.simple() - ) + format: winston.format.combine(winston.format.colorize(), winston.format.simple()), }), new winston.transports.File({ filename: path.join(__dirname, '../logs/error.log'), - level: 'error' + level: 'error', }), new winston.transports.File({ - filename: path.join(__dirname, '../logs/combined.log') - }) - ] + filename: path.join(__dirname, '../logs/combined.log'), + }), + ], }); -module.exports = logger; \ No newline at end of file +module.exports = logger; diff --git a/backend/utils/wallet.js b/backend/utils/wallet.js new file mode 100644 index 0000000..9b4b1ce --- /dev/null +++ b/backend/utils/wallet.js @@ -0,0 +1,4 @@ +import { ethers } from 'ethers'; + +const provider = new ethers.JsonRpcProvider(process.env.ETHEREUM_NETWORK_URL); +const wallet = new ethers.Wallet(process.env.PRIVATE_KEY, provider); diff --git a/backend/yarn.lock b/backend/yarn.lock index 4322b18..9fc138b 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -70,29 +70,108 @@ enabled "2.0.x" kuler "^2.0.0" -"@langchain/community@^0.0.32": - version "0.0.32" - resolved "https://registry.yarnpkg.com/@langchain/community/-/community-0.0.32.tgz#d0e4ce8eeb8152cc9c3ace402e97cefd1ef5ce74" - integrity sha512-jN4BxGKAmLbA87hqXH5Mx1IRMMVOgcn1TY1MLOVyBcBa12EvHFx8suogtXgA2ekfc8U8nIryVb1ftSupwUBv/A== +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" + integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== dependencies: - "@langchain/core" "~0.1.32" - "@langchain/openai" "~0.0.14" - flat "^5.0.2" - langsmith "~0.1.1" - uuid "^9.0.0" - zod "^3.22.3" + eslint-visitor-keys "^3.4.3" -"@langchain/community@~0.0.47": - version "0.0.57" - resolved "https://registry.yarnpkg.com/@langchain/community/-/community-0.0.57.tgz#9d77c5acb74a4a8ec01d2cefb71dcd4088701c44" - integrity sha512-tib4UJNkyA4TPNsTNChiBtZmThVJBr7X/iooSmKeCr+yUEha2Yxly3A4OAO95Vlpj4Q+od8HAfCbZih/1XqAMw== +"@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/config-array@^0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.2.tgz#3060b809e111abfc97adb0bb1172778b90cb46aa" + integrity sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w== dependencies: - "@langchain/core" "~0.1.60" - "@langchain/openai" "~0.0.28" + "@eslint/object-schema" "^2.1.6" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/core@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.12.0.tgz#5f960c3d57728be9f6c65bd84aa6aa613078798e" + integrity sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/eslintrc@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.0.tgz#96a558f45842989cca7ea1ecd785ad5491193846" + integrity sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@9.21.0": + version "9.21.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.21.0.tgz#4303ef4e07226d87c395b8fad5278763e9c15c08" + integrity sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw== + +"@eslint/object-schema@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" + integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== + +"@eslint/plugin-kit@^0.2.7": + version "0.2.7" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz#9901d52c136fb8f375906a73dcc382646c3b6a27" + integrity sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g== + dependencies: + "@eslint/core" "^0.12.0" + levn "^0.4.1" + +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== + +"@humanwhocodes/retry@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.2.tgz#1860473de7dfa1546767448f333db80cb0ff2161" + integrity sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ== + +"@langchain/community@^0.3.34": + version "0.3.34" + resolved "https://registry.yarnpkg.com/@langchain/community/-/community-0.3.34.tgz#fde49311216e7bf73b6c540ae8acd38e85a31197" + integrity sha512-s0KVulgVIPd90s3m6XZtWrCRGQPWsY93uY62seFMmNhzcyF+I65kKnN04Nbiouthrn/YJ9HB4hW8MJAFuX6RRg== + dependencies: + "@langchain/openai" ">=0.2.0 <0.5.0" + binary-extensions "^2.2.0" expr-eval "^2.0.2" flat "^5.0.2" - langsmith "~0.1.1" - uuid "^9.0.0" + js-yaml "^4.1.0" + langchain ">=0.2.3 <0.3.0 || >=0.3.4 <0.4.0" + langsmith ">=0.2.8 <0.4.0" + uuid "^10.0.0" zod "^3.22.3" zod-to-json-schema "^3.22.5" @@ -113,40 +192,21 @@ zod "^3.22.4" zod-to-json-schema "^3.22.3" -"@langchain/core@>0.1.56 <0.3.0", "@langchain/core@>0.2.0 <0.3.0": - version "0.2.36" - resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.2.36.tgz#75754c33aa5b9310dcf117047374a1ae011005a4" - integrity sha512-qHLvScqERDeH7y2cLuJaSAlMwg3f/3Oc9nayRSXRU2UuaK/SOhI42cxiPLj1FnuHJSmN0rBQFkrLx02gI4mcVg== +"@langchain/core@~0.0.6": + version "0.0.11" + resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.0.11.tgz#d769c499720d40efb2147579c3137a78def350d5" + integrity sha512-tiESyyHM1KO1gRTduKcznWbEmE7z/ayPLWZ4+AUXF47qOtdV6lymnlMPzz+MGwnpaSaamzyYkBIxqeMPar256Q== dependencies: ansi-styles "^5.0.0" camelcase "6" decamelize "1.2.0" - js-tiktoken "^1.0.12" - langsmith "^0.1.56-rc.1" - mustache "^4.2.0" - p-queue "^6.6.2" - p-retry "4" - uuid "^10.0.0" - zod "^3.22.4" - zod-to-json-schema "^3.22.3" - -"@langchain/core@~0.1.32", "@langchain/core@~0.1.60": - version "0.1.63" - resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.1.63.tgz#33cc48877739e9fdb5885fbd4b16fd08d1597050" - integrity sha512-+fjyYi8wy6x1P+Ee1RWfIIEyxd9Ee9jksEwvrggPwwI/p45kIDTdYTblXsM13y4mNWTiACyLSdbwnPaxxdoz+w== - dependencies: - ansi-styles "^5.0.0" - camelcase "6" - decamelize "1.2.0" - js-tiktoken "^1.0.12" - langsmith "~0.1.7" + js-tiktoken "^1.0.8" + langsmith "~0.0.48" ml-distance "^4.0.0" - mustache "^4.2.0" p-queue "^6.6.2" p-retry "4" uuid "^9.0.0" - zod "^3.22.4" - zod-to-json-schema "^3.22.3" + zod "^3.22.3" "@langchain/ollama@^0.2.0": version "0.2.0" @@ -158,23 +218,21 @@ zod "^3.24.1" zod-to-json-schema "^3.24.1" -"@langchain/openai@~0.0.14", "@langchain/openai@~0.0.28": - version "0.0.34" - resolved "https://registry.yarnpkg.com/@langchain/openai/-/openai-0.0.34.tgz#36c9bca0721ab9f7e5d40927e7c0429cacbd5b56" - integrity sha512-M+CW4oXle5fdoz2T2SwdOef8pl3/1XmUx1vjn2mXUVM/128aO0l23FMF0SNBsAbRV6P+p/TuzjodchJbi0Ht/A== +"@langchain/openai@>=0.1.0 <0.5.0", "@langchain/openai@>=0.2.0 <0.5.0": + version "0.4.4" + resolved "https://registry.yarnpkg.com/@langchain/openai/-/openai-0.4.4.tgz#1832420495c53c28aa4e6515583bad8f0b83a637" + integrity sha512-UZybJeMd8+UX7Kn47kuFYfqKdBCeBUWNqDtmAr6ZUIMMnlsNIb6MkrEEhGgAEjGCpdT4CU8U/DyyddTz+JayOQ== dependencies: - "@langchain/core" ">0.1.56 <0.3.0" js-tiktoken "^1.0.12" - openai "^4.41.1" + openai "^4.77.0" zod "^3.22.4" zod-to-json-schema "^3.22.3" -"@langchain/textsplitters@~0.0.0": - version "0.0.3" - resolved "https://registry.yarnpkg.com/@langchain/textsplitters/-/textsplitters-0.0.3.tgz#1a3cc93dd2ab330edb225400ded190a22fea14e3" - integrity sha512-cXWgKE3sdWLSqAa8ykbCcUsUF1Kyr5J3HOWYGuobhPEycXW4WI++d5DhzdpL238mzoEXTi90VqfSCra37l5YqA== +"@langchain/textsplitters@>=0.0.0 <0.2.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@langchain/textsplitters/-/textsplitters-0.1.0.tgz#f37620992192df09ecda3dfbd545b36a6bcbae46" + integrity sha512-djI4uw9rlkAb5iMhtLED+xJebDdAG935AdP4eRTB02R7OB/act55Bj9wsskhZsvuyQRpO4O1wQOp85s6T6GWmw== dependencies: - "@langchain/core" ">0.2.0 <0.3.0" js-tiktoken "^1.0.12" "@noble/curves@1.2.0": @@ -237,6 +295,21 @@ resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36" integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg== +"@types/estree@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/luxon@~3.4.0": + version "3.4.2" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.4.2.tgz#e4fc7214a420173cea47739c33cdf10874694db7" + integrity sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA== + "@types/node-fetch@^2.6.4": version "2.6.12" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.12.tgz#8ab5c3ef8330f13100a7479e2cd56d3386830a03" @@ -281,6 +354,11 @@ resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d" integrity sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ== +"@types/uuid@^9.0.1": + version "9.0.8" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.8.tgz#7545ba4fc3c003d6c756f651f3bf163d8f0f29ba" + integrity sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -296,6 +374,16 @@ accepts@~1.3.8: mime-types "~2.1.34" negotiator "0.6.3" +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.14.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + aes-js@4.0.0-beta.5: version "4.0.0-beta.5" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873" @@ -308,6 +396,23 @@ agentkeepalive@^4.2.1: dependencies: humanize-ms "^1.2.1" +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + ansi-styles@^5.0.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" @@ -558,6 +663,11 @@ call-bound@^1.0.2, call-bound@^1.0.3: call-bind-apply-helpers "^1.0.1" get-intrinsic "^1.2.6" +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + camelcase@6: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" @@ -568,6 +678,14 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== +chalk@^4.0.0, chalk@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + charenc@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" @@ -595,12 +713,19 @@ color-convert@^1.9.3: dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + color-name@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== -color-name@^1.0.0: +color-name@^1.0.0, color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== @@ -653,6 +778,13 @@ connect-pg-simple@^10.0.0: dependencies: pg "^8.12.0" +console-table-printer@^2.12.1: + version "2.12.1" + resolved "https://registry.yarnpkg.com/console-table-printer/-/console-table-printer-2.12.1.tgz#4a9646537a246a6d8de57075d4fae1e08abae267" + integrity sha512-wKGOQRRvdnd89pCeH96e2Fn4wkbenSP6LMHfjfyNLMbGuHEFbMqQNuxXqd0oXG9caIOQ1FTvc5Uijp9/4jujnQ== + dependencies: + simple-wcswidth "^1.0.1" + content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -708,6 +840,23 @@ cors@^2.8.5: object-assign "^4" vary "^1" +cron@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cron/-/cron-4.1.0.tgz#1207e890c4c705b9b1ee3b799fb9d9ec17d54792" + integrity sha512-wmcuXr2qP0UZStYgwruG6jC2AYSO9n5VMm2t93hmcEXEjWY3S2bsXe3sfGUrTs/uQ1AvRCrZ0Pp9Q032L/V9tw== + dependencies: + "@types/luxon" "~3.4.0" + luxon "~3.5.0" + +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + crypt@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" @@ -780,11 +929,23 @@ debug@^3.2.7: dependencies: ms "^2.1.1" +debug@^4, debug@^4.3.1, debug@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + decamelize@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + deepmerge@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" @@ -1030,12 +1191,113 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-prettier@^10.0.2: + version "10.0.2" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.0.2.tgz#47444de8aa104ce82c2f91ad2a5e96b62c01e20d" + integrity sha512-1105/17ZIMjmCOJOPNfVdbXafLCLj3hPmkmB7dLgt7XsQ/zkxSuDerE/xgO3RxoHysR1N1whmquY0lSn2O0VLg== + +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^9.21.0: + version "9.21.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.21.0.tgz#b1c9c16f5153ff219791f627b94ab8f11f811591" + integrity sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.19.2" + "@eslint/core" "^0.12.0" + "@eslint/eslintrc" "^3.3.0" + "@eslint/js" "9.21.0" + "@eslint/plugin-kit" "^0.2.7" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.2" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + +espree@^10.0.1, espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + +esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -ethers@^6.7.1: +ethers@6.13.5: version "6.13.5" resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.5.tgz#8c1d6ac988ac08abc3c1d8fabbd4b8b602851ac4" integrity sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ== @@ -1139,11 +1401,33 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + fecha@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd" integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw== +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + file-type@^3.9.0: version "3.9.0" resolved "https://registry.yarnpkg.com/file-type/-/file-type-3.9.0.tgz#257a078384d1db8087bc449d107d52a52672b9e9" @@ -1174,11 +1458,32 @@ finalhandler@1.3.1: statuses "2.0.1" unpipe "~1.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + flat@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== +flatted@^3.2.9: + version "3.3.3" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + fn.name@1.x.x: version "1.1.0" resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" @@ -1310,6 +1615,13 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -1317,6 +1629,16 @@ glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globals@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-16.0.0.tgz#3d7684652c5c4fbd086ec82f9448214da49382d8" + integrity sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A== + globalthis@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.4.tgz#7430ed3a975d97bfb59bcce41f5cabbafa651236" @@ -1345,6 +1667,11 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" @@ -1474,6 +1801,11 @@ ignore-by-default@^1.0.1: resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09" integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== +ignore@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + imap@^0.8.19: version "0.8.19" resolved "https://registry.yarnpkg.com/imap/-/imap-0.8.19.tgz#3678873934ab09cea6ba48741f284da2af59d8d5" @@ -1482,6 +1814,14 @@ imap@^0.8.19: readable-stream "1.1.x" utf7 ">=1.0.2" +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" @@ -1607,7 +1947,7 @@ is-generator-function@^1.0.10: has-tostringtag "^1.0.2" safe-regex-test "^1.1.0" -is-glob@^4.0.1, is-glob@~4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -1723,12 +2063,17 @@ isarray@~1.0.0: resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== -js-tiktoken@^1.0.12, js-tiktoken@^1.0.7: +js-tiktoken@^1.0.12, js-tiktoken@^1.0.7, js-tiktoken@^1.0.8: version "1.0.19" resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.19.tgz#0298b584382f1d47d4b45cb93d382f11780eab78" integrity sha512-XC63YQeEcS47Y53gg950xiZ4IWmkfMe4p2V9OSaBt26q+p47WHn18izuXzSclCI73B7yGqtfRsT6jcZQI0y08g== @@ -1747,11 +2092,26 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + json-schema@0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -1779,6 +2139,13 @@ jsprim@^2.0.2: json-schema "0.4.0" verror "1.10.0" +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + kruptein@^2.0.4: version "2.2.3" resolved "https://registry.yarnpkg.com/kruptein/-/kruptein-2.2.3.tgz#e09a3942b8072ac71728d10fe089293e0b5e7d42" @@ -1791,36 +2158,67 @@ kuler@^2.0.0: resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== -langchain@^0.1.21: - version "0.1.37" - resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.1.37.tgz#15db8ca5c24afc39e61773cab69e216dfb38e1bb" - integrity sha512-rpaLEJtRrLYhAViEp7/aHfSkxbgSqHJ5n10tXv3o4kHP/wOin85RpTgewwvGjEaKc3797jOg+sLSk6a7e0UlMg== +langchain@0.0.200: + version "0.0.200" + resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.0.200.tgz#695364f45575e66a550d15cab1aa77ee81dab0fc" + integrity sha512-ljuwCLPd+NIp8sRtrI0zSHE17ZFbMODOc46JZjnXq0nt9QTF74S3K83y9una+U+w/r0iMmKY8H4QCHThULYHpg== dependencies: "@anthropic-ai/sdk" "^0.9.1" - "@langchain/community" "~0.0.47" - "@langchain/core" "~0.1.60" - "@langchain/openai" "~0.0.28" - "@langchain/textsplitters" "~0.0.0" + "@langchain/core" "~0.0.6" binary-extensions "^2.2.0" + expr-eval "^2.0.2" + flat "^5.0.2" js-tiktoken "^1.0.7" js-yaml "^4.1.0" jsonpointer "^5.0.1" - langchainhub "~0.0.8" - langsmith "~0.1.7" + langchainhub "~0.0.6" + langsmith "~0.0.48" ml-distance "^4.0.0" + openai "^4.19.0" openapi-types "^12.1.3" p-retry "4" uuid "^9.0.0" yaml "^2.2.1" + zod "^3.22.3" + zod-to-json-schema "3.20.3" + +"langchain@>=0.2.3 <0.3.0 || >=0.3.4 <0.4.0": + version "0.3.19" + resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.3.19.tgz#a0329036c4c870018897b91af2ac442c1f07294b" + integrity sha512-aGhoTvTBS5ulatA67RHbJ4bcV5zcYRYdm5IH+hpX99RYSFXG24XF3ghSjhYi6sxW+SUnEQ99fJhA5kroVpKNhw== + dependencies: + "@langchain/openai" ">=0.1.0 <0.5.0" + "@langchain/textsplitters" ">=0.0.0 <0.2.0" + js-tiktoken "^1.0.12" + js-yaml "^4.1.0" + jsonpointer "^5.0.1" + langsmith ">=0.2.8 <0.4.0" + openapi-types "^12.1.3" + p-retry "4" + uuid "^10.0.0" + yaml "^2.2.1" zod "^3.22.4" zod-to-json-schema "^3.22.3" -langchainhub@~0.0.8: +langchainhub@~0.0.6: version "0.0.11" resolved "https://registry.yarnpkg.com/langchainhub/-/langchainhub-0.0.11.tgz#2ce22def9c84699dcbd4fd4b78270d34bd2a9ae9" integrity sha512-WnKI4g9kU2bHQP136orXr2bcRdgz9iiTBpTN0jWt9IlScUKnJBoD0aa2HOzHURQKeQDnt2JwqVmQ6Depf5uDLQ== -langsmith@^0.1.43, langsmith@^0.1.56-rc.1, langsmith@~0.1.1, langsmith@~0.1.7: +"langsmith@>=0.2.8 <0.4.0": + version "0.3.12" + resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.3.12.tgz#5c0080dc53f14ed655f02065076f844afb261855" + integrity sha512-e4qWM27hxEr8GfO6dgXrc3W8La+wxkX1zEtMhxhqS/Th2ujTt5OH7x0uXfXFDqCv9WaC3nquo1Y2s4vpYmLLtg== + dependencies: + "@types/uuid" "^10.0.0" + chalk "^4.1.2" + console-table-printer "^2.12.1" + p-queue "^6.6.2" + p-retry "4" + semver "^7.6.3" + uuid "^10.0.0" + +langsmith@^0.1.43: version "0.1.68" resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.1.68.tgz#848332e822fe5e6734a07f1c36b6530cc1798afb" integrity sha512-otmiysWtVAqzMx3CJ4PrtUBhWRG5Co8Z4o7hSZENPjlit9/j3/vm3TSvbaxpDYakZxtMjhkcJTqrdYFipISEiQ== @@ -1832,11 +2230,30 @@ langsmith@^0.1.43, langsmith@^0.1.56-rc.1, langsmith@~0.1.1, langsmith@~0.1.7: semver "^7.6.3" uuid "^10.0.0" +langsmith@~0.0.48: + version "0.0.70" + resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.0.70.tgz#797be2b26da18843a94a802b6a73c91b72e8042b" + integrity sha512-QFHrzo/efBowGPCxtObv7G40/OdwqQfGshavMbSJtHBgX+OMqnn4lCMqVeEwTdyue4lEcpwAsGNg5Vty91YIyw== + dependencies: + "@types/uuid" "^9.0.1" + commander "^10.0.1" + p-queue "^6.6.2" + p-retry "4" + uuid "^9.0.0" + leac@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/leac/-/leac-0.6.0.tgz#dcf136e382e666bd2475f44a1096061b70dc0912" integrity sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg== +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + libbase64@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-1.3.0.tgz#053314755a05d2e5f08bbfc48d0290e9322f4406" @@ -1864,6 +2281,18 @@ linkify-it@5.0.0: dependencies: uc.micro "^2.0.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + lodash@^4.17.15: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" @@ -1881,6 +2310,11 @@ logform@^2.7.0: safe-stable-stringify "^2.3.1" triple-beam "^1.3.0" +luxon@~3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.5.0.tgz#6b6f65c5cd1d61d1fd19dbf07ee87a50bf4b8e20" + integrity sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ== + mailparser@^3.7.2: version "3.7.2" resolved "https://registry.yarnpkg.com/mailparser/-/mailparser-3.7.2.tgz#00feec656e23c0ae805163581b460c2f72ca75d1" @@ -2005,7 +2439,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@2.1.3, ms@^2.0.0, ms@^2.1.1: +ms@2.1.3, ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -2015,6 +2449,11 @@ mustache@^4.2.0: resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + negotiator@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" @@ -2025,6 +2464,13 @@ node-addon-api@^8.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-8.3.1.tgz#53bc8a4f8dbde3de787b9828059da94ba9fd4eed" integrity sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA== +node-cron@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/node-cron/-/node-cron-3.0.3.tgz#c4bc7173dd96d96c50bdb51122c64415458caff2" + integrity sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A== + dependencies: + uuid "8.3.2" + node-domexception@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" @@ -2037,10 +2483,10 @@ node-fetch@^2.6.7: dependencies: whatwg-url "^5.0.0" -node-telegram-bot-api@^0.64.0: - version "0.64.0" - resolved "https://registry.yarnpkg.com/node-telegram-bot-api/-/node-telegram-bot-api-0.64.0.tgz#2f21f4095aaa1bb61705af8e1206008c19a045fe" - integrity sha512-/gxCuaEDUyWMBiHInP0ufopUkaaKprXiv3lyP9MMZdPy2KPfYKNYNKfd1Ph7o9KhfURDtOYowPZCi4UCr+2caw== +node-telegram-bot-api@^0.66.0: + version "0.66.0" + resolved "https://registry.yarnpkg.com/node-telegram-bot-api/-/node-telegram-bot-api-0.66.0.tgz#9ad27e357b30fd3a89a37365ed95a82a70ac9f94" + integrity sha512-s4Hrg5q+VPl4/tJVG++pImxF6eb8tNJNj4KnDqAOKL6zGU34lo9RXmyAN158njwGN+v8hdNf8s9fWIYW9hPb5A== dependencies: "@cypress/request" "^3.0.1" "@cypress/request-promise" "^5.0.0" @@ -2057,23 +2503,23 @@ nodemailer@6.9.16: resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.16.tgz#3ebdf6c6f477c571c0facb0727b33892635e0b8b" integrity sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ== -nodemailer@^6.9.9: +nodemailer@^6.10.0: version "6.10.0" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.10.0.tgz#1f24c9de94ad79c6206f66d132776b6503003912" integrity sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA== -nodemon@^2.0.22: - version "2.0.22" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.22.tgz#182c45c3a78da486f673d6c1702e00728daf5258" - integrity sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ== +nodemon@^3.1.9: + version "3.1.9" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.1.9.tgz#df502cdc3b120e1c3c0c6e4152349019efa7387b" + integrity sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg== dependencies: chokidar "^3.5.2" - debug "^3.2.7" + debug "^4" ignore-by-default "^1.0.1" minimatch "^3.1.2" pstree.remy "^1.1.8" - semver "^5.7.1" - simple-update-notifier "^1.0.7" + semver "^7.5.3" + simple-update-notifier "^2.0.0" supports-color "^5.5.0" touch "^3.1.0" undefsafe "^2.0.5" @@ -2148,7 +2594,7 @@ one-time@^1.0.0: dependencies: fn.name "1.x.x" -openai@^4.41.1: +openai@^4.19.0, openai@^4.77.0: version "4.86.1" resolved "https://registry.yarnpkg.com/openai/-/openai-4.86.1.tgz#4147252d5e6255e2ae716ea59b1d4e54a1c1472a" integrity sha512-x3iCLyaC3yegFVZaxOmrYJjitKxZ9hpVbLi+ZlT5UHuHTMlEQEbKXkGOM78z9qm2T5GF+XRUZCP2/aV4UPFPJQ== @@ -2166,6 +2612,18 @@ openapi-types@^12.1.3: resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.1.3.tgz#471995eb26c4b97b7bd356aacf7b91b73e777dd3" integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw== +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + own-keys@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/own-keys/-/own-keys-1.0.1.tgz#e4006910a2bf913585289676eebd6f390cf51358" @@ -2180,6 +2638,20 @@ p-finally@^1.0.0: resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-queue@^6.6.2: version "6.6.2" resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" @@ -2203,6 +2675,13 @@ p-timeout@^3.2.0: dependencies: p-finally "^1.0.0" +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parseley@^0.12.0: version "0.12.1" resolved "https://registry.yarnpkg.com/parseley/-/parseley-0.12.1.tgz#4afd561d50215ebe259e3e7a853e62f600683aef" @@ -2216,6 +2695,16 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-to-regexp@0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7" @@ -2319,6 +2808,16 @@ postgres-interval@^1.1.0: dependencies: xtend "^4.0.0" +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5" + integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw== + process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" @@ -2483,6 +2982,11 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" @@ -2553,12 +3057,7 @@ selderee@^0.11.0: dependencies: parseley "^0.12.0" -semver@^5.7.1: - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - -semver@^7.6.3: +semver@^7.5.3, semver@^7.6.3: version "7.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== @@ -2568,11 +3067,6 @@ semver@~5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" integrity sha512-mfmm3/H9+67MCVix1h+IXTpDwL6710LyHuk7+cWC9T1mE0qz4iHhh6r4hU2wrIT9iTsAAC2XQRvfblL028cpLw== -semver@~7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - send@0.19.0: version "0.19.0" resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8" @@ -2655,6 +3149,18 @@ setprototypeof@1.2.0: resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + side-channel-list@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad" @@ -2707,12 +3213,17 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" -simple-update-notifier@^1.0.7: - version "1.1.0" - resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82" - integrity sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg== +simple-update-notifier@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" + integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== dependencies: - semver "~7.0.0" + semver "^7.5.3" + +simple-wcswidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz#8ab18ac0ae342f9d9b629604e54d2aa1ecb018b2" + integrity sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg== siwe@^2.1.4: version "2.3.2" @@ -2815,6 +3326,11 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -2822,6 +3338,13 @@ supports-color@^5.5.0: dependencies: has-flag "^3.0.0" +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + text-hex@1.0.x: version "1.0.0" resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" @@ -2915,6 +3438,13 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -3032,7 +3562,7 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -uri-js@^4.4.1: +uri-js@^4.2.2, uri-js@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== @@ -3064,16 +3594,16 @@ utils-merge@1.0.1: resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== +uuid@8.3.2, uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + uuid@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - uuid@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" @@ -3178,6 +3708,13 @@ which-typed-array@^1.1.16, which-typed-array@^1.1.18: gopd "^1.2.0" has-tostringtag "^1.0.2" +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + winston-transport@^4.9.0: version "4.9.0" resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.9.0.tgz#3bba345de10297654ea6f33519424560003b3bf9" @@ -3204,6 +3741,11 @@ winston@^3.17.0: triple-beam "^1.3.0" winston-transport "^4.9.0" +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -3234,6 +3776,16 @@ yaml@^2.2.1: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.7.0.tgz#aef9bb617a64c937a9a748803786ad8d3ffe1e98" integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zod-to-json-schema@3.20.3: + version "3.20.3" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.20.3.tgz#8c95d8c20f20455ffa0b4b526c29703f35f6d787" + integrity sha512-/Q3wnyxAfCt94ZcrGiXXoiAfRqasxl9CX64LZ9fj+4dKH68zulUtU0uk1WMxQPfAxQ0ZI70dKzcoW7hHj+DwSQ== + zod-to-json-schema@^3.22.3, zod-to-json-schema@^3.22.5, zod-to-json-schema@^3.24.1: version "3.24.3" resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.3.tgz#5958ba111d681f8d01c5b6b647425c9b8a6059e7" diff --git a/frontend/.prettierrc b/frontend/.prettierrc new file mode 100644 index 0000000..c2ea83d --- /dev/null +++ b/frontend/.prettierrc @@ -0,0 +1,10 @@ +{ + "endOfLine": "lf", + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "es5", + "printWidth": 100, + "vueIndentScriptAndStyle": true, + "singleAttributePerLine": false +} \ No newline at end of file diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..0f514fd --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,64 @@ +import globals from 'globals'; +import vuePlugin from 'eslint-plugin-vue'; +import prettierPlugin from 'eslint-plugin-prettier'; +import prettierConfig from '@vue/eslint-config-prettier'; + +export default [ + { + ignores: ['node_modules/**', 'dist/**', 'public/**'], + }, + { + files: ['**/*.js'], + languageOptions: { + ecmaVersion: 2022, + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.es2021, + process: 'readonly', + __dirname: 'readonly', + }, + }, + rules: { + 'no-unused-vars': 'off', + 'no-console': 'off', + 'no-undef': 'error', + 'no-duplicate-imports': 'error', + }, + }, + { + files: ['**/*.vue'], + languageOptions: { + ecmaVersion: 2022, + sourceType: 'module', + globals: { + ...globals.browser, + ...globals.es2021, + }, + parser: vuePlugin.parser, + parserOptions: { + ecmaFeatures: { + jsx: true, + }, + }, + }, + plugins: { + vue: vuePlugin, + prettier: prettierPlugin, + }, + processor: vuePlugin.processors['.vue'], + rules: { + ...prettierConfig.rules, + 'vue/multi-word-component-names': 'off', + 'vue/no-unused-vars': 'warn', + 'vue/html-self-closing': ['warn', { + html: { + void: 'always', + normal: 'always', + component: 'always' + } + }], + 'vue/component-name-in-template-casing': ['warn', 'PascalCase'], + }, + }, +]; diff --git a/frontend/package-lock.json b/frontend/package-lock.json deleted file mode 100644 index 637ddf2..0000000 --- a/frontend/package-lock.json +++ /dev/null @@ -1,2064 +0,0 @@ -{ - "name": "frontend", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "frontend", - "version": "0.1.0", - "dependencies": { - "axios": "^1.3.4", - "buffer": "^6.0.3", - "connect-pg-simple": "^10.0.0", - "ethers": "^5.7.2", - "pinia": "^2.0.33", - "siwe": "^2.1.4", - "vue": "^3.2.47", - "vue-router": "^4.1.6" - }, - "devDependencies": { - "@vitejs/plugin-vue": "^4.1.0", - "axios-mock-adapter": "^2.1.0", - "rollup": "^3.29.4", - "rollup-plugin-polyfill-node": "^0.12.0", - "vite": "^4.2.1" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.26.9" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz", - "integrity": "sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } - }, - "node_modules/@ethersproject/abi": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.8.0.tgz", - "integrity": "sha512-b9YS/43ObplgyV6SlyQsG53/vkSal0MNA1fskSC4mbnCMi8R+NkcH8K9FPYNESf6jUefBUniE4SOKms0E/KK1Q==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/address": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/hash": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/strings": "^5.8.0" - } - }, - "node_modules/@ethersproject/abstract-provider": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.8.0.tgz", - "integrity": "sha512-wC9SFcmh4UK0oKuLJQItoQdzS/qZ51EJegK6EmAWlh+OptpQ/npECOR3QqECd8iGHC0RJb4WKbVdSfif4ammrg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/networks": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/transactions": "^5.8.0", - "@ethersproject/web": "^5.8.0" - } - }, - "node_modules/@ethersproject/abstract-signer": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.8.0.tgz", - "integrity": "sha512-N0XhZTswXcmIZQdYtUnd79VJzvEwXQw6PK0dTl9VoYrEBxxCPXqS0Eod7q5TNKRxe1/5WUMuR0u0nqTF/avdCA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-provider": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0" - } - }, - "node_modules/@ethersproject/address": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.8.0.tgz", - "integrity": "sha512-GhH/abcC46LJwshoN+uBNoKVFPxUuZm6dA257z0vZkKmU1+t8xTn8oK7B9qrj8W2rFRMch4gbJl6PmVxjxBEBA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/rlp": "^5.8.0" - } - }, - "node_modules/@ethersproject/base64": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.8.0.tgz", - "integrity": "sha512-lN0oIwfkYj9LbPx4xEkie6rAMJtySbpOAFXSDVQaBnAzYfB4X2Qr+FXJGxMoc3Bxp2Sm8OwvzMrywxyw0gLjIQ==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0" - } - }, - "node_modules/@ethersproject/basex": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.8.0.tgz", - "integrity": "sha512-PIgTszMlDRmNwW9nhS6iqtVfdTAKosA7llYXNmGPw4YAI1PUyMv28988wAb41/gHF/WqGdoLv0erHaRcHRKW2Q==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/properties": "^5.8.0" - } - }, - "node_modules/@ethersproject/bignumber": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.8.0.tgz", - "integrity": "sha512-ZyaT24bHaSeJon2tGPKIiHszWjD/54Sz8t57Toch475lCLljC6MgPmxk7Gtzz+ddNN5LuHea9qhAe0x3D+uYPA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "bn.js": "^5.2.1" - } - }, - "node_modules/@ethersproject/bytes": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.8.0.tgz", - "integrity": "sha512-vTkeohgJVCPVHu5c25XWaWQOZ4v+DkGoC42/TS2ond+PARCxTJvgTFUNDZovyQ/uAQ4EcpqqowKydcdmRKjg7A==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/constants": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.8.0.tgz", - "integrity": "sha512-wigX4lrf5Vu+axVTIvNsuL6YrV4O5AXl5ubcURKMEME5TnWBouUh0CDTWxZ2GpnRn1kcCgE7l8O5+VbV9QTTcg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0" - } - }, - "node_modules/@ethersproject/contracts": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.8.0.tgz", - "integrity": "sha512-0eFjGz9GtuAi6MZwhb4uvUM216F38xiuR0yYCjKJpNfSEy4HUM8hvqqBj9Jmm0IUz8l0xKEhWwLIhPgxNY0yvQ==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abi": "^5.8.0", - "@ethersproject/abstract-provider": "^5.8.0", - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/address": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/transactions": "^5.8.0" - } - }, - "node_modules/@ethersproject/hash": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.8.0.tgz", - "integrity": "sha512-ac/lBcTbEWW/VGJij0CNSw/wPcw9bSRgCB0AIBz8CvED/jfvDoV9hsIIiWfvWmFEi8RcXtlNwp2jv6ozWOsooA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/address": "^5.8.0", - "@ethersproject/base64": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/strings": "^5.8.0" - } - }, - "node_modules/@ethersproject/hdnode": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.8.0.tgz", - "integrity": "sha512-4bK1VF6E83/3/Im0ERnnUeWOY3P1BZml4ZD3wcH8Ys0/d1h1xaFt6Zc+Dh9zXf9TapGro0T4wvO71UTCp3/uoA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/basex": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/pbkdf2": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/sha2": "^5.8.0", - "@ethersproject/signing-key": "^5.8.0", - "@ethersproject/strings": "^5.8.0", - "@ethersproject/transactions": "^5.8.0", - "@ethersproject/wordlists": "^5.8.0" - } - }, - "node_modules/@ethersproject/json-wallets": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.8.0.tgz", - "integrity": "sha512-HxblNck8FVUtNxS3VTEYJAcwiKYsBIF77W15HufqlBF9gGfhmYOJtYZp8fSDZtn9y5EaXTE87zDwzxRoTFk11w==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/address": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/hdnode": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/pbkdf2": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/random": "^5.8.0", - "@ethersproject/strings": "^5.8.0", - "@ethersproject/transactions": "^5.8.0", - "aes-js": "3.0.0", - "scrypt-js": "3.0.1" - } - }, - "node_modules/@ethersproject/keccak256": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.8.0.tgz", - "integrity": "sha512-A1pkKLZSz8pDaQ1ftutZoaN46I6+jvuqugx5KYNeQOPqq+JZ0Txm7dlWesCHB5cndJSu5vP2VKptKf7cksERng==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "js-sha3": "0.8.0" - } - }, - "node_modules/@ethersproject/logger": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.8.0.tgz", - "integrity": "sha512-Qe6knGmY+zPPWTC+wQrpitodgBfH7XoceCGL5bJVejmH+yCS3R8jJm8iiWuvWbG76RUmyEG53oqv6GMVWqunjA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT" - }, - "node_modules/@ethersproject/networks": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.8.0.tgz", - "integrity": "sha512-egPJh3aPVAzbHwq8DD7Po53J4OUSsA1MjQp8Vf/OZPav5rlmWUaFLiq8cvQiGK0Z5K6LYzm29+VA/p4RL1FzNg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/pbkdf2": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.8.0.tgz", - "integrity": "sha512-wuHiv97BrzCmfEaPbUFpMjlVg/IDkZThp9Ri88BpjRleg4iePJaj2SW8AIyE8cXn5V1tuAaMj6lzvsGJkGWskg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/sha2": "^5.8.0" - } - }, - "node_modules/@ethersproject/properties": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.8.0.tgz", - "integrity": "sha512-PYuiEoQ+FMaZZNGrStmN7+lWjlsoufGIHdww7454FIaGdbe/p5rnaCXTr5MtBYl3NkeoVhHZuyzChPeGeKIpQw==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/providers": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.8.0.tgz", - "integrity": "sha512-3Il3oTzEx3o6kzcg9ZzbE+oCZYyY+3Zh83sKkn4s1DZfTUjIegHnN2Cm0kbn9YFy45FDVcuCLLONhU7ny0SsCw==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-provider": "^5.8.0", - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/address": "^5.8.0", - "@ethersproject/base64": "^5.8.0", - "@ethersproject/basex": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/hash": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/networks": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/random": "^5.8.0", - "@ethersproject/rlp": "^5.8.0", - "@ethersproject/sha2": "^5.8.0", - "@ethersproject/strings": "^5.8.0", - "@ethersproject/transactions": "^5.8.0", - "@ethersproject/web": "^5.8.0", - "bech32": "1.1.4", - "ws": "8.18.0" - } - }, - "node_modules/@ethersproject/random": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.8.0.tgz", - "integrity": "sha512-E4I5TDl7SVqyg4/kkA/qTfuLWAQGXmSOgYyO01So8hLfwgKvYK5snIlzxJMk72IFdG/7oh8yuSqY2KX7MMwg+A==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/rlp": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.8.0.tgz", - "integrity": "sha512-LqZgAznqDbiEunaUvykH2JAoXTT9NV0Atqk8rQN9nx9SEgThA/WMx5DnW8a9FOufo//6FZOCHZ+XiClzgbqV9Q==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/sha2": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.8.0.tgz", - "integrity": "sha512-dDOUrXr9wF/YFltgTBYS0tKslPEKr6AekjqDW2dbn1L1xmjGR+9GiKu4ajxovnrDbwxAKdHjW8jNcwfz8PAz4A==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "hash.js": "1.1.7" - } - }, - "node_modules/@ethersproject/signing-key": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.8.0.tgz", - "integrity": "sha512-LrPW2ZxoigFi6U6aVkFN/fa9Yx/+4AtIUe4/HACTvKJdhm0eeb107EVCIQcrLZkxaSIgc/eCrX8Q1GtbH+9n3w==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "bn.js": "^5.2.1", - "elliptic": "6.6.1", - "hash.js": "1.1.7" - } - }, - "node_modules/@ethersproject/solidity": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.8.0.tgz", - "integrity": "sha512-4CxFeCgmIWamOHwYN9d+QWGxye9qQLilpgTU0XhYs1OahkclF+ewO+3V1U0mvpiuQxm5EHHmv8f7ClVII8EHsA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/sha2": "^5.8.0", - "@ethersproject/strings": "^5.8.0" - } - }, - "node_modules/@ethersproject/strings": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.8.0.tgz", - "integrity": "sha512-qWEAk0MAvl0LszjdfnZ2uC8xbR2wdv4cDabyHiBh3Cldq/T8dPH3V4BbBsAYJUeonwD+8afVXld274Ls+Y1xXg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/transactions": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.8.0.tgz", - "integrity": "sha512-UglxSDjByHG0TuU17bDfCemZ3AnKO2vYrL5/2n2oXvKzvb7Cz+W9gOWXKARjp2URVwcWlQlPOEQyAviKwT4AHg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/address": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/rlp": "^5.8.0", - "@ethersproject/signing-key": "^5.8.0" - } - }, - "node_modules/@ethersproject/units": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.8.0.tgz", - "integrity": "sha512-lxq0CAnc5kMGIiWW4Mr041VT8IhNM+Pn5T3haO74XZWFulk7wH1Gv64HqE96hT4a7iiNMdOCFEBgaxWuk8ETKQ==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/constants": "^5.8.0", - "@ethersproject/logger": "^5.8.0" - } - }, - "node_modules/@ethersproject/wallet": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.8.0.tgz", - "integrity": "sha512-G+jnzmgg6UxurVKRKvw27h0kvG75YKXZKdlLYmAHeF32TGUzHkOFd7Zn6QHOTYRFWnfjtSSFjBowKo7vfrXzPA==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abstract-provider": "^5.8.0", - "@ethersproject/abstract-signer": "^5.8.0", - "@ethersproject/address": "^5.8.0", - "@ethersproject/bignumber": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/hash": "^5.8.0", - "@ethersproject/hdnode": "^5.8.0", - "@ethersproject/json-wallets": "^5.8.0", - "@ethersproject/keccak256": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/random": "^5.8.0", - "@ethersproject/signing-key": "^5.8.0", - "@ethersproject/transactions": "^5.8.0", - "@ethersproject/wordlists": "^5.8.0" - } - }, - "node_modules/@ethersproject/web": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.8.0.tgz", - "integrity": "sha512-j7+Ksi/9KfGviws6Qtf9Q7KCqRhpwrYKQPs+JBA/rKVFF/yaWLHJEH3zfVP2plVu+eys0d2DlFmhoQJayFewcw==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/base64": "^5.8.0", - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/strings": "^5.8.0" - } - }, - "node_modules/@ethersproject/wordlists": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.8.0.tgz", - "integrity": "sha512-2df9bbXicZws2Sb5S6ET493uJ0Z84Fjr3pC4tu/qlnZERibZCeUVuqdtt+7Tv9xxhUxHoIekIA7avrKUWHrezg==", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/bytes": "^5.8.0", - "@ethersproject/hash": "^5.8.0", - "@ethersproject/logger": "^5.8.0", - "@ethersproject/properties": "^5.8.0", - "@ethersproject/strings": "^5.8.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@noble/hashes": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz", - "integrity": "sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==", - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@rollup/plugin-inject": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz", - "integrity": "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/pluginutils": "^5.0.1", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.3" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz", - "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@spruceid/siwe-parser": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@spruceid/siwe-parser/-/siwe-parser-2.1.2.tgz", - "integrity": "sha512-d/r3S1LwJyMaRAKQ0awmo9whfXeE88Qt00vRj91q5uv5ATtWIQEGJ67Yr5eSZw5zp1/fZCXZYuEckt8lSkereQ==", - "license": "Apache-2.0", - "dependencies": { - "@noble/hashes": "^1.1.2", - "apg-js": "^4.3.0", - "uri-js": "^4.4.1", - "valid-url": "^1.0.9" - } - }, - "node_modules/@stablelib/binary": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz", - "integrity": "sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==", - "license": "MIT", - "dependencies": { - "@stablelib/int": "^1.0.1" - } - }, - "node_modules/@stablelib/int": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz", - "integrity": "sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w==", - "license": "MIT" - }, - "node_modules/@stablelib/random": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", - "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", - "license": "MIT", - "dependencies": { - "@stablelib/binary": "^1.0.1", - "@stablelib/wipe": "^1.0.1" - } - }, - "node_modules/@stablelib/wipe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz", - "integrity": "sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg==", - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@vitejs/plugin-vue": { - "version": "4.6.2", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.0.0 || ^5.0.0", - "vue": "^3.2.25" - } - }, - "node_modules/@vue/compiler-core": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz", - "integrity": "sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/shared": "3.5.13", - "entities": "^4.5.0", - "estree-walker": "^2.0.2", - "source-map-js": "^1.2.0" - } - }, - "node_modules/@vue/compiler-dom": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz", - "integrity": "sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==", - "license": "MIT", - "dependencies": { - "@vue/compiler-core": "3.5.13", - "@vue/shared": "3.5.13" - } - }, - "node_modules/@vue/compiler-sfc": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz", - "integrity": "sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.3", - "@vue/compiler-core": "3.5.13", - "@vue/compiler-dom": "3.5.13", - "@vue/compiler-ssr": "3.5.13", - "@vue/shared": "3.5.13", - "estree-walker": "^2.0.2", - "magic-string": "^0.30.11", - "postcss": "^8.4.48", - "source-map-js": "^1.2.0" - } - }, - "node_modules/@vue/compiler-ssr": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz", - "integrity": "sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==", - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.13", - "@vue/shared": "3.5.13" - } - }, - "node_modules/@vue/devtools-api": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", - "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", - "license": "MIT" - }, - "node_modules/@vue/reactivity": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz", - "integrity": "sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==", - "license": "MIT", - "dependencies": { - "@vue/shared": "3.5.13" - } - }, - "node_modules/@vue/runtime-core": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz", - "integrity": "sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==", - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.13", - "@vue/shared": "3.5.13" - } - }, - "node_modules/@vue/runtime-dom": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz", - "integrity": "sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==", - "license": "MIT", - "dependencies": { - "@vue/reactivity": "3.5.13", - "@vue/runtime-core": "3.5.13", - "@vue/shared": "3.5.13", - "csstype": "^3.1.3" - } - }, - "node_modules/@vue/server-renderer": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz", - "integrity": "sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==", - "license": "MIT", - "dependencies": { - "@vue/compiler-ssr": "3.5.13", - "@vue/shared": "3.5.13" - }, - "peerDependencies": { - "vue": "3.5.13" - } - }, - "node_modules/@vue/shared": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz", - "integrity": "sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==", - "license": "MIT" - }, - "node_modules/aes-js": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", - "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==", - "license": "MIT" - }, - "node_modules/apg-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/apg-js/-/apg-js-4.4.0.tgz", - "integrity": "sha512-fefmXFknJmtgtNEXfPwZKYkMFX4Fyeyz+fNF6JWp87biGOPslJbCBVU158zvKRZfHBKnJDy8CMM40oLFGkXT8Q==", - "license": "BSD-2-Clause" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.8.1", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axios-mock-adapter": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-2.1.0.tgz", - "integrity": "sha512-AZUe4OjECGCNNssH8SOdtneiQELsqTsat3SQQCWLPjN436/H+L9AjWfV7bF+Zg/YL9cgbhrz5671hoh+Tbn98w==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.3", - "is-buffer": "^2.0.5" - }, - "peerDependencies": { - "axios": ">= 0.17.0" - } - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/bech32": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", - "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==", - "license": "MIT" - }, - "node_modules/bn.js": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", - "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==", - "license": "MIT" - }, - "node_modules/brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==", - "license": "MIT" - }, - "node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/connect-pg-simple": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/connect-pg-simple/-/connect-pg-simple-10.0.0.tgz", - "integrity": "sha512-pBGVazlqiMrackzCr0eKhn4LO5trJXsOX0nQoey9wCOayh80MYtThCbq8eoLsjpiWgiok/h+1/uti9/2/Una8A==", - "dependencies": { - "pg": "^8.12.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=22.0.0" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/elliptic": { - "version": "6.6.1", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.6.1.tgz", - "integrity": "sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==", - "license": "MIT", - "dependencies": { - "bn.js": "^4.11.9", - "brorand": "^1.1.0", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.1", - "inherits": "^2.0.4", - "minimalistic-assert": "^1.0.1", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/elliptic/node_modules/bn.js": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.1.tgz", - "integrity": "sha512-k8TVBiPkPJT9uHLdOKfFpqcfprwBFOAAXXozRubr7R7PfIuKvQlzcI4M0pALeqXN09vdaMbUdUj+pass+uULAg==", - "license": "MIT" - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz", - "integrity": "sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/android-arm": "0.18.20", - "@esbuild/android-arm64": "0.18.20", - "@esbuild/android-x64": "0.18.20", - "@esbuild/darwin-arm64": "0.18.20", - "@esbuild/darwin-x64": "0.18.20", - "@esbuild/freebsd-arm64": "0.18.20", - "@esbuild/freebsd-x64": "0.18.20", - "@esbuild/linux-arm": "0.18.20", - "@esbuild/linux-arm64": "0.18.20", - "@esbuild/linux-ia32": "0.18.20", - "@esbuild/linux-loong64": "0.18.20", - "@esbuild/linux-mips64el": "0.18.20", - "@esbuild/linux-ppc64": "0.18.20", - "@esbuild/linux-riscv64": "0.18.20", - "@esbuild/linux-s390x": "0.18.20", - "@esbuild/linux-x64": "0.18.20", - "@esbuild/netbsd-x64": "0.18.20", - "@esbuild/openbsd-x64": "0.18.20", - "@esbuild/sunos-x64": "0.18.20", - "@esbuild/win32-arm64": "0.18.20", - "@esbuild/win32-ia32": "0.18.20", - "@esbuild/win32-x64": "0.18.20" - } - }, - "node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/ethers": { - "version": "5.8.0", - "funding": [ - { - "type": "individual", - "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" - }, - { - "type": "individual", - "url": "https://www.buymeacoffee.com/ricmoo" - } - ], - "license": "MIT", - "dependencies": { - "@ethersproject/abi": "5.8.0", - "@ethersproject/abstract-provider": "5.8.0", - "@ethersproject/abstract-signer": "5.8.0", - "@ethersproject/address": "5.8.0", - "@ethersproject/base64": "5.8.0", - "@ethersproject/basex": "5.8.0", - "@ethersproject/bignumber": "5.8.0", - "@ethersproject/bytes": "5.8.0", - "@ethersproject/constants": "5.8.0", - "@ethersproject/contracts": "5.8.0", - "@ethersproject/hash": "5.8.0", - "@ethersproject/hdnode": "5.8.0", - "@ethersproject/json-wallets": "5.8.0", - "@ethersproject/keccak256": "5.8.0", - "@ethersproject/logger": "5.8.0", - "@ethersproject/networks": "5.8.0", - "@ethersproject/pbkdf2": "5.8.0", - "@ethersproject/properties": "5.8.0", - "@ethersproject/providers": "5.8.0", - "@ethersproject/random": "5.8.0", - "@ethersproject/rlp": "5.8.0", - "@ethersproject/sha2": "5.8.0", - "@ethersproject/signing-key": "5.8.0", - "@ethersproject/solidity": "5.8.0", - "@ethersproject/strings": "5.8.0", - "@ethersproject/transactions": "5.8.0", - "@ethersproject/units": "5.8.0", - "@ethersproject/wallet": "5.8.0", - "@ethersproject/web": "5.8.0", - "@ethersproject/wordlists": "5.8.0" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", - "license": "MIT", - "dependencies": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/is-buffer": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", - "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "engines": { - "node": ">=4" - } - }, - "node_modules/js-sha3": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", - "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==", - "license": "MIT" - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "license": "ISC" - }, - "node_modules/minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/pg": { - "version": "8.13.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.3.tgz", - "integrity": "sha512-P6tPt9jXbL9HVu/SSRERNYaYG++MjnscnegFh9pPHihfoBSujsrka0hyuymMzeJKFWrcG8wvCKy8rCe8e5nDUQ==", - "dependencies": { - "pg-connection-string": "^2.7.0", - "pg-pool": "^3.7.1", - "pg-protocol": "^1.7.1", - "pg-types": "^2.1.0", - "pgpass": "1.x" - }, - "engines": { - "node": ">= 8.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.1.1" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz", - "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz", - "integrity": "sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.1.tgz", - "integrity": "sha512-xIOsFoh7Vdhojas6q3596mXFsR8nwBQBXX5JiV7p9buEVAGqYL4yFzclON5P9vFrpu1u7Zwl2oriyDa89n0wbw==", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.1.tgz", - "integrity": "sha512-gjTHWGYWsEgy9MsY0Gp6ZJxV24IjDqdpTW7Eh0x+WfJLFsm/TJx1MzL6T0D88mBvkpxotCQ6TwW6N+Kko7lhgQ==" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pinia": { - "version": "2.3.1", - "license": "MIT", - "dependencies": { - "@vue/devtools-api": "^6.6.3", - "vue-demi": "^0.14.10" - }, - "funding": { - "url": "https://github.com/sponsors/posva" - }, - "peerDependencies": { - "typescript": ">=4.4.4", - "vue": "^2.7.0 || ^3.5.11" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/rollup": { - "version": "3.29.5", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz", - "integrity": "sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==", - "dev": true, - "license": "MIT", - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=14.18.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/rollup-plugin-polyfill-node": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.12.0.tgz", - "integrity": "sha512-PWEVfDxLEKt8JX1nZ0NkUAgXpkZMTb85rO/Ru9AQ69wYW8VUCfDgP4CGRXXWYni5wDF0vIeR1UoF3Jmw/Lt3Ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rollup/plugin-inject": "^5.0.1" - }, - "peerDependencies": { - "rollup": "^1.20.0 || ^2.0.0 || ^3.0.0" - } - }, - "node_modules/scrypt-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", - "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==", - "license": "MIT" - }, - "node_modules/siwe": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/siwe/-/siwe-2.3.2.tgz", - "integrity": "sha512-aSf+6+Latyttbj5nMu6GF3doMfv2UYj83hhwZgUF20ky6fTS83uVhkQABdIVnEuS8y1bBdk7p6ltb9SmlhTTlA==", - "license": "Apache-2.0", - "dependencies": { - "@spruceid/siwe-parser": "^2.1.2", - "@stablelib/random": "^1.0.1", - "uri-js": "^4.4.1", - "valid-url": "^1.0.9" - }, - "peerDependencies": { - "ethers": "^5.6.8 || ^6.0.8" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/valid-url": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz", - "integrity": "sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==" - }, - "node_modules/vite": { - "version": "4.5.9", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.18.10", - "postcss": "^8.4.27", - "rollup": "^3.27.1" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - }, - "peerDependencies": { - "@types/node": ">= 14", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.4.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - } - } - }, - "node_modules/vue": { - "version": "3.5.13", - "license": "MIT", - "dependencies": { - "@vue/compiler-dom": "3.5.13", - "@vue/compiler-sfc": "3.5.13", - "@vue/runtime-dom": "3.5.13", - "@vue/server-renderer": "3.5.13", - "@vue/shared": "3.5.13" - }, - "peerDependencies": { - "typescript": "*" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/vue-demi": { - "version": "0.14.10", - "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz", - "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "vue-demi-fix": "bin/vue-demi-fix.js", - "vue-demi-switch": "bin/vue-demi-switch.js" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/antfu" - }, - "peerDependencies": { - "@vue/composition-api": "^1.0.0-rc.1", - "vue": "^3.0.0-0 || ^2.6.0" - }, - "peerDependenciesMeta": { - "@vue/composition-api": { - "optional": true - } - } - }, - "node_modules/vue-router": { - "version": "4.5.0", - "license": "MIT", - "dependencies": { - "@vue/devtools-api": "^6.6.4" - }, - "funding": { - "url": "https://github.com/sponsors/posva" - }, - "peerDependencies": { - "vue": "^3.2.0" - } - }, - "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "engines": { - "node": ">=0.4" - } - } - } -} diff --git a/frontend/package.json b/frontend/package.json index 37dde71..3dd1a6d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,23 +6,36 @@ "scripts": { "dev": "vite", "build": "vite build", - "preview": "vite preview" + "preview": "vite preview", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --ignore-pattern 'node_modules/'", + "lint:fix": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-pattern 'node_modules/'", + "format": "prettier --write \"**/*.{js,vue,json,md}\"", + "format:check": "prettier --check \"**/*.{js,vue,json,md}\"" }, "dependencies": { - "axios": "^1.3.4", + "axios": "^1.8.1", "buffer": "^6.0.3", "connect-pg-simple": "^10.0.0", "ethers": "6.13.5", "pinia": "^2.0.33", "siwe": "^2.1.4", + "sortablejs": "^1.15.6", "vue": "^3.2.47", + "vue-i18n": "9", "vue-router": "^4.1.6" }, "devDependencies": { - "@vitejs/plugin-vue": "^4.1.0", + "@vitejs/plugin-vue": "^5.2.1", + "@vue/eslint-config-prettier": "^10.2.0", "axios-mock-adapter": "^2.1.0", + "eslint": "^9.21.0", + "eslint-config-prettier": "^10.0.2", + "eslint-plugin-prettier": "^5.2.3", + "eslint-plugin-vue": "^9.32.0", + "globals": "^16.0.0", + "prettier": "^3.5.3", "rollup": "^3.29.4", "rollup-plugin-polyfill-node": "^0.12.0", - "vite": "^4.2.1" + "vite": "^6.2.0" } } diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 0ae33ab..2791293 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,286 +1,215 @@ \ No newline at end of file + +.btn { + padding: 0.5rem 1rem; + border-radius: 4px; + font-weight: 500; + border: none; +} + +.btn-primary { + background-color: #3498db; + color: white; +} + +.btn-secondary { + background-color: #95a5a6; + color: white; +} + +.btn-danger { + background-color: #e74c3c; + color: white; +} + diff --git a/frontend/src/api/auth.js b/frontend/src/api/auth.js new file mode 100644 index 0000000..29534e3 --- /dev/null +++ b/frontend/src/api/auth.js @@ -0,0 +1,4 @@ +import axios from 'axios'; + +// Настройка axios для работы с куками +axios.defaults.withCredentials = true; diff --git a/frontend/src/components/AccessControl.vue b/frontend/src/components/AccessControl.vue index fa872ed..9131a4a 100644 --- a/frontend/src/components/AccessControl.vue +++ b/frontend/src/components/AccessControl.vue @@ -1,40 +1,42 @@ \ No newline at end of file + +async function loadTokens() { + try { + console.log('Загрузка токенов...'); + loading.value = true; + + // Добавляем withCredentials для передачи куки с сессией + const response = await axios.get('/api/access/tokens', { + withCredentials: true, + }); + + console.log('Ответ API:', response.data); + + if (response.data && response.data.length > 0) { + // Если есть токены, берем первый активный + const activeToken = response.data.find((token) => { + const expiresAt = new Date(token.expires_at); + return expiresAt > new Date(); + }); + + if (activeToken) { + accessInfo.value = { + hasAccess: true, + token: activeToken.id, + role: activeToken.role, + expiresAt: activeToken.expires_at, + }; + } else { + accessInfo.value = { hasAccess: false }; + } + } else { + accessInfo.value = { hasAccess: false }; + } + } catch (error) { + console.error('Ошибка при загрузке токенов:', error); + error.value = 'Ошибка при проверке доступа: ' + (error.response?.data?.error || error.message); + accessInfo.value = { hasAccess: false }; + } finally { + loading.value = false; + } +} + +onMounted(async () => { + console.log('Компонент AccessControl загружен'); + console.log('isAdmin:', authStore.isAdmin); + await loadTokens(); +}); + + + diff --git a/frontend/src/components/AccessTest.vue b/frontend/src/components/AccessTest.vue deleted file mode 100644 index 12dba27..0000000 --- a/frontend/src/components/AccessTest.vue +++ /dev/null @@ -1,194 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend/src/components/AccessTokenManager.vue b/frontend/src/components/AccessTokenManager.vue index f0b9a5f..cb8473a 100644 --- a/frontend/src/components/AccessTokenManager.vue +++ b/frontend/src/components/AccessTokenManager.vue @@ -1,209 +1,161 @@ \ No newline at end of file + + + diff --git a/frontend/src/components/Chats.vue b/frontend/src/components/Chats.vue deleted file mode 100644 index 2b87172..0000000 --- a/frontend/src/components/Chats.vue +++ /dev/null @@ -1,298 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/LinkedAccounts.vue b/frontend/src/components/LinkedAccounts.vue index 3e9ae1a..0ee16a4 100644 --- a/frontend/src/components/LinkedAccounts.vue +++ b/frontend/src/components/LinkedAccounts.vue @@ -1,43 +1,41 @@ - \ No newline at end of file + diff --git a/frontend/src/components/Modal.vue b/frontend/src/components/Modal.vue index a7f6fc7..4edb04a 100644 --- a/frontend/src/components/Modal.vue +++ b/frontend/src/components/Modal.vue @@ -15,11 +15,27 @@ - \ No newline at end of file + diff --git a/frontend/src/components/Navigation.vue b/frontend/src/components/Navigation.vue new file mode 100644 index 0000000..27c0596 --- /dev/null +++ b/frontend/src/components/Navigation.vue @@ -0,0 +1,133 @@ + + + + + diff --git a/frontend/src/components/RoleManager.vue b/frontend/src/components/RoleManager.vue new file mode 100644 index 0000000..b4c32b5 --- /dev/null +++ b/frontend/src/components/RoleManager.vue @@ -0,0 +1,161 @@ + + + + + diff --git a/frontend/src/components/WalletConnection.vue b/frontend/src/components/WalletConnection.vue index 9f2a3f2..d4901a9 100644 --- a/frontend/src/components/WalletConnection.vue +++ b/frontend/src/components/WalletConnection.vue @@ -1,51 +1,98 @@ \ No newline at end of file + +.connect-button:disabled { + background-color: #ccc; + cursor: not-allowed; +} + +.error-message { + color: #d32f2f; + margin-bottom: 10px; + padding: 10px; + background-color: #ffebee; + border-radius: 4px; +} + +.spinner { + width: 16px; + height: 16px; + border: 2px solid rgba(255, 255, 255, 0.3); + border-radius: 50%; + border-top-color: white; + animation: spin 1s linear infinite; + margin-right: 8px; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + diff --git a/frontend/src/components/chat/ConversationList.vue b/frontend/src/components/chat/ConversationList.vue new file mode 100644 index 0000000..bdbfb42 --- /dev/null +++ b/frontend/src/components/chat/ConversationList.vue @@ -0,0 +1,225 @@ + + + + + diff --git a/frontend/src/components/chat/MessageInput.vue b/frontend/src/components/chat/MessageInput.vue new file mode 100644 index 0000000..26dfb4c --- /dev/null +++ b/frontend/src/components/chat/MessageInput.vue @@ -0,0 +1,134 @@ + + + + + diff --git a/frontend/src/components/chat/MessageThread.vue b/frontend/src/components/chat/MessageThread.vue new file mode 100644 index 0000000..7b6b08d --- /dev/null +++ b/frontend/src/components/chat/MessageThread.vue @@ -0,0 +1,194 @@ + + + + + diff --git a/frontend/src/components/identity/EmailConnect.vue b/frontend/src/components/identity/EmailConnect.vue new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/components/identity/IdentityManager.vue b/frontend/src/components/identity/IdentityManager.vue new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/components/identity/TelegramConnect.vue b/frontend/src/components/identity/TelegramConnect.vue new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/components/kanban/AddBoardForm.vue b/frontend/src/components/kanban/AddBoardForm.vue deleted file mode 100644 index 4585891..0000000 --- a/frontend/src/components/kanban/AddBoardForm.vue +++ /dev/null @@ -1,128 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/kanban/AddCardForm.vue b/frontend/src/components/kanban/AddCardForm.vue deleted file mode 100644 index 673b984..0000000 --- a/frontend/src/components/kanban/AddCardForm.vue +++ /dev/null @@ -1,121 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/kanban/AddColumnForm.vue b/frontend/src/components/kanban/AddColumnForm.vue deleted file mode 100644 index b780457..0000000 --- a/frontend/src/components/kanban/AddColumnForm.vue +++ /dev/null @@ -1,115 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/kanban/KanbanBoard.vue b/frontend/src/components/kanban/KanbanBoard.vue deleted file mode 100644 index 0060513..0000000 --- a/frontend/src/components/kanban/KanbanBoard.vue +++ /dev/null @@ -1,529 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/kanban/KanbanCard.vue b/frontend/src/components/kanban/KanbanCard.vue deleted file mode 100644 index 2849252..0000000 --- a/frontend/src/components/kanban/KanbanCard.vue +++ /dev/null @@ -1,190 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/components/useEthereum.js b/frontend/src/components/useEthereum.js deleted file mode 100644 index 3ef1c1d..0000000 --- a/frontend/src/components/useEthereum.js +++ /dev/null @@ -1,151 +0,0 @@ -import { ref, onMounted, onUnmounted, watch } from 'vue' -import { ethers } from 'ethers' - -export function useEthereum() { - const address = ref(null) - const isConnected = ref(false) - const chainId = ref(null) - const provider = ref(null) - const signer = ref(null) - - // Инициализация при загрузке компонента - onMounted(async () => { - // Проверяем, есть ли сохраненное состояние подключения - const savedAddress = localStorage.getItem('walletAddress') - const savedConnected = localStorage.getItem('isConnected') === 'true' - - if (savedConnected && savedAddress) { - // Пробуем восстановить подключение - try { - await connect() - } catch (error) { - console.error('Failed to restore connection:', error) - // Очищаем сохраненное состояние при ошибке - localStorage.removeItem('walletAddress') - localStorage.removeItem('isConnected') - } - } - }) - - // Функция для подключения кошелька - async function connect() { - try { - // Проверяем, доступен ли MetaMask - if (typeof window.ethereum === 'undefined') { - throw new Error('MetaMask не установлен') - } - - // Запрашиваем доступ к аккаунтам - const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }) - - if (accounts.length === 0) { - throw new Error('Нет доступных аккаунтов') - } - - // Устанавливаем адрес - address.value = accounts[0] - - try { - // Создаем провайдер и signer (для ethers v5) - provider.value = new ethers.providers.Web3Provider(window.ethereum) - signer.value = provider.value.getSigner() - - // Получаем chainId напрямую из ethereum провайдера - const chainIdHex = await window.ethereum.request({ method: 'eth_chainId' }) - chainId.value = parseInt(chainIdHex, 16).toString() - } catch (providerError) { - console.error('Ошибка при создании провайдера:', providerError) - // Продолжаем выполнение, даже если не удалось получить signer - } - - isConnected.value = true - - // Сохраняем информацию о подключении - localStorage.setItem('walletConnected', 'true') - localStorage.setItem('walletAddress', address.value) - - return address.value - } catch (error) { - console.error('Connection error:', error) - throw error - } - } - - // Функция для отключения кошелька - function disconnect() { - address.value = null - isConnected.value = false - provider.value = null - signer.value = null - chainId.value = null - - // Очищаем localStorage - localStorage.removeItem('walletAddress') - localStorage.removeItem('isConnected') - } - - // Функция для подписи сообщения - async function signMessage(message) { - if (!signer.value) { - throw new Error('Кошелек не подключен') - } - - return await signer.value.signMessage(message) - } - - // Обработчик изменения аккаунтов - async function handleAccountsChanged(accounts) { - if (accounts.length === 0) { - // Пользователь отключил аккаунт - address.value = null - isConnected.value = false - signer.value = null - } else { - // Пользователь сменил аккаунт - address.value = accounts[0] - isConnected.value = true - - // Обновляем signer - if (provider.value) { - signer.value = provider.value.getSigner() - } - } - } - - // Обработчик изменения сети - function handleChainChanged(chainIdHex) { - chainId.value = parseInt(chainIdHex, 16).toString() - window.location.reload() - } - - // Инициализация при монтировании компонента - onMounted(() => { - // Проверяем, есть ли MetaMask - if (window.ethereum) { - // Добавляем слушатели событий - window.ethereum.on('accountsChanged', handleAccountsChanged) - window.ethereum.on('chainChanged', handleChainChanged) - window.ethereum.on('disconnect', disconnect) - } - }) - - // Очистка при размонтировании компонента - onUnmounted(() => { - if (window.ethereum) { - window.ethereum.removeListener('accountsChanged', handleAccountsChanged) - window.ethereum.removeListener('chainChanged', handleChainChanged) - window.ethereum.removeListener('disconnect', disconnect) - } - }) - - return { - address, - isConnected, - chainId, - provider, - signer, - connect, - disconnect, - signMessage - } -} \ No newline at end of file diff --git a/frontend/src/composables/useEthereum.js b/frontend/src/composables/useEthereum.js new file mode 100644 index 0000000..1990b7c --- /dev/null +++ b/frontend/src/composables/useEthereum.js @@ -0,0 +1,23 @@ +import { ref } from 'vue'; + +export function useEthereum() { + const address = ref(''); + const isConnected = ref(true); + + async function connect() { + console.log('Имитация подключения к кошельку'); + return { success: true }; + } + + async function getContract() { + console.log('Имитация получения контракта'); + return {}; + } + + return { + address, + isConnected, + connect, + getContract, + }; +} diff --git a/frontend/src/locales/en.json b/frontend/src/locales/en.json new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/locales/ru.json b/frontend/src/locales/ru.json new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/main.js b/frontend/src/main.js index 510e1ed..9c06a9a 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,27 +1,22 @@ -import { Buffer } from 'buffer' -globalThis.Buffer = Buffer +import { Buffer } from 'buffer'; +globalThis.Buffer = Buffer; -import { createApp } from 'vue' -import { createPinia } from 'pinia' -import App from './App.vue' -import router from './router' -import axios from 'axios' +import { createApp } from 'vue'; +import { createPinia } from 'pinia'; +import App from './App.vue'; +import router from './router'; +import axios from 'axios'; -// Вместо этого -axios.defaults.baseURL = import.meta.env.VITE_API_URL || 'http://localhost:8000' - -// Используйте относительный путь -axios.defaults.baseURL = '' - -// Включение передачи кук -axios.defaults.withCredentials = true +// Настройка axios +axios.defaults.baseURL = 'http://localhost:8000'; +axios.defaults.withCredentials = true; // Важно для работы с сессиями // Создаем и монтируем приложение Vue -const app = createApp(App) -const pinia = createPinia() +const app = createApp(App); +const pinia = createPinia(); -app.use(pinia) -app.use(router) +app.use(pinia); +app.use(router); // Не используем заглушки, так как сервер работает // if (import.meta.env.DEV) { @@ -33,4 +28,6 @@ app.use(router) // ]).catch(err => console.error('Failed to load API mocks:', err)); // } -app.mount('#app') \ No newline at end of file +console.log('API URL:', import.meta.env.VITE_API_URL); + +app.mount('#app'); diff --git a/frontend/src/mocks/api.js b/frontend/src/mocks/api.js deleted file mode 100644 index e81590d..0000000 --- a/frontend/src/mocks/api.js +++ /dev/null @@ -1,69 +0,0 @@ -// Заглушки для API в режиме разработки -export function setupMockServer() { - if (import.meta.env.DEV) { - // Перехватываем fetch запросы - const originalFetch = window.fetch; - - window.fetch = async function(url, options) { - // Проверяем, является ли запрос API запросом - if (typeof url === 'string' && url.includes('/api/')) { - console.log('Перехвачен запрос:', url, options); - - // Заглушка для сессии - if (url.includes('/api/session')) { - return new Response(JSON.stringify({ - authenticated: true, - address: '0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b' - }), { status: 200, headers: { 'Content-Type': 'application/json' } }); - } - - // Заглушка для проверки доступности сервера - if (url.includes('/api/debug/ping')) { - return new Response(JSON.stringify({ status: 'ok' }), - { status: 200, headers: { 'Content-Type': 'application/json' } }); - } - - // Заглушка для досок Канбан - if (url.includes('/api/kanban/boards')) { - return new Response(JSON.stringify({ - ownBoards: [ - { - id: 1, - title: 'Разработка DApp', - description: 'Задачи по разработке DApp', - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), - owner_address: '0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b', - is_public: true - }, - { - id: 2, - title: 'Маркетинг', - description: 'Маркетинговые задачи', - created_at: new Date().toISOString(), - updated_at: new Date().toISOString(), - owner_address: '0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b', - is_public: false - } - ], - sharedBoards: [], - publicBoards: [] - }), { status: 200, headers: { 'Content-Type': 'application/json' } }); - } - - // Заглушка для чата с ИИ - if (url.includes('/api/chat/message') && options?.method === 'POST') { - const body = options.body ? JSON.parse(options.body) : {}; - return new Response(JSON.stringify({ - response: `Это тестовый ответ на ваше сообщение: "${body.message}". В данный момент сервер недоступен.` - }), { status: 200, headers: { 'Content-Type': 'application/json' } }); - } - } - - // Для всех остальных запросов используем оригинальный fetch - return originalFetch.apply(this, arguments); - }; - - console.log('Заглушки API настроены для режима разработки'); - } -} \ No newline at end of file diff --git a/frontend/src/mocks/authApi.js b/frontend/src/mocks/authApi.js deleted file mode 100644 index e3625cc..0000000 --- a/frontend/src/mocks/authApi.js +++ /dev/null @@ -1,68 +0,0 @@ -// Импортируем axios для перехвата запросов -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; - -// Создаем экземпляр MockAdapter -const mock = new MockAdapter(axios, { delayResponse: 1000 }); - -// Мокаем запрос к API для получения nonce -mock.onGet(/\/api\/auth\/nonce/).reply((config) => { - const address = config.url.split('?address=')[1]; - - if (!address) { - return [400, { error: 'Address is required' }]; - } - - return [200, { - message: `Sign this message to authenticate with DApp for Business: ${Math.floor(Math.random() * 1000000)}`, - address - }]; -}); - -// Мокаем запрос к API для верификации подписи -mock.onPost('/api/auth/verify').reply((config) => { - const { address } = JSON.parse(config.data); - - // Проверяем, является ли адрес администратором (для тестирования) - const isAdmin = address.toLowerCase() === '0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b'.toLowerCase(); - - return [200, { - authenticated: true, - address, - isAdmin - }]; -}); - -// Мокаем запрос к API для аутентификации по email -mock.onPost('/api/auth/email').reply((config) => { - const { email } = JSON.parse(config.data); - - // Проверяем формат email - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; - if (!emailRegex.test(email)) { - return [400, { error: 'Invalid email format' }]; - } - - // Проверяем, является ли email администратором (для тестирования) - const isAdmin = email.toLowerCase() === 'admin@example.com'; - - return [200, { - authenticated: true, - address: email, - isAdmin - }]; -}); - -// Мокаем запрос к API для проверки сессии -mock.onGet('/api/auth/check').reply(200, { - authenticated: false, - address: null, - isAdmin: false -}); - -// Мокаем запрос к API для выхода -mock.onPost('/api/auth/logout').reply(200, { - success: true -}); - -export default mock; \ No newline at end of file diff --git a/frontend/src/mocks/chatApi.js b/frontend/src/mocks/chatApi.js deleted file mode 100644 index 3c01712..0000000 --- a/frontend/src/mocks/chatApi.js +++ /dev/null @@ -1,40 +0,0 @@ -// Импортируем axios для перехвата запросов -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; - -// Создаем экземпляр MockAdapter -const mock = new MockAdapter(axios, { delayResponse: 1000 }); - -// Мокаем запрос к API чата -mock.onPost('/api/chat/message').reply((config) => { - const { message, userId, language } = JSON.parse(config.data); - - // Определяем язык ответа - const isRussian = language === 'ru'; - - // Простые ответы на разных языках - const responses = { - ru: [ - 'Я могу помочь вам с различными задачами. Что именно вас интересует?', - 'Для полноценной работы рекомендую авторизоваться в системе.', - 'Это интересный вопрос! Давайте разберемся подробнее.', - 'Я ИИ-ассистент DApp for Business. Чем могу помочь?', - 'Для доступа к расширенным функциям необходимо подключить кошелек или авторизоваться другим способом.' - ], - en: [ - 'I can help you with various tasks. What are you interested in?', - 'For full functionality, I recommend logging into the system.', - 'That\'s an interesting question! Let\'s explore it in more detail.', - 'I\'m the AI assistant for DApp for Business. How can I help?', - 'To access advanced features, you need to connect your wallet or authorize in another way.' - ] - }; - - // Выбираем случайный ответ из соответствующего языка - const randomIndex = Math.floor(Math.random() * responses[isRussian ? 'ru' : 'en'].length); - const reply = responses[isRussian ? 'ru' : 'en'][randomIndex]; - - return [200, { reply }]; -}); - -export default mock; \ No newline at end of file diff --git a/frontend/src/mocks/contractApi.js b/frontend/src/mocks/contractApi.js deleted file mode 100644 index 3041d0f..0000000 --- a/frontend/src/mocks/contractApi.js +++ /dev/null @@ -1,46 +0,0 @@ -// Импортируем axios для перехвата запросов -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; - -// Создаем экземпляр MockAdapter -const mock = new MockAdapter(axios, { delayResponse: 1000 }); - -// Мокаем запрос к API для создания токена доступа -mock.onPost('/api/contracts/tokens').reply(200, { - success: true, - token: { - id: Math.floor(Math.random() * 1000000).toString(), - address: '0x' + Math.random().toString(16).substring(2, 42), - role: 'user', - createdAt: new Date().toISOString(), - expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString() - } -}); - -// Мокаем запрос к API для получения списка токенов -mock.onGet('/api/contracts/tokens').reply(200, { - tokens: [ - { - id: '1', - address: '0x1234567890abcdef1234567890abcdef12345678', - role: 'admin', - createdAt: new Date().toISOString(), - expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString() - }, - { - id: '2', - address: '0xabcdef1234567890abcdef1234567890abcdef12', - role: 'user', - createdAt: new Date().toISOString(), - expiresAt: new Date(Date.now() + 15 * 24 * 60 * 60 * 1000).toISOString() - } - ] -}); - -// Обрабатываем все остальные запросы к API смарт-контрактов -mock.onAny(/\/api\/contracts\/.*/).reply(200, { - success: true, - message: 'Операция выполнена успешно' -}); - -export default mock; \ No newline at end of file diff --git a/frontend/src/mocks/kanbanApi.js b/frontend/src/mocks/kanbanApi.js deleted file mode 100644 index cd59e10..0000000 --- a/frontend/src/mocks/kanbanApi.js +++ /dev/null @@ -1,96 +0,0 @@ -// Импортируем axios для перехвата запросов -import axios from 'axios'; -import MockAdapter from 'axios-mock-adapter'; - -// Создаем экземпляр MockAdapter -const mock = new MockAdapter(axios, { delayResponse: 1000 }); - -// Мокаем запрос к API для получения списка досок -mock.onGet('/api/kanban/boards').reply(200, { - boards: [ - { - id: '1', - title: 'Проект 1', - description: 'Описание проекта 1', - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString() - }, - { - id: '2', - title: 'Проект 2', - description: 'Описание проекта 2', - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString() - } - ] -}); - -// Мокаем запрос к API для получения конкретной доски -mock.onGet(/\/api\/kanban\/boards\/\d+/).reply((config) => { - const id = config.url.split('/').pop(); - - return [200, { - board: { - id, - title: `Проект ${id}`, - description: `Описание проекта ${id}`, - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString(), - columns: [ - { - id: '1', - title: 'К выполнению', - order: 1, - tasks: [ - { - id: '1', - title: 'Задача 1', - description: 'Описание задачи 1', - priority: 'high', - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString() - } - ] - }, - { - id: '2', - title: 'В процессе', - order: 2, - tasks: [ - { - id: '2', - title: 'Задача 2', - description: 'Описание задачи 2', - priority: 'medium', - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString() - } - ] - }, - { - id: '3', - title: 'Завершено', - order: 3, - tasks: [ - { - id: '3', - title: 'Задача 3', - description: 'Описание задачи 3', - priority: 'low', - createdAt: new Date().toISOString(), - updatedAt: new Date().toISOString() - } - ] - } - ] - } - }]; -}); - -// Обрабатываем все остальные запросы к API канбан-досок -mock.onAny(/\/api\/kanban\/.*/).reply(200, { - success: true, - message: 'Операция выполнена успешно' -}); - -export default mock; \ No newline at end of file diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 351994e..6cd586e 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -3,80 +3,84 @@ import { useAuthStore } from '../stores/auth'; // Импортируем компоненты напрямую, если они существуют import HomeView from '../views/HomeView.vue'; import DashboardView from '../views/DashboardView.vue'; -import KanbanView from '../views/KanbanView.vue'; -import KanbanBoardView from '../views/KanbanBoardView.vue'; -import AccessTestPage from '../views/AccessTestPage.vue'; import ProfileView from '../views/ProfileView.vue'; +import AdminView from '../views/AdminView.vue'; // Новый компонент для администраторов +import AccessTestView from '../views/AccessTestView.vue'; +import ConversationsView from '../views/ConversationsView.vue'; const routes = [ { path: '/', name: 'home', component: HomeView, - meta: { requiresAuth: false } + meta: { requiresAuth: false }, }, { path: '/dashboard', - name: 'dashboard', - component: DashboardView, - meta: { requiresAuth: true, requiresAdmin: true } - }, - { - path: '/access-test', - name: 'access-test', - component: AccessTestPage, - meta: { requiresAuth: true, requiresAdmin: true } + name: 'Dashboard', + component: () => import('../views/DashboardView.vue'), + meta: { requiresAuth: true, requiresAdmin: true }, }, // Перенаправляем с /chat на главную страницу { path: '/chat', - redirect: { name: 'home' } - }, - // Маршруты для Канбан-досок - { - path: '/kanban', - name: 'kanban', - component: KanbanView, - meta: { requiresAuth: true } - }, - { - path: '/kanban/boards/:id', - name: 'kanbanBoard', - component: KanbanBoardView, - meta: { requiresAuth: true } + redirect: { name: 'home' }, }, { path: '/profile', name: 'profile', component: ProfileView, - meta: { requiresAuth: true } - } + meta: { requiresAuth: true }, + }, + { + path: '/admin', + name: 'Admin', + component: () => import('../views/AdminView.vue'), + meta: { requiresAdmin: true }, + }, + { + path: '/access-test', + name: 'access-test', + component: AccessTestView, + meta: { requiresAuth: true, requiresAdmin: true }, + }, + { + path: '/conversations', + name: 'Conversations', + component: ConversationsView, + meta: { requiresAuth: true }, + }, ]; const router = createRouter({ history: createWebHistory(), routes, scrollBehavior(to, from, savedPosition) { - return savedPosition || { top: 0 } - } + return savedPosition || { top: 0 }; + }, }); // Навигационный хук для проверки аутентификации -router.beforeEach((to, from, next) => { - const auth = useAuthStore(); - - // Если маршрут требует аутентификации и пользователь не аутентифицирован - if (to.meta.requiresAuth && !auth.isAuthenticated) { - next({ name: 'home' }); - } - // Если маршрут требует прав администратора и пользователь не администратор - else if (to.meta.requiresAdmin && !auth.isAdmin) { - next({ name: 'home' }); +router.beforeEach(async (to, from, next) => { + const authStore = useAuthStore(); + + // Проверяем аутентификацию только если еще не проверяли + if (!authStore.checkPerformed) { + try { + await authStore.checkAuth(); + } catch (error) { + console.error('Ошибка при проверке аутентификации:', error); + } } - // В остальных случаях разрешаем переход - else { + + // Проверка прав доступа + if (to.meta.requiresAuth && !authStore.isAuthenticated) { + next({ name: 'Home' }); + } else if (to.meta.requiresAdmin && !authStore.isAdmin) { + next({ name: 'Home' }); + } else { next(); } }); -export default router; \ No newline at end of file +export default router; diff --git a/frontend/src/services/wallet.js b/frontend/src/services/wallet.js deleted file mode 100644 index fbbcf51..0000000 --- a/frontend/src/services/wallet.js +++ /dev/null @@ -1,106 +0,0 @@ -import axios from 'axios'; -import { ethers } from 'ethers'; -import { useAuthStore } from '../stores/auth'; - -export async function connectWallet(onError) { - const auth = useAuthStore(); - - try { - console.log('Начинаем подключение кошелька...'); - - // Закомментируйте получение CSRF-токена - // const csrfResponse = await axios.get('/api/csrf-token'); - // const csrfToken = csrfResponse.data.csrfToken; - - // Проверяем, доступен ли MetaMask - if (typeof window.ethereum === 'undefined') { - console.error('MetaMask не установлен'); - if (onError) onError('Пожалуйста, установите MetaMask для подключения кошелька.'); - return; - } - - console.log('MetaMask доступен, запрашиваем аккаунты...'); - - // Создаем провайдер и получаем подписчика - const provider = new ethers.BrowserProvider(window.ethereum); - - // Запрашиваем доступ к аккаунтам - const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); - if (!accounts || accounts.length === 0) { - throw new Error('No accounts found'); - } - - const address = accounts[0]; - const signer = await provider.getSigner(); - - try { - // Получаем nonce для подписи без CSRF-токена - const nonceResponse = await axios.get(`/api/auth/nonce?address=${address}`, { - withCredentials: true - // Удалите или закомментируйте заголовки CSRF - // headers: { - // 'CSRF-Token': csrfToken - // } - }); - const { message } = nonceResponse.data; - - // Запрашиваем подпись сообщения - const signature = await signer.signMessage(message); - - // Верифицируем подпись на сервере без CSRF-токена - const verifyResponse = await axios.post('/api/auth/verify', { - address, - signature - }, { - withCredentials: true - // Удалите или закомментируйте заголовки CSRF - // headers: { - // 'CSRF-Token': csrfToken - // } - }); - - // Если верификация успешна, устанавливаем состояние аутентификации - if (verifyResponse.data.authenticated) { - auth.setAuth({ - address, - isAdmin: verifyResponse.data.isAdmin, - authType: 'wallet' - }); - - // Перезагружаем страницу для обновления интерфейса - window.location.reload(); - } - } catch (error) { - console.log('Server error:', error); - - // Временное решение для обхода ошибки сервера - auth.setAuth({ - address, - isAdmin: address.toLowerCase() === '0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b'.toLowerCase(), - authType: 'wallet' - }); - - // Перезагружаем страницу для обновления интерфейса - window.location.reload(); - } - } catch (error) { - console.error('Ошибка при подключении кошелька:', error); - - // Более подробная обработка ошибок - let errorMessage = 'Ошибка при подключении кошелька'; - - if (error.response) { - // Ошибка от сервера - const serverError = error.response.data.error || error.response.data.message; - errorMessage = `Ошибка сервера: ${serverError}`; - } else if (error.message.includes('user rejected')) { - errorMessage = 'Вы отклонили запрос на подпись сообщения'; - } else if (error.message.includes('already processing')) { - errorMessage = 'Уже обрабатывается запрос. Пожалуйста, подождите'; - } else if (error.message.includes('No accounts found')) { - errorMessage = 'Не найдены аккаунты. Пожалуйста, разблокируйте MetaMask'; - } - - if (onError) onError(errorMessage); - } -} \ No newline at end of file diff --git a/frontend/src/stores/auth.js b/frontend/src/stores/auth.js index 00cf184..f9ca147 100644 --- a/frontend/src/stores/auth.js +++ b/frontend/src/stores/auth.js @@ -4,93 +4,82 @@ import axios from 'axios'; export const useAuthStore = defineStore('auth', () => { const isAuthenticated = ref(false); - const address = ref(null); const isAdmin = ref(false); + const address = ref(null); const authType = ref(null); + const loading = ref(false); + const checkPerformed = ref(false); - // Функция для установки состояния аутентификации - function setAuth(authData) { - isAuthenticated.value = true; - address.value = authData.address; - isAdmin.value = authData.isAdmin; - authType.value = authData.authType; - - // Сохраняем состояние аутентификации в localStorage - localStorage.setItem('isAuthenticated', 'true'); - localStorage.setItem('userAddress', authData.address); - localStorage.setItem('isAdmin', authData.isAdmin); - localStorage.setItem('authType', authData.authType); - - console.log('Setting auth:', authData); - } + // Проверка аутентификации + async function checkAuth() { + loading.value = true; - // Функция для выхода из системы - async function disconnect() { - console.log('Disconnecting user'); - try { - // Отправляем запрос на выход - await axios.post('/api/auth/logout'); - } catch (error) { - console.error('Error logging out:', error); - } - - // Сбрасываем состояние аутентификации - isAuthenticated.value = false; - address.value = null; - isAdmin.value = false; - authType.value = null; - - // Удаляем данные из localStorage - localStorage.removeItem('isAuthenticated'); - localStorage.removeItem('userAddress'); - localStorage.removeItem('isAdmin'); - localStorage.removeItem('authType'); - } + console.log('Проверка аутентификации...'); + const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:8000'; + console.log('API URL:', apiUrl); - // Функция для восстановления состояния аутентификации из localStorage - async function restoreAuth() { - try { - const response = await axios.get('/api/auth/check', { withCredentials: true }); - if (response.data.authenticated) { - this.setAuth({ - address: response.data.address, - isAdmin: response.data.isAdmin, - authType: response.data.authType - }); - } + const response = await axios.get(`${apiUrl}/api/auth/check`, { + withCredentials: true, + }); + + console.log('Статус аутентификации:', response.data.authenticated); + console.log('Статус администратора:', response.data.isAdmin); + + isAuthenticated.value = response.data.authenticated; + isAdmin.value = response.data.isAdmin; + address.value = response.data.address; + authType.value = response.data.authType; } catch (error) { - console.error('Error checking auth status:', error); + console.error('Ошибка при проверке аутентификации:', error); + throw error; + } finally { + loading.value = false; + checkPerformed.value = true; } } - // Функция для проверки сессии на сервере - async function checkSession() { + // Обновление состояния аутентификации + function updateAuthState(authData) { + console.log('Обновление состояния аутентификации:', authData); + + isAuthenticated.value = authData.authenticated || false; + isAdmin.value = authData.isAdmin || false; + address.value = authData.address || null; + authType.value = authData.authType || null; + checkPerformed.value = true; + } + + // Выход из системы + async function logout() { try { - const response = await axios.get('/api/auth/check'); - console.log('Session check response:', response.data); - - // Если сессия истекла, выходим из системы - if (!response.data.authenticated && isAuthenticated.value) { - console.log('Session expired, logging out'); - disconnect(); - } - - return response.data; + await axios.post( + `${import.meta.env.VITE_API_URL}/api/auth/logout`, + {}, + { + withCredentials: true, + } + ); } catch (error) { - console.error('Error checking session:', error); - return { authenticated: false }; + console.error('Ошибка при выходе из системы:', error); + } finally { + // Сбрасываем состояние независимо от результата запроса + isAuthenticated.value = false; + isAdmin.value = false; + address.value = null; + authType.value = null; } } return { isAuthenticated, - address, isAdmin, + address, authType, - setAuth, - disconnect, - restoreAuth, - checkSession + loading, + checkPerformed, + checkAuth, + updateAuthState, + logout, }; -}); \ No newline at end of file +}); diff --git a/frontend/src/utils/wallet.js b/frontend/src/utils/wallet.js new file mode 100644 index 0000000..e47425c --- /dev/null +++ b/frontend/src/utils/wallet.js @@ -0,0 +1,127 @@ +import axios from 'axios'; + +async function connectWallet() { + console.log('Начинаем подключение кошелька...'); + + try { + // Проверяем доступность MetaMask + if (typeof window.ethereum === 'undefined') { + throw new Error('MetaMask не установлен'); + } + + console.log('MetaMask доступен, запрашиваем аккаунты...'); + + // Запрашиваем доступ к аккаунтам + const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' }); + const address = accounts[0]; + + if (!address) { + throw new Error('Не удалось получить адрес кошелька'); + } + + console.log('Получен адрес кошелька:', address); + + const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:8000'; + console.log('API URL для nonce:', apiUrl); + + // Получаем nonce для подписи + let nonce; + + // Пробуем прямой запрос к серверу + try { + const directNonceResponse = await axios.get( + `${apiUrl}/api/auth/nonce?address=${address}`, + { + withCredentials: true, + } + ); + + console.log('Прямой ответ сервера:', directNonceResponse.data); + console.log('Cookies after direct request:', document.cookie); + + nonce = directNonceResponse.data.nonce; + } catch (error) { + console.error('Ошибка при получении nonce:', error); + throw new Error('Не удалось получить nonce для подписи'); + } + + console.log('Получен nonce:', nonce); + + // Создаем сообщение для подписи + const message = `Подтвердите вход в DApp for Business с nonce: ${nonce}`; + + try { + // Запрашиваем подпись используя ethereum.request + const signature = await window.ethereum.request({ + method: 'personal_sign', + params: [message, address], + }); + + console.log('Получена подпись:', signature); + + // Отправляем подпись на сервер + const verifyResponse = await axios.post( + `${apiUrl}/api/auth/verify`, + { + address, + signature, + message, + nonce, + }, + { + headers: { + 'X-Auth-Nonce': nonce, + }, + withCredentials: true, + } + ); + + console.log('Ответ сервера:', verifyResponse.data); + + // Сохраняем адрес в localStorage для восстановления сессии + localStorage.setItem('walletAddress', address); + + // Возвращаем данные аутентификации + return { + address, + authenticated: verifyResponse.data.authenticated, + isAdmin: verifyResponse.data.isAdmin, + authType: 'wallet', + }; + } catch (signError) { + console.error('Ошибка при подписи сообщения:', signError); + + if (signError.code === 4001) { + throw new Error('Пользователь отклонил запрос на подпись'); + } + + throw new Error('Не удалось подписать сообщение'); + } + } catch (error) { + console.error('Ошибка при подключении кошелька:', error); + throw error; + } +} + +async function disconnectWallet() { + try { + // Отправляем запрос на выход + await axios.post( + '/api/auth/logout', + {}, + { + withCredentials: true, + } + ); + + // Удаляем адрес из localStorage + localStorage.removeItem('walletAddress'); + + return { success: true }; + } catch (error) { + console.error('Ошибка при отключении кошелька:', error); + throw error; + } +} + +export { connectWallet, disconnectWallet }; diff --git a/frontend/src/views/AccessTestPage.vue b/frontend/src/views/AccessTestPage.vue deleted file mode 100644 index dac932a..0000000 --- a/frontend/src/views/AccessTestPage.vue +++ /dev/null @@ -1,32 +0,0 @@ - - - \ No newline at end of file diff --git a/frontend/src/views/AccessTestView.vue b/frontend/src/views/AccessTestView.vue new file mode 100644 index 0000000..1e44461 --- /dev/null +++ b/frontend/src/views/AccessTestView.vue @@ -0,0 +1,27 @@ + + + + + diff --git a/frontend/src/views/AdminView.vue b/frontend/src/views/AdminView.vue new file mode 100644 index 0000000..59e8e24 --- /dev/null +++ b/frontend/src/views/AdminView.vue @@ -0,0 +1,279 @@ + + + + + diff --git a/frontend/src/views/ChatView.vue b/frontend/src/views/ChatView.vue new file mode 100644 index 0000000..2733040 --- /dev/null +++ b/frontend/src/views/ChatView.vue @@ -0,0 +1,282 @@ + + + + + diff --git a/frontend/src/views/ConversationsView.vue b/frontend/src/views/ConversationsView.vue new file mode 100644 index 0000000..8cdb1fe --- /dev/null +++ b/frontend/src/views/ConversationsView.vue @@ -0,0 +1,77 @@ + + + + + diff --git a/frontend/src/views/DashboardView.vue b/frontend/src/views/DashboardView.vue index 8066603..74c9d06 100644 --- a/frontend/src/views/DashboardView.vue +++ b/frontend/src/views/DashboardView.vue @@ -2,11 +2,26 @@

Дашборд

Добро пожаловать в панель управления!

+ + +
+

Административные функции

+ + + +
- \ No newline at end of file + diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index 8a54652..aa792c2 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -3,38 +3,46 @@

Чат с ИИ-ассистентом

-
+
- +
- +
- +
- +
{{ formatTime(message.timestamp) }}
- +
-
', - timestamp: new Date() + timestamp: new Date(), }); - + // Добавляем функцию для проверки кода в глобальный объект window - window.verifyEmailCode = async function() { + window.verifyEmailCode = async function () { const code = document.getElementById('verification-code').value; if (!code) return; - + try { const verifyResponse = await axios.post('/api/auth/email/verify', { email: email.value, - code + code, }); - + if (verifyResponse.data.authenticated) { - auth.setAuth({ - address: email.value, - isAdmin: verifyResponse.data.isAdmin, - authType: 'email' - }); - + auth.authenticated = true; + auth.address = email.value; + auth.isAdmin = verifyResponse.data.isAdmin; + auth.authType = 'email'; + // Перезагружаем страницу для обновления интерфейса window.location.reload(); } else { messages.value.push({ sender: 'ai', text: 'Неверный код подтверждения. Пожалуйста, попробуйте еще раз.', - timestamp: new Date() + timestamp: new Date(), }); } } catch (error) { console.error('Error verifying email code:', error); - + messages.value.push({ sender: 'ai', text: 'Произошла ошибка при проверке кода. Пожалуйста, попробуйте позже.', - timestamp: new Date() + timestamp: new Date(), }); } }; @@ -282,46 +295,44 @@ async function connectEmail() { messages.value.push({ sender: 'ai', text: `На ваш email ${email.value} отправлено письмо с кодом подтверждения. Пожалуйста, проверьте вашу почту.`, - timestamp: new Date() + timestamp: new Date(), }); - + // Имитируем успешную авторизацию через email setTimeout(() => { - auth.setAuth({ - address: email.value, - isAdmin: email.value.includes('admin'), - authType: 'email' - }); - + auth.authenticated = true; + auth.address = email.value; + auth.isAdmin = email.value.includes('admin'); + auth.authType = 'email'; + // Перезагружаем страницу для обновления интерфейса window.location.reload(); }, 3000); } } catch (error) { console.error('Error connecting with email:', error); - + // Показываем сообщение об ошибке messages.value.push({ sender: 'ai', text: 'Извините, произошла ошибка при подключении Email. Пожалуйста, попробуйте позже.', - timestamp: new Date() + timestamp: new Date(), }); - + // Временное решение для обхода ошибок сервера messages.value.push({ sender: 'ai', text: `На ваш email ${email.value} отправлено письмо с кодом подтверждения. Пожалуйста, проверьте вашу почту.`, - timestamp: new Date() + timestamp: new Date(), }); - + // Имитируем успешную авторизацию через email setTimeout(() => { - auth.setAuth({ - address: email.value, - isAdmin: email.value.includes('admin'), - authType: 'email' - }); - + auth.authenticated = true; + auth.address = email.value; + auth.isAdmin = email.value.includes('admin'); + auth.authType = 'email'; + // Перезагружаем страницу для обновления интерфейса window.location.reload(); }, 3000); @@ -334,7 +345,7 @@ function handleConnectWallet() { messages.value.push({ sender: 'ai', text: errorMessage, - timestamp: new Date() + timestamp: new Date(), }); }); } @@ -562,4 +573,4 @@ textarea { font-size: 1rem; font-weight: 500; } - \ No newline at end of file + diff --git a/frontend/src/views/IdentityView.vue b/frontend/src/views/IdentityView.vue new file mode 100644 index 0000000..e69de29 diff --git a/frontend/src/views/KanbanBoardView.vue b/frontend/src/views/KanbanBoardView.vue deleted file mode 100644 index f5b5cf6..0000000 --- a/frontend/src/views/KanbanBoardView.vue +++ /dev/null @@ -1,451 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/views/KanbanView.vue b/frontend/src/views/KanbanView.vue deleted file mode 100644 index e9b5ec4..0000000 --- a/frontend/src/views/KanbanView.vue +++ /dev/null @@ -1,343 +0,0 @@ - - - - - \ No newline at end of file diff --git a/frontend/src/views/ProfileView.vue b/frontend/src/views/ProfileView.vue index 8f2010f..e437abe 100644 --- a/frontend/src/views/ProfileView.vue +++ b/frontend/src/views/ProfileView.vue @@ -1,12 +1,134 @@ + + diff --git a/frontend/vite.config.js b/frontend/vite.config.js index 1e35421..adf1219 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -1,46 +1,49 @@ -import { defineConfig } from 'vite' -import vue from '@vitejs/plugin-vue' -import polyfillNode from 'rollup-plugin-polyfill-node' -import path from 'path' +import { defineConfig } from 'vite'; +import vue from '@vitejs/plugin-vue'; +import polyfillNode from 'rollup-plugin-polyfill-node'; +import { fileURLToPath } from 'url'; +import path, { dirname } from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); export default defineConfig({ plugins: [ vue(), polyfillNode({ - include: ['buffer', 'process', 'util'] - }) + include: ['buffer', 'process', 'util'], + }), ], resolve: { alias: { - '@': path.resolve(__dirname, 'src'), - buffer: 'buffer/' - } + '@': path.resolve(__dirname, './src'), + buffer: 'buffer/', + }, }, define: { - 'global': 'globalThis', - 'process.env': {} + global: 'globalThis', + 'process.env': {}, }, build: { rollupOptions: { - plugins: [ - polyfillNode() - ] - } + plugins: [polyfillNode()], + }, }, optimizeDeps: { esbuildOptions: { loader: { - '.js': 'jsx' - } - } + '.js': 'jsx', + }, + }, }, server: { port: 5173, proxy: { '/api': { target: 'http://localhost:8000', - changeOrigin: true + changeOrigin: true, + cookieDomainRewrite: 'localhost', } - } - } -}) \ No newline at end of file + }, + }, +}); diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 66e5904..9ae9932 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -9,142 +9,267 @@ "@babel/helper-string-parser@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== "@babel/helper-validator-identifier@^7.25.9": version "7.25.9" - resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== "@babel/parser@^7.25.3": version "7.26.9" - resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.9.tgz#d9e78bee6dc80f9efd8f2349dcfbbcdace280fd5" integrity sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A== dependencies: "@babel/types" "^7.26.9" "@babel/types@^7.26.9": version "7.26.9" - resolved "https://registry.npmjs.org/@babel/types/-/types-7.26.9.tgz" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.9.tgz#08b43dec79ee8e682c2ac631c010bdcac54a21ce" integrity sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw== dependencies: "@babel/helper-string-parser" "^7.25.9" "@babel/helper-validator-identifier" "^7.25.9" -"@esbuild/android-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622" - integrity sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ== +"@esbuild/aix-ppc64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz#499600c5e1757a524990d5d92601f0ac3ce87f64" + integrity sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ== -"@esbuild/android-arm@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.18.20.tgz#fedb265bc3a589c84cc11f810804f234947c3682" - integrity sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw== +"@esbuild/android-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz#b9b8231561a1dfb94eb31f4ee056b92a985c324f" + integrity sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g== -"@esbuild/android-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.18.20.tgz#35cf419c4cfc8babe8893d296cd990e9e9f756f2" - integrity sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg== +"@esbuild/android-arm@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.0.tgz#ca6e7888942505f13e88ac9f5f7d2a72f9facd2b" + integrity sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g== -"@esbuild/darwin-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz#08172cbeccf95fbc383399a7f39cfbddaeb0d7c1" - integrity sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA== +"@esbuild/android-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.0.tgz#e765ea753bac442dfc9cb53652ce8bd39d33e163" + integrity sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg== -"@esbuild/darwin-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz#d70d5790d8bf475556b67d0f8b7c5bdff053d85d" - integrity sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ== +"@esbuild/darwin-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz#fa394164b0d89d4fdc3a8a21989af70ef579fa2c" + integrity sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw== -"@esbuild/freebsd-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz#98755cd12707f93f210e2494d6a4b51b96977f54" - integrity sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw== +"@esbuild/darwin-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz#91979d98d30ba6e7d69b22c617cc82bdad60e47a" + integrity sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg== -"@esbuild/freebsd-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz#c1eb2bff03915f87c29cece4c1a7fa1f423b066e" - integrity sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ== +"@esbuild/freebsd-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz#b97e97073310736b430a07b099d837084b85e9ce" + integrity sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w== -"@esbuild/linux-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz#bad4238bd8f4fc25b5a021280c770ab5fc3a02a0" - integrity sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA== +"@esbuild/freebsd-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz#f3b694d0da61d9910ec7deff794d444cfbf3b6e7" + integrity sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A== -"@esbuild/linux-arm@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz#3e617c61f33508a27150ee417543c8ab5acc73b0" - integrity sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg== +"@esbuild/linux-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz#f921f699f162f332036d5657cad9036f7a993f73" + integrity sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg== -"@esbuild/linux-ia32@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz#699391cccba9aee6019b7f9892eb99219f1570a7" - integrity sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA== +"@esbuild/linux-arm@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz#cc49305b3c6da317c900688995a4050e6cc91ca3" + integrity sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg== -"@esbuild/linux-loong64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz#e6fccb7aac178dd2ffb9860465ac89d7f23b977d" - integrity sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg== +"@esbuild/linux-ia32@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz#3e0736fcfab16cff042dec806247e2c76e109e19" + integrity sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg== -"@esbuild/linux-mips64el@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz#eeff3a937de9c2310de30622a957ad1bd9183231" - integrity sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ== +"@esbuild/linux-loong64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz#ea2bf730883cddb9dfb85124232b5a875b8020c7" + integrity sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw== -"@esbuild/linux-ppc64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz#2f7156bde20b01527993e6881435ad79ba9599fb" - integrity sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA== +"@esbuild/linux-mips64el@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz#4cababb14eede09248980a2d2d8b966464294ff1" + integrity sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ== -"@esbuild/linux-riscv64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz#6628389f210123d8b4743045af8caa7d4ddfc7a6" - integrity sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A== +"@esbuild/linux-ppc64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz#8860a4609914c065373a77242e985179658e1951" + integrity sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw== -"@esbuild/linux-s390x@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz#255e81fb289b101026131858ab99fba63dcf0071" - integrity sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ== +"@esbuild/linux-riscv64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz#baf26e20bb2d38cfb86ee282dff840c04f4ed987" + integrity sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA== -"@esbuild/linux-x64@0.18.20": - version "0.18.20" - resolved "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz" - integrity sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w== +"@esbuild/linux-s390x@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz#8323afc0d6cb1b6dc6e9fd21efd9e1542c3640a4" + integrity sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA== -"@esbuild/netbsd-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz#30e8cd8a3dded63975e2df2438ca109601ebe0d1" - integrity sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A== +"@esbuild/linux-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz#08fcf60cb400ed2382e9f8e0f5590bac8810469a" + integrity sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw== -"@esbuild/openbsd-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz#7812af31b205055874c8082ea9cf9ab0da6217ae" - integrity sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg== +"@esbuild/netbsd-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz#935c6c74e20f7224918fbe2e6c6fe865b6c6ea5b" + integrity sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw== -"@esbuild/sunos-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz#d5c275c3b4e73c9b0ecd38d1ca62c020f887ab9d" - integrity sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ== +"@esbuild/netbsd-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz#414677cef66d16c5a4d210751eb2881bb9c1b62b" + integrity sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA== -"@esbuild/win32-arm64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz#73bc7f5a9f8a77805f357fab97f290d0e4820ac9" - integrity sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg== +"@esbuild/openbsd-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz#8fd55a4d08d25cdc572844f13c88d678c84d13f7" + integrity sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw== -"@esbuild/win32-ia32@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz#ec93cbf0ef1085cc12e71e0d661d20569ff42102" - integrity sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g== +"@esbuild/openbsd-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz#0c48ddb1494bbc2d6bcbaa1429a7f465fa1dedde" + integrity sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg== -"@esbuild/win32-x64@0.18.20": - version "0.18.20" - resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz#786c5f41f043b07afb1af37683d7c33668858f6d" - integrity sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ== +"@esbuild/sunos-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz#86ff9075d77962b60dd26203d7352f92684c8c92" + integrity sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg== + +"@esbuild/win32-arm64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz#849c62327c3229467f5b5cd681bf50588442e96c" + integrity sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw== + +"@esbuild/win32-ia32@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz#f62eb480cd7cca088cb65bb46a6db25b725dc079" + integrity sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA== + +"@esbuild/win32-x64@0.25.0": + version "0.25.0" + resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz#c8e119a30a7c8d60b9d2e22d2073722dde3b710b" + integrity sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ== + +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" + integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/config-array@^0.19.2": + version "0.19.2" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.19.2.tgz#3060b809e111abfc97adb0bb1172778b90cb46aa" + integrity sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w== + dependencies: + "@eslint/object-schema" "^2.1.6" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/core@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.12.0.tgz#5f960c3d57728be9f6c65bd84aa6aa613078798e" + integrity sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/eslintrc@^3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.0.tgz#96a558f45842989cca7ea1ecd785ad5491193846" + integrity sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@9.21.0": + version "9.21.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.21.0.tgz#4303ef4e07226d87c395b8fad5278763e9c15c08" + integrity sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw== + +"@eslint/object-schema@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" + integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== + +"@eslint/plugin-kit@^0.2.7": + version "0.2.7" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz#9901d52c136fb8f375906a73dcc382646c3b6a27" + integrity sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g== + dependencies: + "@eslint/core" "^0.12.0" + levn "^0.4.1" + +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== + +"@humanwhocodes/retry@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.2.tgz#1860473de7dfa1546767448f333db80cb0ff2161" + integrity sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ== + +"@intlify/core-base@9.14.2": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.14.2.tgz#2c074506ea72425e937f911c95c0d845b43f7fdf" + integrity sha512-DZyQ4Hk22sC81MP4qiCDuU+LdaYW91A6lCjq8AWPvY3+mGMzhGDfOCzvyR6YBQxtlPjFqMoFk9ylnNYRAQwXtQ== + dependencies: + "@intlify/message-compiler" "9.14.2" + "@intlify/shared" "9.14.2" + +"@intlify/message-compiler@9.14.2": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.14.2.tgz#7217842ea1875d80bbf0f708e9b3ef5ad7c57a03" + integrity sha512-YsKKuV4Qv4wrLNsvgWbTf0E40uRv+Qiw1BeLQ0LAxifQuhiMe+hfTIzOMdWj/ZpnTDj4RSZtkXjJM7JDiiB5LQ== + dependencies: + "@intlify/shared" "9.14.2" + source-map-js "^1.0.2" + +"@intlify/shared@9.14.2": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.14.2.tgz#f7dceea32db44c9253e3f965745a42a5cb3a1883" + integrity sha512-uRAHAxYPeF+G5DBIboKpPgC/Waecd4Jz8ihtkpJQD5ycb5PwXp0k/+hBGl5dAjwF7w+l74kz/PKA8r8OK//RUw== "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.0" - resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== "@noble/curves@1.2.0": @@ -161,12 +286,17 @@ "@noble/hashes@^1.1.2": version "1.7.1" - resolved "https://registry.npmjs.org/@noble/hashes/-/hashes-1.7.1.tgz" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.7.1.tgz#5738f6d765710921e7a751e00c20ae091ed8db0f" integrity sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ== +"@pkgr/core@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@pkgr/core/-/core-0.1.1.tgz#1ec17e2edbec25c8306d424ecfbf13c7de1aaa31" + integrity sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA== + "@rollup/plugin-inject@^5.0.1": version "5.0.5" - resolved "https://registry.npmjs.org/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz" + resolved "https://registry.yarnpkg.com/@rollup/plugin-inject/-/plugin-inject-5.0.5.tgz#616f3a73fe075765f91c5bec90176608bed277a3" integrity sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg== dependencies: "@rollup/pluginutils" "^5.0.1" @@ -175,16 +305,111 @@ "@rollup/pluginutils@^5.0.1": version "5.1.4" - resolved "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-5.1.4.tgz#bb94f1f9eaaac944da237767cdfee6c5b2262d4a" integrity sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ== dependencies: "@types/estree" "^1.0.0" estree-walker "^2.0.2" picomatch "^4.0.2" +"@rollup/rollup-android-arm-eabi@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz#661a45a4709c70e59e596ec78daa9cb8b8d27604" + integrity sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA== + +"@rollup/rollup-android-arm64@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.9.tgz#128fe8dd510d880cf98b4cb6c7add326815a0c4b" + integrity sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg== + +"@rollup/rollup-darwin-arm64@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz#363467bc49fd0b1e17075798ac8e9ad1e1e29535" + integrity sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ== + +"@rollup/rollup-darwin-x64@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz#c2fe3d85fffe47f0ed0f076b3563ada22c8af19c" + integrity sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q== + +"@rollup/rollup-freebsd-arm64@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.9.tgz#d95bd8f6eaaf829781144fc8bd2d5d71d9f6a9f5" + integrity sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw== + +"@rollup/rollup-freebsd-x64@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.9.tgz#c3576c6011656e4966ded29f051edec636b44564" + integrity sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g== + +"@rollup/rollup-linux-arm-gnueabihf@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.9.tgz#48c87d0dee4f8dc9591a416717f91b4a89d77e3d" + integrity sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg== + +"@rollup/rollup-linux-arm-musleabihf@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.9.tgz#f4c4e7c03a7767f2e5aa9d0c5cfbf5c0f59f2d41" + integrity sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA== + +"@rollup/rollup-linux-arm64-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz#1015c9d07a99005025d13b8622b7600029d0b52f" + integrity sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw== + +"@rollup/rollup-linux-arm64-musl@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz#8f895eb5577748fc75af21beae32439626e0a14c" + integrity sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A== + +"@rollup/rollup-linux-loongarch64-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.9.tgz#c9cd5dbbdc6b3ca4dbeeb0337498cf31949004a0" + integrity sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg== + +"@rollup/rollup-linux-powerpc64le-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.9.tgz#7ebb5b4441faa17843a210f7d0583a20c93b40e4" + integrity sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA== + +"@rollup/rollup-linux-riscv64-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.9.tgz#10f5d7349fbd2fe78f9e36ecc90aab3154435c8d" + integrity sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg== + +"@rollup/rollup-linux-s390x-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.9.tgz#196347d2fa20593ab09d0b7e2589fb69bdf742c6" + integrity sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ== + +"@rollup/rollup-linux-x64-gnu@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz#7193cbd8d128212b8acda37e01b39d9e96259ef8" + integrity sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A== + +"@rollup/rollup-linux-x64-musl@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz#29a6867278ca0420b891574cfab98ecad70c59d1" + integrity sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA== + +"@rollup/rollup-win32-arm64-msvc@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz#89427dcac0c8e3a6d32b13a03a296a275d0de9a9" + integrity sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q== + +"@rollup/rollup-win32-ia32-msvc@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.9.tgz#ecb9711ba2b6d2bf6ee51265abe057ab90913deb" + integrity sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w== + +"@rollup/rollup-win32-x64-msvc@4.34.9": + version "4.34.9" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz#1973871850856ae72bc678aeb066ab952330e923" + integrity sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw== + "@spruceid/siwe-parser@^2.1.2": version "2.1.2" - resolved "https://registry.npmjs.org/@spruceid/siwe-parser/-/siwe-parser-2.1.2.tgz" + resolved "https://registry.yarnpkg.com/@spruceid/siwe-parser/-/siwe-parser-2.1.2.tgz#3e13e7d3ac0bfdaf109a07342590eb21daee2fc3" integrity sha512-d/r3S1LwJyMaRAKQ0awmo9whfXeE88Qt00vRj91q5uv5ATtWIQEGJ67Yr5eSZw5zp1/fZCXZYuEckt8lSkereQ== dependencies: "@noble/hashes" "^1.1.2" @@ -194,19 +419,19 @@ "@stablelib/binary@^1.0.1": version "1.0.1" - resolved "https://registry.npmjs.org/@stablelib/binary/-/binary-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/@stablelib/binary/-/binary-1.0.1.tgz#c5900b94368baf00f811da5bdb1610963dfddf7f" integrity sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q== dependencies: "@stablelib/int" "^1.0.1" "@stablelib/int@^1.0.1": version "1.0.1" - resolved "https://registry.npmjs.org/@stablelib/int/-/int-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/@stablelib/int/-/int-1.0.1.tgz#75928cc25d59d73d75ae361f02128588c15fd008" integrity sha512-byr69X/sDtDiIjIV6m4roLVWnNNlRGzsvxw+agj8CIEazqWGOQp2dTYgQhtyVXV9wpO6WyXRQUzLV/JRNumT2w== "@stablelib/random@^1.0.1": version "1.0.2" - resolved "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/@stablelib/random/-/random-1.0.2.tgz#2dece393636489bf7e19c51229dd7900eddf742c" integrity sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w== dependencies: "@stablelib/binary" "^1.0.1" @@ -214,14 +439,19 @@ "@stablelib/wipe@^1.0.1": version "1.0.1" - resolved "https://registry.npmjs.org/@stablelib/wipe/-/wipe-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/@stablelib/wipe/-/wipe-1.0.1.tgz#d21401f1d59ade56a62e139462a97f104ed19a36" integrity sha512-WfqfX/eXGiAd3RJe4VU2snh/ZPwtSjLG4ynQ/vYzvghTh7dHFcI1wl+nrkWG6lGhukOxOsUHfv8dUXr58D0ayg== -"@types/estree@^1.0.0": +"@types/estree@1.0.6", "@types/estree@^1.0.0", "@types/estree@^1.0.6": version "1.0.6" - resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + "@types/node@22.7.5": version "22.7.5" resolved "https://registry.yarnpkg.com/@types/node/-/node-22.7.5.tgz#cfde981727a7ab3611a481510b473ae54442b92b" @@ -229,14 +459,14 @@ dependencies: undici-types "~6.19.2" -"@vitejs/plugin-vue@^4.1.0": - version "4.6.2" - resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-4.6.2.tgz#057d2ded94c4e71b94e9814f92dcd9306317aa46" - integrity sha512-kqf7SGFoG+80aZG6Pf+gsZIVvGSCKE98JbiWqcCV9cThtg91Jav0yvYFC9Zb+jKetNGF6ZKeoaxgZfND21fWKw== +"@vitejs/plugin-vue@^5.2.1": + 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== "@vue/compiler-core@3.5.13": version "3.5.13" - resolved "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.13.tgz" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.13.tgz#b0ae6c4347f60c03e849a05d34e5bf747c9bda05" integrity sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q== dependencies: "@babel/parser" "^7.25.3" @@ -247,7 +477,7 @@ "@vue/compiler-dom@3.5.13": version "3.5.13" - resolved "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.13.tgz#bb1b8758dbc542b3658dda973b98a1c9311a8a58" integrity sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA== dependencies: "@vue/compiler-core" "3.5.13" @@ -255,7 +485,7 @@ "@vue/compiler-sfc@3.5.13": version "3.5.13" - resolved "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.13.tgz#461f8bd343b5c06fac4189c4fef8af32dea82b46" integrity sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ== dependencies: "@babel/parser" "^7.25.3" @@ -270,27 +500,35 @@ "@vue/compiler-ssr@3.5.13": version "3.5.13" - resolved "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.13.tgz#e771adcca6d3d000f91a4277c972a996d07f43ba" integrity sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA== dependencies: "@vue/compiler-dom" "3.5.13" "@vue/shared" "3.5.13" -"@vue/devtools-api@^6.6.3", "@vue/devtools-api@^6.6.4": +"@vue/devtools-api@^6.5.0", "@vue/devtools-api@^6.6.3", "@vue/devtools-api@^6.6.4": version "6.6.4" - resolved "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz" + resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz#cbe97fe0162b365edc1dba80e173f90492535343" integrity sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g== +"@vue/eslint-config-prettier@^10.2.0": + version "10.2.0" + resolved "https://registry.yarnpkg.com/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz#49a5ed571acb81820a216e6d88ebf1f3def321d0" + integrity sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw== + dependencies: + eslint-config-prettier "^10.0.1" + eslint-plugin-prettier "^5.2.2" + "@vue/reactivity@3.5.13": version "3.5.13" - resolved "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.13.tgz" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.13.tgz#b41ff2bb865e093899a22219f5b25f97b6fe155f" integrity sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg== dependencies: "@vue/shared" "3.5.13" "@vue/runtime-core@3.5.13": version "3.5.13" - resolved "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.13.tgz" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.13.tgz#1fafa4bf0b97af0ebdd9dbfe98cd630da363a455" integrity sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw== dependencies: "@vue/reactivity" "3.5.13" @@ -298,7 +536,7 @@ "@vue/runtime-dom@3.5.13": version "3.5.13" - resolved "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.13.tgz#610fc795de9246300e8ae8865930d534e1246215" integrity sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog== dependencies: "@vue/reactivity" "3.5.13" @@ -308,7 +546,7 @@ "@vue/server-renderer@3.5.13": version "3.5.13" - resolved "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.13.tgz" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.13.tgz#429ead62ee51de789646c22efe908e489aad46f7" integrity sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA== dependencies: "@vue/compiler-ssr" "3.5.13" @@ -316,33 +554,65 @@ "@vue/shared@3.5.13": version "3.5.13" - resolved "https://registry.npmjs.org/@vue/shared/-/shared-3.5.13.tgz" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.13.tgz#87b309a6379c22b926e696893237826f64339b6f" integrity sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ== +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.14.0, acorn@^8.9.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + aes-js@4.0.0-beta.5: version "4.0.0-beta.5" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-4.0.0-beta.5.tgz#8d2452c52adedebc3a3e28465d858c11ca315873" integrity sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q== +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + apg-js@^4.3.0: version "4.4.0" - resolved "https://registry.npmjs.org/apg-js/-/apg-js-4.4.0.tgz" + resolved "https://registry.yarnpkg.com/apg-js/-/apg-js-4.4.0.tgz#09dcecab0731fbde233b9f2352fdd2d07e56b2cf" integrity sha512-fefmXFknJmtgtNEXfPwZKYkMFX4Fyeyz+fNF6JWp87biGOPslJbCBVU158zvKRZfHBKnJDy8CMM40oLFGkXT8Q== +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + asynckit@^0.4.0: version "0.4.0" - resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== axios-mock-adapter@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/axios-mock-adapter/-/axios-mock-adapter-2.1.0.tgz#25ab2d7558f915e391744a40bbeb7374ad5985a4" integrity sha512-AZUe4OjECGCNNssH8SOdtneiQELsqTsat3SQQCWLPjN436/H+L9AjWfV7bF+Zg/YL9cgbhrz5671hoh+Tbn98w== dependencies: fast-deep-equal "^3.1.3" is-buffer "^2.0.5" -axios@^1.3.4: +axios@^1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.1.tgz#7c118d2146e9ebac512b7d1128771cdd738d11e3" integrity sha512-NN+fvwH/kV01dYUQ3PTOZns4LWtWhOFCAhQ/pHb88WQ1hNe5V/dvFwc4VJcDL11LT9xSX0QtsR8sWUuyOuOq7g== @@ -351,14 +621,32 @@ axios@^1.3.4: form-data "^4.0.0" proxy-from-env "^1.1.0" +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + base64-js@^1.3.1: version "1.5.1" - resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + buffer@^6.0.3: version "6.0.3" - resolved "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== dependencies: base64-js "^1.3.1" @@ -366,39 +654,95 @@ buffer@^6.0.3: call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz" + 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== dependencies: es-errors "^1.3.0" function-bind "^1.1.2" +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + combined-stream@^1.0.8: version "1.0.8" - resolved "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== dependencies: delayed-stream "~1.0.0" +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + connect-pg-simple@^10.0.0: version "10.0.0" - resolved "https://registry.npmjs.org/connect-pg-simple/-/connect-pg-simple-10.0.0.tgz" + resolved "https://registry.yarnpkg.com/connect-pg-simple/-/connect-pg-simple-10.0.0.tgz#972b08d9fc6a1861c523a6c9166240a24b4bc3ca" integrity sha512-pBGVazlqiMrackzCr0eKhn4LO5trJXsOX0nQoey9wCOayh80MYtThCbq8eoLsjpiWgiok/h+1/uti9/2/Una8A== dependencies: pg "^8.12.0" +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +cssesc@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" + integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== + csstype@^3.1.3: version "3.1.3" - resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== +debug@^4.3.1, debug@^4.3.2, debug@^4.3.4: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + delayed-stream@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== dunder-proto@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== dependencies: call-bind-apply-helpers "^1.0.1" @@ -407,29 +751,29 @@ dunder-proto@^1.0.1: entities@^4.5.0: version "4.5.0" - resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== es-define-property@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== es-errors@^1.3.0: version "1.3.0" - resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" + 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.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== dependencies: es-errors "^1.3.0" es-set-tostringtag@^2.1.0: version "2.1.0" - resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== dependencies: es-errors "^1.3.0" @@ -437,39 +781,182 @@ es-set-tostringtag@^2.1.0: has-tostringtag "^1.0.2" hasown "^2.0.2" -esbuild@^0.18.10: - version "0.18.20" - resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz" - integrity sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA== +esbuild@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.0.tgz#0de1787a77206c5a79eeb634a623d39b5006ce92" + integrity sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw== optionalDependencies: - "@esbuild/android-arm" "0.18.20" - "@esbuild/android-arm64" "0.18.20" - "@esbuild/android-x64" "0.18.20" - "@esbuild/darwin-arm64" "0.18.20" - "@esbuild/darwin-x64" "0.18.20" - "@esbuild/freebsd-arm64" "0.18.20" - "@esbuild/freebsd-x64" "0.18.20" - "@esbuild/linux-arm" "0.18.20" - "@esbuild/linux-arm64" "0.18.20" - "@esbuild/linux-ia32" "0.18.20" - "@esbuild/linux-loong64" "0.18.20" - "@esbuild/linux-mips64el" "0.18.20" - "@esbuild/linux-ppc64" "0.18.20" - "@esbuild/linux-riscv64" "0.18.20" - "@esbuild/linux-s390x" "0.18.20" - "@esbuild/linux-x64" "0.18.20" - "@esbuild/netbsd-x64" "0.18.20" - "@esbuild/openbsd-x64" "0.18.20" - "@esbuild/sunos-x64" "0.18.20" - "@esbuild/win32-arm64" "0.18.20" - "@esbuild/win32-ia32" "0.18.20" - "@esbuild/win32-x64" "0.18.20" + "@esbuild/aix-ppc64" "0.25.0" + "@esbuild/android-arm" "0.25.0" + "@esbuild/android-arm64" "0.25.0" + "@esbuild/android-x64" "0.25.0" + "@esbuild/darwin-arm64" "0.25.0" + "@esbuild/darwin-x64" "0.25.0" + "@esbuild/freebsd-arm64" "0.25.0" + "@esbuild/freebsd-x64" "0.25.0" + "@esbuild/linux-arm" "0.25.0" + "@esbuild/linux-arm64" "0.25.0" + "@esbuild/linux-ia32" "0.25.0" + "@esbuild/linux-loong64" "0.25.0" + "@esbuild/linux-mips64el" "0.25.0" + "@esbuild/linux-ppc64" "0.25.0" + "@esbuild/linux-riscv64" "0.25.0" + "@esbuild/linux-s390x" "0.25.0" + "@esbuild/linux-x64" "0.25.0" + "@esbuild/netbsd-arm64" "0.25.0" + "@esbuild/netbsd-x64" "0.25.0" + "@esbuild/openbsd-arm64" "0.25.0" + "@esbuild/openbsd-x64" "0.25.0" + "@esbuild/sunos-x64" "0.25.0" + "@esbuild/win32-arm64" "0.25.0" + "@esbuild/win32-ia32" "0.25.0" + "@esbuild/win32-x64" "0.25.0" + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-prettier@^10.0.1, eslint-config-prettier@^10.0.2: + version "10.0.2" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.0.2.tgz#47444de8aa104ce82c2f91ad2a5e96b62c01e20d" + integrity sha512-1105/17ZIMjmCOJOPNfVdbXafLCLj3hPmkmB7dLgt7XsQ/zkxSuDerE/xgO3RxoHysR1N1whmquY0lSn2O0VLg== + +eslint-plugin-prettier@^5.2.2, eslint-plugin-prettier@^5.2.3: + version "5.2.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz#c4af01691a6fa9905207f0fbba0d7bea0902cce5" + integrity sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw== + dependencies: + prettier-linter-helpers "^1.0.0" + synckit "^0.9.1" + +eslint-plugin-vue@^9.32.0: + version "9.32.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-9.32.0.tgz#2b558e827886b567dfaa156cc1cad0f596461fab" + integrity sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + globals "^13.24.0" + natural-compare "^1.4.0" + nth-check "^2.1.1" + postcss-selector-parser "^6.0.15" + semver "^7.6.3" + vue-eslint-parser "^9.4.3" + xml-name-validator "^4.0.0" + +eslint-scope@^7.1.1: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-scope@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.2.0.tgz#377aa6f1cb5dc7592cfd0b7f892fd0cf352ce442" + integrity sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^9.21.0: + version "9.21.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.21.0.tgz#b1c9c16f5153ff219791f627b94ab8f11f811591" + integrity sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.19.2" + "@eslint/core" "^0.12.0" + "@eslint/eslintrc" "^3.3.0" + "@eslint/js" "9.21.0" + "@eslint/plugin-kit" "^0.2.7" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.2" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.2.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + +espree@^10.0.1, espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + +espree@^9.3.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.0, esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== estree-walker@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + ethers@6.13.5: version "6.13.5" resolved "https://registry.yarnpkg.com/ethers/-/ethers-6.13.5.tgz#8c1d6ac988ac08abc3c1d8fabbd4b8b602851ac4" @@ -483,19 +970,62 @@ ethers@6.13.5: tslib "2.7.0" ws "8.17.1" -fast-deep-equal@^3.1.3: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" - resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + +flatted@^3.2.9: + version "3.3.3" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358" + integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg== + follow-redirects@^1.15.6: version "1.15.9" - resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== form-data@^4.0.0: version "4.0.2" - resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.2.tgz#35cabbdd30c3ce73deb2c42d3c8d3ed9ca51794c" integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w== dependencies: asynckit "^0.4.0" @@ -503,19 +1033,19 @@ form-data@^4.0.0: es-set-tostringtag "^2.1.0" mime-types "^2.1.12" -fsevents@~2.3.2: +fsevents@~2.3.2, fsevents@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== function-bind@^1.1.2: version "1.1.2" - resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== get-intrinsic@^1.2.6: version "1.3.0" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz" + 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.2" @@ -531,103 +1061,288 @@ get-intrinsic@^1.2.6: get-proto@^1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== dependencies: dunder-proto "^1.0.1" es-object-atoms "^1.0.0" +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +globals@^13.24.0: + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== + dependencies: + type-fest "^0.20.2" + +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +globals@^16.0.0: + version "16.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-16.0.0.tgz#3d7684652c5c4fbd086ec82f9448214da49382d8" + integrity sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A== + gopd@^1.2.0: version "1.2.0" - resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-symbols@^1.0.3, has-symbols@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== has-tostringtag@^1.0.2: version "1.0.2" - resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== dependencies: has-symbols "^1.0.3" hasown@^2.0.2: version "2.0.2" - resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" ieee754@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== +ignore@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-fresh@^3.2.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf" + integrity sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + is-buffer@^2.0.5: version "2.0.5" - resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + magic-string@^0.30.11, magic-string@^0.30.3: version "0.30.17" - resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453" integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" math-intrinsics@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== mime-db@1.52.0: version "1.52.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== mime-types@^2.1.12: version "2.1.35" - resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" +minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + nanoid@^3.3.8: version "3.3.8" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.8.tgz#b1be3030bee36aaff18bacb375e5cce521684baf" integrity sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w== +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +nth-check@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + pg-cloudflare@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz#e6d5833015b170e23ae819e8c5d7eaedb472ca98" integrity sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q== pg-connection-string@^2.7.0: version "2.7.0" - resolved "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.7.0.tgz" + resolved "https://registry.yarnpkg.com/pg-connection-string/-/pg-connection-string-2.7.0.tgz#f1d3489e427c62ece022dba98d5262efcb168b37" integrity sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA== pg-int8@1.0.1: version "1.0.1" - resolved "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz" + resolved "https://registry.yarnpkg.com/pg-int8/-/pg-int8-1.0.1.tgz#943bd463bf5b71b4170115f80f8efc9a0c0eb78c" integrity sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== pg-pool@^3.7.1: version "3.7.1" - resolved "https://registry.npmjs.org/pg-pool/-/pg-pool-3.7.1.tgz" + resolved "https://registry.yarnpkg.com/pg-pool/-/pg-pool-3.7.1.tgz#d1aaf618618d8f878acf185e86084928b8cd5b3c" integrity sha512-xIOsFoh7Vdhojas6q3596mXFsR8nwBQBXX5JiV7p9buEVAGqYL4yFzclON5P9vFrpu1u7Zwl2oriyDa89n0wbw== pg-protocol@^1.7.1: version "1.7.1" - resolved "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.7.1.tgz" + resolved "https://registry.yarnpkg.com/pg-protocol/-/pg-protocol-1.7.1.tgz#aad61a6f927b51e89dcf721408b76c0e536d43dc" integrity sha512-gjTHWGYWsEgy9MsY0Gp6ZJxV24IjDqdpTW7Eh0x+WfJLFsm/TJx1MzL6T0D88mBvkpxotCQ6TwW6N+Kko7lhgQ== pg-types@^2.1.0: version "2.2.0" - resolved "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz" + resolved "https://registry.yarnpkg.com/pg-types/-/pg-types-2.2.0.tgz#2d0250d636454f7cfa3b6ae0382fdfa8063254a3" integrity sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== dependencies: pg-int8 "1.0.1" @@ -638,7 +1353,7 @@ pg-types@^2.1.0: pg@^8.12.0: version "8.13.3" - resolved "https://registry.npmjs.org/pg/-/pg-8.13.3.tgz" + resolved "https://registry.yarnpkg.com/pg/-/pg-8.13.3.tgz#19cd021d1f9e9d26d860b80cd450f109a8652738" integrity sha512-P6tPt9jXbL9HVu/SSRERNYaYG++MjnscnegFh9pPHihfoBSujsrka0hyuymMzeJKFWrcG8wvCKy8rCe8e5nDUQ== dependencies: pg-connection-string "^2.7.0" @@ -651,19 +1366,19 @@ pg@^8.12.0: pgpass@1.x: version "1.0.5" - resolved "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz" + resolved "https://registry.yarnpkg.com/pgpass/-/pgpass-1.0.5.tgz#9b873e4a564bb10fa7a7dbd55312728d422a223d" integrity sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug== dependencies: split2 "^4.1.0" picocolors@^1.1.1: version "1.1.1" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^4.0.2: version "4.0.2" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab" integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg== pinia@^2.0.33: @@ -674,9 +1389,17 @@ pinia@^2.0.33: "@vue/devtools-api" "^6.6.3" vue-demi "^0.14.10" -postcss@^8.4.27, postcss@^8.4.48: +postcss-selector-parser@^6.0.15: + version "6.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + +postcss@^8.4.48, postcss@^8.5.3: version "8.5.3" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.3.tgz#1463b6f1c7fb16fe258736cba29a2de35237eafb" integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A== dependencies: nanoid "^3.3.8" @@ -685,53 +1408,120 @@ postcss@^8.4.27, postcss@^8.4.48: postgres-array@~2.0.0: version "2.0.0" - resolved "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz" + resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" integrity sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== postgres-bytea@~1.0.0: version "1.0.0" - resolved "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz" + resolved "https://registry.yarnpkg.com/postgres-bytea/-/postgres-bytea-1.0.0.tgz#027b533c0aa890e26d172d47cf9ccecc521acd35" integrity sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w== postgres-date@~1.0.4: version "1.0.7" - resolved "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz" + resolved "https://registry.yarnpkg.com/postgres-date/-/postgres-date-1.0.7.tgz#51bc086006005e5061c591cee727f2531bf641a8" integrity sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== postgres-interval@^1.1.0: version "1.2.0" - resolved "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz" + resolved "https://registry.yarnpkg.com/postgres-interval/-/postgres-interval-1.2.0.tgz#b460c82cb1587507788819a06aa0fffdb3544695" integrity sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== dependencies: xtend "^4.0.0" +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.5.3.tgz#4fc2ce0d657e7a02e602549f053b239cb7dfe1b5" + integrity sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw== + proxy-from-env@^1.1.0: version "1.1.0" - resolved "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== punycode@^2.1.0: version "2.3.1" - resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + rollup-plugin-polyfill-node@^0.12.0: version "0.12.0" - resolved "https://registry.npmjs.org/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.12.0.tgz" + resolved "https://registry.yarnpkg.com/rollup-plugin-polyfill-node/-/rollup-plugin-polyfill-node-0.12.0.tgz#33d421ddb7fcb69c234461e508ca6d2db6193f1d" integrity sha512-PWEVfDxLEKt8JX1nZ0NkUAgXpkZMTb85rO/Ru9AQ69wYW8VUCfDgP4CGRXXWYni5wDF0vIeR1UoF3Jmw/Lt3Ug== dependencies: "@rollup/plugin-inject" "^5.0.1" -rollup@^3.27.1, rollup@^3.29.4: +rollup@^3.29.4: version "3.29.5" - resolved "https://registry.npmjs.org/rollup/-/rollup-3.29.5.tgz" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-3.29.5.tgz#8a2e477a758b520fb78daf04bca4c522c1da8a54" integrity sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w== optionalDependencies: fsevents "~2.3.2" +rollup@^4.30.1: + version "4.34.9" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.34.9.tgz#e1eb397856476778aeb6ac2ac3d09b2ce177a558" + integrity sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ== + dependencies: + "@types/estree" "1.0.6" + optionalDependencies: + "@rollup/rollup-android-arm-eabi" "4.34.9" + "@rollup/rollup-android-arm64" "4.34.9" + "@rollup/rollup-darwin-arm64" "4.34.9" + "@rollup/rollup-darwin-x64" "4.34.9" + "@rollup/rollup-freebsd-arm64" "4.34.9" + "@rollup/rollup-freebsd-x64" "4.34.9" + "@rollup/rollup-linux-arm-gnueabihf" "4.34.9" + "@rollup/rollup-linux-arm-musleabihf" "4.34.9" + "@rollup/rollup-linux-arm64-gnu" "4.34.9" + "@rollup/rollup-linux-arm64-musl" "4.34.9" + "@rollup/rollup-linux-loongarch64-gnu" "4.34.9" + "@rollup/rollup-linux-powerpc64le-gnu" "4.34.9" + "@rollup/rollup-linux-riscv64-gnu" "4.34.9" + "@rollup/rollup-linux-s390x-gnu" "4.34.9" + "@rollup/rollup-linux-x64-gnu" "4.34.9" + "@rollup/rollup-linux-x64-musl" "4.34.9" + "@rollup/rollup-win32-arm64-msvc" "4.34.9" + "@rollup/rollup-win32-ia32-msvc" "4.34.9" + "@rollup/rollup-win32-x64-msvc" "4.34.9" + fsevents "~2.3.2" + +semver@^7.3.6, semver@^7.6.3: + version "7.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + siwe@^2.1.4: version "2.3.2" - resolved "https://registry.npmjs.org/siwe/-/siwe-2.3.2.tgz" + resolved "https://registry.yarnpkg.com/siwe/-/siwe-2.3.2.tgz#0794ae25f734f3068de0ab093ddd2f7867bc2d67" integrity sha512-aSf+6+Latyttbj5nMu6GF3doMfv2UYj83hhwZgUF20ky6fTS83uVhkQABdIVnEuS8y1bBdk7p6ltb9SmlhTTlA== dependencies: "@spruceid/siwe-parser" "^2.1.2" @@ -739,54 +1529,123 @@ siwe@^2.1.4: uri-js "^4.4.1" valid-url "^1.0.9" -source-map-js@^1.2.0, source-map-js@^1.2.1: +sortablejs@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.6.tgz#ff93699493f5b8ab8d828f933227b4988df1d393" + integrity sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A== + +source-map-js@^1.0.2, source-map-js@^1.2.0, source-map-js@^1.2.1: version "1.2.1" - resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46" integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA== split2@^4.1.0: version "4.2.0" - resolved "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz" + resolved "https://registry.yarnpkg.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +synckit@^0.9.1: + version "0.9.2" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.9.2.tgz#a3a935eca7922d48b9e7d6c61822ee6c3ae4ec62" + integrity sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw== + dependencies: + "@pkgr/core" "^0.1.0" + tslib "^2.6.2" + tslib@2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01" integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA== +tslib@^2.6.2: + version "2.8.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" + integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + undici-types@~6.19.2: version "6.19.8" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02" integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw== -uri-js@^4.4.1: +uri-js@^4.2.2, uri-js@^4.4.1: version "4.4.1" - resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" +util-deprecate@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + valid-url@^1.0.9: version "1.0.9" - resolved "https://registry.npmjs.org/valid-url/-/valid-url-1.0.9.tgz" + resolved "https://registry.yarnpkg.com/valid-url/-/valid-url-1.0.9.tgz#1c14479b40f1397a75782f115e4086447433a200" integrity sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA== -vite@^4.2.1: - version "4.5.9" - resolved "https://registry.yarnpkg.com/vite/-/vite-4.5.9.tgz#f4dfd4c4295743b50c3e3f90df798d70de699e4f" - integrity sha512-qK9W4xjgD3gXbC0NmdNFFnVFLMWSNiR3swj957yutwzzN16xF/E7nmtAyp1rT9hviDroQANjE4HK3H4WqWdFtw== +vite@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/vite/-/vite-6.2.0.tgz#9dcb543380dab18d8384eb840a76bf30d78633f0" + integrity sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ== dependencies: - esbuild "^0.18.10" - postcss "^8.4.27" - rollup "^3.27.1" + esbuild "^0.25.0" + postcss "^8.5.3" + rollup "^4.30.1" optionalDependencies: - fsevents "~2.3.2" + fsevents "~2.3.3" vue-demi@^0.14.10: version "0.14.10" - resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz" + resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.14.10.tgz#afc78de3d6f9e11bf78c55e8510ee12814522f04" integrity sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg== +vue-eslint-parser@^9.4.3: + version "9.4.3" + resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz#9b04b22c71401f1e8bca9be7c3e3416a4bde76a8" + integrity sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg== + dependencies: + debug "^4.3.4" + eslint-scope "^7.1.1" + eslint-visitor-keys "^3.3.0" + espree "^9.3.1" + esquery "^1.4.0" + lodash "^4.17.21" + semver "^7.3.6" + +vue-i18n@9: + version "9.14.2" + resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.14.2.tgz#e7f657664fcb3ccf99ecea684fc56e0f8e5335ce" + integrity sha512-JK9Pm80OqssGJU2Y6F7DcM8RFHqVG4WkuCqOZTVsXkEzZME7ABejAUqUdA931zEBedc4thBgSUWxeQh4uocJAQ== + dependencies: + "@intlify/core-base" "9.14.2" + "@intlify/shared" "9.14.2" + "@vue/devtools-api" "^6.5.0" + vue-router@^4.1.6: version "4.5.0" resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.5.0.tgz#58fc5fe374e10b6018f910328f756c3dae081f14" @@ -805,12 +1664,34 @@ vue@^3.2.47: "@vue/server-renderer" "3.5.13" "@vue/shared" "3.5.13" +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + ws@8.17.1: version "8.17.1" resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + xtend@^4.0.0: version "4.0.2" - resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==