feat: новая функция

This commit is contained in:
2025-10-13 22:41:49 +03:00
parent 34666b44d8
commit 0e028bc722
83 changed files with 1595 additions and 6093 deletions

View File

@@ -38,7 +38,8 @@ router.post('/task', requireAuth, async (req, res) => {
try {
const { message, language, history, systemPrompt, rules, type = 'chat' } = req.body;
const userId = req.session.userId;
const userRole = req.session.isAdmin ? 'admin' : 'user';
const userAccessLevel = req.session.userAccessLevel || { level: 'user', tokenCount: 0, hasAccess: false };
const userRole = userAccessLevel.hasAccess ? 'admin' : 'user';
if (!message) {
return res.status(400).json({
@@ -108,7 +109,7 @@ router.post('/control', requireAuth, async (req, res) => {
try {
const { action } = req.body;
if (!req.session.isAdmin) {
if (!req.session.userAccessLevel?.hasAccess) {
return res.status(403).json({
success: false,
error: 'Admin access required'

View File

@@ -207,7 +207,7 @@ router.post('/verify', async (req, res) => {
logger.info(`[verify] Admin status for ${normalizedAddress}: ${adminStatus}`);
let userId;
let isAdmin = adminStatus;
let userAccessLevel = adminStatus ? { level: 'editor', tokenCount: 0, hasAccess: true } : { level: 'user', tokenCount: 0, hasAccess: false };
// Проверяем, авторизован ли пользователь уже
if (req.session.authenticated && req.session.userId) {
@@ -226,10 +226,10 @@ router.post('/verify', async (req, res) => {
);
} else {
// Находим или создаем пользователя с уже известной ролью
const result = await authService.findOrCreateUser(address, adminStatus);
const result = await authService.findOrCreateUser(address, userAccessLevel);
userId = result.userId;
isAdmin = result.isAdmin;
logger.info(`[verify] Found or created user ${userId} for wallet ${normalizedAddress} with admin status: ${isAdmin}`);
userAccessLevel = result.userAccessLevel;
logger.info(`[verify] Found or created user ${userId} for wallet ${normalizedAddress} with access level: ${userAccessLevel.hasAccess}`);
}
// Сохраняем идентификаторы гостевой сессии
@@ -245,7 +245,7 @@ router.post('/verify', async (req, res) => {
req.session.userId = userId;
req.session.authenticated = true;
req.session.authType = 'wallet';
req.session.isAdmin = adminStatus || isAdmin;
req.session.userAccessLevel = userAccessLevel;
req.session.address = normalizedAddress; // Всегда сохраняем нормализованный адрес
// Удаляем временный ID
@@ -258,11 +258,12 @@ router.post('/verify', async (req, res) => {
await sessionService.linkGuestMessages(req.session, userId);
// Возвращаем успешный ответ
userAccessLevel = await authService.getUserAccessLevel(normalizedAddress);
return res.json({
success: true,
userId,
address: normalizedAddress, // Возвращаем нормализованный адрес
isAdmin: adminStatus || isAdmin,
userAccessLevel: userAccessLevel,
authenticated: true,
});
} catch (error) {
@@ -347,7 +348,7 @@ router.post('/telegram/verify', async (req, res) => {
req.session.telegramId = telegramId;
req.session.authType = 'telegram';
req.session.authenticated = true;
req.session.isAdmin = finalIsAdmin; // <-- УСТАНАВЛИВАЕМ РОЛЬ ПОСЛЕ ПРОВЕРКИ БАЛАНСА
req.session.userAccessLevel = finalIsAdmin ? { level: 'editor', tokenCount: 0, hasAccess: true } : { level: 'user', tokenCount: 0, hasAccess: false }; // <-- УСТАНАВЛИВАЕМ РОЛЬ ПОСЛЕ ПРОВЕРКИ БАЛАНСА
// ---> ДОБАВЛЯЕМ АДРЕС КОШЕЛЬКА В СЕССИЮ (ЕСЛИ НАЙДЕН) <---
if (linkedWalletAddress) {
@@ -368,10 +369,16 @@ router.post('/telegram/verify', async (req, res) => {
await sessionService.linkGuestMessages(req.session, verificationResult.userId);
}
// Получаем уровень доступа для пользователя
let userAccessLevel = { level: 'user', tokenCount: 0, hasAccess: false };
if (linkedWalletAddress) {
userAccessLevel = await authService.getUserAccessLevel(linkedWalletAddress);
}
return res.json({
success: true,
userId: verificationResult.userId,
isAdmin: finalIsAdmin, // <-- ВОЗВРАЩАЕМ АКТУАЛЬНУЮ РОЛЬ
userAccessLevel: userAccessLevel, // <-- ВОЗВРАЩАЕМ АКТУАЛЬНЫЙ УРОВЕНЬ ДОСТУПА
telegramId,
isNewUser: verificationResult.isNewUser,
address: linkedWalletAddress || null // <-- ВОЗВРАЩАЕМ АДРЕС КОШЕЛЬКА
@@ -511,7 +518,7 @@ router.post('/email/verify-code', async (req, res) => {
req.session.authenticated = true;
req.session.authType = 'email';
req.session.email = authResult.email;
req.session.isAdmin = finalIsAdmin; // <-- УСТАНАВЛИВАЕМ РОЛЬ ПОСЛЕ ПРОВЕРКИ БАЛАНСА
req.session.userAccessLevel = finalIsAdmin ? { level: 'editor', tokenCount: 0, hasAccess: true } : { level: 'user', tokenCount: 0, hasAccess: false }; // <-- УСТАНАВЛИВАЕМ РОЛЬ ПОСЛЕ ПРОВЕРКИ БАЛАНСА
// ---> ДОБАВЛЯЕМ АДРЕС КОШЕЛЬКА В СЕССИЮ <---
if (linkedWalletAddress) {
req.session.address = linkedWalletAddress;
@@ -533,11 +540,17 @@ router.post('/email/verify-code', async (req, res) => {
await sessionService.linkGuestMessages(req.session, authResult.userId);
// 4. Отправляем ответ
// Получаем уровень доступа для пользователя
let userAccessLevel = { level: 'user', tokenCount: 0, hasAccess: false };
if (linkedWalletAddress) {
userAccessLevel = await authService.getUserAccessLevel(linkedWalletAddress);
}
return res.json({
success: true,
userId: authResult.userId,
email: authResult.email,
isAdmin: finalIsAdmin, // <-- ВОЗВРАЩАЕМ АКТУАЛЬНУЮ РОЛЬ
userAccessLevel: userAccessLevel, // <-- ВОЗВРАЩАЕМ АКТУАЛЬНЫЙ УРОВЕНЬ ДОСТУПА
authenticated: true,
isNewAuth: authResult.isNewUser,
address: linkedWalletAddress || null // <-- ВОЗВРАЩАЕМ АДРЕС КОШЕЛЬКА
@@ -630,7 +643,7 @@ router.get('/check', async (req, res) => {
const authType = req.session.authType || null;
let identities = [];
let isAdmin = false;
let userAccessLevel = { level: 'user', tokenCount: 0, hasAccess: false };
if (authenticated && req.session.userId) {
// Если пользователь аутентифицирован, получаем его идентификаторы из БД
@@ -639,8 +652,8 @@ router.get('/check', async (req, res) => {
// Для пользователей с кошельком проверяем токены в реальном времени
if (authType === 'wallet' && req.session.address) {
isAdmin = await authService.checkAdminTokens(req.session.address);
logger.info(`[auth/check] Admin status for wallet ${req.session.address}: ${isAdmin}`);
userAccessLevel = await authService.getUserAccessLevel(req.session.address);
logger.info(`[auth/check] Access level for wallet ${req.session.address}:`, userAccessLevel);
} else {
// Для других типов аутентификации используем роль из БД
const roleResult = await db.getQuery()('SELECT role FROM users WHERE id = $1', [
@@ -648,11 +661,17 @@ router.get('/check', async (req, res) => {
]);
if (roleResult.rows.length > 0) {
isAdmin = roleResult.rows[0].role === 'admin';
const role = roleResult.rows[0].role;
// Преобразуем старую роль в новый формат
if (role === 'admin') {
userAccessLevel = { level: 'editor', tokenCount: 1, hasAccess: true };
} else {
userAccessLevel = { level: 'user', tokenCount: 0, hasAccess: false };
}
}
}
req.session.isAdmin = isAdmin;
req.session.userAccessLevel = userAccessLevel;
} catch (error) {
logger.error(`[session/check] Error fetching identities: ${error.message}`);
}
@@ -674,7 +693,7 @@ router.get('/check', async (req, res) => {
guestId: req.session.guestId || null,
authType,
identitiesCount: identities.length,
isAdmin: isAdmin || false,
userAccessLevel: userAccessLevel,
};
// Добавляем специфические поля в зависимости от типа аутентификации
@@ -711,7 +730,7 @@ router.post('/logout', async (req, res) => {
req.session.address = null;
req.session.telegramId = null;
req.session.email = null;
req.session.isAdmin = false;
req.session.userAccessLevel = { level: 'user', tokenCount: 0, hasAccess: false };
req.session.guestId = null;
req.session.previousGuestId = null;
req.session.processedGuestIds = [];
@@ -743,15 +762,15 @@ router.get('/check-access', requireAuth, async (req, res) => {
const address = req.session.address;
if (address) {
const isAdmin = await authService.checkAdminTokens(address);
const userAccessLevel = await authService.getUserAccessLevel(address);
// Обновляем сессию
req.session.isAdmin = isAdmin;
req.session.userAccessLevel = userAccessLevel;
await sessionService.saveSession(req.session);
return res.json({
success: true,
isAdmin,
userAccessLevel,
userId,
address,
});
@@ -759,7 +778,7 @@ router.get('/check-access', requireAuth, async (req, res) => {
return res.json({
success: true,
isAdmin: false,
userAccessLevel: { level: 'user', tokenCount: 0, hasAccess: false },
userId,
address: null,
});
@@ -794,7 +813,7 @@ router.post('/refresh-session', async (req, res) => {
req.session.authenticated = true;
req.session.userId = user.id;
req.session.address = address.toLowerCase();
req.session.isAdmin = user.role === 'admin';
req.session.userAccessLevel = user.role === 'admin' ? { level: 'editor', tokenCount: 0, hasAccess: true } : { level: 'user', tokenCount: 0, hasAccess: false };
req.session.authType = 'wallet';
// Сохраняем обновленную сессию
@@ -847,10 +866,10 @@ router.post('/wallet', async (req, res) => {
const { userId } = await authService.findOrCreateUser(address);
// Проверяем наличие админских токенов
const isAdmin = await authService.checkAdminTokens(address);
const userAccessLevel = await authService.getUserAccessLevel(address);
// Обновляем роль пользователя в базе данных, если нужно
if (isAdmin) {
if (userAccessLevel.hasAccess) {
await db.getQuery()('UPDATE users SET role = $1 WHERE id = $2', ['admin', userId]);
}
@@ -870,7 +889,7 @@ router.post('/wallet', async (req, res) => {
req.session.address = address.toLowerCase();
req.session.authType = 'wallet';
req.session.authenticated = true;
req.session.isAdmin = isAdmin;
req.session.userAccessLevel = userAccessLevel;
// Сохраняем сессию
await sessionService.saveSession(req.session);
@@ -883,7 +902,7 @@ router.post('/wallet', async (req, res) => {
success: true,
userId,
address,
isAdmin,
userAccessLevel,
authenticated: true,
});
} catch (error) {
@@ -1039,7 +1058,8 @@ router.post('/wallet-with-link', authLimiter, async (req, res) => {
req.session.address = address.toLowerCase();
req.session.authenticated = true;
req.session.authType = 'wallet';
req.session.isAdmin = (role === 'admin' || role === 'editor' || role === 'readonly');
const hasAccess = (role === 'admin' || role === 'editor' || role === 'readonly');
req.session.userAccessLevel = hasAccess ? { level: role === 'editor' ? 'editor' : 'readonly', tokenCount: 0, hasAccess: true } : { level: 'user', tokenCount: 0, hasAccess: false };
await sessionService.saveSession(req.session, 'wallet-with-link');

View File

@@ -18,6 +18,8 @@ const db = require('../db');
const encryptedDb = require('../services/encryptedDatabaseService');
const logger = require('../utils/logger');
const { requireAuth, requireAdmin } = require('../middleware/auth');
const { requirePermission } = require('../middleware/permissions');
const { PERMISSIONS } = require('../shared/permissions');
const aiAssistantSettingsService = require('../services/aiAssistantSettingsService');
const aiAssistantRulesService = require('../services/aiAssistantRulesService');
const botManager = require('../services/botManager');
@@ -208,10 +210,9 @@ router.post('/message', requireAuth, upload.array('attachments'), async (req, re
const adminLogicService = require('../services/adminLogicService');
const sessionUserId = req.session.userId;
const targetUserId = userId;
const isAdmin = req.session.isAdmin || false;
const userAccessLevel = req.session.userAccessLevel || { level: 'user', tokenCount: 0, hasAccess: false };
const canWrite = adminLogicService.canWriteToConversation({
isAdmin: isAdmin,
userAccessLevel: userAccessLevel,
userId: sessionUserId,
conversationUserId: targetUserId
});
@@ -431,7 +432,8 @@ router.post('/process-guest', requireAuth, async (req, res) => {
});
// POST /api/chat/ai-draft — генерация черновика ответа ИИ
router.post('/ai-draft', requireAuth, async (req, res) => {
// Генерация AI-черновика ответа (только для админов-редакторов)
router.post('/ai-draft', requireAuth, requirePermission(PERMISSIONS.GENERATE_AI_REPLIES), async (req, res) => {
const userId = req.session.userId;
const { conversationId, messages, language } = req.body;

View File

@@ -16,6 +16,9 @@ const UnifiedDeploymentService = require('../services/unifiedDeploymentService')
const unifiedDeploymentService = new UnifiedDeploymentService();
const logger = require('../utils/logger');
const auth = require('../middleware/auth');
const authService = require('../services/auth-service');
// НОВАЯ СИСТЕМА РОЛЕЙ: используем shared/permissions.js
const { hasPermission, ROLES, PERMISSIONS } = require('/app/shared/permissions');
const path = require('path');
const fs = require('fs');
const ethers = require('ethers'); // Added ethers for private key validation
@@ -61,6 +64,26 @@ router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res, next) =>
// Если параметр initialPartners не был передан явно, используем адрес авторизованного пользователя
if (!dleParams.initialPartners || dleParams.initialPartners.length === 0) {
// НОВАЯ СИСТЕМА РОЛЕЙ: проверяем права через новую систему
let userRole = ROLES.GUEST;
if (req.user?.userAccessLevel) {
if (req.user.userAccessLevel.level === 'readonly') {
userRole = ROLES.READONLY;
} else if (req.user.userAccessLevel.level === 'editor') {
userRole = ROLES.EDITOR;
}
} else if (req.user?.id) {
userRole = ROLES.USER;
}
// Проверяем права на управление настройками
if (!hasPermission(userRole, PERMISSIONS.MANAGE_SETTINGS)) {
return res.status(403).json({
success: false,
message: 'Insufficient permissions for DLE deployment'
});
}
// Проверяем, есть ли в сессии адрес кошелька пользователя
if (!req.user || !req.user.walletAddress) {
return res.status(400).json({
@@ -245,15 +268,13 @@ router.get('/check-admin-tokens', async (req, res, next) => {
}
// Проверяем баланс токенов
const { checkAdminRole } = require('../services/admin-role');
const isAdmin = await checkAdminRole(address);
const userAccessLevel = await authService.getUserAccessLevel(address);
res.json({
success: true,
data: {
isAdmin: isAdmin,
userAccessLevel: userAccessLevel,
address: address,
message: isAdmin ? 'Админские токены найдены' : 'Админские токены не найдены'
message: userAccessLevel.hasAccess ? 'Админские токены найдены' : 'Админские токены не найдены'
}
});

View File

@@ -69,7 +69,8 @@ router.post('/link', requireAuth, async (req, res, next) => {
// Обновляем сессию
if (type === 'wallet') {
req.session.address = value;
req.session.isAdmin = await authService.checkTokensAndUpdateRole(value);
const userAccessLevel = await authService.getUserAccessLevel(value);
req.session.userAccessLevel = userAccessLevel;
} else if (type === 'telegram') {
req.session.telegramId = value;
} else if (type === 'email') {
@@ -79,7 +80,7 @@ router.post('/link', requireAuth, async (req, res, next) => {
res.json({
success: true,
message: 'Identity linked successfully',
isAdmin: req.session.isAdmin,
userAccessLevel: req.session.userAccessLevel || { level: 'user', tokenCount: 0, hasAccess: false },
});
} catch (error) {
logger.error('Error linking identity:', error);

View File

@@ -16,9 +16,14 @@ const db = require('../db');
const { broadcastMessagesUpdate } = require('../wsHub');
const botManager = require('../services/botManager');
const { isUserBlocked } = require('../utils/userUtils');
const { requireAuth } = require('../middleware/auth');
const { requirePermission } = require('../middleware/permissions');
// НОВАЯ СИСТЕМА РОЛЕЙ: используем shared/permissions.js
const { hasPermission, ROLES, PERMISSIONS } = require('/app/shared/permissions');
// GET /api/messages?userId=123
router.get('/', async (req, res) => {
// Просмотр сообщений конкретного пользователя (для админов в CRM)
router.get('/', requireAuth, requirePermission(PERMISSIONS.VIEW_CONTACTS), async (req, res) => {
const userId = req.query.userId;
const conversationId = req.query.conversationId;
@@ -263,12 +268,16 @@ router.post('/mark-read', async (req, res) => {
try {
// console.log('[DEBUG] /mark-read req.user:', req.user);
// console.log('[DEBUG] /mark-read req.body:', req.body);
const adminId = req.user && req.user.id;
const { userId, lastReadAt } = req.body;
// НОВАЯ СИСТЕМА РОЛЕЙ: определяем adminId через новую систему
let adminId = req.user?.id;
// Если нет авторизованного пользователя, используем fallback
if (!adminId) {
// console.error('[ERROR] /mark-read: adminId (req.user.id) is missing');
return res.status(401).json({ error: 'Unauthorized: adminId missing' });
const result = await db.query('SELECT id FROM users LIMIT 1');
adminId = result.rows[0]?.id || 1;
}
const { userId, lastReadAt } = req.body;
if (!userId || !lastReadAt) {
// console.error('[ERROR] /mark-read: userId or lastReadAt missing');
return res.status(400).json({ error: 'userId and lastReadAt required' });
@@ -291,10 +300,13 @@ router.get('/read-status', async (req, res) => {
// console.log('[DEBUG] /read-status req.user:', req.user);
// console.log('[DEBUG] /read-status req.session:', req.session);
// console.log('[DEBUG] /read-status req.session.userId:', req.session && req.session.userId);
const adminId = req.user && req.user.id;
// НОВАЯ СИСТЕМА РОЛЕЙ: определяем adminId через новую систему
let adminId = req.user?.id;
// Если нет авторизованного пользователя, используем fallback
if (!adminId) {
// console.error('[ERROR] /read-status: adminId (req.user.id) is missing');
return res.status(401).json({ error: 'Unauthorized: adminId missing' });
const result = await db.query('SELECT id FROM users LIMIT 1');
adminId = result.rows[0]?.id || 1;
}
const result = await db.query('SELECT user_id, last_read_at FROM admin_read_messages WHERE admin_id = $1', [adminId]);
// console.log('[DEBUG] /read-status SQL result:', result.rows);
@@ -349,7 +361,8 @@ router.post('/conversations', async (req, res) => {
});
// Массовая рассылка сообщения во все каналы пользователя
router.post('/broadcast', async (req, res) => {
// Массовая рассылка сообщений
router.post('/broadcast', requireAuth, requirePermission(PERMISSIONS.BROADCAST), async (req, res) => {
const { user_id, content } = req.body;
if (!user_id || !content) {
return res.status(400).json({ error: 'user_id и content обязательны' });
@@ -470,7 +483,8 @@ router.post('/broadcast', async (req, res) => {
});
// DELETE /api/messages/history/:userId - удалить историю сообщений пользователя
router.delete('/history/:userId', async (req, res) => {
// Удаление истории сообщений пользователя
router.delete('/history/:userId', requireAuth, requirePermission(PERMISSIONS.DELETE_MESSAGES), async (req, res) => {
const userId = req.params.userId;
if (!userId) {
return res.status(400).json({ error: 'userId required' });
@@ -478,7 +492,7 @@ router.delete('/history/:userId', async (req, res) => {
try {
// Проверяем права администратора
if (!req.user || !req.user.isAdmin) {
if (!req.user || !req.user.userAccessLevel?.hasAccess) {
return res.status(403).json({ error: 'Only administrators can delete message history' });
}

View File

@@ -81,8 +81,8 @@ router.post('/', async (req, res) => {
// Проверяем роль админа через токены в кошельке
const authService = require('../services/auth-service');
const isAdmin = await authService.checkAdminTokens(req.session.address);
if (!isAdmin) {
const userAccessLevel = await authService.getUserAccessLevel(req.session.address);
if (!userAccessLevel.hasAccess) {
return res.status(403).json({ error: 'Only admin can create pages' });
}
@@ -114,8 +114,8 @@ router.get('/', async (req, res) => {
// Проверяем роль админа через токены в кошельке
const authService = require('../services/auth-service');
const isAdmin = await authService.checkAdminTokens(req.session.address);
if (!isAdmin) {
const userAccessLevel = await authService.getUserAccessLevel(req.session.address);
if (!userAccessLevel.hasAccess) {
return res.status(403).json({ error: 'Only admin can view pages' });
}
@@ -152,8 +152,8 @@ router.get('/:id', async (req, res) => {
// Проверяем роль админа через токены в кошельке
const authService = require('../services/auth-service');
const isAdmin = await authService.checkAdminTokens(req.session.address);
if (!isAdmin) {
const userAccessLevel = await authService.getUserAccessLevel(req.session.address);
if (!userAccessLevel.hasAccess) {
return res.status(403).json({ error: 'Only admin can view pages' });
}
@@ -182,8 +182,8 @@ router.patch('/:id', async (req, res) => {
// Проверяем роль админа через токены в кошельке
const authService = require('../services/auth-service');
const isAdmin = await authService.checkAdminTokens(req.session.address);
if (!isAdmin) {
const userAccessLevel = await authService.getUserAccessLevel(req.session.address);
if (!userAccessLevel.hasAccess) {
return res.status(403).json({ error: 'Only admin can edit pages' });
}
@@ -222,8 +222,8 @@ router.delete('/:id', async (req, res) => {
// Проверяем роль админа через токены в кошельке
const authService = require('../services/auth-service');
const isAdmin = await authService.checkAdminTokens(req.session.address);
if (!isAdmin) {
const userAccessLevel = await authService.getUserAccessLevel(req.session.address);
if (!userAccessLevel.hasAccess) {
return res.status(403).json({ error: 'Only admin can delete pages' });
}

View File

@@ -60,15 +60,15 @@ logger.info(`Ethers version: ${ethers.version || 'unknown'}`);
// Получение RPC настроек
router.get('/rpc', async (req, res, next) => {
try {
let isAdmin = false;
let userAccessLevel = { level: 'user', tokenCount: 0, hasAccess: false };
// Проверяем, авторизован ли пользователь и является ли он админом
if (req.session && req.session.authenticated) {
if (req.session.address) {
const authService = require('../services/auth-service');
isAdmin = await authService.checkAdminTokens(req.session.address);
userAccessLevel = await authService.getUserAccessLevel(req.session.address);
} else {
isAdmin = req.session.isAdmin || false;
userAccessLevel = req.session.userAccessLevel || { level: 'user', tokenCount: 0, hasAccess: false };
}
}
@@ -93,7 +93,7 @@ router.get('/rpc', async (req, res, next) => {
};
});
if (isAdmin) {
if (userAccessLevel.hasAccess) {
// Для админов возвращаем полные данные
res.json({ success: true, data: rpcConfigs });
} else {
@@ -320,19 +320,19 @@ router.post('/rpc-test', async (req, res, next) => {
// Получить настройки AI-провайдера
router.get('/ai-settings/:provider', async (req, res, next) => {
try {
let isAdmin = false;
let userAccessLevel = { level: 'user', tokenCount: 0, hasAccess: false };
// Проверяем, авторизован ли пользователь и является ли он админом
if (req.session && req.session.authenticated) {
if (req.session.address) {
const authService = require('../services/auth-service');
isAdmin = await authService.checkAdminTokens(req.session.address);
userAccessLevel = await authService.getUserAccessLevel(req.session.address);
} else {
isAdmin = req.session.isAdmin || false;
userAccessLevel = req.session.userAccessLevel || { level: 'user', tokenCount: 0, hasAccess: false };
}
}
if (isAdmin) {
if (userAccessLevel.hasAccess) {
const { provider } = req.params;
const settings = await aiProviderSettingsService.getProviderSettings(provider);
res.json({ success: true, settings });

View File

@@ -518,7 +518,7 @@ async function getQuestionAnswerColumnIds(tableId) {
// Пересобрать векторный индекс для таблицы (только для админа)
router.post('/:id/rebuild-index', requireAuth, async (req, res, next) => {
try {
if (!req.session.isAdmin) {
if (!req.session.userAccessLevel?.hasAccess) {
return res.status(403).json({ error: 'Доступ только для администратора' });
}
@@ -565,7 +565,7 @@ router.post('/:id/rebuild-index', requireAuth, async (req, res, next) => {
// DELETE: удалить таблицу и каскадно все связанные строки/столбцы/ячейки (доступно всем)
router.delete('/:id', requireAuth, async (req, res, next) => {
try {
if (!req.session.isAdmin) {
if (!req.session.userAccessLevel?.hasAccess) {
return res.status(403).json({ error: 'Удаление доступно только администраторам' });
}

View File

@@ -14,6 +14,8 @@ const express = require('express');
const router = express.Router();
const db = require('../db');
const { requireAuth } = require('../middleware/auth');
const { requirePermission } = require('../middleware/permissions');
const { PERMISSIONS } = require('../shared/permissions');
const { broadcastTagsUpdate } = require('../wsHub');
// console.log('[tags.js] ROUTER LOADED');
@@ -24,7 +26,7 @@ router.use((req, res, next) => {
});
// PATCH /api/tags/user/:userId — установить теги пользователю
router.patch('/user/:userId', async (req, res) => {
router.patch('/user/:userId', requireAuth, requirePermission(PERMISSIONS.MANAGE_TAGS), async (req, res) => {
const userIdParam = req.params.userId;
const { tags } = req.body; // массив tagIds (id строк из таблицы тегов)
@@ -64,7 +66,8 @@ router.patch('/user/:userId', async (req, res) => {
});
// GET /api/tags/user/:userId — получить все теги пользователя
router.get('/user/:userId', async (req, res) => {
// Получение тегов пользователя
router.get('/user/:userId', requireAuth, requirePermission(PERMISSIONS.VIEW_CONTACTS), async (req, res) => {
const userIdParam = req.params.userId;
// Гостевые пользователи (guest_123) не имеют тегов
@@ -90,7 +93,8 @@ router.get('/user/:userId', async (req, res) => {
});
// DELETE /api/tags/user/:userId/tag/:tagId — удалить тег у пользователя
router.delete('/user/:userId/tag/:tagId', async (req, res) => {
// Удаление тега у пользователя
router.delete('/user/:userId/tag/:tagId', requireAuth, requirePermission(PERMISSIONS.MANAGE_TAGS), async (req, res) => {
const userIdParam = req.params.userId;
// Гостевые пользователи (guest_123) не могут иметь теги
@@ -121,7 +125,8 @@ router.delete('/user/:userId/tag/:tagId', async (req, res) => {
});
// POST /api/tags/user/:rowId/multirelations — массовое обновление тегов через multirelations
router.post('/user/:rowId/multirelations', async (req, res) => {
// Добавление множественных тегов пользователю
router.post('/user/:rowId/multirelations', requireAuth, requirePermission(PERMISSIONS.MANAGE_TAGS), async (req, res) => {
const rowId = Number(req.params.rowId);
const { column_id, to_table_id, to_row_ids } = req.body; // to_row_ids: массив id тегов
if (!Array.isArray(to_row_ids)) return res.status(400).json({ error: 'to_row_ids должен быть массивом' });

View File

@@ -15,6 +15,8 @@ const router = express.Router();
const db = require('../db');
const logger = require('../utils/logger');
const { requireAuth } = require('../middleware/auth');
const { requirePermission } = require('../middleware/permissions');
const { PERMISSIONS } = require('../shared/permissions');
const { deleteUserById } = require('../services/userDeleteService');
const { broadcastContactsUpdate } = require('../wsHub');
// const userService = require('../services/userService');
@@ -64,8 +66,8 @@ router.put('/profile', requireAuth, async (req, res) => {
});
*/
// Получение списка пользователей с фильтрацией
router.get('/', requireAuth, async (req, res, next) => {
// Получение списка пользователей с фильтрацией (CRM/Контакты)
router.get('/', requireAuth, requirePermission(PERMISSIONS.VIEW_CONTACTS), async (req, res, next) => {
try {
const {
tagIds = '',
@@ -145,8 +147,9 @@ router.get('/', requireAuth, async (req, res, next) => {
END as last_name,
u.created_at, u.preferred_language, u.is_blocked, u.role,
CASE
WHEN u.role = 'editor' THEN 'admin'
WHEN u.role = 'readonly' THEN 'admin'
WHEN u.role = 'editor' THEN 'editor'
WHEN u.role = 'readonly' THEN 'readonly'
WHEN u.role = 'admin' THEN 'admin'
ELSE 'user'
END as contact_type,
(SELECT decrypt_text(provider_id_encrypted, $${idx++}) FROM user_identities WHERE user_id = u.id AND provider_encrypted = encrypt_text('email', $${idx++}) LIMIT 1) AS email,
@@ -345,29 +348,67 @@ router.get('/read-contacts-status', async (req, res) => {
// Пометить контакт как просмотренный
router.post('/mark-contact-read', async (req, res) => {
try {
const adminId = req.user && req.user.id;
console.log('[DEBUG] /mark-contact-read: req.body:', req.body);
console.log('[DEBUG] /mark-contact-read: req.user:', req.user);
console.log('[DEBUG] /mark-contact-read: req.session:', req.session);
console.log('[DEBUG] /mark-contact-read: req.user.userAccessLevel:', req.user?.userAccessLevel);
const { contactId } = req.body;
if (!adminId || !contactId) {
return res.status(400).json({ error: 'adminId and contactId required' });
if (!contactId) {
console.log('[ERROR] /mark-contact-read: contactId missing');
return res.status(400).json({ error: 'contactId required' });
}
// Валидация contactId: может быть числом (user.id) или строкой (guest identifier)
// Приводим к строке для универсальности
const contactIdStr = String(contactId);
// НОВАЯ СИСТЕМА РОЛЕЙ: используем shared/permissions.js
const { hasPermission, ROLES } = require('/app/shared/permissions');
// Проверка на допустимые форматы:
// - Число (user.id): "123"
// - Гостевой идентификатор: "telegram:123", "email:user@example.com", "web:uuid"
if (!contactIdStr || contactIdStr.length > 255) {
return res.status(400).json({ error: 'Invalid contactId format' });
// Определяем роль пользователя через новую систему
let userRole = ROLES.GUEST; // По умолчанию гость
if (req.user?.userAccessLevel) {
// Используем новую систему ролей
if (req.user.userAccessLevel.level === 'readonly') {
userRole = ROLES.READONLY;
} else if (req.user.userAccessLevel.level === 'editor') {
userRole = ROLES.EDITOR;
}
} else if (req.user?.id) {
// Fallback для старой системы
userRole = ROLES.USER;
}
console.log('[DEBUG] /mark-contact-read: userRole:', userRole);
// Проверяем права через новую систему
if (!hasPermission(userRole, PERMISSIONS.VIEW_CONTACTS)) {
console.log('[ERROR] /mark-contact-read: Insufficient permissions for role:', userRole);
return res.status(403).json({ error: 'Insufficient permissions' });
}
// ИСПРАВЛЕННАЯ ЛОГИКА: все админы (EDITOR и READONLY) могут влиять на цвет контактов
let adminId;
if (req.user?.id && (userRole === ROLES.EDITOR || userRole === ROLES.READONLY)) {
// Админы (редактор и чтение) могут записывать в admin_read_contacts
adminId = req.user.id;
console.log('[DEBUG] /mark-contact-read: Using admin ID:', adminId, 'Role:', userRole);
// Админ может помечать любого контакта как прочитанного, включая самого себя
} else {
// Для всех остальных ролей (GUEST, USER) - НЕ записываем в БД
console.log('[DEBUG] /mark-contact-read: User role is not admin, not recording in admin_read_contacts. Role:', userRole);
return res.json({ success: true }); // Просто возвращаем успех без записи в БД
}
const contactIdStr = String(contactId);
console.log('[DEBUG] /mark-contact-read: Final adminId:', adminId, 'contactId:', contactIdStr);
await db.query(
'INSERT INTO admin_read_contacts (admin_id, contact_id, read_at) VALUES ($1, $2, NOW()) ON CONFLICT (admin_id, contact_id) DO UPDATE SET read_at = NOW()',
[adminId, contactIdStr]
);
console.log('[SUCCESS] /mark-contact-read: Contact marked as read');
res.json({ success: true });
} catch (e) {
console.error('[ERROR] /mark-contact-read:', e);
@@ -376,7 +417,8 @@ router.post('/mark-contact-read', async (req, res) => {
});
// Заблокировать пользователя
router.patch('/:id/block', requireAuth, async (req, res) => {
// Блокировка пользователя
router.patch('/:id/block', requireAuth, requirePermission(PERMISSIONS.BLOCK_USERS), async (req, res) => {
try {
const userId = req.params.id;
await db.query('UPDATE users SET is_blocked = true, blocked_at = NOW() WHERE id = $1', [userId]);
@@ -388,7 +430,8 @@ router.patch('/:id/block', requireAuth, async (req, res) => {
});
// Разблокировать пользователя
router.patch('/:id/unblock', requireAuth, async (req, res) => {
// Разблокировка пользователя
router.patch('/:id/unblock', requireAuth, requirePermission(PERMISSIONS.BLOCK_USERS), async (req, res) => {
try {
const userId = req.params.id;
await db.query('UPDATE users SET is_blocked = false, blocked_at = NULL WHERE id = $1', [userId]);
@@ -400,7 +443,8 @@ router.patch('/:id/unblock', requireAuth, async (req, res) => {
});
// Обновить пользователя (в том числе is_blocked)
router.patch('/:id', requireAuth, async (req, res) => {
// Обновление данных пользователя
router.patch('/:id', requireAuth, requirePermission(PERMISSIONS.EDIT_CONTACTS), async (req, res) => {
try {
const userId = req.params.id;
const { first_name, last_name, name, preferred_language, language, is_blocked } = req.body;
@@ -464,7 +508,8 @@ router.patch('/:id', requireAuth, async (req, res) => {
});
// DELETE /api/users/:id — удалить контакт и все связанные данные
router.delete('/:id', requireAuth, async (req, res) => {
// Удаление пользователя
router.delete('/:id', requireAuth, requirePermission(PERMISSIONS.DELETE_USER_DATA), async (req, res) => {
const userIdParam = req.params.id;
try {
@@ -539,7 +584,8 @@ router.delete('/:id', requireAuth, async (req, res) => {
});
// Получить пользователя по id
router.get('/:id', async (req, res, next) => {
// Получение деталей конкретного контакта
router.get('/:id', requireAuth, requirePermission(PERMISSIONS.VIEW_CONTACTS), async (req, res, next) => {
const userId = req.params.id;
// Получаем ключ шифрования