import React, { useState, useEffect } from 'react'; import { Card, Table, Button, Modal, Form, Input, Select, Switch, Space, message, Tabs, Tag, Popconfirm, Tooltip, Row, Col, Badge, Tree, Typography, Divider } from 'antd'; import { UserOutlined, TeamOutlined, SecurityScanOutlined, PlusOutlined, EditOutlined, DeleteOutlined, HistoryOutlined, UserAddOutlined, KeyOutlined, DownloadOutlined } from '@ant-design/icons'; import axios from 'axios'; import { useAuth } from '../contexts/AuthContext'; const { TabPane } = Tabs; const { Option } = Select; const { Text, Title } = Typography; const { TextArea } = Input; // Comprehensive Permission tree data structure - Enterprise RBAC const PERMISSION_TREE = [ { title: '📊 Dashboard', key: 'dashboard', children: [ { title: 'View Dashboard', key: 'dashboard.read' }, { title: 'View Statistics', key: 'dashboard.statistics' }, { title: 'View Performance Metrics', key: 'dashboard.metrics' } ] }, { title: '🌐 Frontend Management', key: 'frontends', children: [ { title: 'View Frontends', key: 'frontends.read' }, { title: 'Create Frontend', key: 'frontends.create' }, { title: 'Edit Frontend', key: 'frontends.update' }, { title: 'Delete Frontend', key: 'frontends.delete' }, { title: 'Toggle Frontend', key: 'frontends.toggle' }, { title: 'View Frontend History', key: 'frontends.history' } ] }, { title: '🔧 Backend Management', key: 'backends', children: [ { title: 'View Backends', key: 'backends.read' }, { title: 'Create Backend', key: 'backends.create' }, { title: 'Edit Backend', key: 'backends.update' }, { title: 'Delete Backend', key: 'backends.delete' }, { title: 'Toggle Backend', key: 'backends.toggle' }, { title: 'Manage Servers', key: 'backends.servers' }, { title: 'View Backend History', key: 'backends.history' } ] }, { title: '🛡️ WAF Management', key: 'waf', children: [ { title: 'View WAF Rules', key: 'waf.read' }, { title: 'Create WAF Rule', key: 'waf.create' }, { title: 'Edit WAF Rule', key: 'waf.update' }, { title: 'Delete WAF Rule', key: 'waf.delete' }, { title: 'Toggle WAF Rule', key: 'waf.toggle' }, { title: 'View WAF History', key: 'waf.history' } ] }, { title: '🔐 SSL Management', key: 'ssl', children: [ { title: 'View SSL Certificates', key: 'ssl.read' }, { title: 'Upload SSL Certificate', key: 'ssl.create' }, { title: 'Edit SSL Certificate', key: 'ssl.update' }, { title: 'Delete SSL Certificate', key: 'ssl.delete' }, { title: 'Download SSL Certificate', key: 'ssl.download' }, { title: 'View SSL History', key: 'ssl.history' } ] }, { title: '🚀 Apply Management', key: 'apply', children: [ { title: 'View Pending Changes', key: 'apply.read' }, { title: 'Apply Changes', key: 'apply.execute' }, { title: 'Reject Changes', key: 'apply.reject' }, { title: 'View Apply History', key: 'apply.history' }, { title: 'Bulk Apply', key: 'apply.bulk' }, { title: 'Emergency Apply', key: 'apply.emergency' } ] }, { title: '🤖 Agent Management', key: 'agents', children: [ { title: 'View Agents', key: 'agents.read' }, { title: 'Create Agent', key: 'agents.create' }, { title: 'Edit Agent', key: 'agents.update' }, { title: 'Delete Agent', key: 'agents.delete' }, { title: 'Generate Agent Script', key: 'agents.script' }, { title: 'Toggle Agent', key: 'agents.toggle' }, { title: 'Upgrade Agent', key: 'agents.upgrade' }, { title: 'View Agent Version Info', key: 'agents.version' }, { title: 'View Agent Logs', key: 'agents.logs' } ] }, { title: '🏢 Cluster Management', key: 'clusters', children: [ { title: 'View Clusters', key: 'clusters.read' }, { title: 'Create Cluster', key: 'clusters.create' }, { title: 'Edit Cluster', key: 'clusters.update' }, { title: 'Delete Cluster', key: 'clusters.delete' }, { title: 'Switch Cluster', key: 'clusters.switch' }, { title: 'View Cluster Config', key: 'clusters.config' } ] }, { title: '📝 Configuration', key: 'config', children: [ { title: 'View Configuration', key: 'config.read' }, { title: 'Edit Configuration', key: 'config.update' }, { title: 'Download Config', key: 'config.download' }, { title: 'Upload Config', key: 'config.upload' }, { title: 'Backup Config', key: 'config.backup' }, { title: 'Restore Config', key: 'config.restore' }, { title: 'View Config History', key: 'config.history' }, { title: 'Bulk Import Configurations', key: 'config.bulk_import' }, { title: 'View Config Request', key: 'config.view_request' }, { title: 'Download Config Request', key: 'config.download_request' } ] }, { title: '👥 User Management', key: 'users', children: [ { title: 'View Users', key: 'users.read' }, { title: 'Create User', key: 'users.create' }, { title: 'Edit User', key: 'users.update' }, { title: 'Delete User', key: 'users.delete' }, { title: 'Change Password', key: 'users.password' }, { title: 'Assign Roles', key: 'users.roles' } ] }, { title: '🎭 Role Management', key: 'roles', children: [ { title: 'View Roles', key: 'roles.read' }, { title: 'Create Role', key: 'roles.create' }, { title: 'Edit Role', key: 'roles.update' }, { title: 'Delete Role', key: 'roles.delete' }, { title: 'Manage Permissions', key: 'roles.permissions' } ] }, { title: '📈 Statistics & Monitoring', key: 'statistics', children: [ { title: 'View Statistics', key: 'statistics.read' }, { title: 'View Performance Metrics', key: 'statistics.performance' }, { title: 'View Agent Status', key: 'statistics.agents' }, { title: 'View System Health', key: 'statistics.health' }, { title: 'Export Statistics', key: 'statistics.export' } ] }, { title: '📋 Activity Logs', key: 'activity', children: [ { title: 'View Activity Logs', key: 'activity.read' }, { title: 'View All User Activities', key: 'activity.all' }, { title: 'Export Activity Logs', key: 'activity.export' } ] }, { title: '⚙️ Settings', key: 'settings', children: [ { title: 'View Settings', key: 'settings.read' }, { title: 'Update Settings', key: 'settings.update' }, { title: 'System Settings', key: 'settings.system' }, { title: 'Security Settings', key: 'settings.security' } ] }, { title: '🔧 System Administration', key: 'system', children: [ { title: 'System Restart', key: 'system.restart' }, { title: 'View System Logs', key: 'system.logs' }, { title: 'Database Management', key: 'system.database' }, { title: 'Service Management', key: 'system.services' }, { title: 'Emergency Mode', key: 'system.emergency' } ] } ]; const UserManagement = () => { const { isAdmin } = useAuth(); // Get admin status from auth context const [activeTab, setActiveTab] = useState('users'); // Users state const [users, setUsers] = useState([]); const [usersLoading, setUsersLoading] = useState(false); const [userModalVisible, setUserModalVisible] = useState(false); const [userForm] = Form.useForm(); const [editingUser, setEditingUser] = useState(null); const [passwordModalVisible, setPasswordModalVisible] = useState(false); const [passwordForm] = Form.useForm(); // Roles state const [roles, setRoles] = useState([]); const [rolesLoading, setRolesLoading] = useState(false); const [roleModalVisible, setRoleModalVisible] = useState(false); const [roleForm] = Form.useForm(); const [editingRole, setEditingRole] = useState(null); const [checkedPermissions, setCheckedPermissions] = useState([]); // Role assignment state const [roleAssignmentModalVisible, setRoleAssignmentModalVisible] = useState(false); const [assignmentForm] = Form.useForm(); const [selectedUser, setSelectedUser] = useState(null); // Activity logs state const [activities, setActivities] = useState([]); const [activitiesLoading, setActivitiesLoading] = useState(false); // Cluster state for role assignment const [clusters, setClusters] = useState([]); const [selectedClusters, setSelectedClusters] = useState([]); // Search states const [userSearchText, setUserSearchText] = useState(''); const [filteredUsers, setFilteredUsers] = useState([]); const [roleSearchText, setRoleSearchText] = useState(''); const [filteredRoles, setFilteredRoles] = useState([]); const [activitySearchText, setActivitySearchText] = useState(''); const [filteredActivities, setFilteredActivities] = useState([]); // Fetch data on component mount useEffect(() => { fetchUsers(); fetchRoles(); fetchUserActivity(); fetchClusters(); }, []); // Update filtered data when original data changes useEffect(() => { setFilteredUsers(users); }, [users]); useEffect(() => { setFilteredRoles(roles); }, [roles]); useEffect(() => { setFilteredActivities(activities); }, [activities]); // Search handlers const handleUserSearch = (value) => { setUserSearchText(value); if (!value) { setFilteredUsers(users); } else { const filtered = users.filter(user => user.username.toLowerCase().includes(value.toLowerCase()) || user.email.toLowerCase().includes(value.toLowerCase()) || user.full_name?.toLowerCase().includes(value.toLowerCase()) ); setFilteredUsers(filtered); } }; const handleRoleSearch = (value) => { setRoleSearchText(value); if (!value) { setFilteredRoles(roles); } else { const filtered = roles.filter(role => role.name.toLowerCase().includes(value.toLowerCase()) || role.description?.toLowerCase().includes(value.toLowerCase()) ); setFilteredRoles(filtered); } }; const handleActivitySearch = (value) => { setActivitySearchText(value); if (!value) { setFilteredActivities(activities); } else { const filtered = activities.filter(activity => { const searchLower = value.toLowerCase(); // Search in all text fields const actionMatch = activity.action?.toLowerCase().includes(searchLower); const usernameMatch = activity.username?.toLowerCase().includes(searchLower); const resourceMatch = activity.resource?.toLowerCase().includes(searchLower); const resourceTypeMatch = activity.resource_type?.toLowerCase().includes(searchLower); const ipMatch = activity.ip_address?.toLowerCase().includes(searchLower); // Search in timestamp (formatted date) let timestampMatch = false; const dateValue = activity.timestamp || activity.created_at; if (dateValue) { try { let parsedDate; if (typeof dateValue === 'string') { parsedDate = new Date(dateValue.includes('T') && !dateValue.endsWith('Z') ? dateValue + 'Z' : dateValue); } else { parsedDate = new Date(dateValue); } if (!isNaN(parsedDate.getTime())) { timestampMatch = parsedDate.toLocaleString().toLowerCase().includes(searchLower); } } catch (e) { // Ignore date parsing errors for search } } // Search in details JSON let detailsMatch = false; if (activity.details) { try { const detailsString = typeof activity.details === 'string' ? activity.details : JSON.stringify(activity.details); detailsMatch = detailsString.toLowerCase().includes(searchLower); } catch (e) { // Ignore JSON parsing errors for search } } return actionMatch || usernameMatch || resourceMatch || resourceTypeMatch || ipMatch || timestampMatch || detailsMatch; }); setFilteredActivities(filtered); } }; // Export activities to CSV const exportActivitiesToCSV = () => { try { // Prepare CSV headers const headers = ['User', 'Email', 'Action', 'Resource Type', 'Resource ID', 'IP Address', 'Time', 'Details']; // Prepare CSV rows const rows = filteredActivities.map(activity => { const dateValue = activity.timestamp || activity.created_at; let formattedDate = 'N/A'; if (dateValue) { try { let parsedDate; if (typeof dateValue === 'string') { parsedDate = new Date(dateValue.includes('T') && !dateValue.endsWith('Z') ? dateValue + 'Z' : dateValue); } else { parsedDate = new Date(dateValue); } if (!isNaN(parsedDate.getTime())) { formattedDate = parsedDate.toLocaleString(); } } catch (e) { // Keep default N/A } } // Format details as readable string let detailsString = ''; if (activity.details) { try { const details = typeof activity.details === 'string' ? JSON.parse(activity.details) : activity.details; detailsString = Object.entries(details) .map(([key, value]) => `${key}: ${value}`) .join('; '); } catch (e) { detailsString = String(activity.details); } } return [ activity.username || 'N/A', activity.email || 'N/A', activity.action || 'N/A', activity.resource_type || 'N/A', activity.resource_id || 'N/A', activity.ip_address || 'N/A', formattedDate, detailsString ]; }); // Create CSV content const csvContent = [ headers.join(','), ...rows.map(row => row.map(cell => `"${String(cell).replace(/"/g, '""')}"`).join(',')) ].join('\n'); // Create download link const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); const url = URL.createObjectURL(blob); link.setAttribute('href', url); link.setAttribute('download', `user_activities_${new Date().toISOString().split('T')[0]}.csv`); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); message.success(`Exported ${filteredActivities.length} activities to CSV`); } catch (error) { console.error('Error exporting activities:', error); message.error('Failed to export activities to CSV'); } }; // ==== CLUSTERS MANAGEMENT ==== const fetchClusters = async () => { try { const response = await axios.get('/api/clusters'); setClusters(response.data.clusters || []); } catch (error) { console.error('Failed to fetch clusters:', error); // Don't show error message for clusters as it's not critical } }; // ==== USERS MANAGEMENT ==== const fetchUsers = async () => { setUsersLoading(true); try { const response = await axios.get('/api/users'); setUsers(response.data.users); } catch (error) { message.error('Failed to fetch users: ' + error.message); } finally { setUsersLoading(false); } }; const handleCreateUser = () => { setEditingUser(null); userForm.resetFields(); setUserModalVisible(true); }; const handleEditUser = (user) => { setEditingUser(user); userForm.setFieldsValue({ username: user.username, email: user.email, full_name: user.full_name, phone: user.phone, is_active: user.is_active, is_verified: user.is_verified, role_ids: user.roles ? user.roles.map(role => role.id) : [] }); setUserModalVisible(true); }; const handleUserSubmit = async (values) => { try { if (editingUser) { await axios.put(`/api/users/${editingUser.id}`, values); message.success('User updated successfully'); } else { await axios.post('/api/users', values); message.success('User created successfully'); } setUserModalVisible(false); fetchUsers(); } catch (error) { message.error(error.response?.data?.detail || 'Operation failed'); } }; const handleDeleteUser = async (user) => { try { await axios.delete(`/api/users/${user.id}`); message.success(`User '${user.username}' deleted successfully`); fetchUsers(); } catch (error) { message.error(error.response?.data?.detail || 'Failed to delete user'); } }; const handleChangePassword = (user) => { setSelectedUser(user); passwordForm.resetFields(); setPasswordModalVisible(true); }; const handlePasswordSubmit = async (values) => { try { await axios.put(`/api/users/${selectedUser.id}/password`, values); message.success('Password changed successfully'); setPasswordModalVisible(false); } catch (error) { message.error(error.response?.data?.detail || 'Failed to change password'); } }; const handleAssignRoles = (user) => { setSelectedUser(user); assignmentForm.setFieldsValue({ role_ids: user.roles.map(role => role.id) }); setRoleAssignmentModalVisible(true); }; const handleRoleAssignmentSubmit = async (values) => { try { await axios.post('/api/user-roles', { user_id: selectedUser.id, role_ids: values.role_ids || [] }); message.success('Roles assigned successfully'); setRoleAssignmentModalVisible(false); fetchUsers(); } catch (error) { message.error(error.response?.data?.detail || 'Failed to assign roles'); } }; // ==== ROLES MANAGEMENT ==== const fetchRoles = async () => { setRolesLoading(true); try { const response = await axios.get('/api/roles'); // Parse JSON strings for permissions and cluster_ids const parsedRoles = response.data.roles.map(role => ({ ...role, permissions: typeof role.permissions === 'string' ? JSON.parse(role.permissions) : (role.permissions || []), cluster_ids: typeof role.cluster_ids === 'string' ? JSON.parse(role.cluster_ids) : (role.cluster_ids || null) })); setRoles(parsedRoles); } catch (error) { message.error('Failed to fetch roles: ' + error.message); } finally { setRolesLoading(false); } }; const handleCreateRole = () => { setEditingRole(null); roleForm.resetFields(); setCheckedPermissions([]); setRoleModalVisible(true); }; const handleEditRole = (role) => { setEditingRole(role); roleForm.setFieldsValue({ name: role.name, display_name: role.display_name, description: role.description, is_active: role.is_active, cluster_ids: role.cluster_ids || [] }); // Convert permissions array to checked keys array const permissions = role.permissions || []; setCheckedPermissions(Array.isArray(permissions) ? permissions : []); setRoleModalVisible(true); }; const handleRoleSubmit = async (values) => { // Use checked permissions as array (new format) const roleData = { ...values, permissions: checkedPermissions, cluster_ids: values.cluster_ids || null }; try { if (editingRole) { await axios.put(`/api/roles/${editingRole.id}`, roleData); message.success('Role updated successfully'); } else { await axios.post('/api/roles', roleData); message.success('Role created successfully'); } setRoleModalVisible(false); roleForm.resetFields(); setCheckedPermissions([]); fetchRoles(); } catch (error) { message.error(error.response?.data?.detail || 'Operation failed'); } }; const handleDeleteRole = async (role) => { try { await axios.delete(`/api/roles/${role.id}`); message.success(`Role '${role.display_name}' deleted successfully`); fetchRoles(); } catch (error) { message.error(error.response?.data?.detail || 'Failed to delete role'); } }; // ==== ACTIVITY LOGS ==== const fetchUserActivity = async () => { setActivitiesLoading(true); try { const token = localStorage.getItem('token'); console.log('UserManagement token check:', token ? `Token exists (${token.length} chars)` : 'Token is null/empty'); // Check for null, empty, or 'null' string if (!token || token === 'null' || token === 'undefined' || token.trim() === '') { console.error('No valid authentication token found for UserManagement'); message.error('Authentication required. Please login again.'); window.location.href = '/login'; return; } // Fetch user activity from backend console.log('🔍 ACTIVITY DEBUG: Fetching user activities with token:', token ? 'Token exists' : 'No token'); const response = await axios.get('/api/user-activity', { headers: { Authorization: `Bearer ${token}` } }); console.log('🔍 ACTIVITY DEBUG: API Response:', response.data); console.log('🔍 ACTIVITY DEBUG: Activities count:', response.data.activities?.length || 0); console.log('🔍 ACTIVITY DEBUG: Total from API:', response.data.total || 0); setActivities(response.data.activities || []); } catch (error) { console.error('🔍 ACTIVITY DEBUG: Error in fetchUserActivity:', error); console.error('🔍 ACTIVITY DEBUG: Error response:', error.response?.data); console.error('🔍 ACTIVITY DEBUG: Error status:', error.response?.status); setActivities([]); } finally { setActivitiesLoading(false); } }; // Table columns definitions const userColumns = [ { title: 'Username', dataIndex: 'username', key: 'username', render: (text, record) => ( {text} {!record.is_active && Inactive} {record.is_verified && Verified} ) }, { title: 'Full Name', dataIndex: 'full_name', key: 'full_name' }, { title: 'Email', dataIndex: 'email', key: 'email' }, { title: 'Roles', dataIndex: 'roles', key: 'roles', render: (roles) => ( {roles?.map(role => ( {role.display_name} )) || []} {(!roles || roles.length === 0) && No roles assigned} ) }, { title: 'Last Login', dataIndex: 'last_login_at', key: 'last_login_at', render: (date) => date ? new Date(date).toLocaleString() : 'Never' }, { title: 'Actions', key: 'actions', render: (_, record) => ( {isAdmin() && ( <> )} } > {/* Roles Tab */} Roles ({roles.length}) } key="roles" >
handleRoleSearch(e.target.value)} style={{ width: '100%', height: 32, paddingLeft: 8, paddingRight: 8, border: '1px solid #d9d9d9', borderRadius: 6, fontSize: 14, outline: 'none', boxShadow: 'none', backgroundColor: '#fff', transition: 'border-color 0.3s ease' }} onFocus={(e) => { e.target.style.borderColor = '#1890ff'; e.target.style.outline = 'none'; e.target.style.boxShadow = 'none'; }} onBlur={(e) => { e.target.style.borderColor = '#d9d9d9'; }} onMouseOver={(e) => { if (e.target !== document.activeElement) { e.target.style.borderColor = '#40a9ff'; } }} onMouseOut={(e) => { if (e.target !== document.activeElement) { e.target.style.borderColor = '#d9d9d9'; } }} />
{isAdmin() && ( )} } >
{/* Activity Logs Tab */} Activity Logs } key="activity" > handleActivitySearch(e.target.value)} style={{ width: 300, display: 'flex', alignItems: 'center' }} allowClear /> } >
`Total ${total} activities`, pageSizeOptions: ['10', '20', '50', '100'] }} scroll={{ x: 1200 }} size="small" /> {/* User Modal */} setUserModalVisible(false)} footer={null} width={600} >
} /> {!editingUser && ( )} {/* Role Modal */} setRoleModalVisible(false)} footer={null} width={800} >