diff --git a/EXPORT-TEMPLATE.md b/EXPORT-TEMPLATE.md deleted file mode 100644 index e8f268a..0000000 --- a/EXPORT-TEMPLATE.md +++ /dev/null @@ -1,130 +0,0 @@ -# 🚀 Экспорт шаблона приложения в GitHub - -## Цель -Экспортировать все образы и тома как файлы в GitHub репозиторий для создания полного шаблона приложения. - -## 📋 Команды для экспорта - -### 1. Создать папки для данных -```bash -mkdir -p ./docker-data/images -mkdir -p ./docker-data/volumes -``` - -### 2. Экспортировать образы в файлы -```bash -# Backend образ -docker save digital_legal_entitydle-backend:latest -o ./docker-data/images/backend.tar - -# Frontend образ -docker save digital_legal_entitydle-frontend:latest -o ./docker-data/images/frontend.tar - -# Vector Search образ -docker save digital_legal_entitydle-vector-search:latest -o ./docker-data/images/vector-search.tar - -# Ollama образ -docker save digital_legal_entitydle-ollama:latest -o ./docker-data/images/ollama.tar - -# WebSSH Agent образ -docker save digital_legal_entitydle-webssh-agent:latest -o ./docker-data/images/webssh-agent.tar -``` - -### 3. Экспортировать тома в файлы -```bash -# PostgreSQL данные -docker run --rm -v digital_legal_entitydle_postgres_data:/source -v $(pwd)/docker-data/volumes:/backup alpine tar czf /backup/postgres_data.tar.gz -C /source . - -# Ollama данные -docker run --rm -v digital_legal_entitydle_ollama_data:/source -v $(pwd)/docker-data/volumes:/backup alpine tar czf /backup/ollama_data.tar.gz -C /source . - -# Vector Search данные -docker run --rm -v digital_legal_entitydle_vector_search_data:/source -v $(pwd)/docker-data/volumes:/backup alpine tar czf /backup/vector_search_data.tar.gz -C /source . - -# Backend node_modules -docker run --rm -v digital_legal_entitydle_backend_node_modules:/source -v $(pwd)/docker-data/volumes:/backup alpine tar czf /backup/backend_node_modules.tar.gz -C /source . - -# Frontend node_modules -docker run --rm -v digital_legal_entitydle_frontend_node_modules:/source -v $(pwd)/docker-data/volumes:/backup alpine tar czf /backup/frontend_node_modules.tar.gz -C /source . -``` - -### 4. Загрузить все в GitHub -```bash -# Добавить файлы в git -git add docker-data/ - -# Закоммитить -git commit -m "Add exported images and volumes" - -# Запушить в репозиторий -git push -``` - -## 📊 Результат - -После выполнения всех команд в репозитории будут: -- ✅ Все образы как файлы (.tar) -- ✅ Все тома как файлы (.tar.gz) -- ✅ Полный шаблон приложения - -## 🚀 Использование шаблона - -### Для пользователей: -```bash -# Клонировать репозиторий -git clone https://github.com/VC-HB3-Accelerator/DLE.git -cd DLE - -# Импортировать образы -docker load -i docker-data/images/backend.tar -docker load -i docker-data/images/frontend.tar -docker load -i docker-data/images/vector-search.tar -docker load -i docker-data/images/ollama.tar -docker load -i docker-data/images/webssh-agent.tar - -# Импортировать тома -docker volume create digital_legal_entitydle_postgres_data -docker run --rm -v digital_legal_entitydle_postgres_data:/target -v $(pwd)/docker-data/volumes:/backup alpine tar xzf /backup/postgres_data.tar.gz -C /target - -docker volume create digital_legal_entitydle_ollama_data -docker run --rm -v digital_legal_entitydle_ollama_data:/target -v $(pwd)/docker-data/volumes:/backup alpine tar xzf /backup/ollama_data.tar.gz -C /target - -docker volume create digital_legal_entitydle_vector_search_data -docker run --rm -v digital_legal_entitydle_vector_search_data:/target -v $(pwd)/docker-data/volumes:/backup alpine tar xzf /backup/vector_search_data.tar.gz -C /target - -docker volume create digital_legal_entitydle_backend_node_modules -docker run --rm -v digital_legal_entitydle_backend_node_modules:/target -v $(pwd)/docker-data/volumes:/backup alpine tar xzf /backup/backend_node_modules.tar.gz -C /target - -docker volume create digital_legal_entitydle_frontend_node_modules -docker run --rm -v digital_legal_entitydle_frontend_node_modules:/target -v $(pwd)/docker-data/volumes:/backup alpine tar xzf /backup/frontend_node_modules.tar.gz -C /target - -# Запустить приложение -docker-compose up -d -``` - -## ⚠️ Важные замечания - -1. **Размер файлов**: Образы могут быть большими (несколько GB) -2. **Время загрузки**: Зависит от размера файлов и скорости интернета -3. **Место на диске**: Убедитесь, что достаточно места для всех файлов - -## 📁 Структура после экспорта - -``` -docker-data/ -├── images/ -│ ├── backend.tar -│ ├── frontend.tar -│ ├── vector-search.tar -│ ├── ollama.tar -│ └── webssh-agent.tar -└── volumes/ - ├── postgres_data.tar.gz - ├── ollama_data.tar.gz - ├── vector_search_data.tar.gz - ├── backend_node_modules.tar.gz - └── frontend_node_modules.tar.gz -``` - -## ✅ Готово! - -После выполнения всех команд у вас будет полный шаблон приложения в GitHub репозитории, который можно клонировать и запускать на любом устройстве. diff --git a/README.md b/README.md index 4936381..f5f5e9e 100644 --- a/README.md +++ b/README.md @@ -44,18 +44,55 @@ docker-compose up -d ``` ### Доступ к приложению + +#### Разработка (dev) - **Frontend**: http://localhost:5173 +- **Backend API**: http://localhost:8000 + +#### Продакшн (production) +- **Frontend**: http://localhost:9000 (HTTP) или https://localhost:9443 (HTTPS) +- **Backend API**: http://localhost:9000/api (через nginx proxy) ## 🔧 Управление ### Запуск +``` + +#### Продакшн (production) ```bash -docker-compose up -d +# Пересборка образов +docker-compose build --no-cache + +# Запуск продакшн-сервисов +NODE_ENV=production docker-compose --profile production up -d + +# Проверка +docker-compose ps +curl http://localhost:9000/api/health ``` ### Остановка ```bash -docker-compose down +docker-compose-down +``` + +### Полезные команды +```bash +# Просмотр логов +docker-compose logs -f backend +docker-compose logs -f frontend-nginx + +# Перезапуск сервиса +docker-compose restart backend +docker-compose restart frontend-nginx + +# Пересборка конкретного сервиса +docker-compose build --no-cache backend +docker-compose build --no-cache frontend-nginx + +# Просмотр статуса +docker-compose ps +docker ps # Все контейнеры в системе ``` ## 📝 Лицензия diff --git a/backend/.dockerignore b/backend/.dockerignore index ce70251..9f2d53e 100644 --- a/backend/.dockerignore +++ b/backend/.dockerignore @@ -22,8 +22,7 @@ README* .git/ .gitignore -# Зависимости (будут установлены в контейнере) -node_modules/ +# Логи установки npm-debug.log* yarn-debug.log* yarn-error.log* diff --git a/backend/app.js b/backend/app.js index f4d9e2a..9361e90 100644 --- a/backend/app.js +++ b/backend/app.js @@ -16,6 +16,7 @@ const cors = require('cors'); const session = require('express-session'); const sessionConfig = require('./config/session'); const logger = require('./utils/logger'); +const rateLimit = require('express-rate-limit'); // const csurf = require('csurf'); // Закомментировано, так как не используется const errorHandler = require('./middleware/errorHandler'); // const { version } = require('./package.json'); // Закомментировано, так как не используется @@ -115,14 +116,21 @@ app.set('host', '0.0.0.0'); app.set('port', process.env.PORT || 8000); // Настройка CORS +const corsOrigins = process.env.NODE_ENV === 'production' + ? [ + 'http://localhost:9000', // Локальный Docker nginx + 'https://localhost:9443', // Локальный Docker nginx HTTPS + // Добавьте ваш продакшн домен здесь для VDS + // 'https://yourdomain.com', + ] + : [ + 'http://localhost:5173', // Vite dev server + 'http://127.0.0.1:5173', + ]; + app.use( cors({ - origin: [ - 'http://localhost:5173', - 'http://127.0.0.1:5173', - 'https://hb3-accelerator.com', - 'https://www.hb3-accelerator.com', - ], + origin: corsOrigins, credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization', 'Cookie'], @@ -171,6 +179,36 @@ app.use((req, res, next) => { app.use(express.json()); app.use(express.urlencoded({ extended: true })); +// Определяем режим работы +const isProduction = process.env.NODE_ENV === 'production'; + +// Rate limiting +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 минут + max: isProduction ? 100 : 1000, // 100 запросов в продакшне, 1000 в dev + message: { + error: 'Слишком много запросов, попробуйте позже', + retryAfter: '15 минут' + }, + standardHeaders: true, + legacyHeaders: false, +}); + +// Применяем rate limiting ко всем запросам +app.use(limiter); + +// Строгий rate limiting для чувствительных эндпоинтов +const strictLimiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 минут + max: 10, // 10 попыток + message: { + error: 'Превышен лимит попыток, попробуйте позже', + retryAfter: '15 минут' + }, + standardHeaders: true, + legacyHeaders: false, +}); + // Статическая раздача загруженных файлов (для dev и prod) app.use('/uploads', express.static(path.join(__dirname, 'uploads'))); app.use('/api/uploads', express.static(path.join(__dirname, 'uploads'))); @@ -178,14 +216,26 @@ app.use('/api/uploads', express.static(path.join(__dirname, 'uploads'))); // Настройка безопасности app.use( helmet({ - contentSecurityPolicy: false, // Отключаем CSP для разработки + contentSecurityPolicy: isProduction ? { + directives: { + defaultSrc: ["'self'"], + styleSrc: ["'self'", "'unsafe-inline'"], + scriptSrc: ["'self'"], + imgSrc: ["'self'", "data:", "https:"], + connectSrc: ["'self'", "ws:", "wss:"], + fontSrc: ["'self'"], + objectSrc: ["'none'"], + mediaSrc: ["'self'"], + frameSrc: ["'none'"], + }, + } : false, // Отключаем CSP для разработки }) ); // Маршруты API app.use('/api/tables', tablesRoutes); // ДОЛЖНО БЫТЬ ВЫШЕ! // app.use('/api', identitiesRoutes); -app.use('/api/auth', authRoutes); +app.use('/api/auth', strictLimiter, authRoutes); // Строгий rate limiting для аутентификации app.use('/api/users', usersRoutes); app.use('/api/chat', chatRoutes); app.use('/api/admin', adminRoutes); @@ -195,10 +245,10 @@ app.use('/api/kpp', kppRoutes); // Добавленное использован app.use('/api/geocoding', geocodingRoutes); // Добавленное использование роута app.use('/api/dle-v2', dleV2Routes); // Добавляем маршрут DLE v2 -app.use('/api/settings', settingsRoutes); // Добавляем маршрут настроек +app.use('/api/settings', strictLimiter, settingsRoutes); // Строгий rate limiting для настроек app.use('/api/countries', countriesRoutes); // Добавляем маршрут стран app.use('/api/russian-classifiers', russianClassifiersRoutes); // Добавляем маршрут российских классификаторов -app.use('/api/ollama', ollamaRoutes); // Добавляем маршрут Ollama +app.use('/api/ollama', strictLimiter, ollamaRoutes); // Строгий rate limiting для Ollama app.use('/api/ai-queue', aiQueueRoutes); // Добавляем маршрут AI Queue app.use('/api/tags', tagsRoutes); // Добавляем маршрут тегов app.use('/api/blockchain', blockchainRoutes); // Добавляем маршрут blockchain diff --git a/backend/config/session.js b/backend/config/session.js index 5f50cca..d20dd5a 100644 --- a/backend/config/session.js +++ b/backend/config/session.js @@ -24,6 +24,8 @@ function setPoolChangeCallback(cb) { let sessionMiddleware = createSessionMiddleware(); function createSessionMiddleware() { + const isProduction = process.env.NODE_ENV === 'production'; + return session({ store: new pgSession({ pool: db.getPool(), @@ -36,8 +38,8 @@ function createSessionMiddleware() { cookie: { maxAge: 30 * 24 * 60 * 60 * 1000, httpOnly: true, - secure: false, - sameSite: 'lax', + secure: false, // false для локального Docker (HTTP) + sameSite: 'lax', // lax для локального Docker path: '/', }, }); diff --git a/backend/routes/settings.js b/backend/routes/settings.js index e7f4aa2..fc0ef49 100644 --- a/backend/routes/settings.js +++ b/backend/routes/settings.js @@ -787,21 +787,25 @@ router.get('/encryption-key/status', requireAdmin, async (req, res) => { const exists = fs.existsSync(keyPath); - let key = null; - if (exists) { - try { - key = fs.readFileSync(keyPath, 'utf8').trim(); - } catch (error) { - logger.error('Ошибка чтения ключа:', error); - } + // Возвращаем только метаданные без содержимого ключа + let checksum = null; + if (exists) { + try { + const data = fs.readFileSync(keyPath); + // лёгкая хэш-сумма для проверки целостности без раскрытия ключа + const crypto = require('crypto'); + checksum = crypto.createHash('sha256').update(data).digest('hex'); + } catch (error) { + logger.error('Ошибка чтения ключа для метаданных:', error); } - - res.json({ - success: true, - exists, - path: keyPath, - key: key - }); + } + + res.json({ + success: true, + exists, + path: keyPath, + checksum + }); } catch (error) { logger.error('Ошибка проверки статуса ключа шифрования:', error); res.status(500).json({ success: false, error: error.message }); diff --git a/backend/routes/ssh.js b/backend/routes/ssh.js index c25588c..acb4443 100644 --- a/backend/routes/ssh.js +++ b/backend/routes/ssh.js @@ -1,48 +1,10 @@ const express = require('express'); const router = express.Router(); -const fs = require('fs'); -const path = require('path'); -const https = require('https'); const { promisify } = require('util'); const dns = require('dns'); - const resolve4 = promisify(dns.resolve4); -const SSH_DIR = path.join(process.env.HOME || process.env.USERPROFILE, '.ssh'); -const DEFAULT_KEY_PATH = path.join(SSH_DIR, 'id_rsa'); -const DEFAULT_PUB_KEY_PATH = path.join(SSH_DIR, 'id_rsa.pub'); - -// Helper to read SSH key -const readSshKey = (keyPath) => { - try { - return fs.readFileSync(keyPath, 'utf8'); - } catch (error) { - return null; - } -}; - -// GET /api/ssh-key - Get existing SSH private key -router.get('/ssh-key', (req, res) => { - const privateKey = readSshKey(DEFAULT_KEY_PATH); - const publicKey = readSshKey(DEFAULT_PUB_KEY_PATH); - - if (privateKey) { - res.json({ success: true, sshKey: privateKey, publicKey: publicKey, keyType: 'rsa' }); - } else { - res.status(404).json({ success: false, message: 'SSH private key not found' }); - } -}); - -// GET /api/ssh-key/public - Get existing SSH public key -router.get('/ssh-key/public', (req, res) => { - const publicKey = readSshKey(DEFAULT_PUB_KEY_PATH); - - if (publicKey) { - res.json({ success: true, publicKey: publicKey, keyType: 'rsa' }); - } else { - res.status(404).json({ success: false, message: 'SSH public key not found' }); - } -}); +// Удалено: эндпоинты выдачи приватного/публичного SSH-ключа // GET /api/dns-check/:domain - Check DNS and get IP address router.get('/dns-check/:domain', async (req, res) => { diff --git a/backend/shared/permissions.js b/backend/shared/permissions.js new file mode 100644 index 0000000..087f590 --- /dev/null +++ b/backend/shared/permissions.js @@ -0,0 +1,279 @@ +/** + * 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 + */ + +/** + * Единая матрица прав доступа для DLE + * Используется и на backend, и на frontend + */ + +// Роли в системе +const ROLES = { + GUEST: 'guest', // Неавторизованный гость + USER: 'user', // Авторизованный гость (0 токенов) + READONLY: 'readonly', // Админ чтение (токены > 0 && < X) + EDITOR: 'editor' // Админ редактор (токены >= X) +}; + +// Список всех прав в системе +const PERMISSIONS = { + // Публичный доступ + VIEW_HOME: 'view_home', + CHAT_AI: 'chat_ai', + + // Получение сообщений + RECEIVE_MESSAGES: 'receive_messages', + + // Просмотр данных + VIEW_CRM: 'view_crm', + VIEW_CONTACTS: 'view_contacts', + VIEW_DATA: 'view_data', + + // Отправка сообщений + SEND_TO_USERS: 'send_to_users', + CHAT_WITH_ADMINS: 'chat_with_admins', + + // AI функции + GENERATE_AI_REPLIES: 'generate_ai_replies', + + // Редактирование + EDIT_USER_DATA: 'edit_user_data', + EDIT_CONTACTS: 'edit_contacts', + + // Удаление + DELETE_USER_DATA: 'delete_user_data', + DELETE_MESSAGES: 'delete_messages', + + // Массовые операции + BROADCAST: 'broadcast', + + // Управление тегами + MANAGE_TAGS: 'manage_tags', + + // Блокировка пользователей + BLOCK_USERS: 'block_users', + + // Управление настройками + MANAGE_SETTINGS: 'manage_settings', + + // Контент: юридические документы + VIEW_BASIC_DOCS: 'view_basic_docs', // Базовые документы для пользователей + VIEW_LEGAL_DOCS: 'view_legal_docs', // Юридические документы для читателей + MANAGE_LEGAL_DOCS: 'manage_legal_docs' // Управление документами для редакторов +}; + +// Матрица: какая роль имеет какие права +const PERMISSIONS_MAP = { + [ROLES.GUEST]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI + ], + + [ROLES.USER]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CONTACTS, // Пользователи могут видеть контакты для выбора + PERMISSIONS.SEND_TO_USERS, // Пользователи могут отправлять сообщения + PERMISSIONS.CHAT_WITH_ADMINS, // Авторизованные пользователи могут видеть личные сообщения + PERMISSIONS.VIEW_BASIC_DOCS // Базовые документы для пользователей + ], + + [ROLES.READONLY]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Чтение внутренних юридических документов + PERMISSIONS.VIEW_LEGAL_DOCS + ], + + [ROLES.EDITOR]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + PERMISSIONS.GENERATE_AI_REPLIES, + PERMISSIONS.EDIT_USER_DATA, + PERMISSIONS.EDIT_CONTACTS, + PERMISSIONS.DELETE_USER_DATA, + PERMISSIONS.DELETE_MESSAGES, + PERMISSIONS.BROADCAST, + PERMISSIONS.MANAGE_TAGS, + PERMISSIONS.BLOCK_USERS, + PERMISSIONS.MANAGE_SETTINGS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Полный доступ к юридическим документам + PERMISSIONS.VIEW_LEGAL_DOCS, + PERMISSIONS.MANAGE_LEGAL_DOCS + ] +}; + +/** + * Проверяет, имеет ли роль определенное право + * @param {string} role - Роль пользователя + * @param {string} permission - Требуемое право + * @returns {boolean} + */ +function hasPermission(role, permission) { + if (!role || !permission) return false; + return PERMISSIONS_MAP[role]?.includes(permission) || false; +} + +/** + * Получает все права для роли + * @param {string} role - Роль пользователя + * @returns {Array} + */ +function getPermissionsForRole(role) { + return PERMISSIONS_MAP[role] || []; +} + +/** + * Проверяет, имеет ли роль ХОТЯ БЫ ОДНО из прав + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAnyPermission(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.some(p => hasPermission(role, p)); +} + +/** + * Проверяет, имеет ли роль ВСЕ указанные права + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAllPermissions(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.every(p => hasPermission(role, p)); +} + +/** + * Получает описание роли + * @param {string} role - Роль пользователя + * @returns {string} + */ +function getRoleDescription(role) { + const descriptions = { + [ROLES.GUEST]: 'Неавторизованный гость', + [ROLES.USER]: 'Авторизованный гость', + [ROLES.READONLY]: 'Админ (только чтение)', + [ROLES.EDITOR]: 'Админ (редактор)' + }; + + return descriptions[role] || 'Неизвестная роль'; +} + +/** + * Проверить, может ли отправитель отправлять сообщения получателю + * @param {string} senderRole - Роль отправителя + * @param {string} recipientRole - Роль получателя + * @param {number} senderId - ID отправителя + * @param {number} recipientId - ID получателя + * @returns {Object} { canSend: boolean, errorMessage?: string } + */ +function canSendMessage(senderRole, recipientRole, senderId, recipientId) { + // Проверяем базовое право на отправку сообщений + if (!hasPermission(senderRole, PERMISSIONS.SEND_TO_USERS)) { + return { + canSend: false, + errorMessage: 'У вас нет права на отправку сообщений' + }; + } + + // Собственный чат - всегда разрешен (для ИИ ассистента) + if (senderId === recipientId) { + return { canSend: true }; + } + + // USER и READONLY могут писать только EDITOR + if ((senderRole === 'user' || senderRole === 'readonly') && recipientRole === 'editor') { + return { canSend: true }; + } + + // EDITOR может писать всем (USER, READONLY, EDITOR) + if (senderRole === 'editor') { + return { canSend: true }; + } + + // USER и READONLY НЕ могут писать друг другу + if ((senderRole === 'user' || senderRole === 'readonly') && + (recipientRole === 'user' || recipientRole === 'readonly')) { + return { + canSend: false, + errorMessage: 'Пользователи и читатели не могут отправлять сообщения друг другу' + }; + } + + // Остальные случаи запрещены + return { + canSend: false, + errorMessage: `Роль ${senderRole} не может отправлять сообщения роли ${recipientRole}` + }; +} + +// Экспорты для CommonJS (Node.js) +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + +// ES модули для Frontend +export { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage +}; + +// CommonJS для Backend +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + diff --git a/database_schema_check.md b/database_schema_check.md deleted file mode 100644 index 7e5bef9..0000000 --- a/database_schema_check.md +++ /dev/null @@ -1,248 +0,0 @@ -# Проверка схемы базы данных - -## Таблица `users` -```sql -CREATE TABLE users ( - id SERIAL PRIMARY KEY, - username VARCHAR(255), - email VARCHAR(255) UNIQUE, - address VARCHAR(255) UNIQUE, - first_name_encrypted TEXT, - last_name_encrypted TEXT, - status VARCHAR(50) DEFAULT 'active', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - role user_role DEFAULT 'user', - first_name VARCHAR(255), - last_name VARCHAR(255), - preferred_language JSONB, - is_blocked BOOLEAN DEFAULT false, - blocked_at TIMESTAMP -); -``` - -**Колонки:** -- `id` - SERIAL PRIMARY KEY -- `username` - VARCHAR(255) -- `email` - VARCHAR(255) UNIQUE -- `address` - VARCHAR(255) UNIQUE -- `first_name_encrypted` - TEXT (зашифрованное) -- `last_name_encrypted` - TEXT (зашифрованное) -- `status` - VARCHAR(50) DEFAULT 'active' -- `created_at` - TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- `updated_at` - TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- `role` - user_role DEFAULT 'user' -- `first_name` - VARCHAR(255) (незашифрованное) -- `last_name` - VARCHAR(255) (незашифрованное) -- `preferred_language` - JSONB -- `is_blocked` - BOOLEAN DEFAULT false -- `blocked_at` - TIMESTAMP - -## Таблица `conversations` -```sql -CREATE TABLE conversations ( - id SERIAL PRIMARY KEY, - user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, - title VARCHAR(255), - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - conversation_type VARCHAR(50) DEFAULT 'user_chat' -); -``` - -**Колонки:** -- `id` - SERIAL PRIMARY KEY -- `user_id` - INTEGER REFERENCES users(id) -- `title` - VARCHAR(255) (НЕ зашифрованное!) -- `created_at` - TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- `updated_at` - TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- `conversation_type` - VARCHAR(50) DEFAULT 'user_chat' - -## Таблица `messages` -```sql -CREATE TABLE messages ( - id SERIAL PRIMARY KEY, - conversation_id INTEGER REFERENCES conversations(id) ON DELETE CASCADE, - sender_type_encrypted TEXT NOT NULL, - sender_id INTEGER, - content_encrypted TEXT, - channel_encrypted TEXT NOT NULL, - role_encrypted TEXT NOT NULL DEFAULT 'user', - direction_encrypted TEXT, - metadata JSONB, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, - tokens_used INTEGER DEFAULT 0, - is_processed BOOLEAN DEFAULT false, - role VARCHAR(20) NOT NULL DEFAULT 'user', - attachment_filename TEXT, - attachment_mimetype TEXT, - attachment_size BIGINT, - attachment_data BYTEA, - direction VARCHAR(8), - message_type VARCHAR(20) DEFAULT 'public' -); -``` - -**Колонки:** -- `id` - SERIAL PRIMARY KEY -- `conversation_id` - INTEGER REFERENCES conversations(id) -- `sender_type_encrypted` - TEXT NOT NULL (зашифрованное) -- `sender_id` - INTEGER -- `content_encrypted` - TEXT (зашифрованное) -- `channel_encrypted` - TEXT NOT NULL (зашифрованное) -- `role_encrypted` - TEXT NOT NULL DEFAULT 'user' (зашифрованное) -- `direction_encrypted` - TEXT (зашифрованное) -- `metadata` - JSONB -- `created_at` - TIMESTAMP DEFAULT CURRENT_TIMESTAMP -- `user_id` - INTEGER REFERENCES users(id) -- `tokens_used` - INTEGER DEFAULT 0 -- `is_processed` - BOOLEAN DEFAULT false -- `role` - VARCHAR(20) NOT NULL DEFAULT 'user' (НЕ зашифрованное!) -- `attachment_filename` - TEXT -- `attachment_mimetype` - TEXT -- `attachment_size` - BIGINT -- `attachment_data` - BYTEA -- `direction` - VARCHAR(8) (НЕ зашифрованное!) -- `message_type` - VARCHAR(20) DEFAULT 'public' - -**Триггеры:** -- `trg_set_message_user_id` - автоматически устанавливает user_id - -## Таблица `conversation_participants` -```sql -CREATE TABLE conversation_participants ( - id SERIAL PRIMARY KEY, - conversation_id INTEGER REFERENCES conversations(id) ON DELETE CASCADE, - user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE(conversation_id, user_id) -); -``` - -**Колонки:** -- `id` - SERIAL PRIMARY KEY -- `conversation_id` - INTEGER REFERENCES conversations(id) -- `user_id` - INTEGER REFERENCES users(id) -- `created_at` - TIMESTAMP DEFAULT CURRENT_TIMESTAMP - -**Индексы:** -- PRIMARY KEY на `id` -- UNIQUE CONSTRAINT на `(conversation_id, user_id)` -- Индекс на `conversation_id` -- Индекс на `user_id` - -## Таблица `admin_read_messages` -```sql -CREATE TABLE admin_read_messages ( - admin_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, - user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, - last_read_at TIMESTAMP NOT NULL, - PRIMARY KEY (admin_id, user_id) -); -``` - -**Колонки:** -- `admin_id` - INTEGER NOT NULL REFERENCES users(id) (админ) -- `user_id` - INTEGER NOT NULL REFERENCES users(id) (пользователь) -- `last_read_at` - TIMESTAMP NOT NULL (время последнего прочтения) - -**Индексы:** -- PRIMARY KEY на `(admin_id, user_id)` - -## Таблица `admin_read_contacts` -```sql -CREATE TABLE admin_read_contacts ( - admin_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, - contact_id TEXT NOT NULL, - read_at TIMESTAMP NOT NULL DEFAULT NOW(), - PRIMARY KEY (admin_id, contact_id) -); -``` - -**Колонки:** -- `admin_id` - INTEGER NOT NULL REFERENCES users(id) (админ) -- `contact_id` - TEXT NOT NULL (ID контакта) -- `read_at` - TIMESTAMP NOT NULL DEFAULT NOW() (время прочтения) - -**Индексы:** -- PRIMARY KEY на `(admin_id, contact_id)` -- Индекс на `admin_id` -- Индекс на `contact_id` - -## Таблица `user_identities` -```sql -CREATE TABLE user_identities ( - id SERIAL PRIMARY KEY, - user_id INTEGER REFERENCES users(id) ON DELETE CASCADE, - provider_encrypted TEXT NOT NULL, - provider_id_encrypted TEXT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -); -``` - -**Колонки:** -- `id` - SERIAL PRIMARY KEY -- `user_id` - INTEGER REFERENCES users(id) (пользователь) -- `provider_encrypted` - TEXT NOT NULL (зашифрованный провайдер) -- `provider_id_encrypted` - TEXT NOT NULL (зашифрованный ID провайдера) -- `created_at` - TIMESTAMP DEFAULT CURRENT_TIMESTAMP - -**Индексы:** -- PRIMARY KEY на `id` -- Индекс на `user_id` - -## Таблица `user_preferences` -```sql -CREATE TABLE user_preferences ( - id SERIAL PRIMARY KEY, - user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, - preference_key VARCHAR(50) NOT NULL, - preference_value TEXT, - metadata JSONB DEFAULT '{}', - created_at TIMESTAMP NOT NULL DEFAULT NOW(), - updated_at TIMESTAMP NOT NULL DEFAULT NOW(), - UNIQUE(user_id, preference_key) -); -``` - -**Колонки:** -- `id` - SERIAL PRIMARY KEY -- `user_id` - INTEGER NOT NULL REFERENCES users(id) (пользователь) -- `preference_key` - VARCHAR(50) NOT NULL (ключ настройки) -- `preference_value` - TEXT (значение настройки) -- `metadata` - JSONB DEFAULT '{}' (метаданные) -- `created_at` - TIMESTAMP NOT NULL DEFAULT NOW() -- `updated_at` - TIMESTAMP NOT NULL DEFAULT NOW() - -**Индексы:** -- PRIMARY KEY на `id` -- Индекс на `user_id` -- UNIQUE CONSTRAINT на `(user_id, preference_key)` - -## Таблица `user_tag_links` -```sql -CREATE TABLE user_tag_links ( - id SERIAL PRIMARY KEY, - user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, - tag_id INTEGER NOT NULL REFERENCES user_rows(id) ON DELETE CASCADE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE(user_id, tag_id) -); -``` - -**Колонки:** -- `id` - SERIAL PRIMARY KEY -- `user_id` - INTEGER NOT NULL REFERENCES users(id) (пользователь) -- `tag_id` - INTEGER NOT NULL REFERENCES user_rows(id) (тег) -- `created_at` - TIMESTAMP DEFAULT CURRENT_TIMESTAMP - -**Индексы:** -- PRIMARY KEY на `id` -- Индекс на `user_id` -- Индекс на `tag_id` -- UNIQUE CONSTRAINT на `(user_id, tag_id)` - -## Анализ проблем в коде - -Теперь, имея полную схему базы данных, давайте проверим код на соответствие: diff --git a/docker-compose.yml b/docker-compose.yml index b73c83c..985ff39 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -24,8 +24,6 @@ services: - POSTGRES_DB=${DB_NAME:-dapp_db} - POSTGRES_USER=${DB_USER:-dapp_user} - POSTGRES_PASSWORD=${DB_PASSWORD:-dapp_password} - ports: - - '5432:5432' # Открываем доступ к базе данных извне для разработки healthcheck: test: - CMD-SHELL @@ -138,7 +136,7 @@ services: - ./ssl:/app/ssl - ./shared:/app/shared:ro environment: - - NODE_ENV=${NODE_ENV:-development} + - NODE_ENV=${NODE_ENV:-production} - PORT=${PORT:-8000} - DB_HOST=postgres - DB_PORT=5432 @@ -150,11 +148,12 @@ services: - OLLAMA_BASE_URL=http://ollama:11434 - OLLAMA_MODEL=${OLLAMA_MODEL:-qwen2.5:7b} - OLLAMA_EMBEDDINGS_MODEL=${OLLAMA_EMBEDDINGS_MODEL:-qwen2.5:7b} - - FRONTEND_URL=http://localhost:5173 + # FRONTEND_URL настраивается в коде, не через env - VECTOR_SEARCH_URL=http://vector-search:8001 # Factory адреса теперь хранятся в базе данных - ports: - - '8000:8000' + # Убираем порты для продакшна - доступ только через nginx + # ports: + # - '8000:8000' extra_hosts: - host.docker.internal:host-gateway healthcheck: @@ -164,6 +163,7 @@ services: retries: 5 start_period: 60s frontend: + profiles: ["dev"] # Только для разработки build: context: ./frontend dockerfile: Dockerfile @@ -204,37 +204,33 @@ services: - "9000:80" # Frontend nginx (для production на VDS) - "9443:443" # HTTPS порт для production на VDS environment: - - DOMAIN=${DOMAIN:-localhost:9000} + - DOMAIN=${DOMAIN:-production.local} - BACKEND_CONTAINER=dapp-backend depends_on: - backend - # SSH Key Server для безопасной передачи ключей - ssh-key-server: - image: node:20-slim - container_name: dapp-ssh-key-server - restart: unless-stopped - dns: - - 1.1.1.1 # Cloudflare (приватность) - - 9.9.9.9 # Quad9 (безопасность + блокировка вредоносных доменов) - - 8.8.8.8 # Google (надежность, fallback) - volumes: - - ./scripts/ssh-key-server.js:/app/ssh-key-server.js:ro - - ./ssl:/app/ssl - - ~/.ssh:/root/.ssh:rw - ports: - - '3001:3001' - command: node /app/ssh-key-server.js - healthcheck: - test: ["CMD", "node", "-e", "require('http').get('http://localhost:3001/ssh-key', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })"] - interval: 30s - timeout: 10s - retries: 3 + # SSH Key Server отключен по безопасности (оставлен пример профиля dev при необходимости) + # ssh-key-server: + # profiles: ["dev"] + # image: node:20-slim + # container_name: dapp-ssh-key-server + # restart: unless-stopped + # dns: + # - 1.1.1.1 + # - 9.9.9.9 + # - 8.8.8.8 + # volumes: + # - ./scripts/ssh-key-server.js:/app/ssh-key-server.js:ro + # - ./ssl:/app/ssl:ro + # - ~/.ssh:/root/.ssh:ro + # command: node /app/ssh-key-server.js + # # Порт намеренно не публикуется, без healthcheck # WebSSH Agent для настройки VDS webssh-agent: + profiles: ["dev"] # Только для разработки build: context: ./webssh-agent dockerfile: Dockerfile diff --git a/frontend/.dockerignore b/frontend/.dockerignore index 19d2b10..4363e06 100644 --- a/frontend/.dockerignore +++ b/frontend/.dockerignore @@ -22,8 +22,7 @@ README* .git/ .gitignore -# Зависимости (будут установлены в контейнере) -node_modules/ +# Логи установки npm-debug.log* yarn-debug.log* yarn-error.log* diff --git a/frontend/docker-entrypoint.sh b/frontend/docker-entrypoint.sh index 7bc34bd..d73dc5c 100644 --- a/frontend/docker-entrypoint.sh +++ b/frontend/docker-entrypoint.sh @@ -9,7 +9,7 @@ echo " DOMAIN: $DOMAIN" echo " BACKEND_CONTAINER: $BACKEND_CONTAINER" # Выбор конфигурации в зависимости от домена -if echo "$DOMAIN" | grep -qE '^localhost(:[0-9]+)?$'; then +if echo "$DOMAIN" | grep -qE '^localhost(:[0-9]+)?$|^production\.local$'; then echo " Режим: ЛОКАЛЬНАЯ РАЗРАБОТКА (без SSL)" TEMPLATE_FILE="/etc/nginx/nginx-local.conf.template" else diff --git a/frontend/shared/permissions.js b/frontend/shared/permissions.js new file mode 100644 index 0000000..087f590 --- /dev/null +++ b/frontend/shared/permissions.js @@ -0,0 +1,279 @@ +/** + * 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 + */ + +/** + * Единая матрица прав доступа для DLE + * Используется и на backend, и на frontend + */ + +// Роли в системе +const ROLES = { + GUEST: 'guest', // Неавторизованный гость + USER: 'user', // Авторизованный гость (0 токенов) + READONLY: 'readonly', // Админ чтение (токены > 0 && < X) + EDITOR: 'editor' // Админ редактор (токены >= X) +}; + +// Список всех прав в системе +const PERMISSIONS = { + // Публичный доступ + VIEW_HOME: 'view_home', + CHAT_AI: 'chat_ai', + + // Получение сообщений + RECEIVE_MESSAGES: 'receive_messages', + + // Просмотр данных + VIEW_CRM: 'view_crm', + VIEW_CONTACTS: 'view_contacts', + VIEW_DATA: 'view_data', + + // Отправка сообщений + SEND_TO_USERS: 'send_to_users', + CHAT_WITH_ADMINS: 'chat_with_admins', + + // AI функции + GENERATE_AI_REPLIES: 'generate_ai_replies', + + // Редактирование + EDIT_USER_DATA: 'edit_user_data', + EDIT_CONTACTS: 'edit_contacts', + + // Удаление + DELETE_USER_DATA: 'delete_user_data', + DELETE_MESSAGES: 'delete_messages', + + // Массовые операции + BROADCAST: 'broadcast', + + // Управление тегами + MANAGE_TAGS: 'manage_tags', + + // Блокировка пользователей + BLOCK_USERS: 'block_users', + + // Управление настройками + MANAGE_SETTINGS: 'manage_settings', + + // Контент: юридические документы + VIEW_BASIC_DOCS: 'view_basic_docs', // Базовые документы для пользователей + VIEW_LEGAL_DOCS: 'view_legal_docs', // Юридические документы для читателей + MANAGE_LEGAL_DOCS: 'manage_legal_docs' // Управление документами для редакторов +}; + +// Матрица: какая роль имеет какие права +const PERMISSIONS_MAP = { + [ROLES.GUEST]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI + ], + + [ROLES.USER]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CONTACTS, // Пользователи могут видеть контакты для выбора + PERMISSIONS.SEND_TO_USERS, // Пользователи могут отправлять сообщения + PERMISSIONS.CHAT_WITH_ADMINS, // Авторизованные пользователи могут видеть личные сообщения + PERMISSIONS.VIEW_BASIC_DOCS // Базовые документы для пользователей + ], + + [ROLES.READONLY]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Чтение внутренних юридических документов + PERMISSIONS.VIEW_LEGAL_DOCS + ], + + [ROLES.EDITOR]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + PERMISSIONS.GENERATE_AI_REPLIES, + PERMISSIONS.EDIT_USER_DATA, + PERMISSIONS.EDIT_CONTACTS, + PERMISSIONS.DELETE_USER_DATA, + PERMISSIONS.DELETE_MESSAGES, + PERMISSIONS.BROADCAST, + PERMISSIONS.MANAGE_TAGS, + PERMISSIONS.BLOCK_USERS, + PERMISSIONS.MANAGE_SETTINGS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Полный доступ к юридическим документам + PERMISSIONS.VIEW_LEGAL_DOCS, + PERMISSIONS.MANAGE_LEGAL_DOCS + ] +}; + +/** + * Проверяет, имеет ли роль определенное право + * @param {string} role - Роль пользователя + * @param {string} permission - Требуемое право + * @returns {boolean} + */ +function hasPermission(role, permission) { + if (!role || !permission) return false; + return PERMISSIONS_MAP[role]?.includes(permission) || false; +} + +/** + * Получает все права для роли + * @param {string} role - Роль пользователя + * @returns {Array} + */ +function getPermissionsForRole(role) { + return PERMISSIONS_MAP[role] || []; +} + +/** + * Проверяет, имеет ли роль ХОТЯ БЫ ОДНО из прав + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAnyPermission(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.some(p => hasPermission(role, p)); +} + +/** + * Проверяет, имеет ли роль ВСЕ указанные права + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAllPermissions(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.every(p => hasPermission(role, p)); +} + +/** + * Получает описание роли + * @param {string} role - Роль пользователя + * @returns {string} + */ +function getRoleDescription(role) { + const descriptions = { + [ROLES.GUEST]: 'Неавторизованный гость', + [ROLES.USER]: 'Авторизованный гость', + [ROLES.READONLY]: 'Админ (только чтение)', + [ROLES.EDITOR]: 'Админ (редактор)' + }; + + return descriptions[role] || 'Неизвестная роль'; +} + +/** + * Проверить, может ли отправитель отправлять сообщения получателю + * @param {string} senderRole - Роль отправителя + * @param {string} recipientRole - Роль получателя + * @param {number} senderId - ID отправителя + * @param {number} recipientId - ID получателя + * @returns {Object} { canSend: boolean, errorMessage?: string } + */ +function canSendMessage(senderRole, recipientRole, senderId, recipientId) { + // Проверяем базовое право на отправку сообщений + if (!hasPermission(senderRole, PERMISSIONS.SEND_TO_USERS)) { + return { + canSend: false, + errorMessage: 'У вас нет права на отправку сообщений' + }; + } + + // Собственный чат - всегда разрешен (для ИИ ассистента) + if (senderId === recipientId) { + return { canSend: true }; + } + + // USER и READONLY могут писать только EDITOR + if ((senderRole === 'user' || senderRole === 'readonly') && recipientRole === 'editor') { + return { canSend: true }; + } + + // EDITOR может писать всем (USER, READONLY, EDITOR) + if (senderRole === 'editor') { + return { canSend: true }; + } + + // USER и READONLY НЕ могут писать друг другу + if ((senderRole === 'user' || senderRole === 'readonly') && + (recipientRole === 'user' || recipientRole === 'readonly')) { + return { + canSend: false, + errorMessage: 'Пользователи и читатели не могут отправлять сообщения друг другу' + }; + } + + // Остальные случаи запрещены + return { + canSend: false, + errorMessage: `Роль ${senderRole} не может отправлять сообщения роли ${recipientRole}` + }; +} + +// Экспорты для CommonJS (Node.js) +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + +// ES модули для Frontend +export { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage +}; + +// CommonJS для Backend +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + diff --git a/frontend/src/components/ContactTable.vue b/frontend/src/components/ContactTable.vue index 33a3fa6..3910f2d 100644 --- a/frontend/src/components/ContactTable.vue +++ b/frontend/src/components/ContactTable.vue @@ -129,7 +129,7 @@ import { useTagsWebSocket } from '../composables/useTagsWebSocket'; import { useContactsAndMessagesWebSocket } from '../composables/useContactsWebSocket'; import { usePermissions } from '@/composables/usePermissions'; import { useAuthContext } from '@/composables/useAuth'; -import { PERMISSIONS } from '/app/shared/permissions.js'; +import { PERMISSIONS } from './permissions.js'; import api from '../api/axios'; import { sendMessage, getPrivateUnreadCount } from '../services/messagesService'; import { useRoles } from '@/composables/useRoles'; diff --git a/frontend/src/components/permissions.js b/frontend/src/components/permissions.js new file mode 100644 index 0000000..087f590 --- /dev/null +++ b/frontend/src/components/permissions.js @@ -0,0 +1,279 @@ +/** + * 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 + */ + +/** + * Единая матрица прав доступа для DLE + * Используется и на backend, и на frontend + */ + +// Роли в системе +const ROLES = { + GUEST: 'guest', // Неавторизованный гость + USER: 'user', // Авторизованный гость (0 токенов) + READONLY: 'readonly', // Админ чтение (токены > 0 && < X) + EDITOR: 'editor' // Админ редактор (токены >= X) +}; + +// Список всех прав в системе +const PERMISSIONS = { + // Публичный доступ + VIEW_HOME: 'view_home', + CHAT_AI: 'chat_ai', + + // Получение сообщений + RECEIVE_MESSAGES: 'receive_messages', + + // Просмотр данных + VIEW_CRM: 'view_crm', + VIEW_CONTACTS: 'view_contacts', + VIEW_DATA: 'view_data', + + // Отправка сообщений + SEND_TO_USERS: 'send_to_users', + CHAT_WITH_ADMINS: 'chat_with_admins', + + // AI функции + GENERATE_AI_REPLIES: 'generate_ai_replies', + + // Редактирование + EDIT_USER_DATA: 'edit_user_data', + EDIT_CONTACTS: 'edit_contacts', + + // Удаление + DELETE_USER_DATA: 'delete_user_data', + DELETE_MESSAGES: 'delete_messages', + + // Массовые операции + BROADCAST: 'broadcast', + + // Управление тегами + MANAGE_TAGS: 'manage_tags', + + // Блокировка пользователей + BLOCK_USERS: 'block_users', + + // Управление настройками + MANAGE_SETTINGS: 'manage_settings', + + // Контент: юридические документы + VIEW_BASIC_DOCS: 'view_basic_docs', // Базовые документы для пользователей + VIEW_LEGAL_DOCS: 'view_legal_docs', // Юридические документы для читателей + MANAGE_LEGAL_DOCS: 'manage_legal_docs' // Управление документами для редакторов +}; + +// Матрица: какая роль имеет какие права +const PERMISSIONS_MAP = { + [ROLES.GUEST]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI + ], + + [ROLES.USER]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CONTACTS, // Пользователи могут видеть контакты для выбора + PERMISSIONS.SEND_TO_USERS, // Пользователи могут отправлять сообщения + PERMISSIONS.CHAT_WITH_ADMINS, // Авторизованные пользователи могут видеть личные сообщения + PERMISSIONS.VIEW_BASIC_DOCS // Базовые документы для пользователей + ], + + [ROLES.READONLY]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Чтение внутренних юридических документов + PERMISSIONS.VIEW_LEGAL_DOCS + ], + + [ROLES.EDITOR]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + PERMISSIONS.GENERATE_AI_REPLIES, + PERMISSIONS.EDIT_USER_DATA, + PERMISSIONS.EDIT_CONTACTS, + PERMISSIONS.DELETE_USER_DATA, + PERMISSIONS.DELETE_MESSAGES, + PERMISSIONS.BROADCAST, + PERMISSIONS.MANAGE_TAGS, + PERMISSIONS.BLOCK_USERS, + PERMISSIONS.MANAGE_SETTINGS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Полный доступ к юридическим документам + PERMISSIONS.VIEW_LEGAL_DOCS, + PERMISSIONS.MANAGE_LEGAL_DOCS + ] +}; + +/** + * Проверяет, имеет ли роль определенное право + * @param {string} role - Роль пользователя + * @param {string} permission - Требуемое право + * @returns {boolean} + */ +function hasPermission(role, permission) { + if (!role || !permission) return false; + return PERMISSIONS_MAP[role]?.includes(permission) || false; +} + +/** + * Получает все права для роли + * @param {string} role - Роль пользователя + * @returns {Array} + */ +function getPermissionsForRole(role) { + return PERMISSIONS_MAP[role] || []; +} + +/** + * Проверяет, имеет ли роль ХОТЯ БЫ ОДНО из прав + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAnyPermission(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.some(p => hasPermission(role, p)); +} + +/** + * Проверяет, имеет ли роль ВСЕ указанные права + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAllPermissions(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.every(p => hasPermission(role, p)); +} + +/** + * Получает описание роли + * @param {string} role - Роль пользователя + * @returns {string} + */ +function getRoleDescription(role) { + const descriptions = { + [ROLES.GUEST]: 'Неавторизованный гость', + [ROLES.USER]: 'Авторизованный гость', + [ROLES.READONLY]: 'Админ (только чтение)', + [ROLES.EDITOR]: 'Админ (редактор)' + }; + + return descriptions[role] || 'Неизвестная роль'; +} + +/** + * Проверить, может ли отправитель отправлять сообщения получателю + * @param {string} senderRole - Роль отправителя + * @param {string} recipientRole - Роль получателя + * @param {number} senderId - ID отправителя + * @param {number} recipientId - ID получателя + * @returns {Object} { canSend: boolean, errorMessage?: string } + */ +function canSendMessage(senderRole, recipientRole, senderId, recipientId) { + // Проверяем базовое право на отправку сообщений + if (!hasPermission(senderRole, PERMISSIONS.SEND_TO_USERS)) { + return { + canSend: false, + errorMessage: 'У вас нет права на отправку сообщений' + }; + } + + // Собственный чат - всегда разрешен (для ИИ ассистента) + if (senderId === recipientId) { + return { canSend: true }; + } + + // USER и READONLY могут писать только EDITOR + if ((senderRole === 'user' || senderRole === 'readonly') && recipientRole === 'editor') { + return { canSend: true }; + } + + // EDITOR может писать всем (USER, READONLY, EDITOR) + if (senderRole === 'editor') { + return { canSend: true }; + } + + // USER и READONLY НЕ могут писать друг другу + if ((senderRole === 'user' || senderRole === 'readonly') && + (recipientRole === 'user' || recipientRole === 'readonly')) { + return { + canSend: false, + errorMessage: 'Пользователи и читатели не могут отправлять сообщения друг другу' + }; + } + + // Остальные случаи запрещены + return { + canSend: false, + errorMessage: `Роль ${senderRole} не может отправлять сообщения роли ${recipientRole}` + }; +} + +// Экспорты для CommonJS (Node.js) +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + +// ES модули для Frontend +export { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage +}; + +// CommonJS для Backend +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + diff --git a/frontend/src/composables/permissions.js b/frontend/src/composables/permissions.js new file mode 100644 index 0000000..087f590 --- /dev/null +++ b/frontend/src/composables/permissions.js @@ -0,0 +1,279 @@ +/** + * 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 + */ + +/** + * Единая матрица прав доступа для DLE + * Используется и на backend, и на frontend + */ + +// Роли в системе +const ROLES = { + GUEST: 'guest', // Неавторизованный гость + USER: 'user', // Авторизованный гость (0 токенов) + READONLY: 'readonly', // Админ чтение (токены > 0 && < X) + EDITOR: 'editor' // Админ редактор (токены >= X) +}; + +// Список всех прав в системе +const PERMISSIONS = { + // Публичный доступ + VIEW_HOME: 'view_home', + CHAT_AI: 'chat_ai', + + // Получение сообщений + RECEIVE_MESSAGES: 'receive_messages', + + // Просмотр данных + VIEW_CRM: 'view_crm', + VIEW_CONTACTS: 'view_contacts', + VIEW_DATA: 'view_data', + + // Отправка сообщений + SEND_TO_USERS: 'send_to_users', + CHAT_WITH_ADMINS: 'chat_with_admins', + + // AI функции + GENERATE_AI_REPLIES: 'generate_ai_replies', + + // Редактирование + EDIT_USER_DATA: 'edit_user_data', + EDIT_CONTACTS: 'edit_contacts', + + // Удаление + DELETE_USER_DATA: 'delete_user_data', + DELETE_MESSAGES: 'delete_messages', + + // Массовые операции + BROADCAST: 'broadcast', + + // Управление тегами + MANAGE_TAGS: 'manage_tags', + + // Блокировка пользователей + BLOCK_USERS: 'block_users', + + // Управление настройками + MANAGE_SETTINGS: 'manage_settings', + + // Контент: юридические документы + VIEW_BASIC_DOCS: 'view_basic_docs', // Базовые документы для пользователей + VIEW_LEGAL_DOCS: 'view_legal_docs', // Юридические документы для читателей + MANAGE_LEGAL_DOCS: 'manage_legal_docs' // Управление документами для редакторов +}; + +// Матрица: какая роль имеет какие права +const PERMISSIONS_MAP = { + [ROLES.GUEST]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI + ], + + [ROLES.USER]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CONTACTS, // Пользователи могут видеть контакты для выбора + PERMISSIONS.SEND_TO_USERS, // Пользователи могут отправлять сообщения + PERMISSIONS.CHAT_WITH_ADMINS, // Авторизованные пользователи могут видеть личные сообщения + PERMISSIONS.VIEW_BASIC_DOCS // Базовые документы для пользователей + ], + + [ROLES.READONLY]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Чтение внутренних юридических документов + PERMISSIONS.VIEW_LEGAL_DOCS + ], + + [ROLES.EDITOR]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + PERMISSIONS.GENERATE_AI_REPLIES, + PERMISSIONS.EDIT_USER_DATA, + PERMISSIONS.EDIT_CONTACTS, + PERMISSIONS.DELETE_USER_DATA, + PERMISSIONS.DELETE_MESSAGES, + PERMISSIONS.BROADCAST, + PERMISSIONS.MANAGE_TAGS, + PERMISSIONS.BLOCK_USERS, + PERMISSIONS.MANAGE_SETTINGS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Полный доступ к юридическим документам + PERMISSIONS.VIEW_LEGAL_DOCS, + PERMISSIONS.MANAGE_LEGAL_DOCS + ] +}; + +/** + * Проверяет, имеет ли роль определенное право + * @param {string} role - Роль пользователя + * @param {string} permission - Требуемое право + * @returns {boolean} + */ +function hasPermission(role, permission) { + if (!role || !permission) return false; + return PERMISSIONS_MAP[role]?.includes(permission) || false; +} + +/** + * Получает все права для роли + * @param {string} role - Роль пользователя + * @returns {Array} + */ +function getPermissionsForRole(role) { + return PERMISSIONS_MAP[role] || []; +} + +/** + * Проверяет, имеет ли роль ХОТЯ БЫ ОДНО из прав + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAnyPermission(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.some(p => hasPermission(role, p)); +} + +/** + * Проверяет, имеет ли роль ВСЕ указанные права + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAllPermissions(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.every(p => hasPermission(role, p)); +} + +/** + * Получает описание роли + * @param {string} role - Роль пользователя + * @returns {string} + */ +function getRoleDescription(role) { + const descriptions = { + [ROLES.GUEST]: 'Неавторизованный гость', + [ROLES.USER]: 'Авторизованный гость', + [ROLES.READONLY]: 'Админ (только чтение)', + [ROLES.EDITOR]: 'Админ (редактор)' + }; + + return descriptions[role] || 'Неизвестная роль'; +} + +/** + * Проверить, может ли отправитель отправлять сообщения получателю + * @param {string} senderRole - Роль отправителя + * @param {string} recipientRole - Роль получателя + * @param {number} senderId - ID отправителя + * @param {number} recipientId - ID получателя + * @returns {Object} { canSend: boolean, errorMessage?: string } + */ +function canSendMessage(senderRole, recipientRole, senderId, recipientId) { + // Проверяем базовое право на отправку сообщений + if (!hasPermission(senderRole, PERMISSIONS.SEND_TO_USERS)) { + return { + canSend: false, + errorMessage: 'У вас нет права на отправку сообщений' + }; + } + + // Собственный чат - всегда разрешен (для ИИ ассистента) + if (senderId === recipientId) { + return { canSend: true }; + } + + // USER и READONLY могут писать только EDITOR + if ((senderRole === 'user' || senderRole === 'readonly') && recipientRole === 'editor') { + return { canSend: true }; + } + + // EDITOR может писать всем (USER, READONLY, EDITOR) + if (senderRole === 'editor') { + return { canSend: true }; + } + + // USER и READONLY НЕ могут писать друг другу + if ((senderRole === 'user' || senderRole === 'readonly') && + (recipientRole === 'user' || recipientRole === 'readonly')) { + return { + canSend: false, + errorMessage: 'Пользователи и читатели не могут отправлять сообщения друг другу' + }; + } + + // Остальные случаи запрещены + return { + canSend: false, + errorMessage: `Роль ${senderRole} не может отправлять сообщения роли ${recipientRole}` + }; +} + +// Экспорты для CommonJS (Node.js) +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + +// ES модули для Frontend +export { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage +}; + +// CommonJS для Backend +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + diff --git a/frontend/src/composables/usePermissions.js b/frontend/src/composables/usePermissions.js index 49409d2..390d4f7 100644 --- a/frontend/src/composables/usePermissions.js +++ b/frontend/src/composables/usePermissions.js @@ -12,7 +12,7 @@ import { computed } from 'vue'; import { useAuthContext } from './useAuth'; -import { PERMISSIONS, ROLES, hasPermission as checkPermission, getRoleDescription } from '/app/shared/permissions'; +import { PERMISSIONS, ROLES, hasPermission as checkPermission, getRoleDescription } from './permissions.js'; /** * Composable для работы с правами доступа diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index ac0350c..ef12b7f 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -19,7 +19,7 @@ const SettingsInterfaceView = () => import('../views/settings/Interface/Interfac import axios from 'axios'; import { setToStorage } from '../utils/storage.js'; -import { PERMISSIONS, hasPermission } from '/app/shared/permissions.js'; +import { PERMISSIONS, hasPermission } from './permissions.js'; // console.log('router/index.js: Script loaded'); diff --git a/frontend/src/router/permissions.js b/frontend/src/router/permissions.js new file mode 100644 index 0000000..087f590 --- /dev/null +++ b/frontend/src/router/permissions.js @@ -0,0 +1,279 @@ +/** + * 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 + */ + +/** + * Единая матрица прав доступа для DLE + * Используется и на backend, и на frontend + */ + +// Роли в системе +const ROLES = { + GUEST: 'guest', // Неавторизованный гость + USER: 'user', // Авторизованный гость (0 токенов) + READONLY: 'readonly', // Админ чтение (токены > 0 && < X) + EDITOR: 'editor' // Админ редактор (токены >= X) +}; + +// Список всех прав в системе +const PERMISSIONS = { + // Публичный доступ + VIEW_HOME: 'view_home', + CHAT_AI: 'chat_ai', + + // Получение сообщений + RECEIVE_MESSAGES: 'receive_messages', + + // Просмотр данных + VIEW_CRM: 'view_crm', + VIEW_CONTACTS: 'view_contacts', + VIEW_DATA: 'view_data', + + // Отправка сообщений + SEND_TO_USERS: 'send_to_users', + CHAT_WITH_ADMINS: 'chat_with_admins', + + // AI функции + GENERATE_AI_REPLIES: 'generate_ai_replies', + + // Редактирование + EDIT_USER_DATA: 'edit_user_data', + EDIT_CONTACTS: 'edit_contacts', + + // Удаление + DELETE_USER_DATA: 'delete_user_data', + DELETE_MESSAGES: 'delete_messages', + + // Массовые операции + BROADCAST: 'broadcast', + + // Управление тегами + MANAGE_TAGS: 'manage_tags', + + // Блокировка пользователей + BLOCK_USERS: 'block_users', + + // Управление настройками + MANAGE_SETTINGS: 'manage_settings', + + // Контент: юридические документы + VIEW_BASIC_DOCS: 'view_basic_docs', // Базовые документы для пользователей + VIEW_LEGAL_DOCS: 'view_legal_docs', // Юридические документы для читателей + MANAGE_LEGAL_DOCS: 'manage_legal_docs' // Управление документами для редакторов +}; + +// Матрица: какая роль имеет какие права +const PERMISSIONS_MAP = { + [ROLES.GUEST]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI + ], + + [ROLES.USER]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CONTACTS, // Пользователи могут видеть контакты для выбора + PERMISSIONS.SEND_TO_USERS, // Пользователи могут отправлять сообщения + PERMISSIONS.CHAT_WITH_ADMINS, // Авторизованные пользователи могут видеть личные сообщения + PERMISSIONS.VIEW_BASIC_DOCS // Базовые документы для пользователей + ], + + [ROLES.READONLY]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Чтение внутренних юридических документов + PERMISSIONS.VIEW_LEGAL_DOCS + ], + + [ROLES.EDITOR]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + PERMISSIONS.GENERATE_AI_REPLIES, + PERMISSIONS.EDIT_USER_DATA, + PERMISSIONS.EDIT_CONTACTS, + PERMISSIONS.DELETE_USER_DATA, + PERMISSIONS.DELETE_MESSAGES, + PERMISSIONS.BROADCAST, + PERMISSIONS.MANAGE_TAGS, + PERMISSIONS.BLOCK_USERS, + PERMISSIONS.MANAGE_SETTINGS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Полный доступ к юридическим документам + PERMISSIONS.VIEW_LEGAL_DOCS, + PERMISSIONS.MANAGE_LEGAL_DOCS + ] +}; + +/** + * Проверяет, имеет ли роль определенное право + * @param {string} role - Роль пользователя + * @param {string} permission - Требуемое право + * @returns {boolean} + */ +function hasPermission(role, permission) { + if (!role || !permission) return false; + return PERMISSIONS_MAP[role]?.includes(permission) || false; +} + +/** + * Получает все права для роли + * @param {string} role - Роль пользователя + * @returns {Array} + */ +function getPermissionsForRole(role) { + return PERMISSIONS_MAP[role] || []; +} + +/** + * Проверяет, имеет ли роль ХОТЯ БЫ ОДНО из прав + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAnyPermission(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.some(p => hasPermission(role, p)); +} + +/** + * Проверяет, имеет ли роль ВСЕ указанные права + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAllPermissions(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.every(p => hasPermission(role, p)); +} + +/** + * Получает описание роли + * @param {string} role - Роль пользователя + * @returns {string} + */ +function getRoleDescription(role) { + const descriptions = { + [ROLES.GUEST]: 'Неавторизованный гость', + [ROLES.USER]: 'Авторизованный гость', + [ROLES.READONLY]: 'Админ (только чтение)', + [ROLES.EDITOR]: 'Админ (редактор)' + }; + + return descriptions[role] || 'Неизвестная роль'; +} + +/** + * Проверить, может ли отправитель отправлять сообщения получателю + * @param {string} senderRole - Роль отправителя + * @param {string} recipientRole - Роль получателя + * @param {number} senderId - ID отправителя + * @param {number} recipientId - ID получателя + * @returns {Object} { canSend: boolean, errorMessage?: string } + */ +function canSendMessage(senderRole, recipientRole, senderId, recipientId) { + // Проверяем базовое право на отправку сообщений + if (!hasPermission(senderRole, PERMISSIONS.SEND_TO_USERS)) { + return { + canSend: false, + errorMessage: 'У вас нет права на отправку сообщений' + }; + } + + // Собственный чат - всегда разрешен (для ИИ ассистента) + if (senderId === recipientId) { + return { canSend: true }; + } + + // USER и READONLY могут писать только EDITOR + if ((senderRole === 'user' || senderRole === 'readonly') && recipientRole === 'editor') { + return { canSend: true }; + } + + // EDITOR может писать всем (USER, READONLY, EDITOR) + if (senderRole === 'editor') { + return { canSend: true }; + } + + // USER и READONLY НЕ могут писать друг другу + if ((senderRole === 'user' || senderRole === 'readonly') && + (recipientRole === 'user' || recipientRole === 'readonly')) { + return { + canSend: false, + errorMessage: 'Пользователи и читатели не могут отправлять сообщения друг другу' + }; + } + + // Остальные случаи запрещены + return { + canSend: false, + errorMessage: `Роль ${senderRole} не может отправлять сообщения роли ${recipientRole}` + }; +} + +// Экспорты для CommonJS (Node.js) +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + +// ES модули для Frontend +export { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage +}; + +// CommonJS для Backend +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + diff --git a/frontend/src/shared/permissions.js b/frontend/src/shared/permissions.js new file mode 100644 index 0000000..087f590 --- /dev/null +++ b/frontend/src/shared/permissions.js @@ -0,0 +1,279 @@ +/** + * 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 + */ + +/** + * Единая матрица прав доступа для DLE + * Используется и на backend, и на frontend + */ + +// Роли в системе +const ROLES = { + GUEST: 'guest', // Неавторизованный гость + USER: 'user', // Авторизованный гость (0 токенов) + READONLY: 'readonly', // Админ чтение (токены > 0 && < X) + EDITOR: 'editor' // Админ редактор (токены >= X) +}; + +// Список всех прав в системе +const PERMISSIONS = { + // Публичный доступ + VIEW_HOME: 'view_home', + CHAT_AI: 'chat_ai', + + // Получение сообщений + RECEIVE_MESSAGES: 'receive_messages', + + // Просмотр данных + VIEW_CRM: 'view_crm', + VIEW_CONTACTS: 'view_contacts', + VIEW_DATA: 'view_data', + + // Отправка сообщений + SEND_TO_USERS: 'send_to_users', + CHAT_WITH_ADMINS: 'chat_with_admins', + + // AI функции + GENERATE_AI_REPLIES: 'generate_ai_replies', + + // Редактирование + EDIT_USER_DATA: 'edit_user_data', + EDIT_CONTACTS: 'edit_contacts', + + // Удаление + DELETE_USER_DATA: 'delete_user_data', + DELETE_MESSAGES: 'delete_messages', + + // Массовые операции + BROADCAST: 'broadcast', + + // Управление тегами + MANAGE_TAGS: 'manage_tags', + + // Блокировка пользователей + BLOCK_USERS: 'block_users', + + // Управление настройками + MANAGE_SETTINGS: 'manage_settings', + + // Контент: юридические документы + VIEW_BASIC_DOCS: 'view_basic_docs', // Базовые документы для пользователей + VIEW_LEGAL_DOCS: 'view_legal_docs', // Юридические документы для читателей + MANAGE_LEGAL_DOCS: 'manage_legal_docs' // Управление документами для редакторов +}; + +// Матрица: какая роль имеет какие права +const PERMISSIONS_MAP = { + [ROLES.GUEST]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI + ], + + [ROLES.USER]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CONTACTS, // Пользователи могут видеть контакты для выбора + PERMISSIONS.SEND_TO_USERS, // Пользователи могут отправлять сообщения + PERMISSIONS.CHAT_WITH_ADMINS, // Авторизованные пользователи могут видеть личные сообщения + PERMISSIONS.VIEW_BASIC_DOCS // Базовые документы для пользователей + ], + + [ROLES.READONLY]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Чтение внутренних юридических документов + PERMISSIONS.VIEW_LEGAL_DOCS + ], + + [ROLES.EDITOR]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + PERMISSIONS.GENERATE_AI_REPLIES, + PERMISSIONS.EDIT_USER_DATA, + PERMISSIONS.EDIT_CONTACTS, + PERMISSIONS.DELETE_USER_DATA, + PERMISSIONS.DELETE_MESSAGES, + PERMISSIONS.BROADCAST, + PERMISSIONS.MANAGE_TAGS, + PERMISSIONS.BLOCK_USERS, + PERMISSIONS.MANAGE_SETTINGS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Полный доступ к юридическим документам + PERMISSIONS.VIEW_LEGAL_DOCS, + PERMISSIONS.MANAGE_LEGAL_DOCS + ] +}; + +/** + * Проверяет, имеет ли роль определенное право + * @param {string} role - Роль пользователя + * @param {string} permission - Требуемое право + * @returns {boolean} + */ +function hasPermission(role, permission) { + if (!role || !permission) return false; + return PERMISSIONS_MAP[role]?.includes(permission) || false; +} + +/** + * Получает все права для роли + * @param {string} role - Роль пользователя + * @returns {Array} + */ +function getPermissionsForRole(role) { + return PERMISSIONS_MAP[role] || []; +} + +/** + * Проверяет, имеет ли роль ХОТЯ БЫ ОДНО из прав + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAnyPermission(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.some(p => hasPermission(role, p)); +} + +/** + * Проверяет, имеет ли роль ВСЕ указанные права + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAllPermissions(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.every(p => hasPermission(role, p)); +} + +/** + * Получает описание роли + * @param {string} role - Роль пользователя + * @returns {string} + */ +function getRoleDescription(role) { + const descriptions = { + [ROLES.GUEST]: 'Неавторизованный гость', + [ROLES.USER]: 'Авторизованный гость', + [ROLES.READONLY]: 'Админ (только чтение)', + [ROLES.EDITOR]: 'Админ (редактор)' + }; + + return descriptions[role] || 'Неизвестная роль'; +} + +/** + * Проверить, может ли отправитель отправлять сообщения получателю + * @param {string} senderRole - Роль отправителя + * @param {string} recipientRole - Роль получателя + * @param {number} senderId - ID отправителя + * @param {number} recipientId - ID получателя + * @returns {Object} { canSend: boolean, errorMessage?: string } + */ +function canSendMessage(senderRole, recipientRole, senderId, recipientId) { + // Проверяем базовое право на отправку сообщений + if (!hasPermission(senderRole, PERMISSIONS.SEND_TO_USERS)) { + return { + canSend: false, + errorMessage: 'У вас нет права на отправку сообщений' + }; + } + + // Собственный чат - всегда разрешен (для ИИ ассистента) + if (senderId === recipientId) { + return { canSend: true }; + } + + // USER и READONLY могут писать только EDITOR + if ((senderRole === 'user' || senderRole === 'readonly') && recipientRole === 'editor') { + return { canSend: true }; + } + + // EDITOR может писать всем (USER, READONLY, EDITOR) + if (senderRole === 'editor') { + return { canSend: true }; + } + + // USER и READONLY НЕ могут писать друг другу + if ((senderRole === 'user' || senderRole === 'readonly') && + (recipientRole === 'user' || recipientRole === 'readonly')) { + return { + canSend: false, + errorMessage: 'Пользователи и читатели не могут отправлять сообщения друг другу' + }; + } + + // Остальные случаи запрещены + return { + canSend: false, + errorMessage: `Роль ${senderRole} не может отправлять сообщения роли ${recipientRole}` + }; +} + +// Экспорты для CommonJS (Node.js) +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + +// ES модули для Frontend +export { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage +}; + +// CommonJS для Backend +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + diff --git a/frontend/src/views/ContentPageView.vue b/frontend/src/views/ContentPageView.vue index 12b036f..41f672b 100644 --- a/frontend/src/views/ContentPageView.vue +++ b/frontend/src/views/ContentPageView.vue @@ -178,7 +178,7 @@ import { ref, computed, onMounted } from 'vue'; import { useRouter, useRoute } from 'vue-router'; import BaseLayout from '../components/BaseLayout.vue'; import pagesService from '../services/pagesService'; -import { PERMISSIONS } from '/app/shared/permissions.js'; +import { PERMISSIONS } from './permissions.js'; import { useAuthContext } from '../composables/useAuth'; import { usePermissions } from '../composables/usePermissions'; diff --git a/frontend/src/views/contacts/ContactDetailsView.vue b/frontend/src/views/contacts/ContactDetailsView.vue index c1b0a61..725929f 100644 --- a/frontend/src/views/contacts/ContactDetailsView.vue +++ b/frontend/src/views/contacts/ContactDetailsView.vue @@ -167,7 +167,7 @@ import messagesService from '../../services/messagesService.js'; import { getPublicMessages, getConversationByUserId, sendMessage, getPersonalChatHistory } from '../../services/messagesService.js'; import { useAuthContext } from '@/composables/useAuth'; import { usePermissions } from '@/composables/usePermissions'; -import { PERMISSIONS } from '/app/shared/permissions.js'; +import { PERMISSIONS } from './permissions.js'; import { useContactsAndMessagesWebSocket } from '@/composables/useContactsWebSocket'; const { canEditContacts, canDeleteData, canManageTags, canBlockUsers, canSendToUsers, canGenerateAI, canViewContacts, hasPermission } = usePermissions(); const { address, userId: currentUserId } = useAuthContext(); diff --git a/frontend/src/views/contacts/permissions.js b/frontend/src/views/contacts/permissions.js new file mode 100644 index 0000000..087f590 --- /dev/null +++ b/frontend/src/views/contacts/permissions.js @@ -0,0 +1,279 @@ +/** + * 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 + */ + +/** + * Единая матрица прав доступа для DLE + * Используется и на backend, и на frontend + */ + +// Роли в системе +const ROLES = { + GUEST: 'guest', // Неавторизованный гость + USER: 'user', // Авторизованный гость (0 токенов) + READONLY: 'readonly', // Админ чтение (токены > 0 && < X) + EDITOR: 'editor' // Админ редактор (токены >= X) +}; + +// Список всех прав в системе +const PERMISSIONS = { + // Публичный доступ + VIEW_HOME: 'view_home', + CHAT_AI: 'chat_ai', + + // Получение сообщений + RECEIVE_MESSAGES: 'receive_messages', + + // Просмотр данных + VIEW_CRM: 'view_crm', + VIEW_CONTACTS: 'view_contacts', + VIEW_DATA: 'view_data', + + // Отправка сообщений + SEND_TO_USERS: 'send_to_users', + CHAT_WITH_ADMINS: 'chat_with_admins', + + // AI функции + GENERATE_AI_REPLIES: 'generate_ai_replies', + + // Редактирование + EDIT_USER_DATA: 'edit_user_data', + EDIT_CONTACTS: 'edit_contacts', + + // Удаление + DELETE_USER_DATA: 'delete_user_data', + DELETE_MESSAGES: 'delete_messages', + + // Массовые операции + BROADCAST: 'broadcast', + + // Управление тегами + MANAGE_TAGS: 'manage_tags', + + // Блокировка пользователей + BLOCK_USERS: 'block_users', + + // Управление настройками + MANAGE_SETTINGS: 'manage_settings', + + // Контент: юридические документы + VIEW_BASIC_DOCS: 'view_basic_docs', // Базовые документы для пользователей + VIEW_LEGAL_DOCS: 'view_legal_docs', // Юридические документы для читателей + MANAGE_LEGAL_DOCS: 'manage_legal_docs' // Управление документами для редакторов +}; + +// Матрица: какая роль имеет какие права +const PERMISSIONS_MAP = { + [ROLES.GUEST]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI + ], + + [ROLES.USER]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CONTACTS, // Пользователи могут видеть контакты для выбора + PERMISSIONS.SEND_TO_USERS, // Пользователи могут отправлять сообщения + PERMISSIONS.CHAT_WITH_ADMINS, // Авторизованные пользователи могут видеть личные сообщения + PERMISSIONS.VIEW_BASIC_DOCS // Базовые документы для пользователей + ], + + [ROLES.READONLY]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Чтение внутренних юридических документов + PERMISSIONS.VIEW_LEGAL_DOCS + ], + + [ROLES.EDITOR]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + PERMISSIONS.GENERATE_AI_REPLIES, + PERMISSIONS.EDIT_USER_DATA, + PERMISSIONS.EDIT_CONTACTS, + PERMISSIONS.DELETE_USER_DATA, + PERMISSIONS.DELETE_MESSAGES, + PERMISSIONS.BROADCAST, + PERMISSIONS.MANAGE_TAGS, + PERMISSIONS.BLOCK_USERS, + PERMISSIONS.MANAGE_SETTINGS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Полный доступ к юридическим документам + PERMISSIONS.VIEW_LEGAL_DOCS, + PERMISSIONS.MANAGE_LEGAL_DOCS + ] +}; + +/** + * Проверяет, имеет ли роль определенное право + * @param {string} role - Роль пользователя + * @param {string} permission - Требуемое право + * @returns {boolean} + */ +function hasPermission(role, permission) { + if (!role || !permission) return false; + return PERMISSIONS_MAP[role]?.includes(permission) || false; +} + +/** + * Получает все права для роли + * @param {string} role - Роль пользователя + * @returns {Array} + */ +function getPermissionsForRole(role) { + return PERMISSIONS_MAP[role] || []; +} + +/** + * Проверяет, имеет ли роль ХОТЯ БЫ ОДНО из прав + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAnyPermission(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.some(p => hasPermission(role, p)); +} + +/** + * Проверяет, имеет ли роль ВСЕ указанные права + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAllPermissions(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.every(p => hasPermission(role, p)); +} + +/** + * Получает описание роли + * @param {string} role - Роль пользователя + * @returns {string} + */ +function getRoleDescription(role) { + const descriptions = { + [ROLES.GUEST]: 'Неавторизованный гость', + [ROLES.USER]: 'Авторизованный гость', + [ROLES.READONLY]: 'Админ (только чтение)', + [ROLES.EDITOR]: 'Админ (редактор)' + }; + + return descriptions[role] || 'Неизвестная роль'; +} + +/** + * Проверить, может ли отправитель отправлять сообщения получателю + * @param {string} senderRole - Роль отправителя + * @param {string} recipientRole - Роль получателя + * @param {number} senderId - ID отправителя + * @param {number} recipientId - ID получателя + * @returns {Object} { canSend: boolean, errorMessage?: string } + */ +function canSendMessage(senderRole, recipientRole, senderId, recipientId) { + // Проверяем базовое право на отправку сообщений + if (!hasPermission(senderRole, PERMISSIONS.SEND_TO_USERS)) { + return { + canSend: false, + errorMessage: 'У вас нет права на отправку сообщений' + }; + } + + // Собственный чат - всегда разрешен (для ИИ ассистента) + if (senderId === recipientId) { + return { canSend: true }; + } + + // USER и READONLY могут писать только EDITOR + if ((senderRole === 'user' || senderRole === 'readonly') && recipientRole === 'editor') { + return { canSend: true }; + } + + // EDITOR может писать всем (USER, READONLY, EDITOR) + if (senderRole === 'editor') { + return { canSend: true }; + } + + // USER и READONLY НЕ могут писать друг другу + if ((senderRole === 'user' || senderRole === 'readonly') && + (recipientRole === 'user' || recipientRole === 'readonly')) { + return { + canSend: false, + errorMessage: 'Пользователи и читатели не могут отправлять сообщения друг другу' + }; + } + + // Остальные случаи запрещены + return { + canSend: false, + errorMessage: `Роль ${senderRole} не может отправлять сообщения роли ${recipientRole}` + }; +} + +// Экспорты для CommonJS (Node.js) +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + +// ES модули для Frontend +export { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage +}; + +// CommonJS для Backend +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + diff --git a/frontend/src/views/content/InternalListView.vue b/frontend/src/views/content/InternalListView.vue index 1d72a5e..f5cff4c 100644 --- a/frontend/src/views/content/InternalListView.vue +++ b/frontend/src/views/content/InternalListView.vue @@ -60,7 +60,7 @@ import BaseLayout from '../../components/BaseLayout.vue'; import pagesService from '../../services/pagesService'; import { useAuthContext } from '../../composables/useAuth'; import { usePermissions } from '../../composables/usePermissions'; -import { PERMISSIONS } from '/app/shared/permissions.js'; +import { PERMISSIONS } from './permissions.js'; const props = defineProps({ isAuthenticated: { type: Boolean, default: false }, diff --git a/frontend/src/views/content/PublishedListView.vue b/frontend/src/views/content/PublishedListView.vue index 1870b36..bc96606 100644 --- a/frontend/src/views/content/PublishedListView.vue +++ b/frontend/src/views/content/PublishedListView.vue @@ -55,7 +55,7 @@ import pagesService from '../../services/pagesService'; import api from '../../api/axios'; import { useAuthContext } from '../../composables/useAuth'; import { usePermissions } from '../../composables/usePermissions'; -import { PERMISSIONS as SHARED_PERMISSIONS } from '/app/shared/permissions.js'; +import { PERMISSIONS as SHARED_PERMISSIONS } from './permissions.js'; const props = defineProps({ isAuthenticated: { type: Boolean, default: false }, diff --git a/frontend/src/views/content/permissions.js b/frontend/src/views/content/permissions.js new file mode 100644 index 0000000..087f590 --- /dev/null +++ b/frontend/src/views/content/permissions.js @@ -0,0 +1,279 @@ +/** + * 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 + */ + +/** + * Единая матрица прав доступа для DLE + * Используется и на backend, и на frontend + */ + +// Роли в системе +const ROLES = { + GUEST: 'guest', // Неавторизованный гость + USER: 'user', // Авторизованный гость (0 токенов) + READONLY: 'readonly', // Админ чтение (токены > 0 && < X) + EDITOR: 'editor' // Админ редактор (токены >= X) +}; + +// Список всех прав в системе +const PERMISSIONS = { + // Публичный доступ + VIEW_HOME: 'view_home', + CHAT_AI: 'chat_ai', + + // Получение сообщений + RECEIVE_MESSAGES: 'receive_messages', + + // Просмотр данных + VIEW_CRM: 'view_crm', + VIEW_CONTACTS: 'view_contacts', + VIEW_DATA: 'view_data', + + // Отправка сообщений + SEND_TO_USERS: 'send_to_users', + CHAT_WITH_ADMINS: 'chat_with_admins', + + // AI функции + GENERATE_AI_REPLIES: 'generate_ai_replies', + + // Редактирование + EDIT_USER_DATA: 'edit_user_data', + EDIT_CONTACTS: 'edit_contacts', + + // Удаление + DELETE_USER_DATA: 'delete_user_data', + DELETE_MESSAGES: 'delete_messages', + + // Массовые операции + BROADCAST: 'broadcast', + + // Управление тегами + MANAGE_TAGS: 'manage_tags', + + // Блокировка пользователей + BLOCK_USERS: 'block_users', + + // Управление настройками + MANAGE_SETTINGS: 'manage_settings', + + // Контент: юридические документы + VIEW_BASIC_DOCS: 'view_basic_docs', // Базовые документы для пользователей + VIEW_LEGAL_DOCS: 'view_legal_docs', // Юридические документы для читателей + MANAGE_LEGAL_DOCS: 'manage_legal_docs' // Управление документами для редакторов +}; + +// Матрица: какая роль имеет какие права +const PERMISSIONS_MAP = { + [ROLES.GUEST]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI + ], + + [ROLES.USER]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CONTACTS, // Пользователи могут видеть контакты для выбора + PERMISSIONS.SEND_TO_USERS, // Пользователи могут отправлять сообщения + PERMISSIONS.CHAT_WITH_ADMINS, // Авторизованные пользователи могут видеть личные сообщения + PERMISSIONS.VIEW_BASIC_DOCS // Базовые документы для пользователей + ], + + [ROLES.READONLY]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Чтение внутренних юридических документов + PERMISSIONS.VIEW_LEGAL_DOCS + ], + + [ROLES.EDITOR]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + PERMISSIONS.GENERATE_AI_REPLIES, + PERMISSIONS.EDIT_USER_DATA, + PERMISSIONS.EDIT_CONTACTS, + PERMISSIONS.DELETE_USER_DATA, + PERMISSIONS.DELETE_MESSAGES, + PERMISSIONS.BROADCAST, + PERMISSIONS.MANAGE_TAGS, + PERMISSIONS.BLOCK_USERS, + PERMISSIONS.MANAGE_SETTINGS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Полный доступ к юридическим документам + PERMISSIONS.VIEW_LEGAL_DOCS, + PERMISSIONS.MANAGE_LEGAL_DOCS + ] +}; + +/** + * Проверяет, имеет ли роль определенное право + * @param {string} role - Роль пользователя + * @param {string} permission - Требуемое право + * @returns {boolean} + */ +function hasPermission(role, permission) { + if (!role || !permission) return false; + return PERMISSIONS_MAP[role]?.includes(permission) || false; +} + +/** + * Получает все права для роли + * @param {string} role - Роль пользователя + * @returns {Array} + */ +function getPermissionsForRole(role) { + return PERMISSIONS_MAP[role] || []; +} + +/** + * Проверяет, имеет ли роль ХОТЯ БЫ ОДНО из прав + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAnyPermission(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.some(p => hasPermission(role, p)); +} + +/** + * Проверяет, имеет ли роль ВСЕ указанные права + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAllPermissions(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.every(p => hasPermission(role, p)); +} + +/** + * Получает описание роли + * @param {string} role - Роль пользователя + * @returns {string} + */ +function getRoleDescription(role) { + const descriptions = { + [ROLES.GUEST]: 'Неавторизованный гость', + [ROLES.USER]: 'Авторизованный гость', + [ROLES.READONLY]: 'Админ (только чтение)', + [ROLES.EDITOR]: 'Админ (редактор)' + }; + + return descriptions[role] || 'Неизвестная роль'; +} + +/** + * Проверить, может ли отправитель отправлять сообщения получателю + * @param {string} senderRole - Роль отправителя + * @param {string} recipientRole - Роль получателя + * @param {number} senderId - ID отправителя + * @param {number} recipientId - ID получателя + * @returns {Object} { canSend: boolean, errorMessage?: string } + */ +function canSendMessage(senderRole, recipientRole, senderId, recipientId) { + // Проверяем базовое право на отправку сообщений + if (!hasPermission(senderRole, PERMISSIONS.SEND_TO_USERS)) { + return { + canSend: false, + errorMessage: 'У вас нет права на отправку сообщений' + }; + } + + // Собственный чат - всегда разрешен (для ИИ ассистента) + if (senderId === recipientId) { + return { canSend: true }; + } + + // USER и READONLY могут писать только EDITOR + if ((senderRole === 'user' || senderRole === 'readonly') && recipientRole === 'editor') { + return { canSend: true }; + } + + // EDITOR может писать всем (USER, READONLY, EDITOR) + if (senderRole === 'editor') { + return { canSend: true }; + } + + // USER и READONLY НЕ могут писать друг другу + if ((senderRole === 'user' || senderRole === 'readonly') && + (recipientRole === 'user' || recipientRole === 'readonly')) { + return { + canSend: false, + errorMessage: 'Пользователи и читатели не могут отправлять сообщения друг другу' + }; + } + + // Остальные случаи запрещены + return { + canSend: false, + errorMessage: `Роль ${senderRole} не может отправлять сообщения роли ${recipientRole}` + }; +} + +// Экспорты для CommonJS (Node.js) +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + +// ES модули для Frontend +export { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage +}; + +// CommonJS для Backend +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + diff --git a/frontend/src/views/permissions.js b/frontend/src/views/permissions.js new file mode 100644 index 0000000..087f590 --- /dev/null +++ b/frontend/src/views/permissions.js @@ -0,0 +1,279 @@ +/** + * 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 + */ + +/** + * Единая матрица прав доступа для DLE + * Используется и на backend, и на frontend + */ + +// Роли в системе +const ROLES = { + GUEST: 'guest', // Неавторизованный гость + USER: 'user', // Авторизованный гость (0 токенов) + READONLY: 'readonly', // Админ чтение (токены > 0 && < X) + EDITOR: 'editor' // Админ редактор (токены >= X) +}; + +// Список всех прав в системе +const PERMISSIONS = { + // Публичный доступ + VIEW_HOME: 'view_home', + CHAT_AI: 'chat_ai', + + // Получение сообщений + RECEIVE_MESSAGES: 'receive_messages', + + // Просмотр данных + VIEW_CRM: 'view_crm', + VIEW_CONTACTS: 'view_contacts', + VIEW_DATA: 'view_data', + + // Отправка сообщений + SEND_TO_USERS: 'send_to_users', + CHAT_WITH_ADMINS: 'chat_with_admins', + + // AI функции + GENERATE_AI_REPLIES: 'generate_ai_replies', + + // Редактирование + EDIT_USER_DATA: 'edit_user_data', + EDIT_CONTACTS: 'edit_contacts', + + // Удаление + DELETE_USER_DATA: 'delete_user_data', + DELETE_MESSAGES: 'delete_messages', + + // Массовые операции + BROADCAST: 'broadcast', + + // Управление тегами + MANAGE_TAGS: 'manage_tags', + + // Блокировка пользователей + BLOCK_USERS: 'block_users', + + // Управление настройками + MANAGE_SETTINGS: 'manage_settings', + + // Контент: юридические документы + VIEW_BASIC_DOCS: 'view_basic_docs', // Базовые документы для пользователей + VIEW_LEGAL_DOCS: 'view_legal_docs', // Юридические документы для читателей + MANAGE_LEGAL_DOCS: 'manage_legal_docs' // Управление документами для редакторов +}; + +// Матрица: какая роль имеет какие права +const PERMISSIONS_MAP = { + [ROLES.GUEST]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI + ], + + [ROLES.USER]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CONTACTS, // Пользователи могут видеть контакты для выбора + PERMISSIONS.SEND_TO_USERS, // Пользователи могут отправлять сообщения + PERMISSIONS.CHAT_WITH_ADMINS, // Авторизованные пользователи могут видеть личные сообщения + PERMISSIONS.VIEW_BASIC_DOCS // Базовые документы для пользователей + ], + + [ROLES.READONLY]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Чтение внутренних юридических документов + PERMISSIONS.VIEW_LEGAL_DOCS + ], + + [ROLES.EDITOR]: [ + PERMISSIONS.VIEW_HOME, + PERMISSIONS.CHAT_AI, + PERMISSIONS.RECEIVE_MESSAGES, + PERMISSIONS.VIEW_CRM, + PERMISSIONS.VIEW_CONTACTS, + PERMISSIONS.VIEW_DATA, + PERMISSIONS.SEND_TO_USERS, + PERMISSIONS.CHAT_WITH_ADMINS, + PERMISSIONS.GENERATE_AI_REPLIES, + PERMISSIONS.EDIT_USER_DATA, + PERMISSIONS.EDIT_CONTACTS, + PERMISSIONS.DELETE_USER_DATA, + PERMISSIONS.DELETE_MESSAGES, + PERMISSIONS.BROADCAST, + PERMISSIONS.MANAGE_TAGS, + PERMISSIONS.BLOCK_USERS, + PERMISSIONS.MANAGE_SETTINGS, + // Базовые документы для пользователей + PERMISSIONS.VIEW_BASIC_DOCS, + // Полный доступ к юридическим документам + PERMISSIONS.VIEW_LEGAL_DOCS, + PERMISSIONS.MANAGE_LEGAL_DOCS + ] +}; + +/** + * Проверяет, имеет ли роль определенное право + * @param {string} role - Роль пользователя + * @param {string} permission - Требуемое право + * @returns {boolean} + */ +function hasPermission(role, permission) { + if (!role || !permission) return false; + return PERMISSIONS_MAP[role]?.includes(permission) || false; +} + +/** + * Получает все права для роли + * @param {string} role - Роль пользователя + * @returns {Array} + */ +function getPermissionsForRole(role) { + return PERMISSIONS_MAP[role] || []; +} + +/** + * Проверяет, имеет ли роль ХОТЯ БЫ ОДНО из прав + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAnyPermission(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.some(p => hasPermission(role, p)); +} + +/** + * Проверяет, имеет ли роль ВСЕ указанные права + * @param {string} role - Роль пользователя + * @param {Array} permissions - Список прав + * @returns {boolean} + */ +function hasAllPermissions(role, permissions) { + if (!Array.isArray(permissions)) return false; + return permissions.every(p => hasPermission(role, p)); +} + +/** + * Получает описание роли + * @param {string} role - Роль пользователя + * @returns {string} + */ +function getRoleDescription(role) { + const descriptions = { + [ROLES.GUEST]: 'Неавторизованный гость', + [ROLES.USER]: 'Авторизованный гость', + [ROLES.READONLY]: 'Админ (только чтение)', + [ROLES.EDITOR]: 'Админ (редактор)' + }; + + return descriptions[role] || 'Неизвестная роль'; +} + +/** + * Проверить, может ли отправитель отправлять сообщения получателю + * @param {string} senderRole - Роль отправителя + * @param {string} recipientRole - Роль получателя + * @param {number} senderId - ID отправителя + * @param {number} recipientId - ID получателя + * @returns {Object} { canSend: boolean, errorMessage?: string } + */ +function canSendMessage(senderRole, recipientRole, senderId, recipientId) { + // Проверяем базовое право на отправку сообщений + if (!hasPermission(senderRole, PERMISSIONS.SEND_TO_USERS)) { + return { + canSend: false, + errorMessage: 'У вас нет права на отправку сообщений' + }; + } + + // Собственный чат - всегда разрешен (для ИИ ассистента) + if (senderId === recipientId) { + return { canSend: true }; + } + + // USER и READONLY могут писать только EDITOR + if ((senderRole === 'user' || senderRole === 'readonly') && recipientRole === 'editor') { + return { canSend: true }; + } + + // EDITOR может писать всем (USER, READONLY, EDITOR) + if (senderRole === 'editor') { + return { canSend: true }; + } + + // USER и READONLY НЕ могут писать друг другу + if ((senderRole === 'user' || senderRole === 'readonly') && + (recipientRole === 'user' || recipientRole === 'readonly')) { + return { + canSend: false, + errorMessage: 'Пользователи и читатели не могут отправлять сообщения друг другу' + }; + } + + // Остальные случаи запрещены + return { + canSend: false, + errorMessage: `Роль ${senderRole} не может отправлять сообщения роли ${recipientRole}` + }; +} + +// Экспорты для CommonJS (Node.js) +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + +// ES модули для Frontend +export { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage +}; + +// CommonJS для Backend +if (typeof module !== 'undefined' && module.exports) { + module.exports = { + ROLES, + PERMISSIONS, + PERMISSIONS_MAP, + hasPermission, + getPermissionsForRole, + hasAnyPermission, + hasAllPermissions, + getRoleDescription, + canSendMessage + }; +} + diff --git a/scripts/ssh-key-server.js b/scripts/ssh-key-server.js old mode 100755 new mode 100644 diff --git a/vector-search/.dockerignore b/vector-search/.dockerignore index ce70251..9f2d53e 100644 --- a/vector-search/.dockerignore +++ b/vector-search/.dockerignore @@ -22,8 +22,7 @@ README* .git/ .gitignore -# Зависимости (будут установлены в контейнере) -node_modules/ +# Логи установки npm-debug.log* yarn-debug.log* yarn-error.log*