From ecc166e08b66afa932b46967dfdb0b69547e93ef Mon Sep 17 00:00:00 2001 From: igor_nadein Date: Tue, 24 Feb 2026 18:04:05 +0300 Subject: [PATCH] fix: correct imports after frontend/Igor merge - Fixed PHONE_FIELD import from _helpers module - Fixed _normalize_phone import from employees.utils --- API_QUICKSTART.md | 198 --- DOCKER_ORCHESTRATION.md | 289 ----- EUSRR_API.postman_collection.json | 1094 ----------------- EUSRR_Local.postman_environment.json | 37 - README.md | 372 ------ api_tests.http | 467 ------- backend/api/auth/serializers.py | 3 +- .../templates/includes/rightbar_calendar.html | 17 - backend/test_calendar_frontend.html | 758 ------------ backend/test_calendar_frontend_simple.html | 720 ----------- backend/test_logging.py | 67 - backend/test_message_optimization.py | 85 -- backend/test_refactored_notifications.py | 55 - backend/test_reply.bat | 61 - backend/test_reply.sh | 60 - backend/test_reply_functionality.html | 344 ------ backend/test_toast_calendar.html | 0 backend/test_v2_autoscroll.html | 313 ----- .../FRONTEND_MIGRATION_COMPLETE.md | 135 -- newman_test_results.txt | 208 ---- nginx.conf | 83 -- package-lock.json | 6 - 22 files changed, 2 insertions(+), 5370 deletions(-) delete mode 100644 API_QUICKSTART.md delete mode 100644 DOCKER_ORCHESTRATION.md delete mode 100644 EUSRR_API.postman_collection.json delete mode 100644 EUSRR_Local.postman_environment.json delete mode 100644 README.md delete mode 100644 api_tests.http delete mode 100644 backend/templates/includes/rightbar_calendar.html delete mode 100644 backend/test_calendar_frontend.html delete mode 100644 backend/test_calendar_frontend_simple.html delete mode 100644 backend/test_logging.py delete mode 100644 backend/test_message_optimization.py delete mode 100644 backend/test_refactored_notifications.py delete mode 100644 backend/test_reply.bat delete mode 100644 backend/test_reply.sh delete mode 100644 backend/test_reply_functionality.html delete mode 100644 backend/test_toast_calendar.html delete mode 100644 backend/test_v2_autoscroll.html delete mode 100644 docs/in-progress/FRONTEND_MIGRATION_COMPLETE.md delete mode 100644 newman_test_results.txt delete mode 100644 nginx.conf delete mode 100644 package-lock.json diff --git a/API_QUICKSTART.md b/API_QUICKSTART.md deleted file mode 100644 index cb30b9e3..00000000 --- a/API_QUICKSTART.md +++ /dev/null @@ -1,198 +0,0 @@ -# 🚀 Быстрый старт: Тестирование API - -## Выбор инструмента - -| Инструмент | Использование | Сложность | -|------------|---------------|-----------| -| **Postman** | GUI, удобный интерфейс | ⭐⭐ | -| **REST Client** | VS Code, быстрое тестирование | ⭐ | -| **pytest** | Автоматизация, CI/CD | ⭐⭐⭐ | -| **cURL** | Консоль, скрипты | ⭐⭐ | - -## 1️⃣ Postman (рекомендуется для начинающих) - -### Установка -1. Скачайте [Postman](https://www.postman.com/downloads/) -2. Установите и запустите - -### Импорт коллекции -```bash -1. File → Import -2. Выберите файл: EUSRR_API.postman_collection.json -3. Также импортируйте: EUSRR_Local.postman_environment.json -4. Выберите environment "EUSRR Local" в правом верхнем углу -``` - -### Первый запрос -```bash -1. Откройте "Authentication → Login" -2. Нажмите "Send" -3. Токен автоматически сохранится! ✅ -4. Теперь можно выполнять любые запросы -``` - -## 2️⃣ REST Client (для VS Code) - -### Установка -```bash -1. Откройте Extensions (Ctrl+Shift+X) -2. Найдите "REST Client" -3. Установите от Huachao Mao -``` - -### Использование -```bash -1. Откройте файл: api_tests.http -2. Найдите "### Login" -3. Нажмите "Send Request" над запросом -4. Скопируйте access token -5. Замените @accessToken на ваш токен -6. Готово! 🎉 -``` - -## 3️⃣ pytest (автоматизированное тестирование) - -### Запуск всех тестов -```bash -# В Docker -docker-compose exec web pytest tests/api/ - -# Локально -.venv/Scripts/python -m pytest tests/api/ -``` - -### Запуск конкретного теста -```bash -docker-compose exec web pytest tests/api/examples/test_api_examples.py::TestAuthentication::test_login_with_email -v -``` - -### С покрытием кода -```bash -docker-compose exec web pytest tests/api/ --cov=api --cov-report=html -``` - -## 4️⃣ cURL (консоль) - -### Получить токен -```bash -curl -X POST http://localhost:8000/api/auth/token/ \ - -H "Content-Type: application/json" \ - -d '{"username": "admin@example.com", "password": "password"}' -``` - -### Использовать токен -```bash -TOKEN="your_token_here" - -curl -X GET http://localhost:8000/api/v1/employees/ \ - -H "Authorization: Bearer $TOKEN" -``` - -## 📋 Основные endpoints - -### Аутентификация -- `POST /api/auth/token/` - Получить токен -- `POST /api/auth/token/refresh/` - Обновить токен -- `POST /api/v1/auth/register/` - Регистрация - -### Сотрудники -- `GET /api/v1/employees/` - Список -- `GET /api/v1/employees/me/` - Текущий пользователь -- `GET /api/v1/employees/{id}/` - Конкретный сотрудник -- `PATCH /api/v1/employees/{id}/` - Обновить - -### Посты -- `GET /api/v1/posts/` - Список -- `POST /api/v1/posts/` - Создать -- `POST /api/v1/posts/{id}/like/` - Лайк - -### Мессенджер -- `GET /api/v1/communications/chats/` - Чаты -- `GET /api/v1/communications/messages/?chat={id}` - Сообщения -- `POST /api/v1/communications/messages/` - Отправить - -### Документы -- `GET /api/v1/documents/` - Список -- `POST /api/v1/documents/` - Создать -- `GET /api/v1/documents/{id}/` - Получить - -### Заявки -- `GET /api/v1/requests/` - Список -- `POST /api/v1/requests/` - Создать -- `PATCH /api/v1/requests/{id}/` - Обновить - -### Уведомления -- `GET /api/v1/notifications/` - Список -- `POST /api/v1/notifications/{id}/mark_as_read/` - Пометить прочитанным - -## 🔐 Пример авторизованного запроса - -### Postman -``` -Authorization: Bearer {{access_token}} -(автоматически подставляется) -``` - -### REST Client -```http -GET {{baseUrl}}/api/v1/employees/ -Authorization: Bearer {{accessToken}} -``` - -### cURL -```bash -curl -H "Authorization: Bearer YOUR_TOKEN" \ - http://localhost:8000/api/v1/employees/ -``` - -### Python requests -```python -import requests - -token = "your_token" -headers = {"Authorization": f"Bearer {token}"} -response = requests.get( - "http://localhost:8000/api/v1/employees/", - headers=headers -) -``` - -## ⚠️ Частые ошибки - -### 401 Unauthorized -``` -❌ Токен отсутствует или истек -✅ Выполните Login снова -``` - -### 403 Forbidden -``` -❌ Нет прав доступа -✅ Проверьте роль пользователя -``` - -### 404 Not Found -``` -❌ Неверный URL или ID -✅ Проверьте endpoint -``` - -### 400 Bad Request -``` -❌ Ошибка в данных запроса -✅ Проверьте JSON и обязательные поля -``` - -## 📚 Полная документация - -Детальная документация: [docs/guides/API_TESTING_GUIDE.md](docs/guides/API_TESTING_GUIDE.md) - -## 🎯 Следующие шаги - -1. ✅ Выберите инструмент тестирования -2. ✅ Импортируйте коллекцию/откройте файл -3. ✅ Выполните Login -4. ✅ Протестируйте endpoints -5. ✅ Изучите примеры тестов - -**Вопросы?** Смотрите [API_TESTING_GUIDE.md](docs/guides/API_TESTING_GUIDE.md) diff --git a/DOCKER_ORCHESTRATION.md b/DOCKER_ORCHESTRATION.md deleted file mode 100644 index f1abcf74..00000000 --- a/DOCKER_ORCHESTRATION.md +++ /dev/null @@ -1,289 +0,0 @@ -# EUSRR Docker Orchestration Guide - -## Обзор архитектуры - -Проект EUSRR теперь имеет полную оркестрацию с использованием Docker Compose: - -``` -┌─────────────┐ -│ Nginx │ :80 - Reverse Proxy -│ (Gateway) │ -└──────┬──────┘ - │ - ├──────────┐ - │ │ -┌──────▼──────┐ ┌─▼────────────┐ -│ Frontend │ │ Backend │ -│ (Next.js) │ │ (Django) │ -│ :3000 │ │ :8000 │ -└──────┬──────┘ └──┬─────┬─────┘ - │ │ │ - └───────┬───┘ │ - │ │ - ┌──────▼───┐ ┌──▼────┐ - │ Redis │ │ DB │ - │ :6379 │ │ :5432 │ - └──────────┘ └────────┘ -``` - -## Сервисы - -### 1. **db** (PostgreSQL 16) -- Порт: `5432` -- База данных Django -- Persistent volume: `postgres_data` - -### 2. **redis** (Redis 7) -- Порт: `6379` -- Кэш и Channels backend -- Persistent volume: `redis_data` - -### 3. **web** (Django Backend) -- Порт: `8000` -- ASGI сервер (Daphne) -- REST API + WebSocket -- Подключается к `db` и `redis` - -### 4. **celery** (Celery Worker) -- Фоновые задачи -- Подключается к `redis` и `db` - -### 5. **celery-beat** (Celery Scheduler) -- Планировщик задач -- Django Celery Beat - -### 6. **frontend** (Next.js) -- Порт: `3000` -- Корпоративный фронтенд -- Использует `web` backend через proxy - -### 7. **nginx** (Reverse Proxy) -- Порт: `80` (основной вход) -- Маршрутизация: - - `/` → Frontend (Next.js) - - `/api/` → Backend API - - `/admin/` → Django Admin - - `/ws/` → WebSocket - - `/static/`, `/media/` → Статика - -## Быстрый старт - -### Запуск всех сервисов - -```bash -# Создайте .env файл из примера -cp .env.example .env - -# Запустите все сервисы -docker-compose up -d - -# Просмотр логов -docker-compose logs -f -``` - -### Доступ к приложению - -- **Основной вход (через Nginx)**: http://localhost -- **Frontend напрямую**: http://localhost:3000 -- **Backend API**: http://localhost:8000/api/ -- **Django Admin**: http://localhost/admin/ - -### Остановка - -```bash -# Остановка всех сервисов -docker-compose down - -# Остановка с удалением volumes -docker-compose down -v -``` - -## Конфигурация - -### Environment Variables - -Основные переменные в `.env`: - -```env -# Database -POSTGRES_DB=eusrr -POSTGRES_USER=eusrr_user -POSTGRES_PASSWORD=change-me-postgres-password - -# Django -DEBUG=True -SECRET_KEY=dev-secret-key -ALLOWED_HOSTS=localhost,127.0.0.1,web,nginx - -# Ports -WEB_PORT=8000 -FRONTEND_PORT=3000 -NGINX_PORT=80 -``` - -### Frontend Environment - -Файл `corp.front/.env.local`: - -```env -NEXT_PUBLIC_API_URL=http://web:8000 -NEXT_PUBLIC_WS_URL=ws://web:8000 -``` - -## Разработка - -### Режим разработки - -```bash -# Backend отдельно (с hot-reload) -cd backend -../.venv/Scripts/python manage.py runserver - -# Frontend отдельно (с hot-reload) -cd corp.front -npm run dev -``` - -### Режим Production - -```bash -# Полная оркестрация через Docker -docker-compose up -d -``` - -## Миграции и первичная настройка - -```bash -# Выполнить миграции -docker-compose exec web python manage.py migrate - -# Создать суперпользователя -docker-compose exec web python manage.py createsuperuser - -# Собрать статику -docker-compose exec web python manage.py collectstatic --noinput -``` - -## Мониторинг - -```bash -# Статус сервисов -docker-compose ps - -# Логи конкретного сервиса -docker-compose logs -f web -docker-compose logs -f frontend -docker-compose logs -f nginx - -# Использование ресурсов -docker stats -``` - -## Отладка - -### Проверка сети - -```bash -# Проверить связь frontend → backend -docker-compose exec frontend ping web - -# Проверить связь backend → db -docker-compose exec web pg_isready -h db -U eusrr_user -``` - -### Доступ к контейнерам - -```bash -# Backend shell -docker-compose exec web bash -docker-compose exec web python manage.py shell - -# Frontend shell -docker-compose exec frontend sh - -# Database -docker-compose exec db psql -U eusrr_user -d eusrr -``` - -## Volumes - -Persistent данные сохраняются в volumes: - -- `postgres_data` - база данных -- `redis_data` - Redis данные -- `static_volume` - статические файлы Django -- `media_volume` - загруженные медиафайлы - -## Сеть - -Все сервисы находятся в сети `eusrr_network` и могут обращаться друг к другу по именам сервисов: - -- `web` - Django backend -- `frontend` - Next.js frontend -- `db` - PostgreSQL -- `redis` - Redis -- `nginx` - Nginx - -## Nginx маршрутизация - -- `location /` - Next.js фронтенд -- `location /api/` - Django REST API -- `location /admin/` - Django Admin -- `location /ws/` - WebSocket (Django Channels) -- `location /static/` - Статические файлы -- `location /media/` - Медиа файлы -- `location /health` - Health check endpoint - -## Production Deployment - -Для production окружения: - -1. Обновите `.env`: - - Установите `DEBUG=False` - - Смените `SECRET_KEY` - - Обновите `ALLOWED_HOSTS` - - Используйте надежные пароли БД - -2. Настройте HTTPS (добавьте SSL в nginx) - -3. Используйте внешние managed сервисы для БД и Redis (опционально) - -4. Настройте backup для volumes - -## Troubleshooting - -### Frontend не может подключиться к backend - -Проверьте переменные окружения в `corp.front/.env.local` и убедитесь, что используется правильный URL для Docker сети. - -### Backend не запускается - -```bash -# Проверьте логи -docker-compose logs web - -# Проверьте подключение к БД -docker-compose exec web python manage.py check --database default -``` - -### Nginx 502 Bad Gateway - -Убедитесь, что `web` и `frontend` сервисы запущены: - -```bash -docker-compose ps -``` - -## Файлы конфигурации - -- `docker-compose.yml` - Оркестрация сервисов -- `nginx.conf` - Конфигурация Nginx -- `backend/Dockerfile` - Django image -- `corp.front/Dockerfile` - Next.js image -- `.env.example` - Шаблон переменных окружения -- `corp.front/.env.example` - Шаблон для frontend - ---- - -**Дата создания**: 19 февраля 2025 -**Версия**: 1.0 diff --git a/EUSRR_API.postman_collection.json b/EUSRR_API.postman_collection.json deleted file mode 100644 index 01932643..00000000 --- a/EUSRR_API.postman_collection.json +++ /dev/null @@ -1,1094 +0,0 @@ -{ - "info": { - "_postman_id": "eusrr-api-collection", - "name": "EUSRR API", - "description": "API коллекция для тестирования системы EUSRR", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" - }, - "auth": { - "type": "bearer", - "bearer": [ - { - "key": "token", - "value": "{{access_token}}", - "type": "string" - } - ] - }, - "variable": [ - { - "key": "base_url", - "value": "http://localhost:8000", - "type": "string" - }, - { - "key": "access_token", - "value": "", - "type": "string" - }, - { - "key": "refresh_token", - "value": "", - "type": "string" - } - ], - "item": [ - { - "name": "Authentication", - "item": [ - { - "name": "Login (Obtain Token)", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "if (pm.response.code === 200) {", - " var jsonData = pm.response.json();", - " pm.environment.set(\"access_token\", jsonData.access);", - " pm.environment.set(\"refresh_token\", jsonData.refresh);", - " console.log(\"Access token saved: \" + jsonData.access);", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "noauth" - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"email\": \"admin@example.com\",\n \"password\": \"password\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/auth/token/", - "host": ["{{base_url}}"], - "path": ["api", "auth", "token", ""] - } - }, - "response": [] - }, - { - "name": "Refresh Token", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "if (pm.response.code === 200) {", - " var jsonData = pm.response.json();", - " pm.environment.set(\"access_token\", jsonData.access);", - " console.log(\"Access token refreshed: \" + jsonData.access);", - "}" - ], - "type": "text/javascript" - } - } - ], - "request": { - "auth": { - "type": "noauth" - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"refresh\": \"{{refresh_token}}\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/auth/token/refresh/", - "host": ["{{base_url}}"], - "path": ["api", "auth", "token", "refresh", ""] - } - }, - "response": [] - }, - { - "name": "Register", - "request": { - "auth": { - "type": "noauth" - }, - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"email\": \"newuser@example.com\",\n \"password\": \"StrongPass123!\",\n \"password2\": \"StrongPass123!\",\n \"first_name\": \"Иван\",\n \"last_name\": \"Иванов\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/auth/register/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "auth", "register", ""] - } - }, - "response": [] - } - ] - }, - { - "name": "Employees", - "item": [ - { - "name": "List Employees", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/employees/?page=1", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employees", ""], - "query": [ - { - "key": "page", - "value": "1" - }, - { - "key": "search", - "value": "Иванов", - "disabled": true - } - ] - } - }, - "response": [] - }, - { - "name": "Get Employee by ID", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/employees/1/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employees", "1", ""] - } - }, - "response": [] - }, - { - "name": "Update Employee", - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"first_name\": \"Иван\",\n \"last_name\": \"Петров\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/employees/1/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employees", "1", ""] - } - }, - "response": [] - }, - { - "name": "Get Current User (Me)", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/employees/me/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employees", "me", ""] - } - }, - "response": [] - }, - { - "name": "Update My Profile", - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"first_name\": \"Обновленное Имя\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/employees/me/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employees", "me", ""] - } - }, - "response": [] - }, - { - "name": "Create Employee", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"email\": \"newemployee@example.com\",\n \"password\": \"SecurePass123\",\n \"first_name\": \"Новый\",\n \"last_name\": \"Сотрудник\",\n \"phone_number\": \"+79991234568\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/employees/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employees", ""] - } - }, - "response": [] - }, - { - "name": "Delete Employee", - "request": { - "method": "DELETE", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/employees/2/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employees", "2", ""] - } - }, - "response": [] - }, - { - "name": "Add Skill to Employee", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"skill_name\": \"Python\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/employees/1/add_skill/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employees", "1", "add_skill", ""] - } - }, - "response": [] - }, - { - "name": "Remove Skill from Employee", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"skill_name\": \"Python\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/employees/1/remove_skill/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employees", "1", "remove_skill", ""] - } - }, - "response": [] - }, - { - "name": "Get Employee LDAP Info", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/employees/1/ldap-info/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employees", "1", "ldap-info", ""] - } - }, - "response": [] - }, - { - "name": "Export Employees to Excel", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/employees/export-excel/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employees", "export-excel", ""] - } - }, - "response": [] - } - ] - }, - { - "name": "Departments", - "item": [ - { - "name": "List Departments", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/departments/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "departments", ""] - } - }, - "response": [] - }, - { - "name": "Get Department by ID", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/departments/1/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "departments", "1", ""] - } - }, - "response": [] - }, - { - "name": "Create Department", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"Новый отдел\",\n \"description\": \"Описание отдела\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/departments/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "departments", ""] - } - }, - "response": [] - } - ] - }, - { - "name": "Documents", - "item": [ - { - "name": "List Documents", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/documents/?page=1", - "host": ["{{base_url}}"], - "path": ["api", "v1", "documents", ""], - "query": [ - { - "key": "page", - "value": "1" - } - ] - } - }, - "response": [] - }, - { - "name": "Get Document by ID", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/documents/1/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "documents", "1", ""] - } - }, - "response": [] - }, - { - "name": "Create Document", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"title\": \"Новый документ\",\n \"content\": \"Содержание документа\",\n \"document_type\": \"report\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/documents/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "documents", ""] - } - }, - "response": [] - } - ] - }, - { - "name": "Feed (Posts)", - "item": [ - { - "name": "List Posts", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/posts/?page=1", - "host": ["{{base_url}}"], - "path": ["api", "v1", "posts", ""], - "query": [ - { - "key": "page", - "value": "1" - } - ] - } - }, - "response": [] - }, - { - "name": "Create Post", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"content\": \"Новый пост в ленте\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/posts/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "posts", ""] - } - }, - "response": [] - }, - { - "name": "Like Post", - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/posts/1/like/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "posts", "1", "like", ""] - } - }, - "response": [] - }, - { - "name": "List Comments", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/comments/?post=1", - "host": ["{{base_url}}"], - "path": ["api", "v1", "comments", ""], - "query": [ - { - "key": "post", - "value": "1" - } - ] - } - }, - "response": [] - }, - { - "name": "Create Comment", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"post\": 1,\n \"content\": \"Отличный пост!\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/comments/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "comments", ""] - } - }, - "response": [] - } - ] - }, - { - "name": "Communications (Messenger)", - "item": [ - { - "name": "List Chats", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/communications/chats/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "communications", "chats", ""] - } - }, - "response": [] - }, - { - "name": "Get Chat by ID", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/communications/chats/1/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "communications", "chats", "1", ""] - } - }, - "response": [] - }, - { - "name": "Create Chat", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"Новый чат\",\n \"chat_type\": \"group\",\n \"participants\": [1, 2, 3]\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/communications/chats/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "communications", "chats", ""] - } - }, - "response": [] - }, - { - "name": "List Messages", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/communications/messages/?chat=1", - "host": ["{{base_url}}"], - "path": ["api", "v1", "communications", "messages", ""], - "query": [ - { - "key": "chat", - "value": "1" - } - ] - } - }, - "response": [] - }, - { - "name": "Send Message", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"chat\": 1,\n \"content\": \"Привет! Это тестовое сообщение\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/communications/messages/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "communications", "messages", ""] - } - }, - "response": [] - } - ] - }, - { - "name": "Calendar", - "item": [ - { - "name": "List Events", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/calendar/events/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "calendar", "events", ""] - } - }, - "response": [] - }, - { - "name": "Create Event", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"title\": \"Встреча\",\n \"start\": \"2026-02-20T10:00:00Z\",\n \"end\": \"2026-02-20T11:00:00Z\",\n \"description\": \"Важная встреча\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/calendar/events/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "calendar", "events", ""] - } - }, - "response": [] - }, - { - "name": "List Calendars", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/calendar/calendars/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "calendar", "calendars", ""] - } - }, - "response": [] - } - ] - }, - { - "name": "Requests", - "item": [ - { - "name": "List Requests", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/requests/?page=1", - "host": ["{{base_url}}"], - "path": ["api", "v1", "requests", ""], - "query": [ - { - "key": "page", - "value": "1" - }, - { - "key": "status", - "value": "pending", - "disabled": true - } - ] - } - }, - "response": [] - }, - { - "name": "Get Request by ID", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/requests/1/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "requests", "1", ""] - } - }, - "response": [] - }, - { - "name": "Create Request", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"title\": \"Новая заявка\",\n \"description\": \"Описание заявки\",\n \"request_type\": \"support\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/requests/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "requests", ""] - } - }, - "response": [] - } - ] - }, - { - "name": "Notifications", - "item": [ - { - "name": "List Notifications", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/notifications/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "notifications", ""] - } - }, - "response": [] - }, - { - "name": "Mark as Read", - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/notifications/1/mark_as_read/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "notifications", "1", "mark_as_read", ""] - } - }, - "response": [] - }, - { - "name": "Mark All as Read", - "request": { - "method": "POST", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/notifications/mark_all_as_read/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "notifications", "mark_all_as_read", ""] - } - }, - "response": [] - } - ] - }, - { - "name": "Employee Actions", - "item": [ - { - "name": "List Employee Actions", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/employee-actions/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employee-actions", ""] - } - }, - "response": [] - }, - { - "name": "Get Employee Action by ID", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/employee-actions/1/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employee-actions", "1", ""] - } - }, - "response": [] - }, - { - "name": "Create Employee Action", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"employee\": 1,\n \"action_type\": \"hired\",\n \"date\": \"2026-02-17\",\n \"notes\": \"Принят на работу\"\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/employee-actions/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "employee-actions", ""] - } - }, - "response": [] - } - ] - }, - { - "name": "Department Roles", - "item": [ - { - "name": "List Department Roles", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/department-roles/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "department-roles", ""] - } - }, - "response": [] - }, - { - "name": "Get Role Assignments", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/department-roles/1/assignments/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "department-roles", "1", "assignments", ""] - } - }, - "response": [] - }, - { - "name": "Assign Role to Employee", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"employee_id\": 1\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/department-roles/1/assign/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "department-roles", "1", "assign", ""] - } - }, - "response": [] - } - ] - }, - { - "name": "Advanced Skills", - "item": [ - { - "name": "Bulk Create Skills", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"names\": [\"Python\", \"JavaScript\", \"Docker\", \"Kubernetes\"]\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/skills/bulk_create/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "skills", "bulk_create", ""] - } - }, - "response": [] - }, - { - "name": "Merge Skills", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"source_id\": 2,\n \"target_id\": 1\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/skills/merge/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "skills", "merge", ""] - } - }, - "response": [] - } - ] - }, - { - "name": "Advanced Groups", - "item": [ - { - "name": "Get Group Permissions", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/groups/1/permissions/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "groups", "1", "permissions", ""] - } - }, - "response": [] - }, - { - "name": "Set Group Permissions", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"permission_ids\": [1, 2, 3]\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/groups/1/set-permissions/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "groups", "1", "set-permissions", ""] - } - }, - "response": [] - }, - { - "name": "Get Group Employees", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{base_url}}/api/v1/groups/1/employees/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "groups", "1", "employees", ""] - } - }, - "response": [] - }, - { - "name": "Add Group Members", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"employee_ids\": [1, 2, 3]\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/groups/1/add-members/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "groups", "1", "add-members", ""] - } - }, - "response": [] - }, - { - "name": "Remove Group Members", - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"employee_ids\": [1]\n}" - }, - "url": { - "raw": "{{base_url}}/api/v1/groups/1/remove-members/", - "host": ["{{base_url}}"], - "path": ["api", "v1", "groups", "1", "remove-members", ""] - } - }, - "response": [] - } - ] - } - ] -} diff --git a/EUSRR_Local.postman_environment.json b/EUSRR_Local.postman_environment.json deleted file mode 100644 index cc1a33a4..00000000 --- a/EUSRR_Local.postman_environment.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "id": "eusrr-local-env", - "name": "EUSRR Local", - "values": [ - { - "key": "base_url", - "value": "http://localhost:8000", - "type": "default", - "enabled": true - }, - { - "key": "access_token", - "value": "", - "type": "secret", - "enabled": true - }, - { - "key": "refresh_token", - "value": "", - "type": "secret", - "enabled": true - }, - { - "key": "test_user_email", - "value": "admin@example.com", - "type": "default", - "enabled": true - }, - { - "key": "test_user_password", - "value": "password", - "type": "secret", - "enabled": true - } - ], - "_postman_variable_scope": "environment" -} diff --git a/README.md b/README.md deleted file mode 100644 index 3fd1c7a1..00000000 --- a/README.md +++ /dev/null @@ -1,372 +0,0 @@ -# EUSRR - Корпоративная система управления - -Комплексная корпоративная информационная система с интеграцией Active Directory/LDAP для управления сотрудниками, документами, заявками и внутренними коммуникациями. - -## Основные возможности - -### 🏢 Управление сотрудниками -- Интеграция с Active Directory через LDAP -- Автоматическая синхронизация данных сотрудников -- Управление отделами и должностями -- Профили сотрудников с фотографиями и контактами -- Иерархия отделов с назначением руководителей - -### 📄 Система документооборота -- Загрузка и распространение документов -- Отправка документов всем сотрудникам, конкретным отделам или индивидуально -- Автоматические уведомления о новых документах -- Отслеживание ознакомления с документами -- Визуальное выделение неознакомленных документов - -### 📬 Система уведомлений -- Веб-уведомления в реальном времени (WebSocket) -- Email-уведомления с настраиваемой периодичностью (мгновенно, ежедневно, еженедельно) -- Telegram-уведомления через бота -- Индивидуальные настройки уведомлений для каждого пользователя -- Звуковые оповещения о новых уведомлениях - -### 📋 Система заявок -- Создание и отслеживание заявок -- Назначение ответственных отделов -- История изменений заявок -- Комментарии и обсуждения - -### 💬 Внутренние коммуникации -- Лента новостей организации -- Посты с комментариями -- Упоминания сотрудников (@mention) -- Ленты новостей по отделам - -### 📅 Календарь событий -- Корпоративный календарь -- События отделов и общеорганизационные события -- Интеграция с календарем на главной странице - -## Технологический стек - -### Backend -- **Python 3.13.5** -- **Django 5.2.4** - основной веб-фреймворк -- **Django REST Framework** - REST API -- **Django Channels** - WebSocket поддержка -- **PostgreSQL / SQLite** - база данных -- **python-ldap** - интеграция с Active Directory -- **Celery** (опционально) - асинхронные задачи - -### Frontend -- **Bootstrap 5** - UI фреймворк -- **Vanilla JavaScript (ES6+)** - модульная архитектура -- **WebSocket** - реальное время -- **Fetch API** - HTTP запросы - -### Интеграции -- **Active Directory (LDAP)** - управление пользователями -- **Telegram Bot API** - уведомления в Telegram -- **SMTP (Yandex)** - email-рассылка -- **SMS Gateway** - SMS-уведомления - -## Установка и запуск - -### Предварительные требования - -- Python 3.11+ -- PostgreSQL 14+ или SQLite (для разработки) -- Active Directory (опционально, можно отключить через `LDAP_ENABLED=False`) -- Git - -### Установка - -1. Клонирование репозитория: -```bash -git clone https://github.com/IgorNadein/EUSRR.git -cd EUSRR -``` - -2. Создание виртуального окружения: -```bash -python -m venv .venv -source .venv/bin/activate # Linux/Mac -# или -.venv\Scripts\activate # Windows -``` - -3. Установка зависимостей: -```bash -cd backend -pip install -r requirements.txt -``` - -4. Настройка окружения: -```bash -# Скопируйте .env.example в .env и настройте параметры -cp .env.example .env -``` - -Основные параметры в `.env`: -```env -# База данных -USE_SQLITE=True # True для SQLite, False для PostgreSQL - -# Django -SECRET_KEY=your-secret-key -DEBUG=True -ALLOWED_HOSTS=localhost,127.0.0.1 - -# LDAP (можно отключить) -LDAP_ENABLED=False # True для интеграции с AD - -# Email -EMAIL_HOST=smtp.yandex.ru -EMAIL_PORT=465 -EMAIL_HOST_USER=your-email@yandex.ru -EMAIL_HOST_PASSWORD=your-password - -# Telegram (опционально) -TELEGRAM_BOT_TOKEN=your-bot-token -``` - -5. Применение миграций: -```bash -python manage.py migrate -``` - -6. Создание суперпользователя: -```bash -python manage.py createsuperuser -``` - -7. Сбор статических файлов: -```bash -python manage.py collectstatic --noinput -``` - -### Запуск разработки - -```bash -# Запуск сервера разработки -python manage.py runserver 9000 - -# В отдельном терминале - Telegram бот (опционально) -python manage.py run_telegram_bot -``` - -Приложение будет доступно по адресу: http://localhost:9000 - -### Запуск в продакшене - -1. Настройте `.env` для продакшена: -```env -DEBUG=False -USE_SQLITE=False -ALLOWED_HOSTS=your-domain.com -LDAP_ENABLED=True -``` - -2. Используйте WSGI сервер (Gunicorn): -```bash -pip install gunicorn -gunicorn eusrr_backend.wsgi:application --bind 0.0.0.0:8000 -``` - -3. Настройте веб-сервер (Nginx) как reverse proxy - -4. Настройте supervisor/systemd для автозапуска - -## Структура проекта - -``` -EUSRR/ -├── backend/ # Django проект -│ ├── api/ # REST API -│ │ └── v1/ # API версия 1 -│ │ ├── documents/ # API документов -│ │ ├── employees/ # API сотрудников -│ │ └── ... -│ ├── documents/ # Приложение документов -│ ├── employees/ # Приложение сотрудников -│ ├── notifications/ # Система уведомлений -│ ├── requests_app/ # Система заявок -│ ├── feed/ # Лента новостей -│ ├── calendar_app/ # Календарь событий -│ ├── communications/ # Коммуникации -│ ├── static/ # Статические файлы -│ │ ├── css/ # Стили -│ │ └── js/ # JavaScript модули -│ ├── templates/ # HTML шаблоны -│ ├── media/ # Загруженные файлы -│ └── eusrr_backend/ # Настройки Django -└── README.md # Этот файл -``` - -## Основные функции - -### Работа с документами - -1. **Создание документа:** - - Загрузка файла - - Выбор получателей (все/отделы/конкретные сотрудники) - - Автоматические уведомления - -2. **Отслеживание ознакомления:** - - Клик на документ автоматически отмечает ознакомление - - Визуальное выделение неознакомленных документов - - Ведомость ознакомлений для администраторов - -### Система уведомлений - -1. **Веб-уведомления:** - - Звуковое оповещение - - Счётчик непрочитанных - - Список в реальном времени - -2. **Email-уведомления:** - - Мгновенная отправка - - Ежедневный дайджест - - Еженедельный дайджест - -3. **Telegram-уведомления:** - - Привязка аккаунта через код - - Мгновенная отправка важных уведомлений - -### LDAP интеграция - -- Двусторонняя синхронизация данных -- Создание пользователей в AD -- Управление отделами через OU -- Автоматическое обновление при изменениях - -## API Документация - -REST API доступен по адресу `/api/v1/` - -Основные эндпоинты: -- `/api/v1/employees/` - Сотрудники -- `/api/v1/departments/` - Отделы -- `/api/v1/documents/` - Документы -- `/api/v1/requests/` - Заявки -- `/api/v1/calendar/events/` - События календаря -- `/api/notifications/` - Уведомления - -Документация API: `/api/docs/` (если установлен drf-spectacular) - -## Разработка - -### Создание миграций -```bash -python manage.py makemigrations -python manage.py migrate -``` - -### Запуск тестов -```bash -pytest -# или -python manage.py test -``` - -### Линтинг кода -```bash -flake8 . -black . -isort . -``` - -## Настройка уведомлений - -### Email -Настройте SMTP в `.env`: -```env -EMAIL_HOST=smtp.yandex.ru -EMAIL_PORT=465 -EMAIL_USE_SSL=true -EMAIL_HOST_USER=your-email@yandex.ru -EMAIL_HOST_PASSWORD=your-app-password -DEFAULT_FROM_EMAIL=Company -``` - -### Telegram -1. Создайте бота через @BotFather -2. Получите токен -3. Настройте в `.env`: -```env -TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklMNOpqrsTUVwxyz -TELEGRAM_BOT_USERNAME=your_bot_name -``` -4. Запустите бота: -```bash -python manage.py run_telegram_bot -``` - -### Отложенная отправка -Для дайджестов используйте команду: -```bash -python manage.py send_pending_notifications --batch-size=100 -``` - -Настройте cron для автоматического запуска: -```bash -# Ежедневно в 9:00 -0 9 * * * /path/to/python /path/to/manage.py send_pending_notifications -``` - -## Безопасность - -- Используйте HTTPS в продакшене -- Настройте CORS правильно -- Используйте сильные пароли -- Регулярно обновляйте зависимости -- Настройте файрвол -- Используйте переменные окружения для секретов - -## Документация и troubleshooting - -### Архитектура -- **[REALTIME_MIGRATION_GUIDE.md](REALTIME_MIGRATION_GUIDE.md)** ⭐ - **Миграция на архитектуру realtime** -- [backend/realtime/README.md](backend/realtime/README.md) - WebSocket API документация - -### Руководства по функциям -- [MESSAGE_EDITING_ANALYSIS.md](MESSAGE_EDITING_ANALYSIS.md) - Редактирование сообщений -- [POLLS_IMPLEMENTATION.md](POLLS_IMPLEMENTATION.md) - Система опросов -- [PERSONAL_CALENDAR_FEATURE.md](PERSONAL_CALENDAR_FEATURE.md) - Персональный календарь -- [REACTIONS_FROM_DATABASE.md](REACTIONS_FROM_DATABASE.md) - Система реакций -- [REQUESTS_RECIPIENTS_PLAN.md](REQUESTS_RECIPIENTS_PLAN.md) - Система получателей заявок - -### WebSocket диагностика -- **[WEBSOCKET_1006_QUICK_GUIDE.md](WEBSOCKET_1006_QUICK_GUIDE.md)** ⚡ - **Быстрая шпаргалка (5 минут)** -- [WEBSOCKET_1006_PRIORITY_CHECK.md](WEBSOCKET_1006_PRIORITY_CHECK.md) - Приоритетная диагностика -- [WEBSOCKET_1006_DIAGNOSTIC_PLAN.md](WEBSOCKET_1006_DIAGNOSTIC_PLAN.md) - Полный план диагностики -- [WEBSOCKET_KEEPALIVE_FIX.md](WEBSOCKET_KEEPALIVE_FIX.md) - Исправление keepalive -- [WEBSOCKET_UNIFIED_MIGRATION.md](WEBSOCKET_UNIFIED_MIGRATION.md) - Миграция на унифицированный WS - -### Рефакторинг и оптимизация -- [REFACTORING_SUMMARY.md](REFACTORING_SUMMARY.md) - Общий итог рефакторинга -- [CACHE_OPTIMIZATION_SUMMARY.md](CACHE_OPTIMIZATION_SUMMARY.md) - Оптимизация кеширования -- [API_REFACTORING_TESTING.md](API_REFACTORING_TESTING.md) - Тестирование API - -## Лицензия - -Проприетарное ПО © 2024-2025 Robotail - -## Контакты - -- Автор: Игорь Надеин -- GitHub: [@IgorNadein](https://github.com/IgorNadein) -- Проект: [EUSRR](https://github.com/IgorNadein/EUSRR) - -## Changelog - -### v2.0.0 (21.11.2025) -- ✨ Добавлена возможность отправки документов на отделы -- ✨ Визуальное выделение неознакомленных документов -- ✨ Автоматическое ознакомление при клике на документ -- ✨ Адаптивная мобильная версия -- 🐛 Исправлена фильтрация "Мои документы" для загрузивших -- 🐛 Исправлена ведомость ознакомлений с учётом отделов -- 🔧 Добавлена поддержка массовых рассылок с порогом - -### v1.0.0 -- 🎉 Первый релиз -- ✨ Базовая система документооборота -- ✨ Интеграция с LDAP -- ✨ Система уведомлений -- ✨ Управление заявками diff --git a/api_tests.http b/api_tests.http deleted file mode 100644 index bfdc8764..00000000 --- a/api_tests.http +++ /dev/null @@ -1,467 +0,0 @@ -### EUSRR API Tests -### Используйте расширение REST Client для VS Code -### https://marketplace.visualstudio.com/items?itemName=humao.rest-client - -@baseUrl = http://localhost:8000 -@contentType = application/json - -### Variables (будут заменены после выполнения Login запроса) -@accessToken = your_access_token_here -@refreshToken = your_refresh_token_here - -############################################# -# Authentication -############################################# - -### Login - Получение токена -# @name login -POST {{baseUrl}}/api/auth/token/ -Content-Type: {{contentType}} - -{ - "email": "admin@example.com", - "password": "password" -} - -### Save tokens to variables (автоматически после успешного login) -@accessToken = {{login.response.body.access}} -@refreshToken = {{login.response.body.refresh}} - -### Refresh Token -POST {{baseUrl}}/api/auth/token/refresh/ -Content-Type: {{contentType}} - -{ - "refresh": "{{refreshToken}}" -} - -### Register New User -POST {{baseUrl}}/api/v1/auth/register/ -Content-Type: {{contentType}} - -{ - "email": "newuser@example.com", - "password": "StrongPass123!", - "password2": "StrongPass123!", - "first_name": "Иван", - "last_name": "Иванов" -} - -############################################# -# Employees -############################################# - -### List Employees -GET {{baseUrl}}/api/v1/employees/?page=1 -Authorization: Bearer {{accessToken}} - -### Get Current User (Me) -GET {{baseUrl}}/api/v1/employees/me/ -Authorization: Bearer {{accessToken}} - -### Get Employee by ID -GET {{baseUrl}}/api/v1/employees/1/ -Authorization: Bearer {{accessToken}} - -### Update Employee -PATCH {{baseUrl}}/api/v1/employees/1/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "first_name": "Иван", - "last_name": "Петров" -} - -### Search Employees -GET {{baseUrl}}/api/v1/employees/?search=Иванов -Authorization: Bearer {{accessToken}} - -############################################# -# Departments -############################################# - -### List Departments -GET {{baseUrl}}/api/v1/departments/ -Authorization: Bearer {{accessToken}} - -### Get Department by ID -GET {{baseUrl}}/api/v1/departments/1/ -Authorization: Bearer {{accessToken}} - -### Create Department -POST {{baseUrl}}/api/v1/departments/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "name": "Новый отдел", - "description": "Описание отдела" -} - -### Update Department -PATCH {{baseUrl}}/api/v1/departments/1/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "name": "Обновленное название отдела" -} - -############################################# -# Positions -############################################# - -### List Positions -GET {{baseUrl}}/api/v1/positions/ -Authorization: Bearer {{accessToken}} - -### Create Position -POST {{baseUrl}}/api/v1/positions/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "title": "Разработчик", - "description": "Full-stack разработчик" -} - -############################################# -# Documents -############################################# - -### List Documents -GET {{baseUrl}}/api/v1/documents/?page=1 -Authorization: Bearer {{accessToken}} - -### Get Document by ID -GET {{baseUrl}}/api/v1/documents/1/ -Authorization: Bearer {{accessToken}} - -### Create Document -POST {{baseUrl}}/api/v1/documents/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "title": "Новый документ", - "content": "Содержание документа", - "document_type": "report" -} - -### Update Document -PATCH {{baseUrl}}/api/v1/documents/1/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "title": "Обновленное название" -} - -### Delete Document -DELETE {{baseUrl}}/api/v1/documents/1/ -Authorization: Bearer {{accessToken}} - -############################################# -# Feed (Posts & Comments) -############################################# - -### List Posts -GET {{baseUrl}}/api/v1/posts/?page=1 -Authorization: Bearer {{accessToken}} - -### Get Post by ID -GET {{baseUrl}}/api/v1/posts/1/ -Authorization: Bearer {{accessToken}} - -### Create Post -POST {{baseUrl}}/api/v1/posts/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "content": "Новый пост в ленте компании! #важное" -} - -### Update Post -PATCH {{baseUrl}}/api/v1/posts/1/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "content": "Обновленный пост" -} - -### Delete Post -DELETE {{baseUrl}}/api/v1/posts/1/ -Authorization: Bearer {{accessToken}} - -### Like Post -POST {{baseUrl}}/api/v1/posts/1/like/ -Authorization: Bearer {{accessToken}} - -### Unlike Post -DELETE {{baseUrl}}/api/v1/posts/1/unlike/ -Authorization: Bearer {{accessToken}} - -### List Comments for Post -GET {{baseUrl}}/api/v1/comments/?post=1 -Authorization: Bearer {{accessToken}} - -### Create Comment -POST {{baseUrl}}/api/v1/comments/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "post": 1, - "content": "Отличный пост!" -} - -############################################# -# Communications (Messenger) -############################################# - -### List Chats -GET {{baseUrl}}/api/v1/communications/chats/ -Authorization: Bearer {{accessToken}} - -### Get Chat by ID -GET {{baseUrl}}/api/v1/communications/chats/1/ -Authorization: Bearer {{accessToken}} - -### Create Private Chat -POST {{baseUrl}}/api/v1/communications/chats/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "chat_type": "private", - "participants": [2] -} - -### Create Group Chat -POST {{baseUrl}}/api/v1/communications/chats/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "name": "Рабочая группа", - "chat_type": "group", - "participants": [2, 3, 4] -} - -### List Messages in Chat -GET {{baseUrl}}/api/v1/communications/messages/?chat=1 -Authorization: Bearer {{accessToken}} - -### Send Message -POST {{baseUrl}}/api/v1/communications/messages/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "chat": 1, - "content": "Привет! Это тестовое сообщение" -} - -### Update Message -PATCH {{baseUrl}}/api/v1/communications/messages/1/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "content": "Исправленное сообщение" -} - -### Delete Message -DELETE {{baseUrl}}/api/v1/communications/messages/1/ -Authorization: Bearer {{accessToken}} - -### Mark Message as Read -POST {{baseUrl}}/api/v1/communications/messages/1/mark_as_read/ -Authorization: Bearer {{accessToken}} - -############################################# -# Calendar -############################################# - -### List Calendars -GET {{baseUrl}}/api/v1/calendar/calendars/ -Authorization: Bearer {{accessToken}} - -### List Events -GET {{baseUrl}}/api/v1/calendar/events/ -Authorization: Bearer {{accessToken}} - -### Get Events for Date Range -GET {{baseUrl}}/api/v1/calendar/events/?start=2026-02-01&end=2026-02-28 -Authorization: Bearer {{accessToken}} - -### Create Event -POST {{baseUrl}}/api/v1/calendar/events/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "title": "Встреча", - "start": "2026-02-20T10:00:00Z", - "end": "2026-02-20T11:00:00Z", - "description": "Важная встреча", - "location": "Офис" -} - -### Update Event -PATCH {{baseUrl}}/api/v1/calendar/events/1/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "title": "Перенесенная встреча" -} - -### Delete Event -DELETE {{baseUrl}}/api/v1/calendar/events/1/ -Authorization: Bearer {{accessToken}} - -############################################# -# Requests (Заявки) -############################################# - -### List Requests -GET {{baseUrl}}/api/v1/requests/?page=1 -Authorization: Bearer {{accessToken}} - -### Filter Requests by Status -GET {{baseUrl}}/api/v1/requests/?status=pending -Authorization: Bearer {{accessToken}} - -### Get Request by ID -GET {{baseUrl}}/api/v1/requests/1/ -Authorization: Bearer {{accessToken}} - -### Create Request -POST {{baseUrl}}/api/v1/requests/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "title": "Новая заявка в IT", - "description": "Не работает интернет в кабинете 205", - "request_type": "support", - "priority": "high" -} - -### Update Request -PATCH {{baseUrl}}/api/v1/requests/1/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "status": "in_progress" -} - -############################################# -# Notifications -############################################# - -### List Notifications -GET {{baseUrl}}/api/v1/notifications/ -Authorization: Bearer {{accessToken}} - -### Get Unread Notifications Count -GET {{baseUrl}}/api/v1/notifications/unread_count/ -Authorization: Bearer {{accessToken}} - -### Get Notification by ID -GET {{baseUrl}}/api/v1/notifications/1/ -Authorization: Bearer {{accessToken}} - -### Mark Notification as Read -POST {{baseUrl}}/api/v1/notifications/1/mark_as_read/ -Authorization: Bearer {{accessToken}} - -### Mark All Notifications as Read -POST {{baseUrl}}/api/v1/notifications/mark_all_as_read/ -Authorization: Bearer {{accessToken}} - -### Delete Notification -DELETE {{baseUrl}}/api/v1/notifications/1/ -Authorization: Bearer {{accessToken}} - -############################################# -# Groups -############################################# - -### List Groups -GET {{baseUrl}}/api/v1/groups/ -Authorization: Bearer {{accessToken}} - -### Get Group by ID -GET {{baseUrl}}/api/v1/groups/1/ -Authorization: Bearer {{accessToken}} - -### Create Group -POST {{baseUrl}}/api/v1/groups/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "name": "Администраторы", - "description": "Группа администраторов системы" -} - -############################################# -# Skills -############################################# - -### List Skills -GET {{baseUrl}}/api/v1/skills/ -Authorization: Bearer {{accessToken}} - -### Create Skill -POST {{baseUrl}}/api/v1/skills/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "name": "Python", - "category": "programming" -} - -############################################# -# Polls -############################################# - -### List Polls -GET {{baseUrl}}/api/v1/communications/polls/ -Authorization: Bearer {{accessToken}} - -### Get Poll by ID -GET {{baseUrl}}/api/v1/communications/polls/1/ -Authorization: Bearer {{accessToken}} - -### Create Poll -POST {{baseUrl}}/api/v1/communications/polls/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "question": "Где провести корпоратив?", - "options": [ - {"text": "Ресторан"}, - {"text": "Боулинг"}, - {"text": "Квест-комната"} - ], - "multiple_choice": false, - "anonymous": false -} - -### Vote in Poll -POST {{baseUrl}}/api/v1/communications/polls/1/vote/ -Authorization: Bearer {{accessToken}} -Content-Type: {{contentType}} - -{ - "option_id": 1 -} diff --git a/backend/api/auth/serializers.py b/backend/api/auth/serializers.py index 96cd2784..91cec7fd 100644 --- a/backend/api/auth/serializers.py +++ b/backend/api/auth/serializers.py @@ -1,5 +1,6 @@ # api/auth/serializers.py -from api.v1.employees.views import PHONE_FIELD, _normalize_phone +from api.v1.employees.views._helpers import PHONE_FIELD +from employees.utils import _normalize_phone from django.contrib.auth import get_user_model from employees.models import Employee from rest_framework import serializers diff --git a/backend/templates/includes/rightbar_calendar.html b/backend/templates/includes/rightbar_calendar.html deleted file mode 100644 index a7daf8ad..00000000 --- a/backend/templates/includes/rightbar_calendar.html +++ /dev/null @@ -1,17 +0,0 @@ -{% load static %} -{# === Правая колонка с календарём - рефакторированная версия === #} - -{# Десктопная версия #} -{% include "includes/components/calendar_desktop.html" %} - -{# Мобильная версия #} -{% include "includes/components/calendar_mobile.html" %} - -{# Модальные окна #} -{% include "includes/components/calendar_modal_create.html" %} -{% include "includes/components/calendar_modal_details.html" %} -{% include "includes/components/calendar_modal_manage.html" %} -{% include "includes/components/calendar_context_menu.html" %} - -{# JavaScript инициализация #} -{% include "includes/components/calendar_scripts.html" %} diff --git a/backend/test_calendar_frontend.html b/backend/test_calendar_frontend.html deleted file mode 100644 index 69baf8c9..00000000 --- a/backend/test_calendar_frontend.html +++ /dev/null @@ -1,758 +0,0 @@ - - - - - - Calendar Frontend Test - - - - - - - - -
-

