diff --git a/backend/app.js b/backend/app.js index 0da8959..4296241 100644 --- a/backend/app.js +++ b/backend/app.js @@ -26,46 +26,47 @@ app.set('host', '0.0.0.0'); app.set('port', process.env.PORT || 8000); // Настройка CORS -app.use(cors({ - origin: [ - 'http://localhost:5173', - 'http://127.0.0.1:5173' // Добавляем альтернативный origin - ], - credentials: true, - methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization', 'Cookie'] -})); +app.use( + cors({ + origin: [ + 'http://localhost:5173', + 'http://127.0.0.1:5173', // Добавляем альтернативный origin + ], + credentials: true, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization', 'Cookie'], + }) +); // Настройка сессии -app.use(session({ - store: new pgSession({ - pool, - tableName: 'session', - }), - secret: process.env.SESSION_SECRET || 'hb3atoken', - name: 'sessionId', - resave: false, - saveUninitialized: true, - cookie: { - maxAge: 30 * 24 * 60 * 60 * 1000, - httpOnly: true, - secure: false, - sameSite: 'lax', - path: '/' - } -})); +app.use( + session({ + store: new pgSession({ + pool, + tableName: 'session', + }), + secret: process.env.SESSION_SECRET || 'hb3atoken', + name: 'sessionId', + resave: false, + saveUninitialized: true, + cookie: { + maxAge: 30 * 24 * 60 * 60 * 1000, + httpOnly: true, + secure: false, + sameSite: 'lax', + path: '/', + }, + }) +); // Добавим middleware для проверки сессии app.use(async (req, res, next) => { console.log('Request cookies:', req.headers.cookie); console.log('Session ID:', req.sessionID); - + // Проверяем сессию в базе данных if (req.sessionID) { - const result = await pool.query( - 'SELECT sess FROM session WHERE sid = $1', - [req.sessionID] - ); + const result = await pool.query('SELECT sess FROM session WHERE sid = $1', [req.sessionID]); console.log('Session from DB:', result.rows[0]?.sess); } @@ -80,28 +81,31 @@ app.use(async (req, res, next) => { const token = authHeader.split(' ')[1]; try { // Находим пользователя по токену - const { rows } = await pool.query(` + const { rows } = await pool.query( + ` SELECT u.id, (u.role = 'admin') as is_admin, u.address FROM users u WHERE u.id = $1 - `, [token]); - + `, + [token] + ); + if (rows.length > 0) { const user = rows[0]; req.session.userId = user.id; req.session.address = user.address; req.session.isAdmin = user.is_admin; req.session.authenticated = true; - - await new Promise(resolve => req.session.save(resolve)); + + await new Promise((resolve) => req.session.save(resolve)); } } catch (error) { console.error('Error checking auth header:', error); } } - + next(); }); @@ -110,9 +114,11 @@ app.use(express.json()); app.use(express.urlencoded({ extended: true })); // Настройка безопасности -app.use(helmet({ - contentSecurityPolicy: false // Отключаем CSP для разработки -})); +app.use( + helmet({ + contentSecurityPolicy: false, // Отключаем CSP для разработки + }) +); // Логирование запросов app.use((req, res, next) => { @@ -156,32 +162,35 @@ app.get('/api/health', async (req, res) => { try { // Проверяем подключение к БД await pool.query('SELECT NOW()'); - + // Проверяем AI сервис const aiStatus = await aiAssistant.checkHealth(); - + res.json({ status: 'ok', timestamp: new Date().toISOString(), database: 'connected', - ai: aiStatus + ai: aiStatus, }); } catch (error) { logger.error('Health check failed:', error); res.status(500).json({ status: 'error', - error: error.message + error: error.message, }); } }); // Очистка старых сессий -setInterval(async () => { - try { - await pool.query('DELETE FROM session WHERE expire < NOW()'); - } catch (error) { - console.error('Error cleaning old sessions:', error); - } -}, 15 * 60 * 1000); // Каждые 15 минут +setInterval( + async () => { + try { + await pool.query('DELETE FROM session WHERE expire < NOW()'); + } catch (error) { + console.error('Error cleaning old sessions:', error); + } + }, + 15 * 60 * 1000 +); // Каждые 15 минут module.exports = { app, nonceStore }; diff --git a/backend/config/session.js b/backend/config/session.js index 289d6f7..aa9929b 100644 --- a/backend/config/session.js +++ b/backend/config/session.js @@ -16,10 +16,10 @@ const sessionConfig = { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', - path: '/' - } + path: '/', + }, }; module.exports = { - sessionMiddleware: session(sessionConfig) -}; \ No newline at end of file + sessionMiddleware: session(sessionConfig), +}; diff --git a/backend/db.js b/backend/db.js index 2b13556..552d2b8 100644 --- a/backend/db.js +++ b/backend/db.js @@ -19,10 +19,10 @@ const pool = new Pool({ pool.query('SELECT NOW()', (err, res) => { if (err) { console.error('Ошибка подключения к базе данных:', err); - + // Пробуем альтернативное подключение console.log('Попытка альтернативного подключения через прямые параметры...'); - + const altPool = new Pool({ host: process.env.DB_HOST || 'localhost', port: parseInt(process.env.DB_PORT || '5432'), @@ -30,7 +30,7 @@ pool.query('SELECT NOW()', (err, res) => { user: process.env.DB_USER || 'dapp_user', password: process.env.DB_PASSWORD, }); - + altPool.query('SELECT NOW()', (altErr, altRes) => { if (altErr) { console.error('Альтернативное подключение тоже не удалось:', altErr); @@ -56,10 +56,13 @@ const query = (text, params) => { // Функция для сохранения гостевого сообщения в базе данных async function saveGuestMessageToDatabase(message, language, guestId) { try { - await query(` + await query( + ` INSERT INTO guest_messages (guest_id, content, language, created_at) VALUES ($1, $2, $3, NOW()) - `, [guestId, message, language]); + `, + [guestId, message, language] + ); console.log('Гостевое сообщение успешно сохранено:', message); } catch (error) { console.error('Ошибка при сохранении гостевого сообщения:', error); diff --git a/backend/db/index.js b/backend/db/index.js index 64e65aa..eff32a0 100644 --- a/backend/db/index.js +++ b/backend/db/index.js @@ -18,4 +18,4 @@ pool.query('SELECT NOW()', (err, res) => { } }); -module.exports = { pool }; \ No newline at end of file +module.exports = { pool }; diff --git a/backend/db/init.js b/backend/db/init.js index 4342d93..4111a3e 100644 --- a/backend/db/init.js +++ b/backend/db/init.js @@ -13,7 +13,7 @@ async function initRoles() { WHERE table_name = 'roles' ); `); - + if (!tableExists.rows[0].exists) { // Создаем таблицу roles await pool.query(` @@ -24,40 +24,44 @@ async function initRoles() { created_at TIMESTAMP NOT NULL DEFAULT NOW() ); `); - + // Добавляем роли await pool.query(` INSERT INTO roles (id, name, description) VALUES (3, 'user', 'Обычный пользователь'), (4, 'admin', 'Администратор с полным доступом'); `); - + console.log('Таблица roles создана и заполнена'); } else { // Проверяем наличие ролей const rolesExist = await pool.query(` SELECT COUNT(*) FROM roles WHERE id IN (3, 4); `); - + if (rolesExist.rows[0].count < 2) { // Добавляем недостающие роли - const userRoleExists = await pool.query(`SELECT EXISTS (SELECT FROM roles WHERE name = 'user');`); - const adminRoleExists = await pool.query(`SELECT EXISTS (SELECT FROM roles WHERE name = 'admin');`); - + const userRoleExists = await pool.query( + `SELECT EXISTS (SELECT FROM roles WHERE name = 'user');` + ); + const adminRoleExists = await pool.query( + `SELECT EXISTS (SELECT FROM roles WHERE name = 'admin');` + ); + if (!userRoleExists.rows[0].exists) { await pool.query(` INSERT INTO roles (id, name, description) VALUES (3, 'user', 'Обычный пользователь'); `); } - + if (!adminRoleExists.rows[0].exists) { await pool.query(` INSERT INTO roles (id, name, description) VALUES (4, 'admin', 'Администратор с полным доступом'); `); } - + console.log('Таблица roles обновлена'); } } @@ -80,31 +84,29 @@ async function initializeDatabase() { // Путь к папке с миграциями const migrationsPath = path.join(__dirname, 'migrations'); - + // Получаем все файлы миграций - const migrationFiles = fs.readdirSync(migrationsPath) - .filter(file => file.endsWith('.sql')) + const migrationFiles = fs + .readdirSync(migrationsPath) + .filter((file) => file.endsWith('.sql')) .sort(); // Получаем выполненные миграции const { rows } = await pool.query('SELECT name FROM migrations'); - const executedMigrations = new Set(rows.map(row => row.name)); + const executedMigrations = new Set(rows.map((row) => row.name)); // Выполняем только новые миграции for (const file of migrationFiles) { if (!executedMigrations.has(file)) { const filePath = path.join(migrationsPath, file); const sql = fs.readFileSync(filePath, 'utf8'); - + logger.info(`Executing migration: ${file}`); await pool.query(sql); - + // Записываем выполненную миграцию - await pool.query( - 'INSERT INTO migrations (name) VALUES ($1)', - [file] - ); - + await pool.query('INSERT INTO migrations (name) VALUES ($1)', [file]); + logger.info(`Migration completed: ${file}`); } } @@ -116,4 +118,4 @@ async function initializeDatabase() { } } -module.exports = { initializeDatabase }; \ No newline at end of file +module.exports = { initializeDatabase }; diff --git a/backend/docs/identity_architecture.md b/backend/docs/identity_architecture.md index 89e01b4..1d042e5 100644 --- a/backend/docs/identity_architecture.md +++ b/backend/docs/identity_architecture.md @@ -7,6 +7,7 @@ Система идентификации пользователей построена на следующих таблицах: 1. **users** - Основная таблица пользователей + - `id SERIAL PRIMARY KEY` - Основной идентификатор пользователя - `status` - Статус пользователя (active, blocked) - `role` - Роль пользователя (user, admin) @@ -14,6 +15,7 @@ - Поля `username`, `email` и `address` являются устаревшими и должны быть NULL 2. **user_identities** - Таблица идентификаторов пользователей + - `id SERIAL PRIMARY KEY` - Идентификатор записи - `user_id INTEGER REFERENCES users(id)` - Ссылка на пользователя - `provider VARCHAR(50)` - Тип идентификатора (email, wallet, telegram, username) @@ -22,6 +24,7 @@ - Ограничение `CHECK (provider IN ('email', 'wallet', 'telegram', 'username'))` - запрещает тип 'guest' 3. **guest_user_mapping** - Таблица связи гостевых идентификаторов с пользователями + - `id SERIAL PRIMARY KEY` - Идентификатор записи - `user_id INTEGER REFERENCES users(id)` - Ссылка на пользователя - `guest_id VARCHAR(255)` - Гостевой идентификатор @@ -29,6 +32,7 @@ - Уникальный ключ `guest_id` 4. **messages** - Таблица сообщений + - `id SERIAL PRIMARY KEY` - Идентификатор сообщения - `conversation_id INTEGER REFERENCES conversations(id)` - Ссылка на диалог - `user_id INTEGER REFERENCES users(id)` - Прямая ссылка на пользователя @@ -56,7 +60,6 @@ - Создается запись в таблице `users` - Создается запись в таблице `user_identities` с соответствующим провайдером - Гостевой ID сохраняется в таблице `guest_user_mapping` (не в user_identities) - 2. После аутентификации система автоматически обрабатывает гостевые сообщения: - Вызывается метод `linkGuestMessages` - Создается новый диалог для гостевых сообщений @@ -69,7 +72,7 @@ Если пользователь аутентифицируется разными способами, система может объединить его данные: 1. Система проверяет связанных пользователей через `user_identities` -2. Если находятся связанные пользователи, вызывается метод `migrateUserData` +2. Если находятся связанные пользователи, вызывается метод `migrateUserData` 3. Данные от вторичных аккаунтов мигрируют к основному: - Идентификаторы в таблице `user_identities` - Гостевые связи в таблице `guest_user_mapping` @@ -89,6 +92,7 @@ ## Обработка ошибок 1. Если возникает ошибка при обработке гостевых сообщений, система: + - Логирует ошибку - Продолжает попытки обработки при следующих авторизациях - Не удаляет гостевые сообщения до успешной обработки @@ -98,6 +102,7 @@ ## Оптимизации 1. Индексы созданы для всех полей, используемых в запросах: + - `user_identities(user_id)` - `user_identities(provider, provider_id)` - `guest_user_mapping(guest_id)` @@ -106,6 +111,7 @@ - `messages(conversation_id)` 2. Триггеры автоматически поддерживают целостность данных: + - Автоматическое заполнение `user_id` в таблице `messages` - Очистка неиспользуемых полей в таблице `users` @@ -117,6 +123,7 @@ ## Функции для диагностики 1. **verify_migration_017()** - проверяет состояние гостевых идентификаторов + - `guest_identities_count` - количество гостевых идентификаторов в таблице user_identities - `guest_mapping_count` - количество записей в таблице guest_user_mapping - `missing_mappings` - количество гостевых ID, которые отсутствуют в guest_user_mapping @@ -127,4 +134,4 @@ - `wallet_identities` - количество идентификаторов wallet - `email_identities` - количество идентификаторов email - `telegram_identities` - количество идентификаторов telegram - - `duplicate_provider_ids` - количество дублирующихся идентификаторов \ No newline at end of file + - `duplicate_provider_ids` - количество дублирующихся идентификаторов diff --git a/backend/docs/migration_guide.md b/backend/docs/migration_guide.md index 50bf770..5809a77 100644 --- a/backend/docs/migration_guide.md +++ b/backend/docs/migration_guide.md @@ -47,10 +47,10 @@ CREATE TABLE IF NOT EXISTS example_table ( ); -- Добавление колонки, если она отсутствует -DO $$ -BEGIN +DO $$ +BEGIN IF NOT EXISTS ( - SELECT 1 FROM information_schema.columns + SELECT 1 FROM information_schema.columns WHERE table_name = 'example_table' AND column_name = 'new_column' ) THEN ALTER TABLE example_table ADD COLUMN new_column INTEGER; @@ -69,6 +69,7 @@ END $$; - `verify_identity_system()` - проверка состояния системы идентификации пользователей Пример использования: + ```sql SELECT * FROM verify_identity_system(); -``` \ No newline at end of file +``` diff --git a/backend/eslint.config.js b/backend/eslint.config.cjs similarity index 58% rename from backend/eslint.config.js rename to backend/eslint.config.cjs index 8be5868..088d2a1 100644 --- a/backend/eslint.config.js +++ b/backend/eslint.config.cjs @@ -1,6 +1,6 @@ -import globals from 'globals'; +const globals = require('globals'); -export default [ +module.exports = [ { ignores: ['node_modules/**', 'artifacts/**', 'sessions/**', 'logs/**', 'data/**'], }, @@ -8,10 +8,11 @@ export default [ files: ['**/*.js'], languageOptions: { ecmaVersion: 2022, - sourceType: 'module', + sourceType: 'module', // Оставляем module, т.к. ESLint может анализировать ES модули globals: { ...globals.node, ...globals.es2021, + // Для тестов Mocha describe: 'readonly', it: 'readonly', beforeEach: 'readonly', @@ -21,10 +22,10 @@ export default [ }, }, rules: { - 'no-unused-vars': 'off', - 'no-console': 'off', + 'no-unused-vars': 'warn', // Лучше warn, чем off + 'no-console': 'off', // Оставляем off для логов в Node.js 'no-undef': 'error', 'no-duplicate-imports': 'error', }, }, -]; +]; \ No newline at end of file diff --git a/backend/logs/combined.log b/backend/logs/combined.log index b65dce1..9cc010c 100644 --- a/backend/logs/combined.log +++ b/backend/logs/combined.log @@ -7920,3 +7920,578 @@ {"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T13:51:33.815Z"} {"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T13:52:15.206Z"} {"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T13:52:15.210Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T13:52:34.099Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T13:52:34.105Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T13:53:04.292Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T13:53:04.296Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T13:54:15.742Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T13:54:15.746Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T13:55:15.978Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T13:55:15.981Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T13:56:16.276Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T13:56:16.281Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T13:57:14.461Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T13:57:14.468Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T13:58:14.617Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T13:58:14.622Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T13:59:14.424Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T13:59:14.428Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:00:15.127Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:00:15.132Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:01:15.786Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:01:15.789Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:02:15.874Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:02:15.883Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:03:16.140Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:03:16.145Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:04:16.400Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:04:16.403Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:05:14.537Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:05:14.541Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:06:14.778Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:06:14.783Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:07:15.131Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:07:15.136Z"} +{"level":"info","message":"POST /api/auth/logout","timestamp":"2025-04-21T14:07:24.527Z"} +{"level":"info","message":"Session saved successfully","timestamp":"2025-04-21T14:07:24.547Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:07:24.572Z"} +{"level":"info","message":"Session saved successfully","timestamp":"2025-04-21T14:07:24.578Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:15:43.797Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:15:43.819Z"} +{"level":"info","message":"POST /api/auth/telegram/init","timestamp":"2025-04-21T14:23:49.891Z"} +{"level":"info","message":"Generated verification code: FBRKH5","timestamp":"2025-04-21T14:23:49.892Z"} +{"level":"info","message":"Creating verification code for telegram:e50442b5c6fad89fc34176fc078d56f1, userId: null","timestamp":"2025-04-21T14:23:49.892Z"} +{"level":"info","message":"Verification code created successfully for telegram:e50442b5c6fad89fc34176fc078d56f1","timestamp":"2025-04-21T14:23:49.900Z"} +{"level":"info","message":"[initTelegramAuth] Created verification code for guestId: e50442b5c6fad89fc34176fc078d56f1","timestamp":"2025-04-21T14:23:49.901Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:23:51.775Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:23:53.643Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:23:55.499Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:23:57.546Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:23:59.414Z"} +{"level":"info","message":"Starting Telegram auth process for code:","timestamp":"2025-04-21T14:24:01.260Z"} +{"level":"info","message":"Using existing user 1 for Telegram account 5155951987","timestamp":"2025-04-21T14:24:01.269Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:24:01.312Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:24:01.320Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:24:01.362Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:24:01.365Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:24:01.390Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:24:01.394Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:24:01.415Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:24:01.483Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:24:05.271Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:24:05.274Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:24:05.335Z"} +{"level":"info","message":"GET /api/chat/history?offset=36&limit=30","timestamp":"2025-04-21T14:24:05.410Z"} +{"level":"info","message":"POST /api/auth/logout","timestamp":"2025-04-21T14:24:10.741Z"} +{"level":"info","message":"Session saved successfully","timestamp":"2025-04-21T14:24:10.762Z"} +{"level":"info","message":"GET /api/auth/nonce?address=0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B","timestamp":"2025-04-21T14:42:10.379Z"} +{"level":"info","message":"Nonce f90108f4ef8e168fc79bc36648d6fd95 сохранен для адреса 0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B","timestamp":"2025-04-21T14:42:10.387Z"} +{"level":"info","message":"POST /api/auth/verify","timestamp":"2025-04-21T14:42:12.698Z"} +{"level":"info","message":"[verify] Verifying signature for address: 0xF45aa4917b3775bA37f48Aeb3dc1a943561e9e0B","timestamp":"2025-04-21T14:42:12.699Z"} +{"level":"info","message":"Checking admin role for address: 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","timestamp":"2025-04-21T14:42:12.793Z"} +{"address":"0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","balance":"500000.0","contract":"0x4B294265720B09ca39BFBA18c7E368413c0f68eB","hasTokens":true,"level":"info","message":"Token balance on bsc:","timestamp":"2025-04-21T14:42:13.708Z"} +{"level":"info","message":"Found admin tokens on bsc","timestamp":"2025-04-21T14:42:13.709Z"} +{"address":"0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","balance":"1500000.0","contract":"0xd95a45fc46a7300e6022885afec3d618d7d3f27c","hasTokens":true,"level":"info","message":"Token balance on eth:","timestamp":"2025-04-21T14:42:13.990Z"} +{"level":"info","message":"Found admin tokens on eth","timestamp":"2025-04-21T14:42:13.990Z"} +{"address":"0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","balance":"499999.9","contract":"0xdce769b847a0a697239777d0b1c7dd33b6012ba0","hasTokens":true,"level":"info","message":"Token balance on arbitrum:","timestamp":"2025-04-21T14:42:13.992Z"} +{"level":"info","message":"Found admin tokens on arbitrum","timestamp":"2025-04-21T14:42:13.993Z"} +{"address":"0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","balance":"454852.0","contract":"0x351f59de4fedbdf7601f5592b93db3b9330c1c1d","hasTokens":true,"level":"info","message":"Token balance on polygon:","timestamp":"2025-04-21T14:42:16.371Z"} +{"level":"info","message":"Found admin tokens on polygon","timestamp":"2025-04-21T14:42:16.372Z"} +{"balances":{"arbitrum":"499999.9","bsc":"500000.0","eth":"1500000.0","polygon":"454852.0"},"level":"info","message":"Admin role summary for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b:","networks":["bsc","eth","arbitrum","polygon"],"timestamp":"2025-04-21T14:42:16.372Z"} +{"level":"info","message":"Admin role granted for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","timestamp":"2025-04-21T14:42:16.373Z"} +{"level":"info","message":"[verify] Found or created user 1 for wallet 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","timestamp":"2025-04-21T14:42:16.373Z"} +{"level":"info","message":"Checking admin tokens for address: 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","timestamp":"2025-04-21T14:42:16.373Z"} +{"level":"info","message":"Checking admin role for address: 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","timestamp":"2025-04-21T14:42:16.374Z"} +{"address":"0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","balance":"500000.0","contract":"0x4B294265720B09ca39BFBA18c7E368413c0f68eB","hasTokens":true,"level":"info","message":"Token balance on bsc:","timestamp":"2025-04-21T14:42:16.662Z"} +{"level":"info","message":"Found admin tokens on bsc","timestamp":"2025-04-21T14:42:16.662Z"} +{"address":"0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","balance":"1500000.0","contract":"0xd95a45fc46a7300e6022885afec3d618d7d3f27c","hasTokens":true,"level":"info","message":"Token balance on eth:","timestamp":"2025-04-21T14:42:16.788Z"} +{"level":"info","message":"Found admin tokens on eth","timestamp":"2025-04-21T14:42:16.789Z"} +{"address":"0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","balance":"454852.0","contract":"0x351f59de4fedbdf7601f5592b93db3b9330c1c1d","hasTokens":true,"level":"info","message":"Token balance on polygon:","timestamp":"2025-04-21T14:42:16.834Z"} +{"level":"info","message":"Found admin tokens on polygon","timestamp":"2025-04-21T14:42:16.835Z"} +{"address":"0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","balance":"499999.9","contract":"0xdce769b847a0a697239777d0b1c7dd33b6012ba0","hasTokens":true,"level":"info","message":"Token balance on arbitrum:","timestamp":"2025-04-21T14:42:17.006Z"} +{"level":"info","message":"Found admin tokens on arbitrum","timestamp":"2025-04-21T14:42:17.007Z"} +{"balances":{"arbitrum":"499999.9","bsc":"500000.0","eth":"1500000.0","polygon":"454852.0"},"level":"info","message":"Admin role summary for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b:","networks":["bsc","eth","polygon","arbitrum"],"timestamp":"2025-04-21T14:42:17.007Z"} +{"level":"info","message":"Admin role granted for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","timestamp":"2025-04-21T14:42:17.007Z"} +{"level":"info","message":"Updated user 1 role to admin based on token holdings","timestamp":"2025-04-21T14:42:17.013Z"} +{"level":"info","message":"Session saved successfully","timestamp":"2025-04-21T14:42:17.019Z"} +{"level":"info","message":"[linkGuestMessages] Starting for user 1 with guestId=undefined, previousGuestId=undefined","timestamp":"2025-04-21T14:42:17.020Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=bdbb9e9a6b156fb309480755554c017c","timestamp":"2025-04-21T14:42:17.023Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=e4ca877a9e8a823241c5f1669ad4b177","timestamp":"2025-04-21T14:42:17.029Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=63404462543ed032df62eb0597cfbf92","timestamp":"2025-04-21T14:42:17.032Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=53e3ea1b2c59ed4f4b37a6402da579db","timestamp":"2025-04-21T14:42:17.036Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=1745240790811-mxagvk0lf","timestamp":"2025-04-21T14:42:17.040Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=8d447b0a532867a90636e000c9bbb72a","timestamp":"2025-04-21T14:42:17.043Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=3987b209726bd85f79f6668c69cec67a","timestamp":"2025-04-21T14:42:17.047Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=e7dd7cc2be1f4cf5628ac67496f0ff60","timestamp":"2025-04-21T14:42:17.050Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=1643e4ae05e31df62ceaa1c784dbf2e7","timestamp":"2025-04-21T14:42:17.054Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=b6de4a34d192b6d3c6a04d64921bc2c6","timestamp":"2025-04-21T14:42:17.057Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=d85d2c40506052a630bb7737bc2769e6","timestamp":"2025-04-21T14:42:17.062Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=8edb112101cbbaa5bef2c3a08f275ec6","timestamp":"2025-04-21T14:42:17.066Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=1745079293895-f5bi3mnlq","timestamp":"2025-04-21T14:42:17.069Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=f74f7950c4b8085f2cead1620d087bb5","timestamp":"2025-04-21T14:42:17.073Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=9c7f2342b8ff1f8fe4d545f6d2af05d4","timestamp":"2025-04-21T14:42:17.076Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=bf299ef8e5ac941443b24bc023d55bdf","timestamp":"2025-04-21T14:42:17.080Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=e70ff3b19f49cb1ce11e3bf9d4c3f5b5","timestamp":"2025-04-21T14:42:17.083Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=dca0cf1d9efe806bc9915e5a57c344b3","timestamp":"2025-04-21T14:42:17.087Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=19ca3c1e1a77f6688e48e996ec94e32b","timestamp":"2025-04-21T14:42:17.090Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=dc34a86ca22b491572bc728db26ec8d2","timestamp":"2025-04-21T14:42:17.094Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=cd399a7b2782ab9dcd510a6d624311ef","timestamp":"2025-04-21T14:42:17.097Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=5fbeaeed77f7e790089e3687b6272cc4","timestamp":"2025-04-21T14:42:17.101Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=06a8da2f6b8ccb05be8606d6f5785c3e","timestamp":"2025-04-21T14:42:17.105Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=12b912bafbdae6136947a2405c43afc7","timestamp":"2025-04-21T14:42:17.109Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=75e90bb5664f78f2d4e89dba9c764340","timestamp":"2025-04-21T14:42:17.112Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=d1a1a0c6759fa0092a9ac54cc6bca38c","timestamp":"2025-04-21T14:42:17.116Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=5c62c06c28391e47ca9da69d82d6b665","timestamp":"2025-04-21T14:42:17.119Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=e1313ff9002847b61b9f8ea825150512","timestamp":"2025-04-21T14:42:17.123Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=4b83b24f9c0cb26179c0637632426e4a","timestamp":"2025-04-21T14:42:17.126Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=221d5a74f84eca5d55db182e0a3d9143","timestamp":"2025-04-21T14:42:17.130Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=7e030d98195da1dd7f81b669c666dca7","timestamp":"2025-04-21T14:42:17.133Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=4256b2a7e0d8b7ed947ba6c1f5f72d15","timestamp":"2025-04-21T14:42:17.136Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=a87f7b05515098ad5a3079377690f769","timestamp":"2025-04-21T14:42:17.141Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=4401b63702956724a4ccb3bb849f7207","timestamp":"2025-04-21T14:42:17.144Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=5957393c426559cd5ffb817d29e4da37","timestamp":"2025-04-21T14:42:17.148Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=064746c1fd4456b7021c936bb9638dfc","timestamp":"2025-04-21T14:42:17.151Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=1745098219233-r5wj2olz0","timestamp":"2025-04-21T14:42:17.155Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=1745243339797-5jz07df5q","timestamp":"2025-04-21T14:42:17.158Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=e50442b5c6fad89fc34176fc078d56f1","timestamp":"2025-04-21T14:42:17.162Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=1745243066352-5zqw1zro4","timestamp":"2025-04-21T14:42:17.165Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=8f897f5f63a679da6893cac2548cdd8c","timestamp":"2025-04-21T14:42:17.169Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=f622d55418e4159e864ea4e6e18115d9","timestamp":"2025-04-21T14:42:17.172Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=cee8775f1ef14ec06069222412527621","timestamp":"2025-04-21T14:42:17.176Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=4801418ef94eac344f7372c5db4bcd41","timestamp":"2025-04-21T14:42:17.180Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=6f93b96d30b4ef36faa2943c5635d949","timestamp":"2025-04-21T14:42:17.184Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=ddf4efa4dca07970401397faae6ad10d","timestamp":"2025-04-21T14:42:17.187Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=a9c5cab9491ffd9a8340488892a3a83f","timestamp":"2025-04-21T14:42:17.190Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=fa541d53f0b7910b5d90be7822f211c0","timestamp":"2025-04-21T14:42:17.194Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=9b1fb20e39a4215abe7c83b8eb752b40","timestamp":"2025-04-21T14:42:17.197Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=750e6a49666b28814122e1d901aea6b5","timestamp":"2025-04-21T14:42:17.201Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=8bbf43a7d107802415e69966ec47edd7","timestamp":"2025-04-21T14:42:17.204Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=784e89fbeba721cf5c3dda2267d9ddd5","timestamp":"2025-04-21T14:42:17.208Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=30c3be3e079722b3a8d20203b1a567f9","timestamp":"2025-04-21T14:42:17.211Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=1745221905698-9rejq7xoe","timestamp":"2025-04-21T14:42:17.215Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=85f386bbe2c9b7affbd50d0366a39147","timestamp":"2025-04-21T14:42:17.218Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=2d284866bfe0da04a3716d3f96d2cc64","timestamp":"2025-04-21T14:42:17.222Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=d6c03e2a1a30f997e7ae94097b8bdc33","timestamp":"2025-04-21T14:42:17.225Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=00e90de9aef9134750d4519a5e10c4ee","timestamp":"2025-04-21T14:42:17.229Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=f79a4edd77d8db2def23394f52e0d0a4","timestamp":"2025-04-21T14:42:17.234Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=b710161e84700cd43778fb846c4017ff","timestamp":"2025-04-21T14:42:17.238Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=5a7dec430e4da72e53b8b07fc1713a4e","timestamp":"2025-04-21T14:42:17.241Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=2f2e2330b4263952294d93ae6a86146a","timestamp":"2025-04-21T14:42:17.245Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=b2e89db3924be1b1d3b0f06f130cf8e0","timestamp":"2025-04-21T14:42:17.250Z"} +{"level":"info","message":"[processGuestMessagesWrapper] Processing messages: userId=1, guestId=b4460596d1947e93312e2607cda758ba","timestamp":"2025-04-21T14:42:17.254Z"} +{"level":"info","message":"Session saved successfully","timestamp":"2025-04-21T14:42:17.262Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:42:17.275Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:42:17.278Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:42:17.300Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:42:17.306Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:42:17.321Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:42:17.324Z"} +{"level":"info","message":"GET /api/tokens/balances","timestamp":"2025-04-21T14:42:17.330Z"} +{"level":"info","message":"Fetching token balances for address: 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","timestamp":"2025-04-21T14:42:17.332Z"} +{"balance":"1500000.0","contract":"0xd95a45fc46a7300e6022885afec3d618d7d3f27c","level":"info","message":"Token balance for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b on eth:","timestamp":"2025-04-21T14:42:17.758Z"} +{"balance":"500000.0","contract":"0x4B294265720B09ca39BFBA18c7E368413c0f68eB","level":"info","message":"Token balance for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b on bsc:","timestamp":"2025-04-21T14:42:18.103Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:42:18.241Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:42:18.244Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:42:18.258Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:42:18.278Z"} +{"balance":"499999.9","contract":"0xdce769b847a0a697239777d0b1c7dd33b6012ba0","level":"info","message":"Token balance for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b on arbitrum:","timestamp":"2025-04-21T14:42:18.720Z"} +{"balance":"454852.0","contract":"0x351f59de4fedbdf7601f5592b93db3b9330c1c1d","level":"info","message":"Token balance for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b on polygon:","timestamp":"2025-04-21T14:42:19.150Z"} +{"arbitrum":"499999.9","bsc":"500000.0","eth":"1500000.0","level":"info","message":"Token balances fetched for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b:","polygon":"454852.0","timestamp":"2025-04-21T14:42:19.150Z"} +{"level":"info","message":"GET /api/tokens/balances","timestamp":"2025-04-21T14:42:19.158Z"} +{"level":"info","message":"Fetching token balances for address: 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b","timestamp":"2025-04-21T14:42:19.159Z"} +{"balance":"1500000.0","contract":"0xd95a45fc46a7300e6022885afec3d618d7d3f27c","level":"info","message":"Token balance for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b on eth:","timestamp":"2025-04-21T14:42:19.602Z"} +{"level":"info","message":"POST /api/auth/logout","timestamp":"2025-04-21T14:42:19.890Z"} +{"level":"info","message":"Session saved successfully","timestamp":"2025-04-21T14:42:19.893Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:42:19.907Z"} +{"level":"info","message":"Session saved successfully","timestamp":"2025-04-21T14:42:19.910Z"} +{"balance":"500000.0","contract":"0x4B294265720B09ca39BFBA18c7E368413c0f68eB","level":"info","message":"Token balance for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b on bsc:","timestamp":"2025-04-21T14:42:19.946Z"} +{"balance":"499999.9","contract":"0xdce769b847a0a697239777d0b1c7dd33b6012ba0","level":"info","message":"Token balance for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b on arbitrum:","timestamp":"2025-04-21T14:42:20.678Z"} +{"balance":"454852.0","contract":"0x351f59de4fedbdf7601f5592b93db3b9330c1c1d","level":"info","message":"Token balance for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b on polygon:","timestamp":"2025-04-21T14:42:21.104Z"} +{"arbitrum":"499999.9","bsc":"500000.0","eth":"1500000.0","level":"info","message":"Token balances fetched for 0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b:","polygon":"454852.0","timestamp":"2025-04-21T14:42:21.104Z"} +{"level":"info","message":"POST /api/auth/telegram/init","timestamp":"2025-04-21T14:42:21.356Z"} +{"level":"info","message":"Generated verification code: FDOU1Z","timestamp":"2025-04-21T14:42:21.357Z"} +{"level":"info","message":"Creating verification code for telegram:16236f2b04a47bc549afe4960f6aeded, userId: null","timestamp":"2025-04-21T14:42:21.357Z"} +{"level":"info","message":"Verification code created successfully for telegram:16236f2b04a47bc549afe4960f6aeded","timestamp":"2025-04-21T14:42:21.361Z"} +{"level":"info","message":"[initTelegramAuth] Created verification code for guestId: 16236f2b04a47bc549afe4960f6aeded","timestamp":"2025-04-21T14:42:21.361Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:42:23.254Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:42:25.753Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:42:27.630Z"} +{"level":"info","message":"Starting Telegram auth process for code:","timestamp":"2025-04-21T14:42:29.032Z"} +{"level":"info","message":"Using existing user 1 for Telegram account 5155951987","timestamp":"2025-04-21T14:42:29.034Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:42:29.498Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:42:29.499Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:42:29.514Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:42:29.515Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:42:29.527Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:42:29.530Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:42:29.542Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:42:29.586Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:42:31.366Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:42:31.368Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:42:31.382Z"} +{"level":"info","message":"GET /api/chat/history?offset=36&limit=30","timestamp":"2025-04-21T14:42:31.396Z"} +{"level":"info","message":"POST /api/auth/email/init","timestamp":"2025-04-21T14:42:52.368Z"} +{"level":"info","message":"[initEmailAuth] Using existing authenticated user 1 for email hb3accelerator@gmail.com","timestamp":"2025-04-21T14:42:52.372Z"} +{"level":"info","message":"Generated verification code: CIC646","timestamp":"2025-04-21T14:42:52.373Z"} +{"level":"info","message":"Creating verification code for email:hb3accelerator@gmail.com, userId: 1","timestamp":"2025-04-21T14:42:52.373Z"} +{"level":"info","message":"Verification code created successfully for email:hb3accelerator@gmail.com","timestamp":"2025-04-21T14:42:52.377Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error sending verification code: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:42:55.058Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:42:55.058Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:42:55.059Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:43:00.542Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:43:00.544Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:43:29.809Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:43:29.812Z"} +{"level":"info","message":"POST /api/auth/email/init","timestamp":"2025-04-21T14:43:48.555Z"} +{"level":"info","message":"[initEmailAuth] Using existing authenticated user 1 for email hb3accelerator@gmail.com","timestamp":"2025-04-21T14:43:48.560Z"} +{"level":"info","message":"Generated verification code: 1UEQBE","timestamp":"2025-04-21T14:43:48.560Z"} +{"level":"info","message":"Creating verification code for email:hb3accelerator@gmail.com, userId: 1","timestamp":"2025-04-21T14:43:48.560Z"} +{"level":"info","message":"Verification code created successfully for email:hb3accelerator@gmail.com","timestamp":"2025-04-21T14:43:48.565Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error sending verification code: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:43:53.439Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:43:53.439Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:43:53.440Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:43:59.923Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:43:59.925Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:44:30.983Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:44:30.986Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:45:01.130Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:45:01.133Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:45:39.212Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:45:39.217Z"} +{"level":"info","message":"POST /api/auth/email/init","timestamp":"2025-04-21T14:45:40.648Z"} +{"level":"info","message":"[initEmailAuth] Using existing authenticated user 1 for email hb3accelerator@gmail.com","timestamp":"2025-04-21T14:45:40.650Z"} +{"level":"info","message":"Generated verification code: DPO9NP","timestamp":"2025-04-21T14:45:40.651Z"} +{"level":"info","message":"Creating verification code for email:hb3accelerator@gmail.com, userId: 1","timestamp":"2025-04-21T14:45:40.651Z"} +{"level":"info","message":"Verification code created successfully for email:hb3accelerator@gmail.com","timestamp":"2025-04-21T14:45:40.654Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error sending verification code: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:45:43.536Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:45:43.537Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:45:43.537Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:46:01.386Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:46:01.389Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:46:29.398Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:46:29.401Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:46:59.562Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:46:59.566Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:47:29.639Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:29.642Z"} +{"client":{"_connected":true,"_connecting":false,"_connectionCallback":null,"_connectionError":false,"_connectionTimeoutMillis":0,"_ended":false,"_ending":true,"_events":{},"_eventsCount":1,"_poolUseCount":4,"_queryable":false,"_types":{"_types":{"arrayParser":{},"builtins":{"ABSTIME":702,"ACLITEM":1033,"BIT":1560,"BOOL":16,"BPCHAR":1042,"BYTEA":17,"CHAR":18,"CID":29,"CIDR":650,"CIRCLE":718,"DATE":1082,"FLOAT4":700,"FLOAT8":701,"GTSVECTOR":3642,"INET":869,"INT2":21,"INT4":23,"INT8":20,"INTERVAL":1186,"JSON":114,"JSONB":3802,"MACADDR":829,"MACADDR8":774,"MONEY":790,"NUMERIC":1700,"OID":26,"PATH":602,"PG_DEPENDENCIES":3402,"PG_LSN":3220,"PG_NDISTINCT":3361,"PG_NODE_TREE":194,"POLYGON":604,"REFCURSOR":1790,"REGCLASS":2205,"REGCONFIG":3734,"REGDICTIONARY":3769,"REGNAMESPACE":4089,"REGOPER":2203,"REGOPERATOR":2204,"REGPROC":24,"REGPROCEDURE":2202,"REGROLE":4096,"REGTYPE":2206,"RELTIME":703,"SMGR":210,"TEXT":25,"TID":27,"TIME":1083,"TIMESTAMP":1114,"TIMESTAMPTZ":1184,"TIMETZ":1266,"TINTERVAL":704,"TSQUERY":3615,"TSVECTOR":3614,"TXID_SNAPSHOT":2970,"UUID":2950,"VARBIT":1562,"VARCHAR":1043,"XID":28,"XML":142}},"binary":{},"text":{}},"activeQuery":null,"binary":false,"connection":{"_connecting":true,"_emitMessage":false,"_ending":true,"_events":{"end":[null,null]},"_eventsCount":23,"_keepAlive":false,"_keepAliveInitialDelayMillis":0,"lastBuffer":false,"parsedStatements":{},"ssl":false,"stream":{"_closeAfterHandlingError":false,"_events":{"end":[null,null]},"_eventsCount":4,"_hadError":false,"_host":"postgres","_parent":null,"_pendingData":null,"_pendingEncoding":"","_readableState":{"awaitDrainWriters":null,"buffer":[],"bufferIndex":0,"highWaterMark":16384,"length":0,"pipes":[]},"_server":null,"_sockname":null,"_writableState":{"bufferedIndex":0,"corked":0,"highWaterMark":16384,"length":0,"pendingcb":0,"writelen":0},"allowHalfOpen":false,"connecting":false,"server":null}},"connectionParameters":{"binary":false,"client_encoding":"","connect_timeout":0,"database":"dapp_db","host":"postgres","idle_in_transaction_session_timeout":false,"isDomainSocket":false,"lock_timeout":false,"port":5432,"query_timeout":false,"ssl":false,"statement_timeout":false,"user":"dapp_user"},"database":"dapp_db","hasExecuted":true,"host":"postgres","port":5432,"processID":32813,"queryQueue":[],"readyForQuery":true,"saslSession":null,"secretKey":-471726342,"ssl":false,"user":"dapp_user"},"code":"57P01","file":"postgres.c","length":116,"level":"error","line":"3286","message":"Uncaught Exception: terminating connection due to administrator command","name":"error","routine":"ProcessInterrupts","severity":"FATAL","stack":"error: terminating connection due to administrator command\n at Parser.parseErrorMessage (/app/node_modules/pg-protocol/dist/parser.js:283:98)\n at Parser.handlePacket (/app/node_modules/pg-protocol/dist/parser.js:122:29)\n at Parser.parse (/app/node_modules/pg-protocol/dist/parser.js:35:38)\n at Socket. (/app/node_modules/pg-protocol/dist/index.js:11:42)\n at Socket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)\n at TCP.onStreamRead (node:internal/stream_base_commons:191:23)","timestamp":"2025-04-21T14:47:41.049Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:47:50.599Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:50.616Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:47:50.640Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:50.644Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:47:50.675Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:50.685Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:47:50.701Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:50.706Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:47:50.722Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:50.725Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:47:50.737Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:50.744Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:47:50.752Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:47:50.789Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:47:50.793Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:47:50.818Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:47:51.613Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:51.615Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:47:51.630Z"} +{"level":"info","message":"GET /api/chat/history?offset=36&limit=30","timestamp":"2025-04-21T14:47:51.651Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:47:59.736Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:59.738Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:47:59.753Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:59.755Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:47:59.778Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:59.780Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:47:59.793Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:59.795Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:47:59.811Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:59.814Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:47:59.838Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:47:59.841Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:47:59.849Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:47:59.866Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:47:59.878Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:47:59.895Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:48:00.729Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:48:00.731Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:48:00.745Z"} +{"level":"info","message":"GET /api/chat/history?offset=36&limit=30","timestamp":"2025-04-21T14:48:00.762Z"} +{"level":"info","message":"POST /api/auth/email/init","timestamp":"2025-04-21T14:48:20.774Z"} +{"level":"info","message":"[initEmailAuth] Using existing authenticated user 1 for email hb3accelerator@gmail.com","timestamp":"2025-04-21T14:48:20.780Z"} +{"level":"info","message":"Generated verification code: TYVZAH","timestamp":"2025-04-21T14:48:20.781Z"} +{"level":"info","message":"Creating verification code for email:hb3accelerator@gmail.com, userId: 1","timestamp":"2025-04-21T14:48:20.781Z"} +{"level":"info","message":"Verification code created successfully for email:hb3accelerator@gmail.com","timestamp":"2025-04-21T14:48:20.789Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error sending verification code: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:48:23.553Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:48:23.554Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:48:23.554Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:48:29.942Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:48:29.945Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:48:29.956Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:48:29.958Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:49:00.057Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:49:00.060Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:49:00.071Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:49:00.073Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:49:30.195Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:49:30.198Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:49:30.207Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:49:30.209Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:49:47.048Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:49:47.052Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:49:47.074Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:49:47.076Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:49:47.092Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:49:47.094Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:49:47.118Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:49:47.142Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:49:47.169Z"} +{"level":"info","message":"GET /api/chat/history?offset=36&limit=30","timestamp":"2025-04-21T14:49:47.187Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:49:48.257Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:49:48.260Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:49:48.278Z"} +{"level":"info","message":"GET /api/chat/history?offset=36&limit=30","timestamp":"2025-04-21T14:49:48.299Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:50:00.443Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:50:00.446Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:50:15.384Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:50:15.387Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:50:30.536Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:50:30.539Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:50:45.460Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:50:45.463Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:51:14.633Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:51:14.637Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:51:15.540Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:51:15.541Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:51:45.712Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:51:45.715Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:52:13.860Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:13.864Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:52:21.090Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:21.092Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:52:26.148Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:26.150Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:52:26.170Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:26.172Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:52:26.195Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:26.197Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:52:26.218Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:26.221Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:52:26.238Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:26.240Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:52:26.258Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:26.260Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:52:26.288Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:52:26.307Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:52:26.315Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:52:26.333Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:52:27.158Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:27.160Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:52:27.173Z"} +{"level":"info","message":"GET /api/chat/history?offset=36&limit=30","timestamp":"2025-04-21T14:52:27.192Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:52:41.975Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:41.979Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:52:42.003Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:42.005Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:52:42.034Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:42.036Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:52:42.054Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:42.056Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:52:42.073Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:42.076Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:52:42.091Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:42.094Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:52:42.131Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:52:42.156Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:52:42.164Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:52:42.189Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:52:42.977Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:52:42.978Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:52:42.994Z"} +{"level":"info","message":"GET /api/chat/history?offset=36&limit=30","timestamp":"2025-04-21T14:52:43.016Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:53:11.492Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:53:11.496Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:53:12.411Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:53:12.413Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:53:41.670Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:53:41.673Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:53:42.585Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:53:42.587Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:54:11.793Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:54:11.795Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:54:12.712Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:54:12.714Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:54:42.849Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:54:42.852Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:54:42.863Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:54:42.870Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:55:12.054Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:55:12.057Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:55:15.772Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:55:15.774Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:56:16.054Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:56:16.058Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:56:16.070Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:56:16.073Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:57:16.339Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:57:16.342Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:57:16.354Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:57:16.356Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:58:00.474Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:58:00.479Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:58:00.489Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:58:00.492Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:58:11.657Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:58:11.659Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:58:11.669Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:58:11.671Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:58:28.544Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:58:28.564Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:58:28.585Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:58:28.590Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:58:28.622Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:58:28.628Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:58:28.652Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:58:28.655Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:58:28.670Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:58:28.672Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:58:28.685Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:58:28.692Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:58:28.700Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:58:28.728Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:58:28.732Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:58:28.753Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:58:29.559Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:58:29.561Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:58:29.575Z"} +{"level":"info","message":"GET /api/chat/history?offset=36&limit=30","timestamp":"2025-04-21T14:58:29.594Z"} +{"level":"info","message":"POST /api/auth/email/init","timestamp":"2025-04-21T14:58:45.743Z"} +{"level":"info","message":"[initEmailAuth] Using existing authenticated user 1 for email hb3accelerator@gmail.com","timestamp":"2025-04-21T14:58:45.748Z"} +{"level":"info","message":"Generated verification code: 2G8B80","timestamp":"2025-04-21T14:58:45.749Z"} +{"level":"info","message":"Creating verification code for email:hb3accelerator@gmail.com, userId: 1","timestamp":"2025-04-21T14:58:45.749Z"} +{"level":"info","message":"Verification code created successfully for email:hb3accelerator@gmail.com","timestamp":"2025-04-21T14:58:45.754Z"} +{"level":"info","message":"Verification code sent to hb3accelerator@gmail.com","timestamp":"2025-04-21T14:58:46.694Z"} +{"level":"info","message":"Generated verification code for Email auth for hb3accelerator@gmail.com and sent to user's email","timestamp":"2025-04-21T14:58:46.695Z"} +{"level":"info","message":"Session saved successfully","timestamp":"2025-04-21T14:58:46.699Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:58:58.901Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:58:58.905Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:58:58.916Z"} +{"level":"info","message":"[IdentityService] Found 2 identities for user 1","timestamp":"2025-04-21T14:58:58.918Z"} +{"level":"info","message":"POST /api/auth/email/verify-code","timestamp":"2025-04-21T14:59:07.050Z"} +{"level":"info","message":"Verifying code for email:hb3accelerator@gmail.com","timestamp":"2025-04-21T14:59:07.051Z"} +{"level":"info","message":"Normalized code: 2G8B80","timestamp":"2025-04-21T14:59:07.051Z"} +{"level":"info","message":"Found codes for email:hb3accelerator@gmail.com: [\"DPO9NP\",\"TYVZAH\",\"2G8B80\"]","timestamp":"2025-04-21T14:59:07.056Z"} +{"level":"info","message":"Code verified successfully for email:hb3accelerator@gmail.com","timestamp":"2025-04-21T14:59:07.060Z"} +{"level":"info","message":"[email/verify-code] Linking email hb3accelerator@gmail.com to existing authenticated user 1","timestamp":"2025-04-21T14:59:07.060Z"} +{"level":"info","message":"[AuthService] Linking identity email:hb3accelerator@gmail.com to user 1","timestamp":"2025-04-21T14:59:07.061Z"} +{"level":"info","message":"[AuthService] Identity email:hb3accelerator@gmail.com successfully linked to user 1","timestamp":"2025-04-21T14:59:07.072Z"} +{"level":"info","message":"Session saved successfully","timestamp":"2025-04-21T14:59:07.074Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:59:07.087Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T14:59:07.089Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:59:07.104Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T14:59:07.106Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:59:07.121Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T14:59:07.123Z"} +{"level":"info","message":"POST /api/auth/identities/link","timestamp":"2025-04-21T14:59:07.139Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:59:28.925Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T14:59:28.928Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:59:28.940Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T14:59:28.945Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:59:31.705Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T14:59:31.707Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:59:31.720Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T14:59:31.722Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:59:31.742Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T14:59:31.745Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:59:31.761Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T14:59:31.767Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:59:31.782Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T14:59:31.785Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T14:59:31.798Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T14:59:31.801Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:59:31.829Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:59:31.854Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:59:31.861Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T14:59:31.892Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T14:59:32.694Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T14:59:32.696Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T14:59:32.709Z"} +{"level":"info","message":"GET /api/chat/history?offset=36&limit=30","timestamp":"2025-04-21T14:59:32.728Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:00:01.973Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:00:01.976Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:00:01.986Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:00:01.988Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:00:32.108Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:00:32.111Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:00:32.126Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:00:32.128Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T15:00:48.645Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:00:48.648Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:00:48.668Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:00:48.671Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:00:48.685Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:00:48.687Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T15:00:48.702Z"} +{"level":"info","message":"GET /api/chat/history?offset=36&limit=30","timestamp":"2025-04-21T15:00:48.727Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T15:00:49.852Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:00:49.853Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T15:00:49.867Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T15:00:49.883Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:01:02.246Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:02.249Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T15:01:09.844Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:09.846Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T15:01:09.865Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:09.868Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:01:09.899Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:09.905Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:01:09.926Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:09.930Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:01:09.945Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:09.950Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:01:09.964Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:09.967Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T15:01:10.844Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:10.846Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T15:01:10.861Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T15:01:10.879Z"} +{"level":"info","message":"POST /api/auth/logout","timestamp":"2025-04-21T15:01:14.966Z"} +{"level":"info","message":"Session saved successfully","timestamp":"2025-04-21T15:01:14.970Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T15:01:14.988Z"} +{"level":"info","message":"Session saved successfully","timestamp":"2025-04-21T15:01:14.991Z"} +{"level":"info","message":"POST /api/auth/telegram/init","timestamp":"2025-04-21T15:01:17.266Z"} +{"level":"info","message":"Generated verification code: 077C49","timestamp":"2025-04-21T15:01:17.267Z"} +{"level":"info","message":"Creating verification code for telegram:8890d8475daa4b9515427d7db88cd3f6, userId: null","timestamp":"2025-04-21T15:01:17.267Z"} +{"level":"info","message":"Verification code created successfully for telegram:8890d8475daa4b9515427d7db88cd3f6","timestamp":"2025-04-21T15:01:17.270Z"} +{"level":"info","message":"[initTelegramAuth] Created verification code for guestId: 8890d8475daa4b9515427d7db88cd3f6","timestamp":"2025-04-21T15:01:17.271Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T15:01:19.151Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T15:01:21.860Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T15:01:23.730Z"} +{"level":"info","message":"Starting Telegram auth process for code:","timestamp":"2025-04-21T15:01:25.195Z"} +{"level":"info","message":"Using existing user 1 for Telegram account 5155951987","timestamp":"2025-04-21T15:01:25.196Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T15:01:25.611Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:25.613Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:01:25.630Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:25.632Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:01:25.646Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:25.648Z"} +{"level":"info","message":"GET /api/auth/check","timestamp":"2025-04-21T15:01:27.473Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:27.474Z"} +{"level":"info","message":"GET /api/chat/history?count_only=true","timestamp":"2025-04-21T15:01:27.487Z"} +{"level":"info","message":"GET /api/chat/history?offset=6&limit=30","timestamp":"2025-04-21T15:01:27.585Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:01:40.114Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:40.116Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:01:56.741Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:01:56.744Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:02:10.937Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:02:10.941Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:02:26.784Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:02:26.787Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:02:41.062Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:02:41.065Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:02:56.976Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:02:56.979Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:03:11.193Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:03:11.196Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:03:27.119Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:03:27.122Z"} +{"level":"info","message":"GET /api/auth/identities","timestamp":"2025-04-21T15:03:41.328Z"} +{"level":"info","message":"[IdentityService] Found 3 identities for user 1","timestamp":"2025-04-21T15:03:41.331Z"} diff --git a/backend/logs/error.log b/backend/logs/error.log index 9e8c2d2..76df9f8 100644 --- a/backend/logs/error.log +++ b/backend/logs/error.log @@ -37,3 +37,16 @@ {"level":"error","message":"Provider for polygon is not available: Network check timeout","timestamp":"2025-04-21T12:36:03.317Z"} {"level":"error","message":"Provider for bsc is not available: Network check timeout","timestamp":"2025-04-21T13:48:41.033Z"} {"level":"error","message":"Provider for polygon is not available: Network check timeout","timestamp":"2025-04-21T13:48:48.026Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error sending verification code: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:42:55.058Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:42:55.058Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:42:55.059Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error sending verification code: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:43:53.439Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:43:53.439Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:43:53.440Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error sending verification code: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:45:43.536Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:45:43.537Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:45:43.537Z"} +{"client":{"_connected":true,"_connecting":false,"_connectionCallback":null,"_connectionError":false,"_connectionTimeoutMillis":0,"_ended":false,"_ending":true,"_events":{},"_eventsCount":1,"_poolUseCount":4,"_queryable":false,"_types":{"_types":{"arrayParser":{},"builtins":{"ABSTIME":702,"ACLITEM":1033,"BIT":1560,"BOOL":16,"BPCHAR":1042,"BYTEA":17,"CHAR":18,"CID":29,"CIDR":650,"CIRCLE":718,"DATE":1082,"FLOAT4":700,"FLOAT8":701,"GTSVECTOR":3642,"INET":869,"INT2":21,"INT4":23,"INT8":20,"INTERVAL":1186,"JSON":114,"JSONB":3802,"MACADDR":829,"MACADDR8":774,"MONEY":790,"NUMERIC":1700,"OID":26,"PATH":602,"PG_DEPENDENCIES":3402,"PG_LSN":3220,"PG_NDISTINCT":3361,"PG_NODE_TREE":194,"POLYGON":604,"REFCURSOR":1790,"REGCLASS":2205,"REGCONFIG":3734,"REGDICTIONARY":3769,"REGNAMESPACE":4089,"REGOPER":2203,"REGOPERATOR":2204,"REGPROC":24,"REGPROCEDURE":2202,"REGROLE":4096,"REGTYPE":2206,"RELTIME":703,"SMGR":210,"TEXT":25,"TID":27,"TIME":1083,"TIMESTAMP":1114,"TIMESTAMPTZ":1184,"TIMETZ":1266,"TINTERVAL":704,"TSQUERY":3615,"TSVECTOR":3614,"TXID_SNAPSHOT":2970,"UUID":2950,"VARBIT":1562,"VARCHAR":1043,"XID":28,"XML":142}},"binary":{},"text":{}},"activeQuery":null,"binary":false,"connection":{"_connecting":true,"_emitMessage":false,"_ending":true,"_events":{"end":[null,null]},"_eventsCount":23,"_keepAlive":false,"_keepAliveInitialDelayMillis":0,"lastBuffer":false,"parsedStatements":{},"ssl":false,"stream":{"_closeAfterHandlingError":false,"_events":{"end":[null,null]},"_eventsCount":4,"_hadError":false,"_host":"postgres","_parent":null,"_pendingData":null,"_pendingEncoding":"","_readableState":{"awaitDrainWriters":null,"buffer":[],"bufferIndex":0,"highWaterMark":16384,"length":0,"pipes":[]},"_server":null,"_sockname":null,"_writableState":{"bufferedIndex":0,"corked":0,"highWaterMark":16384,"length":0,"pendingcb":0,"writelen":0},"allowHalfOpen":false,"connecting":false,"server":null}},"connectionParameters":{"binary":false,"client_encoding":"","connect_timeout":0,"database":"dapp_db","host":"postgres","idle_in_transaction_session_timeout":false,"isDomainSocket":false,"lock_timeout":false,"port":5432,"query_timeout":false,"ssl":false,"statement_timeout":false,"user":"dapp_user"},"database":"dapp_db","hasExecuted":true,"host":"postgres","port":5432,"processID":32813,"queryQueue":[],"readyForQuery":true,"saslSession":null,"secretKey":-471726342,"ssl":false,"user":"dapp_user"},"code":"57P01","file":"postgres.c","length":116,"level":"error","line":"3286","message":"Uncaught Exception: terminating connection due to administrator command","name":"error","routine":"ProcessInterrupts","severity":"FATAL","stack":"error: terminating connection due to administrator command\n at Parser.parseErrorMessage (/app/node_modules/pg-protocol/dist/parser.js:283:98)\n at Parser.handlePacket (/app/node_modules/pg-protocol/dist/parser.js:122:29)\n at Parser.parse (/app/node_modules/pg-protocol/dist/parser.js:35:38)\n at Socket. (/app/node_modules/pg-protocol/dist/index.js:11:42)\n at Socket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)\n at TCP.onStreamRead (node:internal/stream_base_commons:191:23)","timestamp":"2025-04-21T14:47:41.049Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error sending verification code: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:48:23.553Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:48:23.554Z"} +{"code":"EAUTH","command":"AUTH PLAIN","level":"error","message":"Error in email auth initialization: Invalid login: 535 5.7.8 Error: authentication failed: ","response":"535 5.7.8 Error: authentication failed: ","responseCode":535,"stack":"Error: Invalid login: 535 5.7.8 Error: authentication failed: \n at SMTPConnection._formatError (/app/node_modules/nodemailer/lib/smtp-connection/index.js:809:19)\n at SMTPConnection._actionAUTHComplete (/app/node_modules/nodemailer/lib/smtp-connection/index.js:1588:34)\n at SMTPConnection. (/app/node_modules/nodemailer/lib/smtp-connection/index.js:556:26)\n at SMTPConnection._processResponse (/app/node_modules/nodemailer/lib/smtp-connection/index.js:993:20)\n at SMTPConnection._onData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:774:14)\n at SMTPConnection._onSocketData (/app/node_modules/nodemailer/lib/smtp-connection/index.js:195:44)\n at TLSSocket.emit (node:events:524:28)\n at addChunk (node:internal/streams/readable:561:12)\n at readableAddChunkPushByteMode (node:internal/streams/readable:512:3)\n at Readable.push (node:internal/streams/readable:392:5)","timestamp":"2025-04-21T14:48:23.554Z"} diff --git a/backend/middleware/auth.js b/backend/middleware/auth.js index 2cff23a..bf8df57 100644 --- a/backend/middleware/auth.js +++ b/backend/middleware/auth.js @@ -13,19 +13,19 @@ const requireAuth = async (req, res, next) => { console.log('Session in requireAuth:', { id: req.sessionID, userId: req.session?.userId, - authenticated: req.session?.authenticated + authenticated: req.session?.authenticated, }); // Проверяем сессию if (req.session?.authenticated && req.session?.userId) { // Обновляем время жизни сессии req.session.touch(); - + req.user = { userId: req.session.userId, address: req.session.address, isAdmin: req.session.isAdmin, - authType: req.session.authType + authType: req.session.authType, }; return next(); } @@ -34,19 +34,22 @@ const requireAuth = async (req, res, next) => { const authHeader = req.headers.authorization; if (authHeader?.startsWith('Bearer ')) { const address = authHeader.split(' ')[1]; - + if (address.startsWith('0x')) { - const result = await db.query(` + const result = await db.query( + ` SELECT u.id, u.is_admin FROM users u JOIN user_identities ui ON u.id = ui.user_id WHERE ui.identity_type = 'wallet' AND LOWER(ui.identity_value) = LOWER($1) - `, [address]); + `, + [address] + ); if (result.rows.length > 0) { const user = result.rows[0]; - + // Создаем новую сессию req.session.regenerate(async (err) => { if (err) { @@ -68,7 +71,7 @@ const requireAuth = async (req, res, next) => { userId: user.id, address: address, isAdmin: user.is_admin, - authType: 'wallet' + authType: 'wallet', }; next(); }); @@ -111,7 +114,9 @@ async function requireAdmin(req, res, next) { // Проверка через ID пользователя if (req.session.userId) { - const userResult = await db.query('SELECT role FROM users WHERE id = $1', [req.session.userId]); + const userResult = await db.query('SELECT role FROM users WHERE id = $1', [ + req.session.userId, + ]); if (userResult.rows.length > 0 && userResult.rows[0].role === USER_ROLES.ADMIN) { // Обновляем сессию req.session.isAdmin = true; @@ -146,7 +151,9 @@ function requireRole(role) { // Проверка через ID пользователя if (req.session.userId) { - const userResult = await db.query('SELECT role FROM users WHERE id = $1', [req.session.userId]); + const userResult = await db.query('SELECT role FROM users WHERE id = $1', [ + req.session.userId, + ]); if (userResult.rows.length > 0 && userResult.rows[0].role === role) { return next(); } @@ -192,5 +199,5 @@ module.exports = { requireAuth, requireAdmin, requireRole, - checkRole + checkRole, }; diff --git a/backend/middleware/errorHandler.js b/backend/middleware/errorHandler.js index 74efd18..a031332 100644 --- a/backend/middleware/errorHandler.js +++ b/backend/middleware/errorHandler.js @@ -11,7 +11,7 @@ function errorHandler(err, req, res, next) { url: req.originalUrl, method: req.method, ip: req.ip, - userId: req.session?.userId + userId: req.session?.userId, }); // Определяем тип ошибки @@ -39,17 +39,15 @@ function errorHandler(err, req, res, next) { } // В режиме разработки возвращаем стек ошибки - const devError = process.env.NODE_ENV === 'development' - ? { stack: err.stack } - : {}; + const devError = process.env.NODE_ENV === 'development' ? { stack: err.stack } : {}; // Отправляем ответ клиенту res.status(statusCode).json({ error: { code: errorCode, message: errorMessage, - ...devError - } + ...devError, + }, }); } @@ -67,5 +65,5 @@ function createError(message, status) { module.exports = { errorHandler, - createError -}; \ No newline at end of file + createError, +}; diff --git a/backend/middleware/logger.js b/backend/middleware/logger.js index 5c99e33..a5184fd 100644 --- a/backend/middleware/logger.js +++ b/backend/middleware/logger.js @@ -11,4 +11,4 @@ const requestLogger = (req, res, next) => { next(); }; -module.exports = requestLogger; \ No newline at end of file +module.exports = requestLogger; diff --git a/backend/routes/admin.js b/backend/routes/admin.js index a668aca..434a483 100644 --- a/backend/routes/admin.js +++ b/backend/routes/admin.js @@ -1,5 +1,6 @@ const express = require('express'); const router = express.Router(); +const db = require('../db'); const { requireAdmin } = require('../middleware/auth'); const authService = require('../services/auth-service'); const logger = require('../utils/logger'); diff --git a/backend/routes/auth.js b/backend/routes/auth.js index d22b7ba..312b814 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -35,29 +35,28 @@ router.get('/nonce', async (req, res) => { // Генерируем случайный nonce const nonce = crypto.randomBytes(16).toString('hex'); - + // Проверяем, существует ли уже nonce для этого адреса - const existingNonce = await db.query( - 'SELECT id FROM nonces WHERE identity_value = $1', - [address.toLowerCase()] - ); - + const existingNonce = await db.query('SELECT id FROM nonces WHERE identity_value = $1', [ + address.toLowerCase(), + ]); + if (existingNonce.rows.length > 0) { // Обновляем существующий nonce await db.query( - 'UPDATE nonces SET nonce = $1, expires_at = NOW() + INTERVAL \'15 minutes\' WHERE identity_value = $2', + "UPDATE nonces SET nonce = $1, expires_at = NOW() + INTERVAL '15 minutes' WHERE identity_value = $2", [nonce, address.toLowerCase()] ); } else { // Создаем новый nonce await db.query( - 'INSERT INTO nonces (identity_value, nonce, expires_at) VALUES ($1, $2, NOW() + INTERVAL \'15 minutes\')', + "INSERT INTO nonces (identity_value, nonce, expires_at) VALUES ($1, $2, NOW() + INTERVAL '15 minutes')", [address.toLowerCase(), nonce] ); } - + logger.info(`Nonce ${nonce} сохранен для адреса ${address}`); - + res.json({ nonce }); } catch (error) { logger.error('Error generating nonce:', error); @@ -69,53 +68,58 @@ router.get('/nonce', async (req, res) => { router.post('/verify', async (req, res) => { try { const { address, message, signature } = req.body; - + logger.info(`[verify] Verifying signature for address: ${address}`); - + // Сохраняем гостевые ID до проверки const guestId = req.session.guestId; const previousGuestId = req.session.previousGuestId; - + // Проверяем подпись const isValid = await authService.verifySignature(message, signature, address); if (!isValid) { return res.status(401).json({ success: false, error: 'Invalid signature' }); } - + // Нормализуем адрес для использования в запросах const normalizedAddress = ethers.getAddress(address).toLowerCase(); - + // Проверяем nonce - const nonceResult = await db.query('SELECT nonce FROM nonces WHERE identity_value = $1', [normalizedAddress]); - if (nonceResult.rows.length === 0 || nonceResult.rows[0].nonce !== message.match(/Nonce: ([^\n]+)/)[1]) { + const nonceResult = await db.query('SELECT nonce FROM nonces WHERE identity_value = $1', [ + normalizedAddress, + ]); + if ( + nonceResult.rows.length === 0 || + nonceResult.rows[0].nonce !== message.match(/Nonce: ([^\n]+)/)[1] + ) { return res.status(401).json({ success: false, error: 'Invalid nonce' }); } - + let userId; let isAdmin = false; - + // Проверяем, авторизован ли пользователь уже if (req.session.authenticated && req.session.userId) { // Если пользователь уже авторизован, привязываем кошелек к существующему пользователю userId = req.session.userId; - logger.info(`[verify] Using existing authenticated user ${userId} for wallet ${normalizedAddress}`); - - // Связываем кошелек с пользователем через identity-service для предотвращения дубликатов - const linkResult = await authService.linkIdentity( - userId, - 'wallet', - address + logger.info( + `[verify] Using existing authenticated user ${userId} for wallet ${normalizedAddress}` ); - + + // Связываем кошелек с пользователем через identity-service для предотвращения дубликатов + const linkResult = await authService.linkIdentity(userId, 'wallet', address); + if (!linkResult.success && linkResult.error) { return res.status(400).json({ success: false, - error: linkResult.error + error: linkResult.error, }); } - + // Если linkResult.message содержит 'already exists', значит кошелек уже привязан - logger.info(`[verify] Wallet ${normalizedAddress} linked to user ${userId}: ${linkResult.message || 'success'}`); + logger.info( + `[verify] Wallet ${normalizedAddress} linked to user ${userId}: ${linkResult.message || 'success'}` + ); } else { // Находим или создаем пользователя, если не авторизован const result = await authService.findOrCreateUser(address); @@ -123,49 +127,48 @@ router.post('/verify', async (req, res) => { isAdmin = result.isAdmin; logger.info(`[verify] Found or created user ${userId} for wallet ${normalizedAddress}`); } - + // Сохраняем идентификаторы гостевой сессии if (guestId) { await identityService.saveIdentity(userId, 'guest', guestId, true); } - + if (previousGuestId && previousGuestId !== guestId) { await identityService.saveIdentity(userId, 'guest', previousGuestId, true); } - + // Проверяем наличие админских токенов const adminStatus = await authService.checkAdminTokens(normalizedAddress); - + if (adminStatus) { await db.query('UPDATE users SET role = $1 WHERE id = $2', ['admin', userId]); isAdmin = true; } - + // Обновляем сессию req.session.userId = userId; req.session.authenticated = true; req.session.authType = 'wallet'; req.session.isAdmin = adminStatus || isAdmin; req.session.address = normalizedAddress; // Всегда сохраняем нормализованный адрес - + // Удаляем временный ID delete req.session.tempUserId; - + // Сохраняем сессию await sessionService.saveSession(req.session); - + // Связываем гостевые сообщения с пользователем await sessionService.linkGuestMessages(req.session, userId); - + // Возвращаем успешный ответ return res.json({ success: true, userId, address: normalizedAddress, // Возвращаем нормализованный адрес isAdmin: adminStatus || isAdmin, - authenticated: true + authenticated: true, }); - } catch (error) { logger.error('[verify] Error:', error); res.status(500).json({ success: false, error: 'Server error' }); @@ -176,17 +179,17 @@ router.post('/verify', async (req, res) => { router.post('/telegram/verify', async (req, res) => { try { const { telegramId, verificationCode } = req.body; - + if (!telegramId || !verificationCode) { return res.status(400).json({ success: false, - error: 'Missing required fields' + error: 'Missing required fields', }); } // Сохраняем гостевой ID из текущей сессии const guestId = req.session.guestId; - + // Передаем сессию в метод верификации const verificationResult = await authService.verifyTelegramAuth( telegramId, @@ -197,7 +200,7 @@ router.post('/telegram/verify', async (req, res) => { if (!verificationResult.success) { return res.status(400).json({ success: false, - error: verificationResult.error || 'Verification failed' + error: verificationResult.error || 'Verification failed', }); } @@ -205,9 +208,9 @@ router.post('/telegram/verify', async (req, res) => { req.session.regenerate(async (err) => { if (err) { logger.error('[telegram/verify] Error regenerating session:', err); - return res.status(500).json({ - success: false, - error: 'Session error' + return res.status(500).json({ + success: false, + error: 'Session error', }); } @@ -217,7 +220,7 @@ router.post('/telegram/verify', async (req, res) => { req.session.authType = 'telegram'; req.session.authenticated = true; req.session.role = verificationResult.role; - + // Восстанавливаем гостевой ID, если он был if (guestId) { req.session.guestId = guestId; @@ -236,14 +239,14 @@ router.post('/telegram/verify', async (req, res) => { userId: verificationResult.userId, role: verificationResult.role, telegramId, - isNewUser: verificationResult.isNewUser + isNewUser: verificationResult.isNewUser, }); }); } catch (error) { logger.error('[telegram/verify] Error:', error); return res.status(500).json({ success: false, - error: 'Internal server error' + error: 'Internal server error', }); } }); @@ -252,26 +255,26 @@ router.post('/telegram/verify', async (req, res) => { router.post('/email/request', authLimiter, async (req, res) => { try { const { email } = req.body; - + if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) { return res.status(400).json({ error: 'Invalid email format' }); } - + // Инициализация email аутентификации const result = await emailAuth.initEmailAuth(req.session, email); - + // Сохраняем сессию после установки pendingEmail await sessionService.saveSession(req.session); - + if (result.success) { - res.json({ - success: true, - message: 'Код подтверждения отправлен на email' + res.json({ + success: true, + message: 'Код подтверждения отправлен на email', }); } else { - res.status(500).json({ - success: false, - error: result.error || 'Ошибка отправки кода' + res.status(500).json({ + success: false, + error: result.error || 'Ошибка отправки кода', }); } } catch (error) { @@ -284,150 +287,155 @@ router.post('/email/request', authLimiter, async (req, res) => { router.post('/email/verify-code', async (req, res) => { try { const { code, email } = req.body; - + if (!code) { - return res.status(400).json({ - success: false, - error: 'Код подтверждения обязателен' + return res.status(400).json({ + success: false, + error: 'Код подтверждения обязателен', }); } - + // Если email передан в запросе, сохраняем его в сессии if (email && !req.session.pendingEmail) { req.session.pendingEmail = email.toLowerCase(); } - + if (!req.session.pendingEmail) { return res.status(400).json({ success: false, - error: 'Email не найден в сессии. Пожалуйста, запросите код подтверждения снова.' + error: 'Email не найден в сессии. Пожалуйста, запросите код подтверждения снова.', }); } - + // Сохраняем гостевой ID до проверки const guestId = req.session.guestId; const previousGuestId = req.session.previousGuestId; - + // Проверяем код через сервис верификации const verificationResult = await verificationService.verifyCode( - code, - 'email', + code, + 'email', req.session.pendingEmail ); - + if (!verificationResult.success) { return res.status(400).json({ success: false, - error: verificationResult.error || 'Неверный код подтверждения' + error: verificationResult.error || 'Неверный код подтверждения', }); } - + // Получаем или создаем пользователя let userId; let isNewAuth = false; - + // Проверяем, авторизован ли пользователь if (req.session.authenticated && req.session.userId) { // Связываем email с существующим пользователем userId = req.session.userId; - logger.info(`[email/verify-code] Linking email ${req.session.pendingEmail} to existing authenticated user ${userId}`); - - // Связываем email с текущим аккаунтом - const linkResult = await authService.linkIdentity( - userId, - 'email', - req.session.pendingEmail + logger.info( + `[email/verify-code] Linking email ${req.session.pendingEmail} to existing authenticated user ${userId}` ); - + + // Связываем email с текущим аккаунтом + const linkResult = await authService.linkIdentity(userId, 'email', req.session.pendingEmail); + // Сохраняем email в сессии req.session.email = req.session.pendingEmail; - + // Удаляем временные данные delete req.session.pendingEmail; - + // Сохраняем сессию await sessionService.saveSession(req.session); - - return res.json({ + + return res.json({ success: true, userId, email: req.session.email, authenticated: true, - linked: true + linked: true, }); } else { // Если пользователь не авторизован, ищем существующего пользователя или создаем нового - + // Ищем существующего пользователя по email const existingUser = await identityService.findUserByIdentity( - 'email', + 'email', req.session.pendingEmail ); - + if (existingUser) { // Используем существующего пользователя userId = existingUser.id; - logger.info(`[email/verify-code] Using existing user ${userId} with email ${req.session.pendingEmail}`); + logger.info( + `[email/verify-code] Using existing user ${userId} with email ${req.session.pendingEmail}` + ); } else if (req.session.userId) { // Используем текущего пользователя userId = req.session.userId; - logger.info(`[email/verify-code] Using current user ${userId} for email ${req.session.pendingEmail}`); + logger.info( + `[email/verify-code] Using current user ${userId} for email ${req.session.pendingEmail}` + ); } else if (req.session.tempUserId) { // Используем временного пользователя userId = req.session.tempUserId; - logger.info(`[email/verify-code] Using temporary user ${userId} for email ${req.session.pendingEmail}`); + logger.info( + `[email/verify-code] Using temporary user ${userId} for email ${req.session.pendingEmail}` + ); } else { // Создаем нового пользователя - const newUser = await db.query( - 'INSERT INTO users (role) VALUES ($1) RETURNING id', - ['user'] - ); + const newUser = await db.query('INSERT INTO users (role) VALUES ($1) RETURNING id', [ + 'user', + ]); userId = newUser.rows[0].id; isNewAuth = true; - logger.info(`[email/verify-code] Created new user ${userId} for email ${req.session.pendingEmail}`); + logger.info( + `[email/verify-code] Created new user ${userId} for email ${req.session.pendingEmail}` + ); } - + // Сохраняем email как идентификатор await identityService.saveIdentity(userId, 'email', req.session.pendingEmail, true); - + // Сохраняем гостевые идентификаторы if (guestId) { await identityService.saveIdentity(userId, 'guest', guestId, true); } - + if (previousGuestId && previousGuestId !== guestId) { await identityService.saveIdentity(userId, 'guest', previousGuestId, true); } - + // Устанавливаем сессию req.session.userId = userId; req.session.authenticated = true; req.session.authType = 'email'; req.session.email = req.session.pendingEmail; - + // Удаляем временные данные delete req.session.tempUserId; delete req.session.pendingEmail; - + // Сохраняем сессию await sessionService.saveSession(req.session); - + // Связываем гостевые сообщения await sessionService.linkGuestMessages(req.session, userId); - - return res.json({ + + return res.json({ success: true, userId, email: req.session.email, authenticated: true, - isNewAuth + isNewAuth, }); } } catch (error) { logger.error('[email/verify-code] Error:', error); - return res.status(500).json({ - success: false, - error: 'Ошибка сервера' + return res.status(500).json({ + success: false, + error: 'Ошибка сервера', }); } }); @@ -436,29 +444,29 @@ router.post('/email/verify-code', async (req, res) => { router.post('/telegram/init', async (req, res) => { try { const { verificationCode, botLink } = await initTelegramAuth(req.session); - + if (!verificationCode || !botLink) { throw new Error('Failed to generate verification code'); } - - res.json({ + + res.json({ success: true, - verificationCode, - botLink + verificationCode, + botLink, }); } catch (error) { logger.error('Error initializing Telegram auth:', error); - + if (error.message === 'Telegram уже привязан к этому аккаунту') { - return res.status(400).json({ + return res.status(400).json({ success: false, - error: error.message + error: error.message, }); } - - res.status(500).json({ + + res.status(500).json({ success: false, - error: 'Failed to initialize Telegram auth' + error: 'Failed to initialize Telegram auth', }); } }); @@ -467,29 +475,29 @@ router.post('/telegram/init', async (req, res) => { router.post('/email/init', async (req, res) => { try { const { email } = req.body; - + if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) { return res.status(400).json({ success: false, - error: 'Некорректный формат email' + error: 'Некорректный формат email', }); } - + // Инициализация email аутентификации const result = await emailAuth.initEmailAuth(req.session, email); - + // Сохраняем сессию await sessionService.saveSession(req.session); - + return res.json({ success: true, - message: 'Код верификации отправлен на email' + message: 'Код верификации отправлен на email', }); } catch (error) { logger.error('Error in email auth initialization:', error); res.status(500).json({ success: false, - error: 'Внутренняя ошибка сервера' + error: 'Внутренняя ошибка сервера', }); } }); @@ -499,21 +507,20 @@ router.get('/check', async (req, res) => { try { const authenticated = req.session.authenticated || false; const authType = req.session.authType || null; - + let identities = []; let isAdmin = false; - + if (authenticated && req.session.userId) { // Если пользователь аутентифицирован, получаем его идентификаторы из БД try { identities = await identityService.getUserIdentities(req.session.userId); - + // Проверяем роль пользователя - const roleResult = await db.query( - 'SELECT role FROM users WHERE id = $1', - [req.session.userId] - ); - + const roleResult = await db.query('SELECT role FROM users WHERE id = $1', [ + req.session.userId, + ]); + if (roleResult.rows.length > 0) { isAdmin = roleResult.rows[0].role === 'admin'; req.session.isAdmin = isAdmin; @@ -522,15 +529,15 @@ router.get('/check', async (req, res) => { logger.error(`[session/check] Error fetching identities: ${error.message}`); } } - + // Проверяем, нужно ли создать новый гостевой ID if (!authenticated && !req.session.guestId) { req.session.guestId = crypto.randomBytes(16).toString('hex'); - + // Сохраняем сессию с новым гостевым ID await sessionService.saveSession(req.session); } - + // Формируем ответ const response = { success: true, @@ -539,9 +546,9 @@ router.get('/check', async (req, res) => { guestId: req.session.guestId || null, authType, identitiesCount: identities.length, - isAdmin: isAdmin || false + isAdmin: isAdmin || false, }; - + // Добавляем специфические поля в зависимости от типа аутентификации if (authType === 'wallet') { response.address = req.session.address || null; @@ -556,13 +563,13 @@ router.get('/check', async (req, res) => { response.telegramFirstName = req.session.telegramFirstName; } } - + return res.json(response); } catch (error) { logger.error('[session/check] Error:', error); return res.status(500).json({ success: false, - error: 'Internal server error' + error: 'Internal server error', }); } }); @@ -609,7 +616,7 @@ router.get('/check-access', requireAuth, async (req, res) => { if (address) { const isAdmin = await authService.checkAdminTokens(address); - + // Обновляем сессию req.session.isAdmin = isAdmin; await sessionService.saveSession(req.session); @@ -618,7 +625,7 @@ router.get('/check-access', requireAuth, async (req, res) => { success: true, isAdmin, userId, - address + address, }); } @@ -626,9 +633,8 @@ router.get('/check-access', requireAuth, async (req, res) => { success: true, isAdmin: false, userId, - address: null + address: null, }); - } catch (error) { logger.error('Error checking access:', error); return res.status(500).json({ error: 'Internal server error' }); @@ -639,22 +645,22 @@ router.get('/check-access', requireAuth, async (req, res) => { router.post('/refresh-session', async (req, res) => { try { const { address } = req.body; - + if (req.session && req.session.authenticated) { logger.info('Обновление сессии для пользователя:', req.session.userId); - + // Обновляем время жизни сессии req.session.cookie.maxAge = 30 * 24 * 60 * 60 * 1000; // 30 дней - + // Сохраняем обновленную сессию await sessionService.saveSession(req.session); - + return res.json({ success: true }); } else if (address) { // Если сессия не аутентифицирована, но есть адрес try { const user = await identityService.findUserByIdentity('wallet', address.toLowerCase()); - + if (user) { // Обновляем сессию req.session.authenticated = true; @@ -662,17 +668,17 @@ router.post('/refresh-session', async (req, res) => { req.session.address = address.toLowerCase(); req.session.isAdmin = user.role === 'admin'; req.session.authType = 'wallet'; - + // Сохраняем обновленную сессию await sessionService.saveSession(req.session); - + return res.json({ success: true }); } } catch (error) { logger.error('Ошибка при проверке пользователя:', error); } } - + // Если не удалось обновить сессию, возвращаем успех=false, но не ошибку return res.json({ success: false }); } catch (error) { @@ -685,82 +691,78 @@ router.post('/refresh-session', async (req, res) => { router.post('/wallet', async (req, res) => { try { const { address, nonce, signature } = req.body; - + if (!address || !nonce || !signature) { - return res.status(400).json({ - success: false, - error: 'Missing required fields' + return res.status(400).json({ + success: false, + error: 'Missing required fields', }); } - + // Сохраняем гостевые ID до аутентификации const guestId = req.session.guestId; const previousGuestId = req.session.previousGuestId; // Формируем сообщение для проверки const message = `Sign this message to authenticate with HB3 DApp: ${nonce}`; - + // Проверяем подпись const validSignature = await authService.verifySignature(message, signature, address); if (!validSignature) { - return res.status(401).json({ - success: false, - error: 'Invalid signature' + return res.status(401).json({ + success: false, + error: 'Invalid signature', }); } - + // Получаем или создаем пользователя const { userId } = await authService.findOrCreateUser(address); - + // Проверяем наличие админских токенов const isAdmin = await authService.checkAdminTokens(address); - + // Обновляем роль пользователя в базе данных, если нужно if (isAdmin) { - await db.query( - 'UPDATE users SET role = $1 WHERE id = $2', - ['admin', userId] - ); + await db.query('UPDATE users SET role = $1 WHERE id = $2', ['admin', userId]); } - + // Сохраняем идентификаторы await identityService.saveIdentity(userId, 'wallet', address.toLowerCase(), true); - + if (guestId) { await identityService.saveIdentity(userId, 'guest', guestId, true); } - + if (previousGuestId && previousGuestId !== guestId) { await identityService.saveIdentity(userId, 'guest', previousGuestId, true); } - + // Устанавливаем сессию req.session.userId = userId; req.session.address = address.toLowerCase(); req.session.authType = 'wallet'; req.session.authenticated = true; req.session.isAdmin = isAdmin; - + // Сохраняем сессию await sessionService.saveSession(req.session); - + // Связываем гостевые сообщения с пользователем await sessionService.linkGuestMessages(req.session, userId); - + // Возвращаем успешный ответ return res.json({ success: true, userId, address, isAdmin, - authenticated: true + authenticated: true, }); - } catch (error) { logger.error('[wallet] Error:', error); - res.status(500).json({ - success: false, - error: 'Server error during wallet authentication' + res.status(500).json({ + success: false, + error: 'Server error during wallet authentication', }); } }); @@ -769,19 +771,19 @@ router.post('/wallet', async (req, res) => { router.get('/identities', requireAuth, async (req, res) => { try { const { userId } = req.session; - + // Получаем все идентификаторы пользователя const identities = await identityService.getUserIdentities(userId); - + res.json({ success: true, - identities + identities, }); } catch (error) { logger.error('Error getting user identities:', error); res.status(500).json({ success: false, - error: 'Internal server error' + error: 'Internal server error', }); } }); @@ -794,17 +796,17 @@ router.get('/check-session', async (req, res) => { req.session.guestId = crypto.randomBytes(16).toString('hex'); await sessionService.saveSession(req.session); } - + res.json({ success: true, guestId: req.session.guestId, - isAuthenticated: req.session.authenticated || false + isAuthenticated: req.session.authenticated || false, }); } catch (error) { logger.error('Error checking session:', error); res.status(500).json({ success: false, - error: 'Internal server error' + error: 'Internal server error', }); } }); @@ -813,21 +815,21 @@ router.get('/check-session', async (req, res) => { router.get('/check-tokens/:address', async (req, res) => { try { const { address } = req.params; - + // Получаем балансы токенов на всех сетях const balances = await authService.getTokenBalances(address); - + res.json({ success: true, - balances + balances, }); } catch (error) { logger.error('Error checking token balances:', error); res.status(500).json({ success: false, - error: 'Internal server error' + error: 'Internal server error', }); } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/chat.js b/backend/routes/chat.js index f0a5fd6..b3bc650 100644 --- a/backend/routes/chat.js +++ b/backend/routes/chat.js @@ -12,19 +12,19 @@ const { v4: uuidv4 } = require('uuid'); async function processGuestMessages(userId, guestId) { try { console.log(`Processing guest messages for user ${userId} with guest ID ${guestId}`); - + // Проверяем, обрабатывались ли уже эти сообщения const mappingCheck = await db.query( 'SELECT processed FROM guest_user_mapping WHERE guest_id = $1', [guestId] ); - + // Если сообщения уже обработаны, пропускаем if (mappingCheck.rows.length > 0 && mappingCheck.rows[0].processed) { console.log(`Guest messages for guest ID ${guestId} were already processed.`); return { success: true, message: 'Guest messages already processed' }; } - + // Проверяем наличие mapping записи и создаем если нет if (mappingCheck.rows.length === 0) { await db.query( @@ -33,49 +33,49 @@ async function processGuestMessages(userId, guestId) { ); console.log(`Created mapping for guest ID ${guestId} to user ${userId}`); } - + // Получаем все гостевые сообщения const guestMessagesResult = await db.query( 'SELECT * FROM guest_messages WHERE guest_id = $1 ORDER BY created_at ASC', [guestId] ); - + if (guestMessagesResult.rows.length === 0) { console.log('No guest messages found'); - + // Помечаем как обработанные, даже если сообщений нет - await db.query( - 'UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', - [guestId] - ); - + await db.query('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [ + guestId, + ]); + return { success: true, message: 'No guest messages found' }; } - + const guestMessages = guestMessagesResult.rows; console.log(`Found ${guestMessages.length} guest messages`); - + // Создаем новый диалог для этих сообщений const firstMessage = guestMessages[0]; - const title = firstMessage.content.length > 30 - ? `${firstMessage.content.substring(0, 30)}...` - : firstMessage.content; - + const title = + firstMessage.content.length > 30 + ? `${firstMessage.content.substring(0, 30)}...` + : firstMessage.content; + const newConversationResult = await db.query( 'INSERT INTO conversations (user_id, title) VALUES ($1, $2) RETURNING *', [userId, title] ); - + const conversation = newConversationResult.rows[0]; console.log('Created new conversation for guest messages:', conversation); - + // Отслеживаем успешные сохранения сообщений const savedMessageIds = []; - + // Обрабатываем каждое гостевое сообщение for (const guestMessage of guestMessages) { console.log(`Processing guest message ID ${guestMessage.id}: ${guestMessage.content}`); - + try { // Сохраняем сообщение пользователя const userMessageResult = await db.query( @@ -85,26 +85,26 @@ async function processGuestMessages(userId, guestId) { ($1, $2, $3, $4, $5, $6, $7) RETURNING *`, [ - conversation.id, - guestMessage.content, - 'user', - 'user', + conversation.id, + guestMessage.content, + 'user', + 'user', 'web', guestMessage.created_at, - userId // Добавляем userId в сообщение для прямой связи + userId, // Добавляем userId в сообщение для прямой связи ] ); - + console.log(`Saved user message with ID ${userMessageResult.rows[0].id}`); savedMessageIds.push(guestMessage.id); - + // Получаем ответ от ИИ только для сообщений пользователя (не AI) if (!guestMessage.is_ai) { console.log('Getting AI response for:', guestMessage.content); const language = guestMessage.language || 'auto'; const aiResponse = await aiAssistant.getResponse(guestMessage.content, language); console.log('AI response received:', aiResponse); - + // Сохраняем ответ от ИИ const aiMessageResult = await db.query( `INSERT INTO messages @@ -113,16 +113,16 @@ async function processGuestMessages(userId, guestId) { ($1, $2, $3, $4, $5, $6, $7) RETURNING *`, [ - conversation.id, - aiResponse, - 'assistant', - 'assistant', + conversation.id, + aiResponse, + 'assistant', + 'assistant', 'web', new Date(), - userId // Добавляем userId в сообщение для прямой связи + userId, // Добавляем userId в сообщение для прямой связи ] ); - + console.log(`Saved AI response with ID ${aiMessageResult.rows[0].id}`); } } catch (error) { @@ -130,25 +130,26 @@ async function processGuestMessages(userId, guestId) { // Продолжаем с другими сообщениями в случае ошибки } } - + // Удаляем только успешно обработанные гостевые сообщения if (savedMessageIds.length > 0) { await db.query('DELETE FROM guest_messages WHERE id = ANY($1)', [savedMessageIds]); - console.log(`Deleted ${savedMessageIds.length} processed guest messages for guest ID ${guestId}`); - - // Помечаем гостевой ID как обработанный - await db.query( - 'UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', - [guestId] + console.log( + `Deleted ${savedMessageIds.length} processed guest messages for guest ID ${guestId}` ); + + // Помечаем гостевой ID как обработанный + await db.query('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [ + guestId, + ]); } else { console.log('No guest messages were successfully processed, skipping deletion'); } - - return { - success: true, + + return { + success: true, message: `Processed ${savedMessageIds.length} of ${guestMessages.length} guest messages`, - conversationId: conversation.id + conversationId: conversation.id, }; } catch (error) { console.error('Error processing guest messages:', error); @@ -160,11 +161,11 @@ async function processGuestMessages(userId, guestId) { router.post('/guest-message', async (req, res) => { try { const { content, language, guestId: requestGuestId } = req.body; - + if (!content) { return res.status(400).json({ success: false, error: 'Content is required' }); } - + // Используем гостевой ID из запроса или из сессии, или генерируем новый const guestId = requestGuestId || req.session.guestId || crypto.randomBytes(16).toString('hex'); @@ -182,9 +183,9 @@ router.post('/guest-message', async (req, res) => { console.log('Guest message saved:', result.rows[0]); - res.json({ - success: true, - messageId: result.rows[0].id + res.json({ + success: true, + messageId: result.rows[0].id, }); } catch (error) { console.error('Error saving guest message:', error); @@ -195,43 +196,48 @@ router.post('/guest-message', async (req, res) => { // Маршрут для обычных сообщений (для аутентифицированных пользователей) router.post('/message', requireAuth, async (req, res) => { const { message, conversationId, language = 'auto' } = req.body; - + if (!message) { return res.status(400).json({ error: 'Message is required' }); } try { - console.log('Processing message:', { message, conversationId, language, userId: req.session.userId }); + console.log('Processing message:', { + message, + conversationId, + language, + userId: req.session.userId, + }); const userId = req.session.userId; - + let conversation; - + // Если указан ID диалога, проверяем его существование и принадлежность пользователю if (conversationId) { const conversationResult = await db.query( 'SELECT * FROM conversations WHERE id = $1 AND user_id = $2', [conversationId, userId] ); - + if (conversationResult.rows.length === 0) { return res.status(404).json({ error: 'Conversation not found or access denied' }); } - + conversation = conversationResult.rows[0]; console.log('Using existing conversation:', conversation); } else { // Создаем новый диалог const title = message.length > 30 ? `${message.substring(0, 30)}...` : message; - + const newConversationResult = await db.query( 'INSERT INTO conversations (user_id, title) VALUES ($1, $2) RETURNING *', [userId, title] ); - + conversation = newConversationResult.rows[0]; console.log('Created new conversation:', conversation); } - + // Сохраняем сообщение пользователя console.log('Saving user message'); const userMessageResult = await db.query( @@ -242,12 +248,12 @@ router.post('/message', requireAuth, async (req, res) => { RETURNING *`, [conversation.id, message, 'user', 'user', 0, 'web', new Date()] ); - + // Получаем ответ от ИИ console.log('Getting AI response'); const aiResponse = await aiAssistant.getResponse(message, language); console.log('AI response received:', aiResponse); - + // Сохраняем ответ от ИИ console.log('Saving AI response'); const aiMessageResult = await db.query( @@ -258,14 +264,14 @@ router.post('/message', requireAuth, async (req, res) => { RETURNING *`, [conversation.id, aiResponse, 'assistant', 'assistant', 0, 'web', new Date()] ); - + const response = { success: true, userMessage: userMessageResult.rows[0], aiMessage: aiMessageResult.rows[0], - conversation + conversation, }; - + res.json(response); } catch (error) { console.error('Error processing message:', error); @@ -296,9 +302,9 @@ router.get('/history', async (req, res) => { userId: req.session.userId, address: req.session.address, authenticated: req.session.authenticated, - guestId: req.session.guestId + guestId: req.session.guestId, }); - + const limit = parseInt(req.query.limit) || 50; const offset = parseInt(req.query.offset) || 0; @@ -308,11 +314,11 @@ router.get('/history', async (req, res) => { try { console.log('Automatically linking guest messages before fetching history'); await processGuestMessages(req.session.userId, req.session.guestId); - + // Очищаем guestId из сессии после связывания req.session.guestId = null; await req.session.save(); - + console.log('Guest messages automatically linked'); } catch (linkError) { console.error('Error auto-linking guest messages:', linkError); @@ -349,17 +355,16 @@ router.get('/history', async (req, res) => { LIMIT $2 OFFSET $3`, [req.session.userId, limit, offset] ); - + messages = result.rows; console.log(`Found ${messages.length} messages for authenticated user`); } - + return res.json({ success: true, messages: messages, - total: total + total: total, }); - } catch (error) { logger.error('Error getting chat history:', error); return res.status(500).json({ error: 'Internal server error' }); @@ -368,4 +373,4 @@ router.get('/history', async (req, res) => { // Экспортируем маршрутизатор и функцию processGuestMessages отдельно module.exports = router; -module.exports.processGuestMessages = processGuestMessages; \ No newline at end of file +module.exports.processGuestMessages = processGuestMessages; diff --git a/backend/routes/identities.js b/backend/routes/identities.js index 54d75c7..949debb 100644 --- a/backend/routes/identities.js +++ b/backend/routes/identities.js @@ -26,20 +26,20 @@ router.post('/link', requireAuth, async (req, res) => { // Если тип - wallet, сначала проверим, не привязан ли он уже к другому пользователю if (type === 'wallet') { const normalizedWallet = value.toLowerCase(); - + // Проверяем, существует ли уже такой кошелек const existingCheck = await db.query( `SELECT user_id FROM user_identities WHERE provider = 'wallet' AND provider_id = $1`, [normalizedWallet] ); - + if (existingCheck.rows.length > 0) { const existingUserId = existingCheck.rows[0].user_id; if (existingUserId !== userId) { return res.status(400).json({ success: false, - error: `This wallet (${value}) is already linked to another account` + error: `This wallet (${value}) is already linked to another account`, }); } } @@ -57,22 +57,22 @@ router.post('/link', requireAuth, async (req, res) => { req.session.email = value; } - res.json({ - success: true, + res.json({ + success: true, message: 'Identity linked successfully', - isAdmin: req.session.isAdmin + isAdmin: req.session.isAdmin, }); } catch (error) { logger.error('Error linking identity:', error); - + // Делаем более понятные сообщения об ошибках if (error.message && error.message.includes('already belongs to another user')) { - return res.status(400).json({ + return res.status(400).json({ success: false, - error: `This identity is already linked to another account` + error: `This identity is already linked to another account`, }); } - + res.status(500).json({ error: error.message || 'Internal server error' }); } }); @@ -93,13 +93,13 @@ router.get('/token-balances', requireAuth, async (req, res) => { // Здесь логирование инициирования получения баланса может быть полезно logger.info(`Fetching token balances for user ${userId} with wallet ${wallet}`); - + // Получаем балансы токенов const balances = await authService.getTokenBalances(wallet); res.json({ success: true, - balances + balances, }); } catch (error) { logger.error('Error getting token balances:', error); diff --git a/backend/routes/tokens.js b/backend/routes/tokens.js index 54d743b..4c983df 100644 --- a/backend/routes/tokens.js +++ b/backend/routes/tokens.js @@ -8,23 +8,23 @@ const logger = require('../utils/logger'); router.get('/balances', requireAuth, async (req, res) => { try { const { address } = req.session; - + if (!address) { - return res.status(400).json({ - error: 'No wallet address in session' + return res.status(400).json({ + error: 'No wallet address in session', }); } logger.info(`Fetching token balances for address: ${address}`); const balances = await authService.getTokenBalances(address); - + res.json(balances); } catch (error) { logger.error('Error fetching token balances:', error); - res.status(500).json({ - error: 'Failed to fetch token balances' + res.status(500).json({ + error: 'Failed to fetch token balances', }); } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/users.js b/backend/routes/users.js index 9ba662d..63adbc2 100644 --- a/backend/routes/users.js +++ b/backend/routes/users.js @@ -23,19 +23,16 @@ 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] - ); - + 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); @@ -48,22 +45,23 @@ router.post('/update-profile', requireAuth, async (req, res) => { try { const { firstName, lastName } = req.body; const userId = req.session.userId; - + // Проверка валидности данных if (firstName && firstName.length > 255) { return res.status(400).json({ error: 'Имя слишком длинное (максимум 255 символов)' }); } - + if (lastName && lastName.length > 255) { return res.status(400).json({ error: 'Фамилия слишком длинная (максимум 255 символов)' }); } - + // Обновление имени и фамилии в базе данных - await db.query( - 'UPDATE users SET first_name = $1, last_name = $2 WHERE id = $3', - [firstName || null, lastName || null, userId] - ); - + await db.query('UPDATE users SET first_name = $1, last_name = $2 WHERE id = $3', [ + firstName || null, + lastName || null, + userId, + ]); + res.json({ success: true }); } catch (error) { logger.error('Error updating user profile:', error); @@ -75,29 +73,29 @@ router.post('/update-profile', requireAuth, async (req, res) => { router.get('/profile/current', requireAuth, async (req, res) => { try { const userId = req.session.userId; - + // Получение данных пользователя const userResult = await db.query( 'SELECT id, username, first_name, last_name, role, status, created_at, preferred_language FROM users WHERE id = $1', [userId] ); - + if (userResult.rows.length === 0) { return res.status(404).json({ error: 'Пользователь не найден' }); } - + // Получение идентификаторов пользователя const identitiesResult = await db.query( 'SELECT provider, provider_id FROM user_identities WHERE user_id = $1', [userId] ); - + const user = userResult.rows[0]; const identities = identitiesResult.rows.reduce((acc, identity) => { acc[identity.provider] = identity.provider_id; return acc; }, {}); - + res.json({ id: user.id, username: user.username, @@ -107,7 +105,7 @@ router.get('/profile/current', requireAuth, async (req, res) => { status: user.status, createdAt: user.created_at, preferredLanguage: user.preferred_language, - identities + identities, }); } catch (error) { logger.error('Error getting user profile:', error); diff --git a/backend/scripts/fix-duplicate-identities.js b/backend/scripts/fix-duplicate-identities.js index dd98d2a..4e4a761 100644 --- a/backend/scripts/fix-duplicate-identities.js +++ b/backend/scripts/fix-duplicate-identities.js @@ -15,7 +15,7 @@ if (!fs.existsSync(logDir)) { const logFile = path.join(logDir, 'fix-duplicates.log'); const logger = { - log: message => { + log: (message) => { const timestamp = new Date().toISOString(); const logMessage = `[${timestamp}] ${message}\n`; console.log(message); @@ -27,7 +27,7 @@ const logger = { const logMessage = `[${timestamp}] ERROR: ${message}${errorDetail}\n`; console.error(`ERROR: ${message}${errorDetail}`); fs.appendFileSync(logFile, logMessage); - } + }, }; // Создаем подключение к базе данных @@ -54,10 +54,10 @@ function normalizeWalletAddress(address) { */ async function findDuplicateWallets() { const client = await pool.connect(); - + try { logger.log('Поиск дубликатов wallet-идентификаторов...'); - + // Находим пары идентификаторов, которые отличаются только регистром const result = await client.query(` SELECT @@ -77,9 +77,9 @@ async function findDuplicateWallets() { LOWER(ui1.provider_id) = LOWER(ui2.provider_id) AND ui1.provider_id <> ui2.provider_id `); - + logger.log(`Найдено ${result.rows.length} потенциальных дубликатов wallet-идентификаторов`); - + return result.rows; } catch (error) { logger.error('Ошибка при поиске дубликатов wallet-идентификаторов', error); @@ -95,46 +95,52 @@ async function findDuplicateWallets() { */ async function fixDuplicates(duplicates) { const client = await pool.connect(); - + try { logger.log('Исправление дубликатов идентификаторов...'); - + await client.query('BEGIN'); - + for (const dup of duplicates) { // Проверяем, принадлежат ли идентификаторы одному пользователю if (dup.user_id1 === dup.user_id2) { // Если да, удаляем один из дубликатов (не в нижнем регистре) const normalizedAddress = normalizeWalletAddress(dup.provider_id1); - + // Определяем, какой идентификатор нужно удалить const idToDelete = dup.provider_id1 === normalizedAddress ? dup.id2 : dup.id1; - + logger.log(`Удаление дубликата ID ${idToDelete} для адреса ${normalizedAddress}`); - + await client.query('DELETE FROM user_identities WHERE id = $1', [idToDelete]); - + // Проверяем, что второй идентификатор в нормализованной форме const remainingId = dup.provider_id1 === normalizedAddress ? dup.id1 : dup.id2; - const remainingAddress = dup.provider_id1 === normalizedAddress ? dup.provider_id1 : dup.provider_id2; - + const remainingAddress = + dup.provider_id1 === normalizedAddress ? dup.provider_id1 : dup.provider_id2; + if (remainingAddress !== normalizedAddress) { - logger.log(`Обновление идентификатора ID ${remainingId} до нормализованного значения ${normalizedAddress}`); - - await client.query( - 'UPDATE user_identities SET provider_id = $1 WHERE id = $2', - [normalizedAddress, remainingId] + logger.log( + `Обновление идентификатора ID ${remainingId} до нормализованного значения ${normalizedAddress}` ); + + await client.query('UPDATE user_identities SET provider_id = $1 WHERE id = $2', [ + normalizedAddress, + remainingId, + ]); } } else { // Если идентификаторы принадлежат разным пользователям, нужно решить конфликт // Для определения какой пользователь является основным, можно использовать: // 1. Количество сообщений/активности // 2. Дату создания аккаунта - logger.log(`Конфликт: адрес ${dup.provider_id1}/${dup.provider_id2} привязан к разным пользователям: ${dup.user_id1} и ${dup.user_id2}`); - + logger.log( + `Конфликт: адрес ${dup.provider_id1}/${dup.provider_id2} привязан к разным пользователям: ${dup.user_id1} и ${dup.user_id2}` + ); + // Определяем, какой пользователь является основным - const userInfoResult = await client.query(` + const userInfoResult = await client.query( + ` SELECT id, (SELECT COUNT(*) FROM messages WHERE user_id = users.id) as message_count, @@ -145,45 +151,55 @@ async function fixDuplicates(duplicates) { id IN ($1, $2) ORDER BY message_count DESC, created_at ASC - `, [dup.user_id1, dup.user_id2]); - + `, + [dup.user_id1, dup.user_id2] + ); + // Если нет пользователей, пропускаем if (userInfoResult.rows.length === 0) { logger.log(`Пропуск: не найдены пользователи ${dup.user_id1} и ${dup.user_id2}`); continue; } - + // Выбираем первого пользователя как основного (с наибольшим количеством сообщений или самого старого) const mainUserId = userInfoResult.rows[0].id; const secondaryUserId = mainUserId === dup.user_id1 ? dup.user_id2 : dup.user_id1; - - logger.log(`Объединение пользователей: сохраняем ID ${mainUserId}, удаляем ID ${secondaryUserId}`); - + + logger.log( + `Объединение пользователей: сохраняем ID ${mainUserId}, удаляем ID ${secondaryUserId}` + ); + // Переносим все идентификаторы от вторичного пользователя к основному - await client.query(` + await client.query( + ` INSERT INTO user_identities (user_id, provider, provider_id) SELECT $1, provider, provider_id FROM user_identities WHERE user_id = $2 ON CONFLICT DO NOTHING - `, [mainUserId, secondaryUserId]); - + `, + [mainUserId, secondaryUserId] + ); + // Переносим сообщения - await client.query(` + await client.query( + ` UPDATE messages SET user_id = $1 WHERE user_id = $2 - `, [mainUserId, secondaryUserId]); - + `, + [mainUserId, secondaryUserId] + ); + // Переносим другие связанные данные... // ... - + // Удаляем вторичного пользователя await client.query('DELETE FROM user_identities WHERE user_id = $1', [secondaryUserId]); await client.query('DELETE FROM users WHERE id = $1', [secondaryUserId]); } } - + await client.query('COMMIT'); logger.log('Исправление дубликатов успешно завершено'); } catch (error) { @@ -201,43 +217,43 @@ async function fixDuplicates(duplicates) { async function main() { try { logger.log('Запуск скрипта исправления дубликатов идентификаторов...'); - + // Шаг 1: Нормализация всех адресов кошельков (приведение к нижнему регистру) const client = await pool.connect(); - + try { logger.log('Нормализация всех существующих адресов кошельков...'); - + await client.query('BEGIN'); - + // Получаем все идентификаторы кошельков const walletsResult = await client.query(` SELECT id, provider_id FROM user_identities WHERE provider = 'wallet' `); - + logger.log(`Найдено ${walletsResult.rows.length} идентификаторов кошельков`); - + // Обновляем каждый адрес к нормализованной форме let updatedCount = 0; - + for (const wallet of walletsResult.rows) { try { const normalizedAddress = normalizeWalletAddress(wallet.provider_id); - + if (normalizedAddress !== wallet.provider_id) { - await client.query( - 'UPDATE user_identities SET provider_id = $1 WHERE id = $2', - [normalizedAddress, wallet.id] - ); + await client.query('UPDATE user_identities SET provider_id = $1 WHERE id = $2', [ + normalizedAddress, + wallet.id, + ]); updatedCount++; } } catch (error) { logger.error(`Ошибка при нормализации адреса ${wallet.provider_id}`, error); } } - + await client.query('COMMIT'); logger.log(`Нормализовано ${updatedCount} адресов кошельков`); } catch (error) { @@ -246,16 +262,16 @@ async function main() { } finally { client.release(); } - + // Шаг 2: Поиск и исправление дубликатов const duplicates = await findDuplicateWallets(); - + if (duplicates.length > 0) { await fixDuplicates(duplicates); } else { logger.log('Дубликатов wallet-идентификаторов не найдено'); } - + logger.log('Скрипт успешно завершил работу'); } catch (error) { logger.error('Критическая ошибка при выполнении скрипта', error); @@ -265,4 +281,4 @@ async function main() { } // Запускаем скрипт -main(); \ No newline at end of file +main(); diff --git a/backend/scripts/run-migrations.js b/backend/scripts/run-migrations.js index c5cc15e..7ae2dd2 100644 --- a/backend/scripts/run-migrations.js +++ b/backend/scripts/run-migrations.js @@ -19,7 +19,7 @@ async function runMigrations() { // Получаем список выполненных миграций const { rows } = await pool.query('SELECT name FROM migrations'); - const executedMigrations = new Set(rows.map(row => row.name)); + const executedMigrations = new Set(rows.map((row) => row.name)); // Читаем файлы миграций const migrationsDir = path.join(__dirname, '../db/migrations'); @@ -27,7 +27,7 @@ async function runMigrations() { // Сортируем файлы по номеру const migrationFiles = files - .filter(f => f.endsWith('.sql')) + .filter((f) => f.endsWith('.sql')) .sort((a, b) => { const numA = parseInt(a.split('_')[0]); const numB = parseInt(b.split('_')[0]); @@ -55,14 +55,19 @@ async function runMigrations() { // Выполняем SQL-функции const functionsDir = path.join(migrationsDir, 'functions'); - if (await fs.stat(functionsDir).then(() => true).catch(() => false)) { + if ( + await fs + .stat(functionsDir) + .then(() => true) + .catch(() => false) + ) { const functionFiles = await fs.readdir(functionsDir); - + for (const file of functionFiles) { if (file.endsWith('.sql')) { const filePath = path.join(functionsDir, file); const sql = await fs.readFile(filePath, 'utf-8'); - + try { await pool.query(sql); logger.info(`Function ${file} executed successfully`); diff --git a/backend/server.js b/backend/server.js index 6db0cd4..10eaf2d 100644 --- a/backend/server.js +++ b/backend/server.js @@ -26,15 +26,15 @@ console.log('Используемый порт:', process.env.PORT || 8000); async function initServices() { try { console.log('Инициализация сервисов...'); - + // Останавливаем предыдущий экземпляр бота await stopBot(); - + // Добавляем обработку ошибок при запуске бота try { await getBot(); // getBot теперь асинхронный и сам запускает бота console.log('Telegram bot started'); - + // Добавляем graceful shutdown process.once('SIGINT', async () => { await stopBot(); @@ -46,14 +46,16 @@ async function initServices() { }); } catch (error) { if (error.code === 409) { - logger.warn('Another instance of Telegram bot is running. This is normal during development with nodemon'); + logger.warn( + 'Another instance of Telegram bot is running. This is normal during development with nodemon' + ); // Просто логируем ошибку и продолжаем работу // Бот будет запущен при следующем перезапуске } else { logger.error('Error launching Telegram bot:', error); } } - + console.log('Все сервисы успешно инициализированы'); } catch (error) { console.error('Ошибка при инициализации сервисов:', error); @@ -61,20 +63,22 @@ async function initServices() { } // Настройка сессий -app.use(session({ - store: new pgSession({ - pool: pool, - tableName: 'session' - }), - secret: process.env.SESSION_SECRET || 'hb3atoken', - resave: false, - saveUninitialized: true, - cookie: { - secure: process.env.NODE_ENV === 'production', - httpOnly: true, - maxAge: 30 * 24 * 60 * 60 * 1000 // 30 дней - } -})); +app.use( + session({ + store: new pgSession({ + pool: pool, + tableName: 'session', + }), + secret: process.env.SESSION_SECRET || 'hb3atoken', + resave: false, + saveUninitialized: true, + cookie: { + secure: process.env.NODE_ENV === 'production', + httpOnly: true, + maxAge: 30 * 24 * 60 * 60 * 1000, // 30 дней + }, + }) +); // Маршруты API app.use('/api/users', usersRouter); diff --git a/backend/services/ai-assistant.js b/backend/services/ai-assistant.js index 5f20f7e..64d05a1 100644 --- a/backend/services/ai-assistant.js +++ b/backend/services/ai-assistant.js @@ -12,9 +12,10 @@ class AIAssistant { // Создание экземпляра ChatOllama с нужными параметрами createChat(language = 'ru') { - const systemPrompt = language === 'ru' - ? 'Вы - полезный ассистент. Отвечайте на русском языке.' - : 'You are a helpful assistant. Respond in English.'; + const systemPrompt = + language === 'ru' + ? 'Вы - полезный ассистент. Отвечайте на русском языке.' + : 'You are a helpful assistant. Respond in English.'; return new ChatOllama({ baseUrl: this.baseUrl, @@ -22,7 +23,7 @@ class AIAssistant { system: systemPrompt, temperature: 0.7, maxTokens: 1000, - timeout: 30000 // 30 секунд таймаут + timeout: 30000, // 30 секунд таймаут }); } @@ -36,14 +37,12 @@ class AIAssistant { async getResponse(message, language = 'auto') { try { console.log('getResponse called with:', { message, language }); - + // Определяем язык, если не указан явно - const detectedLanguage = language === 'auto' - ? this.detectLanguage(message) - : language; + const detectedLanguage = language === 'auto' ? this.detectLanguage(message) : language; console.log('Detected language:', detectedLanguage); - + // Сначала пробуем прямой API запрос try { console.log('Trying direct API request...'); @@ -67,7 +66,7 @@ class AIAssistant { } } catch (error) { console.error('Error in getResponse:', error); - return "Извините, я не смог обработать ваш запрос. Пожалуйста, попробуйте позже."; + return 'Извините, я не смог обработать ваш запрос. Пожалуйста, попробуйте позже.'; } } @@ -75,10 +74,11 @@ class AIAssistant { async fallbackRequest(message, language) { try { console.log('Using fallback request method with:', { message, language }); - - const systemPrompt = language === 'ru' - ? 'Вы - полезный ассистент. Отвечайте на русском языке.' - : 'You are a helpful assistant. Respond in English.'; + + const systemPrompt = + language === 'ru' + ? 'Вы - полезный ассистент. Отвечайте на русском языке.' + : 'You are a helpful assistant. Respond in English.'; console.log('Sending request to Ollama API...'); const response = await fetch(`${this.baseUrl}/api/generate`, { @@ -91,15 +91,15 @@ class AIAssistant { stream: false, options: { temperature: 0.7, - num_predict: 1000 - } + num_predict: 1000, + }, }), }); - + if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } - + const data = await response.json(); console.log('Ollama API response:', data); return data.response; diff --git a/backend/services/auth-service.js b/backend/services/auth-service.js index df92479..00c4d4e 100644 --- a/backend/services/auth-service.js +++ b/backend/services/auth-service.js @@ -6,15 +6,13 @@ const { processMessage } = require('./ai-assistant'); // Используем AI const verificationService = require('./verification-service'); // Используем сервис верификации const ADMIN_CONTRACTS = [ - { address: "0xd95a45fc46a7300e6022885afec3d618d7d3f27c", network: "eth" }, - { address: "0x4B294265720B09ca39BFBA18c7E368413c0f68eB", network: "bsc" }, - { address: "0xdce769b847a0a697239777d0b1c7dd33b6012ba0", network: "arbitrum" }, - { address: "0x351f59de4fedbdf7601f5592b93db3b9330c1c1d", network: "polygon" } + { address: '0xd95a45fc46a7300e6022885afec3d618d7d3f27c', network: 'eth' }, + { address: '0x4B294265720B09ca39BFBA18c7E368413c0f68eB', network: 'bsc' }, + { address: '0xdce769b847a0a697239777d0b1c7dd33b6012ba0', network: 'arbitrum' }, + { address: '0x351f59de4fedbdf7601f5592b93db3b9330c1c1d', network: 'polygon' }, ]; -const ERC20_ABI = [ - "function balanceOf(address owner) view returns (uint256)" -]; +const ERC20_ABI = ['function balanceOf(address owner) view returns (uint256)']; class AuthService { constructor() { @@ -22,7 +20,7 @@ class AuthService { eth: new ethers.JsonRpcProvider(process.env.RPC_URL_ETH), polygon: new ethers.JsonRpcProvider(process.env.RPC_URL_POLYGON), bsc: new ethers.JsonRpcProvider(process.env.RPC_URL_BSC), - arbitrum: new ethers.JsonRpcProvider(process.env.RPC_URL_ARBITRUM) + arbitrum: new ethers.JsonRpcProvider(process.env.RPC_URL_ARBITRUM), }; } @@ -30,13 +28,13 @@ class AuthService { async verifySignature(message, signature, address) { try { if (!message || !signature || !address) return false; - + // Нормализуем входящий адрес const normalizedAddress = ethers.getAddress(address).toLowerCase(); - + // Восстанавливаем адрес из подписи const recoveredAddress = ethers.verifyMessage(message, signature); - + // Сравниваем нормализованные адреса return ethers.getAddress(recoveredAddress).toLowerCase() === normalizedAddress; } catch (error) { @@ -54,20 +52,23 @@ class AuthService { try { // Нормализуем адрес - всегда приводим к нижнему регистру const normalizedAddress = ethers.getAddress(address).toLowerCase(); - + // Ищем пользователя по адресу в таблице user_identities - const userResult = await db.query(` + const userResult = await db.query( + ` SELECT u.* FROM users u JOIN user_identities ui ON u.id = ui.user_id WHERE ui.provider = 'wallet' AND ui.provider_id = $1 - `, [normalizedAddress]); - + `, + [normalizedAddress] + ); + if (userResult.rows.length > 0) { const user = userResult.rows[0]; - + // Проверяем роль администратора при каждой аутентификации const isAdmin = await this.checkAdminRole(normalizedAddress); - + // Если статус админа изменился, обновляем роль в базе данных if (user.role === 'admin' && !isAdmin) { await db.query('UPDATE users SET role = $1 WHERE id = $2', ['user', user.id]); @@ -78,37 +79,38 @@ class AuthService { logger.info(`Updated user ${user.id} role to admin (admin tokens found)`); return { userId: user.id, isAdmin: true }; } - - return { - userId: user.id, - isAdmin: user.role === 'admin' + + return { + userId: user.id, + isAdmin: user.role === 'admin', }; } - + // Если пользователь не найден, создаем нового - const newUserResult = await db.query( - 'INSERT INTO users (role) VALUES ($1) RETURNING id', - ['user'] - ); - + const newUserResult = await db.query('INSERT INTO users (role) VALUES ($1) RETURNING id', [ + 'user', + ]); + const userId = newUserResult.rows[0].id; - + // Добавляем идентификатор кошелька (всегда в нижнем регистре) await db.query( 'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)', [userId, 'wallet', normalizedAddress] ); - + // Проверяем, есть ли у пользователя роль админа const isAdmin = await this.checkAdminRole(normalizedAddress); logger.info(`New user ${userId} role check result: ${isAdmin ? 'admin' : 'user'}`); - + // Если у пользователя есть админские токены, обновляем его роль if (isAdmin) { await db.query('UPDATE users SET role = $1 WHERE id = $2', ['admin', userId]); - logger.info(`New user ${userId} with wallet ${normalizedAddress} automatically granted admin role`); + logger.info( + `New user ${userId} with wallet ${normalizedAddress} automatically granted admin role` + ); } - + return { userId, isAdmin }; } catch (error) { logger.error('Error finding or creating user:', error); @@ -123,13 +125,13 @@ class AuthService { */ async checkAdminRole(address) { if (!address) return false; - + logger.info(`Checking admin role for address: ${address}`); let foundTokens = false; let errorCount = 0; const balances = {}; const totalNetworks = ADMIN_CONTRACTS.length; - + // Создаем массив промисов для параллельной проверки балансов const checkPromises = ADMIN_CONTRACTS.map(async (contract) => { try { @@ -140,83 +142,83 @@ class AuthService { errorCount++; return null; } - + // Проверяем доступность провайдера try { // Проверка доступности сети с таймаутом const networkCheckPromise = provider.getNetwork(); - const timeoutPromise = new Promise((_, reject) => + const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Network check timeout')), 3000) ); - + await Promise.race([networkCheckPromise, timeoutPromise]); } catch (networkError) { - logger.error(`Provider for ${contract.network} is not available: ${networkError.message}`); + logger.error( + `Provider for ${contract.network} is not available: ${networkError.message}` + ); balances[contract.network] = 'Error: Network unavailable'; errorCount++; return null; } - - const tokenContract = new ethers.Contract( - contract.address, - ERC20_ABI, - provider - ); - + + const tokenContract = new ethers.Contract(contract.address, ERC20_ABI, provider); + // Создаем промис с таймаутом const balancePromise = tokenContract.balanceOf(address); - const timeoutPromise = new Promise((_, reject) => + const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 3000) ); - + // Ждем первый выполненный промис const balance = await Promise.race([balancePromise, timeoutPromise]); const formattedBalance = ethers.formatUnits(balance, 18); balances[contract.network] = formattedBalance; - + logger.info(`Token balance on ${contract.network}:`, { address, contract: contract.address, balance: formattedBalance, - hasTokens: balance > 0 + hasTokens: balance > 0, }); if (parseFloat(formattedBalance) > 0) { logger.info(`Found admin tokens on ${contract.network}`); foundTokens = true; } - + return { network: contract.network, balance: formattedBalance }; } catch (error) { logger.error(`Error checking balance in ${contract.network}:`, { address, contract: contract.address, - error: error.message || 'Unknown error' + error: error.message || 'Unknown error', }); balances[contract.network] = 'Error'; errorCount++; return null; } }); - + // Ждем выполнения всех проверок await Promise.all(checkPromises); - + // Если все запросы завершились с ошибкой, считаем, что проверка не удалась if (errorCount === totalNetworks) { logger.error(`All network checks for ${address} failed. Cannot verify admin status.`); return false; } - + if (foundTokens) { logger.info(`Admin role summary for ${address}:`, { - networks: Object.keys(balances).filter(net => balances[net] > 0 && balances[net] !== 'Error'), - balances + networks: Object.keys(balances).filter( + (net) => balances[net] > 0 && balances[net] !== 'Error' + ), + balances, }); logger.info(`Admin role granted for ${address}`); return true; } - + logger.info(`Admin role denied - no tokens found for ${address}`); return false; } @@ -233,13 +235,13 @@ class AuthService { eth: '0', bsc: '0', arbitrum: '0', - polygon: '0' + polygon: '0', }; } const balances = {}; const timeout = 3000; // 3 секунды таймаут - + for (const contract of ADMIN_CONTRACTS) { try { const provider = this.providers[contract.network]; @@ -253,37 +255,35 @@ class AuthService { try { // Проверка доступности сети с таймаутом const networkCheckPromise = provider.getNetwork(); - const networkTimeoutPromise = new Promise((_, reject) => + const networkTimeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Network check timeout')), timeout) ); - + await Promise.race([networkCheckPromise, networkTimeoutPromise]); } catch (networkError) { - logger.error(`Provider for ${contract.network} is not available: ${networkError.message}`); + logger.error( + `Provider for ${contract.network} is not available: ${networkError.message}` + ); balances[contract.network] = '0'; continue; } - const tokenContract = new ethers.Contract( - contract.address, - ERC20_ABI, - provider - ); + const tokenContract = new ethers.Contract(contract.address, ERC20_ABI, provider); // Создаем промис с таймаутом const balancePromise = tokenContract.balanceOf(address); - const timeoutPromise = new Promise((_, reject) => + const timeoutPromise = new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), timeout) ); // Ждем первый выполненный промис const balance = await Promise.race([balancePromise, timeoutPromise]); const formattedBalance = ethers.formatUnits(balance, 18); - + logger.info(`Token balance for ${address} on ${contract.network}:`, { contract: contract.address, balance: formattedBalance, - timestamp: new Date().toISOString() + timestamp: new Date().toISOString(), }); balances[contract.network] = formattedBalance; @@ -292,17 +292,17 @@ class AuthService { address, contract: contract.address, error: error.message || 'Unknown error', - timestamp: new Date().toISOString() + timestamp: new Date().toISOString(), }); balances[contract.network] = '0'; } } - + logger.info(`Token balances fetched for ${address}:`, { ...balances, - timestamp: new Date().toISOString() + timestamp: new Date().toISOString(), }); - + return balances; } @@ -318,26 +318,29 @@ class AuthService { session.userId = userId; session.authenticated = authenticated; session.authType = authType; - + // Сохраняем адрес кошелька если есть if (address) { session.address = address; } - + // Сохраняем сессию в БД const result = await db.query( `UPDATE session SET sess = $1 WHERE sid = $2`, - [JSON.stringify({ - userId, - authenticated, - authType, - address, - cookie: session.cookie - }), session.id] + [ + JSON.stringify({ + userId, + authenticated, + authType, + address, + cookie: session.cookie, + }), + session.id, + ] ); - + return true; } catch (error) { logger.error('Error creating session:', error); @@ -400,17 +403,19 @@ class AuthService { try { // Проверяем наличие связанного кошелька const wallet = await this.getLinkedWallet(userId); - + // Если кошелек не привязан, пользователь получает роль user // с базовым доступом к чату и истории сообщений if (!wallet) { logger.info(`No wallet linked for user ${userId}, assigning basic user role`); return 'user'; } - + // Если есть кошелек, проверяем админские токены const isAdmin = await this.checkAdminRole(wallet); - logger.info(`Role check for user ${userId} with wallet ${wallet}: ${isAdmin ? 'admin' : 'user'}`); + logger.info( + `Role check for user ${userId} with wallet ${wallet}: ${isAdmin ? 'admin' : 'user'}` + ); return isAdmin ? 'admin' : 'user'; } catch (error) { logger.error('Error checking user role:', error); @@ -423,20 +428,17 @@ class AuthService { try { // Проверяем код через сервис верификации const result = await verificationService.verifyCode(code, 'email', null); - + if (!result.success) { return { verified: false }; } - + const userId = result.userId; const email = result.providerId; - + // Проверяем, существует ли пользователь с таким email - const userResult = await db.query( - 'SELECT * FROM users WHERE id = $1', - [userId] - ); - + const userResult = await db.query('SELECT * FROM users WHERE id = $1', [userId]); + if (userResult.rows.length === 0) { return { verified: false }; } @@ -444,7 +446,7 @@ class AuthService { // Проверяем наличие кошелька и определяем роль const wallet = await this.getLinkedWallet(userId); let role = 'user'; // Базовая роль для доступа к чату - + if (wallet) { // Если есть кошелек, проверяем баланс токенов const isAdmin = await this.checkAdminRole(wallet); @@ -453,13 +455,13 @@ class AuthService { } else { logger.info(`User ${userId} has no wallet, using basic user role`); } - + return { verified: true, userId, email, role, - wallet: wallet || null + wallet: wallet || null, }; } catch (error) { logger.error('Error checking email verification:', error); @@ -473,28 +475,30 @@ class AuthService { async verifyTelegramAuth(telegramId, verificationCode, session) { try { logger.info(`[verifyTelegramAuth] Starting for telegramId: ${telegramId}`); - + let userId; let isNewUser = false; - + // Проверяем наличие аутентифицированного пользователя в сессии if (session && session.authenticated && session.userId) { // Если есть авторизованный пользователь в сессии, связываем Telegram с ним userId = session.userId; - logger.info(`[verifyTelegramAuth] Using existing authenticated user ${userId} from session`); - + logger.info( + `[verifyTelegramAuth] Using existing authenticated user ${userId} from session` + ); + // Связываем Telegram с текущим пользователем await this.linkIdentity(userId, 'telegram', telegramId); - + return { success: true, userId, role: session.isAdmin ? 'admin' : 'user', telegramId, - isNewUser: false + isNewUser: false, }; } - + // Если в сессии нет авторизованного пользователя, проверяем существующие идентификаторы // Проверяем, существует ли уже пользователь с таким Telegram ID const existingUserResult = await db.query( @@ -509,23 +513,26 @@ class AuthService { if (existingUserResult.rows.length > 0) { const existingUser = existingUserResult.rows[0]; userId = existingUser.id; - logger.info(`[verifyTelegramAuth] Found existing user ${userId} for Telegram ID ${telegramId}`); + logger.info( + `[verifyTelegramAuth] Found existing user ${userId} for Telegram ID ${telegramId}` + ); } else { // Создаем нового пользователя для нового telegramId - const newUserResult = await db.query( - 'INSERT INTO users (role) VALUES ($1) RETURNING id', - ['user'] - ); + const newUserResult = await db.query('INSERT INTO users (role) VALUES ($1) RETURNING id', [ + 'user', + ]); userId = newUserResult.rows[0].id; isNewUser = true; - + // Добавляем Telegram идентификатор await db.query( 'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)', [userId, 'telegram', telegramId] ); - - logger.info(`[verifyTelegramAuth] Created new user ${userId} for Telegram ID ${telegramId}`); + + logger.info( + `[verifyTelegramAuth] Created new user ${userId} for Telegram ID ${telegramId}` + ); } // Если есть гостевой ID в сессии, сохраняем его для нового пользователя @@ -542,7 +549,7 @@ class AuthService { userId, role: 'user', telegramId, - isNewUser + isNewUser, }; } catch (error) { logger.error('[verifyTelegramAuth] Error:', error); @@ -553,30 +560,28 @@ class AuthService { // Добавляем псевдоним функции checkAdminRole для обратной совместимости async checkAdminTokens(address) { if (!address) return false; - + logger.info(`Checking admin tokens for address: ${address}`); - + try { const isAdmin = await this.checkAdminRole(address); - + // Обновляем роль пользователя в базе данных, если есть админские токены if (isAdmin) { try { // Находим userId по адресу - const userResult = await db.query(` + const userResult = await db.query( + ` SELECT u.id FROM users u JOIN user_identities ui ON u.id = ui.user_id WHERE ui.provider = 'wallet' AND ui.provider_id = $1`, [address.toLowerCase()] ); - + if (userResult.rows.length > 0) { const userId = userResult.rows[0].id; // Обновляем роль пользователя - await db.query( - 'UPDATE users SET role = $1 WHERE id = $2', - ['admin', userId] - ); + await db.query('UPDATE users SET role = $1 WHERE id = $2', ['admin', userId]); logger.info(`Updated user ${userId} role to admin based on token holdings`); } } catch (error) { @@ -586,26 +591,24 @@ class AuthService { } else { // Если пользователь не является администратором, сбрасываем роль на "user", если она была "admin" try { - const userResult = await db.query(` + const userResult = await db.query( + ` SELECT u.id, u.role FROM users u JOIN user_identities ui ON u.id = ui.user_id WHERE ui.provider = 'wallet' AND ui.provider_id = $1`, [address.toLowerCase()] ); - + if (userResult.rows.length > 0 && userResult.rows[0].role === 'admin') { const userId = userResult.rows[0].id; - await db.query( - 'UPDATE users SET role = $1 WHERE id = $2', - ['user', userId] - ); + await db.query('UPDATE users SET role = $1 WHERE id = $2', ['user', userId]); logger.info(`Reset user ${userId} role from admin to user (no tokens found)`); } } catch (error) { logger.error('Error updating user role:', error); } } - + return isAdmin; } catch (error) { logger.error(`Error in checkAdminTokens: ${error.message}`); @@ -622,24 +625,21 @@ class AuthService { try { // Получаем все идентификаторы пользователя const identities = await this.getUserIdentities(userId); - + // Фильтруем только гостевые идентификаторы - const guestIdentities = identities.filter(id => id.identity_type === 'guest'); - + const guestIdentities = identities.filter((id) => id.identity_type === 'guest'); + // Если гостевых идентификаторов больше 3, удаляем старые if (guestIdentities.length > 3) { // Сортируем по дате создания (новые первые) guestIdentities.sort((a, b) => new Date(b.created_at) - new Date(a.created_at)); - + // Оставляем только 3 последних идентификатора const identitiesToDelete = guestIdentities.slice(3); - + // Удаляем старые идентификаторы for (const identity of identitiesToDelete) { - await db.query( - 'DELETE FROM user_identities WHERE id = $1', - [identity.id] - ); + await db.query('DELETE FROM user_identities WHERE id = $1', [identity.id]); logger.info(`Deleted old guest identity: ${identity.identity_value}`); } } @@ -676,9 +676,7 @@ class AuthService { try { const balance = await Promise.race([ this.getTokenBalance(address, ADMIN_CONTRACTS.ARBITRUM), - new Promise((_, reject) => - setTimeout(() => reject(new Error('TIMEOUT')), timeout) - ) + new Promise((_, reject) => setTimeout(() => reject(new Error('TIMEOUT')), timeout)), ]); return { balance, hasTokens: balance > 0 }; } catch (error) { @@ -697,10 +695,12 @@ class AuthService { async linkIdentity(userId, provider, providerId) { try { if (!userId || !provider || !providerId) { - logger.warn(`[AuthService] Missing parameters for linkIdentity: userId=${userId}, provider=${provider}, providerId=${providerId}`); + logger.warn( + `[AuthService] Missing parameters for linkIdentity: userId=${userId}, provider=${provider}, providerId=${providerId}` + ); throw new Error('Missing parameters'); } - + // Нормализуем значение идентификатора let normalizedProviderId = providerId; if (provider === 'wallet') { @@ -714,113 +714,63 @@ class AuthService { } else if (provider === 'email') { normalizedProviderId = providerId.toLowerCase(); } - - logger.info(`[AuthService] Linking identity ${provider}:${normalizedProviderId} to user ${userId}`); - + + logger.info( + `[AuthService] Linking identity ${provider}:${normalizedProviderId} to user ${userId}` + ); + // Проверяем, существует ли уже такой идентификатор const existingResult = await db.query( `SELECT user_id FROM user_identities WHERE provider = $1 AND provider_id = $2`, [provider, normalizedProviderId] ); - + if (existingResult.rows.length > 0) { const existingUserId = existingResult.rows[0].user_id; - + // Если идентификатор уже принадлежит этому пользователю, ничего не делаем if (existingUserId === userId) { - logger.info(`[AuthService] Identity ${provider}:${normalizedProviderId} already exists for user ${userId}`); + logger.info( + `[AuthService] Identity ${provider}:${normalizedProviderId} already exists for user ${userId}` + ); return { success: true, message: 'Identity already exists' }; } else { // Если идентификатор принадлежит другому пользователю, возвращаем ошибку - logger.warn(`[AuthService] Identity ${provider}:${normalizedProviderId} already belongs to user ${existingUserId}, not user ${userId}`); + logger.warn( + `[AuthService] Identity ${provider}:${normalizedProviderId} already belongs to user ${existingUserId}, not user ${userId}` + ); throw new Error(`Identity already belongs to another user (${existingUserId})`); } } - + // Добавляем новый идентификатор для пользователя await db.query( `INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)`, [userId, provider, normalizedProviderId] ); - + // Проверяем и обновляем роль администратора, если это идентификатор кошелька let isAdmin = false; if (provider === 'wallet') { isAdmin = await this.checkAdminTokens(normalizedProviderId); - + // Обновляем роль пользователя в базе данных, если нужно if (isAdmin) { - await db.query( - 'UPDATE users SET role = $1 WHERE id = $2', - ['admin', userId] - ); + await db.query('UPDATE users SET role = $1 WHERE id = $2', ['admin', userId]); logger.info(`[AuthService] Updated user ${userId} role to admin based on token holdings`); } } - - logger.info(`[AuthService] Identity ${provider}:${normalizedProviderId} successfully linked to user ${userId}`); + + logger.info( + `[AuthService] Identity ${provider}:${normalizedProviderId} successfully linked to user ${userId}` + ); return { success: true, isAdmin }; } catch (error) { - logger.error(`[AuthService] Error linking identity ${provider}:${providerId} to user ${userId}:`, error); - throw error; - } - } - - /** - * Обработка гостевых сообщений после аутентификации - * ПРИМЕЧАНИЕ: Эта функция оставлена для обратной совместимости. - * Фактически все маршруты теперь используют версию функции из auth.js, - * которая корректно обрабатывает сообщения для всех типов аутентификации. - * @deprecated Используйте функцию linkGuestMessagesAfterAuth из routes/auth.js - */ - async linkGuestMessagesAfterAuth(userId, currentGuestId, previousGuestId) { - try { - logger.info(`[linkGuestMessagesAfterAuth] Starting for user ${userId} with guestId=${currentGuestId}`); - - // Проверяем, есть ли идентификатор для обработки - if (!currentGuestId) { - logger.debug('[linkGuestMessagesAfterAuth] No guest ID to process'); - return { success: true, message: 'No guest ID to process' }; - } - - // Проверяем, не привязаны ли уже эти гостевые сообщения к другому пользователю - const existingMessagesCheck = await db.query( - `SELECT DISTINCT user_id - FROM messages - WHERE guest_message_id IN ( - SELECT id FROM guest_messages WHERE guest_id = $1 - )`, - [currentGuestId] + logger.error( + `[AuthService] Error linking identity ${provider}:${providerId} to user ${userId}:`, + error ); - - if (existingMessagesCheck.rows.length > 0) { - const existingUserId = existingMessagesCheck.rows[0].user_id; - if (existingUserId !== userId) { - logger.warn(`[linkGuestMessagesAfterAuth] Guest messages for ${currentGuestId} are already linked to user ${existingUserId}`); - return { - success: false, - error: 'Guest messages are already linked to another user' - }; - } - } - - // Используем ту же функцию processGuestMessages что и в auth.js - const result = await processGuestMessages(userId, currentGuestId); - logger.info(`[linkGuestMessagesAfterAuth] Guest messages processed: ${JSON.stringify(result)}`); - - // Если есть предыдущий гостевой ID, обработаем и его - if (previousGuestId && previousGuestId !== currentGuestId) { - const prevResult = await processGuestMessages(userId, previousGuestId); - logger.info(`[linkGuestMessagesAfterAuth] Previous guest messages processed: ${JSON.stringify(prevResult)}`); - } - - return { - success: true, - result: result - }; - } catch (error) { - logger.error('[linkGuestMessagesAfterAuth] Error:', error); throw error; } } @@ -828,4 +778,4 @@ class AuthService { // Создаем и экспортируем единственный экземпляр const authService = new AuthService(); -module.exports = authService; \ No newline at end of file +module.exports = authService; diff --git a/backend/services/emailAuth.js b/backend/services/emailAuth.js index 2a8d1ce..96e2eb2 100644 --- a/backend/services/emailAuth.js +++ b/backend/services/emailAuth.js @@ -15,7 +15,7 @@ class EmailAuth { if (!email || !email.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/)) { throw new Error('Некорректный формат email'); } - + // Проверяем, существует ли пользователь с таким email const existingEmailUser = await db.query( `SELECT u.id FROM users u @@ -23,44 +23,47 @@ class EmailAuth { WHERE i.provider = 'email' AND i.provider_id = $1`, [email.toLowerCase()] ); - + // Создаем или получаем ID пользователя let userId; - + if (session.authenticated && session.userId) { // Если пользователь уже аутентифицирован, используем его ID userId = session.userId; - logger.info(`[initEmailAuth] Using existing authenticated user ${userId} for email ${email}`); + logger.info( + `[initEmailAuth] Using existing authenticated user ${userId} for email ${email}` + ); } else if (existingEmailUser.rows.length > 0) { // Если найден пользователь с таким email, используем его ID userId = existingEmailUser.rows[0].id; logger.info(`[initEmailAuth] Found existing user ${userId} with email ${email}`); } else { // Создаем временного пользователя, если нужно будет создать нового - const userResult = await db.query( - 'INSERT INTO users (role) VALUES ($1) RETURNING id', - ['user'] - ); + const userResult = await db.query('INSERT INTO users (role) VALUES ($1) RETURNING id', [ + 'user', + ]); userId = userResult.rows[0].id; session.tempUserId = userId; logger.info(`[initEmailAuth] Created temporary user ${userId} for email ${email}`); } - + // Сохраняем email в сессии session.pendingEmail = email.toLowerCase(); - + // Создаем код через сервис верификации const verificationCode = await verificationService.createVerificationCode( 'email', email.toLowerCase(), userId ); - + // Отправляем код на email await this.emailBot.sendVerificationCode(email, verificationCode); - - logger.info(`Generated verification code for Email auth for ${email} and sent to user's email`); - + + logger.info( + `Generated verification code for Email auth for ${email} and sent to user's email` + ); + return { success: true, verificationCode }; } catch (error) { logger.error('Error in email auth initialization:', error); @@ -80,7 +83,7 @@ class EmailAuth { // Проверяем код через сервис верификации const result = await verificationService.verifyCode(code, 'email', session.pendingEmail); - + if (!result.success) { // Используем сообщение об ошибке из сервиса верификации return { verified: false, message: result.error || 'Неверный код верификации' }; @@ -93,28 +96,31 @@ class EmailAuth { if (session.authenticated && session.userId) { finalUserId = session.userId; logger.info(`[checkEmailVerification] Using existing authenticated user ${finalUserId}`); - + // Связываем email с существующим пользователем await authService.linkIdentity(finalUserId, 'email', email); - + // Очищаем временные данные delete session.pendingEmail; - + return { verified: true, userId: finalUserId, - email: email + email: email, }; } // Если пользователь не авторизован, ищем всех пользователей с похожими идентификаторами const identities = { email: email, - guest: session.guestId + guest: session.guestId, }; const relatedUsers = await authService.identityService.findRelatedUsers(identities); - logger.info(`[checkEmailVerification] Found ${relatedUsers.length} related users for identities:`, identities); + logger.info( + `[checkEmailVerification] Found ${relatedUsers.length} related users for identities:`, + identities + ); if (relatedUsers.length > 0) { // Берем первого найденного пользователя как основного @@ -124,13 +130,17 @@ class EmailAuth { // Мигрируем данные от остальных пользователей к основному for (const userId of relatedUsers.slice(1)) { await authService.identityService.migrateUserData(userId, finalUserId); - logger.info(`[checkEmailVerification] Migrated data from user ${userId} to ${finalUserId}`); + logger.info( + `[checkEmailVerification] Migrated data from user ${userId} to ${finalUserId}` + ); } // Если у нас есть временный пользователь, мигрируем его данные тоже if (session.tempUserId && !relatedUsers.includes(session.tempUserId)) { await authService.identityService.migrateUserData(session.tempUserId, finalUserId); - logger.info(`[checkEmailVerification] Migrated temporary user ${session.tempUserId} to ${finalUserId}`); + logger.info( + `[checkEmailVerification] Migrated temporary user ${session.tempUserId} to ${finalUserId}` + ); } } else { // Если связанных пользователей нет, используем временного или создаем нового @@ -154,7 +164,9 @@ class EmailAuth { // Если есть гостевой ID, добавляем его тоже if (session.guestId) { await authService.identityService.saveIdentity(finalUserId, 'guest', session.guestId, true); - logger.info(`[checkEmailVerification] Added guest identity ${session.guestId} for user ${finalUserId}`); + logger.info( + `[checkEmailVerification] Added guest identity ${session.guestId} for user ${finalUserId}` + ); } // Очищаем временные данные @@ -166,7 +178,7 @@ class EmailAuth { return { verified: true, userId: finalUserId, - email: email + email: email, }; } catch (error) { logger.error('Error checking email verification:', error); @@ -177,4 +189,4 @@ class EmailAuth { // Создаем и экспортируем единственный экземпляр const emailAuth = new EmailAuth(); -module.exports = emailAuth; \ No newline at end of file +module.exports = emailAuth; diff --git a/backend/services/emailBot.js b/backend/services/emailBot.js index 9c73cd6..7adf63b 100644 --- a/backend/services/emailBot.js +++ b/backend/services/emailBot.js @@ -19,8 +19,8 @@ const transporter = nodemailer.createTransport({ maxConnections: 3, maxMessages: 5, tls: { - rejectUnauthorized: false - } + rejectUnauthorized: false, + }, }); // Конфигурация для получения писем @@ -31,11 +31,11 @@ const imapConfig = { port: process.env.EMAIL_IMAP_PORT, tls: true, tlsOptions: { rejectUnauthorized: false }, - keepalive: { + keepalive: { interval: 10000, idleInterval: 300000, - forceNoop: true - } + forceNoop: true, + }, }; class EmailBotService { @@ -66,7 +66,7 @@ class EmailBotService { try { // Отправляем код на email await this.sendVerificationCode(email, code); - + return { success: true }; } catch (error) { logger.error('Error initializing email verification:', error); @@ -91,7 +91,7 @@ class EmailBotService {

