From 14f020e819dfc7d455f31931ed779db00405a010 Mon Sep 17 00:00:00 2001 From: arkonsadter Date: Wed, 14 Jan 2026 22:36:59 +0600 Subject: [PATCH] Added user account overview for admins --- VIEW_USER_PROFILES.md | 187 ++++++++++++++++++++++++++++ backend/main.py | 54 ++++++++ frontend/src/App.jsx | 21 +++- frontend/src/components/Profile.jsx | 93 ++++++++------ frontend/src/components/Users.jsx | 10 +- 5 files changed, 318 insertions(+), 47 deletions(-) create mode 100644 VIEW_USER_PROFILES.md diff --git a/VIEW_USER_PROFILES.md b/VIEW_USER_PROFILES.md new file mode 100644 index 0000000..0ff7532 --- /dev/null +++ b/VIEW_USER_PROFILES.md @@ -0,0 +1,187 @@ +# 👁️ Просмотр профилей пользователей + +## Что добавлено + +### Возможность просмотра профилей для админов и тех. поддержки +Администраторы и сотрудники технической поддержки теперь могут просматривать личные кабинеты других пользователей, нажав на их логин в списке пользователей. + +## 🎯 Как использовать + +### Просмотр профиля пользователя +1. Войдите как администратор или тех. поддержка +2. Нажмите кнопку "Пользователи" в header +3. Найдите нужного пользователя в списке +4. **Нажмите на логин пользователя** (он теперь кликабельный и подсвечивается при наведении) +5. Откроется личный кабинет этого пользователя + +### Что можно увидеть +- ✅ Имя пользователя +- ✅ Роль пользователя +- ✅ Статистику по серверам (всего, мои, доступные) +- ✅ Список серверов пользователя +- ✅ Статистику по тикетам (всего, по статусам) + +### Что нельзя сделать +- ❌ Изменить имя пользователя (вкладка скрыта) +- ❌ Изменить пароль пользователя (вкладка скрыта) +- ❌ Редактировать профиль другого пользователя + +## 🎨 Визуальные изменения + +### В списке пользователей (Users.jsx) +- **Логин пользователя** теперь кликабельный +- При наведении логин подсвечивается синим цветом +- Курсор меняется на pointer (указатель) +- Подсказка "Просмотреть профиль" при наведении + +### В личном кабинете (Profile.jsx) +- **Заголовок**: "Профиль пользователя: [username]" (вместо "Личный кабинет") +- **Подзаголовок**: "Просмотр профиля другого пользователя" +- **Вкладки**: скрыты вкладки "Имя пользователя" и "Пароль" +- **Только вкладка "Обзор"**: показывается статистика пользователя + +## 📋 Технические детали + +### Backend (main.py) + +#### Новый endpoint +```python +@app.get("/api/profile/stats/{username}") +async def get_user_profile_stats(username: str, user: dict = Depends(get_current_user)): + """Получить статистику профиля другого пользователя""" + # Проверка прав доступа + if user["role"] not in ["admin", "support"]: + raise HTTPException(403, "Недостаточно прав") + + # Возвращает статистику указанного пользователя +``` + +#### Проверка прав +- Только администраторы и тех. поддержка могут просматривать чужие профили +- Обычные пользователи получат ошибку 403 + +### Frontend + +#### App.jsx +```javascript +const [viewingUsername, setViewingUsername] = useState(null); + +const handleViewProfile = (username) => { + setViewingUsername(username); + setShowProfile(true); + setShowUsers(false); +}; +``` + +#### Users.jsx +```javascript + +``` + +#### Profile.jsx +```javascript +const isViewingOther = viewingUsername && viewingUsername !== user?.username; + +const loadStats = async () => { + const endpoint = isViewingOther + ? `${API_URL}/api/profile/stats/${viewingUsername}` + : `${API_URL}/api/profile/stats`; + // ... +}; +``` + +## 🔐 Безопасность + +### Проверка прав на уровне API +- Endpoint `/api/profile/stats/{username}` проверяет роль пользователя +- Только `admin` и `support` могут получить доступ +- Обычные пользователи получат ошибку 403 + +### Защита на уровне UI +- Вкладки изменения имени и пароля скрыты при просмотре чужого профиля +- Невозможно редактировать данные другого пользователя +- Только просмотр статистики + +## 📊 Доступные роли + +### Кто может просматривать чужие профили +1. **Администратор** (admin) - ✅ Может просматривать все профили +2. **Тех. поддержка** (support) - ✅ Может просматривать все профили +3. **Пользователь** (user) - ❌ Не может просматривать чужие профили +4. **Забанен** (banned) - ❌ Не имеет доступа к панели + +## ✅ Примеры использования + +### Сценарий 1: Проверка активности пользователя +1. Админ хочет проверить, сколько серверов у пользователя +2. Открывает "Пользователи" +3. Нажимает на логин пользователя +4. Видит статистику: 3 сервера, 5 тикетов + +### Сценарий 2: Помощь пользователю +1. Тех. поддержка получила тикет от пользователя +2. Хочет посмотреть его серверы для диагностики +3. Открывает "Пользователи" +4. Нажимает на логин пользователя +5. Видит список серверов и их названия + +### Сценарий 3: Модерация +1. Админ хочет проверить активность пользователя перед баном +2. Открывает профиль пользователя +3. Видит статистику по тикетам и серверам +4. Принимает решение о блокировке + +## 🎯 Возврат к списку пользователей + +### Из профиля пользователя +1. Нажмите кнопку "Серверы" в header +2. Вы вернётесь к главной странице +3. Снова откройте "Пользователи" для просмотра других профилей + +### Или откройте свой профиль +1. Нажмите кнопку "Личный кабинет" в header +2. Откроется ваш собственный профиль +3. Будут доступны все вкладки (Обзор, Имя пользователя, Пароль) + +## ⚠️ Важные замечания + +### Ограничения +- Нельзя редактировать чужие профили +- Нельзя изменить имя или пароль другого пользователя +- Только просмотр статистики + +### Рекомендации +- Используйте эту функцию для помощи пользователям +- Не злоупотребляйте просмотром чужих профилей +- Соблюдайте конфиденциальность данных пользователей + +## ✅ Готово! + +Функция просмотра профилей пользователей полностью интегрирована в MC Panel. Администраторы и тех. поддержка могут легко просматривать информацию о пользователях для помощи и модерации. + +### Тестирование + +1. **Войдите как администратор** + - Логин: none + - Пароль: none + +2. **Создайте тестового пользователя** + - Зарегистрируйте нового пользователя + - Создайте несколько серверов от его имени + +3. **Просмотрите его профиль** + - Откройте "Пользователи" + - Нажмите на логин тестового пользователя + - Увидите его статистику + +4. **Вернитесь к своему профилю** + - Нажмите "Личный кабинет" + - Откроется ваш профиль со всеми вкладками + +**Удобного использования! 👁️** diff --git a/backend/main.py b/backend/main.py index 820b082..48f5d29 100644 --- a/backend/main.py +++ b/backend/main.py @@ -404,6 +404,60 @@ async def get_profile_stats(user: dict = Depends(get_current_user)): "total_servers": len(owned_servers) + len(accessible_servers) } +@app.get("/api/profile/stats/{username}") +async def get_user_profile_stats(username: str, user: dict = Depends(get_current_user)): + """Получить статистику профиля другого пользователя (только для админов и тех. поддержки)""" + # Проверка прав доступа + if user["role"] not in ["admin", "support"]: + raise HTTPException(403, "Недостаточно прав для просмотра профилей других пользователей") + + users = load_users() + + # Проверка существования пользователя + if username not in users: + raise HTTPException(404, "Пользователь не найден") + + target_user = users[username] + + # Подсчитываем серверы пользователя + owned_servers = [] + accessible_servers = [] + + for server_dir in SERVERS_DIR.iterdir(): + if server_dir.is_dir(): + config = load_server_config(server_dir.name) + if config.get("owner") == username: + owned_servers.append({ + "name": server_dir.name, + "displayName": config.get("displayName", server_dir.name) + }) + elif username in target_user.get("servers", []) or target_user["role"] == "admin": + accessible_servers.append({ + "name": server_dir.name, + "displayName": config.get("displayName", server_dir.name) + }) + + # Подсчитываем тикеты + tickets = load_tickets() + user_tickets = [t for t in tickets.values() if t["author"] == username] + + tickets_stats = { + "total": len(user_tickets), + "pending": len([t for t in user_tickets if t["status"] == "pending"]), + "in_progress": len([t for t in user_tickets if t["status"] == "in_progress"]), + "closed": len([t for t in user_tickets if t["status"] == "closed"]) + } + + return { + "username": username, + "role": target_user["role"], + "owned_servers": owned_servers, + "accessible_servers": accessible_servers, + "tickets": tickets_stats, + "total_servers": len(owned_servers) + len(accessible_servers), + "is_viewing_other": True # Флаг что это чужой профиль + } + # API для серверов @app.get("/api/servers") async def get_servers(user: dict = Depends(get_current_user)): diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 25ff32f..2179ab2 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -25,6 +25,7 @@ function App() { const [showUsers, setShowUsers] = useState(false); const [showTickets, setShowTickets] = useState(false); const [showProfile, setShowProfile] = useState(false); + const [viewingUsername, setViewingUsername] = useState(null); const [connectionError, setConnectionError] = useState(false); const [theme, setTheme] = useState(localStorage.getItem('theme') || 'dark'); const [sidebarOpen, setSidebarOpen] = useState(true); @@ -118,6 +119,12 @@ function App() { loadServers(); }; + const handleViewProfile = (username) => { + setViewingUsername(username); + setShowProfile(true); + setShowUsers(false); + }; + const startServer = async (serverName) => { try { const response = await axios.post( @@ -201,7 +208,7 @@ function App() { - + ); } @@ -284,7 +291,10 @@ function App() { - - - + {!isViewingOther && ( +
+ + + +
+ )} {/* Overview Tab */} - {activeTab === 'overview' && ( + {(activeTab === 'overview' || isViewingOther) && (
{/* User Info Card */}
diff --git a/frontend/src/components/Users.jsx b/frontend/src/components/Users.jsx index 464ece7..5605817 100644 --- a/frontend/src/components/Users.jsx +++ b/frontend/src/components/Users.jsx @@ -3,7 +3,7 @@ import { Users as UsersIcon, Trash2, Shield, User } from 'lucide-react'; import axios from 'axios'; import { API_URL } from '../config'; -export default function Users({ token }) { +export default function Users({ token, onViewProfile }) { const [users, setUsers] = useState([]); const [servers, setServers] = useState([]); const [loading, setLoading] = useState(true); @@ -111,7 +111,13 @@ export default function Users({ token }) { )}
-

{user.username}

+

{user.role === 'admin' ? 'Администратор' : user.role === 'support' ? 'Тех. поддержка' : user.role === 'banned' ? 'Забанен' : 'Пользователь'}