🧪 Calendar Frontend Test Suite

- - -
-
-

🎮 Test Controls

-
- - -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
- -
-
-
- - -
-
-

📊 Test Results

-
- Ready -
-
- -
- - - - - - - - - - - - - - -
TestStatusDurationDetails
No tests run yet
-
-
- - -
-
-

📅 Calendar UI Demo

- -
- -
-
-
My Calendars
-
-
-
- Loading... -
-

Loading calendars...

-
-
-
-
-
-
-
-
- - -
-
-

📝 Console Log

- -
-
-
-
- - - - - - - - - - - - diff --git a/backend/test_calendar_frontend_simple.html b/backend/test_calendar_frontend_simple.html deleted file mode 100644 index 4c7b072a..00000000 --- a/backend/test_calendar_frontend_simple.html +++ /dev/null @@ -1,720 +0,0 @@ - - - - - - Calendar Frontend Test - Simple Version - - - - -
-

🧪 Calendar Frontend Test Suite (Offline Version)

- - -
-
-

🎮 Test Controls

-
- - -
-
- -
- - - - - - -
-
- - -
-
-

📊 Test Results

-
- Ready -
-
- - - - - - - - - - - - - - - -
TestStatusDurationDetails
No tests run yet
-
- - -
-
-

📅 Calendar List Demo

