ваше сообщение коммита

This commit is contained in:
2025-05-22 16:24:39 +03:00
parent 5e231e547d
commit 0c7b312aff
43 changed files with 9310 additions and 1295 deletions

View File

@@ -34,8 +34,9 @@ nano frontend/.env
4. Выполните миграции изнутри контейнера backend:
```
docker exec dapp-backend yarn migrate
```
docker exec -e NODE_ENV=migration dapp-backend yarn migrate
```
Скрипт автоматически:
- Проверит наличие Docker и Docker Compose

View File

@@ -2,12 +2,12 @@ const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const session = require('express-session');
const { sessionMiddleware } = require('./config/session');
const sessionConfig = require('./config/session');
const logger = require('./utils/logger');
// const csurf = require('csurf'); // Закомментировано, так как не используется
const { errorHandler } = require('./middleware/errorHandler');
const errorHandler = require('./middleware/errorHandler');
// const { version } = require('./package.json'); // Закомментировано, так как не используется
const pool = require('./db'); // Добавляем импорт pool
const db = require('./db'); // Добавляем импорт db
const aiAssistant = require('./services/ai-assistant'); // Добавляем импорт aiAssistant
const fs = require('fs');
const path = require('path');
@@ -48,6 +48,9 @@ const ensureDirectoriesExist = () => {
// Вызываем функцию проверки директорий при запуске сервера
ensureDirectoriesExist();
// Регистрируем коллбек для пересоздания session middleware при смене пула
db.setPoolChangeCallback(sessionConfig.reloadSessionMiddleware);
// Импорт маршрутов
const authRoutes = require('./routes/auth');
const usersRoutes = require('./routes/users');
@@ -79,8 +82,8 @@ app.use(
})
);
// Настройка сессии (ИСПОЛЬЗУЕМ ИМПОРТИРОВАННОЕ MIDDLEWARE)
app.use(sessionMiddleware);
// Настройка сессии (используем геттер, чтобы всегда был актуальный middleware)
app.use((req, res, next) => sessionConfig.sessionMiddleware(req, res, next));
// Добавим middleware для проверки сессии
app.use(async (req, res, next) => {
@@ -89,7 +92,7 @@ app.use(async (req, res, next) => {
// Проверяем сессию в базе данных
if (req.sessionID) {
const result = await pool.query('SELECT sess FROM session WHERE sid = $1', [req.sessionID]);
const result = await db.getQuery()('SELECT sess FROM session WHERE sid = $1', [req.sessionID]);
console.log('Session from DB:', result.rows[0]?.sess);
}
@@ -104,7 +107,7 @@ app.use(async (req, res, next) => {
const token = authHeader.split(' ')[1];
try {
// Находим пользователя по токену
const { rows } = await pool.query(
const { rows } = await db.getQuery(
`
SELECT u.id,
(u.role = 'admin') as is_admin,
@@ -152,7 +155,7 @@ app.use((req, res, next) => {
// Маршруты API
app.use('/api/auth', authRoutes);
app.use('/api/users', usersRoutes);
app.use('/api/identities', identitiesRoutes);
app.use('/api', identitiesRoutes);
app.use('/api/chat', chatRoutes);
app.use('/api/admin', adminRoutes);
app.use('/api/tokens', tokensRouter);
@@ -181,6 +184,8 @@ console.log('OPENAI_API_KEY:', redactedValue);
console.log('EMAIL_USER:', process.env.EMAIL_USER);
console.log('EMAIL_PASSWORD:', redactedValue);
console.log('typeof errorHandler:', typeof errorHandler, errorHandler.name);
// Добавляем обработчик ошибок последним
app.use(errorHandler);
@@ -188,7 +193,7 @@ app.use(errorHandler);
app.get('/api/health', async (req, res) => {
try {
// Проверяем подключение к БД
await pool.query('SELECT NOW()');
await db.getQuery('SELECT NOW()');
// Проверяем AI сервис
const aiStatus = await aiAssistant.checkHealth();
@@ -212,7 +217,7 @@ app.get('/api/health', async (req, res) => {
setInterval(
async () => {
try {
await pool.query('DELETE FROM session WHERE expire < NOW()');
await db.getQuery('DELETE FROM session WHERE expire < NOW()');
} catch (error) {
console.error('Error cleaning old sessions:', error);
}

View File

@@ -1,10 +1,19 @@
const session = require('express-session');
const pgSession = require('connect-pg-simple')(session);
const { pool } = require('../db');
const db = require('../db');
const sessionConfig = {
let onPoolChangeCallback = null;
function setPoolChangeCallback(cb) {
onPoolChangeCallback = cb;
}
let sessionMiddleware = createSessionMiddleware();
function createSessionMiddleware() {
return session({
store: new pgSession({
pool,
pool: db.getPool(),
tableName: 'session',
}),
secret: process.env.SESSION_SECRET || 'hb3atoken',
@@ -18,8 +27,20 @@ const sessionConfig = {
sameSite: 'lax',
path: '/',
},
};
});
}
function reloadSessionMiddleware() {
sessionMiddleware = createSessionMiddleware();
if (onPoolChangeCallback) {
onPoolChangeCallback();
}
}
module.exports = {
sessionMiddleware: session(sessionConfig),
get sessionMiddleware() {
return sessionMiddleware;
},
reloadSessionMiddleware,
setPoolChangeCallback,
};

View File

@@ -9,10 +9,14 @@ console.log('DB_PORT:', process.env.DB_PORT);
console.log('DB_NAME:', process.env.DB_NAME);
console.log('DB_USER:', process.env.DB_USER);
// Создаем пул соединений с базой данных
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
ssl: process.env.NODE_ENV === 'production' ? { rejectUnauthorized: false } : false,
// Первичное подключение по дефолтным значениям
let pool = new Pool({
host: process.env.DB_HOST || 'postgres',
port: parseInt(process.env.DB_PORT || '5432'),
database: process.env.DB_NAME || 'dapp_db',
user: process.env.DB_USER || 'dapp_user',
password: process.env.DB_PASSWORD,
ssl: false,
});
// Проверяем подключение к базе данных
@@ -21,36 +25,59 @@ pool.query('SELECT NOW()')
console.log('Успешное подключение к базе данных:', res.rows[0]);
})
.catch(err => {
console.error('Failed to connect to the database using DATABASE_URL:', err);
console.log('Attempting alternative database connection...');
// Пробуем альтернативное подключение
const altPool = new Pool({
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT || '5432'),
database: process.env.DB_NAME || 'dapp_db',
user: process.env.DB_USER || 'dapp_user',
password: process.env.DB_PASSWORD,
console.error('Ошибка подключения к базе данных:', err);
});
altPool.query('SELECT NOW()')
.then(altRes => {
console.log('Альтернативное подключение успешно:', altRes.rows[0]);
// Заменяем основной пул на альтернативный
module.exports.pool = altPool;
module.exports.query = (text, params) => altPool.query(text, params);
})
.catch(altErr => {
console.error('Альтернативное подключение тоже не удалось:', altErr);
console.log('Переключение на временное хранилище данных в памяти...');
module.exports = createInMemoryStorage();
});
});
console.log('Пул создан:', pool.options || pool);
// Функция для выполнения SQL-запросов
const query = (text, params) => {
return pool.query(text, params);
};
function getPool() {
return pool;
}
function getQuery() {
return pool.query.bind(pool);
}
let poolChangeCallback = null;
function setPoolChangeCallback(cb) {
poolChangeCallback = cb;
}
// Функция для пересоздания пула из db_settings
async function reinitPoolFromDbSettings() {
try {
const res = await pool.query('SELECT * FROM db_settings ORDER BY id LIMIT 1');
if (!res.rows.length) throw new Error('DB settings not found');
const settings = res.rows[0];
// Закрываем старый пул
await pool.end();
// Создаём новый пул
pool = new Pool({
host: settings.db_host,
port: parseInt(settings.db_port),
database: settings.db_name,
user: settings.db_user,
password: settings.db_password,
ssl: false,
});
// Пересоздаём session middleware
if (poolChangeCallback) {
poolChangeCallback();
}
console.log('Пул пересоздан с новыми параметрами:', settings);
} catch (err) {
console.error('Ошибка пересоздания пула:', err);
throw err;
}
}
// При старте приложения — сразу пробуем инициализировать из db_settings
if (process.env.NODE_ENV !== 'migration') {
reinitPoolFromDbSettings();
}
const query = (text, params) => pool.query(text, params);
// Функция для сохранения гостевого сообщения в базе данных
async function saveGuestMessageToDatabase(message, language, guestId) {
@@ -71,70 +98,9 @@ async function saveGuestMessageToDatabase(message, language, guestId) {
// Экспортируем функции для работы с базой данных
module.exports = {
query,
pool,
getPool,
getQuery,
reinitPoolFromDbSettings,
saveGuestMessageToDatabase,
setPoolChangeCallback,
};
// Функция для создания временного хранилища данных в памяти
function createInMemoryStorage() {
console.log('Используется временное хранилище данных в памяти');
const users = [];
let userId = 1;
// Эмуляция функции query для работы с пользователями
const inMemoryQuery = async (text, params) => {
console.log('SQL query (in-memory):', text, 'Params:', params);
// Эмуляция запроса SELECT * FROM users WHERE address = $1
if (text.includes('SELECT * FROM users WHERE address = $1')) {
const address = params[0];
const user = users.find((u) => u.address === address);
return { rows: user ? [user] : [] };
}
// Эмуляция запроса SELECT * FROM users WHERE email = $1
if (text.includes('SELECT * FROM users WHERE email = $1')) {
const email = params[0];
const user = users.find((u) => u.email === email);
return { rows: user ? [user] : [] };
}
// Эмуляция запроса INSERT INTO users
if (text.includes('INSERT INTO users')) {
let newUser;
if (text.includes('address')) {
newUser = { id: userId++, address: params[0], created_at: new Date(), is_admin: false };
} else if (text.includes('email')) {
newUser = { id: userId++, email: params[0], created_at: new Date(), is_admin: false };
}
if (newUser) {
users.push(newUser);
return { rows: [newUser] };
}
}
return { rows: [] };
};
return {
query: inMemoryQuery,
pool: {
query: (text, params, callback) => {
if (callback) {
try {
const result = inMemoryQuery(text, params);
callback(null, result);
} catch (err) {
callback(err);
}
} else {
return inMemoryQuery(text, params);
}
},
},
};
}

View File

@@ -1,21 +0,0 @@
const { Pool } = require('pg');
const logger = require('../utils/logger');
const pool = new Pool({
user: process.env.DB_USER || 'dapp_user',
host: process.env.DB_HOST || 'localhost',
database: process.env.DB_NAME || 'dapp_db',
password: process.env.DB_PASSWORD,
port: process.env.DB_PORT || 5432,
});
// Проверка подключения
pool.query('SELECT NOW()', (err, res) => {
if (err) {
logger.error('Error connecting to database:', err);
} else {
logger.info('Успешное подключение к базе данных:', res.rows[0]);
}
});
module.exports = { pool };

View File

@@ -0,0 +1,17 @@
CREATE TABLE IF NOT EXISTS email_settings (
id SERIAL PRIMARY KEY,
smtp_host VARCHAR(255) NOT NULL,
smtp_port INTEGER NOT NULL,
smtp_user VARCHAR(255) NOT NULL,
smtp_password VARCHAR(255) NOT NULL,
imap_host VARCHAR(255),
imap_port INTEGER,
from_email VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- Для простоты предполагаем, что настройки всегда одни (id=1)
INSERT INTO email_settings (smtp_host, smtp_port, smtp_user, smtp_password, imap_host, imap_port, from_email)
VALUES ('smtp.example.com', 465, 'user@example.com', 'password', 'imap.example.com', 993, 'noreply@example.com')
ON CONFLICT DO NOTHING;

View File

@@ -0,0 +1,12 @@
CREATE TABLE IF NOT EXISTS telegram_settings (
id SERIAL PRIMARY KEY,
bot_token VARCHAR(255) NOT NULL,
bot_username VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- Для простоты предполагаем, что настройки всегда одни (id=1)
INSERT INTO telegram_settings (bot_token, bot_username)
VALUES ('your-telegram-bot-token', 'your_bot_username')
ON CONFLICT DO NOTHING;

View File

@@ -0,0 +1,15 @@
CREATE TABLE IF NOT EXISTS db_settings (
id SERIAL PRIMARY KEY,
db_host VARCHAR(255) NOT NULL,
db_port INTEGER NOT NULL,
db_name VARCHAR(255) NOT NULL,
db_user VARCHAR(255) NOT NULL,
db_password VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- Для простоты предполагаем, что настройки всегда одни (id=1)
INSERT INTO db_settings (db_host, db_port, db_name, db_user, db_password)
VALUES ('localhost', 5432, 'dapp_db', 'dapp_user', 'dapp_password')
ON CONFLICT DO NOTHING;

View File

@@ -0,0 +1,14 @@
CREATE TABLE IF NOT EXISTS ai_providers_settings (
id SERIAL PRIMARY KEY,
provider VARCHAR(32) NOT NULL UNIQUE, -- openai, anthropic, google, ollama
api_key VARCHAR(255),
base_url VARCHAR(255),
selected_model VARCHAR(128),
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
-- Пример заполнения для Ollama (без ключа)
INSERT INTO ai_providers_settings (provider, base_url, selected_model)
VALUES ('ollama', 'http://localhost:11434', 'qwen2.5')
ON CONFLICT (provider) DO NOTHING;

File diff suppressed because it is too large Load Diff

View File

@@ -1 +1,297 @@
{"code":"42703","file":"parse_relation.c","hint":"Perhaps you meant to reference the column \"user_identities.created_at\".","length":183,"level":"error","line":"3729","message":"[IdentityService] Error finding wallet identity for user 1: column \"updated_at\" does not exist","name":"error","position":"43","routine":"errorMissingColumn","severity":"ERROR","stack":"error: column \"updated_at\" does not exist\n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async IdentityService.findIdentity (/app/services/identity-service.js:258:22)\n at async /app/routes/auth.js:368:32","timestamp":"2025-05-22T09:09:22.928Z"}
{"code":"ENETUNREACH","errno":"ENETUNREACH","level":"error","message":"Error launching Telegram bot: request to https://api.telegram.org/bot7498026249:[REDACTED]/getMe failed, reason: connect ENETUNREACH 2001:67c:4e8:f004::9:443 - Local (:::0)","stack":"FetchError: request to https://api.telegram.org/bot7498026249:[REDACTED]/getMe failed, reason: connect ENETUNREACH 2001:67c:4e8:f004::9:443 - Local (:::0)\n at ClientRequest.<anonymous> (/app/node_modules/node-fetch/lib/index.js:1501:11)\n at ClientRequest.emit (node:events:524:28)\n at emitErrorEvent (node:_http_client:101:11)\n at TLSSocket.socketErrorListener (node:_http_client:504:5)\n at TLSSocket.emit (node:events:524:28)\n at emitErrorNT (node:internal/streams/destroy:169:8)\n at emitErrorCloseNT (node:internal/streams/destroy:128:3)\n at process.processTicksAndRejections (node:internal/process/task_queues:82:21)","timestamp":"2025-05-22T10:08:06.855Z","type":"system"}
{"level":"error","message":"Error: undefined","timestamp":"2025-05-22T10:13:14.109Z"}
{"level":"error","message":"Error: undefined","timestamp":"2025-05-22T10:13:32.259Z"}
{"level":"error","message":"Error: undefined","timestamp":"2025-05-22T10:15:50.550Z"}
{"level":"error","message":"Error: undefined","timestamp":"2025-05-22T10:17:16.194Z"}
{"code":"42703","file":"parse_relation.c","hint":"Perhaps you meant to reference the column \"user_identities.created_at\".","length":183,"level":"error","line":"3729","message":"[IdentityService] Error finding wallet identity for user 1: column \"updated_at\" does not exist","name":"error","position":"43","routine":"errorMissingColumn","severity":"ERROR","stack":"error: column \"updated_at\" does not exist\n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async IdentityService.findIdentity (/app/services/identity-service.js:258:22)\n at async /app/routes/auth.js:368:32","timestamp":"2025-05-22T10:23:20.333Z"}
{"level":"error","message":"Error launching Telegram bot: 404: Not Found","on":{"method":"getMe","payload":{}},"response":{"description":"Not Found","error_code":404,"ok":false},"stack":"Error: 404: Not Found\n at Telegram.callApi (/app/node_modules/telegraf/lib/core/network/client.js:315:19)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Telegraf.launch (/app/node_modules/telegraf/lib/telegraf.js:188:78)\n at async getBot (/app/services/telegramBot.js:255:5)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T10:28:07.421Z"}
{"code":"ENETUNREACH","level":"error","message":"[getUserTokenBalances] Ошибка получения баланса для HB3A (0xdCe769b847a0a697239777D0B1C7dd33b6012ba0) в сети arbitrum:","stack":"AggregateError [ENETUNREACH]: \n at internalConnectMultiple (node:net:1122:18)\n at internalConnectMultiple (node:net:1190:5)\n at internalConnectMultiple (node:net:1190:5)\n at defaultTriggerAsyncIdScope (node:internal/async_hooks:464:18)\n at GetAddrInfoReqWrap.emitLookup [as callback] (node:net:1526:7)\n at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:132:8)","timestamp":"2025-05-22T10:31:59.871Z"}
{"code":"ENETUNREACH","errno":"ENETUNREACH","level":"error","message":"Error launching Telegram bot: request to https://api.telegram.org/bot7498026249:[REDACTED]/getMe failed, reason: connect ENETUNREACH 2001:67c:4e8:f004::9:443 - Local (:::0)","stack":"FetchError: request to https://api.telegram.org/bot7498026249:[REDACTED]/getMe failed, reason: connect ENETUNREACH 2001:67c:4e8:f004::9:443 - Local (:::0)\n at ClientRequest.<anonymous> (/app/node_modules/node-fetch/lib/index.js:1501:11)\n at ClientRequest.emit (node:events:524:28)\n at emitErrorEvent (node:_http_client:101:11)\n at TLSSocket.socketErrorListener (node:_http_client:504:5)\n at TLSSocket.emit (node:events:524:28)\n at emitErrorNT (node:internal/streams/destroy:169:8)\n at emitErrorCloseNT (node:internal/streams/destroy:128:3)\n at process.processTicksAndRejections (node:internal/process/task_queues:82:21)","timestamp":"2025-05-22T10:35:13.309Z","type":"system"}
{"code":"ENETUNREACH","level":"error","message":"[getUserTokenBalances] Ошибка получения баланса для HB3A (0xdCe769b847a0a697239777D0B1C7dd33b6012ba0) в сети arbitrum:","stack":"AggregateError [ENETUNREACH]: \n at internalConnectMultiple (node:net:1122:18)\n at internalConnectMultiple (node:net:1190:5)\n at internalConnectMultiple (node:net:1190:5)\n at defaultTriggerAsyncIdScope (node:internal/async_hooks:464:18)\n at GetAddrInfoReqWrap.emitLookup [as callback] (node:net:1526:7)\n at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:132:8)","timestamp":"2025-05-22T11:10:09.263Z"}
{"code":"ECONNREFUSED","level":"error","message":"Error launching Telegram bot:","stack":"AggregateError [ECONNREFUSED]: \n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async getTelegramSettings (/app/services/telegramBot.js:13:20)\n at async getBot (/app/services/telegramBot.js:22:22)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:18:06.450Z"}
{"code":"ECONNREFUSED","level":"error","message":"Error launching Telegram bot:","stack":"AggregateError [ECONNREFUSED]: \n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async getTelegramSettings (/app/services/telegramBot.js:13:20)\n at async getBot (/app/services/telegramBot.js:22:22)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:19:03.150Z"}
{"code":"ECONNREFUSED","level":"error","message":"Error launching Telegram bot:","stack":"AggregateError [ECONNREFUSED]: \n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async getTelegramSettings (/app/services/telegramBot.js:13:20)\n at async getBot (/app/services/telegramBot.js:22:22)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:19:22.997Z"}
{"code":"ECONNREFUSED","level":"error","message":"Error launching Telegram bot:","stack":"AggregateError [ECONNREFUSED]: \n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async getTelegramSettings (/app/services/telegramBot.js:13:20)\n at async getBot (/app/services/telegramBot.js:22:22)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:32:50.019Z"}
{"code":"ECONNREFUSED","level":"error","message":"Error launching Telegram bot:","stack":"AggregateError [ECONNREFUSED]: \n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async getTelegramSettings (/app/services/telegramBot.js:13:20)\n at async getBot (/app/services/telegramBot.js:22:22)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:36:54.404Z"}
{"code":"ECONNREFUSED","level":"error","message":"Error launching Telegram bot:","stack":"AggregateError [ECONNREFUSED]: \n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async getTelegramSettings (/app/services/telegramBot.js:13:20)\n at async getBot (/app/services/telegramBot.js:22:22)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:38:37.130Z"}
{"code":"ECONNREFUSED","level":"error","message":"Error launching Telegram bot:","stack":"AggregateError [ECONNREFUSED]: \n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async getTelegramSettings (/app/services/telegramBot.js:13:20)\n at async getBot (/app/services/telegramBot.js:22:22)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:40:58.680Z"}
{"code":"ECONNREFUSED","level":"error","message":"Error launching Telegram bot:","stack":"AggregateError [ECONNREFUSED]: \n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async getTelegramSettings (/app/services/telegramBot.js:13:20)\n at async getBot (/app/services/telegramBot.js:22:22)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:42:31.758Z"}
{"code":"ECONNREFUSED","level":"error","message":"Error launching Telegram bot:","stack":"AggregateError [ECONNREFUSED]: \n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async getTelegramSettings (/app/services/telegramBot.js:13:20)\n at async getBot (/app/services/telegramBot.js:22:22)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:42:36.298Z"}
{"code":"ECONNREFUSED","level":"error","message":"Error launching Telegram bot:","stack":"AggregateError [ECONNREFUSED]: \n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async getTelegramSettings (/app/services/telegramBot.js:13:20)\n at async getBot (/app/services/telegramBot.js:22:22)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:42:57.653Z"}
{"code":"ECONNREFUSED","level":"error","message":"Error launching Telegram bot:","stack":"AggregateError [ECONNREFUSED]: \n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async getTelegramSettings (/app/services/telegramBot.js:13:20)\n at async getBot (/app/services/telegramBot.js:22:22)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:46:50.798Z"}
{"code":"ECONNREFUSED","level":"error","message":"Error launching Telegram bot:","stack":"AggregateError [ECONNREFUSED]: \n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async getTelegramSettings (/app/services/telegramBot.js:13:20)\n at async getBot (/app/services/telegramBot.js:22:22)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:46:54.659Z"}
{"code":"ECONNREFUSED","level":"error","message":"Error launching Telegram bot:","stack":"AggregateError [ECONNREFUSED]: \n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async getTelegramSettings (/app/services/telegramBot.js:13:20)\n at async getBot (/app/services/telegramBot.js:22:22)\n at async initServices (/app/server.js:35:7)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:48:11.768Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:56:56.984Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:57:50.382Z"}
{"level":"error","message":"Unhandled Rejection: pool.query is not a function","stack":"TypeError: pool.query is not a function\n at /app/app.js:92:31\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at trim_prefix (/app/node_modules/express/lib/router/index.js:328:13)\n at /app/node_modules/express/lib/router/index.js:286:9\n at Function.process_params (/app/node_modules/express/lib/router/index.js:346:12)\n at next (/app/node_modules/express/lib/router/index.js:280:10)\n at /app/node_modules/express-session/index.js:514:7\n at /app/node_modules/connect-pg-simple/index.js:370:18\n at process.processTicksAndRejections (node:internal/process/task_queues:82:21)","timestamp":"2025-05-22T11:57:55.063Z"}
{"level":"error","message":"Unhandled Rejection: pool.query is not a function","stack":"TypeError: pool.query is not a function\n at /app/app.js:92:31\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at trim_prefix (/app/node_modules/express/lib/router/index.js:328:13)\n at /app/node_modules/express/lib/router/index.js:286:9\n at Function.process_params (/app/node_modules/express/lib/router/index.js:346:12)\n at next (/app/node_modules/express/lib/router/index.js:280:10)\n at /app/node_modules/express-session/index.js:514:7\n at /app/node_modules/connect-pg-simple/index.js:370:18\n at process.processTicksAndRejections (node:internal/process/task_queues:82:21)","timestamp":"2025-05-22T11:57:59.985Z"}
{"level":"error","message":"Unhandled Rejection: pool.query is not a function","stack":"TypeError: pool.query is not a function\n at /app/app.js:92:31\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at trim_prefix (/app/node_modules/express/lib/router/index.js:328:13)\n at /app/node_modules/express/lib/router/index.js:286:9\n at Function.process_params (/app/node_modules/express/lib/router/index.js:346:12)\n at next (/app/node_modules/express/lib/router/index.js:280:10)\n at /app/node_modules/express-session/index.js:514:7\n at /app/node_modules/connect-pg-simple/index.js:370:18\n at process.processTicksAndRejections (node:internal/process/task_queues:82:21)","timestamp":"2025-05-22T11:58:14.446Z"}
{"level":"error","message":"Unhandled Rejection: pool.query is not a function","stack":"TypeError: pool.query is not a function\n at /app/app.js:92:31\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at trim_prefix (/app/node_modules/express/lib/router/index.js:328:13)\n at /app/node_modules/express/lib/router/index.js:286:9\n at Function.process_params (/app/node_modules/express/lib/router/index.js:346:12)\n at next (/app/node_modules/express/lib/router/index.js:280:10)\n at /app/node_modules/express-session/index.js:514:7\n at /app/node_modules/connect-pg-simple/index.js:370:18\n at process.processTicksAndRejections (node:internal/process/task_queues:82:21)","timestamp":"2025-05-22T11:58:14.448Z"}
{"level":"error","message":"Unhandled Rejection: pool.query is not a function","stack":"TypeError: pool.query is not a function\n at /app/app.js:92:31\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at trim_prefix (/app/node_modules/express/lib/router/index.js:328:13)\n at /app/node_modules/express/lib/router/index.js:286:9\n at Function.process_params (/app/node_modules/express/lib/router/index.js:346:12)\n at next (/app/node_modules/express/lib/router/index.js:280:10)\n at /app/node_modules/express-session/index.js:514:7\n at /app/node_modules/connect-pg-simple/index.js:370:18\n at process.processTicksAndRejections (node:internal/process/task_queues:82:21)","timestamp":"2025-05-22T11:58:14.453Z"}
{"level":"error","message":"Unhandled Rejection: pool.query is not a function","stack":"TypeError: pool.query is not a function\n at /app/app.js:92:31\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at trim_prefix (/app/node_modules/express/lib/router/index.js:328:13)\n at /app/node_modules/express/lib/router/index.js:286:9\n at Function.process_params (/app/node_modules/express/lib/router/index.js:346:12)\n at next (/app/node_modules/express/lib/router/index.js:280:10)\n at /app/node_modules/express-session/index.js:514:7\n at /app/node_modules/connect-pg-simple/index.js:370:18\n at process.processTicksAndRejections (node:internal/process/task_queues:82:21)","timestamp":"2025-05-22T11:58:14.455Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:59:41.974Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:59:50.126Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:59:55.851Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T11:59:59.406Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:00:04.949Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:00:14.518Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:00:15.678Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:00:21.533Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:00:25.358Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:00:31.705Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:00:37.432Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:00:43.163Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:00:49.163Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:00:53.058Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:00:59.835Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:01:05.271Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:01:14.525Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:01:20.428Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:06:13.986Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:06:24.753Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:06:25.236Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:06:25.273Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:06:25.297Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:06:25.315Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:06:25.339Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:06:25.481Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:06:25.500Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:06:25.520Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:06:25.541Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:06:25.571Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:06:25.608Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:06:25.627Z"}
{"level":"error","message":"Error fetching message history for user 1: db.query is not a function","stack":"TypeError: db.query is not a function\n at /app/routes/chat.js:446:36\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at next (/app/node_modules/express/lib/router/route.js:149:13)\n at requireAuth (/app/middleware/auth.js:30:14)\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at next (/app/node_modules/express/lib/router/route.js:149:13)\n at Route.dispatch (/app/node_modules/express/lib/router/route.js:119:3)\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at /app/node_modules/express/lib/router/index.js:284:15\n at Function.process_params (/app/node_modules/express/lib/router/index.js:346:12)","timestamp":"2025-05-22T12:06:26.935Z"}
{"level":"error","message":"Error fetching message history for user 1: db.query is not a function","stack":"TypeError: db.query is not a function\n at /app/routes/chat.js:484:29\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at next (/app/node_modules/express/lib/router/route.js:149:13)\n at requireAuth (/app/middleware/auth.js:30:14)\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at next (/app/node_modules/express/lib/router/route.js:149:13)\n at Route.dispatch (/app/node_modules/express/lib/router/route.js:119:3)\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at /app/node_modules/express/lib/router/index.js:284:15\n at Function.process_params (/app/node_modules/express/lib/router/index.js:346:12)","timestamp":"2025-05-22T12:06:26.953Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:08.210Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:08.230Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:08.270Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:08.291Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:08.311Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:08.332Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:08.392Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:08.427Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:08.452Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:08.478Z"}
{"level":"error","message":"Error getting db settings: db.query is not a function","stack":"TypeError: db.query is not a function\n at /app/routes/identities.js:214:31\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at next (/app/node_modules/express/lib/router/route.js:149:13)\n at requireAuth (/app/middleware/auth.js:30:14)\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at next (/app/node_modules/express/lib/router/route.js:149:13)\n at Route.dispatch (/app/node_modules/express/lib/router/route.js:119:3)\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at /app/node_modules/express/lib/router/index.js:284:15\n at Function.process_params (/app/node_modules/express/lib/router/index.js:346:12)","timestamp":"2025-05-22T12:07:09.967Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:09.975Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:09.991Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:10.008Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:10.028Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:10.049Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:10.064Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:10.082Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:10.100Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:10.122Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:07:10.140Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:12.203Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:12.218Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:12.235Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:12.250Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:12.270Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:12.292Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:12.312Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:12.332Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:12.352Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:12.372Z"}
{"level":"error","message":"Error fetching message history for user 1: db.query is not a function","stack":"TypeError: db.query is not a function\n at /app/routes/chat.js:446:36\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at next (/app/node_modules/express/lib/router/route.js:149:13)\n at requireAuth (/app/middleware/auth.js:30:14)\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at next (/app/node_modules/express/lib/router/route.js:149:13)\n at Route.dispatch (/app/node_modules/express/lib/router/route.js:119:3)\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at /app/node_modules/express/lib/router/index.js:284:15\n at Function.process_params (/app/node_modules/express/lib/router/index.js:346:12)","timestamp":"2025-05-22T12:08:13.852Z"}
{"level":"error","message":"Error fetching message history for user 1: db.query is not a function","stack":"TypeError: db.query is not a function\n at /app/routes/chat.js:484:29\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at next (/app/node_modules/express/lib/router/route.js:149:13)\n at requireAuth (/app/middleware/auth.js:30:14)\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at next (/app/node_modules/express/lib/router/route.js:149:13)\n at Route.dispatch (/app/node_modules/express/lib/router/route.js:119:3)\n at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5)\n at /app/node_modules/express/lib/router/index.js:284:15\n at Function.process_params (/app/node_modules/express/lib/router/index.js:346:12)","timestamp":"2025-05-22T12:08:13.866Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:08:25.906Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:08:33.657Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:40.758Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:40.808Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:40.835Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:40.852Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:40.884Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:40.930Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:40.957Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:40.992Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:41.120Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:41.139Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:41.165Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:41.185Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:45.085Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:45.104Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:45.141Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:45.159Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:45.178Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:45.205Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:45.229Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:08:43.966Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:49.896Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:49.925Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:49.951Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:49.976Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:49.996Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:50.015Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:50.034Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:50.052Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:50.073Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:50.098Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:53.175Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:53.192Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:53.218Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:53.241Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:53.276Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:53.290Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:53.315Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:53.353Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:53.392Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:53.420Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:55.343Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:55.360Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:55.379Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:55.401Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:55.423Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:55.444Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:55.463Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:55.486Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:55.506Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:08:55.529Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:08:58.944Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:09:07.901Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:09:12.663Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:29.544Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:29.566Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:29.595Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:29.624Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:29.647Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:29.673Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:29.708Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:29.742Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:29.849Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:29.870Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:29.892Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:29.916Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:38.914Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:38.936Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:38.958Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:38.977Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:38.998Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:39.015Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:39.032Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:39.056Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:39.075Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:39.093Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:41.700Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:41.716Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:41.741Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:41.763Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:41.782Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:41.797Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:41.817Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:41.833Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:41.854Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:41.878Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:43.724Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:43.740Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:43.765Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:43.791Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:43.813Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:43.832Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:43.851Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:43.874Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:43.909Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:09:43.929Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:09:59.700Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:10.780Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:10.856Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:10.875Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:10.954Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:11.045Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:11.067Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:11.082Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:11.102Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:11.120Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:11.138Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:11.155Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:11.176Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:13.779Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:13.810Z"}
{"code":"ENETUNREACH","level":"error","message":"[getUserTokenBalances] Ошибка получения баланса для HB3A (0x351f59de4fedbdf7601f5592b93db3b9330c1c1d) в сети polygon:","stack":"AggregateError [ENETUNREACH]: \n at internalConnectMultiple (node:net:1122:18)\n at internalConnectMultiple (node:net:1190:5)\n at internalConnectMultiple (node:net:1190:5)\n at defaultTriggerAsyncIdScope (node:internal/async_hooks:464:18)\n at GetAddrInfoReqWrap.emitLookup [as callback] (node:net:1526:7)\n at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:132:8)","timestamp":"2025-05-22T12:13:16.145Z"}
{"code":"ENETUNREACH","level":"error","message":"[getUserTokenBalances] Ошибка получения баланса для HB3A (0xdCe769b847a0a697239777D0B1C7dd33b6012ba0) в сети arbitrum:","stack":"AggregateError [ENETUNREACH]: \n at internalConnectMultiple (node:net:1122:18)\n at internalConnectMultiple (node:net:1190:5)\n at internalConnectMultiple (node:net:1190:5)\n at defaultTriggerAsyncIdScope (node:internal/async_hooks:464:18)\n at GetAddrInfoReqWrap.emitLookup [as callback] (node:net:1526:7)\n at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:132:8)","timestamp":"2025-05-22T12:13:16.233Z"}
{"level":"error","message":"[getLinkedWallet] Error fetching linked wallet for userId 1: db.query is not a function","stack":"TypeError: db.query is not a function\n at AuthService.getLinkedWallet (/app/services/auth-service.js:366:31)\n at AuthService.handleEmailVerification (/app/services/auth-service.js:817:41)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async /app/routes/auth.js:359:24","timestamp":"2025-05-22T12:13:42.185Z"}
{"level":"error","message":"[handleEmailVerification] Error checking admin role: db.query is not a function","stack":"TypeError: db.query is not a function\n at AuthService.handleEmailVerification (/app/services/auth-service.js:833:40)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async /app/routes/auth.js:359:24","timestamp":"2025-05-22T12:13:42.186Z"}
{"level":"error","message":"Error fetching current user role after role check error: db.query is not a function","stack":"TypeError: db.query is not a function\n at AuthService.handleEmailVerification (/app/services/auth-service.js:842:40)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async /app/routes/auth.js:359:24","timestamp":"2025-05-22T12:13:42.187Z"}
{"code":"42703","file":"parse_relation.c","hint":"Perhaps you meant to reference the column \"user_identities.created_at\".","length":183,"level":"error","line":"3729","message":"[IdentityService] Error finding wallet identity for user 1: column \"updated_at\" does not exist","name":"error","position":"43","routine":"errorMissingColumn","severity":"ERROR","stack":"error: column \"updated_at\" does not exist\n at /app/node_modules/pg-pool/index.js:45:11\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async IdentityService.findIdentity (/app/services/identity-service.js:258:22)\n at async /app/routes/auth.js:368:32","timestamp":"2025-05-22T12:13:42.191Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:57.723Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:57.800Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:57.817Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:57.843Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:57.863Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:57.882Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:57.921Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:57.942Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:57.960Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:57.983Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:57.998Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:13:58.016Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:15:11.547Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:15:11.575Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:21:07.817Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:21:08.276Z"}
{"level":"error","message":"Error updating user role: db.query is not a function","stack":"TypeError: db.query is not a function\n at AuthService.checkAdminTokens (/app/services/auth-service.js:558:39)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async requireAdmin (/app/middleware/auth.js:107:23)","timestamp":"2025-05-22T12:21:11.015Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:21:27.439Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:21:27.468Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:24:21.976Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:24:22.037Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:24:22.079Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:24:22.094Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:24:22.109Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:24:22.122Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:24:22.138Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:24:22.151Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:24:22.166Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:24:22.183Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:24:22.199Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:24:22.216Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:25:21.981Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:25:22.010Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:26:01.494Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:26:01.529Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:28:33.564Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:28:33.592Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:34:40.368Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:34:40.409Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:34:55.750Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:34:55.791Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:35:11.297Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:35:11.349Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:35:11.375Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:35:11.394Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:35:11.410Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:35:11.428Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:35:11.447Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:35:11.465Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:35:11.482Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:35:11.496Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:35:11.522Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T12:35:11.540Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:48:43.958Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:51:53.499Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:53:28.746Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:53:30.967Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:53:58.243Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T12:54:01.602Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T13:12:59.556Z"}
{"level":"error","message":"Error: undefined","timestamp":"2025-05-22T13:13:15.709Z"}
{"level":"error","message":"Error launching Telegram bot: db.query is not a function","stack":"TypeError: db.query is not a function\n at getTelegramSettings (/app/services/telegramBot.js:13:29)\n at getBot (/app/services/telegramBot.js:22:28)\n at initServices (/app/server.js:35:13)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)\n at async Server.<anonymous> (/app/server.js:98:5)","timestamp":"2025-05-22T13:13:55.154Z"}
{"code":"ENETUNREACH","errno":"ENETUNREACH","level":"error","message":"Error launching Telegram bot: request to https://api.telegram.org/bot7498026249:[REDACTED]/getMe failed, reason: connect ENETUNREACH 2001:67c:4e8:f004::9:443 - Local (:::0)","stack":"FetchError: request to https://api.telegram.org/bot7498026249:[REDACTED]/getMe failed, reason: connect ENETUNREACH 2001:67c:4e8:f004::9:443 - Local (:::0)\n at ClientRequest.<anonymous> (/app/node_modules/node-fetch/lib/index.js:1501:11)\n at ClientRequest.emit (node:events:524:28)\n at emitErrorEvent (node:_http_client:101:11)\n at TLSSocket.socketErrorListener (node:_http_client:504:5)\n at TLSSocket.emit (node:events:524:28)\n at emitErrorNT (node:internal/streams/destroy:169:8)\n at emitErrorCloseNT (node:internal/streams/destroy:128:3)\n at process.processTicksAndRejections (node:internal/process/task_queues:82:21)","timestamp":"2025-05-22T13:16:36.804Z","type":"system"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:17:36.366Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:17:36.462Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:17:36.627Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:17:36.653Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:17:36.675Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:17:36.696Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:17:36.718Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:17:36.859Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:17:36.890Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:17:36.922Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:17:36.950Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:17:36.977Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:18:39.257Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:18:39.303Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:21:07.268Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:21:07.326Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:21:18.209Z"}
{"level":"error","message":"[session/check] Error fetching identities: db.query is not a function","timestamp":"2025-05-22T13:21:18.274Z"}

View File

@@ -7,6 +7,11 @@ const { ERROR_CODES } = require('../utils/constants');
*/
// eslint-disable-next-line no-unused-vars
const errorHandler = (err, req, res, /* next */) => {
console.error('errorHandler: err =', err);
console.error('errorHandler: typeof err =', typeof err);
console.error('errorHandler: stack =', err && err.stack);
console.log('errorHandler called, typeof res:', typeof res, 'res:', res);
console.log('typeof res:', typeof res, 'isFunction:', typeof res === 'function');
// Логируем ошибку
logger.error(`Error: ${err.message}`, {
stack: err.stack,
@@ -65,7 +70,6 @@ function createError(message, status) {
return error;
}
module.exports = {
errorHandler,
createError,
};
module.exports = errorHandler;
// Если нужен createError для других файлов:
// module.exports.createError = createError;

View File

@@ -23,6 +23,8 @@
"fix-duplicates": "node scripts/fix-duplicate-identities.js"
},
"dependencies": {
"@anthropic-ai/sdk": "^0.51.0",
"@google/genai": "^1.0.1",
"@langchain/community": "^0.3.34",
"@langchain/core": "0.3.0",
"@langchain/ollama": "^0.2.0",
@@ -47,6 +49,7 @@
"node-cron": "^3.0.3",
"node-telegram-bot-api": "^0.66.0",
"nodemailer": "^6.10.0",
"openai": "^4.102.0",
"pg": "^8.10.0",
"semver": "^7.7.1",
"session-file-store": "^1.5.0",

View File

@@ -6,49 +6,49 @@ const authService = require('../services/auth-service');
const logger = require('../utils/logger');
// Роли
router.get('/roles', requireAdmin, async (req, res) => {
router.get('/roles', requireAdmin, async (req, res, next) => {
try {
const roles = await authService.getAllRoles();
res.json({ success: true, roles });
} catch (error) {
logger.error('Error getting roles:', error);
res.status(500).json({ error: 'Internal server error' });
next(error);
}
});
router.post('/roles', requireAdmin, async (req, res) => {
router.post('/roles', requireAdmin, async (req, res, next) => {
try {
const { name, permissions } = req.body;
const role = await authService.createRole(name, permissions);
res.json({ success: true, role });
} catch (error) {
logger.error('Error creating role:', error);
res.status(500).json({ error: 'Internal server error' });
next(error);
}
});
// Админ функции
router.get('/users', requireAdmin, async (req, res) => {
router.get('/users', requireAdmin, async (req, res, next) => {
try {
const users = await authService.getAllUsers();
res.json({ success: true, users });
} catch (error) {
logger.error('Error getting users:', error);
res.status(500).json({ error: 'Internal server error' });
next(error);
}
});
// Маршрут для получения статистики (защищен middleware requireAdmin)
router.get('/stats', requireAdmin, async (req, res) => {
router.get('/stats', requireAdmin, async (req, res, next) => {
try {
// Получаем количество пользователей
const usersCount = await db.query('SELECT COUNT(*) FROM users');
const usersCount = await db.getQuery()('SELECT COUNT(*) FROM users');
// Получаем количество досок
const boardsCount = await db.query('SELECT COUNT(*) FROM kanban_boards');
const boardsCount = await db.getQuery()('SELECT COUNT(*) FROM kanban_boards');
// Получаем количество задач
const tasksCount = await db.query('SELECT COUNT(*) FROM kanban_tasks');
const tasksCount = await db.getQuery()('SELECT COUNT(*) FROM kanban_tasks');
res.json({
userCount: parseInt(usersCount.rows[0].count),
@@ -57,18 +57,18 @@ router.get('/stats', requireAdmin, async (req, res) => {
});
} catch (error) {
console.error('Ошибка при получении статистики:', error);
res.status(500).json({ error: 'Internal server error' });
next(error);
}
});
// Маршрут для получения логов
router.get('/logs', requireAdmin, async (req, res) => {
router.get('/logs', requireAdmin, async (req, res, next) => {
try {
const result = await db.query('SELECT * FROM logs ORDER BY created_at DESC LIMIT 100');
const result = await db.getQuery()('SELECT * FROM logs ORDER BY created_at DESC LIMIT 100');
res.json(result.rows);
} catch (error) {
console.error('Ошибка при получении логов:', error);
res.status(500).json({ error: 'Internal server error' });
next(error);
}
});

View File

@@ -34,7 +34,7 @@ router.get('/nonce', async (req, res) => {
const nonce = crypto.randomBytes(16).toString('hex');
// Проверяем, существует ли уже nonce для этого адреса
const existingNonce = await db.query('SELECT id FROM nonces WHERE identity_value = $1', [
const existingNonce = await db.getQuery()('SELECT id FROM nonces WHERE identity_value = $1', [
address.toLowerCase(),
]);

View File

@@ -17,7 +17,7 @@ async function processGuestMessages(userId, guestId) {
logger.info(`Processing guest messages for user ${userId} with guest ID ${guestId}`);
// Проверяем, обрабатывались ли уже эти сообщения
const mappingCheck = await db.query(
const mappingCheck = await db.getQuery()(
'SELECT processed FROM guest_user_mapping WHERE guest_id = $1',
[guestId]
);
@@ -30,7 +30,7 @@ async function processGuestMessages(userId, guestId) {
// Проверяем наличие mapping записи и создаем если нет
if (mappingCheck.rows.length === 0) {
await db.query(
await db.getQuery()(
'INSERT INTO guest_user_mapping (user_id, guest_id) VALUES ($1, $2) ON CONFLICT (guest_id) DO UPDATE SET user_id = $1',
[userId, guestId]
);
@@ -38,7 +38,7 @@ async function processGuestMessages(userId, guestId) {
}
// Получаем все гостевые сообщения со всеми новыми полями
const guestMessagesResult = await db.query(
const guestMessagesResult = await db.getQuery()(
`SELECT
id, guest_id, content, language, is_ai, created_at,
attachment_filename, attachment_mimetype, attachment_size, attachment_data
@@ -48,9 +48,9 @@ async function processGuestMessages(userId, guestId) {
if (guestMessagesResult.rows.length === 0) {
logger.info(`No guest messages found for guest ID ${guestId}`);
const checkResult = await db.query('SELECT 1 FROM guest_user_mapping WHERE guest_id = $1', [guestId]);
const checkResult = await db.getQuery()('SELECT 1 FROM guest_user_mapping WHERE guest_id = $1', [guestId]);
if (checkResult.rows.length > 0) {
await db.query('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [guestId]);
await db.getQuery()('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [guestId]);
logger.info(`Marked guest mapping as processed (no messages found) for guest ID ${guestId}`);
} else {
logger.warn(`Attempted to mark non-existent guest mapping as processed for guest ID ${guestId}`);
@@ -67,7 +67,7 @@ async function processGuestMessages(userId, guestId) {
? (firstMessage.content.length > 30 ? `${firstMessage.content.substring(0, 30)}...` : firstMessage.content)
: (firstMessage.attachment_filename ? `Файл: ${firstMessage.attachment_filename}` : 'Новый диалог');
const newConversationResult = await db.query(
const newConversationResult = await db.getQuery()(
'INSERT INTO conversations (user_id, title) VALUES ($1, $2) RETURNING *',
[userId, title]
);
@@ -84,7 +84,7 @@ async function processGuestMessages(userId, guestId) {
try {
// Сохраняем сообщение пользователя в таблицу messages, включая данные файла
const userMessageResult = await db.query(
const userMessageResult = await db.getQuery()(
`INSERT INTO messages
(conversation_id, content, sender_type, role, channel, created_at, user_id,
attachment_filename, attachment_mimetype, attachment_size, attachment_data)
@@ -118,7 +118,7 @@ async function processGuestMessages(userId, guestId) {
if (aiResponseContent) {
// Сохраняем ответ от ИИ (у него нет вложений)
const aiMessageResult = await db.query(
const aiMessageResult = await db.getQuery()(
`INSERT INTO messages
(conversation_id, content, sender_type, role, channel, created_at, user_id)
VALUES
@@ -144,20 +144,20 @@ async function processGuestMessages(userId, guestId) {
// Удаляем только успешно обработанные гостевые сообщения
if (savedMessageIds.length > 0) {
await db.query('DELETE FROM guest_messages WHERE id = ANY($1::int[])', [savedMessageIds]);
await db.getQuery()('DELETE FROM guest_messages WHERE id = ANY($1::int[])', [savedMessageIds]);
logger.info(
`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', [
await db.getQuery()('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [
guestId,
]);
logger.info(`Marked guest mapping as processed for guest ID ${guestId}`);
} else {
logger.warn(`No guest messages were successfully processed, skipping deletion for guest ID ${guestId}`);
// Если не было успешных, все равно пометим как обработанные, чтобы не пытаться снова
await db.query('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [guestId]);
await db.getQuery()('UPDATE guest_user_mapping SET processed = true WHERE guest_id = $1', [guestId]);
logger.info(`Marked guest mapping as processed (no successful messages) for guest ID ${guestId}`);
}
@@ -221,7 +221,7 @@ router.post('/guest-message', upload.array('attachments'), async (req, res) => {
});
// Сохраняем сообщение пользователя с текстом или файлом
const result = await db.query(
const result = await db.getQuery()(
`INSERT INTO guest_messages
(guest_id, content, language, is_ai,
attachment_filename, attachment_mimetype, attachment_size, attachment_data)
@@ -293,7 +293,7 @@ router.post('/message', requireAuth, upload.array('attachments'), async (req, re
try {
// Найти или создать диалог
if (conversationId) {
const convResult = await db.query(
const convResult = await db.getQuery()(
'SELECT * FROM conversations WHERE id = $1 AND user_id = $2',
[conversationId, userId]
);
@@ -308,7 +308,7 @@ router.post('/message', requireAuth, upload.array('attachments'), async (req, re
? (message.length > 50 ? `${message.substring(0, 50)}...` : message)
: (file ? `Файл: ${file.originalname}` : 'Новый диалог');
const newConvResult = await db.query(
const newConvResult = await db.getQuery()(
'INSERT INTO conversations (user_id, title) VALUES ($1, $2) RETURNING *',
[userId, title]
);
@@ -325,7 +325,7 @@ router.post('/message', requireAuth, upload.array('attachments'), async (req, re
const attachmentData = file ? file.buffer : null;
// Сохраняем сообщение пользователя
const userMessageResult = await db.query(
const userMessageResult = await db.getQuery()(
`INSERT INTO messages
(conversation_id, user_id, content, sender_type, role, channel,
attachment_filename, attachment_mimetype, attachment_size, attachment_data)
@@ -354,7 +354,7 @@ router.post('/message', requireAuth, upload.array('attachments'), async (req, re
logger.info('AI response received' + (aiResponseContent ? '' : ' (empty)'), { conversationId });
if (aiResponseContent) {
const aiMessageResult = await db.query(
const aiMessageResult = await db.getQuery()(
`INSERT INTO messages
(conversation_id, user_id, content, sender_type, role, channel)
VALUES ($1, $2, $3, 'assistant', 'assistant', 'web')
@@ -443,7 +443,7 @@ router.get('/history', requireAuth, async (req, res) => {
countQuery += ' AND conversation_id = $2';
countParams.push(conversationId);
}
const countResult = await db.query(countQuery, countParams);
const countResult = await db.getQuery()(countQuery, countParams);
const totalCount = parseInt(countResult.rows[0].count, 10);
return res.json({ success: true, count: totalCount });
}
@@ -481,7 +481,7 @@ router.get('/history', requireAuth, async (req, res) => {
logger.debug('Executing history query:', { query, params });
const result = await db.query(query, params);
const result = await db.getQuery()(query, params);
// Обрабатываем результаты для фронтенда
const messages = result.rows.map(msg => {
@@ -522,7 +522,7 @@ router.get('/history', requireAuth, async (req, res) => {
totalCountQuery += ' AND conversation_id = $2';
totalCountParams.push(conversationId);
}
const totalCountResult = await db.query(totalCountQuery, totalCountParams);
const totalCountResult = await db.getQuery()(totalCountQuery, totalCountParams);
const totalMessages = parseInt(totalCountResult.rows[0].count, 10);
logger.info(`Returning message history for user ${userId}`, { count: messages.length, offset, limit, total: totalMessages });

View File

@@ -11,7 +11,7 @@ const fs = require('fs');
* @desc Создать новое DLE (Digital Legal Entity)
* @access Private (только для авторизованных пользователей с ролью admin)
*/
router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res) => {
router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res, next) => {
try {
const dleParams = req.body;
logger.info('Получен запрос на создание DLE:', dleParams);
@@ -44,11 +44,7 @@ router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res) => {
});
} catch (error) {
logger.error('Ошибка при создании DLE:', error);
res.status(500).json({
success: false,
message: error.message || 'Произошла ошибка при создании DLE',
error: process.env.NODE_ENV === 'development' ? error.stack : undefined
});
next(error);
}
});
@@ -57,7 +53,7 @@ router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res) => {
* @desc Получить список всех DLE
* @access Private (только для авторизованных пользователей)
*/
router.get('/', auth.requireAuth, async (req, res) => {
router.get('/', auth.requireAuth, async (req, res, next) => {
try {
const dles = await dleService.getAllDLEs();
res.json({
@@ -66,11 +62,7 @@ router.get('/', auth.requireAuth, async (req, res) => {
});
} catch (error) {
logger.error('Ошибка при получении списка DLE:', error);
res.status(500).json({
success: false,
message: error.message || 'Произошла ошибка при получении списка DLE',
error: process.env.NODE_ENV === 'development' ? error.stack : undefined
});
next(error);
}
});
@@ -99,7 +91,7 @@ router.get('/settings', auth.requireAuth, (req, res) => {
* @desc Удалить DLE по адресу токена
* @access Private (только для авторизованных пользователей с ролью admin)
*/
router.delete('/:tokenAddress', auth.requireAuth, auth.requireAdmin, async (req, res) => {
router.delete('/:tokenAddress', auth.requireAuth, auth.requireAdmin, async (req, res, next) => {
try {
const { tokenAddress } = req.params;
logger.info(`Получен запрос на удаление DLE с адресом токена: ${tokenAddress}`);
@@ -142,11 +134,7 @@ router.delete('/:tokenAddress', auth.requireAuth, auth.requireAdmin, async (req,
});
} catch (error) {
logger.error('Ошибка при удалении DLE:', error);
res.status(500).json({
success: false,
message: error.message || 'Произошла ошибка при удалении DLE',
error: process.env.NODE_ENV === 'development' ? error.stack : undefined
});
next(error);
}
});
@@ -155,7 +143,7 @@ router.delete('/:tokenAddress', auth.requireAuth, auth.requireAdmin, async (req,
* @desc Удалить пустое DLE по имени файла
* @access Private (только для авторизованных пользователей с ролью admin)
*/
router.delete('/empty/:fileName', auth.requireAuth, auth.requireAdmin, async (req, res) => {
router.delete('/empty/:fileName', auth.requireAuth, auth.requireAdmin, async (req, res, next) => {
try {
const { fileName } = req.params;
logger.info(`Получен запрос на удаление пустого DLE с именем файла: ${fileName}`);
@@ -180,11 +168,7 @@ router.delete('/empty/:fileName', auth.requireAuth, auth.requireAdmin, async (re
});
} catch (error) {
logger.error('Ошибка при удалении пустого DLE:', error);
res.status(500).json({
success: false,
message: error.message || 'Произошла ошибка при удалении пустого DLE',
error: process.env.NODE_ENV === 'development' ? error.stack : undefined
});
next(error);
}
});

View File

@@ -6,19 +6,19 @@ const logger = require('../utils/logger');
const db = require('../db');
// Получение всех идентификаторов пользователя
router.get('/', requireAuth, async (req, res) => {
router.get('/', requireAuth, async (req, res, next) => {
try {
const userId = req.session.userId;
const identities = await authService.getUserIdentities(userId);
res.json({ success: true, identities });
} catch (error) {
logger.error('Error getting identities:', error);
res.status(500).json({ error: 'Internal server error' });
next(error);
}
});
// Связывание нового идентификатора
router.post('/link', requireAuth, async (req, res) => {
router.post('/link', requireAuth, async (req, res, next) => {
try {
const { type, value } = req.body;
const userId = req.session.userId;
@@ -28,7 +28,7 @@ router.post('/link', requireAuth, async (req, res) => {
const normalizedWallet = value.toLowerCase();
// Проверяем, существует ли уже такой кошелек
const existingCheck = await db.query(
const existingCheck = await db.getQuery()(
`SELECT user_id FROM user_identities
WHERE provider = 'wallet' AND provider_id = $1`,
[normalizedWallet]
@@ -73,12 +73,12 @@ router.post('/link', requireAuth, async (req, res) => {
});
}
res.status(500).json({ error: error.message || 'Internal server error' });
next(error);
}
});
// Получение балансов токенов
router.get('/token-balances', requireAuth, async (req, res) => {
router.get('/token-balances', requireAuth, async (req, res, next) => {
try {
const userId = req.session.userId;
if (!userId) {
@@ -103,12 +103,12 @@ router.get('/token-balances', requireAuth, async (req, res) => {
});
} catch (error) {
logger.error('Error getting token balances:', error);
res.status(500).json({ error: 'Internal server error' });
next(error);
}
});
// Удаление идентификатора пользователя
router.delete('/:provider/:providerId', requireAuth, async (req, res) => {
router.delete('/:provider/:providerId', requireAuth, async (req, res, next) => {
try {
const userId = req.session.userId;
const { provider, providerId } = req.params;
@@ -120,7 +120,135 @@ router.delete('/:provider/:providerId', requireAuth, async (req, res) => {
}
} catch (error) {
logger.error('Error deleting identity:', error);
res.status(500).json({ error: 'Internal server error' });
next(error);
}
});
// Получение email-настроек
router.get('/email-settings', requireAuth, async (req, res, next) => {
try {
const { rows } = await db.getQuery()('SELECT * FROM email_settings ORDER BY id LIMIT 1');
if (!rows.length) return res.status(404).json({ success: false, error: 'Not found' });
const settings = rows[0];
delete settings.smtp_password; // не возвращаем пароль
res.json({ success: true, settings });
} catch (error) {
logger.error('Error getting email settings:', error, error && error.stack);
next(error);
}
});
// Обновление email-настроек
router.put('/email-settings', requireAuth, async (req, res, next) => {
try {
const { smtp_host, smtp_port, smtp_user, smtp_password, imap_host, imap_port, from_email } = req.body;
if (!smtp_host || !smtp_port || !smtp_user || !from_email) {
return res.status(400).json({ success: false, error: 'Missing required fields' });
}
const { rows } = await db.getQuery()('SELECT id FROM email_settings ORDER BY id LIMIT 1');
if (rows.length) {
// Обновляем существующую запись
await db.getQuery()(
`UPDATE email_settings SET smtp_host=$1, smtp_port=$2, smtp_user=$3, smtp_password=COALESCE($4, smtp_password), imap_host=$5, imap_port=$6, from_email=$7, updated_at=NOW() WHERE id=$8`,
[smtp_host, smtp_port, smtp_user, smtp_password, imap_host, imap_port, from_email, rows[0].id]
);
} else {
// Вставляем новую
await db.getQuery()(
`INSERT INTO email_settings (smtp_host, smtp_port, smtp_user, smtp_password, imap_host, imap_port, from_email) VALUES ($1,$2,$3,$4,$5,$6,$7)`,
[smtp_host, smtp_port, smtp_user, smtp_password, imap_host, imap_port, from_email]
);
}
res.json({ success: true });
} catch (error) {
logger.error('Error updating email settings:', error);
next(error);
}
});
// Получение telegram-настроек
router.get('/telegram-settings', requireAuth, async (req, res, next) => {
try {
const { rows } = await db.getQuery()('SELECT * FROM telegram_settings ORDER BY id LIMIT 1');
if (!rows.length) return res.status(404).json({ success: false, error: 'Not found' });
const settings = rows[0];
delete settings.bot_token; // не возвращаем токен
res.json({ success: true, settings });
} catch (error) {
logger.error('Error getting telegram settings:', error, error && error.stack);
next(error);
}
});
// Обновление telegram-настроек
router.put('/telegram-settings', requireAuth, async (req, res, next) => {
try {
const { bot_token, bot_username } = req.body;
if (!bot_token || !bot_username) {
return res.status(400).json({ success: false, error: 'Missing required fields' });
}
const { rows } = await db.getQuery()('SELECT id FROM telegram_settings ORDER BY id LIMIT 1');
if (rows.length) {
// Обновляем существующую запись
await db.getQuery()(
`UPDATE telegram_settings SET bot_token=$1, bot_username=$2, updated_at=NOW() WHERE id=$3`,
[bot_token, bot_username, rows[0].id]
);
} else {
// Вставляем новую
await db.getQuery()(
`INSERT INTO telegram_settings (bot_token, bot_username) VALUES ($1,$2)` ,
[bot_token, bot_username]
);
}
res.json({ success: true });
} catch (error) {
logger.error('Error updating telegram settings:', error);
next(error);
}
});
// Получение db-настроек
router.get('/db-settings', requireAuth, async (req, res, next) => {
try {
const { rows } = await db.getQuery()('SELECT * FROM db_settings ORDER BY id LIMIT 1');
if (!rows.length) return res.status(404).json({ success: false, error: 'Not found' });
const settings = rows[0];
delete settings.db_password; // не возвращаем пароль
res.json({ success: true, settings });
} catch (error) {
logger.error('Error getting db settings:', error, error && error.stack);
next(error);
}
});
// Обновление db-настроек
router.put('/db-settings', requireAuth, async (req, res, next) => {
try {
const { db_host, db_port, db_name, db_user, db_password } = req.body;
if (!db_host || !db_port || !db_name || !db_user) {
return res.status(400).json({ success: false, error: 'Missing required fields' });
}
const { rows } = await db.getQuery()('SELECT id FROM db_settings ORDER BY id LIMIT 1');
if (rows.length) {
// Обновляем существующую запись
await db.getQuery()(
`UPDATE db_settings SET db_host=$1, db_port=$2, db_name=$3, db_user=$4, db_password=COALESCE($5, db_password), updated_at=NOW() WHERE id=$6`,
[db_host, db_port, db_name, db_user, db_password, rows[0].id]
);
} else {
// Вставляем новую
await db.getQuery()(
`INSERT INTO db_settings (db_host, db_port, db_name, db_user, db_password) VALUES ($1,$2,$3,$4,$5)` ,
[db_host, db_port, db_name, db_user, db_password]
);
}
// Пересоздаём пул соединений с новыми настройками
await db.reinitPoolFromDbSettings();
res.json({ success: true });
} catch (error) {
logger.error('Error updating db settings:', error);
next(error);
}
});

View File

@@ -1,6 +1,6 @@
const express = require('express');
const router = express.Router();
const { pool } = require('../db'); // Убедитесь, что путь к вашему db-коннектору правильный
const db = require('../db');
const logger = require('../utils/logger'); // Если используете логгер
/**
@@ -98,7 +98,7 @@ router.get('/codes', async (req, res) => {
if (parent_code) {
try {
const parentResult = await pool.query('SELECT code_level FROM isic_rev4_codes WHERE code = $1', [parent_code]);
const parentResult = await db.getQuery()('SELECT code_level FROM isic_rev4_codes WHERE code = $1', [parent_code]);
if (parentResult.rows.length > 0) {
const parentLevel = parentResult.rows[0].code_level;
if (parentLevel >= 1 && parentLevel < 6) {
@@ -146,7 +146,7 @@ router.get('/codes', async (req, res) => {
}
if (parent_code) {
// Предполагаем, что parent_code уже добавлен в countQueryParams
const parentLevelResult = await pool.query('SELECT code_level FROM isic_rev4_codes WHERE code = $1', [parent_code]); // Нужно будет передать parent_code в countQueryParams
const parentLevelResult = await db.getQuery()('SELECT code_level FROM isic_rev4_codes WHERE code = $1', [parent_code]); // Нужно будет передать parent_code в countQueryParams
if (parentLevelResult.rows.length > 0) {
const parentLevel = parentLevelResult.rows[0].code_level;
if (parentLevel >=1 && parentLevel < 6) {
@@ -174,7 +174,7 @@ router.get('/codes', async (req, res) => {
const queryWhereConditions = [];
if (level) queryWhereConditions.push(`c.code_level = $${currentQueryParamIndex++}`);
if (parent_code) {
const parentLevelResult = await pool.query('SELECT code_level FROM isic_rev4_codes WHERE code = $1', [parent_code]); // Это дублирование, лучше получить parentLevel один раз
const parentLevelResult = await db.getQuery()('SELECT code_level FROM isic_rev4_codes WHERE code = $1', [parent_code]); // Это дублирование, лучше получить parentLevel один раз
if (parentLevelResult.rows.length > 0) {
const parentLevel = parentLevelResult.rows[0].code_level;
if (parentLevel >=1 && parentLevel < 6) {
@@ -193,12 +193,12 @@ router.get('/codes', async (req, res) => {
try {
logger.debug('Executing count query:', finalCountQuery, 'Params:', countQueryParams);
const totalItemsResult = await pool.query(finalCountQuery, countQueryParams);
const totalItemsResult = await db.getQuery()(finalCountQuery, countQueryParams);
const totalItems = parseInt(totalItemsResult.rows[0].total, 10);
// Параметры для основного запроса - это все, что в queryParams (включая limit и offset)
logger.debug('Executing data query:', finalQuery, 'Params:', queryParams);
const result = await pool.query(finalQuery, queryParams);
const result = await db.getQuery()(finalQuery, queryParams);
res.json({
totalItems,
@@ -253,13 +253,13 @@ router.get('/tree', async (req, res) => {
try {
let items;
if (!root_code) { // Если нет root_code, возвращаем секции (уровень 1)
const result = await pool.query(
const result = await db.getQuery()(
"SELECT code, description, code_level FROM isic_rev4_codes WHERE code_level = 1 ORDER BY sort_order, code"
);
items = result.rows.map(row => ({ ...row, children: [] })); // Добавляем пустой массив children
} else {
// Получаем сам root_code
const rootResult = await pool.query(
const rootResult = await db.getQuery()(
"SELECT code, description, code_level FROM isic_rev4_codes WHERE code = $1",
[root_code]
);
@@ -281,7 +281,7 @@ router.get('/tree', async (req, res) => {
if (childrenQuery) {
const childrenResult = await pool.query(childrenQuery, childrenParams);
const childrenResult = await db.getQuery()(childrenQuery, childrenParams);
rootNode.children = childrenResult.rows.map(row => ({ ...row, children: [] }));
}
items = [rootNode];

View File

@@ -5,23 +5,25 @@ const logger = require('../utils/logger');
const { ethers } = require('ethers');
const rpcProviderService = require('../services/rpcProviderService');
const authTokenService = require('../services/authTokenService');
const aiProviderSettingsService = require('../services/aiProviderSettingsService');
const aiAssistant = require('../services/ai-assistant');
// Логируем версию ethers для отладки
logger.info(`Ethers version: ${ethers.version || 'unknown'}`);
// Получение RPC настроек
router.get('/rpc', requireAdmin, async (req, res) => {
router.get('/rpc', requireAdmin, async (req, res, next) => {
try {
const rpcConfigs = await rpcProviderService.getAllRpcProviders();
res.json({ success: true, data: rpcConfigs });
} catch (error) {
logger.error('Ошибка при получении RPC настроек:', error);
res.status(500).json({ success: false, error: 'Ошибка сервера при получении настроек RPC' });
next(error);
}
});
// Добавление/обновление одного или нескольких RPC
router.post('/rpc', requireAdmin, async (req, res) => {
router.post('/rpc', requireAdmin, async (req, res, next) => {
try {
// Если пришёл массив rpcConfigs — bulk-режим
if (Array.isArray(req.body.rpcConfigs)) {
@@ -41,35 +43,35 @@ router.post('/rpc', requireAdmin, async (req, res) => {
res.json({ success: true, message: 'RPC провайдер сохранён' });
} catch (error) {
logger.error('Ошибка при сохранении RPC:', error);
res.status(500).json({ success: false, error: 'Ошибка сервера при сохранении RPC' });
next(error);
}
});
// Удаление одного RPC
router.delete('/rpc/:networkId', requireAdmin, async (req, res) => {
router.delete('/rpc/:networkId', requireAdmin, async (req, res, next) => {
try {
const { networkId } = req.params;
await rpcProviderService.deleteRpcProvider(networkId);
res.json({ success: true, message: 'RPC провайдер удалён' });
} catch (error) {
logger.error('Ошибка при удалении RPC:', error);
res.status(500).json({ success: false, error: 'Ошибка сервера при удалении RPC' });
next(error);
}
});
// Получение токенов для аутентификации
router.get('/auth-tokens', requireAdmin, async (req, res) => {
router.get('/auth-tokens', requireAdmin, async (req, res, next) => {
try {
const authTokens = await authTokenService.getAllAuthTokens();
res.json({ success: true, data: authTokens });
} catch (error) {
logger.error('Ошибка при получении токенов аутентификации:', error);
res.status(500).json({ success: false, error: 'Ошибка сервера при получении токенов аутентификации' });
next(error);
}
});
// Сохранение токенов для аутентификации
router.post('/auth-tokens', requireAdmin, async (req, res) => {
router.post('/auth-tokens', requireAdmin, async (req, res, next) => {
try {
const { authTokens } = req.body;
if (!Array.isArray(authTokens)) {
@@ -79,12 +81,12 @@ router.post('/auth-tokens', requireAdmin, async (req, res) => {
res.json({ success: true, message: 'Токены аутентификации успешно сохранены' });
} catch (error) {
logger.error('Ошибка при сохранении токенов аутентификации:', error);
res.status(500).json({ success: false, error: 'Ошибка сервера при сохранении токенов аутентификации' });
next(error);
}
});
// Добавление/обновление одного токена
router.post('/auth-token', requireAdmin, async (req, res) => {
router.post('/auth-token', requireAdmin, async (req, res, next) => {
try {
const { name, address, network, minBalance } = req.body;
if (!name || !address || !network) {
@@ -94,24 +96,24 @@ router.post('/auth-token', requireAdmin, async (req, res) => {
res.json({ success: true, message: 'Токен аутентификации сохранён' });
} catch (error) {
logger.error('Ошибка при сохранении токена аутентификации:', error);
res.status(500).json({ success: false, error: 'Ошибка сервера при сохранении токена' });
next(error);
}
});
// Удаление одного токена
router.delete('/auth-token/:address/:network', requireAdmin, async (req, res) => {
router.delete('/auth-token/:address/:network', requireAdmin, async (req, res, next) => {
try {
const { address, network } = req.params;
await authTokenService.deleteAuthToken(address, network);
res.json({ success: true, message: 'Токен аутентификации удалён' });
} catch (error) {
logger.error('Ошибка при удалении токена аутентификации:', error);
res.status(500).json({ success: false, error: 'Ошибка сервера при удалении токена' });
next(error);
}
});
// Тестирование RPC соединения
router.post('/rpc-test', requireAdmin, async (req, res) => {
router.post('/rpc-test', requireAdmin, async (req, res, next) => {
try {
const { rpcUrl, networkId } = req.body;
@@ -164,4 +166,76 @@ router.post('/rpc-test', requireAdmin, async (req, res) => {
}
});
// Получить настройки AI-провайдера
router.get('/ai-settings/:provider', requireAdmin, async (req, res, next) => {
try {
const { provider } = req.params;
const settings = await aiProviderSettingsService.getProviderSettings(provider);
res.json({ success: true, settings });
} catch (error) {
logger.error('Ошибка при получении AI-настроек:', error);
next(error);
}
});
// Сохранить/обновить настройки AI-провайдера
router.put('/ai-settings/:provider', requireAdmin, async (req, res, next) => {
try {
const { provider } = req.params;
const { api_key, base_url, selected_model } = req.body;
const updated = await aiProviderSettingsService.upsertProviderSettings({ provider, api_key, base_url, selected_model });
res.json({ success: true, settings: updated });
} catch (error) {
logger.error('Ошибка при сохранении AI-настроек:', error);
next(error);
}
});
// Удалить настройки AI-провайдера
router.delete('/ai-settings/:provider', requireAdmin, async (req, res, next) => {
try {
const { provider } = req.params;
await aiProviderSettingsService.deleteProviderSettings(provider);
res.json({ success: true });
} catch (error) {
logger.error('Ошибка при удалении AI-настроек:', error);
next(error);
}
});
// Получить список моделей для провайдера
router.get('/ai-settings/:provider/models', requireAdmin, async (req, res, next) => {
try {
const { provider } = req.params;
const settings = await aiProviderSettingsService.getProviderSettings(provider);
let models = [];
if (provider === 'ollama') {
models = await aiAssistant.getAvailableModels();
} else {
models = await aiProviderSettingsService.getProviderModels(provider, settings || {});
}
res.json({ success: true, models });
} catch (error) {
logger.error('Ошибка при получении моделей AI:', error);
res.status(500).json({ success: false, error: error.message });
}
});
// Проверить валидность ключа (verify)
router.post('/ai-settings/:provider/verify', requireAdmin, async (req, res, next) => {
try {
const { provider } = req.params;
const { api_key, base_url } = req.body;
const result = await aiProviderSettingsService.verifyProviderKey(provider, { api_key, base_url });
if (result.success) {
res.json({ success: true });
} else {
res.status(400).json({ success: false, error: result.error });
}
} catch (error) {
logger.error('Ошибка при проверке AI-ключа:', error);
res.status(500).json({ success: false, error: error.message });
}
});
module.exports = router;

View File

@@ -4,7 +4,7 @@ const logger = require('../utils/logger');
const authService = require('../services/auth-service');
// Получение балансов токенов пользователя по токенам из базы
router.get('/balances', async (req, res) => {
router.get('/balances', async (req, res, next) => {
try {
const address = req.query.address;
if (!address) {
@@ -15,7 +15,7 @@ router.get('/balances', async (req, res) => {
res.json({ success: true, data: balances });
} catch (error) {
logger.error('Error fetching token balances:', error);
res.status(500).json({ success: false, error: 'Failed to fetch token balances' });
next(error);
}
});

View File

@@ -20,53 +20,42 @@ router.get('/:address', (req, res) => {
});
// Маршрут для обновления языка пользователя
router.post('/update-language', requireAuth, async (req, res) => {
router.post('/update-language', requireAuth, async (req, res, next) => {
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.getQuery()('UPDATE users SET preferred_language = $1 WHERE id = $2', [language, userId]);
res.json({ success: true });
} catch (error) {
logger.error('Error updating language:', error);
res.status(500).json({ error: 'Внутренняя ошибка сервера' });
next(error);
}
});
// Маршрут для обновления имени и фамилии пользователя
router.post('/update-profile', requireAuth, async (req, res) => {
router.post('/update-profile', requireAuth, async (req, res, next) => {
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', [
await db.getQuery()('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);
res.status(500).json({ error: 'Внутренняя ошибка сервера' });
next(error);
}
});

View File

@@ -1,7 +1,8 @@
const fs = require('fs').promises;
const path = require('path');
require('dotenv').config();
const { pool } = require('../db');
const { getPool } = require('../db');
const pool = getPool();
const logger = require('../utils/logger');
async function runMigrations() {

View File

@@ -0,0 +1,105 @@
const db = require('../db');
const OpenAI = require('openai');
const Anthropic = require('@anthropic-ai/sdk');
const { GoogleGenAI } = require('@google/genai');
const TABLE = 'ai_providers_settings';
async function getProviderSettings(provider) {
const { rows } = await db.getQuery()(
`SELECT * FROM ${TABLE} WHERE provider = $1 LIMIT 1`,
[provider]
);
return rows[0] || null;
}
async function upsertProviderSettings({ provider, api_key, base_url, selected_model }) {
const { rows } = await db.getQuery()(
`INSERT INTO ${TABLE} (provider, api_key, base_url, selected_model, updated_at)
VALUES ($1, $2, $3, $4, NOW())
ON CONFLICT (provider) DO UPDATE SET
api_key = EXCLUDED.api_key,
base_url = EXCLUDED.base_url,
selected_model = EXCLUDED.selected_model,
updated_at = NOW()
RETURNING *`,
[provider, api_key, base_url, selected_model]
);
return rows[0];
}
async function deleteProviderSettings(provider) {
await db.getQuery()(
`DELETE FROM ${TABLE} WHERE provider = $1`,
[provider]
);
}
async function getProviderModels(provider, { api_key, base_url } = {}) {
try {
if (provider === 'openai') {
const client = new OpenAI({ apiKey: api_key, baseURL: base_url });
const res = await client.models.list();
return res.data ? res.data.map(m => ({ id: m.id, ...m })) : [];
}
if (provider === 'anthropic') {
const client = new Anthropic({ apiKey: api_key, baseURL: base_url });
const res = await client.models.list();
return res.data ? res.data.map(m => ({ id: m.id, ...m })) : [];
}
if (provider === 'google') {
const ai = new GoogleGenAI({ apiKey: api_key, baseUrl: base_url });
const pager = await ai.models.list();
const models = [];
for await (const model of pager) {
models.push(model);
}
return models;
}
if (provider === 'ollama') {
// Для Ollama — через ai-assistant.js
return [];
}
return [];
} catch (error) {
return [];
}
}
async function verifyProviderKey(provider, { api_key, base_url } = {}) {
try {
if (provider === 'openai') {
const client = new OpenAI({ apiKey: api_key, baseURL: base_url });
await client.models.list();
return { success: true };
}
if (provider === 'anthropic') {
const client = new Anthropic({ apiKey: api_key, baseURL: base_url });
await client.models.list();
return { success: true };
}
if (provider === 'google') {
const ai = new GoogleGenAI({ apiKey: api_key, baseUrl: base_url });
const pager = await ai.models.list();
for await (const _ of pager) {
break;
}
return { success: true };
}
if (provider === 'ollama') {
// Для Ollama — всегда true (локальный)
return { success: true };
}
return { success: false, error: 'Unknown provider' };
} catch (error) {
return { success: false, error: error.message };
}
}
module.exports = {
getProviderSettings,
upsertProviderSettings,
deleteProviderSettings,
getProviderModels,
verifyProviderKey,
};

View File

@@ -1,7 +1,7 @@
const db = require('../db');
async function getAllAuthTokens() {
const { rows } = await db.query('SELECT * FROM auth_tokens ORDER BY id');
const { rows } = await db.getQuery()('SELECT * FROM auth_tokens ORDER BY id');
return rows;
}

View File

@@ -1,13 +1,13 @@
const { pool } = require('../db');
const verificationService = require('./verification-service');
const logger = require('../utils/logger');
const emailBot = require('./emailBot');
const EmailBotService = require('./emailBot');
const db = require('../db');
const authService = require('./auth-service');
class EmailAuth {
constructor() {
this.emailBot = emailBot;
this.emailBot = new EmailBotService();
}
async initEmailAuth(session, email) {
@@ -17,7 +17,7 @@ class EmailAuth {
}
// Проверяем, существует ли пользователь с таким email
const existingEmailUser = await db.query(
const existingEmailUser = await db.getQuery()(
`SELECT u.id FROM users u
JOIN user_identities i ON u.id = i.user_id
WHERE i.provider = 'email' AND i.provider_id = $1`,

View File

@@ -1,4 +1,4 @@
const { pool } = require('../db');
const db = require('../db');
const nodemailer = require('nodemailer');
const Imap = require('imap');
const simpleParser = require('mailparser').simpleParser;
@@ -6,29 +6,37 @@ const { processMessage } = require('./ai-assistant');
const { inspect } = require('util');
const logger = require('../utils/logger');
// Конфигурация для отправки писем
const transporter = nodemailer.createTransport({
host: process.env.EMAIL_SMTP_HOST || 'smtp.hostland.ru',
port: process.env.EMAIL_SMTP_PORT || 465,
class EmailBotService {
async getSettingsFromDb() {
const { rows } = await db.getQuery()('SELECT * FROM email_settings ORDER BY id LIMIT 1');
if (!rows.length) throw new Error('Email settings not found in DB');
return rows[0];
}
async getTransporter() {
const settings = await this.getSettingsFromDb();
return nodemailer.createTransport({
host: settings.smtp_host,
port: settings.smtp_port,
secure: true,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASSWORD,
user: settings.smtp_user,
pass: settings.smtp_password,
},
pool: true,
maxConnections: 3,
maxMessages: 5,
tls: {
rejectUnauthorized: false,
},
});
tls: { rejectUnauthorized: false },
});
}
// Конфигурация для получения писем
const imapConfig = {
user: process.env.EMAIL_USER,
password: process.env.EMAIL_PASSWORD,
host: process.env.EMAIL_IMAP_HOST,
port: process.env.EMAIL_IMAP_PORT,
async getImapConfig() {
const settings = await this.getSettingsFromDb();
return {
user: settings.smtp_user,
password: settings.smtp_password,
host: settings.imap_host,
port: settings.imap_port,
tls: true,
tlsOptions: { rejectUnauthorized: false },
keepalive: {
@@ -36,29 +44,7 @@ const imapConfig = {
idleInterval: 300000,
forceNoop: true,
},
};
class EmailBotService {
constructor() {
this.transporter = transporter;
this.imap = new Imap(imapConfig);
this.initialize();
}
initialize() {
this.imap.once('error', (err) => {
logger.error(`IMAP connection error: ${err.message}`);
setTimeout(() => {
try {
if (this.imap.state !== 'connected') {
this.imap = new Imap(imapConfig);
this.initialize();
}
} catch (e) {
logger.error(`Error reconnecting IMAP: ${e.message}`);
}
}, 60000);
});
};
}
// Метод для инициализации email верификации
@@ -77,24 +63,16 @@ class EmailBotService {
// Отправка кода верификации
async sendVerificationCode(email, code) {
try {
const settings = await this.getSettingsFromDb();
const transporter = await this.getTransporter();
const mailOptions = {
from: process.env.EMAIL_USER,
from: settings.from_email,
to: email,
subject: 'Код подтверждения',
text: `Ваш код подтверждения: ${code}\n\nКод действителен в течение 15 минут.`,
html: `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h2 style="color: #333;">Код подтверждения</h2>
<p style="font-size: 16px; color: #666;">Ваш код подтверждения:</p>
<div style="background-color: #f5f5f5; padding: 15px; border-radius: 5px; text-align: center; margin: 20px 0;">
<span style="font-size: 24px; font-weight: bold; color: #333;">${code}</span>
</div>
<p style="font-size: 14px; color: #999;">Код действителен в течение 15 минут.</p>
</div>
`,
html: `<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;"><h2 style="color: #333;">Код подтверждения</h2><p style="font-size: 16px; color: #666;">Ваш код подтверждения:</p><div style="background-color: #f5f5f5; padding: 15px; border-radius: 5px; text-align: center; margin: 20px 0;"><span style="font-size: 24px; font-weight: bold; color: #333;">${code}</span></div><p style="font-size: 14px; color: #999;">Код действителен в течение 15 минут.</p></div>`,
};
await this.transporter.sendMail(mailOptions);
await transporter.sendMail(mailOptions);
logger.info(`Verification code sent to ${email}`);
} catch (error) {
logger.error('Error sending verification code:', error);
@@ -187,14 +165,15 @@ class EmailBotService {
// Метод для отправки email
async sendEmail(to, subject, text) {
try {
const settings = await this.getSettingsFromDb();
const transporter = await this.getTransporter();
const mailOptions = {
from: process.env.EMAIL_USER,
from: settings.from_email,
to,
subject,
text,
};
await this.transporter.sendMail(mailOptions);
await transporter.sendMail(mailOptions);
logger.info(`Email sent to ${to}`);
return true;
} catch (error) {
@@ -204,5 +183,4 @@ class EmailBotService {
}
}
// Экспортируем singleton instance
module.exports = new EmailBotService();
module.exports = EmailBotService;

View File

@@ -62,7 +62,7 @@ class IdentityService {
);
try {
await db.query(
await db.getQuery()(
'INSERT INTO guest_user_mapping (user_id, guest_id) VALUES ($1, $2) ON CONFLICT (guest_id) DO UPDATE SET user_id = $1',
[userId, normalizedProviderId]
);
@@ -91,7 +91,7 @@ class IdentityService {
);
// Проверяем, существует ли уже такой идентификатор
const existingResult = await db.query(
const existingResult = await db.getQuery()(
`SELECT user_id FROM user_identities WHERE provider = $1 AND provider_id = $2`,
[normalizedProvider, normalizedProviderId]
);
@@ -116,7 +116,7 @@ class IdentityService {
}
} else {
// Создаем новую запись
await db.query(
await db.getQuery()(
`INSERT INTO user_identities (user_id, provider, provider_id)
VALUES ($1, $2, $3)`,
[userId, normalizedProvider, normalizedProviderId]
@@ -148,7 +148,7 @@ class IdentityService {
return [];
}
const result = await db.query(
const result = await db.getQuery()(
`SELECT provider, provider_id FROM user_identities WHERE user_id = $1`,
[userId]
);
@@ -174,7 +174,7 @@ class IdentityService {
return [];
}
const result = await db.query(
const result = await db.getQuery()(
`SELECT provider_id FROM user_identities WHERE user_id = $1 AND provider = $2`,
[userId, provider]
);
@@ -211,7 +211,7 @@ class IdentityService {
const { provider: normalizedProvider, providerId: normalizedProviderId } =
this.normalizeIdentity(provider, providerId);
const result = await db.query(
const result = await db.getQuery()(
`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`,
@@ -255,7 +255,7 @@ class IdentityService {
// Нормализуем провайдера
const normalizedProvider = provider.toLowerCase();
const result = await db.query(
const result = await db.getQuery()(
`SELECT provider, provider_id, created_at, updated_at
FROM user_identities
WHERE user_id = $1 AND provider = $2
@@ -320,7 +320,7 @@ class IdentityService {
// Сохраняем гостевые идентификаторы в guest_user_mapping
if (session.guestId) {
try {
await db.query(
await db.getQuery()(
'INSERT INTO guest_user_mapping (user_id, guest_id) VALUES ($1, $2) ON CONFLICT (guest_id) DO UPDATE SET user_id = $1',
[userId, session.guestId]
);
@@ -333,7 +333,7 @@ class IdentityService {
if (session.previousGuestId && session.previousGuestId !== session.guestId) {
try {
await db.query(
await db.getQuery()(
'INSERT INTO guest_user_mapping (user_id, guest_id) VALUES ($1, $2) ON CONFLICT (guest_id) DO UPDATE SET user_id = $1',
[userId, session.previousGuestId]
);
@@ -479,7 +479,7 @@ class IdentityService {
for (const [provider, providerId] of Object.entries(identities)) {
if (!providerId) continue;
const result = await db.query(
const result = await db.getQuery()(
`SELECT DISTINCT user_id
FROM user_identities
WHERE provider = $1 AND provider_id = $2`,
@@ -510,7 +510,7 @@ class IdentityService {
return { success: false, error: 'Missing required parameters' };
}
const { provider: normalizedProvider, providerId: normalizedProviderId } = this.normalizeIdentity(provider, providerId);
const result = await db.query(
const result = await db.getQuery()(
`DELETE FROM user_identities WHERE user_id = $1 AND provider = $2 AND provider_id = $3`,
[userId, normalizedProvider, normalizedProviderId]
);

View File

@@ -1,7 +1,7 @@
const db = require('../db');
async function getAllRpcProviders() {
const { rows } = await db.query('SELECT * FROM rpc_providers ORDER BY id');
const { rows } = await db.getQuery()('SELECT * FROM rpc_providers ORDER BY id');
return rows;
}

View File

@@ -51,7 +51,7 @@ class SessionService {
}
// Получаем все гостевые ID для текущего пользователя из новой таблицы
const guestIdsResult = await db.query(
const guestIdsResult = await db.getQuery()(
'SELECT guest_id FROM guest_user_mapping WHERE user_id = $1',
[userId]
);

View File

@@ -6,11 +6,21 @@ const verificationService = require('./verification-service');
const crypto = require('crypto');
let botInstance = null;
let telegramSettingsCache = null;
async function getTelegramSettings() {
if (telegramSettingsCache) return telegramSettingsCache;
const { rows } = await db.getQuery()('SELECT * FROM telegram_settings ORDER BY id LIMIT 1');
if (!rows.length) throw new Error('Telegram settings not found in DB');
telegramSettingsCache = rows[0];
return telegramSettingsCache;
}
// Создание и настройка бота
async function getBot() {
if (!botInstance) {
botInstance = new Telegraf(process.env.TELEGRAM_BOT_TOKEN);
const settings = await getTelegramSettings();
botInstance = new Telegraf(settings.bot_token);
// Обработка команды /start
botInstance.command('start', (ctx) => {
@@ -23,7 +33,7 @@ async function getBot() {
try {
// Получаем код верификации для всех активных кодов с провайдером telegram
const codeResult = await db.query(
const codeResult = await db.getQuery()(
`SELECT * FROM verification_codes
WHERE code = $1
AND provider = 'telegram'
@@ -44,14 +54,14 @@ async function getBot() {
let userRole = 'user'; // Роль по умолчанию
// Отмечаем код как использованный
await db.query('UPDATE verification_codes SET used = true WHERE id = $1', [
await db.getQuery()('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(
const existingTelegramUser = await db.getQuery()(
`SELECT ui.user_id
FROM user_identities ui
WHERE ui.provider = 'telegram' AND ui.provider_id = $1`,
@@ -68,7 +78,7 @@ async function getBot() {
// Используем userId из кода верификации
userId = linkedUserId;
// Связываем Telegram с этим пользователем
await db.query(
await db.getQuery()(
`INSERT INTO user_identities
(user_id, provider, provider_id, created_at)
VALUES ($1, $2, $3, NOW())`,
@@ -81,7 +91,7 @@ async function getBot() {
// Проверяем, есть ли пользователь, связанный с гостевым идентификатором
let existingUserWithGuestId = null;
if (providerId) {
const guestUserResult = await db.query(
const guestUserResult = await db.getQuery()(
`SELECT user_id FROM guest_user_mapping WHERE guest_id = $1`,
[providerId]
);
@@ -96,7 +106,7 @@ async function getBot() {
if (existingUserWithGuestId) {
// Используем существующего пользователя и добавляем ему Telegram идентификатор
userId = existingUserWithGuestId;
await db.query(
await db.getQuery()(
`INSERT INTO user_identities
(user_id, provider, provider_id, created_at)
VALUES ($1, $2, $3, NOW())`,
@@ -105,14 +115,14 @@ async function getBot() {
logger.info(`Linked Telegram account ${ctx.from.id} to existing user ${userId}`);
} else {
// Создаем нового пользователя, если не нашли существующего
const userResult = await db.query(
const userResult = await db.getQuery()(
'INSERT INTO users (created_at, role) VALUES (NOW(), $1) RETURNING id',
['user']
);
userId = userResult.rows[0].id;
// Связываем Telegram с новым пользователем
await db.query(
await db.getQuery()(
`INSERT INTO user_identities
(user_id, provider, provider_id, created_at)
VALUES ($1, $2, $3, NOW())`,
@@ -121,7 +131,7 @@ async function getBot() {
// Если был гостевой ID, связываем его с новым пользователем
if (providerId) {
await db.query(
await db.getQuery()(
`INSERT INTO guest_user_mapping
(user_id, guest_id)
VALUES ($1, $2)
@@ -147,15 +157,15 @@ async function getBot() {
logger.info(`[TelegramBot] Role for user ${userId} determined as: ${userRole}`);
// Опционально: Обновить роль в таблице users
const currentUser = await db.query('SELECT role FROM users WHERE id = $1', [userId]);
const currentUser = await db.getQuery()('SELECT role FROM users WHERE id = $1', [userId]);
if (currentUser.rows.length > 0 && currentUser.rows[0].role !== userRole) {
await db.query('UPDATE users SET role = $1 WHERE id = $2', [userRole, userId]);
await db.getQuery()('UPDATE users SET role = $1 WHERE id = $2', [userRole, userId]);
logger.info(`[TelegramBot] Updated user role in DB to ${userRole}`);
}
} else {
logger.info(`[TelegramBot] No linked wallet found for user ${userId}. Checking current DB role.`);
// Если кошелька нет, берем текущую роль из базы
const currentUser = await db.query('SELECT role FROM users WHERE id = $1', [userId]);
const currentUser = await db.getQuery()('SELECT role FROM users WHERE id = $1', [userId]);
if (currentUser.rows.length > 0) {
userRole = currentUser.rows[0].role;
}
@@ -164,7 +174,7 @@ async function getBot() {
logger.error(`[TelegramBot] Error checking admin role for user ${userId}:`, roleCheckError);
// В случае ошибки берем роль из базы или оставляем 'user'
try {
const currentUser = await db.query('SELECT role FROM users WHERE id = $1', [userId]);
const currentUser = await db.getQuery()('SELECT role FROM users WHERE id = $1', [userId]);
if (currentUser.rows.length > 0) { userRole = currentUser.rows[0].role; }
} catch (dbError) { /* ignore */ }
}
@@ -181,7 +191,7 @@ async function getBot() {
try {
// Ищем сессию, где есть userId и она не истекла (проверка expires_at)
// Сортируем по expires_at DESC чтобы взять самую "свежую", если их несколько
const sessionResult = await db.query(
const sessionResult = await db.getQuery()(
`SELECT sid FROM session
WHERE sess ->> 'userId' = $1
AND expire > NOW()
@@ -195,7 +205,7 @@ async function getBot() {
logger.info(`[telegramBot] Found active session ID ${activeSessionId} for user ${userId}`);
// Обновляем найденную сессию в базе данных, добавляя/перезаписывая данные Telegram
const updateResult = await db.query(
const updateResult = await db.getQuery()(
`UPDATE session
SET sess = (sess::jsonb || $1::jsonb)::json
WHERE sid = $2`,
@@ -275,7 +285,7 @@ async function initTelegramAuth(session) {
const guestId = session.guestId || tempId;
// Связываем гостевой ID с текущим пользователем
await db.query(
await db.getQuery()(
`INSERT INTO guest_user_mapping (user_id, guest_id)
VALUES ($1, $2)
ON CONFLICT (guest_id) DO UPDATE SET user_id = $1`,
@@ -298,9 +308,10 @@ async function initTelegramAuth(session) {
`[initTelegramAuth] Created verification code for guestId: ${session.guestId || tempId}${session.authenticated ? `, userId: ${session.userId}` : ''}`
);
const settings = await getTelegramSettings();
return {
verificationCode: code,
botLink: `https://t.me/${process.env.TELEGRAM_BOT_USERNAME}`,
botLink: `https://t.me/${settings.bot_username}`,
};
} catch (error) {
logger.error('Error initializing Telegram auth:', error);
@@ -308,8 +319,13 @@ async function initTelegramAuth(session) {
}
}
function clearSettingsCache() {
telegramSettingsCache = null;
}
module.exports = {
getBot,
stopBot,
initTelegramAuth,
clearSettingsCache,
};

View File

@@ -29,14 +29,14 @@ class VerificationService {
// Если userId не указан, добавляем запись без ссылки на пользователя
if (userId === null || userId === undefined) {
await db.query(
await db.getQuery()(
`INSERT INTO verification_codes
(code, provider, provider_id, expires_at)
VALUES ($1, $2, $3, $4)`,
[code, provider, providerId, expiresAt]
);
} else {
await db.query(
await db.getQuery()(
`INSERT INTO verification_codes
(code, provider, provider_id, user_id, expires_at)
VALUES ($1, $2, $3, $4, $5)`,
@@ -67,7 +67,7 @@ class VerificationService {
logger.info(`Normalized code: ${normalizedCode}`);
// Проверим, есть ли такой код в базе (для отладки)
const checkResult = await db.query(
const checkResult = await db.getQuery()(
`SELECT code FROM verification_codes
WHERE provider = $1
AND provider_id = $2
@@ -84,7 +84,7 @@ class VerificationService {
logger.warn(`No active codes found for ${provider}:${providerId}`);
}
const result = await db.query(
const result = await db.getQuery()(
`SELECT * FROM verification_codes
WHERE code = $1
AND provider = $2
@@ -104,7 +104,10 @@ class VerificationService {
const verification = result.rows[0];
// Отмечаем код как использованный
await db.query('UPDATE verification_codes SET used = true WHERE id = $1', [verification.id]);
await db.getQuery()(
'UPDATE verification_codes SET used = true WHERE id = $1',
[verification.id]
);
logger.info(`Code verified successfully for ${provider}:${providerId}`);
return {
@@ -126,7 +129,7 @@ class VerificationService {
// Очистка истекших кодов
async cleanupExpiredCodes() {
try {
const result = await db.query(
const result = await db.getQuery()(
'DELETE FROM verification_codes WHERE expires_at <= NOW() RETURNING id'
);
logger.info(`Cleaned up ${result.rowCount} expired verification codes`);

View File

@@ -21,7 +21,7 @@ function generateVerificationCode(length = 6) {
// Проверка существования идентификатора пользователя
async function checkUserIdentity(userId, provider, providerId) {
const result = await db.query(
const result = await db.getQuery()(
'SELECT * FROM user_identities WHERE user_id = $1 AND provider = $2 AND provider_id = $3',
[userId, provider, providerId]
);
@@ -31,7 +31,7 @@ async function checkUserIdentity(userId, provider, providerId) {
// Добавление новой идентификации
async function addUserIdentity(userId, provider, providerId) {
try {
await db.query(
await db.getQuery()(
'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)',
[userId, provider, providerId]
);

View File

@@ -12,6 +12,11 @@
resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.11.0.tgz#42cc67c5baa407ac25059fcd7d405cc5ecdb0c33"
integrity sha512-/3DDPKHqqIqxUULp8yP4zODUY1i+2xvVWsv8A79xGWdCAG+8sb0hRh0Rk2QyOJUnnbyPUAZYcpBuRe3nS2OIUg==
"@anthropic-ai/sdk@^0.51.0":
version "0.51.0"
resolved "https://registry.yarnpkg.com/@anthropic-ai/sdk/-/sdk-0.51.0.tgz#2cd022c47e0eb6f4d645d8e8ed9ee0f3c5745ea8"
integrity sha512-fAFC/uHhyzfw7rs65EPVV+scXDytGNm5BjttxHf6rP/YGvaBRKEvp2lwyuMigTwMI95neeG4bzrZigz7KCikjw==
"@colors/colors@1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9"
@@ -354,6 +359,16 @@
resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d"
integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==
"@google/genai@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@google/genai/-/genai-1.0.1.tgz#ccef337f733f27fdc2e5bf741af51889e2847082"
integrity sha512-qf8sq9vpuKUeBKukAn43z2eC1I/Jw63b9wo6O+1x3EIroF3oDouJOtW1AzwvfO+9gzCPfLjuCUONhMKiBC8vkQ==
dependencies:
google-auth-library "^9.14.2"
ws "^8.18.0"
zod "^3.22.4"
zod-to-json-schema "^3.22.4"
"@humanfs/core@^0.19.1":
version "0.19.1"
resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77"
@@ -1223,6 +1238,11 @@ agent-base@6:
dependencies:
debug "4"
agent-base@^7.1.2:
version "7.1.3"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1"
integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==
agentkeepalive@^4.2.1:
version "4.6.0"
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a"
@@ -1504,7 +1524,7 @@ base-x@^3.0.2:
dependencies:
safe-buffer "^5.0.1"
base64-js@^1.5.1:
base64-js@^1.3.0, base64-js@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
@@ -1516,6 +1536,11 @@ bcrypt-pbkdf@^1.0.0:
dependencies:
tweetnacl "^0.14.3"
bignumber.js@^9.0.0:
version "9.3.0"
resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.3.0.tgz#bdba7e2a4c1a2eba08290e8dcad4f36393c92acd"
integrity sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==
binary-extensions@^2.0.0, binary-extensions@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.3.0.tgz#f6e14a97858d327252200242d4ccfe522c445522"
@@ -1671,6 +1696,11 @@ buffer-alloc@^1.2.0:
buffer-alloc-unsafe "^1.1.0"
buffer-fill "^1.0.0"
buffer-equal-constant-time@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==
buffer-fill@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c"
@@ -2309,6 +2339,13 @@ ecc-jsbn@~0.1.1:
jsbn "~0.1.0"
safer-buffer "^2.1.0"
ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11:
version "1.0.11"
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
dependencies:
safe-buffer "^5.0.1"
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@@ -2842,7 +2879,7 @@ express@^4.21.2:
utils-merge "1.0.1"
vary "~1.1.2"
extend@~3.0.2:
extend@^3.0.2, extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==
@@ -3117,6 +3154,26 @@ functions-have-names@^1.2.3:
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
gaxios@^6.0.0, gaxios@^6.1.1:
version "6.7.1"
resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-6.7.1.tgz#ebd9f7093ede3ba502685e73390248bb5b7f71fb"
integrity sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==
dependencies:
extend "^3.0.2"
https-proxy-agent "^7.0.1"
is-stream "^2.0.0"
node-fetch "^2.6.9"
uuid "^9.0.1"
gcp-metadata@^6.1.0:
version "6.1.1"
resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-6.1.1.tgz#f65aa69f546bc56e116061d137d3f5f90bdec494"
integrity sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==
dependencies:
gaxios "^6.1.1"
google-logging-utils "^0.0.2"
json-bigint "^1.0.0"
get-caller-file@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
@@ -3295,6 +3352,23 @@ globby@^10.0.1:
merge2 "^1.2.3"
slash "^3.0.0"
google-auth-library@^9.14.2:
version "9.15.1"
resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-9.15.1.tgz#0c5d84ed1890b2375f1cd74f03ac7b806b392928"
integrity sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==
dependencies:
base64-js "^1.3.0"
ecdsa-sig-formatter "^1.0.11"
gaxios "^6.1.1"
gcp-metadata "^6.1.0"
gtoken "^7.0.0"
jws "^4.0.0"
google-logging-utils@^0.0.2:
version "0.0.2"
resolved "https://registry.yarnpkg.com/google-logging-utils/-/google-logging-utils-0.0.2.tgz#5fd837e06fa334da450433b9e3e1870c1594466a"
integrity sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==
gopd@^1.0.1, gopd@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
@@ -3305,6 +3379,14 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
gtoken@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-7.1.0.tgz#d61b4ebd10132222817f7222b1e6064bd463fc26"
integrity sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==
dependencies:
gaxios "^6.0.0"
jws "^4.0.0"
handlebars@^4.0.1:
version "4.7.8"
resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9"
@@ -3550,6 +3632,14 @@ https-proxy-agent@^5.0.0:
agent-base "6"
debug "4"
https-proxy-agent@^7.0.1:
version "7.0.6"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9"
integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==
dependencies:
agent-base "^7.1.2"
debug "4"
humanize-ms@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed"
@@ -3951,6 +4041,13 @@ jsbn@~0.1.0:
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==
json-bigint@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1"
integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==
dependencies:
bignumber.js "^9.0.0"
json-buffer@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
@@ -4027,6 +4124,23 @@ jsprim@^2.0.2:
json-schema "0.4.0"
verror "1.10.0"
jwa@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.1.tgz#bf8176d1ad0cd72e0f3f58338595a13e110bc804"
integrity sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==
dependencies:
buffer-equal-constant-time "^1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"
jws@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.0.tgz#2d4e8cf6a318ffaa12615e9dec7e86e6c97310f4"
integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==
dependencies:
jwa "^2.0.0"
safe-buffer "^5.0.1"
keccak@^3.0.0, keccak@^3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d"
@@ -4520,7 +4634,7 @@ node-emoji@^1.10.0:
dependencies:
lodash "^4.17.21"
node-fetch@^2.6.7, node-fetch@^2.7.0:
node-fetch@^2.6.7, node-fetch@^2.6.9, node-fetch@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
@@ -4663,6 +4777,19 @@ one-time@^1.0.0:
dependencies:
fn.name "1.x.x"
openai@^4.102.0:
version "4.102.0"
resolved "https://registry.yarnpkg.com/openai/-/openai-4.102.0.tgz#fcf09d2ad2b33eb399fe79bb43df1872bf2a15c8"
integrity sha512-CWk15CMhPSHNZnjz+6rwVYV551xaC8CwOd7/zxImrC1btEo37dX/Ii5tBKWfqqxqyzpJ6p3Y4bICzzKhW03WhQ==
dependencies:
"@types/node" "^18.11.18"
"@types/node-fetch" "^2.6.4"
abort-controller "^3.0.0"
agentkeepalive "^4.2.1"
form-data-encoder "1.7.2"
formdata-node "^4.3.2"
node-fetch "^2.6.7"
openai@^4.93.0:
version "4.96.0"
resolved "https://registry.yarnpkg.com/openai/-/openai-4.96.0.tgz#d1a821e99949ac2c55709f4e28e18bb1d9fd8ef9"
@@ -6355,6 +6482,11 @@ uuid@^10.0.0:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294"
integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==
uuid@^9.0.1:
version "9.0.1"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30"
integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==
v8-compile-cache-lib@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf"
@@ -6595,7 +6727,7 @@ write-file-atomic@3.0.3:
signal-exit "^3.0.2"
typedarray-to-buffer "^3.1.5"
ws@8.17.1, ws@8.18.1, ws@^7.4.6, ws@^8.18.1:
ws@8.17.1, ws@8.18.1, ws@^7.4.6, ws@^8.18.0, ws@^8.18.1:
version "8.18.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.1.tgz#ea131d3784e1dfdff91adb0a4a116b127515e3cb"
integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==
@@ -6653,7 +6785,7 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
zod-to-json-schema@^3.22.3, zod-to-json-schema@^3.22.5, zod-to-json-schema@^3.24.1:
zod-to-json-schema@^3.22.3, zod-to-json-schema@^3.22.4, zod-to-json-schema@^3.22.5, zod-to-json-schema@^3.24.1:
version "3.24.5"
resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz#d1095440b147fb7c2093812a53c54df8d5df50a3"
integrity sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==

View File

@@ -142,7 +142,7 @@ const props = defineProps({
isLoadingTokens: Boolean
});
const emit = defineEmits(['update:modelValue', 'wallet-auth', 'disconnect-wallet', 'telegram-auth', 'email-auth']);
const emit = defineEmits(['update:modelValue', 'wallet-auth', 'disconnect-wallet', 'telegram-auth', 'email-auth', 'cancel-email-auth']);
const { deleteIdentity } = useAuth();

View File

@@ -36,24 +36,24 @@
</template>
<script setup>
import { ref, computed } from 'vue';
import axios from '@/api/axios';
import { useAuth } from '@/composables/useAuth';
import { ref, computed } from 'vue';
import axios from '@/api/axios';
import { useAuth } from '@/composables/useAuth';
const emit = defineEmits(['close', 'success']);
const { linkIdentity } = useAuth();
const { linkIdentity } = useAuth();
const email = ref('');
const code = ref('');
const error = ref('');
const isLoading = ref(false);
const showVerification = ref(false);
const email = ref('');
const code = ref('');
const error = ref('');
const isLoading = ref(false);
const showVerification = ref(false);
const isValidEmail = computed(() => {
const isValidEmail = computed(() => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value);
});
});
const requestCode = async () => {
const requestCode = async () => {
try {
isLoading.value = true;
error.value = '';
@@ -70,9 +70,9 @@ const requestCode = async () => {
} finally {
isLoading.value = false;
}
};
};
const verifyCode = async () => {
const verifyCode = async () => {
try {
isLoading.value = true;
error.value = '';
@@ -90,14 +90,14 @@ const verifyCode = async () => {
} finally {
isLoading.value = false;
}
};
};
const resetForm = () => {
const resetForm = () => {
email.value = '';
code.value = '';
error.value = '';
showVerification.value = false;
};
};
</script>
<style scoped>

View File

@@ -427,48 +427,16 @@ export function useAuth() {
/**
* Связывает новый идентификатор с текущим аккаунтом пользователя
* @param {string} provider - Тип идентификатора (wallet, email, telegram)
* @param {string} providerId - Значение идентификатора
* @param {string} type - Тип идентификатора (wallet, email, telegram)
* @param {string} value - Значение идентификатора
* @returns {Promise<Object>} - Результат операции
*/
const linkIdentity = async (provider, providerId) => {
try {
if (!isAuthenticated.value) {
console.error('Невозможно связать идентификатор: пользователь не аутентифицирован');
return { success: false, error: 'Пользователь не аутентифицирован' };
}
const response = await axios.post('/api/auth/identities/link', {
type: provider,
value: providerId,
const linkIdentity = async (type, value) => {
const response = await axios.post('/api/link', {
type,
value,
});
if (response.data.success) {
// Обновляем локальные данные при необходимости
if (provider === 'wallet') {
address.value = providerId;
isAdmin.value = response.data.isAdmin || false;
} else if (provider === 'telegram') {
telegramId.value = providerId;
} else if (provider === 'email') {
email.value = providerId;
}
// Обновляем список идентификаторов
await updateIdentities();
console.log(`Идентификатор ${provider} успешно связан с аккаунтом`);
return { success: true };
}
return response.data;
} catch (error) {
console.error('Ошибка при связывании идентификатора:', error);
return {
success: false,
error: error.response?.data?.error || error.message,
};
}
};
/**
@@ -478,16 +446,8 @@ export function useAuth() {
* @returns {Promise<Object>} - Результат операции
*/
const deleteIdentity = async (provider, providerId) => {
try {
const response = await axios.delete(`/api/identities/${provider}/${encodeURIComponent(providerId)}`);
if (response.data.success) {
await updateIdentities();
return { success: true };
}
return { success: false, error: response.data.error };
} catch (error) {
return { success: false, error: error.response?.data?.error || error.message };
}
const response = await axios.delete(`/api/${provider}/${encodeURIComponent(providerId)}`);
return response.data;
};
return {

View File

@@ -0,0 +1,203 @@
<template>
<div class="ai-provider-settings settings-panel">
<h2>{{ label }}</h2>
<p class="desc">{{ description }}</p>
<form @submit.prevent="onSave">
<div v-if="showApiKey">
<label>API Key:</label>
<input type="password" v-model="apiKey" :placeholder="apiKeyPlaceholder" />
<button type="button" class="verify-btn" @click="onVerify" :disabled="verifying">Verify</button>
<span v-if="verifyStatus === true" class="ok"></span>
<span v-if="verifyStatus === false" class="error">Ошибка: {{ verifyError }}</span>
</div>
<div v-if="showBaseUrl">
<label>Base URL:</label>
<input type="text" v-model="baseUrl" :placeholder="baseUrlPlaceholder" />
</div>
<div v-if="models.length">
<label>Модель:</label>
<select v-model="selectedModel">
<option v-for="model in models" :key="model.id || model" :value="model.id || model">
{{ model.id || model }}
</option>
</select>
</div>
<div class="actions">
<button type="submit" :disabled="saving">Сохранить</button>
<button type="button" @click="onDelete" v-if="hasSettings">Удалить ключ</button>
<button type="button" @click="$emit('cancel')">Закрыть</button>
</div>
<div v-if="saveStatus === true" class="ok">Сохранено!</div>
<div v-if="saveStatus === false" class="error">Ошибка: {{ saveError }}</div>
</form>
</div>
</template>
<script setup>
import { ref, onMounted, watch } from 'vue';
import axios from 'axios';
const props = defineProps({
provider: { type: String, required: true },
label: { type: String, required: true },
description: { type: String, default: '' },
showApiKey: { type: Boolean, default: true },
showBaseUrl: { type: Boolean, default: true },
apiKeyPlaceholder: { type: String, default: '' },
baseUrlPlaceholder: { type: String, default: '' },
});
const apiKey = ref('');
const baseUrl = ref('');
const selectedModel = ref('');
const models = ref([]);
const hasSettings = ref(false);
const verifying = ref(false);
const verifyStatus = ref(null);
const verifyError = ref('');
const saving = ref(false);
const saveStatus = ref(null);
const saveError = ref('');
async function loadSettings() {
try {
const { data } = await axios.get(`/api/settings/ai-settings/${props.provider}`);
if (data.settings) {
apiKey.value = data.settings.api_key || '';
baseUrl.value = data.settings.base_url || '';
selectedModel.value = data.settings.selected_model || '';
hasSettings.value = true;
if (apiKey.value || props.provider === 'ollama') {
await loadModels();
}
} else {
hasSettings.value = false;
}
} catch (e) {
hasSettings.value = false;
}
}
async function loadModels() {
try {
const { data } = await axios.get(`/api/settings/ai-settings/${props.provider}/models`);
models.value = data.models || [];
if (!selectedModel.value && models.value.length) {
selectedModel.value = models.value[0].id || models.value[0];
}
} catch (e) {
models.value = [];
}
}
async function onVerify() {
verifying.value = true;
verifyStatus.value = null;
verifyError.value = '';
try {
const { data } = await axios.post(`/api/settings/ai-settings/${props.provider}/verify`, {
api_key: apiKey.value,
base_url: baseUrl.value,
});
verifyStatus.value = data.success;
if (data.success) {
await loadModels();
}
} catch (e) {
verifyStatus.value = false;
verifyError.value = e.response?.data?.error || e.message;
} finally {
verifying.value = false;
}
}
async function onSave() {
saving.value = true;
saveStatus.value = null;
saveError.value = '';
try {
await axios.put(`/api/settings/ai-settings/${props.provider}`, {
api_key: apiKey.value,
base_url: baseUrl.value,
selected_model: selectedModel.value,
});
saveStatus.value = true;
hasSettings.value = true;
} catch (e) {
saveStatus.value = false;
saveError.value = e.response?.data?.error || e.message;
} finally {
saving.value = false;
}
}
async function onDelete() {
await axios.delete(`/api/settings/ai-settings/${props.provider}`);
apiKey.value = '';
baseUrl.value = '';
selectedModel.value = '';
models.value = [];
hasSettings.value = false;
}
onMounted(loadSettings);
watch([apiKey, baseUrl], () => {
verifyStatus.value = null;
verifyError.value = '';
saveStatus.value = null;
saveError.value = '';
});
</script>
<style scoped>
.ai-provider-settings.settings-panel {
padding: var(--block-padding);
background-color: var(--color-light);
border-radius: var(--radius-md);
margin-top: var(--spacing-lg);
max-width: 500px;
}
.desc {
color: #666;
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.3rem;
font-weight: 500;
}
input[type="password"], input[type="text"], select {
width: 100%;
padding: 0.5rem 1rem;
border-radius: 6px;
border: 1px solid #ccc;
font-size: 1rem;
margin-bottom: 1rem;
}
.verify-btn {
margin-left: 0.5rem;
background: var(--color-primary);
color: #fff;
border: none;
border-radius: 6px;
padding: 0.4rem 1.2rem;
cursor: pointer;
font-size: 1rem;
transition: background 0.2s;
}
.verify-btn:hover {
background: var(--color-primary-dark);
}
.actions {
display: flex;
gap: 1rem;
margin-top: 1.5rem;
}
.ok {
color: #2cae4f;
margin-left: 1rem;
}
.error {
color: #d32f2f;
margin-left: 1rem;
}
</style>

View File

@@ -1,26 +1,97 @@
<template>
<div class="ai-settings settings-panel">
<h2>Интеграции</h2>
<div class="integration-blocks">
<div class="integration-blocks" v-if="!showProvider && !showEmailSettings && !showTelegramSettings">
<div class="integration-block">
<h3>OpenAI</h3>
<p>Интеграция с OpenAI (GPT-4, GPT-3.5 и др.).</p>
<button class="details-btn" @click="showProvider = 'openai'">Подробнее</button>
</div>
<div class="integration-block">
<h3>Anthropic</h3>
<p>Интеграция с Anthropic Claude (Claude 3 и др.).</p>
<button class="details-btn" @click="showProvider = 'anthropic'">Подробнее</button>
</div>
<div class="integration-block">
<h3>Google Gemini</h3>
<p>Интеграция с Google Gemini (Gemini 1.5, 1.0 и др.).</p>
<button class="details-btn" @click="showProvider = 'google'">Подробнее</button>
</div>
<div class="integration-block">
<h3>Ollama</h3>
<p>Локальные open-source модели через Ollama.</p>
<button class="details-btn" @click="showProvider = 'ollama'">Подробнее</button>
</div>
<div class="integration-block">
<h3>Telegram</h3>
<p>Интеграция с Telegram-ботом для уведомлений и авторизации.</p>
<button class="details-btn" @click="goToTelegram">Подробнее</button>
<button class="details-btn" @click="showTelegramSettings = true">Подробнее</button>
</div>
<div class="integration-block">
<h3>Email</h3>
<p>Интеграция с Email для отправки писем и уведомлений.</p>
<button class="details-btn" @click="goToEmail">Подробнее</button>
<button class="details-btn" @click="showEmailSettings = true">Подробнее</button>
</div>
</div>
<AIProviderSettings
v-if="showProvider"
:provider="showProvider"
:label="providerLabels[showProvider].label"
:description="providerLabels[showProvider].description"
:apiKeyPlaceholder="providerLabels[showProvider].apiKeyPlaceholder"
:baseUrlPlaceholder="providerLabels[showProvider].baseUrlPlaceholder"
:showApiKey="providerLabels[showProvider].showApiKey"
:showBaseUrl="providerLabels[showProvider].showBaseUrl"
@cancel="showProvider = null"
/>
<TelegramSettingsView v-if="showTelegramSettings" @cancel="showTelegramSettings = false" />
<EmailSettingsView v-if="showEmailSettings" @cancel="showEmailSettings = false" />
</div>
</template>
<script setup>
import { useRouter } from 'vue-router';
const router = useRouter();
const goToTelegram = () => router.push({ name: 'settings-telegram' });
const goToEmail = () => router.push({ name: 'settings-email' });
import { ref } from 'vue';
import AIProviderSettings from './AIProviderSettings.vue';
import TelegramSettingsView from './TelegramSettingsView.vue';
import EmailSettingsView from './EmailSettingsView.vue';
const showProvider = ref(null);
const showTelegramSettings = ref(false);
const showEmailSettings = ref(false);
const providerLabels = {
openai: {
label: 'OpenAI API Key',
description: 'Введите OpenAI API Key и (опционально) Base URL для кастомных endpoint.',
apiKeyPlaceholder: 'sk-...',
baseUrlPlaceholder: 'https://api.openai.com/v1',
showApiKey: true,
showBaseUrl: true,
},
anthropic: {
label: 'Anthropic API Key',
description: 'Введите Anthropic API Key и (опционально) Base URL.',
apiKeyPlaceholder: '...',
baseUrlPlaceholder: 'https://api.anthropic.com/v1',
showApiKey: true,
showBaseUrl: true,
},
google: {
label: 'Google Gemini API Key',
description: 'Введите Google Gemini API Key и (опционально) Base URL.',
apiKeyPlaceholder: '...',
baseUrlPlaceholder: 'https://generativelanguage.googleapis.com/v1beta',
showApiKey: true,
showBaseUrl: true,
},
ollama: {
label: 'Ollama (локальные модели)',
description: 'Настройка Ollama для локальных open-source моделей. Ключ не требуется.',
apiKeyPlaceholder: '',
baseUrlPlaceholder: 'http://localhost:11434',
showApiKey: false,
showBaseUrl: true,
},
};
</script>
<style scoped>

View File

@@ -0,0 +1,171 @@
<template>
<div class="db-settings settings-panel">
<h2>Настройки базы данных</h2>
<form v-if="editMode" @submit.prevent="saveDbSettings" class="settings-form">
<div class="form-group">
<label for="dbHost">Host</label>
<input id="dbHost" v-model="form.dbHost" type="text" required />
</div>
<div class="form-group">
<label for="dbPort">Port</label>
<input id="dbPort" v-model.number="form.dbPort" type="number" required />
</div>
<div class="form-group">
<label for="dbName">Database</label>
<input id="dbName" v-model="form.dbName" type="text" required />
</div>
<div class="form-group">
<label for="dbUser">User</label>
<input id="dbUser" v-model="form.dbUser" type="text" required />
</div>
<div class="form-group">
<label for="dbPassword">Password</label>
<input id="dbPassword" v-model="form.dbPassword" type="password" :placeholder="form.dbPassword ? 'Изменить пароль' : 'Введите пароль'" />
</div>
<button type="submit" class="save-btn">Сохранить</button>
<button type="button" class="cancel-btn" @click="cancelEdit">Отмена</button>
</form>
<div v-else class="settings-view">
<div class="view-row"><span>Host:</span> <b>{{ form.dbHost }}</b></div>
<div class="view-row"><span>Port:</span> <b>{{ form.dbPort }}</b></div>
<div class="view-row"><span>Database:</span> <b>{{ form.dbName }}</b></div>
<div class="view-row"><span>User:</span> <b>{{ form.dbUser }}</b></div>
<div class="view-row"><span>Password:</span> <b></b></div>
<button type="button" class="edit-btn" @click="editMode = true">Изменить</button>
<button type="button" class="cancel-btn" @click="$emit('cancel')">Закрыть</button>
</div>
</div>
</template>
<script setup>
import { reactive, ref, onMounted } from 'vue';
import api from '@/api/axios';
const form = reactive({
dbHost: '',
dbPort: 5432,
dbName: '',
dbUser: '',
dbPassword: ''
});
const original = reactive({});
const editMode = ref(false);
const loadDbSettings = async () => {
try {
const res = await api.get('/api/db-settings');
if (res.data.success) {
const s = res.data.settings;
form.dbHost = s.db_host;
form.dbPort = s.db_port;
form.dbName = s.db_name;
form.dbUser = s.db_user;
form.dbPassword = '';
Object.assign(original, JSON.parse(JSON.stringify(form)));
}
} catch (e) {
// обработка ошибки
}
};
onMounted(async () => {
await loadDbSettings();
editMode.value = false;
});
const saveDbSettings = async () => {
try {
await api.put('/api/db-settings', {
db_host: form.dbHost,
db_port: form.dbPort,
db_name: form.dbName,
db_user: form.dbUser,
db_password: form.dbPassword || undefined
});
alert('Настройки базы данных сохранены');
form.dbPassword = '';
Object.assign(original, JSON.parse(JSON.stringify(form)));
editMode.value = false;
} catch (e) {
alert('Ошибка сохранения настроек базы данных');
}
};
const cancelEdit = () => {
Object.assign(form, JSON.parse(JSON.stringify(original)));
form.dbPassword = '';
editMode.value = false;
};
</script>
<style scoped>
.settings-panel {
padding: var(--block-padding);
background-color: var(--color-light);
border-radius: var(--radius-md);
margin-top: var(--spacing-lg);
max-width: 500px;
}
.settings-form {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.form-group {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.save-btn {
background: var(--color-primary);
color: #fff;
border: none;
border-radius: 6px;
padding: 0.5rem 1.5rem;
cursor: pointer;
font-size: 1rem;
transition: background 0.2s;
}
.save-btn:hover {
background: var(--color-primary-dark);
}
.cancel-btn {
background: #eee;
color: #333;
border: none;
border-radius: 6px;
padding: 0.5rem 1.5rem;
cursor: pointer;
font-size: 1rem;
margin-left: 1rem;
}
.settings-view {
display: flex;
flex-direction: column;
gap: 1.2rem;
}
.view-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1rem;
background: #f8f8f8;
border-radius: 4px;
padding: 0.5rem 1rem;
}
.edit-btn {
background: var(--color-primary);
color: #fff;
border: none;
border-radius: 6px;
padding: 0.5rem 1.5rem;
cursor: pointer;
font-size: 1rem;
align-self: flex-end;
margin-top: 1.5rem;
transition: background 0.2s;
}
.edit-btn:hover {
background: var(--color-primary-dark);
}
</style>

View File

@@ -1,40 +1,115 @@
<template>
<div class="email-settings settings-panel">
<h2>Настройки Email</h2>
<form @submit.prevent="saveEmailSettings" class="settings-form">
<form v-if="editMode" @submit.prevent="saveEmailSettings" class="settings-form">
<div class="form-group">
<label for="smtpHost">SMTP Host</label>
<input id="smtpHost" v-model="form.smtpHost" type="text" required />
</div>
<div class="form-group">
<label for="smtpPort">SMTP Port</label>
<input id="smtpPort" v-model.number="form.smtpPort" type="number" required />
</div>
<div class="form-group">
<label for="smtpUser">SMTP User</label>
<input id="smtpUser" v-model="form.smtpUser" type="text" required />
</div>
<div class="form-group">
<label for="smtpPassword">SMTP Password</label>
<input id="smtpPassword" v-model="form.smtpPassword" type="password" required />
<input id="smtpPassword" v-model="form.smtpPassword" type="password" :placeholder="form.smtpPassword ? 'Изменить пароль' : 'Введите пароль'" />
</div>
<div class="form-group">
<label for="imapHost">IMAP Host</label>
<input id="imapHost" v-model="form.imapHost" type="text" required />
</div>
<div class="form-group">
<label for="imapPort">IMAP Port</label>
<input id="imapPort" v-model.number="form.imapPort" type="number" required />
</div>
<div class="form-group">
<label for="fromEmail">From Email</label>
<input id="fromEmail" v-model="form.fromEmail" type="email" required />
</div>
<button type="submit" class="save-btn">Сохранить</button>
<button type="button" class="cancel-btn" @click="cancelEdit">Отмена</button>
</form>
<div v-else class="settings-view">
<div class="view-row"><span>SMTP Host:</span> <b>{{ form.smtpHost }}</b></div>
<div class="view-row"><span>SMTP Port:</span> <b>{{ form.smtpPort }}</b></div>
<div class="view-row"><span>SMTP User:</span> <b>{{ form.smtpUser }}</b></div>
<div class="view-row"><span>IMAP Host:</span> <b>{{ form.imapHost }}</b></div>
<div class="view-row"><span>IMAP Port:</span> <b>{{ form.imapPort }}</b></div>
<div class="view-row"><span>From Email:</span> <b>{{ form.fromEmail }}</b></div>
<button type="button" class="edit-btn" @click="editMode = true">Изменить</button>
<button type="button" class="cancel-btn" @click="$emit('cancel')">Закрыть</button>
</div>
</div>
</template>
<script setup>
import { reactive } from 'vue';
// TODO: Импортировать API для сохранения
import { reactive, ref, onMounted } from 'vue';
import api from '@/api/axios';
const form = reactive({
smtpHost: '',
smtpPort: 465,
smtpUser: '',
smtpPassword: '',
imapHost: '',
imapPort: 993,
fromEmail: ''
});
const original = reactive({});
const editMode = ref(false);
const loadEmailSettings = async () => {
try {
const res = await api.get('/api/email-settings');
if (res.data.success) {
const s = res.data.settings;
form.smtpHost = s.smtp_host;
form.smtpPort = s.smtp_port;
form.smtpUser = s.smtp_user;
form.imapHost = s.imap_host || '';
form.imapPort = s.imap_port || 993;
form.fromEmail = s.from_email;
form.smtpPassword = '';
Object.assign(original, JSON.parse(JSON.stringify(form)));
}
} catch (e) {
// обработка ошибки
}
};
onMounted(async () => {
await loadEmailSettings();
editMode.value = false;
});
const saveEmailSettings = async () => {
// TODO: Реализовать вызов API для сохранения
alert('Настройки Email сохранены (заглушка)');
try {
await api.put('/api/email-settings', {
smtp_host: form.smtpHost,
smtp_port: form.smtpPort,
smtp_user: form.smtpUser,
smtp_password: form.smtpPassword || undefined,
imap_host: form.imapHost,
imap_port: form.imapPort,
from_email: form.fromEmail
});
alert('Настройки Email сохранены');
form.smtpPassword = '';
Object.assign(original, JSON.parse(JSON.stringify(form)));
editMode.value = false;
} catch (e) {
alert('Ошибка сохранения email-настроек');
}
};
const cancelEdit = () => {
Object.assign(form, JSON.parse(JSON.stringify(original)));
form.smtpPassword = '';
editMode.value = false;
};
</script>
@@ -69,4 +144,43 @@ const saveEmailSettings = async () => {
.save-btn:hover {
background: var(--color-primary-dark);
}
.cancel-btn {
background: #eee;
color: #333;
border: none;
border-radius: 6px;
padding: 0.5rem 1.5rem;
cursor: pointer;
font-size: 1rem;
margin-left: 1rem;
}
.settings-view {
display: flex;
flex-direction: column;
gap: 1.2rem;
}
.view-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1rem;
background: #f8f8f8;
border-radius: 4px;
padding: 0.5rem 1rem;
}
.edit-btn {
background: var(--color-primary);
color: #fff;
border: none;
border-radius: 6px;
padding: 0.5rem 1.5rem;
cursor: pointer;
font-size: 1rem;
align-self: flex-end;
margin-top: 1.5rem;
transition: background 0.2s;
}
.edit-btn:hover {
background: var(--color-primary-dark);
}
</style>

View File

@@ -0,0 +1,63 @@
<template>
<div class="ollama-settings settings-panel">
<h2>Настройки Ollama</h2>
<div class="current-model-block">
<span>Текущая модель:</span>
<b>{{ currentModel }}</b>
</div>
<div class="select-model-block">
<label for="ollamaModel">Доступные модели для загрузки:</label>
<select id="ollamaModel" v-model="selectedModel">
<option v-for="model in availableModels" :key="model" :value="model">{{ model }}</option>
</select>
</div>
<button class="close-btn" @click="$emit('cancel')">Закрыть</button>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
// TODO: заменить на реальный API Ollama
const currentModel = ref('qwen2.5');
const availableModels = ref(['qwen2.5', 'llama3', 'mistral', 'phi3', 'gemma']);
const selectedModel = ref(currentModel.value);
onMounted(() => {
// Здесь будет запрос к Ollama для получения списка моделей и текущей
});
</script>
<style scoped>
.ollama-settings.settings-panel {
padding: var(--block-padding);
background-color: var(--color-light);
border-radius: var(--radius-md);
margin-top: var(--spacing-lg);
max-width: 500px;
}
.current-model-block {
margin-bottom: 1.5rem;
font-size: 1.1rem;
}
.select-model-block {
margin-bottom: 2rem;
}
select {
padding: 0.5rem 1rem;
border-radius: 6px;
border: 1px solid #ccc;
font-size: 1rem;
}
.close-btn {
background: #eee;
color: #333;
border: none;
border-radius: 6px;
padding: 0.5rem 1.5rem;
cursor: pointer;
font-size: 1rem;
}
.close-btn:hover {
background: #ddd;
}
</style>

View File

@@ -1,7 +1,7 @@
<template>
<div class="telegram-settings settings-panel">
<h2>Настройки Telegram</h2>
<form @submit.prevent="saveTelegramSettings" class="settings-form">
<form v-if="editMode" @submit.prevent="saveTelegramSettings" class="settings-form">
<div class="form-group">
<label for="botToken">Bot Token</label>
<input id="botToken" v-model="form.botToken" type="text" required />
@@ -11,20 +11,66 @@
<input id="botUsername" v-model="form.botUsername" type="text" required />
</div>
<button type="submit" class="save-btn">Сохранить</button>
<button type="button" class="cancel-btn" @click="cancelEdit">Отмена</button>
</form>
<div v-else class="settings-view">
<div class="view-row"><span>Bot Token:</span> <b></b></div>
<div class="view-row"><span>Bot Username:</span> <b>{{ form.botUsername }}</b></div>
<button type="button" class="edit-btn" @click="editMode = true">Изменить</button>
<button type="button" class="cancel-btn" @click="$emit('cancel')">Закрыть</button>
</div>
</div>
</template>
<script setup>
import { reactive } from 'vue';
// TODO: Импортировать API для сохранения
import { reactive, ref, onMounted } from 'vue';
import api from '@/api/axios';
const form = reactive({
botToken: '',
botUsername: ''
});
const original = reactive({});
const editMode = ref(false);
const loadTelegramSettings = async () => {
try {
const res = await api.get('/api/telegram-settings');
if (res.data.success) {
const s = res.data.settings;
form.botToken = '';
form.botUsername = s.bot_username;
Object.assign(original, JSON.parse(JSON.stringify(form)));
}
} catch (e) {
// обработка ошибки
}
};
onMounted(async () => {
await loadTelegramSettings();
editMode.value = false;
});
const saveTelegramSettings = async () => {
// TODO: Реализовать вызов API для сохранения
alert('Настройки Telegram сохранены (заглушка)');
try {
await api.put('/api/telegram-settings', {
bot_token: form.botToken,
bot_username: form.botUsername
});
alert('Настройки Telegram сохранены');
form.botToken = '';
Object.assign(original, JSON.parse(JSON.stringify(form)));
editMode.value = false;
} catch (e) {
alert('Ошибка сохранения telegram-настроек');
}
};
const cancelEdit = () => {
Object.assign(form, JSON.parse(JSON.stringify(original)));
form.botToken = '';
editMode.value = false;
};
</script>
@@ -59,4 +105,43 @@ const saveTelegramSettings = async () => {
.save-btn:hover {
background: var(--color-primary-dark);
}
.cancel-btn {
background: #eee;
color: #333;
border: none;
border-radius: 6px;
padding: 0.5rem 1.5rem;
cursor: pointer;
font-size: 1rem;
margin-left: 1rem;
}
.settings-view {
display: flex;
flex-direction: column;
gap: 1.2rem;
}
.view-row {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 1rem;
background: #f8f8f8;
border-radius: 4px;
padding: 0.5rem 1rem;
}
.edit-btn {
background: var(--color-primary);
color: #fff;
border: none;
border-radius: 6px;
padding: 0.5rem 1.5rem;
cursor: pointer;
font-size: 1rem;
align-self: flex-end;
margin-top: 1.5rem;
transition: background 0.2s;
}
.edit-btn:hover {
background: var(--color-primary-dark);
}
</style>