diff --git a/backend/app.js b/backend/app.js
index 7b658f2..88948f2 100644
--- a/backend/app.js
+++ b/backend/app.js
@@ -163,7 +163,7 @@ app.use((req, res, next) => {
app.use('/api/tables', tablesRoutes); // ДОЛЖНО БЫТЬ ВЫШЕ!
// app.use('/api', identitiesRoutes);
app.use('/api/auth', authRoutes);
-app.use('/api/users/:userId/tags', userTagsRoutes);
+app.use('/api/users', userTagsRoutes);
app.use('/api/users', usersRoutes);
app.use('/api/chat', chatRoutes);
app.use('/api/admin', adminRoutes);
diff --git a/backend/routes/messages.js b/backend/routes/messages.js
index ccc6c5b..3fa5094 100644
--- a/backend/routes/messages.js
+++ b/backend/routes/messages.js
@@ -5,15 +5,23 @@ const db = require('../db');
// GET /api/messages?userId=123
router.get('/', async (req, res) => {
const userId = req.query.userId;
- if (!userId) return res.status(400).json({ error: 'userId required' });
try {
- const result = await db.getQuery()(
- `SELECT id, user_id, sender_type, content, channel, role, direction, created_at, attachment_filename, attachment_mimetype, attachment_size, attachment_data, metadata
- FROM messages
- WHERE user_id = $1
- ORDER BY created_at ASC`,
- [userId]
- );
+ let result;
+ if (userId) {
+ result = await db.getQuery()(
+ `SELECT id, user_id, sender_type, content, channel, role, direction, created_at, attachment_filename, attachment_mimetype, attachment_size, attachment_data, metadata
+ FROM messages
+ WHERE user_id = $1
+ ORDER BY created_at ASC`,
+ [userId]
+ );
+ } else {
+ result = await db.getQuery()(
+ `SELECT id, user_id, sender_type, content, channel, role, direction, created_at, attachment_filename, attachment_mimetype, attachment_size, attachment_data, metadata
+ FROM messages
+ ORDER BY created_at ASC`
+ );
+ }
res.json(result.rows);
} catch (e) {
res.status(500).json({ error: 'DB error', details: e.message });
diff --git a/backend/server.js b/backend/server.js
index 315a666..0aca925 100644
--- a/backend/server.js
+++ b/backend/server.js
@@ -3,6 +3,8 @@ const { app, nonceStore } = require('./app');
const http = require('http');
const { initWSS } = require('./wsHub');
const logger = require('./utils/logger');
+const { getBot } = require('./services/telegramBot');
+const EmailBotService = require('./services/emailBot');
const PORT = process.env.PORT || 8000;
@@ -14,9 +16,26 @@ console.log('Используемый порт:', process.env.PORT || 8000);
async function initServices() {
try {
console.log('Инициализация сервисов...');
- // Здесь может быть инициализация ботов, email-сервисов и т.д.
- // ...
- console.log('Все сервисы успешно инициализированы');
+ console.log('[initServices] Запуск Email-бота...');
+ console.log('[initServices] Создаю экземпляр EmailBotService...');
+ let emailBot;
+ try {
+ emailBot = new EmailBotService();
+ console.log('[initServices] Экземпляр EmailBotService создан');
+ } catch (err) {
+ console.error('[initServices] Ошибка при создании экземпляра EmailBotService:', err);
+ throw err;
+ }
+ console.log('[initServices] Перед вызовом emailBot.start()');
+ try {
+ await emailBot.start();
+ console.log('[initServices] Email-бот успешно запущен');
+ } catch (err) {
+ console.error('[initServices] Ошибка при запуске emailBot:', err);
+ }
+ console.log('[initServices] Запуск Telegram-бота...');
+ await getBot();
+ console.log('[initServices] Telegram-бот успешно запущен');
} catch (error) {
console.error('Ошибка при инициализации сервисов:', error);
}
diff --git a/backend/services/ai-assistant.js b/backend/services/ai-assistant.js
index e2828fd..05c6d32 100644
--- a/backend/services/ai-assistant.js
+++ b/backend/services/ai-assistant.js
@@ -1,3 +1,5 @@
+console.log('[ai-assistant] loaded');
+
const { ChatOllama } = require('@langchain/ollama');
const { HNSWLib } = require('@langchain/community/vectorstores/hnswlib');
const { OpenAIEmbeddings } = require('@langchain/openai');
diff --git a/backend/services/aiProviderSettingsService.js b/backend/services/aiProviderSettingsService.js
index 080c25e..a1d0df4 100644
--- a/backend/services/aiProviderSettingsService.js
+++ b/backend/services/aiProviderSettingsService.js
@@ -1,7 +1,6 @@
const db = require('../db');
const OpenAI = require('openai');
const Anthropic = require('@anthropic-ai/sdk');
-const { GoogleGenAI } = require('@google/genai');
const TABLE = 'ai_providers_settings';
@@ -48,6 +47,7 @@ async function getProviderModels(provider, { api_key, base_url } = {}) {
return res.data ? res.data.map(m => ({ id: m.id, ...m })) : [];
}
if (provider === 'google') {
+ const { GoogleGenAI } = await import('@google/genai');
const ai = new GoogleGenAI({ apiKey: api_key, baseUrl: base_url });
const pager = await ai.models.list();
const models = [];
@@ -79,6 +79,7 @@ async function verifyProviderKey(provider, { api_key, base_url } = {}) {
return { success: true };
}
if (provider === 'google') {
+ const { GoogleGenAI } = await import('@google/genai');
const ai = new GoogleGenAI({ apiKey: api_key, baseUrl: base_url });
const pager = await ai.models.list();
for await (const _ of pager) {
diff --git a/backend/services/emailBot.js b/backend/services/emailBot.js
index fed7dff..1bcac1b 100644
--- a/backend/services/emailBot.js
+++ b/backend/services/emailBot.js
@@ -1,3 +1,4 @@
+console.log('[EmailBot] emailBot.js loaded');
const db = require('../db');
const nodemailer = require('nodemailer');
const Imap = require('imap');
@@ -9,6 +10,10 @@ const identityService = require('./identity-service');
const aiAssistant = require('./ai-assistant');
class EmailBotService {
+ constructor() {
+ console.log('[EmailBot] constructor called');
+ }
+
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');
@@ -46,6 +51,7 @@ class EmailBotService {
idleInterval: 300000,
forceNoop: true,
},
+ connTimeout: 30000, // 30 секунд
};
}
@@ -228,55 +234,63 @@ class EmailBotService {
}
async start() {
- logger.info('[EmailBot] start() called');
- const imapConfig = await this.getImapConfig();
- // Логируем IMAP-конфиг (без пароля)
- const safeConfig = { ...imapConfig };
- if (safeConfig.password) safeConfig.password = '***';
- logger.info('[EmailBot] IMAP config:', safeConfig);
- let attempt = 0;
- const maxAttempts = 3;
- this.isChecking = false;
- const tryConnect = () => {
- attempt++;
- logger.info(`[EmailBot] IMAP connect attempt ${attempt}`);
- this.imap = new Imap(imapConfig);
- this.imap.once('ready', () => {
- logger.info('[EmailBot] IMAP connection ready');
- this.imap.openBox('INBOX', false, (err, box) => {
- if (err) {
- logger.error(`[EmailBot] Error opening INBOX: ${err.message}`);
- this.imap.end();
- return;
- }
- logger.info('[EmailBot] INBOX opened successfully');
+ try {
+ console.log('[EmailBot] start() called');
+ logger.info('[EmailBot] start() called');
+ const imapConfig = await this.getImapConfig();
+ // Логируем IMAP-конфиг (без пароля)
+ const safeConfig = { ...imapConfig };
+ if (safeConfig.password) safeConfig.password = '***';
+ logger.info('[EmailBot] IMAP config:', safeConfig);
+ let attempt = 0;
+ const maxAttempts = 3;
+ this.isChecking = false;
+ const tryConnect = () => {
+ attempt++;
+ logger.info(`[EmailBot] IMAP connect attempt ${attempt}`);
+ this.imap = new Imap(imapConfig);
+ this.imap.once('ready', () => {
+ logger.info('[EmailBot] IMAP connection ready');
+ this.imap.openBox('INBOX', false, (err, box) => {
+ if (err) {
+ logger.error(`[EmailBot] Error opening INBOX: ${err.message}`);
+ this.imap.end();
+ return;
+ }
+ logger.info('[EmailBot] INBOX opened successfully');
+ });
+ // После успешного подключения — обычная логика
+ this.checkEmails();
+ logger.info('[EmailBot] Email bot started and IMAP connection initiated');
+ // Периодическая проверка почты
+ setInterval(async () => {
+ if (this.isChecking) return;
+ this.isChecking = true;
+ try {
+ await this.checkEmails();
+ } catch (e) {
+ logger.error('[EmailBot] Error in periodic checkEmails:', e);
+ }
+ this.isChecking = false;
+ }, 60000); // 60 секунд
});
- // После успешного подключения — обычная логика
- this.checkEmails();
- logger.info('[EmailBot] Email bot started and IMAP connection initiated');
- // Периодическая проверка почты
- setInterval(async () => {
- if (this.isChecking) return;
- this.isChecking = true;
- try {
- await this.checkEmails();
- } catch (e) {
- logger.error('[EmailBot] Error in periodic checkEmails:', e);
+ this.imap.once('error', (err) => {
+ logger.error(`[EmailBot] IMAP connection error: ${err.message}`);
+ if (err.message && err.message.toLowerCase().includes('timed out') && attempt < maxAttempts) {
+ logger.warn(`[EmailBot] IMAP reconnecting in 10 seconds (attempt ${attempt + 1})...`);
+ setTimeout(tryConnect, 10000);
}
- this.isChecking = false;
- }, 60000); // 60 секунд
- });
- this.imap.once('error', (err) => {
- logger.error(`[EmailBot] IMAP connection error: ${err.message}`);
- if (err.message && err.message.toLowerCase().includes('timed out') && attempt < maxAttempts) {
- logger.warn(`[EmailBot] IMAP reconnecting in 10 seconds (attempt ${attempt + 1})...`);
- setTimeout(tryConnect, 10000);
- }
- });
- this.imap.connect();
- };
- tryConnect();
+ });
+ this.imap.connect();
+ };
+ tryConnect();
+ } catch (err) {
+ console.error('[EmailBot] Ошибка при старте:', err);
+ logger.error('[EmailBot] Ошибка при старте:', err);
+ throw err;
+ }
}
}
+console.log('[EmailBot] module.exports = EmailBotService');
module.exports = EmailBotService;
diff --git a/backend/services/identity-service.js b/backend/services/identity-service.js
index 94dfb1f..b40682b 100644
--- a/backend/services/identity-service.js
+++ b/backend/services/identity-service.js
@@ -1,7 +1,10 @@
+console.log('[identity-service] loaded');
+
const db = require('../db');
const logger = require('../utils/logger');
const { getLinkedWallet } = require('./wallet-service');
const { checkAdminRole } = require('./admin-role');
+const { broadcastContactsUpdate } = require('../wsHub');
/**
* Сервис для работы с идентификаторами пользователей
@@ -541,6 +544,7 @@ class IdentityService {
await this.saveIdentity(userId, provider, providerId, true);
user = { id: userId, role: 'user' };
isNew = true;
+ broadcastContactsUpdate();
}
// Проверяем связь с кошельком
const wallet = await getLinkedWallet(user.id);
diff --git a/backend/services/telegramBot.js b/backend/services/telegramBot.js
index 6bad442..ae2c3eb 100644
--- a/backend/services/telegramBot.js
+++ b/backend/services/telegramBot.js
@@ -332,8 +332,9 @@ async function getBot() {
}
});
- // Запускаем бота
+ // Запуск бота
await botInstance.launch();
+ logger.info('[TelegramBot] Бот запущен');
}
return botInstance;
diff --git a/frontend/src/components/ContactTable.vue b/frontend/src/components/ContactTable.vue
index 89c3682..143dde1 100644
--- a/frontend/src/components/ContactTable.vue
+++ b/frontend/src/components/ContactTable.vue
@@ -53,8 +53,8 @@ function showDetails(contact) {
border-radius: 16px;
box-shadow: 0 4px 32px rgba(0,0,0,0.12);
padding: 32px 24px 24px 24px;
- max-width: 950px;
- margin: 40px auto;
+ width: 100%;
+ margin-top: 40px;
position: relative;
overflow-x: auto;
}
@@ -65,6 +65,9 @@ function showDetails(contact) {
margin-bottom: 24px;
}
.close-btn {
+ position: absolute;
+ top: 18px;
+ right: 18px;
background: none;
border: none;
font-size: 2rem;
diff --git a/frontend/src/components/DleManagement.vue b/frontend/src/components/DleManagement.vue
index 2e399ab..cb7697f 100644
--- a/frontend/src/components/DleManagement.vue
+++ b/frontend/src/components/DleManagement.vue
@@ -357,8 +357,8 @@ function uninstallModule(module) {
border-radius: 16px;
box-shadow: 0 4px 32px rgba(0,0,0,0.12);
padding: 32px 24px 24px 24px;
- max-width: 950px;
- margin: 40px auto;
+ width: 100%;
+ margin-top: 40px;
position: relative;
overflow-x: auto;
}
diff --git a/frontend/src/components/MessagesTable.vue b/frontend/src/components/MessagesTable.vue
new file mode 100644
index 0000000..4476fe9
--- /dev/null
+++ b/frontend/src/components/MessagesTable.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/Sidebar.vue b/frontend/src/components/Sidebar.vue
index dd722d7..b2ee1d6 100644
--- a/frontend/src/components/Sidebar.vue
+++ b/frontend/src/components/Sidebar.vue
@@ -402,15 +402,15 @@ h3 {
/* Медиа-запросы для адаптивности */
@media screen and (min-width: 1200px) {
.wallet-sidebar {
- width: 30%;
- max-width: 350px;
+ width: 420px;
+ max-width: 420px;
}
}
@media screen and (min-width: 769px) and (max-width: 1199px) {
.wallet-sidebar {
- width: 40%;
- max-width: 320px;
+ width: 350px;
+ max-width: 350px;
}
}
diff --git a/frontend/src/components/tables/TagsTableView.vue b/frontend/src/components/tables/TagsTableView.vue
new file mode 100644
index 0000000..6abff4b
--- /dev/null
+++ b/frontend/src/components/tables/TagsTableView.vue
@@ -0,0 +1,285 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/frontend/src/components/tables/UserTablesList.vue b/frontend/src/components/tables/UserTablesList.vue
index 3f1fce1..46880c1 100644
--- a/frontend/src/components/tables/UserTablesList.vue
+++ b/frontend/src/components/tables/UserTablesList.vue
@@ -1,63 +1,33 @@
Удалить таблицу {{ selectedTable?.name }}?
+
+