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:
2025-07-22 16:29:53 +02:00
commit bb44b143ec
43 changed files with 6631 additions and 0 deletions
+221
View File
@@ -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;