- -
- -
-
Loading calendars...
-
-
- - -
-
-

📝 Console Log

- -
-
-
-
- - - - diff --git a/backend/test_logging.py b/backend/test_logging.py deleted file mode 100644 index 80da771d..00000000 --- a/backend/test_logging.py +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env python -"""Тест модернизированной системы логирования.""" - -import os -import sys - -# Добавляем backend в путь -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'eusrr_backend.settings') - -import django -django.setup() - -import logging - -# Тестируем разные логгеры -print("=" * 70) -print("ТЕСТ МОДЕРНИЗИРОВАННОЙ СИСТЕМЫ ЛОГИРОВАНИЯ") -print("=" * 70) - -# 1. Логгер employees -print("\n1. Тест логгера 'employees':") -employees_logger = logging.getLogger('employees') -employees_logger.debug('Тестовое DEBUG сообщение') -employees_logger.info('Тестовое INFO сообщение') -employees_logger.warning('Тестовое WARNING сообщение') -employees_logger.error('Тестовое ERROR сообщение') - -# 2. Логгер security -print("\n2. Тест логгера 'employees.ldap' (security):") -ldap_logger = logging.getLogger('employees.ldap') -ldap_logger.info('LDAP аутентификация успешна', extra={'username': 'test_user'}) -ldap_logger.warning('LDAP подключение медленное', extra={'latency': '1000ms'}) - -# 3. Логгер с исключением -print("\n3. Тест логирования исключений:") -try: - result = 10 / 0 -except ZeroDivisionError: - employees_logger.exception('Тестовое исключение для демонстрации трассировки') - -# 4. Django логгер -print("\n4. Тест Django логгера:") -django_logger = logging.getLogger('django') -django_logger.info('Django логгер работает') - -print("\n" + "=" * 70) -print("✓ Все логгеры протестированы!") -print("=" * 70) - -# Проверяем созданные файлы -logs_dir = os.path.join(os.path.dirname(__file__), 'logs') -if os.path.exists(logs_dir): - log_files = [f for f in os.listdir(logs_dir) if f.endswith('.log')] - print(f"\n📁 Найдено файлов логов: {len(log_files)}") - for log_file in sorted(log_files): - file_path = os.path.join(logs_dir, log_file) - file_size = os.path.getsize(file_path) - print(f" - {log_file:20s} ({file_size:,} bytes)") -else: - print("\n⚠ Директория logs/ не найдена") - -print("\n💡 Проверьте содержимое файлов:") -print(" tail -f backend/logs/all.log") -print(" tail -f backend/logs/error.log") -print(" tail -f backend/logs/security.log") diff --git a/backend/test_message_optimization.py b/backend/test_message_optimization.py deleted file mode 100644 index c8da3461..00000000 --- a/backend/test_message_optimization.py +++ /dev/null @@ -1,85 +0,0 @@ -""" -Тестовый скрипт для проверки оптимизации уведомлений о сообщениях. - -Запуск: - cd backend - ../.venv/Scripts/python test_message_optimization.py -""" - -import os -import django -import time - -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'eusrr_backend.settings') -django.setup() - -from communications.models import Chat, Message -from django.contrib.auth import get_user_model - -User = get_user_model() - - -def test_message_creation_speed(): - """Тест скорости создания сообщения.""" - - print("=" * 60) - print("ТЕСТ: Скорость создания сообщения в групповом чате") - print("=" * 60) - - # Находим групповой чат с участниками - chat = Chat.objects.filter(type='group').first() - - if not chat: - print("❌ Групповой чат не найден") - return - - participants_count = chat.participants.count() - print(f"📊 Чат: {chat.name or chat.id}") - print(f"📊 Участников: {participants_count}") - - # Находим автора (первого участника) - author = chat.participants.first() - - if not author: - print("❌ Участники не найдены") - return - - print(f"📊 Автор: {author.get_full_name()}") - print() - - # Создаем тестовое сообщение и замеряем время - start_time = time.time() - - message = Message.objects.create( - chat=chat, - author=author, - content=f"Тестовое сообщение для проверки оптимизации (время: {time.time()})" - ) - - end_time = time.time() - elapsed_ms = (end_time - start_time) * 1000 - - print(f"✅ Сообщение создано: ID={message.id}") - print(f"⏱️ Время создания: {elapsed_ms:.2f}ms") - print() - - # Оценка результата - if elapsed_ms < 50: - print("🎉 ОТЛИЧНО! Время < 50ms - оптимизация работает!") - elif elapsed_ms < 100: - print("✅ ХОРОШО! Время < 100ms - приемлемо") - elif elapsed_ms < 200: - print("⚠️ СРЕДНЕ: Время < 200ms - можно улучшить") - else: - print(f"❌ МЕДЛЕННО: {elapsed_ms:.2f}ms - проверьте Celery worker") - - print() - print("Рекомендации:") - print(" 1. Убедитесь что Celery worker запущен") - print(" 2. Проверьте логи worker'а: sudo tail -f /var/log/celery/worker.log") - print(" 3. На продакшене время должно быть < 50ms") - print() - - -if __name__ == '__main__': - test_message_creation_speed() diff --git a/backend/test_refactored_notifications.py b/backend/test_refactored_notifications.py deleted file mode 100644 index 63a5ce3c..00000000 --- a/backend/test_refactored_notifications.py +++ /dev/null @@ -1,55 +0,0 @@ -""" -Тест рефакторенных уведомлений -""" -import os -import django - -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'eusrr_backend.settings') -django.setup() - -try: - # Импорты задач - from notifications.tasks import ( - process_message_notifications_task, - process_event_notifications_task, - process_post_notifications_task, - process_request_notifications_task, - process_document_notifications_task, - ) - - # Импорты процессоров - from notifications.task_base import ( - BaseNotificationProcessor, - MessageNotificationProcessor, - EventNotificationProcessor, - PostNotificationProcessor, - RequestNotificationProcessor, - DocumentNotificationProcessor, - truncate_text, - ) - - print("✅ Все импорты успешны!") - print(f"✅ process_message_notifications_task: {process_message_notifications_task}") - print(f"✅ process_event_notifications_task: {process_event_notifications_task}") - print(f"✅ process_post_notifications_task: {process_post_notifications_task}") - print(f"✅ process_request_notifications_task: {process_request_notifications_task}") - print(f"✅ process_document_notifications_task: {process_document_notifications_task}") - print(f"✅ BaseNotificationProcessor: {BaseNotificationProcessor}") - print(f"✅ MessageNotificationProcessor: {MessageNotificationProcessor}") - print(f"✅ EventNotificationProcessor: {EventNotificationProcessor}") - print(f"✅ PostNotificationProcessor: {PostNotificationProcessor}") - print(f"✅ RequestNotificationProcessor: {RequestNotificationProcessor}") - print(f"✅ DocumentNotificationProcessor: {DocumentNotificationProcessor}") - print(f"✅ truncate_text: {truncate_text}") - - # Тест функции truncate_text - assert truncate_text("Short text", 100) == "Short text" - assert truncate_text("Very long text that should be truncated", 10) == "Very long..." - print("✅ truncate_text работает корректно") - - print("\n🎉 Рефакторинг завершён успешно!") - -except Exception as e: - print(f"❌ Ошибка: {e}") - import traceback - traceback.print_exc() diff --git a/backend/test_reply.bat b/backend/test_reply.bat deleted file mode 100644 index 9e0b76b6..00000000 --- a/backend/test_reply.bat +++ /dev/null @@ -1,61 +0,0 @@ -@echo off -REM Скрипт для проверки функции ответа на сообщение (Windows) -REM Запускает сервер и открывает тестовую страницу - -echo. -echo 🧪 Проверка функции ответа на сообщение -echo ======================================== -echo. - -REM Проверка виртуального окружения -if not exist ".venv" ( - echo ❌ Виртуальное окружение не найдено - echo Создайте его командой: python -m venv .venv - exit /b 1 -) - -echo ✅ Виртуальное окружение найдено -echo. - -REM Проверка Python в виртуальном окружении -if exist ".venv\Scripts\python.exe" ( - .venv\Scripts\python.exe --version - echo ✅ Python найден в виртуальном окружении -) else ( - echo ❌ Python не найден в .venv\Scripts\ - exit /b 1 -) - -echo. -echo 📊 Что будет проверено: -echo 1. Контекстное меню -^> Ответить -echo 2. ChatFormManager (режим reply) -echo 3. Превью ответа в сообщениях -echo 4. Загрузка реакций (дефолтные) -echo. - -echo 🚀 Запуск Django сервера... -echo. - -REM Запуск сервера -start "Django Server" .venv\Scripts\python.exe manage.py runserver 9000 - -echo ⏳ Ожидание запуска сервера (5 сек)... -timeout /t 5 /nobreak > nul - -echo. -echo ✅ Сервер запущен! -echo. -echo 📝 Откройте в браузере: -echo http://localhost:9000/test_reply_functionality.html -echo. -echo 🌐 Или нажмите Enter чтобы открыть автоматически... -pause - -REM Открыть браузер -start http://localhost:9000/test_reply_functionality.html - -echo. -echo Закройте окно "Django Server" чтобы остановить сервер -echo. -pause diff --git a/backend/test_reply.sh b/backend/test_reply.sh deleted file mode 100644 index 24d8977f..00000000 --- a/backend/test_reply.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash - -# Скрипт для проверки функции ответа на сообщение -# Запускает сервер и открывает тестовую страницу - -echo "🧪 Проверка функции ответа на сообщение" -echo "========================================" -echo "" - -# Проверка виртуального окружения -if [ ! -d ".venv" ]; then - echo "❌ Виртуальное окружение не найдено" - echo "Создайте его командой: python -m venv .venv" - exit 1 -fi - -echo "✅ Виртуальное окружение найдено" - -# Активация виртуального окружения (Windows) -if [ -f ".venv/Scripts/activate" ]; then - source .venv/Scripts/activate - echo "✅ Виртуальное окружение активировано" -else - echo "⚠️ Не удалось активировать виртуальное окружение" -fi - -# Проверка Python -python_version=$(python --version 2>&1) -echo "Python: $python_version" - -echo "" -echo "📊 Что будет проверено:" -echo " 1. Контекстное меню -> Ответить" -echo " 2. ChatFormManager (режим reply)" -echo " 3. Превью ответа в сообщениях" -echo " 4. Загрузка реакций (дефолтные)" -echo "" - -# Запуск сервера -echo "🚀 Запуск Django сервера..." -echo "" -python manage.py runserver 9000 & -SERVER_PID=$! - -echo "Server PID: $SERVER_PID" -echo "" -echo "⏳ Ожидание запуска сервера (5 сек)..." -sleep 5 - -echo "" -echo "✅ Сервер запущен!" -echo "" -echo "📝 Откройте в браузере:" -echo " http://localhost:9000/test_reply_functionality.html" -echo "" -echo "Нажмите Ctrl+C для остановки сервера" -echo "" - -# Ожидание завершения -wait $SERVER_PID diff --git a/backend/test_reply_functionality.html b/backend/test_reply_functionality.html deleted file mode 100644 index 644700f8..00000000 --- a/backend/test_reply_functionality.html +++ /dev/null @@ -1,344 +0,0 @@ - - - - - - Тест функции ответа на сообщение - - - - - -
-

