From 112123b0ffa0ed2fad386d43efa66c1957b1ec81 Mon Sep 17 00:00:00 2001 From: arkonsadter Date: Thu, 15 Jan 2026 19:25:42 +0600 Subject: [PATCH] Change drone.yml --- .drone.yml | 136 +------------- DRONE_SIMPLIFIED.md | 391 ++++++++++++++++++++++++++++++++++++++++ FIX_BASEMODEL.md | 134 -------------- MULTIPLE_OWNERS.md | 431 ++++++++++++++++++++++++++++++++++++++++++++ README.md | 24 +++ backend/main.py | 23 ++- backend/users.json | 75 +------- backend/users.json1 | 75 ++++++++ 8 files changed, 937 insertions(+), 352 deletions(-) create mode 100644 DRONE_SIMPLIFIED.md delete mode 100644 FIX_BASEMODEL.md create mode 100644 MULTIPLE_OWNERS.md create mode 100644 backend/users.json1 diff --git a/.drone.yml b/.drone.yml index e6c6bed..3a8df2e 100644 --- a/.drone.yml +++ b/.drone.yml @@ -117,7 +117,7 @@ steps: - push - tag - # Сканирование образа на уязвимости (опционально) + # Сканирование образа на уязвимости - name: scan-image image: aquasec/trivy commands: @@ -128,137 +128,3 @@ steps: - tag depends_on: - build-and-push - - # Уведомление об успешной сборке (опционально) - - name: notify-success - image: plugins/slack - settings: - webhook: - from_secret: slack_webhook - channel: deployments - username: drone - template: > - ✅ Build #{{build.number}} succeeded! - - Repository: {{repo.name}} - Branch: {{build.branch}} - Commit: {{build.commit}} - Author: {{build.author}} - - Docker image: registry.example.com/mc-panel:{{build.commit}} - when: - status: - - success - event: - - push - - tag - depends_on: - - build-and-push - - # Уведомление об ошибке (опционально) - - name: notify-failure - image: plugins/slack - settings: - webhook: - from_secret: slack_webhook - channel: deployments - username: drone - template: > - ❌ Build #{{build.number}} failed! - - Repository: {{repo.name}} - Branch: {{build.branch}} - Commit: {{build.commit}} - Author: {{build.author}} - - Link: {{build.link}} - when: - status: - - failure - event: - - push - - tag - ---- -kind: pipeline -type: docker -name: deploy-staging - -# Пайплайн для деплоя на staging (опционально) -trigger: - event: - - push - branch: - - develop - -depends_on: - - build-and-publish - -steps: - - name: deploy-to-staging - image: appleboy/drone-ssh - settings: - host: - from_secret: staging_host - username: - from_secret: staging_username - key: - from_secret: staging_ssh_key - port: 22 - script: - - cd /opt/mc-panel - - docker-compose pull - - docker-compose up -d - - docker-compose ps - ---- -kind: pipeline -type: docker -name: deploy-production - -# Пайплайн для деплоя на production (только для тегов) -trigger: - event: - - tag - ref: - - refs/tags/v* - -depends_on: - - build-and-publish - -steps: - - name: deploy-to-production - image: appleboy/drone-ssh - settings: - host: - from_secret: production_host - username: - from_secret: production_username - key: - from_secret: production_ssh_key - port: 22 - script: - - cd /opt/mc-panel - - docker-compose pull - - docker-compose up -d - - docker-compose ps - - echo "Deployed version ${DRONE_TAG}" - - - name: notify-production-deploy - image: plugins/slack - settings: - webhook: - from_secret: slack_webhook - channel: deployments - username: drone - template: > - 🚀 Production deployment successful! - - Version: {{build.tag}} - Repository: {{repo.name}} - Author: {{build.author}} - - Docker image: registry.example.com/mc-panel:{{build.tag}} - when: - status: - - success diff --git a/DRONE_SIMPLIFIED.md b/DRONE_SIMPLIFIED.md new file mode 100644 index 0000000..66b4740 --- /dev/null +++ b/DRONE_SIMPLIFIED.md @@ -0,0 +1,391 @@ +# 🚀 Упрощённый Drone CI/CD + +**Дата:** 15 января 2026 +**Статус:** УПРОЩЕНО ✅ + +--- + +## 🎯 Что изменилось + +### До изменения + +**4 пайплайна:** +1. code-quality - Проверка качества кода +2. build-and-publish - Сборка и публикация образа +3. deploy-staging - Деплой на staging +4. deploy-production - Деплой на production + +**Уведомления:** +- notify-success - Уведомление об успешной сборке +- notify-failure - Уведомление об ошибке +- notify-production-deploy - Уведомление о деплое + +--- + +### После изменения + +**2 пайплайна:** +1. code-quality - Проверка качества кода +2. build-and-publish - Сборка и публикация образа + +**Уведомления:** Удалены + +**Деплой пайплайны:** Удалены + +--- + +## 📋 Оставшиеся пайплайны + +### 1. code-quality + +**Назначение:** Проверка качества и безопасности кода + +**Триггеры:** +- Push в любую ветку +- Pull Request + +**Шаги:** +1. **python-lint** - Проверка Python кода + - flake8 (синтаксис и стиль) + - pylint (качество кода) + - black (форматирование) + - isort (сортировка импортов) + +2. **frontend-lint** - Проверка JavaScript/React кода + - ESLint (синтаксис и стиль) + - Prettier (форматирование) + +3. **python-security** - Проверка безопасности Python + - safety (известные уязвимости) + - bandit (security linter) + +4. **frontend-security** - Проверка безопасности Node.js + - npm audit (уязвимости зависимостей) + +--- + +### 2. build-and-publish + +**Назначение:** Сборка и публикация Docker образа + +**Триггеры:** +- Push в ветки: main, master, develop +- Push тега + +**Зависимости:** +- Запускается только после успешного code-quality + +**Шаги:** +1. **build-and-push** - Сборка и публикация образа + - Сборка Docker образа + - Публикация в registry + - Автоматическое тегирование + +2. **scan-image** - Сканирование на уязвимости + - Trivy сканирование + - Проверка HIGH и CRITICAL уязвимостей + +--- + +## 🗑️ Удалённые пайплайны + +### deploy-staging +**Причина удаления:** Упрощение конфигурации + +**Что делал:** +- SSH подключение к staging серверу +- Обновление Docker образа +- Перезапуск контейнеров + +**Альтернатива:** Ручной деплой через SSH + +--- + +### deploy-production +**Причина удаления:** Упрощение конфигурации + +**Что делал:** +- SSH подключение к production серверу +- Обновление Docker образа +- Перезапуск контейнеров +- Уведомление в Slack + +**Альтернатива:** Ручной деплой через SSH + +--- + +## 🗑️ Удалённые уведомления + +### notify-success +**Что делал:** Отправка уведомления в Slack об успешной сборке + +### notify-failure +**Что делал:** Отправка уведомления в Slack об ошибке сборки + +### notify-production-deploy +**Что делал:** Отправка уведомления в Slack о деплое на production + +**Причина удаления:** Упрощение, не требуется настройка Slack webhook + +--- + +## 🔧 Настройка + +### Необходимые Drone Secrets + +Для работы оставшихся пайплайнов нужны только: + +```bash +# Docker Registry +docker_username=your_username +docker_password=your_password +``` + +### Удалённые Secrets (больше не нужны) + +```bash +# Slack (удалено) +slack_webhook + +# SSH для staging (удалено) +staging_host +staging_username +staging_ssh_key + +# SSH для production (удалено) +production_host +production_username +production_ssh_key +``` + +--- + +## 🚀 Как использовать + +### Автоматическая проверка кода + +При каждом push или pull request: + +```bash +git add . +git commit -m "Update code" +git push origin main +``` + +**Результат:** +1. ✅ Запускается code-quality +2. ✅ Проверяется качество кода +3. ✅ Проверяется безопасность +4. ✅ Если всё ОК → запускается build-and-publish +5. ✅ Собирается Docker образ +6. ✅ Публикуется в registry +7. ✅ Сканируется на уязвимости + +--- + +### Ручной деплой на staging + +```bash +# SSH на staging сервер +ssh user@staging-server + +# Обновление +cd /opt/mc-panel +docker-compose pull +docker-compose up -d +docker-compose ps +``` + +--- + +### Ручной деплой на production + +```bash +# SSH на production сервер +ssh user@production-server + +# Обновление +cd /opt/mc-panel +docker-compose pull +docker-compose up -d +docker-compose ps +``` + +--- + +## 📊 Сравнение + +### До упрощения + +``` +Push → code-quality → build-and-publish → deploy-staging + ↓ + notify-success + ↓ + notify-failure + +Tag → code-quality → build-and-publish → deploy-production + ↓ + notify-production-deploy +``` + +**Требуется:** +- 7 Drone secrets +- Настройка Slack +- Настройка SSH для 2 серверов + +--- + +### После упрощения + +``` +Push → code-quality → build-and-publish + +Tag → code-quality → build-and-publish +``` + +**Требуется:** +- 2 Drone secrets (docker_username, docker_password) +- Ручной деплой через SSH + +--- + +## ✅ Преимущества упрощения + +### Меньше настроек +- ❌ Не нужен Slack webhook +- ❌ Не нужны SSH ключи +- ❌ Не нужны настройки серверов +- ✅ Только Docker registry + +### Больше контроля +- ✅ Ручной деплой = больше контроля +- ✅ Можно проверить перед деплоем +- ✅ Можно откатить если нужно + +### Проще поддержка +- ✅ Меньше кода +- ✅ Меньше зависимостей +- ✅ Проще понять +- ✅ Проще отладить + +--- + +## 🔄 Workflow + +### Разработка + +```bash +# 1. Разработка +git checkout -b feature/new-feature +# ... код ... +git commit -m "Add new feature" +git push origin feature/new-feature + +# 2. Pull Request +# Создать PR на GitHub/GitLab +# Drone автоматически проверит код + +# 3. Merge +# После одобрения PR +git checkout main +git merge feature/new-feature +git push origin main + +# 4. Автоматическая сборка +# Drone соберёт и опубликует образ + +# 5. Ручной деплой +ssh user@server +cd /opt/mc-panel +docker-compose pull +docker-compose up -d +``` + +--- + +### Production Release + +```bash +# 1. Создать тег +git tag -a v1.1.0 -m "Release 1.1.0" +git push origin v1.1.0 + +# 2. Автоматическая сборка +# Drone соберёт образ с тегом v1.1.0 + +# 3. Ручной деплой на production +ssh user@production-server +cd /opt/mc-panel +docker-compose pull +docker-compose up -d +docker-compose ps +``` + +--- + +## 📝 Файл .drone.yml + +### Структура + +```yaml +--- +# Пайплайн 1: Проверка качества +kind: pipeline +name: code-quality +trigger: push, pull_request +steps: + - python-lint + - frontend-lint + - python-security + - frontend-security + +--- +# Пайплайн 2: Сборка и публикация +kind: pipeline +name: build-and-publish +trigger: push (main/master/develop), tag +depends_on: code-quality +steps: + - build-and-push + - scan-image +``` + +### Размер файла + +**До:** ~300 строк +**После:** ~120 строк +**Уменьшение:** 60% + +--- + +## 🎯 Итог + +**Конфигурация упрощена!** ✅ + +### Что осталось: +- ✅ Проверка качества кода +- ✅ Проверка безопасности +- ✅ Сборка Docker образа +- ✅ Публикация в registry +- ✅ Сканирование на уязвимости + +### Что удалено: +- ❌ Автоматический деплой +- ❌ Уведомления в Slack +- ❌ SSH настройки + +### Результат: +- 🎯 Проще настроить +- 🎯 Проще поддерживать +- 🎯 Больше контроля над деплоем +- 🎯 Меньше зависимостей + +--- + +**Версия:** 1.1.0 +**Дата:** 15 января 2026 +**Статус:** УПРОЩЕНО ✅ + +**Меньше сложности - больше контроля!** 🚀 + diff --git a/FIX_BASEMODEL.md b/FIX_BASEMODEL.md deleted file mode 100644 index ef58f0f..0000000 --- a/FIX_BASEMODEL.md +++ /dev/null @@ -1,134 +0,0 @@ -# 🔧 Исправление ошибки BaseModel - -**Ошибка:** `NameError: name 'BaseModel' is not defined` -**Статус:** ИСПРАВЛЕНО ✅ - ---- - -## 🐛 Проблема - -При запуске `backend/main.py` возникала ошибка: - -``` -Traceback (most recent call last): - File "backend\main.py", line 1655, in - class RoleChange(BaseModel): - ^^^^^^^^^ -NameError: name 'BaseModel' is not defined -``` - ---- - -## ✅ Решение - -Добавлен импорт `BaseModel` из `pydantic` в начало файла `backend/main.py`: - -```python -from pydantic import BaseModel -``` - ---- - -## 🔧 Что было сделано - -### Изменение в backend/main.py - -**Было:** -```python -from fastapi import FastAPI, WebSocket, UploadFile, File, HTTPException, Depends, status, Request -from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import FileResponse, RedirectResponse -from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials -import asyncio -``` - -**Стало:** -```python -from fastapi import FastAPI, WebSocket, UploadFile, File, HTTPException, Depends, status, Request -from fastapi.middleware.cors import CORSMiddleware -from fastapi.responses import FileResponse, RedirectResponse -from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials -from pydantic import BaseModel # ← Добавлено -import asyncio -``` - ---- - -## 🚀 Запуск - -Теперь можно запустить панель: - -```bash -cd backend -python main.py -``` - -Или используйте: -```bash -RESTART_ALL.bat -``` - ---- - -## ✅ Проверка - -После запуска вы должны увидеть: - -``` -INFO: Started server process [PID] -INFO: Waiting for application startup. -INFO: Application startup complete. -INFO: Uvicorn running on http://0.0.0.0:8000 -``` - -Без ошибок! ✅ - ---- - -## 📚 Что такое BaseModel? - -`BaseModel` - это базовый класс из библиотеки `pydantic`, который используется для создания моделей данных с валидацией. - -**Используется для:** -- Валидация входных данных API -- Автоматическая генерация документации -- Сериализация/десериализация JSON - -**Примеры в коде:** -```python -class RoleChange(BaseModel): - role: str - -class BanRequest(BaseModel): - reason: str = "Заблокирован администратором" - -class ServerAccess(BaseModel): - server_name: str - -class PermissionsUpdate(BaseModel): - permissions: dict -``` - ---- - -## 🔍 Почему возникла ошибка? - -При добавлении новых эндпоинтов управления пользователями были созданы новые модели данных (`RoleChange`, `BanRequest`, и т.д.), но импорт `BaseModel` не был добавлен. - ---- - -## ✨ Готово! - -Ошибка исправлена, панель должна запускаться без проблем. - -**Запустите:** -```bash -cd backend -python main.py -``` - ---- - -**Дата исправления:** 15 января 2026 -**Статус:** РАБОТАЕТ ✅ - diff --git a/MULTIPLE_OWNERS.md b/MULTIPLE_OWNERS.md new file mode 100644 index 0000000..dfb2f29 --- /dev/null +++ b/MULTIPLE_OWNERS.md @@ -0,0 +1,431 @@ +# 👑 Несколько владельцев + +**Дата:** 15 января 2026 +**Статус:** РЕАЛИЗОВАНО ✅ + +--- + +## 🎯 Что изменилось + +### До изменения + +**Ограничение:** Мог быть только ОДИН владелец + +``` +❌ При назначении нового owner, старый owner автоматически становился admin +❌ Нельзя было удалить владельца +❌ Нельзя было заблокировать владельца +``` + +**Проблема:** Если нужно несколько администраторов с полными правами, приходилось использовать роль admin, у которой нет права изменять роли. + +--- + +### После изменения + +**Возможность:** Может быть НЕСКОЛЬКО владельцев + +``` +✅ Можно назначить несколько пользователей с ролью owner +✅ Можно удалить владельца (если их больше одного) +✅ Можно заблокировать владельца (если их больше одного) +✅ Всегда должен остаться хотя бы один владелец +``` + +**Преимущество:** Несколько человек могут иметь полный контроль над панелью. + +--- + +## 📊 Новая логика + +### Назначение владельца + +**Было:** +```python +# Если назначается новый owner, текущий owner становится admin +if role_data.role == "owner": + for user in users.values(): + if user.get("role") == "owner": + user["role"] = "admin" # Понижение роли +``` + +**Стало:** +```python +# Разрешаем несколько владельцев +# Просто назначаем роль без изменения других владельцев +users[username]["role"] = role_data.role +``` + +--- + +### Удаление владельца + +**Было:** +```python +if users[username].get("role") == "owner": + raise HTTPException(400, "Нельзя удалить владельца") +``` + +**Стало:** +```python +if users[username].get("role") == "owner": + owners_count = sum(1 for u in users.values() if u.get("role") == "owner") + if owners_count <= 1: + raise HTTPException(400, "Нельзя удалить последнего владельца") +``` + +**Логика:** +- Если владельцев больше одного → можно удалить +- Если владелец последний → нельзя удалить + +--- + +### Блокировка владельца + +**Было:** +```python +if users[username].get("role") == "owner": + raise HTTPException(400, "Нельзя заблокировать владельца") +``` + +**Стало:** +```python +if users[username].get("role") == "owner": + owners_count = sum(1 for u in users.values() if u.get("role") == "owner") + if owners_count <= 1: + raise HTTPException(400, "Нельзя заблокировать последнего владельца") +``` + +**Логика:** +- Если владельцев больше одного → можно заблокировать +- Если владелец последний → нельзя заблокировать + +--- + +## 💡 Примеры использования + +### Сценарий 1: Назначить второго владельца + +**Текущее состояние:** +```json +{ + "Root": {"role": "owner"}, + "Admin1": {"role": "admin"} +} +``` + +**Действие:** Назначить Admin1 владельцем + +**Результат:** +```json +{ + "Root": {"role": "owner"}, + "Admin1": {"role": "owner"} // Теперь тоже владелец! +} +``` + +**Оба пользователя имеют полные права!** ✅ + +--- + +### Сценарий 2: Удалить одного из владельцев + +**Текущее состояние:** +```json +{ + "Root": {"role": "owner"}, + "Admin1": {"role": "owner"}, + "User1": {"role": "user"} +} +``` + +**Действие:** Удалить Admin1 + +**Результат:** +```json +{ + "Root": {"role": "owner"}, + "User1": {"role": "user"} +} +``` + +**Успешно!** Root остался владельцем ✅ + +--- + +### Сценарий 3: Попытка удалить последнего владельца + +**Текущее состояние:** +```json +{ + "Root": {"role": "owner"}, + "User1": {"role": "user"} +} +``` + +**Действие:** Удалить Root + +**Результат:** +``` +❌ Ошибка: "Нельзя удалить последнего владельца. Должен остаться хотя бы один владелец." +``` + +**Защита от потери контроля!** 🔒 + +--- + +### Сценарий 4: Три владельца + +**Возможно:** +```json +{ + "Root": {"role": "owner"}, + "Admin1": {"role": "owner"}, + "Admin2": {"role": "owner"}, + "User1": {"role": "user"} +} +``` + +**Все три владельца имеют:** +- ✅ Управление пользователями +- ✅ Изменение ролей +- ✅ Удаление пользователей +- ✅ Просмотр всех ресурсов +- ✅ Все права + +--- + +## 🎯 Как использовать + +### Назначить нового владельца + +1. Войдите как владелец +2. Нажмите "Управление" +3. Найдите пользователя +4. Нажмите "Роль" +5. Выберите "Владелец" +6. Готово! Теперь два владельца + +### Понизить владельца до админа + +1. Войдите как владелец +2. Нажмите "Управление" +3. Найдите другого владельца +4. Нажмите "Роль" +5. Выберите "Администратор" +6. Владелец понижен до админа + +### Удалить владельца + +1. Убедитесь что владельцев больше одного +2. Войдите как владелец +3. Нажмите "Управление" +4. Найдите владельца для удаления +5. Нажмите кнопку удаления (красная) +6. Подтвердите +7. Владелец удалён (если их было больше одного) + +--- + +## 🔒 Правила безопасности + +### Что НЕЛЬЗЯ сделать: + +- ❌ Удалить последнего владельца +- ❌ Заблокировать последнего владельца +- ❌ Изменить свою роль +- ❌ Удалить самого себя +- ❌ Заблокировать самого себя + +### Что МОЖНО: + +- ✅ Назначить несколько владельцев +- ✅ Удалить владельца (если их больше одного) +- ✅ Заблокировать владельца (если их больше одного) +- ✅ Понизить владельца до админа +- ✅ Повысить админа до владельца + +--- + +## 📊 Сравнение ролей + +### Owner (Владелец) - Несколько человек ✅ + +**Права:** +- ✅ Управление пользователями +- ✅ Изменение ролей (включая назначение других владельцев) +- ✅ Удаление пользователей +- ✅ Управление серверами +- ✅ Просмотр всех ресурсов +- ✅ Все права + +**Ограничения:** +- Должен быть хотя бы один владелец +- Нельзя удалить/заблокировать себя + +### Admin (Администратор) - Несколько человек ✅ + +**Права:** +- ✅ Управление пользователями +- ✅ Управление серверами +- ✅ Просмотр всех ресурсов +- ❌ Изменение ролей +- ❌ Удаление пользователей + +**Отличие от Owner:** +- Не может назначать владельцев +- Не может удалять пользователей + +--- + +## 🎨 UI изменения + +### Модальное окно изменения роли + +Теперь при выборе "Владелец" не будет предупреждения о понижении текущего владельца: + +``` +┌─────────────────────────────────┐ +│ Изменить роль: Admin1 │ +├─────────────────────────────────┤ +│ [👑 Владелец] │ +│ Полный контроль над панелью │ +│ ⚠️ Может быть несколько │ ← Новое +│ │ +│ [🛡️ Администратор] ← Текущая │ +│ Управление без изменения │ +│ ролей │ +│ │ +│ [Отмена] │ +└─────────────────────────────────┘ +``` + +### Список пользователей + +Теперь может быть несколько пользователей с меткой "👑 Владелец": + +``` +1. Root [👑 Владелец] +2. Admin1 [👑 Владелец] ← Новое +3. Admin2 [🛡️ Админ] +4. User1 [✅ Пользователь] +``` + +--- + +## 🔧 Технические детали + +### Изменённые файлы + +**Файл:** `backend/main.py` + +**Изменённые эндпоинты:** +1. `PUT /api/users/{username}/role` - Убрано автоматическое понижение +2. `DELETE /api/users/{username}` - Добавлена проверка количества владельцев +3. `POST /api/users/{username}/ban` - Добавлена проверка количества владельцев + +**Добавленная логика:** +```python +# Подсчёт владельцев +owners_count = sum(1 for u in users.values() if u.get("role") == "owner") + +# Проверка перед удалением/блокировкой +if owners_count <= 1: + raise HTTPException(400, "Нельзя удалить/заблокировать последнего владельца") +``` + +--- + +## 🎯 Рекомендации + +### Когда использовать несколько владельцев + +**Хорошие случаи:** +- ✅ Несколько администраторов проекта +- ✅ Команда разработчиков +- ✅ Резервный владелец на случай отсутствия основного +- ✅ Разделение ответственности + +**Плохие случаи:** +- ❌ Слишком много владельцев (риск безопасности) +- ❌ Назначение владельцем ненадёжных пользователей +- ❌ Владелец "на пробу" + +### Рекомендуемая структура + +``` +👑 Owner (2-3 человека) - Основные администраторы + ↓ +🛡️ Admin (3-5 человек) - Помощники администраторов + ↓ +💬 Support (5-10 человек) - Техническая поддержка + ↓ +✅ User (неограниченно) - Обычные пользователи +``` + +--- + +## 🚀 Перезапуск + +После изменений перезапустите панель: + +```bash +RESTART_ALL.bat +``` + +Или вручную: +```bash +cd backend +python main.py +``` + +--- + +## ✅ Проверка + +### Тест 1: Назначить второго владельца + +1. Войдите как Root +2. Управление → MihailPrud → Роль → Владелец +3. Проверьте что оба имеют роль owner + +### Тест 2: Попытка удалить единственного владельца + +1. Понизьте всех владельцев кроме одного +2. Попытайтесь удалить последнего +3. Должна быть ошибка + +### Тест 3: Удаление одного из нескольких владельцев + +1. Назначьте двух владельцев +2. Удалите одного +3. Должно пройти успешно + +--- + +## 🎉 Итог + +**Теперь можно иметь несколько владельцев!** ✅ + +### Преимущества: + +- ✅ Гибкость в управлении +- ✅ Резервирование доступа +- ✅ Разделение ответственности +- ✅ Защита от потери контроля (всегда остаётся хотя бы один владелец) + +### Безопасность: + +- 🔒 Нельзя удалить последнего владельца +- 🔒 Нельзя заблокировать последнего владельца +- 🔒 Нельзя изменить свою роль +- 🔒 Нельзя удалить себя + +--- + +**Версия:** 1.1.0 +**Дата:** 15 января 2026 +**Статус:** РАБОТАЕТ ✅ + +**Несколько владельцев - больше контроля!** 👑👑👑 + diff --git a/README.md b/README.md index b828494..d914542 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,30 @@ Comprehensive overview всего проекта: **Полный контроль над всеми ресурсами!** 🖥️ +### 👑 [MULTIPLE_OWNERS.md](MULTIPLE_OWNERS.md) +**Несколько владельцев** + +Возможность назначить несколько владельцев: +- 🎯 Что изменилось +- 📊 Новая логика +- 💡 Примеры использования +- 🔒 Правила безопасности +- 🎯 Рекомендации + +**Больше владельцев - больше контроля!** 👑👑 + +### 🚀 [DRONE_SIMPLIFIED.md](DRONE_SIMPLIFIED.md) +**Упрощённый CI/CD** + +Упрощение Drone конфигурации: +- 🎯 Что изменилось (4→2 пайплайна) +- 📋 Оставшиеся пайплайны +- 🗑️ Удалённые компоненты +- 🔧 Настройка +- ✅ Преимущества + +**Меньше сложности - больше контроля!** 🔧 + ### 📝 [CHANGELOG.md](CHANGELOG.md) **История изменений** diff --git a/backend/main.py b/backend/main.py index 2044671..f57756e 100644 --- a/backend/main.py +++ b/backend/main.py @@ -445,9 +445,11 @@ async def delete_user(username: str, user: dict = Depends(get_current_user)): if username not in users: raise HTTPException(404, "Пользователь не найден") - # Нельзя удалить другого владельца + # Проверяем, что не удаляем последнего владельца if users[username]["role"] == "owner": - raise HTTPException(400, "Нельзя удалить владельца") + owners_count = sum(1 for u in users.values() if u.get("role") == "owner") + if owners_count <= 1: + raise HTTPException(400, "Нельзя удалить последнего владельца") del users[username] save_users(users) @@ -1672,11 +1674,8 @@ async def change_user_role(username: str, role_data: RoleChange, current_user: d if role_data.role not in valid_roles: raise HTTPException(status_code=400, detail=f"Неверная роль") - # Если назначается новый owner, текущий owner становится admin - if role_data.role == "owner": - for user in users.values(): - if user.get("role") == "owner": - user["role"] = "admin" + # Разрешаем несколько владельцев (убрано ограничение на одного) + # Теперь можно назначить несколько пользователей с ролью owner old_role = users[username].get("role", "user") users[username]["role"] = role_data.role @@ -1733,8 +1732,11 @@ async def ban_user(username: str, ban_data: BanRequest, current_user: dict = Dep if username == current_user.get("username"): raise HTTPException(status_code=400, detail="Нельзя заблокировать самого себя") + # Проверяем, что не блокируем последнего владельца if users[username].get("role") == "owner": - raise HTTPException(status_code=400, detail="Нельзя заблокировать владельца") + owners_count = sum(1 for u in users.values() if u.get("role") == "owner") + if owners_count <= 1: + raise HTTPException(status_code=400, detail="Нельзя заблокировать последнего владельца. Должен остаться хотя бы один владелец.") users[username]["role"] = "banned" users[username]["permissions"] = { @@ -1786,8 +1788,11 @@ async def delete_user(username: str, current_user: dict = Depends(get_current_us if username == current_user.get("username"): raise HTTPException(status_code=400, detail="Нельзя удалить самого себя") + # Проверяем, что не удаляем последнего владельца if users[username].get("role") == "owner": - raise HTTPException(status_code=400, detail="Нельзя удалить владельца") + owners_count = sum(1 for u in users.values() if u.get("role") == "owner") + if owners_count <= 1: + raise HTTPException(status_code=400, detail="Нельзя удалить последнего владельца. Должен остаться хотя бы один владелец.") del users[username] save_users_dict(users) diff --git a/backend/users.json b/backend/users.json index 14684e5..9e26dfe 100644 --- a/backend/users.json +++ b/backend/users.json @@ -1,74 +1 @@ -{ - "Root": { - "username": "Root", - "password": "$2b$12$PAaomoUWn3Ip5ov.S/uYPeTIRiDMq7DbA57ahyYQnw3QHT2zuYMlG", - "role": "owner", - "servers": [], - "permissions": { - "manage_users": true, - "manage_roles": true, - "manage_servers": true, - "manage_tickets": true, - "manage_files": true, - "delete_users": true, - "view_all_resources": true - }, - "resource_access": { - "servers": [], - "tickets": [], - "files": [] - } - }, - "MihailPrud": { - "username": "MihailPrud", - "password": "$2b$12$GfbQN4scE.b.mtUHofWWE.Dn1tQpT1zwLAxeICv90sHP4zGv0dc2G", - "role": "user", - "servers": [ - "test", - "nya" - ], - "permissions": { - "manage_users": false, - "manage_roles": false, - "manage_servers": true, - "manage_tickets": true, - "manage_files": true, - "delete_users": false, - "view_all_resources": false - }, - "resource_access": { - "servers": [ - "test", - "nya" - ], - "tickets": [], - "files": [] - } - }, - "arkonsad": { - "username": "arkonsad", - "password": "$2b$12$z.AYkfa/MlTYFd9rLNfBmu9JHOFKUe8YdddnqCmRqAxc7vGQeo392", - "role": "user", - "servers": [ - "123", - "sdfsdf" - ], - "permissions": { - "manage_users": false, - "manage_roles": false, - "manage_servers": true, - "manage_tickets": true, - "manage_files": true, - "delete_users": false, - "view_all_resources": false - }, - "resource_access": { - "servers": [ - "123", - "sdfsdf" - ], - "tickets": [], - "files": [] - } - } -} \ No newline at end of file +{} \ No newline at end of file diff --git a/backend/users.json1 b/backend/users.json1 new file mode 100644 index 0000000..87b7b55 --- /dev/null +++ b/backend/users.json1 @@ -0,0 +1,75 @@ +{ + "Root": { + "username": "Root", + "password": "$2b$12$PAaomoUWn3Ip5ov.S/uYPeTIRiDMq7DbA57ahyYQnw3QHT2zuYMlG", + "role": "owner", + "servers": [], + "permissions": { + "manage_users": true, + "manage_roles": true, + "manage_servers": true, + "manage_tickets": true, + "manage_files": true, + "delete_users": true, + "view_all_resources": true + }, + "resource_access": { + "servers": [], + "tickets": [], + "files": [] + } + }, + "MihailPrud": { + "username": "MihailPrud", + "password": "$2b$12$GfbQN4scE.b.mtUHofWWE.Dn1tQpT1zwLAxeICv90sHP4zGv0dc2G", + "role": "owner", + "servers": [ + "test", + "nya" + ], + "permissions": { + "manage_users": true, + "manage_roles": true, + "manage_servers": true, + "manage_tickets": true, + "manage_files": true, + "delete_users": true, + "view_all_resources": true + }, + "resource_access": { + "servers": [ + "test", + "nya" + ], + "tickets": [], + "files": [] + } + }, + "arkonsad": { + "username": "arkonsad", + "password": "$2b$12$z.AYkfa/MlTYFd9rLNfBmu9JHOFKUe8YdddnqCmRqAxc7vGQeo392", + "role": "banned", + "servers": [ + "123", + "sdfsdf" + ], + "permissions": { + "manage_users": false, + "manage_roles": false, + "manage_servers": false, + "manage_tickets": false, + "manage_files": false, + "delete_users": false, + "view_all_resources": false + }, + "resource_access": { + "servers": [ + "123", + "sdfsdf" + ], + "tickets": [], + "files": [] + }, + "ban_reason": "Заблокирован администратором" + } +} \ No newline at end of file