Change drone.yml

This commit is contained in:
2026-01-15 19:25:42 +06:00
parent d25d7fc2f9
commit 112123b0ff
8 changed files with 937 additions and 352 deletions

View File

@@ -117,7 +117,7 @@ steps:
- push - push
- tag - tag
# Сканирование образа на уязвимости (опционально) # Сканирование образа на уязвимости
- name: scan-image - name: scan-image
image: aquasec/trivy image: aquasec/trivy
commands: commands:
@@ -128,137 +128,3 @@ steps:
- tag - tag
depends_on: depends_on:
- build-and-push - 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

391
DRONE_SIMPLIFIED.md Normal file
View File

@@ -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
**Статус:** УПРОЩЕНО ✅
**Меньше сложности - больше контроля!** 🚀

View File

@@ -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 <module>
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
**Статус:** РАБОТАЕТ ✅

431
MULTIPLE_OWNERS.md Normal file
View File

@@ -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
**Статус:** РАБОТАЕТ ✅
**Несколько владельцев - больше контроля!** 👑👑👑

View File

@@ -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) ### 📝 [CHANGELOG.md](CHANGELOG.md)
**История изменений** **История изменений**

View File

@@ -445,9 +445,11 @@ async def delete_user(username: str, user: dict = Depends(get_current_user)):
if username not in users: if username not in users:
raise HTTPException(404, "Пользователь не найден") raise HTTPException(404, "Пользователь не найден")
# Нельзя удалить другого владельца # Проверяем, что не удаляем последнего владельца
if users[username]["role"] == "owner": 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] del users[username]
save_users(users) 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: if role_data.role not in valid_roles:
raise HTTPException(status_code=400, detail=f"Неверная роль") raise HTTPException(status_code=400, detail=f"Неверная роль")
# Если назначается новый owner, текущий owner становится admin # Разрешаем несколько владельцев (убрано ограничение на одного)
if role_data.role == "owner": # Теперь можно назначить несколько пользователей с ролью owner
for user in users.values():
if user.get("role") == "owner":
user["role"] = "admin"
old_role = users[username].get("role", "user") old_role = users[username].get("role", "user")
users[username]["role"] = role_data.role 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"): if username == current_user.get("username"):
raise HTTPException(status_code=400, detail="Нельзя заблокировать самого себя") raise HTTPException(status_code=400, detail="Нельзя заблокировать самого себя")
# Проверяем, что не блокируем последнего владельца
if users[username].get("role") == "owner": 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]["role"] = "banned"
users[username]["permissions"] = { 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"): if username == current_user.get("username"):
raise HTTPException(status_code=400, detail="Нельзя удалить самого себя") raise HTTPException(status_code=400, detail="Нельзя удалить самого себя")
# Проверяем, что не удаляем последнего владельца
if users[username].get("role") == "owner": 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] del users[username]
save_users_dict(users) save_users_dict(users)

View File

@@ -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": []
}
}
}

75
backend/users.json1 Normal file
View File

@@ -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": "Заблокирован администратором"
}
}