🧪 Тест функции ответа на сообщение

- - -
-

📊 Статус загрузки реакций

-
- Проверка... -
-
- - -
-

1️⃣ Тест контекстного меню

-

Симуляция вызова функции ответа из контекстного меню

- -
-
- Иван Петров · 10:30 -
-
- Привет! Как дела с проектом? -
-
- - - -
-
- - -
-

2️⃣ Тест ChatFormManager

-

Проверка состояния формы при ответе

- -
- - -
- -
- -
- - - -
- -
- Состояние формы: -
{ "mode": "send" }
-
-
- - -
-

3️⃣ Тест превью ответа в сообщении

-

Проверка рендеринга превью при ответе на сообщение

- -
-
-
- - Мария Иванова -
-
- Давайте встретимся завтра -
-
- -
- Отлично, в какое время удобно? -
-
- - -
- - -
-

📝 Журнал событий

- -
-
-
- - - - - diff --git a/backend/test_toast_calendar.html b/backend/test_toast_calendar.html deleted file mode 100644 index e69de29b..00000000 diff --git a/backend/test_v2_autoscroll.html b/backend/test_v2_autoscroll.html deleted file mode 100644 index 94e2cfbd..00000000 --- a/backend/test_v2_autoscroll.html +++ /dev/null @@ -1,313 +0,0 @@ - - - - - - Test ChatControllerV2 - Smart Autoscroll - - - -