Код действителен в течение 15 минут.

- ` + `, }; await this.transporter.sendMail(mailOptions); @@ -121,7 +121,7 @@ class EmailBotService { this.imap.end(); return; } - + // Ищем непрочитанные письма this.imap.search(['UNSEEN'], (err, results) => { if (err) { @@ -129,16 +129,16 @@ class EmailBotService { this.imap.end(); return; } - + if (!results || results.length === 0) { logger.info('No new messages found'); this.imap.end(); return; } - + try { const f = this.imap.fetch(results, { bodies: '' }); - + f.on('message', (msg, seqno) => { msg.on('body', (stream, info) => { simpleParser(stream, async (err, parsed) => { @@ -149,11 +149,11 @@ class EmailBotService { }); }); }); - + f.once('error', (err) => { logger.error(`Fetch error: ${err}`); }); - + f.once('end', () => { try { this.imap.end(); @@ -172,7 +172,7 @@ class EmailBotService { }); }); }); - + this.imap.connect(); } catch (error) { logger.error(`Global error checking emails: ${error.message}`); @@ -191,7 +191,7 @@ class EmailBotService { from: process.env.EMAIL_USER, to, subject, - text + text, }; await this.transporter.sendMail(mailOptions); diff --git a/backend/services/identity-service.js b/backend/services/identity-service.js index 546900f..6691aa5 100644 --- a/backend/services/identity-service.js +++ b/backend/services/identity-service.js @@ -15,19 +15,19 @@ class IdentityService { if (!provider || !providerId) { return { provider, providerId }; } - + // Приводим провайдер к нижнему регистру const normalizedProvider = provider.toLowerCase(); - + // Для email и wallet приводим значение к нижнему регистру let normalizedProviderId = providerId; if (normalizedProvider === 'wallet' || normalizedProvider === 'email') { normalizedProviderId = providerId.toLowerCase(); } - - return { - provider: normalizedProvider, - providerId: normalizedProviderId + + return { + provider: normalizedProvider, + providerId: normalizedProviderId, }; } @@ -42,21 +42,25 @@ class IdentityService { async saveIdentity(userId, provider, providerId, verified = true) { try { if (!userId || !provider || !providerId) { - logger.warn(`[IdentityService] Missing required parameters: userId=${userId}, provider=${provider}, providerId=${providerId}`); + logger.warn( + `[IdentityService] Missing required parameters: userId=${userId}, provider=${provider}, providerId=${providerId}` + ); return { success: false, - error: 'Missing required parameters' + error: 'Missing required parameters', }; } - + // Нормализуем значения - const { provider: normalizedProvider, providerId: normalizedProviderId } = + const { provider: normalizedProvider, providerId: normalizedProviderId } = this.normalizeIdentity(provider, providerId); - + // Проверяем тип провайдера и перенаправляем гостевые идентификаторы в guest_user_mapping if (normalizedProvider === 'guest') { - logger.info(`[IdentityService] Converting guest identity for user ${userId} to guest_user_mapping: ${normalizedProviderId}`); - + logger.info( + `[IdentityService] Converting guest identity for user ${userId} to guest_user_mapping: ${normalizedProviderId}` + ); + try { await db.query( 'INSERT INTO guest_user_mapping (user_id, guest_id) VALUES ($1, $2) ON CONFLICT (guest_id) DO UPDATE SET user_id = $1', @@ -64,41 +68,50 @@ class IdentityService { ); return { success: true }; } catch (guestError) { - logger.error(`[IdentityService] Error saving guest identity for user ${userId}:`, guestError); + logger.error( + `[IdentityService] Error saving guest identity for user ${userId}:`, + guestError + ); return { success: false, error: guestError.message }; } } - + // Проверяем, разрешен ли такой тип провайдера const allowedProviders = ['email', 'wallet', 'telegram', 'username']; if (!allowedProviders.includes(normalizedProvider)) { logger.warn(`[IdentityService] Invalid provider type: ${normalizedProvider}`); return { success: false, - error: `Invalid provider type. Allowed types: ${allowedProviders.join(', ')}` + error: `Invalid provider type. Allowed types: ${allowedProviders.join(', ')}`, }; } - - logger.info(`[IdentityService] Saving identity for user ${userId}: ${normalizedProvider}:${normalizedProviderId}`); - + + logger.info( + `[IdentityService] Saving identity for user ${userId}: ${normalizedProvider}:${normalizedProviderId}` + ); + // Проверяем, существует ли уже такой идентификатор const existingResult = await db.query( `SELECT user_id FROM user_identities WHERE provider = $1 AND provider_id = $2`, [normalizedProvider, normalizedProviderId] ); - + if (existingResult.rows.length > 0) { const existingUserId = existingResult.rows[0].user_id; - + // Если идентификатор уже принадлежит этому пользователю, ничего не делаем if (existingUserId === userId) { - logger.info(`[IdentityService] Identity ${normalizedProvider}:${normalizedProviderId} already exists for user ${userId}`); + logger.info( + `[IdentityService] Identity ${normalizedProvider}:${normalizedProviderId} already exists for user ${userId}` + ); } else { // Если идентификатор принадлежит другому пользователю, логируем это - logger.warn(`[IdentityService] Identity ${normalizedProvider}:${normalizedProviderId} already belongs to user ${existingUserId}, not user ${userId}`); + logger.warn( + `[IdentityService] Identity ${normalizedProvider}:${normalizedProviderId} already belongs to user ${existingUserId}, not user ${userId}` + ); return { success: false, - error: `Identity already belongs to another user (${existingUserId})` + error: `Identity already belongs to another user (${existingUserId})`, }; } } else { @@ -108,16 +121,21 @@ class IdentityService { VALUES ($1, $2, $3)`, [userId, normalizedProvider, normalizedProviderId] ); - logger.info(`[IdentityService] Created new identity ${normalizedProvider}:${normalizedProviderId} for user ${userId}`); + logger.info( + `[IdentityService] Created new identity ${normalizedProvider}:${normalizedProviderId} for user ${userId}` + ); } - + return { success: true }; } catch (error) { - logger.error(`[IdentityService] Error saving identity ${provider}:${providerId} for user ${userId}:`, error); + logger.error( + `[IdentityService] Error saving identity ${provider}:${providerId} for user ${userId}:`, + error + ); return { success: false, error: error.message }; } } - + /** * Получает все идентификаторы пользователя * @param {number} userId - ID пользователя @@ -129,12 +147,12 @@ class IdentityService { logger.warn('[IdentityService] Missing userId parameter'); return []; } - + const result = await db.query( `SELECT provider, provider_id FROM user_identities WHERE user_id = $1`, [userId] ); - + logger.info(`[IdentityService] Found ${result.rows.length} identities for user ${userId}`); return result.rows; } catch (error) { @@ -142,7 +160,7 @@ class IdentityService { return []; } } - + /** * Получает все идентификаторы пользователя определенного типа * @param {number} userId - ID пользователя @@ -155,20 +173,25 @@ class IdentityService { logger.warn(`[IdentityService] Missing parameters: userId=${userId}, provider=${provider}`); return []; } - + const result = await db.query( `SELECT provider_id FROM user_identities WHERE user_id = $1 AND provider = $2`, [userId, provider] ); - - logger.info(`[IdentityService] Found ${result.rows.length} ${provider} identities for user ${userId}`); - return result.rows.map(row => row.provider_id); + + logger.info( + `[IdentityService] Found ${result.rows.length} ${provider} identities for user ${userId}` + ); + return result.rows.map((row) => row.provider_id); } catch (error) { - logger.error(`[IdentityService] Error getting ${provider} identities for user ${userId}:`, error); + logger.error( + `[IdentityService] Error getting ${provider} identities for user ${userId}:`, + error + ); return []; } } - + /** * Находит пользователя по идентификатору * @param {string} provider - Тип идентификатора @@ -178,34 +201,43 @@ class IdentityService { async findUserByIdentity(provider, providerId) { try { if (!provider || !providerId) { - logger.warn(`[IdentityService] Missing parameters: provider=${provider}, providerId=${providerId}`); + logger.warn( + `[IdentityService] Missing parameters: provider=${provider}, providerId=${providerId}` + ); return null; } - + // Нормализуем значения - const { provider: normalizedProvider, providerId: normalizedProviderId } = + const { provider: normalizedProvider, providerId: normalizedProviderId } = this.normalizeIdentity(provider, providerId); - + const result = await db.query( `SELECT u.id, u.role FROM users u JOIN user_identities ui ON u.id = ui.user_id WHERE ui.provider = $1 AND ui.provider_id = $2`, [normalizedProvider, normalizedProviderId] ); - + if (result.rows.length === 0) { - logger.info(`[IdentityService] No user found with identity ${normalizedProvider}:${normalizedProviderId}`); + logger.info( + `[IdentityService] No user found with identity ${normalizedProvider}:${normalizedProviderId}` + ); return null; } - - logger.info(`[IdentityService] Found user ${result.rows[0].id} with identity ${normalizedProvider}:${normalizedProviderId}`); + + logger.info( + `[IdentityService] Found user ${result.rows[0].id} with identity ${normalizedProvider}:${normalizedProviderId}` + ); return result.rows[0]; } catch (error) { - logger.error(`[IdentityService] Error finding user by identity ${provider}:${providerId}:`, error); + logger.error( + `[IdentityService] Error finding user by identity ${provider}:${providerId}:`, + error + ); return null; } } - + /** * Сохраняет идентификаторы из сессии для пользователя * @param {object} session - Объект сессии @@ -218,25 +250,30 @@ class IdentityService { logger.warn(`[IdentityService] Missing parameters: session=${!!session}, userId=${userId}`); return { success: false, error: 'Missing required parameters' }; } - + const results = []; - + // Сохраняем все постоянные идентификаторы из сессии if (session.email) { const emailResult = await this.saveIdentity(userId, 'email', session.email, true); results.push({ type: 'email', result: emailResult }); } - + if (session.address) { const walletResult = await this.saveIdentity(userId, 'wallet', session.address, true); results.push({ type: 'wallet', result: walletResult }); } - + if (session.telegramId) { - const telegramResult = await this.saveIdentity(userId, 'telegram', session.telegramId, true); + const telegramResult = await this.saveIdentity( + userId, + 'telegram', + session.telegramId, + true + ); results.push({ type: 'telegram', result: telegramResult }); } - + // Сохраняем гостевые идентификаторы в guest_user_mapping if (session.guestId) { try { @@ -250,7 +287,7 @@ class IdentityService { results.push({ type: 'guest', result: { success: false, error: error.message } }); } } - + if (session.previousGuestId && session.previousGuestId !== session.guestId) { try { await db.query( @@ -259,19 +296,27 @@ class IdentityService { ); results.push({ type: 'previousGuest', result: { success: true } }); } catch (error) { - logger.error(`[IdentityService] Error saving previous guest ID for user ${userId}:`, error); + logger.error( + `[IdentityService] Error saving previous guest ID for user ${userId}:`, + error + ); results.push({ type: 'previousGuest', result: { success: false, error: error.message } }); } } - - logger.info(`[IdentityService] Saved ${results.length} identities from session for user ${userId}`); + + logger.info( + `[IdentityService] Saved ${results.length} identities from session for user ${userId}` + ); return { success: true, results }; } catch (error) { - logger.error(`[IdentityService] Error saving identities from session for user ${userId}:`, error); + logger.error( + `[IdentityService] Error saving identities from session for user ${userId}:`, + error + ); return { success: false, error: error.message }; } } - + /** * Мигрирует все идентификаторы и сообщения от одного пользователя к другому * @param {number} fromUserId - ID исходного пользователя @@ -281,7 +326,9 @@ class IdentityService { async migrateUserData(fromUserId, toUserId) { try { if (!fromUserId || !toUserId) { - logger.warn(`[IdentityService] Missing parameters: fromUserId=${fromUserId}, toUserId=${toUserId}`); + logger.warn( + `[IdentityService] Missing parameters: fromUserId=${fromUserId}, toUserId=${toUserId}` + ); return { success: false, error: 'Missing required parameters' }; } @@ -295,7 +342,7 @@ class IdentityService { `SELECT provider, provider_id FROM user_identities WHERE user_id = $1`, [fromUserId] ); - + // Переносим каждый идентификатор for (const identity of identitiesResult.rows) { await client.query( @@ -304,7 +351,7 @@ class IdentityService { ON CONFLICT (provider, provider_id) DO NOTHING`, [toUserId, identity.provider, identity.provider_id] ); - + // Удаляем старый идентификатор await client.query( `DELETE FROM user_identities @@ -312,13 +359,13 @@ class IdentityService { [fromUserId, identity.provider, identity.provider_id] ); } - + // Мигрируем гостевые идентификаторы из новой таблицы guest_user_mapping const guestMappingsResult = await client.query( `SELECT guest_id, processed FROM guest_user_mapping WHERE user_id = $1`, [fromUserId] ); - + // Переносим каждый гостевой идентификатор for (const mapping of guestMappingsResult.rows) { await client.query( @@ -329,12 +376,9 @@ class IdentityService { [toUserId, mapping.guest_id, mapping.processed] ); } - + // Удаляем старые гостевые маппинги - await client.query( - `DELETE FROM guest_user_mapping WHERE user_id = $1`, - [fromUserId] - ); + await client.query(`DELETE FROM guest_user_mapping WHERE user_id = $1`, [fromUserId]); // Переносим все сообщения await client.query( @@ -351,7 +395,7 @@ class IdentityService { WHERE user_id = $2`, [toUserId, fromUserId] ); - + // Переносим настройки пользователя await client.query( `UPDATE user_preferences @@ -362,8 +406,10 @@ class IdentityService { // Завершаем транзакцию await client.query('COMMIT'); - - logger.info(`[IdentityService] Successfully migrated data from user ${fromUserId} to ${toUserId}`); + + logger.info( + `[IdentityService] Successfully migrated data from user ${fromUserId} to ${toUserId}` + ); return { success: true }; } catch (error) { await client.query('ROLLBACK'); @@ -386,20 +432,20 @@ class IdentityService { async findRelatedUsers(identities) { try { const userIds = new Set(); - + for (const [provider, providerId] of Object.entries(identities)) { if (!providerId) continue; - + const result = await db.query( `SELECT DISTINCT user_id FROM user_identities WHERE provider = $1 AND provider_id = $2`, [provider, providerId] ); - - result.rows.forEach(row => userIds.add(row.user_id)); + + result.rows.forEach((row) => userIds.add(row.user_id)); } - + return Array.from(userIds); } catch (error) { logger.error(`[IdentityService] Error finding related users:`, error); @@ -408,4 +454,4 @@ class IdentityService { } } -module.exports = new IdentityService(); \ No newline at end of file +module.exports = new IdentityService(); diff --git a/backend/services/index.js b/backend/services/index.js index 75e865b..7ed9379 100644 --- a/backend/services/index.js +++ b/backend/services/index.js @@ -31,5 +31,5 @@ module.exports = { getConversationHistory: aiAssistant.getConversationHistory, telegramBot, - aiAssistant + aiAssistant, }; diff --git a/backend/services/session-service.js b/backend/services/session-service.js index 4ad2aeb..50979a4 100644 --- a/backend/services/session-service.js +++ b/backend/services/session-service.js @@ -14,7 +14,7 @@ class SessionService { async saveSession(session) { try { return new Promise((resolve, reject) => { - session.save(err => { + session.save((err) => { if (err) { logger.error('Error saving session:', err); reject(err); @@ -38,7 +38,9 @@ class SessionService { */ async linkGuestMessages(session, userId) { try { - logger.info(`[linkGuestMessages] Starting for user ${userId} with guestId=${session.guestId}, previousGuestId=${session.previousGuestId}`); + logger.info( + `[linkGuestMessages] Starting for user ${userId} with guestId=${session.guestId}, previousGuestId=${session.previousGuestId}` + ); // Инициализируем массив обработанных гостевых ID, если его нет if (!session.processedGuestIds) { @@ -50,15 +52,15 @@ class SessionService { 'SELECT guest_id FROM guest_user_mapping WHERE user_id = $1', [userId] ); - const userGuestIds = guestIdsResult.rows.map(row => row.guest_id); + const userGuestIds = guestIdsResult.rows.map((row) => row.guest_id); // Собираем все гостевые ID, которые нужно обработать const guestIdsToProcess = new Set(); - + // Добавляем текущий гостевой ID if (session.guestId && !session.processedGuestIds.includes(session.guestId)) { guestIdsToProcess.add(session.guestId); - + // Записываем связь с пользователем в новую таблицу await db.query( 'INSERT INTO guest_user_mapping (user_id, guest_id) VALUES ($1, $2) ON CONFLICT (guest_id) DO UPDATE SET user_id = $1', @@ -69,7 +71,7 @@ class SessionService { // Добавляем предыдущий гостевой ID if (session.previousGuestId && !session.processedGuestIds.includes(session.previousGuestId)) { guestIdsToProcess.add(session.previousGuestId); - + // Записываем связь с пользователем в новую таблицу await db.query( 'INSERT INTO guest_user_mapping (user_id, guest_id) VALUES ($1, $2) ON CONFLICT (guest_id) DO UPDATE SET user_id = $1', @@ -88,12 +90,11 @@ class SessionService { for (const guestId of guestIdsToProcess) { await this.processGuestMessagesWrapper(userId, guestId); session.processedGuestIds.push(guestId); - + // Помечаем guestId как обработанный в базе данных - await db.query( - 'UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', - [guestId] - ); + await db.query('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [ + guestId, + ]); } // Сохраняем сессию @@ -114,7 +115,9 @@ class SessionService { */ async processGuestMessagesWrapper(userId, guestId) { try { - logger.info(`[processGuestMessagesWrapper] Processing messages: userId=${userId}, guestId=${guestId}`); + logger.info( + `[processGuestMessagesWrapper] Processing messages: userId=${userId}, guestId=${guestId}` + ); return await processGuestMessages(userId, guestId); } catch (error) { logger.error(`[processGuestMessagesWrapper] Error: ${error.message}`, error); @@ -146,7 +149,7 @@ class SessionService { async destroySession(session) { try { return new Promise((resolve, reject) => { - session.destroy(err => { + session.destroy((err) => { if (err) { logger.error('Error destroying session:', err); reject(err); @@ -173,29 +176,26 @@ class SessionService { logger.warn('[SessionService] Cannot restore session without sessionId'); return null; } - + logger.info(`[SessionService] Attempting to retrieve session ${sessionId}`); - - const result = await db.query( - 'SELECT sess FROM session WHERE sid = $1', - [sessionId] - ); - + + const result = await db.query('SELECT sess FROM session WHERE sid = $1', [sessionId]); + if (result.rows.length === 0) { logger.info(`[SessionService] No session found with ID ${sessionId}`); return null; } - + const sessionData = result.rows[0].sess; logger.info(`[SessionService] Retrieved session data for ${sessionId}`); - + return sessionData; } catch (error) { logger.error(`[SessionService] Error retrieving session ${sessionId}:`, error); return null; } } - + /** * Обновляет данные аутентификации в сессии * @param {object} session - Объект сессии @@ -208,23 +208,23 @@ class SessionService { logger.warn('[SessionService] Missing parameters for updateAuthData'); return false; } - + const { userId, authType, isAdmin, ...otherData } = authData; - + if (!userId || !authType) { logger.warn('[SessionService] Missing userId or authType in authData'); return false; } - + // Обновляем основные поля аутентификации session.userId = userId; session.authType = authType; session.authenticated = true; - + if (isAdmin !== undefined) { session.isAdmin = isAdmin; } - + // Обновляем дополнительные данные в зависимости от типа аутентификации if (authType === 'wallet' && otherData.address) { session.address = otherData.address.toLowerCase(); @@ -235,16 +235,16 @@ class SessionService { if (otherData.telegramUsername) session.telegramUsername = otherData.telegramUsername; if (otherData.telegramFirstName) session.telegramFirstName = otherData.telegramFirstName; } - + // Сохраняем гостевые ID, если они предоставлены и не были ранее в сессии if (otherData.guestId && !session.guestId) { session.guestId = otherData.guestId; } - + if (otherData.previousGuestId && !session.previousGuestId) { session.previousGuestId = otherData.previousGuestId; } - + // Сохраняем обновленную сессию return await this.saveSession(session, 'updateAuthData'); } catch (error) { @@ -252,7 +252,7 @@ class SessionService { return false; } } - + /** * Очищает данные аутентификации в сессии * @param {object} session - Объект сессии @@ -264,10 +264,10 @@ class SessionService { logger.warn('[SessionService] Cannot logout null session'); return false; } - + // Сохраняем гостевые ID перед очисткой const guestId = session.guestId; - + // Удаляем данные аутентификации delete session.userId; delete session.authenticated; @@ -278,12 +278,12 @@ class SessionService { delete session.telegramId; delete session.telegramUsername; delete session.telegramFirstName; - + // Восстанавливаем гостевой ID для продолжения работы if (guestId) { session.guestId = guestId; } - + // Сохраняем обновленную сессию return await this.saveSession(session, 'logout'); } catch (error) { @@ -294,4 +294,4 @@ class SessionService { } const sessionService = new SessionService(); -module.exports = sessionService; \ No newline at end of file +module.exports = sessionService; diff --git a/backend/services/telegramBot.js b/backend/services/telegramBot.js index e223fa2..2d53b73 100644 --- a/backend/services/telegramBot.js +++ b/backend/services/telegramBot.js @@ -20,7 +20,7 @@ async function getBot() { // Обработка кодов верификации botInstance.on('text', async (ctx) => { const code = ctx.message.text.trim(); - + try { // Получаем код верификации для всех активных кодов с провайдером telegram const codeResult = await db.query( @@ -31,25 +31,24 @@ async function getBot() { AND expires_at > NOW()`, [code] ); - + if (codeResult.rows.length === 0) { ctx.reply('Неверный код подтверждения'); return; } - + const verification = codeResult.rows[0]; const providerId = verification.provider_id; - const linkedUserId = verification.user_id; // Получаем связанный userId если он есть + const linkedUserId = verification.user_id; // Получаем связанный userId если он есть let userId; - + // Отмечаем код как использованный - await db.query( - 'UPDATE verification_codes SET used = true WHERE id = $1', - [verification.id] - ); - + await db.query('UPDATE verification_codes SET used = true WHERE id = $1', [ + verification.id, + ]); + logger.info('Starting Telegram auth process for code:', code); - + // Проверяем, существует ли уже пользователь с таким Telegram ID const existingTelegramUser = await db.query( `SELECT ui.user_id @@ -57,7 +56,7 @@ async function getBot() { WHERE ui.provider = 'telegram' AND ui.provider_id = $1`, [ctx.from.id.toString()] ); - + if (existingTelegramUser.rows.length > 0) { // Если пользователь с таким Telegram ID уже существует, используем его userId = existingTelegramUser.rows[0].user_id; @@ -74,7 +73,9 @@ async function getBot() { VALUES ($1, $2, $3, NOW())`, [userId, 'telegram', ctx.from.id.toString()] ); - logger.info(`Linked Telegram account ${ctx.from.id} to pre-authenticated user ${userId}`); + logger.info( + `Linked Telegram account ${ctx.from.id} to pre-authenticated user ${userId}` + ); } else { // Проверяем, есть ли пользователь, связанный с гостевым идентификатором let existingUserWithGuestId = null; @@ -85,10 +86,12 @@ async function getBot() { ); if (guestUserResult.rows.length > 0) { existingUserWithGuestId = guestUserResult.rows[0].user_id; - logger.info(`Found existing user ${existingUserWithGuestId} by guest ID ${providerId}`); + logger.info( + `Found existing user ${existingUserWithGuestId} by guest ID ${providerId}` + ); } } - + if (existingUserWithGuestId) { // Используем существующего пользователя и добавляем ему Telegram идентификатор userId = existingUserWithGuestId; @@ -106,7 +109,7 @@ async function getBot() { ['user'] ); userId = userResult.rows[0].id; - + // Связываем Telegram с новым пользователем await db.query( `INSERT INTO user_identities @@ -114,7 +117,7 @@ async function getBot() { VALUES ($1, $2, $3, NOW())`, [userId, 'telegram', ctx.from.id.toString()] ); - + // Если был гостевой ID, связываем его с новым пользователем if (providerId) { await db.query( @@ -125,12 +128,12 @@ async function getBot() { [userId, providerId] ); } - + logger.info(`Created new user ${userId} with Telegram account ${ctx.from.id}`); } } } - + // Обновляем сессию в базе данных await db.query( `UPDATE session @@ -140,23 +143,22 @@ async function getBot() { JSON.stringify({ userId: userId.toString(), authenticated: true, - authType: "telegram", - telegramId: ctx.from.id.toString() + authType: 'telegram', + telegramId: ctx.from.id.toString(), }), - JSON.stringify({guestId: providerId}) + JSON.stringify({ guestId: providerId }), ] ); - + // Отправляем сообщение об успешной аутентификации await ctx.reply('Аутентификация успешна! Можете вернуться в приложение.'); - + // Удаляем сообщение с кодом try { await ctx.deleteMessage(ctx.message.message_id); } catch (error) { logger.warn('Could not delete code message:', error); } - } catch (error) { logger.error('Error in Telegram auth:', error); await ctx.reply('Произошла ошибка при аутентификации. Попробуйте позже.'); @@ -166,7 +168,7 @@ async function getBot() { // Запускаем бота await botInstance.launch(); } - + return botInstance; } @@ -190,12 +192,12 @@ async function initTelegramAuth(session) { // Используем временный идентификатор для создания кода верификации // Реальный пользователь будет создан или найден при проверке кода через бота const tempId = crypto.randomBytes(16).toString('hex'); - + // Если пользователь уже авторизован, сохраняем его userId в guest_user_mapping // чтобы потом при авторизации через бота этот пользователь был найден if (session && session.authenticated && session.userId) { const guestId = session.guestId || tempId; - + // Связываем гостевой ID с текущим пользователем await db.query( `INSERT INTO guest_user_mapping (user_id, guest_id) @@ -203,22 +205,26 @@ async function initTelegramAuth(session) { ON CONFLICT (guest_id) DO UPDATE SET user_id = $1`, [session.userId, guestId] ); - - logger.info(`[initTelegramAuth] Linked guestId ${guestId} to authenticated user ${session.userId}`); + + logger.info( + `[initTelegramAuth] Linked guestId ${guestId} to authenticated user ${session.userId}` + ); } - + // Создаем код через сервис верификации с идентификатором const code = await verificationService.createVerificationCode( 'telegram', session.guestId || tempId, session.authenticated ? session.userId : null ); - - logger.info(`[initTelegramAuth] Created verification code for guestId: ${session.guestId || tempId}${session.authenticated ? `, userId: ${session.userId}` : ''}`); - + + logger.info( + `[initTelegramAuth] Created verification code for guestId: ${session.guestId || tempId}${session.authenticated ? `, userId: ${session.userId}` : ''}` + ); + return { verificationCode: code, - botLink: `https://t.me/${process.env.TELEGRAM_BOT_USERNAME}` + botLink: `https://t.me/${process.env.TELEGRAM_BOT_USERNAME}`, }; } catch (error) { logger.error('Error initializing Telegram auth:', error); @@ -229,5 +235,5 @@ async function initTelegramAuth(session) { module.exports = { getBot, stopBot, - initTelegramAuth -}; \ No newline at end of file + initTelegramAuth, +}; diff --git a/backend/services/verification-service.js b/backend/services/verification-service.js index dcded02..914c586 100644 --- a/backend/services/verification-service.js +++ b/backend/services/verification-service.js @@ -9,7 +9,10 @@ class VerificationService { // Генерация кода generateCode() { - const code = Math.random().toString(36).substring(2, 2 + this.codeLength).toUpperCase(); + const code = Math.random() + .toString(36) + .substring(2, 2 + this.codeLength) + .toUpperCase(); logger.info(`Generated verification code: ${code}`); return code; } @@ -20,8 +23,10 @@ class VerificationService { const expiresAt = new Date(Date.now() + this.expirationMinutes * 60 * 1000); try { - logger.info(`Creating verification code for ${provider}:${providerId}, userId: ${userId || 'null'}`); - + logger.info( + `Creating verification code for ${provider}:${providerId}, userId: ${userId || 'null'}` + ); + // Если userId не указан, добавляем запись без ссылки на пользователя if (userId === null || userId === undefined) { await db.query( @@ -46,7 +51,7 @@ class VerificationService { error: error.message, provider, providerId, - userId + userId, }); throw error; } @@ -56,11 +61,11 @@ class VerificationService { async verifyCode(code, provider, providerId) { try { logger.info(`Verifying code for ${provider}:${providerId}`); - + // Преобразуем код в верхний регистр для сравнения const normalizedCode = code.toUpperCase(); logger.info(`Normalized code: ${normalizedCode}`); - + // Проверим, есть ли такой код в базе (для отладки) const checkResult = await db.query( `SELECT code FROM verification_codes @@ -70,13 +75,15 @@ class VerificationService { AND expires_at > NOW()`, [provider, providerId] ); - + if (checkResult.rows.length > 0) { - logger.info(`Found codes for ${provider}:${providerId}: ${JSON.stringify(checkResult.rows.map(r => r.code))}`); + logger.info( + `Found codes for ${provider}:${providerId}: ${JSON.stringify(checkResult.rows.map((r) => r.code))}` + ); } else { logger.warn(`No active codes found for ${provider}:${providerId}`); } - + const result = await db.query( `SELECT * FROM verification_codes WHERE code = $1 @@ -88,30 +95,29 @@ class VerificationService { ); if (result.rows.length === 0) { - logger.warn(`Invalid or expired code for ${provider}:${providerId}. Input: ${normalizedCode}`); + logger.warn( + `Invalid or expired code for ${provider}:${providerId}. Input: ${normalizedCode}` + ); return { success: false, error: 'Неверный или истекший код' }; } const verification = result.rows[0]; // Отмечаем код как использованный - await db.query( - 'UPDATE verification_codes SET used = true WHERE id = $1', - [verification.id] - ); + await db.query('UPDATE verification_codes SET used = true WHERE id = $1', [verification.id]); logger.info(`Code verified successfully for ${provider}:${providerId}`); return { success: true, userId: verification.user_id, - providerId: verification.provider_id + providerId: verification.provider_id, }; } catch (error) { logger.error('Error verifying code:', { error: error.message, code, provider, - providerId + providerId, }); throw error; } @@ -131,4 +137,4 @@ class VerificationService { } const verificationService = new VerificationService(); -module.exports = verificationService; \ No newline at end of file +module.exports = verificationService; diff --git a/backend/utils/constants.js b/backend/utils/constants.js index 0443eef..2000c12 100644 --- a/backend/utils/constants.js +++ b/backend/utils/constants.js @@ -1,28 +1,28 @@ // Роли пользователей const USER_ROLES = { USER: 1, - ADMIN: 2 + ADMIN: 2, }; // Типы идентификаторов const IDENTITY_TYPES = { WALLET: 'wallet', EMAIL: 'email', - TELEGRAM: 'telegram' + TELEGRAM: 'telegram', }; // Каналы сообщений const MESSAGE_CHANNELS = { WEB: 'web', TELEGRAM: 'telegram', - EMAIL: 'email' + EMAIL: 'email', }; // Типы отправителей сообщений const SENDER_TYPES = { USER: 'user', AI: 'ai', - ADMIN: 'admin' + ADMIN: 'admin', }; // Коды ошибок @@ -31,20 +31,20 @@ const ERROR_CODES = { FORBIDDEN: 'forbidden', NOT_FOUND: 'not_found', INTERNAL_ERROR: 'internal_error', - BAD_REQUEST: 'bad_request' + BAD_REQUEST: 'bad_request', }; // Настройки сессии const SESSION_CONFIG = { COOKIE_MAX_AGE: 24 * 60 * 60 * 1000, // 24 часа COOKIE_SECURE: process.env.NODE_ENV === 'production', - COOKIE_SAME_SITE: 'lax' + COOKIE_SAME_SITE: 'lax', }; // Настройки API const API_CONFIG = { RATE_LIMIT: 100, // запросов в минуту - TIMEOUT: 30000 // 30 секунд + TIMEOUT: 30000, // 30 секунд }; module.exports = { @@ -54,5 +54,5 @@ module.exports = { SENDER_TYPES, ERROR_CODES, SESSION_CONFIG, - API_CONFIG -}; \ No newline at end of file + API_CONFIG, +}; diff --git a/backend/utils/error.js b/backend/utils/error.js index c170f54..d9c4fb3 100644 --- a/backend/utils/error.js +++ b/backend/utils/error.js @@ -10,4 +10,4 @@ function createError(message, statusCode = 500) { return error; } -module.exports = { createError }; \ No newline at end of file +module.exports = { createError }; diff --git a/backend/utils/helpers.js b/backend/utils/helpers.js index edc66d0..c74b365 100644 --- a/backend/utils/helpers.js +++ b/backend/utils/helpers.js @@ -37,7 +37,8 @@ async function addUserIdentity(userId, provider, providerId) { ); return true; } catch (error) { - if (error.code === '23505') { // Уникальное ограничение нарушено + if (error.code === '23505') { + // Уникальное ограничение нарушено return false; } throw error; @@ -49,5 +50,5 @@ module.exports = { isValidEmail, generateVerificationCode, checkUserIdentity, - addUserIdentity + addUserIdentity, }; diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..eaa4d5a --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,4936 @@ +{ + "name": "frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.1.0", + "dependencies": { + "axios": "^1.8.4", + "buffer": "^6.0.3", + "connect-pg-simple": "^10.0.0", + "dompurify": "^3.2.4", + "ethers": "6.13.5", + "marked": "^15.0.7", + "siwe": "^2.1.4", + "sortablejs": "^1.15.6", + "vue": "^3.2.47", + "vue-i18n": "^11.1.2", + "vue-router": "^4.1.6" + }, + "devDependencies": { + "@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", + "postcss-html": "^1.8.0", + "prettier": "^3.5.3", + "rollup": "^3.29.4", + "rollup-plugin-polyfill-node": "^0.12.0", + "stylelint": "^14.16.1", + "stylelint-config-prettier": "^9.0.5", + "stylelint-config-standard-vue": "^1.0.0", + "vite": "^6.2.3" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "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==", + "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==", + "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==", + "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==", + "dependencies": { + "@babel/helper-string-parser": "^7.25.9", + "@babel/helper-validator-identifier": "^7.25.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.2.0.tgz", + "integrity": "sha512-+OJ9konv95ClSTOJCmMZqpd5+YGsB2S+x6w3E1oaM8UuR5j8nTNHYSz8c9BEPGDOCMQYIEEGlVPj/VY64iTbGw==", + "dev": true, + "engines": { + "node": "^14 || ^16 || >=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss-selector-parser": "^6.0.10" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", + "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", + "dev": true, + "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" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.21.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz", + "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", + "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", + "dev": true, + "dependencies": { + "@eslint/core": "^0.12.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@intlify/core-base": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.1.2.tgz", + "integrity": "sha512-nmG512G8QOABsserleechwHGZxzKSAlggGf9hQX0nltvSwyKNVuB/4o6iFeG2OnjXK253r8p8eSDOZf8PgFdWw==", + "dependencies": { + "@intlify/message-compiler": "11.1.2", + "@intlify/shared": "11.1.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/message-compiler": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.1.2.tgz", + "integrity": "sha512-T/xbNDzi+Yv0Qn2Dfz2CWCAJiwNgU5d95EhhAEf4YmOgjCKktpfpiUSmLcBvK1CtLpPQ85AMMQk/2NCcXnNj1g==", + "dependencies": { + "@intlify/shared": "11.1.2", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "node_modules/@intlify/shared": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.1.2.tgz", + "integrity": "sha512-dF2iMMy8P9uKVHV/20LA1ulFLL+MKSbfMiixSmn6fpwqzvix38OIc7ebgnFbBqElvghZCW9ACtzKTGKsTGTWGA==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + } + }, + "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==" + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "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, + "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, + "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/@rollup/rollup-android-arm-eabi": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.9.tgz", + "integrity": "sha512-qZdlImWXur0CFakn2BJ2znJOdqYZKiedEPEVNTBrpfPjc/YuTGcaYZcdmNFTkUj3DU0ZM/AElcM8Ybww3xVLzA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.9.tgz", + "integrity": "sha512-4KW7P53h6HtJf5Y608T1ISKvNIYLWRKMvfnG0c44M6In4DQVU58HZFEVhWINDZKp7FZps98G3gxwC1sb0wXUUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.9.tgz", + "integrity": "sha512-0CY3/K54slrzLDjOA7TOjN1NuLKERBgk9nY5V34mhmuu673YNb+7ghaDUs6N0ujXR7fz5XaS5Aa6d2TNxZd0OQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.9.tgz", + "integrity": "sha512-eOojSEAi/acnsJVYRxnMkPFqcxSMFfrw7r2iD9Q32SGkb/Q9FpUY1UlAu1DH9T7j++gZ0lHjnm4OyH2vCI7l7Q==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.9.tgz", + "integrity": "sha512-2lzjQPJbN5UnHm7bHIUKFMulGTQwdvOkouJDpPysJS+QFBGDJqcfh+CxxtG23Ik/9tEvnebQiylYoazFMAgrYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.9.tgz", + "integrity": "sha512-SLl0hi2Ah2H7xQYd6Qaiu01kFPzQ+hqvdYSoOtHYg/zCIFs6t8sV95kaoqjzjFwuYQLtOI0RZre/Ke0nPaQV+g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.9.tgz", + "integrity": "sha512-88I+D3TeKItrw+Y/2ud4Tw0+3CxQ2kLgu3QvrogZ0OfkmX/DEppehus7L3TS2Q4lpB+hYyxhkQiYPJ6Mf5/dPg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.9.tgz", + "integrity": "sha512-3qyfWljSFHi9zH0KgtEPG4cBXHDFhwD8kwg6xLfHQ0IWuH9crp005GfoUUh/6w9/FWGBwEHg3lxK1iHRN1MFlA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.9.tgz", + "integrity": "sha512-6TZjPHjKZUQKmVKMUowF3ewHxctrRR09eYyvT5eFv8w/fXarEra83A2mHTVJLA5xU91aCNOUnM+DWFMSbQ0Nxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.9.tgz", + "integrity": "sha512-LD2fytxZJZ6xzOKnMbIpgzFOuIKlxVOpiMAXawsAZ2mHBPEYOnLRK5TTEsID6z4eM23DuO88X0Tq1mErHMVq0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.9.tgz", + "integrity": "sha512-dRAgTfDsn0TE0HI6cmo13hemKpVHOEyeciGtvlBTkpx/F65kTvShtY/EVyZEIfxFkV5JJTuQ9tP5HGBS0hfxIg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.9.tgz", + "integrity": "sha512-PHcNOAEhkoMSQtMf+rJofwisZqaU8iQ8EaSps58f5HYll9EAY5BSErCZ8qBDMVbq88h4UxaNPlbrKqfWP8RfJA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.9.tgz", + "integrity": "sha512-Z2i0Uy5G96KBYKjeQFKbbsB54xFOL5/y1P5wNBsbXB8yE+At3oh0DVMjQVzCJRJSfReiB2tX8T6HUFZ2k8iaKg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.9.tgz", + "integrity": "sha512-U+5SwTMoeYXoDzJX5dhDTxRltSrIax8KWwfaaYcynuJw8mT33W7oOgz0a+AaXtGuvhzTr2tVKh5UO8GVANTxyQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.9.tgz", + "integrity": "sha512-FwBHNSOjUTQLP4MG7y6rR6qbGw4MFeQnIBrMe161QGaQoBQLqSUEKlHIiVgF3g/mb3lxlxzJOpIBhaP+C+KP2A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.9.tgz", + "integrity": "sha512-cYRpV4650z2I3/s6+5/LONkjIz8MBeqrk+vPXV10ORBnshpn8S32bPqQ2Utv39jCiDcO2eJTuSlPXpnvmaIgRA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.9.tgz", + "integrity": "sha512-z4mQK9dAN6byRA/vsSgQiPeuO63wdiDxZ9yg9iyX2QTzKuQM7T4xlBoeUP/J8uiFkqxkcWndWi+W7bXdPbt27Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.9.tgz", + "integrity": "sha512-KB48mPtaoHy1AwDNkAJfHXvHp24H0ryZog28spEs0V48l3H1fr4i37tiyHsgKZJnCmvxsbATdZGBpbmxTE3a9w==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.9.tgz", + "integrity": "sha512-AyleYRPU7+rgkMWbEh71fQlrzRfeP6SyMnRf9XX4fCdDPAJumdSBqYEcWPMzVQ4ScAl7E4oFfK0GUVn77xSwbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "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==", + "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==", + "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==" + }, + "node_modules/@stablelib/random": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@stablelib/random/-/random-1.0.2.tgz", + "integrity": "sha512-rIsE83Xpb7clHPVRlBj8qNe5L8ISQOzjghYQm/dZ7VaM2KHYwMW5adjQjrzTZCchFnNCNhkwtnOBa9HTMJCI8w==", + "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==" + }, + "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 + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true + }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true + }, + "node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "dev": true + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "optional": true + }, + "node_modules/@vitejs/plugin-vue": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.2.1.tgz", + "integrity": "sha512-cxh314tzaWwOLqVes2gnnCtvBDcM1UMdn+iFR+UjAn411dPT3tOmqrJjbMd7koZpMAmBM/GqeV4n9ge7JSiJJQ==", + "dev": true, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "vite": "^5.0.0 || ^6.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==", + "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==", + "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==", + "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==", + "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==" + }, + "node_modules/@vue/eslint-config-prettier": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz", + "integrity": "sha512-GL3YBLwv/+b86yHcNNfPJxOTtVFJ4Mbc9UU3zR+KVoG7SwGTjPT+32fXamscNumElhcpXW3mT0DgzS9w32S7Bw==", + "dev": true, + "dependencies": { + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.2" + }, + "peerDependencies": { + "eslint": ">= 8.21.0", + "prettier": ">= 3.0.0" + } + }, + "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==", + "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==", + "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==", + "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==", + "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==" + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==" + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "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" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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==" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "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/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "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" + } + ] + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "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" + } + ], + "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==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "dev": true + }, + "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==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "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/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-functions-list": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.3.tgz", + "integrity": "sha512-IQOkD3hbR5KrN93MtcYuad6YPuTSUhntLHDuLEbFWE+ff2/XSZNdZG+LcbbIW5AXKg/WFIfYItIzVoHngHXZzA==", + "dev": true, + "engines": { + "node": ">=12 || >=16" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/dompurify": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", + "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "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==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.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==", + "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==", + "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==", + "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==", + "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.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@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" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.21.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz", + "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==", + "dev": true, + "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" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.2.tgz", + "integrity": "sha512-1105/17ZIMjmCOJOPNfVdbXafLCLj3hPmkmB7dLgt7XsQ/zkxSuDerE/xgO3RxoHysR1N1whmquY0lSn2O0VLg==", + "dev": true, + "bin": { + "eslint-config-prettier": "build/bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", + "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==", + "dev": true, + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue": { + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.32.0.tgz", + "integrity": "sha512-b/Y05HYmnB/32wqVcjxjHZzNpwxj1onBOvqW89W+V+XNG1dRuaFbNd3vT9CLbr2LXjEoq+3vn8DanWf7XU22Ug==", + "dev": true, + "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" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-vue/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "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==" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ethers": { + "version": "6.13.5", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.5.tgz", + "integrity": "sha512-+knKNieu5EKRThQJWwqaJ10a6HE9sSehGeqWN65//wE7j47ZpFhKAnHB/JJFibwwg61I/koxaPsXbXpD/skNOQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "22.7.5", + "aes-js": "4.0.0-beta.5", + "tslib": "2.7.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.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/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fast-uri": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", + "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "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" + } + ], + "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==", + "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/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "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==", + "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==", + "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==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/globals": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.0.0.tgz", + "integrity": "sha512-iInW14XItCXET01CQFqudPOWP2jYMl7T+QRQT+UNcR/iQncN/F0UNpgd76iFkBPgNQb4+X3LV9tLJYzwh+Gl3A==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globjoin": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", + "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==", + "dev": true + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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==", + "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==", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-tags": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.3.1.tgz", + "integrity": "sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "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" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-lazy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz", + "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "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/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/known-css-properties": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.26.0.tgz", + "integrity": "sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==", + "dev": true + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/marked": { + "version": "15.0.7", + "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.7.tgz", + "integrity": "sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "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==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mathml-tag-names": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz", + "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dev": true, + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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==", + "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==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "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" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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==" + }, + "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, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "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" + } + ], + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-html": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/postcss-html/-/postcss-html-1.8.0.tgz", + "integrity": "sha512-5mMeb1TgLWoRKxZ0Xh9RZDfwUUIqRrcxO2uXO+Ezl1N5lqpCiSU5Gk6+1kZediBfBHFtPCdopr2UZ2SgUsKcgQ==", + "dev": true, + "dependencies": { + "htmlparser2": "^8.0.0", + "js-tokens": "^9.0.0", + "postcss": "^8.5.0", + "postcss-safe-parser": "^6.0.0" + }, + "engines": { + "node": "^12 || >=14" + } + }, + "node_modules/postcss-media-query-parser": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz", + "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==", + "dev": true + }, + "node_modules/postcss-resolve-nested-selector": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.6.tgz", + "integrity": "sha512-0sglIs9Wmkzbr8lQwEyIzlDOOC9bGmfVKcJTaxv3vMmd3uo4o4DerC3En0bnmgceeql9BfC8hRkp7cg0fjdVqw==", + "dev": true + }, + "node_modules/postcss-safe-parser": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/postcss-safe-parser/-/postcss-safe-parser-6.0.0.tgz", + "integrity": "sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==", + "dev": true, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.3.3" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "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/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.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==" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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" + } + ] + }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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, + "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, + "dependencies": { + "@rollup/plugin-inject": "^5.0.1" + }, + "peerDependencies": { + "rollup": "^1.20.0 || ^2.0.0 || ^3.0.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/siwe": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/siwe/-/siwe-2.3.2.tgz", + "integrity": "sha512-aSf+6+Latyttbj5nMu6GF3doMfv2UYj83hhwZgUF20ky6fTS83uVhkQABdIVnEuS8y1bBdk7p6ltb9SmlhTTlA==", + "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/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/sortablejs": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.6.tgz", + "integrity": "sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==" + }, + "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==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true + }, + "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/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/style-search": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/style-search/-/style-search-0.1.0.tgz", + "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==", + "dev": true + }, + "node_modules/stylelint": { + "version": "14.16.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.16.1.tgz", + "integrity": "sha512-ErlzR/T3hhbV+a925/gbfc3f3Fep9/bnspMiJPorfGEmcBbXdS+oo6LrVtoUZ/w9fqD6o6k7PtUlCOsCRdjX/A==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.2", + "balanced-match": "^2.0.0", + "colord": "^2.9.3", + "cosmiconfig": "^7.1.0", + "css-functions-list": "^3.1.0", + "debug": "^4.3.4", + "fast-glob": "^3.2.12", + "fastest-levenshtein": "^1.0.16", + "file-entry-cache": "^6.0.1", + "global-modules": "^2.0.0", + "globby": "^11.1.0", + "globjoin": "^0.1.4", + "html-tags": "^3.2.0", + "ignore": "^5.2.1", + "import-lazy": "^4.0.0", + "imurmurhash": "^0.1.4", + "is-plain-object": "^5.0.0", + "known-css-properties": "^0.26.0", + "mathml-tag-names": "^2.1.3", + "meow": "^9.0.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.19", + "postcss-media-query-parser": "^0.2.3", + "postcss-resolve-nested-selector": "^0.1.1", + "postcss-safe-parser": "^6.0.0", + "postcss-selector-parser": "^6.0.11", + "postcss-value-parser": "^4.2.0", + "resolve-from": "^5.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "style-search": "^0.1.0", + "supports-hyperlinks": "^2.3.0", + "svg-tags": "^1.0.0", + "table": "^6.8.1", + "v8-compile-cache": "^2.3.0", + "write-file-atomic": "^4.0.2" + }, + "bin": { + "stylelint": "bin/stylelint.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + } + }, + "node_modules/stylelint-config-html": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stylelint-config-html/-/stylelint-config-html-1.1.0.tgz", + "integrity": "sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==", + "dev": true, + "engines": { + "node": "^12 || >=14" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "postcss-html": "^1.0.0", + "stylelint": ">=14.0.0" + } + }, + "node_modules/stylelint-config-prettier": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/stylelint-config-prettier/-/stylelint-config-prettier-9.0.5.tgz", + "integrity": "sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA==", + "dev": true, + "bin": { + "stylelint-config-prettier": "bin/check.js", + "stylelint-config-prettier-check": "bin/check.js" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "stylelint": ">= 11.x < 15" + } + }, + "node_modules/stylelint-config-recommended-vue": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended-vue/-/stylelint-config-recommended-vue-1.6.0.tgz", + "integrity": "sha512-syk1adIHvbH2T1OiR/spUK4oQy35PZIDw8Zmc7E0+eVK9Z9SK3tdMpGRT/bgGnAPpMt/WaL9K1u0tlF6xM0sMQ==", + "dev": true, + "dependencies": { + "semver": "^7.3.5", + "stylelint-config-html": ">=1.0.0", + "stylelint-config-recommended": ">=6.0.0" + }, + "engines": { + "node": "^12 || >=14" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "postcss-html": "^1.0.0", + "stylelint": ">=14.0.0" + } + }, + "node_modules/stylelint-config-recommended-vue/node_modules/stylelint-config-recommended": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-16.0.0.tgz", + "integrity": "sha512-4RSmPjQegF34wNcK1e1O3Uz91HN8P1aFdFzio90wNK9mjgAI19u5vsU868cVZboKzCaa5XbpvtTzAAGQAxpcXA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.16.0" + } + }, + "node_modules/stylelint-config-standard-vue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard-vue/-/stylelint-config-standard-vue-1.0.0.tgz", + "integrity": "sha512-wAzU7p6DSlo04pWfCbOcaMq09Nojt0FEsbdxhCBTdC7IguD9ZVl7FP/bvyA0HAHjZGC4JkW7m6WiQaoVMDSuFw==", + "dev": true, + "dependencies": { + "stylelint-config-html": ">=1.0.0", + "stylelint-config-recommended-vue": ">=1.1.0", + "stylelint-config-standard": ">=24.0.0" + }, + "engines": { + "node": "^12 || >=14" + }, + "funding": { + "url": "https://github.com/sponsors/ota-meshi" + }, + "peerDependencies": { + "postcss-html": "^1.0.0", + "stylelint": ">=14.0.0" + } + }, + "node_modules/stylelint-config-standard-vue/node_modules/stylelint-config-standard": { + "version": "38.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-38.0.0.tgz", + "integrity": "sha512-uj3JIX+dpFseqd/DJx8Gy3PcRAJhlEZ2IrlFOc4LUxBX/PNMEQ198x7LCOE2Q5oT9Vw8nyc4CIL78xSqPr6iag==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "dependencies": { + "stylelint-config-recommended": "^16.0.0" + }, + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.18.0" + } + }, + "node_modules/stylelint-config-standard-vue/node_modules/stylelint-config-standard/node_modules/stylelint-config-recommended": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-16.0.0.tgz", + "integrity": "sha512-4RSmPjQegF34wNcK1e1O3Uz91HN8P1aFdFzio90wNK9mjgAI19u5vsU868cVZboKzCaa5XbpvtTzAAGQAxpcXA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/stylelint" + }, + { + "type": "github", + "url": "https://github.com/sponsors/stylelint" + } + ], + "engines": { + "node": ">=18.12.0" + }, + "peerDependencies": { + "stylelint": "^16.16.0" + } + }, + "node_modules/stylelint/node_modules/balanced-match": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-2.0.0.tgz", + "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", + "dev": true + }, + "node_modules/stylelint/node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/stylelint/node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/stylelint/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/svg-tags": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/svg-tags/-/svg-tags-1.0.0.tgz", + "integrity": "sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==", + "dev": true + }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/table/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/v8-compile-cache": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "dev": true + }, + "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/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/vite": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.3.tgz", + "integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==", + "dev": true, + "dependencies": { + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/rollup": { + "version": "4.34.9", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.9.tgz", + "integrity": "sha512-nF5XYqWWp9hx/LrpC8sZvvvmq0TeTjQgaZHYmAgwysT9nh8sWnZhBnM8ZyVbbJFIQBLwHDNoMqsBZBbUo4U8sQ==", + "dev": true, + "dependencies": { + "@types/estree": "1.0.6" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "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" + } + }, + "node_modules/vue": { + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.13.tgz", + "integrity": "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==", + "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-eslint-parser": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", + "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", + "dev": true, + "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" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-i18n": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.1.2.tgz", + "integrity": "sha512-MfdkdKGUHN+jkkaMT5Zbl4FpRmN7kfelJIwKoUpJ32ONIxdFhzxZiLTVaAXkAwvH3y9GmWpoiwjDqbPIkPIMFA==", + "dependencies": { + "@intlify/core-base": "11.1.2", + "@intlify/shared": "11.1.2", + "@vue/devtools-api": "^6.5.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/kazupon" + }, + "peerDependencies": { + "vue": "^3.0.0" + } + }, + "node_modules/vue-router": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz", + "integrity": "sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==", + "dependencies": { + "@vue/devtools-api": "^6.6.4" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "vue": "^3.2.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "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/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "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" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yaml": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz", + "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==", + "dev": true, + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json index e7aede2..51e644a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -9,6 +9,8 @@ "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/'", + "lint:style": "stylelint \"**/*.{vue,css}\"", + "lint:style:fix": "stylelint \"**/*.{vue,css}\" --fix", "format": "prettier --write \"**/*.{js,vue,json,md}\"", "format:check": "prettier --check \"**/*.{js,vue,json,md}\"" }, @@ -34,9 +36,13 @@ "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-vue": "^9.32.0", "globals": "^16.0.0", + "postcss-html": "^1.8.0", "prettier": "^3.5.3", "rollup": "^3.29.4", "rollup-plugin-polyfill-node": "^0.12.0", + "stylelint": "^14.16.1", + "stylelint-config-prettier": "^9.0.5", + "stylelint-config-standard-vue": "^1.0.0", "vite": "^6.2.3" } } diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue index 70059d5..074dbe8 100644 --- a/frontend/src/views/HomeView.vue +++ b/frontend/src/views/HomeView.vue @@ -634,11 +634,17 @@ const loadMessages = async (options = {}) => { const { silent = false, initial = false, authType = null } = options; + // Усиленная проверка для предотвращения параллельного выполнения + if (messageLoading.value.isInProgress) { + console.warn('[loadMessages] Выполнение уже идет, пропуск вызова.'); + return; + } + messageLoading.value.isInProgress = true; // Устанавливаем флаг НЕМЕДЛЕННО + if (messageLoading.value.isLoadingMore && !initial) return; try { messageLoading.value.isLoadingMore = true; - messageLoading.value.isInProgress = true; if (!silent) isLoading.value = true; console.log( @@ -732,8 +738,8 @@ removeFromStorage('guestId'); } } else if (response.data.messages && response.data.messages.length) { - // Иначе добавляем к существующим - messages.value = [...messages.value, ...response.data.messages]; + // Иначе добавляем к существующим (В НАЧАЛО МАССИВА) + messages.value = [...response.data.messages, ...messages.value]; } console.log(`Загружено ${messages.value.length} сообщений из истории`); diff --git a/frontend/stylelint.config.cjs b/frontend/stylelint.config.cjs new file mode 100644 index 0000000..d001029 --- /dev/null +++ b/frontend/stylelint.config.cjs @@ -0,0 +1,15 @@ +module.exports = { + extends: [ + // Используем стандартную конфигурацию для Vue (включает CSS и