Added Role Owner and new UI for Owner

This commit is contained in:
2026-01-15 19:00:09 +06:00
parent 9a1e2df04d
commit 551d733dc4
15 changed files with 3999 additions and 22 deletions

View File

@@ -2,6 +2,7 @@ from fastapi import FastAPI, WebSocket, UploadFile, File, HTTPException, Depends
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, RedirectResponse
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from pydantic import BaseModel
import asyncio
import subprocess
import psutil
@@ -160,8 +161,15 @@ def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(securit
raise HTTPException(status_code=401, detail="Неверный токен")
def check_server_access(user: dict, server_name: str):
# Владелец имеет доступ ко всем серверам
if user["role"] == "owner":
return True
# Админы имеют доступ ко всем серверам
if user["role"] == "admin":
return True
# Проверяем права на серверы
if not user.get("permissions", {}).get("servers", True):
return False
return server_name in user.get("servers", [])
# API для аутентификации
@@ -301,13 +309,25 @@ async def register(data: dict):
if username in users:
raise HTTPException(400, "Пользователь уже существует")
role = "admin" if len(users) == 0 else "user"
# Первый пользователь становится владельцем
role = "owner" if len(users) == 0 else "user"
users[username] = {
"username": username,
"password": get_password_hash(password),
"role": role,
"servers": []
"servers": [],
"permissions": {
"servers": True,
"tickets": True,
"users": True if role == "owner" else False,
"files": True
} if role == "owner" else {
"servers": True,
"tickets": True,
"users": False,
"files": True
}
}
save_users(users)
@@ -343,23 +363,43 @@ async def login(data: dict):
@app.get("/api/auth/me")
async def get_me(user: dict = Depends(get_current_user)):
users = load_users()
user_data = users.get(user["username"], {})
# Если у пользователя нет прав, создаем дефолтные
if "permissions" not in user_data:
user_data["permissions"] = {
"servers": True,
"tickets": True,
"users": user_data["role"] in ["owner", "admin"],
"files": True
}
users[user["username"]] = user_data
save_users(users)
return {
"username": user["username"],
"role": user["role"],
"servers": user.get("servers", [])
"servers": user.get("servers", []),
"permissions": user_data.get("permissions", {})
}
# API для управления пользователями
@app.get("/api/users")
async def get_users(user: dict = Depends(get_current_user)):
# Админы видят всех пользователей
# Обычные пользователи тоже видят всех (для управления доступом к своим серверам)
# Владелец, админы и тех. поддержка видят всех пользователей
users = load_users()
return [
{
"username": u["username"],
"role": u["role"],
"servers": u.get("servers", [])
"servers": u.get("servers", []),
"permissions": u.get("permissions", {
"servers": True,
"tickets": True,
"users": u["role"] in ["owner", "admin"],
"files": True
})
}
for u in users.values()
]
@@ -394,8 +434,9 @@ async def update_user_servers(username: str, data: dict, user: dict = Depends(ge
@app.delete("/api/users/{username}")
async def delete_user(username: str, user: dict = Depends(get_current_user)):
if user["role"] != "admin":
raise HTTPException(403, "Доступ запрещен")
# Только владелец может удалять пользователей
if user["role"] != "owner":
raise HTTPException(403, "Только владелец может удалять пользователей")
if username == user["username"]:
raise HTTPException(400, "Нельзя удалить самого себя")
@@ -404,6 +445,10 @@ 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, "Нельзя удалить владельца")
del users[username]
save_users(users)
@@ -411,8 +456,9 @@ async def delete_user(username: str, user: dict = Depends(get_current_user)):
@app.put("/api/users/{username}/role")
async def update_user_role(username: str, data: dict, user: dict = Depends(get_current_user)):
if user["role"] != "admin":
raise HTTPException(403, "Доступ запрещен")
# Только владелец может изменять роли
if user["role"] != "owner":
raise HTTPException(403, "Только владелец может изменять роли")
if username == user["username"]:
raise HTTPException(400, "Нельзя изменить свою роль")
@@ -425,11 +471,187 @@ async def update_user_role(username: str, data: dict, user: dict = Depends(get_c
if new_role not in ["admin", "user", "support", "banned"]:
raise HTTPException(400, "Неверная роль")
# Нельзя назначить роль owner
if new_role == "owner":
raise HTTPException(400, "Нельзя назначить роль владельца")
users[username]["role"] = new_role
save_users(users)
return {"message": "Роль обновлена"}
@app.get("/api/users/{username}/permissions")
async def get_user_permissions(username: str, user: dict = Depends(get_current_user)):
"""Получить права пользователя"""
# Только владелец и админы могут просматривать права
if user["role"] not in ["owner", "admin"]:
raise HTTPException(403, "Недостаточно прав")
users = load_users()
if username not in users:
raise HTTPException(404, "Пользователь не найден")
target_user = users[username]
# Если у пользователя нет прав, создаем дефолтные
if "permissions" not in target_user:
target_user["permissions"] = {
"servers": True,
"tickets": True,
"users": target_user["role"] in ["owner", "admin"],
"files": True
}
users[username] = target_user
save_users(users)
return {
"username": username,
"role": target_user["role"],
"permissions": target_user["permissions"]
}
@app.put("/api/users/{username}/permissions")
async def update_user_permissions(username: str, data: dict, user: dict = Depends(get_current_user)):
"""Обновить права пользователя (только для владельца)"""
if user["role"] != "owner":
raise HTTPException(403, "Только владелец может изменять права")
if username == user["username"]:
raise HTTPException(400, "Нельзя изменить свои права")
users = load_users()
if username not in users:
raise HTTPException(404, "Пользователь не найден")
target_user = users[username]
# Нельзя изменять права владельца
if target_user["role"] == "owner":
raise HTTPException(400, "Нельзя изменять права владельца")
permissions = data.get("permissions", {})
# Валидация прав
valid_permissions = ["servers", "tickets", "users", "files"]
for perm in permissions:
if perm not in valid_permissions:
raise HTTPException(400, f"Неверное право: {perm}")
# Обновляем права
if "permissions" not in target_user:
target_user["permissions"] = {
"servers": True,
"tickets": True,
"users": False,
"files": True
}
target_user["permissions"].update(permissions)
users[username] = target_user
save_users(users)
return {
"message": "Права обновлены",
"permissions": target_user["permissions"]
}
@app.post("/api/users/{username}/revoke-access")
async def revoke_user_access(username: str, data: dict, user: dict = Depends(get_current_user)):
"""Забрать доступ к определенным ресурсам (только для владельца)"""
if user["role"] != "owner":
raise HTTPException(403, "Только владелец может забирать доступ")
users = load_users()
if username not in users:
raise HTTPException(404, "Пользователь не найден")
target_user = users[username]
# Нельзя забирать доступ у владельца
if target_user["role"] == "owner":
raise HTTPException(400, "Нельзя забирать доступ у владельца")
resource_type = data.get("type") # "servers", "tickets", "all"
if resource_type == "servers":
# Забираем доступ ко всем серверам
target_user["servers"] = []
if "permissions" in target_user:
target_user["permissions"]["servers"] = False
elif resource_type == "tickets":
# Забираем доступ к тикетам
if "permissions" in target_user:
target_user["permissions"]["tickets"] = False
elif resource_type == "files":
# Забираем доступ к файлам
if "permissions" in target_user:
target_user["permissions"]["files"] = False
elif resource_type == "all":
# Забираем весь доступ
target_user["servers"] = []
if "permissions" in target_user:
target_user["permissions"] = {
"servers": False,
"tickets": False,
"users": False,
"files": False
}
else:
raise HTTPException(400, "Неверный тип ресурса")
users[username] = target_user
save_users(users)
return {
"message": f"Доступ к {resource_type} забран",
"permissions": target_user.get("permissions", {})
}
@app.post("/api/users/{username}/grant-access")
async def grant_user_access(username: str, data: dict, user: dict = Depends(get_current_user)):
"""Выдать доступ к определенным ресурсам (только для владельца)"""
if user["role"] != "owner":
raise HTTPException(403, "Только владелец может выдавать доступ")
users = load_users()
if username not in users:
raise HTTPException(404, "Пользователь не найден")
target_user = users[username]
resource_type = data.get("type") # "servers", "tickets", "files"
if "permissions" not in target_user:
target_user["permissions"] = {
"servers": False,
"tickets": False,
"users": False,
"files": False
}
if resource_type == "servers":
target_user["permissions"]["servers"] = True
elif resource_type == "tickets":
target_user["permissions"]["tickets"] = True
elif resource_type == "files":
target_user["permissions"]["files"] = True
elif resource_type == "all":
target_user["permissions"] = {
"servers": True,
"tickets": True,
"users": target_user["role"] in ["admin"],
"files": True
}
else:
raise HTTPException(400, "Неверный тип ресурса")
users[username] = target_user
save_users(users)
return {
"message": f"Доступ к {resource_type} выдан",
"permissions": target_user["permissions"]
}
# API для личного кабинета
@app.put("/api/profile/username")
async def update_username(data: dict, user: dict = Depends(get_current_user)):
@@ -613,9 +835,13 @@ async def get_user_profile_stats(username: str, user: dict = Depends(get_current
async def get_servers(user: dict = Depends(get_current_user)):
servers = []
try:
# Владелец и администратор видят все серверы
can_view_all = user.get("role") in ["owner", "admin"] or user.get("permissions", {}).get("view_all_resources", False)
for server_dir in SERVERS_DIR.iterdir():
if server_dir.is_dir():
if user["role"] != "admin" and server_dir.name not in user.get("servers", []):
# Проверка доступа: владелец/админ видят всё, остальные только свои
if not can_view_all and server_dir.name not in user.get("servers", []):
continue
config = load_server_config(server_dir.name)
@@ -633,7 +859,7 @@ async def get_servers(user: dict = Depends(get_current_user)):
"displayName": config.get("displayName", server_dir.name),
"status": "running" if is_running else "stopped"
})
print(f"Найдено серверов для {user['username']}: {len(servers)}")
print(f"Найдено серверов для {user['username']} ({user.get('role', 'user')}): {len(servers)}")
except Exception as e:
print(f"Ошибка загрузки серверов: {e}")
return servers
@@ -1243,10 +1469,14 @@ async def rename_file(server_name: str, old_path: str, new_name: str, user: dict
@app.get("/api/tickets")
async def get_tickets(user: dict = Depends(get_current_user)):
"""Получить список тикетов"""
# Проверяем права на тикеты
if not user.get("permissions", {}).get("tickets", True):
raise HTTPException(403, "Нет доступа к тикетам")
tickets = load_tickets()
# Админы и тех. поддержка видят все тикеты
if user["role"] in ["admin", "support"]:
# Владелец, админы и тех. поддержка видят все тикеты
if user["role"] in ["owner", "admin", "support"]:
return list(tickets.values())
# Обычные пользователи видят только свои тикеты
@@ -1256,6 +1486,10 @@ async def get_tickets(user: dict = Depends(get_current_user)):
@app.post("/api/tickets/create")
async def create_ticket(data: dict, user: dict = Depends(get_current_user)):
"""Создать новый тикет"""
# Проверяем права на тикеты
if not user.get("permissions", {}).get("tickets", True):
raise HTTPException(403, "Нет доступа к тикетам")
tickets = load_tickets()
# Генерируем ID тикета
@@ -1286,6 +1520,10 @@ async def create_ticket(data: dict, user: dict = Depends(get_current_user)):
@app.get("/api/tickets/{ticket_id}")
async def get_ticket(ticket_id: str, user: dict = Depends(get_current_user)):
"""Получить тикет по ID"""
# Проверяем права на тикеты
if not user.get("permissions", {}).get("tickets", True):
raise HTTPException(403, "Нет доступа к тикетам")
tickets = load_tickets()
if ticket_id not in tickets:
@@ -1294,7 +1532,7 @@ async def get_ticket(ticket_id: str, user: dict = Depends(get_current_user)):
ticket = tickets[ticket_id]
# Проверка доступа
if user["role"] not in ["admin", "support"] and ticket["author"] != user["username"]:
if user["role"] not in ["owner", "admin", "support"] and ticket["author"] != user["username"]:
raise HTTPException(403, "Нет доступа к этому тикету")
return ticket
@@ -1302,6 +1540,10 @@ async def get_ticket(ticket_id: str, user: dict = Depends(get_current_user)):
@app.post("/api/tickets/{ticket_id}/message")
async def add_ticket_message(ticket_id: str, data: dict, user: dict = Depends(get_current_user)):
"""Добавить сообщение в тикет"""
# Проверяем права на тикеты
if not user.get("permissions", {}).get("tickets", True):
raise HTTPException(403, "Нет доступа к тикетам")
tickets = load_tickets()
if ticket_id not in tickets:
@@ -1310,7 +1552,7 @@ async def add_ticket_message(ticket_id: str, data: dict, user: dict = Depends(ge
ticket = tickets[ticket_id]
# Проверка доступа
if user["role"] not in ["admin", "support"] and ticket["author"] != user["username"]:
if user["role"] not in ["owner", "admin", "support"] and ticket["author"] != user["username"]:
raise HTTPException(403, "Нет доступа к этому тикету")
message = {
@@ -1329,10 +1571,14 @@ async def add_ticket_message(ticket_id: str, data: dict, user: dict = Depends(ge
@app.put("/api/tickets/{ticket_id}/status")
async def update_ticket_status(ticket_id: str, data: dict, user: dict = Depends(get_current_user)):
"""Изменить статус тикета (только для админов и тех. поддержки)"""
if user["role"] not in ["admin", "support"]:
"""Изменить статус тикета (только для владельца, админов и тех. поддержки)"""
if user["role"] not in ["owner", "admin", "support"]:
raise HTTPException(403, "Недостаточно прав")
# Проверяем права на тикеты
if not user.get("permissions", {}).get("tickets", True):
raise HTTPException(403, "Нет доступа к тикетам")
tickets = load_tickets()
if ticket_id not in tickets:
@@ -1366,6 +1612,258 @@ async def update_ticket_status(ticket_id: str, data: dict, user: dict = Depends(
return {"message": "Статус обновлён", "ticket": ticket}
# ============================================
# УПРАВЛЕНИЕ ПОЛЬЗОВАТЕЛЯМИ (v1.1.0)
# ============================================
# Загрузка пользователей
def load_users_dict():
users_file = Path("users.json")
if not users_file.exists():
return {}
with open(users_file, "r", encoding="utf-8") as f:
return json.load(f)
def save_users_dict(users):
with open("users.json", "w", encoding="utf-8") as f:
json.dump(users, f, indent=2, ensure_ascii=False)
# Проверка прав
def require_owner(current_user: dict):
if current_user.get("role") != "owner":
raise HTTPException(status_code=403, detail="Требуется роль владельца")
def require_admin_or_owner(current_user: dict):
if current_user.get("role") not in ["owner", "admin"]:
raise HTTPException(status_code=403, detail="Требуется роль администратора или владельца")
# 1. Получить список пользователей
@app.get("/api/users")
async def get_users(current_user: dict = Depends(get_current_user)):
require_admin_or_owner(current_user)
users = load_users_dict()
users_list = []
for username, user_data in users.items():
user_copy = user_data.copy()
user_copy.pop("password", None)
users_list.append(user_copy)
return users_list
# 2. Изменить роль пользователя
class RoleChange(BaseModel):
role: str
@app.put("/api/users/{username}/role")
async def change_user_role(username: str, role_data: RoleChange, current_user: dict = Depends(get_current_user)):
require_owner(current_user)
users = load_users_dict()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
if username == current_user.get("username"):
raise HTTPException(status_code=400, detail="Нельзя изменить свою роль")
valid_roles = ["owner", "admin", "support", "user", "banned"]
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"
old_role = users[username].get("role", "user")
users[username]["role"] = role_data.role
# Обновляем права
if role_data.role == "owner":
users[username]["permissions"] = {
"manage_users": True, "manage_roles": True, "manage_servers": True,
"manage_tickets": True, "manage_files": True, "delete_users": True,
"view_all_resources": True
}
elif role_data.role == "admin":
users[username]["permissions"] = {
"manage_users": True, "manage_roles": False, "manage_servers": True,
"manage_tickets": True, "manage_files": True, "delete_users": False,
"view_all_resources": True
}
elif role_data.role == "support":
users[username]["permissions"] = {
"manage_users": False, "manage_roles": False, "manage_servers": False,
"manage_tickets": True, "manage_files": False, "delete_users": False,
"view_all_resources": False
}
elif role_data.role == "banned":
users[username]["permissions"] = {
"manage_users": False, "manage_roles": False, "manage_servers": False,
"manage_tickets": False, "manage_files": False, "delete_users": False,
"view_all_resources": False
}
else: # user
users[username]["permissions"] = {
"manage_users": False, "manage_roles": False, "manage_servers": True,
"manage_tickets": True, "manage_files": True, "delete_users": False,
"view_all_resources": False
}
save_users_dict(users)
return {"message": f"Роль изменена с {old_role} на {role_data.role}", "user": {"username": username, "role": role_data.role}}
# 3. Заблокировать пользователя
class BanRequest(BaseModel):
reason: str = "Заблокирован администратором"
@app.post("/api/users/{username}/ban")
async def ban_user(username: str, ban_data: BanRequest, current_user: dict = Depends(get_current_user)):
require_admin_or_owner(current_user)
users = load_users_dict()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
if username == current_user.get("username"):
raise HTTPException(status_code=400, detail="Нельзя заблокировать самого себя")
if users[username].get("role") == "owner":
raise HTTPException(status_code=400, detail="Нельзя заблокировать владельца")
users[username]["role"] = "banned"
users[username]["permissions"] = {
"manage_users": False, "manage_roles": False, "manage_servers": False,
"manage_tickets": False, "manage_files": False, "delete_users": False,
"view_all_resources": False
}
users[username]["ban_reason"] = ban_data.reason
save_users_dict(users)
return {"message": f"Пользователь {username} заблокирован", "username": username, "reason": ban_data.reason}
# 4. Разблокировать пользователя
@app.post("/api/users/{username}/unban")
async def unban_user(username: str, current_user: dict = Depends(get_current_user)):
require_admin_or_owner(current_user)
users = load_users_dict()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
if users[username].get("role") != "banned":
raise HTTPException(status_code=400, detail="Пользователь не заблокирован")
users[username]["role"] = "user"
users[username]["permissions"] = {
"manage_users": False, "manage_roles": False, "manage_servers": True,
"manage_tickets": True, "manage_files": True, "delete_users": False,
"view_all_resources": False
}
users[username].pop("ban_reason", None)
save_users_dict(users)
return {"message": f"Пользователь {username} разблокирован", "username": username}
# 5. Удалить пользователя
@app.delete("/api/users/{username}")
async def delete_user(username: str, current_user: dict = Depends(get_current_user)):
require_owner(current_user)
users = load_users_dict()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
if username == current_user.get("username"):
raise HTTPException(status_code=400, detail="Нельзя удалить самого себя")
if users[username].get("role") == "owner":
raise HTTPException(status_code=400, detail="Нельзя удалить владельца")
del users[username]
save_users_dict(users)
return {"message": f"Пользователь {username} удалён", "username": username}
# 6. Выдать доступ к серверу
class ServerAccess(BaseModel):
server_name: str
@app.post("/api/users/{username}/access/servers")
async def grant_server_access(username: str, access: ServerAccess, current_user: dict = Depends(get_current_user)):
require_admin_or_owner(current_user)
users = load_users_dict()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
if "resource_access" not in users[username]:
users[username]["resource_access"] = {"servers": [], "tickets": [], "files": []}
if access.server_name not in users[username]["resource_access"]["servers"]:
users[username]["resource_access"]["servers"].append(access.server_name)
# Также добавляем в старое поле servers для совместимости
if "servers" not in users[username]:
users[username]["servers"] = []
if access.server_name not in users[username]["servers"]:
users[username]["servers"].append(access.server_name)
save_users_dict(users)
return {"message": f"Доступ к серверу {access.server_name} выдан", "server": access.server_name, "user": username}
# 7. Забрать доступ к серверу
@app.delete("/api/users/{username}/access/servers/{server_name}")
async def revoke_server_access(username: str, server_name: str, current_user: dict = Depends(get_current_user)):
require_admin_or_owner(current_user)
users = load_users_dict()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
if "resource_access" in users[username] and "servers" in users[username]["resource_access"]:
if server_name in users[username]["resource_access"]["servers"]:
users[username]["resource_access"]["servers"].remove(server_name)
# Также удаляем из старого поля servers
if "servers" in users[username] and server_name in users[username]["servers"]:
users[username]["servers"].remove(server_name)
save_users_dict(users)
return {"message": f"Доступ к серверу {server_name} отозван", "server": server_name, "user": username}
# 8. Изменить права пользователя
class PermissionsUpdate(BaseModel):
permissions: dict
@app.put("/api/users/{username}/permissions")
async def update_user_permissions(username: str, perms: PermissionsUpdate, current_user: dict = Depends(get_current_user)):
require_owner(current_user)
users = load_users_dict()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
users[username]["permissions"] = perms.permissions
save_users_dict(users)
return {"message": f"Права пользователя {username} обновлены", "permissions": perms.permissions}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)

242
backend/migrate_users.py Normal file
View File

@@ -0,0 +1,242 @@
#!/usr/bin/env python3
"""
Скрипт миграции пользователей для MC Panel v1.1.0
Добавляет роль владельца и систему прав
"""
import json
from pathlib import Path
from datetime import datetime
def migrate_users():
"""Миграция пользователей на новую систему прав"""
users_file = Path("users.json")
# Проверка существования файла
if not users_file.exists():
print("❌ Файл users.json не найден")
print(" Создайте файл users.json или запустите панель для автоматического создания")
return False
# Создание backup
backup_file = Path(f"users_backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json")
try:
with open(users_file, "r", encoding="utf-8") as f:
backup_data = f.read()
with open(backup_file, "w", encoding="utf-8") as f:
f.write(backup_data)
print(f"✅ Backup создан: {backup_file}")
except Exception as e:
print(f"❌ Ошибка создания backup: {e}")
return False
# Загрузка пользователей
try:
with open(users_file, "r", encoding="utf-8") as f:
users_data = json.load(f)
except json.JSONDecodeError:
print("❌ Ошибка чтения users.json - неверный формат JSON")
return False
except Exception as e:
print(f"❌ Ошибка чтения файла: {e}")
return False
# Проверка формата (объект или список)
if isinstance(users_data, dict):
# Формат: {"username": {...}}
users_list = list(users_data.values())
is_dict_format = True
print(" Обнаружен формат: объект (словарь)")
elif isinstance(users_data, list):
# Формат: [{...}, {...}]
users_list = users_data
is_dict_format = False
print(" Обнаружен формат: список")
else:
print("❌ Неизвестный формат users.json")
return False
if not users_list:
print(" Нет пользователей для миграции")
return True
print(f"\n📊 Найдено пользователей: {len(users_list)}")
print("=" * 50)
# Миграция первого пользователя (владелец)
if users_list:
first_user = users_list[0]
print(f"\n👑 Назначение владельца: {first_user.get('username', 'Unknown')}")
first_user["role"] = "owner"
first_user["permissions"] = {
"manage_users": True,
"manage_roles": True,
"manage_servers": True,
"manage_tickets": True,
"manage_files": True,
"delete_users": True,
"view_all_resources": True
}
if "resource_access" not in first_user:
first_user["resource_access"] = {
"servers": first_user.get("servers", []),
"tickets": [],
"files": []
}
# Миграция остальных пользователей
for i, user in enumerate(users_list[1:], start=2):
username = user.get("username", f"User{i}")
current_role = user.get("role", "user")
print(f"\n👤 Пользователь {i}: {username}")
print(f" Текущая роль: {current_role}")
# Установка роли по умолчанию
if "role" not in user or user["role"] not in ["admin", "support", "user", "banned"]:
user["role"] = "user"
print(f" ➜ Установлена роль: user")
# Добавление прав
if "permissions" not in user:
if user["role"] == "admin":
user["permissions"] = {
"manage_users": True,
"manage_roles": False,
"manage_servers": True,
"manage_tickets": True,
"manage_files": True,
"delete_users": False,
"view_all_resources": True
}
print(" ➜ Добавлены права администратора")
elif user["role"] == "support":
user["permissions"] = {
"manage_users": False,
"manage_roles": False,
"manage_servers": False,
"manage_tickets": True,
"manage_files": False,
"delete_users": False,
"view_all_resources": False
}
print(" ➜ Добавлены права поддержки")
elif user["role"] == "banned":
user["permissions"] = {
"manage_users": False,
"manage_roles": False,
"manage_servers": False,
"manage_tickets": False,
"manage_files": False,
"delete_users": False,
"view_all_resources": False
}
print(" ➜ Пользователь заблокирован")
else: # user
user["permissions"] = {
"manage_users": False,
"manage_roles": False,
"manage_servers": True,
"manage_tickets": True,
"manage_files": True,
"delete_users": False,
"view_all_resources": False
}
print(" ➜ Добавлены права пользователя")
# Добавление доступа к ресурсам
if "resource_access" not in user:
user["resource_access"] = {
"servers": user.get("servers", []),
"tickets": [],
"files": []
}
print(" ➜ Добавлен доступ к ресурсам")
# Сохранение в правильном формате
try:
if is_dict_format:
# Сохраняем обратно как объект
users_dict = {user["username"]: user for user in users_list}
with open(users_file, "w", encoding="utf-8") as f:
json.dump(users_dict, f, indent=2, ensure_ascii=False)
else:
# Сохраняем как список
with open(users_file, "w", encoding="utf-8") as f:
json.dump(users_list, f, indent=2, ensure_ascii=False)
print("\n" + "=" * 50)
print("✅ Миграция успешно завершена!")
print(f"✅ Обновлено пользователей: {len(users_list)}")
print(f"👑 Владелец: {users_list[0]['username']}")
print(f"📁 Backup: {backup_file}")
return True
except Exception as e:
print(f"\n❌ Ошибка сохранения: {e}")
print(f" Восстановите из backup: {backup_file}")
return False
def show_users():
"""Показать список пользователей после миграции"""
users_file = Path("users.json")
if not users_file.exists():
print("❌ Файл users.json не найден")
return
try:
with open(users_file, "r", encoding="utf-8") as f:
users_data = json.load(f)
except Exception as e:
print(f"❌ Ошибка чтения файла: {e}")
return
# Преобразуем в список если это объект
if isinstance(users_data, dict):
users_list = list(users_data.values())
else:
users_list = users_data
print("\n" + "=" * 50)
print("📋 СПИСОК ПОЛЬЗОВАТЕЛЕЙ")
print("=" * 50)
for i, user in enumerate(users_list, start=1):
print(f"\n{i}. {user.get('username', 'Unknown')}")
print(f" Роль: {user.get('role', 'unknown')}")
print(f" Права:")
for perm, value in user.get('permissions', {}).items():
status = "" if value else ""
print(f" {status} {perm}")
# Показать доступ к ресурсам
resource_access = user.get('resource_access', {})
if resource_access:
servers = resource_access.get('servers', [])
if servers:
print(f" Серверы: {', '.join(servers)}")
if __name__ == "__main__":
print("=" * 50)
print("MC Panel - Миграция пользователей v1.1.0")
print("=" * 50)
# Запуск миграции
success = migrate_users()
if success:
# Показать результат
show_users()
print("\n" + "=" * 50)
print("📝 СЛЕДУЮЩИЕ ШАГИ:")
print("=" * 50)
print("1. Перезапустите панель")
print("2. Войдите как владелец")
print("3. Проверьте права пользователей")
print("4. Настройте доступ к ресурсам")
print("\n✨ Готово!")
else:
print("\n❌ Миграция не выполнена")
print(" Проверьте ошибки выше и попробуйте снова")

View File

@@ -0,0 +1,320 @@
"""
API эндпоинты для управления пользователями (v1.1.0)
Требуется роль owner или admin
"""
from fastapi import APIRouter, HTTPException, Depends
from pydantic import BaseModel
from typing import Optional, List
import json
from pathlib import Path
router = APIRouter()
# Модели данных
class RoleChange(BaseModel):
role: str
class PermissionsUpdate(BaseModel):
permissions: dict
class ServerAccess(BaseModel):
server_name: str
class BanRequest(BaseModel):
reason: str = "Заблокирован администратором"
# Загрузка пользователей
def load_users():
users_file = Path("users.json")
if not users_file.exists():
return {}
with open(users_file, "r", encoding="utf-8") as f:
return json.load(f)
# Сохранение пользователей
def save_users(users):
with open("users.json", "w", encoding="utf-8") as f:
json.dump(users, f, indent=2, ensure_ascii=False)
# Проверка прав
def require_owner(current_user: dict):
if current_user.get("role") != "owner":
raise HTTPException(status_code=403, detail="Требуется роль владельца")
def require_admin_or_owner(current_user: dict):
if current_user.get("role") not in ["owner", "admin"]:
raise HTTPException(status_code=403, detail="Требуется роль администратора или владельца")
# 1. Получить список пользователей
@router.get("/api/users")
async def get_users(current_user: dict = Depends()):
require_admin_or_owner(current_user)
users = load_users()
# Возвращаем список пользователей (без паролей)
users_list = []
for username, user_data in users.items():
user_copy = user_data.copy()
user_copy.pop("password", None)
users_list.append(user_copy)
return users_list
# 2. Изменить роль пользователя
@router.put("/api/users/{username}/role")
async def change_user_role(username: str, role_data: RoleChange, current_user: dict = Depends()):
require_owner(current_user)
users = load_users()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
if username == current_user.get("username"):
raise HTTPException(status_code=400, detail="Нельзя изменить свою роль")
# Проверка валидности роли
valid_roles = ["owner", "admin", "support", "user", "banned"]
if role_data.role not in valid_roles:
raise HTTPException(status_code=400, detail=f"Неверная роль. Доступные: {', '.join(valid_roles)}")
# Если назначается новый owner, текущий owner становится admin
if role_data.role == "owner":
for user in users.values():
if user.get("role") == "owner":
user["role"] = "admin"
# Изменяем роль
old_role = users[username].get("role", "user")
users[username]["role"] = role_data.role
# Обновляем права в зависимости от роли
if role_data.role == "owner":
users[username]["permissions"] = {
"manage_users": True,
"manage_roles": True,
"manage_servers": True,
"manage_tickets": True,
"manage_files": True,
"delete_users": True,
"view_all_resources": True
}
elif role_data.role == "admin":
users[username]["permissions"] = {
"manage_users": True,
"manage_roles": False,
"manage_servers": True,
"manage_tickets": True,
"manage_files": True,
"delete_users": False,
"view_all_resources": True
}
elif role_data.role == "support":
users[username]["permissions"] = {
"manage_users": False,
"manage_roles": False,
"manage_servers": False,
"manage_tickets": True,
"manage_files": False,
"delete_users": False,
"view_all_resources": False
}
elif role_data.role == "banned":
users[username]["permissions"] = {
"manage_users": False,
"manage_roles": False,
"manage_servers": False,
"manage_tickets": False,
"manage_files": False,
"delete_users": False,
"view_all_resources": False
}
else: # user
users[username]["permissions"] = {
"manage_users": False,
"manage_roles": False,
"manage_servers": True,
"manage_tickets": True,
"manage_files": True,
"delete_users": False,
"view_all_resources": False
}
save_users(users)
return {
"message": f"Роль пользователя {username} изменена с {old_role} на {role_data.role}",
"user": {
"username": username,
"role": role_data.role
}
}
# 3. Изменить права пользователя
@router.put("/api/users/{username}/permissions")
async def update_user_permissions(username: str, perms: PermissionsUpdate, current_user: dict = Depends()):
require_owner(current_user)
users = load_users()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
users[username]["permissions"] = perms.permissions
save_users(users)
return {
"message": f"Права пользователя {username} обновлены",
"permissions": perms.permissions
}
# 4. Выдать доступ к серверу
@router.post("/api/users/{username}/access/servers")
async def grant_server_access(username: str, access: ServerAccess, current_user: dict = Depends()):
require_admin_or_owner(current_user)
users = load_users()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
if "resource_access" not in users[username]:
users[username]["resource_access"] = {"servers": [], "tickets": [], "files": []}
if access.server_name not in users[username]["resource_access"]["servers"]:
users[username]["resource_access"]["servers"].append(access.server_name)
# Также добавляем в старое поле servers для совместимости
if "servers" not in users[username]:
users[username]["servers"] = []
if access.server_name not in users[username]["servers"]:
users[username]["servers"].append(access.server_name)
save_users(users)
return {
"message": f"Доступ к серверу {access.server_name} выдан пользователю {username}",
"server": access.server_name,
"user": username
}
# 5. Забрать доступ к серверу
@router.delete("/api/users/{username}/access/servers/{server_name}")
async def revoke_server_access(username: str, server_name: str, current_user: dict = Depends()):
require_admin_or_owner(current_user)
users = load_users()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
if "resource_access" in users[username] and "servers" in users[username]["resource_access"]:
if server_name in users[username]["resource_access"]["servers"]:
users[username]["resource_access"]["servers"].remove(server_name)
# Также удаляем из старого поля servers
if "servers" in users[username] and server_name in users[username]["servers"]:
users[username]["servers"].remove(server_name)
save_users(users)
return {
"message": f"Доступ к серверу {server_name} отозван у пользователя {username}",
"server": server_name,
"user": username
}
# 6. Удалить пользователя
@router.delete("/api/users/{username}")
async def delete_user(username: str, current_user: dict = Depends()):
require_owner(current_user)
users = load_users()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
if username == current_user.get("username"):
raise HTTPException(status_code=400, detail="Нельзя удалить самого себя")
if users[username].get("role") == "owner":
raise HTTPException(status_code=400, detail="Нельзя удалить владельца")
del users[username]
save_users(users)
return {
"message": f"Пользователь {username} удалён",
"username": username
}
# 7. Заблокировать пользователя
@router.post("/api/users/{username}/ban")
async def ban_user(username: str, ban_data: BanRequest, current_user: dict = Depends()):
require_admin_or_owner(current_user)
users = load_users()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
if username == current_user.get("username"):
raise HTTPException(status_code=400, detail="Нельзя заблокировать самого себя")
if users[username].get("role") == "owner":
raise HTTPException(status_code=400, detail="Нельзя заблокировать владельца")
users[username]["role"] = "banned"
users[username]["permissions"] = {
"manage_users": False,
"manage_roles": False,
"manage_servers": False,
"manage_tickets": False,
"manage_files": False,
"delete_users": False,
"view_all_resources": False
}
users[username]["ban_reason"] = ban_data.reason
save_users(users)
return {
"message": f"Пользователь {username} заблокирован",
"username": username,
"reason": ban_data.reason
}
# 8. Разблокировать пользователя
@router.post("/api/users/{username}/unban")
async def unban_user(username: str, current_user: dict = Depends()):
require_admin_or_owner(current_user)
users = load_users()
if username not in users:
raise HTTPException(status_code=404, detail="Пользователь не найден")
if users[username].get("role") != "banned":
raise HTTPException(status_code=400, detail="Пользователь не заблокирован")
users[username]["role"] = "user"
users[username]["permissions"] = {
"manage_users": False,
"manage_roles": False,
"manage_servers": True,
"manage_tickets": True,
"manage_files": True,
"delete_users": False,
"view_all_resources": False
}
users[username].pop("ban_reason", None)
save_users(users)
return {
"message": f"Пользователь {username} разблокирован",
"username": username
}

View File

@@ -1,4 +1,24 @@
{
"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",
@@ -6,7 +26,24 @@
"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",
@@ -15,6 +52,23 @@
"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": []
}
}
}