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

@@ -0,0 +1,327 @@
import { useState, useEffect } from 'react';
import { Users, Shield, Ban, Trash2, UserCheck, Server, AlertCircle, CheckCircle } from 'lucide-react';
import axios from 'axios';
const UserManagement = ({ currentUser, addNotification }) => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [selectedUser, setSelectedUser] = useState(null);
const [showRoleModal, setShowRoleModal] = useState(false);
const [showAccessModal, setShowAccessModal] = useState(false);
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000';
// Загрузка пользователей
const loadUsers = async () => {
try {
const token = localStorage.getItem('token');
const response = await axios.get(`${API_URL}/api/users`, {
headers: { Authorization: `Bearer ${token}` }
});
// Преобразуем объект в массив если нужно
const usersData = Array.isArray(response.data)
? response.data
: Object.values(response.data);
setUsers(usersData);
setLoading(false);
} catch (error) {
console.error('Ошибка загрузки пользователей:', error);
addNotification('error', 'Ошибка загрузки пользователей');
setLoading(false);
}
};
useEffect(() => {
loadUsers();
}, []);
// Изменить роль
const changeRole = async (username, newRole) => {
try {
const token = localStorage.getItem('token');
await axios.put(
`${API_URL}/api/users/${username}/role`,
{ role: newRole },
{ headers: { Authorization: `Bearer ${token}` } }
);
addNotification('success', `Роль пользователя ${username} изменена на ${newRole}`);
loadUsers();
setShowRoleModal(false);
} catch (error) {
console.error('Ошибка изменения роли:', error);
addNotification('error', error.response?.data?.detail || 'Ошибка изменения роли');
}
};
// Заблокировать пользователя
const banUser = async (username) => {
if (!confirm(`Заблокировать пользователя ${username}?`)) return;
try {
const token = localStorage.getItem('token');
await axios.post(
`${API_URL}/api/users/${username}/ban`,
{ reason: 'Заблокирован администратором' },
{ headers: { Authorization: `Bearer ${token}` } }
);
addNotification('success', `Пользователь ${username} заблокирован`);
loadUsers();
} catch (error) {
console.error('Ошибка блокировки:', error);
addNotification('error', error.response?.data?.detail || 'Ошибка блокировки');
}
};
// Разблокировать пользователя
const unbanUser = async (username) => {
try {
const token = localStorage.getItem('token');
await axios.post(
`${API_URL}/api/users/${username}/unban`,
{},
{ headers: { Authorization: `Bearer ${token}` } }
);
addNotification('success', `Пользователь ${username} разблокирован`);
loadUsers();
} catch (error) {
console.error('Ошибка разблокировки:', error);
addNotification('error', error.response?.data?.detail || 'Ошибка разблокировки');
}
};
// Удалить пользователя
const deleteUser = async (username) => {
if (!confirm(`Удалить пользователя ${username}? Это действие необратимо!`)) return;
try {
const token = localStorage.getItem('token');
await axios.delete(
`${API_URL}/api/users/${username}`,
{ headers: { Authorization: `Bearer ${token}` } }
);
addNotification('success', `Пользователь ${username} удалён`);
loadUsers();
} catch (error) {
console.error('Ошибка удаления:', error);
addNotification('error', error.response?.data?.detail || 'Ошибка удаления');
}
};
// Цвета ролей
const getRoleColor = (role) => {
switch (role) {
case 'owner': return 'text-yellow-400';
case 'admin': return 'text-red-400';
case 'support': return 'text-blue-400';
case 'user': return 'text-green-400';
case 'banned': return 'text-gray-400';
default: return 'text-gray-400';
}
};
const getRoleName = (role) => {
switch (role) {
case 'owner': return 'Владелец';
case 'admin': return 'Администратор';
case 'support': return 'Поддержка';
case 'user': return 'Пользователь';
case 'banned': return 'Заблокирован';
default: return role;
}
};
if (loading) {
return (
<div className="flex items-center justify-center h-64">
<div className="text-gray-400">Загрузка пользователей...</div>
</div>
);
}
return (
<div className="p-6">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-3">
<Users className="w-8 h-8 text-blue-400" />
<div>
<h2 className="text-2xl font-bold text-white">Управление пользователями</h2>
<p className="text-gray-400">Всего пользователей: {users.length}</p>
</div>
</div>
</div>
{/* Список пользователей */}
<div className="grid gap-4">
{users.map((user) => (
<div
key={user.username}
className="bg-gray-800 rounded-lg p-4 border border-gray-700 hover:border-gray-600 transition-colors"
>
<div className="flex items-center justify-between">
{/* Информация о пользователе */}
<div className="flex items-center gap-4">
<div className="w-12 h-12 bg-gray-700 rounded-full flex items-center justify-center">
<Users className="w-6 h-6 text-gray-400" />
</div>
<div>
<div className="flex items-center gap-2">
<h3 className="text-lg font-semibold text-white">{user.username}</h3>
{user.role === 'owner' && (
<span className="px-2 py-1 bg-yellow-500/20 text-yellow-400 text-xs rounded">
👑 Владелец
</span>
)}
{user.role === 'admin' && (
<span className="px-2 py-1 bg-red-500/20 text-red-400 text-xs rounded">
🛡 Админ
</span>
)}
{user.role === 'support' && (
<span className="px-2 py-1 bg-blue-500/20 text-blue-400 text-xs rounded">
💬 Поддержка
</span>
)}
{user.role === 'banned' && (
<span className="px-2 py-1 bg-gray-500/20 text-gray-400 text-xs rounded">
🚫 Заблокирован
</span>
)}
</div>
<div className="flex items-center gap-4 mt-1 text-sm text-gray-400">
<span className={getRoleColor(user.role)}>
{getRoleName(user.role)}
</span>
{user.resource_access?.servers && user.resource_access.servers.length > 0 && (
<span className="flex items-center gap-1">
<Server className="w-4 h-4" />
{user.resource_access.servers.length} серверов
</span>
)}
</div>
{/* Права */}
{user.permissions && (
<div className="flex flex-wrap gap-2 mt-2">
{user.permissions.manage_users && (
<span className="px-2 py-0.5 bg-green-500/20 text-green-400 text-xs rounded">
Управление пользователями
</span>
)}
{user.permissions.manage_servers && (
<span className="px-2 py-0.5 bg-blue-500/20 text-blue-400 text-xs rounded">
Управление серверами
</span>
)}
{user.permissions.view_all_resources && (
<span className="px-2 py-0.5 bg-purple-500/20 text-purple-400 text-xs rounded">
Просмотр всех ресурсов
</span>
)}
</div>
)}
</div>
</div>
{/* Действия */}
{currentUser.role === 'owner' && user.username !== currentUser.username && (
<div className="flex items-center gap-2">
{/* Изменить роль */}
<button
onClick={() => {
setSelectedUser(user);
setShowRoleModal(true);
}}
className="px-3 py-2 bg-blue-500 hover:bg-blue-600 text-white rounded transition-colors flex items-center gap-2"
title="Изменить роль"
>
<Shield className="w-4 h-4" />
Роль
</button>
{/* Заблокировать/Разблокировать */}
{user.role !== 'banned' ? (
<button
onClick={() => banUser(user.username)}
className="px-3 py-2 bg-orange-500 hover:bg-orange-600 text-white rounded transition-colors flex items-center gap-2"
title="Заблокировать"
>
<Ban className="w-4 h-4" />
</button>
) : (
<button
onClick={() => unbanUser(user.username)}
className="px-3 py-2 bg-green-500 hover:bg-green-600 text-white rounded transition-colors flex items-center gap-2"
title="Разблокировать"
>
<UserCheck className="w-4 h-4" />
</button>
)}
{/* Удалить */}
<button
onClick={() => deleteUser(user.username)}
className="px-3 py-2 bg-red-500 hover:bg-red-600 text-white rounded transition-colors flex items-center gap-2"
title="Удалить"
>
<Trash2 className="w-4 h-4" />
</button>
</div>
)}
</div>
</div>
))}
</div>
{/* Модальное окно изменения роли */}
{showRoleModal && selectedUser && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div className="bg-gray-800 rounded-lg p-6 w-96 border border-gray-700">
<h3 className="text-xl font-bold text-white mb-4">
Изменить роль: {selectedUser.username}
</h3>
<div className="space-y-2">
{['owner', 'admin', 'support', 'user', 'banned'].map((role) => (
<button
key={role}
onClick={() => changeRole(selectedUser.username, role)}
className={`w-full px-4 py-3 rounded text-left transition-colors ${
selectedUser.role === role
? 'bg-blue-500 text-white'
: 'bg-gray-700 text-gray-300 hover:bg-gray-600'
}`}
>
<div className="font-semibold">{getRoleName(role)}</div>
<div className="text-sm opacity-75">
{role === 'owner' && 'Полный контроль над панелью'}
{role === 'admin' && 'Управление панелью без изменения ролей'}
{role === 'support' && 'Работа с тикетами поддержки'}
{role === 'user' && 'Базовые возможности'}
{role === 'banned' && 'Доступ заблокирован'}
</div>
</button>
))}
</div>
<button
onClick={() => setShowRoleModal(false)}
className="w-full mt-4 px-4 py-2 bg-gray-700 hover:bg-gray-600 text-white rounded transition-colors"
>
Отмена
</button>
</div>
</div>
)}
</div>
);
};
export default UserManagement;