feat: новая функция
This commit is contained in:
@@ -160,7 +160,7 @@ app.use((req, res, next) => {
|
||||
if (req.session && req.session.userId) {
|
||||
req.user = {
|
||||
id: req.session.userId,
|
||||
isAdmin: req.session.isAdmin,
|
||||
userAccessLevel: req.session.userAccessLevel || { level: 'user', tokenCount: 0, hasAccess: false },
|
||||
address: req.session.address,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
const { createError } = require('../utils/error');
|
||||
const authService = require('../services/auth-service');
|
||||
const logger = require('../utils/logger');
|
||||
// Используем новые роли: 'editor' и 'readonly' вместо 'admin'
|
||||
// НОВАЯ СИСТЕМА РОЛЕЙ: используем shared/permissions.js
|
||||
const { hasPermission, ROLES, PERMISSIONS } = require('/app/shared/permissions');
|
||||
const db = require('../db');
|
||||
const { checkAdminTokens } = require('../services/auth-service');
|
||||
|
||||
@@ -45,7 +46,7 @@ async function requireAdmin(req, res, next) {
|
||||
logger.info(`[requireAdmin] Session:`, {
|
||||
exists: !!req.session,
|
||||
authenticated: req.session?.authenticated,
|
||||
isAdmin: req.session?.isAdmin,
|
||||
userAccessLevel: req.session?.userAccessLevel,
|
||||
userId: req.session?.userId,
|
||||
address: req.session?.address
|
||||
});
|
||||
@@ -57,18 +58,18 @@ async function requireAdmin(req, res, next) {
|
||||
}
|
||||
|
||||
// Проверка через сессию
|
||||
if (req.session.isAdmin) {
|
||||
// logger.info(`[requireAdmin] Доступ разрешен через сессию isAdmin`); // Убрано
|
||||
if (req.session.userAccessLevel?.hasAccess) {
|
||||
// logger.info(`[requireAdmin] Доступ разрешен через сессию userAccessLevel`); // Убрано
|
||||
return next();
|
||||
}
|
||||
|
||||
// Проверка через кошелек
|
||||
if (req.session.address) {
|
||||
// logger.info(`[requireAdmin] Проверка через кошелек: ${req.session.address}`); // Убрано
|
||||
const isAdmin = await authService.checkAdminTokens(req.session.address);
|
||||
if (isAdmin) {
|
||||
const userAccessLevel = await authService.getUserAccessLevel(req.session.address);
|
||||
if (userAccessLevel.hasAccess) {
|
||||
// Обновляем сессию
|
||||
req.session.isAdmin = true;
|
||||
req.session.userAccessLevel = userAccessLevel;
|
||||
// logger.info(`[requireAdmin] Доступ разрешен через кошелек`); // Убрано
|
||||
return next();
|
||||
}
|
||||
@@ -82,7 +83,7 @@ async function requireAdmin(req, res, next) {
|
||||
]);
|
||||
if (userResult.rows.length > 0 && (userResult.rows[0].role === 'editor' || userResult.rows[0].role === 'readonly')) {
|
||||
// Обновляем сессию
|
||||
req.session.isAdmin = true;
|
||||
req.session.userAccessLevel = { level: 'editor', tokenCount: 0, hasAccess: true };
|
||||
// logger.info(`[requireAdmin] Доступ разрешен через userId`); // Убрано
|
||||
return next();
|
||||
}
|
||||
@@ -110,7 +111,7 @@ function requireRole(role) {
|
||||
}
|
||||
|
||||
// Для администраторов разрешаем все
|
||||
if (req.session.isAdmin) {
|
||||
if (req.session.userAccessLevel?.hasAccess) {
|
||||
return next();
|
||||
}
|
||||
|
||||
@@ -145,11 +146,11 @@ async function checkRole(req, res, next) {
|
||||
|
||||
// Если есть адрес кошелька - проверяем токены
|
||||
if (req.session.address) {
|
||||
req.session.isAdmin = await checkAdminTokens(req.session.address);
|
||||
req.session.userAccessLevel = await authService.getUserAccessLevel(req.session.address);
|
||||
await req.session.save();
|
||||
}
|
||||
|
||||
if (!req.session.isAdmin) {
|
||||
if (!req.session.userAccessLevel?.hasAccess) {
|
||||
return res.status(403).json({ error: 'Access denied' });
|
||||
}
|
||||
|
||||
@@ -166,9 +167,29 @@ async function checkRole(req, res, next) {
|
||||
const isAuthenticated = requireAuth;
|
||||
|
||||
/**
|
||||
* Проверка прав администратора - алиас для requireAdmin
|
||||
* НОВАЯ СИСТЕМА: проверка прав через permissions
|
||||
*/
|
||||
const isAdmin = requireAdmin;
|
||||
const isAdmin = (req, res, next) => {
|
||||
// Определяем роль пользователя через новую систему
|
||||
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.VIEW_CRM)) {
|
||||
return res.status(403).json({ error: 'Insufficient permissions' });
|
||||
}
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
requireAuth,
|
||||
|
||||
128
backend/middleware/permissions.js
Normal file
128
backend/middleware/permissions.js
Normal file
@@ -0,0 +1,128 @@
|
||||
/**
|
||||
* Copyright (c) 2024-2025 Тарабанов Александр Викторович
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software is proprietary and confidential.
|
||||
* Unauthorized copying, modification, or distribution is prohibited.
|
||||
*
|
||||
* For licensing inquiries: info@hb3-accelerator.com
|
||||
* Website: https://hb3-accelerator.com
|
||||
* GitHub: https://github.com/HB3-ACCELERATOR
|
||||
*/
|
||||
|
||||
const { PERMISSIONS_MAP, hasPermission, hasAnyPermission } = require('../shared/permissions');
|
||||
const logger = require('../utils/logger');
|
||||
|
||||
/**
|
||||
* Получить роль пользователя из сессии
|
||||
* @param {Object} req - Express request
|
||||
* @returns {Promise<string>} - Роль: 'guest', 'user', 'readonly', 'editor'
|
||||
*/
|
||||
async function getUserRole(req) {
|
||||
const userId = req.session?.userId;
|
||||
const address = req.session?.address;
|
||||
|
||||
// Неавторизованный пользователь
|
||||
if (!userId) {
|
||||
return 'guest';
|
||||
}
|
||||
|
||||
// Авторизован, но нет кошелька или адреса
|
||||
if (!address) {
|
||||
return 'user';
|
||||
}
|
||||
|
||||
// Используем существующую логику из auth-service для получения роли
|
||||
try {
|
||||
const authService = require('../services/auth-service');
|
||||
const accessLevel = await authService.getUserAccessLevel(address);
|
||||
|
||||
// accessLevel.level может быть: 'user', 'readonly', 'editor'
|
||||
return accessLevel?.level || 'user';
|
||||
} catch (error) {
|
||||
logger.error('[Permissions] Error getting user role:', error);
|
||||
return 'user'; // Безопасное значение по умолчанию
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware: Требует конкретное право доступа
|
||||
* @param {string} permission - Требуемое право
|
||||
* @returns {Function} Express middleware
|
||||
*/
|
||||
function requirePermission(permission) {
|
||||
return async (req, res, next) => {
|
||||
try {
|
||||
const role = await getUserRole(req);
|
||||
|
||||
if (!hasPermission(role, permission)) {
|
||||
logger.warn(`[Permissions] Access denied: ${role} tried to access ${permission}`);
|
||||
return res.status(403).json({
|
||||
error: 'Доступ запрещен',
|
||||
required: permission,
|
||||
yourRole: role
|
||||
});
|
||||
}
|
||||
|
||||
// Сохраняем роль в req для использования в route handlers
|
||||
req.userRole = role;
|
||||
next();
|
||||
} catch (error) {
|
||||
logger.error('[Permissions] Error checking permission:', error);
|
||||
res.status(500).json({ error: 'Ошибка проверки прав доступа' });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware: Требует хотя бы одно из прав
|
||||
* @param {Array<string>} permissions - Список прав (достаточно одного)
|
||||
* @returns {Function} Express middleware
|
||||
*/
|
||||
function requireAnyPermission(permissions) {
|
||||
return async (req, res, next) => {
|
||||
try {
|
||||
const role = await getUserRole(req);
|
||||
|
||||
if (!hasAnyPermission(role, permissions)) {
|
||||
logger.warn(`[Permissions] Access denied: ${role} tried to access any of [${permissions.join(', ')}]`);
|
||||
return res.status(403).json({
|
||||
error: 'Доступ запрещен',
|
||||
required: permissions,
|
||||
yourRole: role
|
||||
});
|
||||
}
|
||||
|
||||
req.userRole = role;
|
||||
next();
|
||||
} catch (error) {
|
||||
logger.error('[Permissions] Error checking permissions:', error);
|
||||
res.status(500).json({ error: 'Ошибка проверки прав доступа' });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Middleware: Проверяет право в route handler (не блокирует запрос)
|
||||
* Добавляет req.hasPermission() для использования в контроллере
|
||||
*/
|
||||
function attachPermissionChecker(req, res, next) {
|
||||
getUserRole(req).then(role => {
|
||||
req.userRole = role;
|
||||
req.hasPermission = (permission) => hasPermission(role, permission);
|
||||
next();
|
||||
}).catch(error => {
|
||||
logger.error('[Permissions] Error attaching permission checker:', error);
|
||||
req.userRole = 'guest';
|
||||
req.hasPermission = () => false;
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getUserRole,
|
||||
requirePermission,
|
||||
requireAnyPermission,
|
||||
attachPermissionChecker
|
||||
};
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 ? 'Админские токены найдены' : 'Админские токены не найдены'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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' });
|
||||
}
|
||||
|
||||
|
||||
@@ -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' });
|
||||
}
|
||||
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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: 'Удаление доступно только администраторам' });
|
||||
}
|
||||
|
||||
|
||||
@@ -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 должен быть массивом' });
|
||||
|
||||
@@ -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;
|
||||
|
||||
// Получаем ключ шифрования
|
||||
|
||||
@@ -210,10 +210,10 @@ class IdentityLinkService {
|
||||
);
|
||||
|
||||
// 6. Проверяем админские права
|
||||
const { checkAdminRole } = require('./admin-role');
|
||||
const isAdmin = await checkAdminRole(walletAddress);
|
||||
const authService = require('./auth-service');
|
||||
const userAccessLevel = await authService.getUserAccessLevel(walletAddress);
|
||||
|
||||
if (isAdmin) {
|
||||
if (userAccessLevel.hasAccess) {
|
||||
await db.getQuery()(
|
||||
`UPDATE users SET role = $1 WHERE id = $2`,
|
||||
['editor', userId]
|
||||
@@ -235,7 +235,7 @@ class IdentityLinkService {
|
||||
userId,
|
||||
identifier,
|
||||
provider: tokenData.source_provider,
|
||||
role: isAdmin ? 'admin' : 'user'
|
||||
role: userAccessLevel.hasAccess ? 'admin' : 'user'
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
|
||||
@@ -47,16 +47,16 @@ function shouldGenerateAiReply(params) {
|
||||
/**
|
||||
* Проверить, может ли пользователь писать в беседу
|
||||
* @param {Object} params - Параметры
|
||||
* @param {boolean} params.isAdmin - Является ли админом
|
||||
* @param {Object} params.userAccessLevel - Уровень доступа пользователя
|
||||
* @param {number} params.userId - ID пользователя
|
||||
* @param {number} params.conversationUserId - ID владельца беседы
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function canWriteToConversation(params) {
|
||||
const { isAdmin, userId, conversationUserId } = params;
|
||||
const { userAccessLevel, userId, conversationUserId } = params;
|
||||
|
||||
// Админ может писать в любую беседу
|
||||
if (isAdmin) {
|
||||
if (userAccessLevel?.hasAccess) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,10 +59,10 @@ class AuthService {
|
||||
/**
|
||||
* Находит или создает пользователя по адресу кошелька
|
||||
* @param {string} address - Адрес кошелька
|
||||
* @param {boolean} isAdmin - Предварительно проверенный статус админа
|
||||
* @returns {Promise<{userId: number, isAdmin: boolean}>}
|
||||
* @param {Object} userAccessLevel - Предварительно проверенный уровень доступа
|
||||
* @returns {Promise<{userId: number, userAccessLevel: Object}>}
|
||||
*/
|
||||
async findOrCreateUser(address, isAdmin = null) {
|
||||
async findOrCreateUser(address, userAccessLevel = null) {
|
||||
try {
|
||||
// Нормализуем адрес - всегда приводим к нижнему регистру
|
||||
const normalizedAddress = ethers.getAddress(address).toLowerCase();
|
||||
@@ -80,29 +80,27 @@ class AuthService {
|
||||
}
|
||||
const userData = user[0];
|
||||
|
||||
// Используем предварительно проверенный статус админа или проверяем заново
|
||||
const adminStatus = isAdmin !== null ? isAdmin : await checkAdminRole(normalizedAddress);
|
||||
// Используем предварительно проверенный уровень доступа или проверяем заново
|
||||
const currentAccessLevel = userAccessLevel !== null ? userAccessLevel : await this.getUserAccessLevel(normalizedAddress);
|
||||
|
||||
// Если статус админа изменился, обновляем роль в базе данных
|
||||
if (userData.role === 'admin' && !adminStatus) {
|
||||
await db.getQuery()('UPDATE users SET role = $1 WHERE id = $2', ['user', userData.id]);
|
||||
logger.info(`Updated user ${userData.id} role to user (admin tokens no longer present)`);
|
||||
return { userId: userData.id, isAdmin: false };
|
||||
} else if (userData.role !== 'admin' && adminStatus) {
|
||||
await db.getQuery()('UPDATE users SET role = $1 WHERE id = $2', ['admin', userData.id]);
|
||||
logger.info(`Updated user ${userData.id} role to admin (admin tokens found)`);
|
||||
return { userId: userData.id, isAdmin: true };
|
||||
// Если уровень доступа изменился, обновляем роль в базе данных
|
||||
const currentRole = userData.role === 'admin' ? 'editor' : 'user';
|
||||
const newRole = currentAccessLevel.hasAccess ? 'admin' : 'user';
|
||||
|
||||
if (currentRole !== newRole) {
|
||||
await db.getQuery()('UPDATE users SET role = $1 WHERE id = $2', [newRole, userData.id]);
|
||||
logger.info(`Updated user ${userData.id} role to ${newRole} (access level changed)`);
|
||||
}
|
||||
|
||||
return {
|
||||
userId: userData.id,
|
||||
isAdmin: userData.role === 'admin',
|
||||
userAccessLevel: currentAccessLevel,
|
||||
};
|
||||
}
|
||||
|
||||
// Если пользователь не найден, создаем нового с правильной ролью
|
||||
const adminStatus = isAdmin !== null ? isAdmin : await checkAdminRole(normalizedAddress);
|
||||
const initialRole = adminStatus ? 'admin' : 'user';
|
||||
const currentAccessLevel = userAccessLevel !== null ? userAccessLevel : await this.getUserAccessLevel(normalizedAddress);
|
||||
const initialRole = currentAccessLevel.hasAccess ? 'admin' : 'user';
|
||||
|
||||
const newUserResult = await db.getQuery()('INSERT INTO users (role) VALUES ($1) RETURNING id', [initialRole]);
|
||||
const userId = newUserResult.rows[0].id;
|
||||
@@ -118,7 +116,7 @@ class AuthService {
|
||||
|
||||
broadcastContactsUpdate();
|
||||
|
||||
return { userId, isAdmin: adminStatus };
|
||||
return { userId, userAccessLevel: currentAccessLevel };
|
||||
} catch (error) {
|
||||
logger.error('Error finding or creating user:', error);
|
||||
throw error;
|
||||
@@ -210,7 +208,7 @@ class AuthService {
|
||||
}
|
||||
|
||||
// Создание сессии с проверкой роли
|
||||
async createSession(session, { userId, authenticated, authType, guestId, address, isAdmin }) {
|
||||
async createSession(session, { userId, authenticated, authType, guestId, address, userAccessLevel }) {
|
||||
try {
|
||||
// Если пользователь аутентифицирован, обрабатываем гостевые сообщения
|
||||
if (authenticated && guestId) {
|
||||
@@ -221,7 +219,7 @@ class AuthService {
|
||||
session.userId = userId;
|
||||
session.authenticated = authenticated;
|
||||
session.authType = authType;
|
||||
session.isAdmin = isAdmin || false;
|
||||
session.userAccessLevel = userAccessLevel || { level: 'user', tokenCount: 0, hasAccess: false };
|
||||
|
||||
// Сохраняем адрес кошелька если есть
|
||||
if (address) {
|
||||
@@ -239,7 +237,7 @@ class AuthService {
|
||||
authenticated,
|
||||
authType,
|
||||
address,
|
||||
isAdmin: isAdmin || false,
|
||||
userAccessLevel: userAccessLevel || { level: 'user', tokenCount: 0, hasAccess: false },
|
||||
cookie: session.cookie,
|
||||
}),
|
||||
session.id,
|
||||
@@ -306,12 +304,12 @@ class AuthService {
|
||||
return 'user';
|
||||
}
|
||||
|
||||
// Если есть кошелек, проверяем админские токены
|
||||
const isAdmin = await checkAdminRole(wallet);
|
||||
// Если есть кошелек, проверяем уровень доступа
|
||||
const userAccessLevel = await this.getUserAccessLevel(wallet);
|
||||
logger.info(
|
||||
`Role check for user ${userId} with wallet ${wallet}: ${isAdmin ? 'admin' : 'user'}`
|
||||
`Role check for user ${userId} with wallet ${wallet}: ${userAccessLevel.hasAccess ? 'admin' : 'user'}`
|
||||
);
|
||||
return isAdmin ? 'admin' : 'user';
|
||||
return userAccessLevel.hasAccess ? 'admin' : 'user';
|
||||
} catch (error) {
|
||||
logger.error('Error checking user role:', error);
|
||||
return 'user';
|
||||
@@ -343,9 +341,9 @@ class AuthService {
|
||||
let role = 'user'; // Базовая роль для доступа к чату
|
||||
|
||||
if (wallet) {
|
||||
// Если есть кошелек, проверяем баланс токенов
|
||||
const isAdmin = await checkAdminRole(wallet);
|
||||
role = isAdmin ? 'admin' : 'user';
|
||||
// Если есть кошелек, проверяем уровень доступа
|
||||
const userAccessLevel = await this.getUserAccessLevel(wallet);
|
||||
role = userAccessLevel.hasAccess ? 'admin' : 'user';
|
||||
logger.info(`User ${userId} has wallet ${wallet}, role set to ${role}`);
|
||||
} else {
|
||||
logger.info(`User ${userId} has no wallet, using basic user role`);
|
||||
@@ -388,7 +386,7 @@ class AuthService {
|
||||
return {
|
||||
success: true,
|
||||
userId,
|
||||
role: session.isAdmin ? 'admin' : 'user',
|
||||
role: session.userAccessLevel?.hasAccess ? 'admin' : 'user',
|
||||
telegramId,
|
||||
isNewUser: false,
|
||||
};
|
||||
@@ -651,10 +649,10 @@ class AuthService {
|
||||
try {
|
||||
// Используем новую функцию для определения уровня доступа
|
||||
const accessLevel = await this.getUserAccessLevel(address);
|
||||
const isAdmin = accessLevel.hasAccess; // Любой доступ выше 'user' считается админским
|
||||
const hasAccess = accessLevel.hasAccess; // Любой доступ выше 'user' считается админским
|
||||
|
||||
// Обновляем роль пользователя в базе данных
|
||||
if (isAdmin) {
|
||||
if (hasAccess) {
|
||||
try {
|
||||
// Получаем ключ шифрования
|
||||
const fs = require('fs');
|
||||
@@ -725,7 +723,7 @@ class AuthService {
|
||||
}
|
||||
}
|
||||
|
||||
return isAdmin;
|
||||
return hasAccess;
|
||||
} catch (error) {
|
||||
logger.error(`Error in checkAdminTokens: ${error.message}`);
|
||||
return false; // При любой ошибке считаем, что пользователь не админ
|
||||
@@ -946,12 +944,12 @@ class AuthService {
|
||||
});
|
||||
|
||||
// Проверяем и обновляем роль администратора, если это идентификатор кошелька
|
||||
let isAdmin = false;
|
||||
let userAccessLevel = { level: 'user', tokenCount: 0, hasAccess: false };
|
||||
if (provider === 'wallet') {
|
||||
isAdmin = await this.checkAdminTokens(normalizedProviderId);
|
||||
userAccessLevel = await this.getUserAccessLevel(normalizedProviderId);
|
||||
|
||||
// Обновляем роль пользователя в базе данных, если нужно
|
||||
if (isAdmin) {
|
||||
if (userAccessLevel.hasAccess) {
|
||||
await db.getQuery()('UPDATE users SET role = $1 WHERE id = $2', ['admin', userId]);
|
||||
logger.info(`[AuthService] Updated user ${userId} role to admin based on token holdings`);
|
||||
}
|
||||
@@ -960,7 +958,7 @@ class AuthService {
|
||||
logger.info(
|
||||
`[AuthService] Identity ${provider}:${normalizedProviderId} successfully linked to user ${userId}`
|
||||
);
|
||||
return { success: true, isAdmin };
|
||||
return { success: true, userAccessLevel };
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`[AuthService] Error linking identity ${provider}:${providerId} to user ${userId}:`,
|
||||
@@ -1027,8 +1025,8 @@ class AuthService {
|
||||
const linkedWallet = await getLinkedWallet(userId);
|
||||
if (linkedWallet && linkedWallet.provider_id) {
|
||||
logger.info(`[handleEmailVerification] Found linked wallet ${linkedWallet.provider_id}. Checking role...`);
|
||||
const isAdmin = await checkAdminRole(linkedWallet.provider_id);
|
||||
userRole = isAdmin ? 'admin' : 'user';
|
||||
const userAccessLevel = await this.getUserAccessLevel(linkedWallet.provider_id);
|
||||
userRole = userAccessLevel.hasAccess ? 'admin' : 'user';
|
||||
logger.info(`[handleEmailVerification] Role determined as: ${userRole}`);
|
||||
|
||||
// Опционально: Обновить роль в таблице users
|
||||
|
||||
@@ -244,8 +244,9 @@ class EmailAuth {
|
||||
const linkedWallet = await authService.getLinkedWallet(finalUserId);
|
||||
if (linkedWallet) {
|
||||
logger.info(`[checkEmailVerification] Found linked wallet ${linkedWallet} for user ${finalUserId}. Checking admin role...`);
|
||||
const isAdmin = await checkAdminRole(linkedWallet);
|
||||
userRole = isAdmin ? 'admin' : 'user';
|
||||
const authService = require('./auth-service');
|
||||
const userAccessLevel = await authService.getUserAccessLevel(linkedWallet);
|
||||
userRole = userAccessLevel.hasAccess ? 'admin' : 'user';
|
||||
logger.info(`[checkEmailVerification] Role for user ${finalUserId} determined as: ${userRole}`);
|
||||
|
||||
// Опционально: Обновить роль в таблице users, если она отличается
|
||||
|
||||
@@ -521,8 +521,8 @@ class IdentityService {
|
||||
const wallet = await getLinkedWallet(user.id);
|
||||
let role = 'user';
|
||||
if (wallet) {
|
||||
const isAdmin = await checkAdminRole(wallet);
|
||||
role = isAdmin ? 'admin' : 'user';
|
||||
const userAccessLevel = await authService.getUserAccessLevel(wallet);
|
||||
role = userAccessLevel.hasAccess ? 'admin' : 'user';
|
||||
// Обновляем роль в users, если изменилась
|
||||
if (user.role !== role) {
|
||||
await encryptedDb.saveData('users', {
|
||||
|
||||
@@ -225,7 +225,7 @@ class SessionService {
|
||||
return false;
|
||||
}
|
||||
|
||||
const { userId, authType, isAdmin, ...otherData } = authData;
|
||||
const { userId, authType, userAccessLevel, ...otherData } = authData;
|
||||
|
||||
if (!userId || !authType) {
|
||||
logger.warn('[SessionService] Missing userId or authType in authData');
|
||||
@@ -237,8 +237,8 @@ class SessionService {
|
||||
session.authType = authType;
|
||||
session.authenticated = true;
|
||||
|
||||
if (isAdmin !== undefined) {
|
||||
session.isAdmin = isAdmin;
|
||||
if (userAccessLevel !== undefined) {
|
||||
session.userAccessLevel = userAccessLevel;
|
||||
}
|
||||
|
||||
// Обновляем дополнительные данные в зависимости от типа аутентификации
|
||||
@@ -314,7 +314,7 @@ class SessionService {
|
||||
delete session.userId;
|
||||
delete session.authenticated;
|
||||
delete session.authType;
|
||||
delete session.isAdmin;
|
||||
delete session.userAccessLevel;
|
||||
delete session.address;
|
||||
delete session.email;
|
||||
delete session.telegramId;
|
||||
|
||||
@@ -19,6 +19,8 @@ const adminLogicService = require('./adminLogicService');
|
||||
const universalGuestService = require('./UniversalGuestService');
|
||||
const identityService = require('./identity-service');
|
||||
const { broadcastMessagesUpdate } = require('../wsHub');
|
||||
// НОВАЯ СИСТЕМА РОЛЕЙ: используем shared/permissions.js
|
||||
const { hasPermission, ROLES, PERMISSIONS } = require('/app/shared/permissions');
|
||||
|
||||
/**
|
||||
* Унифицированный процессор сообщений для всех каналов
|
||||
@@ -87,8 +89,8 @@ async function processMessage(messageData) {
|
||||
role: userRole
|
||||
});
|
||||
|
||||
// 3. Проверяем: админ или обычный пользователь?
|
||||
const isAdmin = userRole === 'editor' || userRole === 'readonly';
|
||||
// НОВАЯ СИСТЕМА РОЛЕЙ: определяем права через новую систему
|
||||
const isAdmin = userRole === ROLES.EDITOR || userRole === ROLES.READONLY;
|
||||
|
||||
// 4. Определяем нужно ли генерировать AI ответ
|
||||
const shouldGenerateAi = adminLogicService.shouldGenerateAiReply({
|
||||
@@ -98,7 +100,7 @@ async function processMessage(messageData) {
|
||||
channel: channel
|
||||
});
|
||||
|
||||
logger.info('[UnifiedMessageProcessor] Генерация AI:', { shouldGenerateAi, isAdmin });
|
||||
logger.info('[UnifiedMessageProcessor] Генерация AI:', { shouldGenerateAi, userRole, isAdmin });
|
||||
|
||||
// 5. Получаем или создаем беседу
|
||||
let conversation;
|
||||
|
||||
Reference in New Issue
Block a user