Initial commit: MinIO WebUI - Complete implementation
- Backend: Express.js API with MinIO CLI integration - Frontend: React with Material-UI for non-technical users - Features: Bucket management, user creation, storage monitoring - Security: JWT auth, IP filtering, encrypted passwords - Docker support for easy deployment - Automated weekly storage reports - Setup and deployment scripts included
This commit is contained in:
@@ -0,0 +1,221 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Outlet, useNavigate, useLocation } from 'react-router-dom';
|
||||
import {
|
||||
Box,
|
||||
Drawer,
|
||||
AppBar,
|
||||
Toolbar,
|
||||
List,
|
||||
Typography,
|
||||
Divider,
|
||||
IconButton,
|
||||
ListItem,
|
||||
ListItemButton,
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
Avatar,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Tooltip,
|
||||
} from '@mui/material';
|
||||
import {
|
||||
Menu as MenuIcon,
|
||||
ChevronLeft as ChevronLeftIcon,
|
||||
Dashboard as DashboardIcon,
|
||||
Storage as StorageIcon,
|
||||
People as PeopleIcon,
|
||||
Policy as PolicyIcon,
|
||||
Assessment as AssessmentIcon,
|
||||
Logout as LogoutIcon,
|
||||
AccountCircle as AccountCircleIcon,
|
||||
} from '@mui/icons-material';
|
||||
import useAuthStore from '../../store/authStore';
|
||||
|
||||
const drawerWidth = 240;
|
||||
|
||||
interface NavItem {
|
||||
text: string;
|
||||
icon: React.ReactElement;
|
||||
path: string;
|
||||
}
|
||||
|
||||
const navItems: NavItem[] = [
|
||||
{ text: 'Dashboard', icon: <DashboardIcon />, path: '/' },
|
||||
{ text: 'Buckets', icon: <StorageIcon />, path: '/buckets' },
|
||||
{ text: 'Users', icon: <PeopleIcon />, path: '/users' },
|
||||
{ text: 'Policies', icon: <PolicyIcon />, path: '/policies' },
|
||||
{ text: 'Reports', icon: <AssessmentIcon />, path: '/reports' },
|
||||
];
|
||||
|
||||
const Layout: React.FC = () => {
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const { user, logout } = useAuthStore();
|
||||
const [open, setOpen] = useState(true);
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
|
||||
const handleDrawerToggle = () => {
|
||||
setOpen(!open);
|
||||
};
|
||||
|
||||
const handleMenuOpen = (event: React.MouseEvent<HTMLElement>) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleMenuClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const handleLogout = async () => {
|
||||
await logout();
|
||||
navigate('/login');
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ display: 'flex', width: '100%' }}>
|
||||
<AppBar
|
||||
position="fixed"
|
||||
sx={{
|
||||
width: `calc(100% - ${open ? drawerWidth : 0}px)`,
|
||||
ml: `${open ? drawerWidth : 0}px`,
|
||||
transition: (theme) =>
|
||||
theme.transitions.create(['margin', 'width'], {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<Toolbar>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
aria-label="toggle drawer"
|
||||
onClick={handleDrawerToggle}
|
||||
edge="start"
|
||||
sx={{ mr: 2 }}
|
||||
>
|
||||
{open ? <ChevronLeftIcon /> : <MenuIcon />}
|
||||
</IconButton>
|
||||
|
||||
<Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}>
|
||||
MinIO WebUI
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<Tooltip title="Account">
|
||||
<IconButton
|
||||
onClick={handleMenuOpen}
|
||||
size="small"
|
||||
sx={{ ml: 2 }}
|
||||
aria-controls={Boolean(anchorEl) ? 'account-menu' : undefined}
|
||||
aria-haspopup="true"
|
||||
aria-expanded={Boolean(anchorEl) ? 'true' : undefined}
|
||||
>
|
||||
<Avatar sx={{ width: 32, height: 32 }}>
|
||||
<AccountCircleIcon />
|
||||
</Avatar>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</Box>
|
||||
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
id="account-menu"
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleMenuClose}
|
||||
onClick={handleMenuClose}
|
||||
PaperProps={{
|
||||
elevation: 0,
|
||||
sx: {
|
||||
overflow: 'visible',
|
||||
filter: 'drop-shadow(0px 2px 8px rgba(0,0,0,0.32))',
|
||||
mt: 1.5,
|
||||
'& .MuiAvatar-root': {
|
||||
width: 32,
|
||||
height: 32,
|
||||
ml: -0.5,
|
||||
mr: 1,
|
||||
},
|
||||
},
|
||||
}}
|
||||
transformOrigin={{ horizontal: 'right', vertical: 'top' }}
|
||||
anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
|
||||
>
|
||||
<MenuItem disabled>
|
||||
<Typography variant="body2">
|
||||
Logged in as {user?.role || 'Admin'}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
<Divider />
|
||||
<MenuItem onClick={handleLogout}>
|
||||
<ListItemIcon>
|
||||
<LogoutIcon fontSize="small" />
|
||||
</ListItemIcon>
|
||||
Logout
|
||||
</MenuItem>
|
||||
</Menu>
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
|
||||
<Drawer
|
||||
sx={{
|
||||
width: drawerWidth,
|
||||
flexShrink: 0,
|
||||
'& .MuiDrawer-paper': {
|
||||
width: drawerWidth,
|
||||
boxSizing: 'border-box',
|
||||
},
|
||||
}}
|
||||
variant="persistent"
|
||||
anchor="left"
|
||||
open={open}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
padding: (theme) => theme.spacing(0, 1),
|
||||
...((theme) => theme.mixins.toolbar),
|
||||
justifyContent: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" noWrap component="div">
|
||||
MinIO Manager
|
||||
</Typography>
|
||||
</Box>
|
||||
<Divider />
|
||||
<List>
|
||||
{navItems.map((item) => (
|
||||
<ListItem key={item.text} disablePadding>
|
||||
<ListItemButton
|
||||
selected={location.pathname === item.path}
|
||||
onClick={() => navigate(item.path)}
|
||||
>
|
||||
<ListItemIcon>{item.icon}</ListItemIcon>
|
||||
<ListItemText primary={item.text} />
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
))}
|
||||
</List>
|
||||
</Drawer>
|
||||
|
||||
<Box
|
||||
component="main"
|
||||
sx={{
|
||||
flexGrow: 1,
|
||||
padding: 3,
|
||||
transition: (theme) =>
|
||||
theme.transitions.create('margin', {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
marginLeft: open ? 0 : `-${drawerWidth}px`,
|
||||
mt: 8,
|
||||
}}
|
||||
>
|
||||
<Outlet />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Layout;
|
||||
Reference in New Issue
Block a user