ChatControllerV2 - Smart Autoscroll Test

- -
- - - - - -
- -
- -
-

Результаты тестов:

-
Нажмите "Запустить все тесты"
-
- -
- - - - diff --git a/docs/in-progress/FRONTEND_MIGRATION_COMPLETE.md b/docs/in-progress/FRONTEND_MIGRATION_COMPLETE.md deleted file mode 100644 index 2981b543..00000000 --- a/docs/in-progress/FRONTEND_MIGRATION_COMPLETE.md +++ /dev/null @@ -1,135 +0,0 @@ -# Миграция фронтенда на django-scheduler - Завершено - -**Дата**: 24 февраля 2026 -**Ветка**: `feature/django-scheduler-integration` -**Коммит**: `ee657b1` - -## ✅ Что сделано - -### 1. Обновлены API endpoints -- `apiUrls.js`: Все endpoints заменены с `/api/v1/calendar/` на `/api/v1/schedule/` -- Добавлены новые endpoints: `RULES`, `OCCURRENCES`, `RELATIONS` -- Удалены deprecated: `SUBSCRIPTIONS`, `CALENDAR_SUBSCRIBE`, `CALENDAR_INVITE` - -### 2. Упрощена архитектура -- **Убрана концепция subscriptions**: django-scheduler не имеет подписок -- **Видимость календарей**: Теперь хранится только в localStorage (для всех календарей) -- **Права доступа**: Управляются на уровне django-scheduler через permissions - -### 3. Адаптирован backend serializer -- `CalendarSerializer` теперь принимает поле `title` (для совместимости с EUSRR) -- Автогенерация `slug` из `name/title` если не указан -- Поддержка старых и новых форматов данных - -### 4. Обновлены компоненты -- `calendarsApi.js`: Удалены функции `subscribeToCalendar()`, `unsubscribeFromCalendar()`, `inviteUserToCalendar()`, `getMySubscriptions()`, `updateSubscription()` -- `calendarManager.js`: Упрощена логика `toggleCalendarVisibility()`, `subscribe()`, `unsubscribe()` - теперь работают только с localStorage - -## 📝 Изменения в файлах - -### Modified -1. `backend/api/v1/schedule/serializers.py` - добавлена совместимость с EUSRR API -2. `backend/static/js/constants/apiUrls.js` - обновлены все endpoints -3. `backend/static/js/api/calendarsApi.js` - удалены subscriptions функции -4. `backend/static/js/components/calendarManager.js` - упрощена логика видимости - -## 🧪 Тестирование - -### 1. Запуск сервера -```bash -cd /c/Users/igor_/Dev/EUSRR/backend -/c/Users/igor_/Dev/EUSRR/.venv/Scripts/python manage.py runserver -``` - -### 2. Создание тестового календаря -```python -from schedule.models import Calendar, Event -from datetime import datetime, timedelta -import pytz - -# Создать календарь -cal = Calendar.objects.create( - name="Тестовый календарь", - slug="test-calendar" -) - -# Создать событие -tz = pytz.timezone('Europe/Moscow') -now = datetime.now(tz) - -Event.objects.create( - title="Тестовое событие", - description="Проверка django-scheduler", - start=now + timedelta(hours=1), - end=now + timedelta(hours=2), - calendar=cal, - color_event="#3498db" -) -``` - -### 3. Проверка через API -```bash -# Список календарей -curl http://localhost:8000/api/v1/schedule/calendars/ - -# Список событий -curl http://localhost:8000/api/v1/schedule/events/ - -# События с фильтрацией -curl "http://localhost:8000/api/v1/schedule/events/?calendar=1&start=2026-02-01T00:00:00Z&end=2026-03-01T00:00:00Z" -``` - -### 4. Проверка фронтенда -1. Открыть https://corp.robotail.pro (или localhost:8000) -2. Авторизоваться -3. Проверить правую панель календаря -4. Создать тестовый календарь через модальное окно -5. Создать событие -6. Переключить видимость календаря (должно сохраняться в localStorage) - -## 🔄 Совместимость - -### Что работает как раньше: -- ✅ Создание/редактирование/удаление календарей -- ✅ Создание/редактирование/удаление событий -- ✅ Просмотр событий в FullCalendar -- ✅ Переключение видимости календарей -- ✅ Legacy календари (Компания, Личный, Отделы) - -### Что изменилось: -- ⚠️ Нет API подписок (subscribe/unsubscribe) - используется localStorage -- ⚠️ Нет API приглашений (invite) - нужно реализовать через EventRelation если требуется -- ⚠️ Права доступа теперь управляются на уровне django-scheduler permissions - -### Что добавилось: -- ✨ Поддержка recurring events через RFC 5545 rrule (Rules) -- ✨ Материализованные вхождения повторяющихся событий (Occurrences) -- ✨ Связи событий (EventRelation) для управления участниками -- ✨ Timezone-aware события из коробки - -## 📊 Статистика изменений - -**Коммит**: ee657b1 -- **4 файла изменено** -- **+91 строк добавлено** -- **-342 строк удалено** -- **Чистый результат**: -251 строка (упрощение кода) - -## 🚀 Следующие шаги - -1. **Тестирование**: Проверить все функции календаря на dev окружении -2. **Миграция данных**: Создать скрипт переноса календарей/событий из `calendar_app` в `schedule` -3. **Документация**: Обновить пользовательскую документацию -4. **Удаление старого кода**: После успешного тестирования удалить `calendar_app` - -## 📌 Полезные ссылки - -- [API v1/schedule Guide](./SCHEDULE_API_V1_GUIDE.md) -- [Django Scheduler Integration Plan](./DJANGO_SCHEDULER_INTEGRATION.md) -- [Django Scheduler Docs](https://github.com/thauber/django-scheduler) -- [RFC 5545 (iCalendar)](https://datatracker.ietf.org/doc/html/rfc5545) - ---- - -**Автор**: GitHub Copilot -**Ревью**: Требуется diff --git a/newman_test_results.txt b/newman_test_results.txt deleted file mode 100644 index 4b6546d3..00000000 --- a/newman_test_results.txt +++ /dev/null @@ -1,208 +0,0 @@ -(node:37944) [DEP0176] DeprecationWarning: fs.F_OK is deprecated, use fs.constants.F_OK instead -(Use `node --trace-deprecation ...` to show where the warning was created) -newman - -EUSRR API - -□ Authentication -└ Login (Obtain Token) - POST http://localhost:8000/api/auth/token/ [200 OK, 748B, 190ms] - ┌ - │ 'Access token saved: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXV - │ CJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzcxMzI5OD - │ IzLCJpYXQiOjE3NzEzMjgwMjMsImp0aSI6IjI5NzZiNzMyZjdmMjQx - │ OTY4NTIwYzNlOTUxN2FjNDY1IiwidXNlcl9pZCI6IjEifQ._B6LK9J - │ KQnjyNoVCh14QJBhPn7VV18YIR2XpQtDEMIs' - └ - -└ Refresh Token - POST http://localhost:8000/api/auth/token/refresh/ [200 OK, 503B, 14ms] - ┌ - │ 'Access token refreshed: eyJhbGciOiJIUzI1NiIsInR5cCI6I - │ kpXVCJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNzcxMz - │ I5ODIzLCJpYXQiOjE3NzEzMjgwMjMsImp0aSI6IjE1MGZjZTZlZWQ0 - │ MjRjMWY4NWViZmNmYzUyODA3YTFjIiwidXNlcl9pZCI6IjEifQ.4km - │ qq0EskSsrG3TLexrABC2MAM2IFDNy0XVIyVwdcK4' - └ - -└ Register - POST http://localhost:8000/api/v1/auth/register/ [400 Bad Request, 372B, 14ms] - -□ Employees -└ List Employees - GET http://localhost:8000/api/v1/employees/?page=1 [200 OK, 1.1kB, 30ms] - -└ Get Employee by ID - GET http://localhost:8000/api/v1/employees/1/ [200 OK, 1.47kB, 32ms] - -└ Update Employee - PATCH http://localhost:8000/api/v1/employees/1/ [200 OK, 745B, 37ms] - -└ Get Current User (Me) - GET http://localhost:8000/api/v1/employees/me/ [200 OK, 15.07kB, 39ms] - -└ Update My Profile - PATCH http://localhost:8000/api/v1/employees/me/ [200 OK, 15.09kB, 41ms] - -└ Create Employee - POST http://localhost:8000/api/v1/employees/ [400 Bad Request, 477B, 17ms] - -└ Delete Employee - DELETE http://localhost:8000/api/v1/employees/2/ [404 Not Found, 339B, 16ms] - -└ Add Skill to Employee - POST http://localhost:8000/api/v1/employees/1/add_skill/ [200 OK, 302B, 26ms] - -└ Remove Skill from Employee - POST http://localhost:8000/api/v1/employees/1/remove_skill/ [200 OK, 304B, 28ms] - -└ Get Employee LDAP Info - GET http://localhost:8000/api/v1/employees/1/ldap-info/ [404 Not Found, 343B, 29ms] - -└ Export Employees to Excel - GET http://localhost:8000/api/v1/employees/export-excel/ [200 OK, 6.21kB, 31ms] - -□ Departments -└ List Departments - GET http://localhost:8000/api/v1/departments/ [200 OK, 321B, 17ms] - -└ Get Department by ID - GET http://localhost:8000/api/v1/departments/1/ [404 Not Found, 341B, 19ms] - -└ Create Department - POST http://localhost:8000/api/v1/departments/ [500 Internal Server Error, 122.17kB, 49ms] - -□ Documents -└ List Documents - GET http://localhost:8000/api/v1/documents/?page=1 [200 OK, 321B, 14ms] - -└ Get Document by ID - GET http://localhost:8000/api/v1/documents/1/ [404 Not Found, 339B, 20ms] - -└ Create Document - POST http://localhost:8000/api/v1/documents/ [400 Bad Request, 325B, 13ms] - -□ Feed (Posts) -└ List Posts - GET http://localhost:8000/api/v1/posts/?page=1 [200 OK, 321B, 19ms] - -└ Create Post - POST http://localhost:8000/api/v1/posts/ [400 Bad Request, 419B, 15ms] - -└ Like Post - POST http://localhost:8000/api/v1/posts/1/like/ [404 Not Found, 310B, 24ms] - -└ List Comments - GET http://localhost:8000/api/v1/comments/?post=1 [200 OK, 321B, 17ms] - -└ Create Comment - POST http://localhost:8000/api/v1/comments/ [400 Bad Request, 392B, 16ms] - -□ Communications (Messenger) -└ List Chats - GET http://localhost:8000/api/v1/communications/chats/ [200 OK, 321B, 26ms] - -└ Get Chat by ID - GET http://localhost:8000/api/v1/communications/chats/1/ [404 Not Found, 335B, 28ms] - -└ Create Chat - POST http://localhost:8000/api/v1/communications/chats/ [400 Bad Request, 446B, 26ms] - -└ List Messages - GET http://localhost:8000/api/v1/communications/messages/?chat=1 [200 OK, 321B, 20ms] - -└ Send Message - POST http://localhost:8000/api/v1/communications/messages/ [400 Bad Request, 392B, 16ms] - -□ Calendar -└ List Events - GET http://localhost:8000/api/v1/calendar/events/ [200 OK, 262B, 12ms] - -└ Create Event - POST http://localhost:8000/api/v1/calendar/events/ [400 Bad Request, 323B, 15ms] - -└ List Calendars - GET http://localhost:8000/api/v1/calendar/calendars/ [200 OK, 313B, 19ms] - -□ Requests -└ List Requests - GET http://localhost:8000/api/v1/requests/?page=1 [200 OK, 321B, 15ms] - -└ Get Request by ID - GET http://localhost:8000/api/v1/requests/1/ [404 Not Found, 338B, 15ms] - -└ Create Request - POST http://localhost:8000/api/v1/requests/ [400 Bad Request, 325B, 14ms] - -□ Notifications -└ List Notifications - GET http://localhost:8000/api/v1/notifications/ [200 OK, 311B, 18ms] - -└ Mark as Read - POST http://localhost:8000/api/v1/notifications/1/mark_as_read/ [404 Not Found, 118.87kB, 17ms] - -└ Mark All as Read - POST http://localhost:8000/api/v1/notifications/mark_all_as_read/ [404 Not Found, 118.88kB, 18ms] - -□ Employee Actions -└ List Employee Actions - GET http://localhost:8000/api/v1/employee-actions/ [200 OK, 783B, 15ms] - -└ Get Employee Action by ID - GET http://localhost:8000/api/v1/employee-actions/1/ [200 OK, 539B, 17ms] - -└ Create Employee Action - POST http://localhost:8000/api/v1/employee-actions/ [400 Bad Request, 327B, 13ms] - -□ Department Roles -└ List Department Roles - GET http://localhost:8000/api/v1/department-roles/ [200 OK, 321B, 19ms] - -└ Get Role Assignments - GET http://localhost:8000/api/v1/department-roles/1/assignments/ [404 Not Found, 325B, 13ms] - -└ Assign Role to Employee - POST http://localhost:8000/api/v1/department-roles/1/assign/ [404 Not Found, 320B, 17ms] - -□ Advanced Skills -└ Bulk Create Skills - POST http://localhost:8000/api/v1/skills/bulk_create/ [201 Created, 295B, 17ms] - -└ Merge Skills - POST http://localhost:8000/api/v1/skills/merge/ [404 Not Found, 301B, 16ms] - -□ Advanced Groups -└ Get Group Permissions - GET http://localhost:8000/api/v1/groups/1/permissions/ [404 Not Found, 316B, 14ms] - -└ Set Group Permissions - POST http://localhost:8000/api/v1/groups/1/set-permissions/ [404 Not Found, 311B, 17ms] - -└ Get Group Employees - GET http://localhost:8000/api/v1/groups/1/employees/ [404 Not Found, 111.62kB, 15ms] - -└ Add Group Members - POST http://localhost:8000/api/v1/groups/1/add-members/ [404 Not Found, 311B, 14ms] - -└ Remove Group Members - POST http://localhost:8000/api/v1/groups/1/remove-members/ [404 Not Found, 311B, 19ms] - -┌─────────────────────────┬───────────────────┬───────────────────┐ -│ │ executed │ failed │ -├─────────────────────────┼───────────────────┼───────────────────┤ -│ iterations │ 1 │ 0 │ -├─────────────────────────┼───────────────────┼───────────────────┤ -│ requests │ 52 │ 0 │ -├─────────────────────────┼───────────────────┼───────────────────┤ -│ test-scripts │ 2 │ 0 │ -├─────────────────────────┼───────────────────┼───────────────────┤ -│ prerequest-scripts │ 0 │ 0 │ -├─────────────────────────┼───────────────────┼───────────────────┤ -│ assertions │ 0 │ 0 │ -├─────────────────────────┴───────────────────┴───────────────────┤ -│ total run duration: 4.6s │ -├─────────────────────────────────────────────────────────────────┤ -│ total data received: 512.31kB (approx) │ -├─────────────────────────────────────────────────────────────────┤ -│ average response time: 23ms [min: 12ms, max: 190ms, s.d.: 24ms] │ -└─────────────────────────────────────────────────────────────────┘ diff --git a/nginx.conf b/nginx.conf deleted file mode 100644 index dda9dac8..00000000 --- a/nginx.conf +++ /dev/null @@ -1,83 +0,0 @@ -upstream backend { - server web:8000; -} - -upstream frontend { - server frontend:3000; -} - -# WebSocket upgrade handling -map $http_upgrade $connection_upgrade { - default upgrade; - '' close; -} - -server { - listen 80; - server_name localhost; - client_max_body_size 100M; - - # Frontend - Next.js application - location / { - proxy_pass http://frontend; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - } - - # Backend API - location /api/ { - proxy_pass http://backend; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Django Admin - location /admin/ { - proxy_pass http://backend; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # WebSocket connections - location /ws/ { - proxy_pass http://backend; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection $connection_upgrade; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_read_timeout 86400; - } - - # Static files - location /static/ { - alias /app/staticfiles/; - expires 30d; - add_header Cache-Control "public, immutable"; - } - - # Media files - location /media/ { - alias /app/media/; - expires 30d; - } - - # Health check - location /health { - access_log off; - return 200 "healthy\n"; - add_header Content-Type text/plain; - } -} diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 74e84bb8..00000000 --- a/package-lock.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "HiRo", - "lockfileVersion": 3, - "requires": true, - "packages": {} -}