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,193 @@
|
||||
const express = require('express');
|
||||
const { body, param, validationResult } = require('express-validator');
|
||||
const MinIOService = require('../../services/minio.service');
|
||||
const { authMiddleware } = require('../../middleware/auth.middleware');
|
||||
const { logger, logAudit } = require('../../utils/logger');
|
||||
const { AppError } = require('../../middleware/errorHandler.middleware');
|
||||
|
||||
const router = express.Router();
|
||||
const minioService = new MinIOService();
|
||||
|
||||
// Apply auth middleware to all routes
|
||||
router.use(authMiddleware);
|
||||
|
||||
// Validation rules
|
||||
const userValidation = {
|
||||
username: body('username')
|
||||
.trim()
|
||||
.notEmpty().withMessage('Username is required')
|
||||
.matches(/^[a-zA-Z0-9_-]+$/).withMessage('Invalid username format')
|
||||
.isLength({ min: 3, max: 32 }).withMessage('Username must be 3-32 characters'),
|
||||
|
||||
password: body('password')
|
||||
.notEmpty().withMessage('Password is required')
|
||||
.isLength({ min: 8 }).withMessage('Password must be at least 8 characters')
|
||||
.matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/).withMessage('Password must contain uppercase, lowercase, and number'),
|
||||
|
||||
usernameParam: param('username')
|
||||
.trim()
|
||||
.notEmpty().withMessage('Username is required')
|
||||
.matches(/^[a-zA-Z0-9_-]+$/).withMessage('Invalid username format'),
|
||||
};
|
||||
|
||||
// Handle validation errors
|
||||
const handleValidationErrors = (req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
error: 'Validation Error',
|
||||
errors: errors.array(),
|
||||
});
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
// GET /api/users
|
||||
router.get('/', async (req, res, next) => {
|
||||
try {
|
||||
const users = await minioService.listUsers();
|
||||
|
||||
res.json({
|
||||
users,
|
||||
count: users.length,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/users
|
||||
router.post('/',
|
||||
[userValidation.username, userValidation.password],
|
||||
handleValidationErrors,
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const { username, password } = req.body;
|
||||
|
||||
// Create user without bucket (admin can assign policies later)
|
||||
await minioService.executeCommand(
|
||||
`mc admin user add ${minioService.alias} ${username} ${password}`
|
||||
);
|
||||
|
||||
logAudit('USER_CREATE', {
|
||||
userId: req.user.role,
|
||||
ip: req.ip,
|
||||
resource: username,
|
||||
status: 'success',
|
||||
});
|
||||
|
||||
res.status(201).json({
|
||||
message: 'User created successfully',
|
||||
username,
|
||||
});
|
||||
} catch (error) {
|
||||
logAudit('USER_CREATE', {
|
||||
userId: req.user.role,
|
||||
ip: req.ip,
|
||||
resource: req.body.username,
|
||||
status: 'failed',
|
||||
details: { error: error.message },
|
||||
});
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// DELETE /api/users/:username
|
||||
router.delete('/:username',
|
||||
userValidation.usernameParam,
|
||||
handleValidationErrors,
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const { username } = req.params;
|
||||
|
||||
const result = await minioService.removeUser(username);
|
||||
|
||||
logAudit('USER_DELETE', {
|
||||
userId: req.user.role,
|
||||
ip: req.ip,
|
||||
resource: username,
|
||||
status: 'success',
|
||||
});
|
||||
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
logAudit('USER_DELETE', {
|
||||
userId: req.user.role,
|
||||
ip: req.ip,
|
||||
resource: req.params.username,
|
||||
status: 'failed',
|
||||
details: { error: error.message },
|
||||
});
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// PUT /api/users/:username/status
|
||||
router.put('/:username/status',
|
||||
[
|
||||
userValidation.usernameParam,
|
||||
body('status').isIn(['enabled', 'disabled']).withMessage('Status must be enabled or disabled'),
|
||||
],
|
||||
handleValidationErrors,
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const { username } = req.params;
|
||||
const { status } = req.body;
|
||||
|
||||
let result;
|
||||
if (status === 'enabled') {
|
||||
result = await minioService.enableUser(username);
|
||||
} else {
|
||||
result = await minioService.disableUser(username);
|
||||
}
|
||||
|
||||
logAudit('USER_STATUS_CHANGE', {
|
||||
userId: req.user.role,
|
||||
ip: req.ip,
|
||||
resource: username,
|
||||
status: 'success',
|
||||
details: { newStatus: status },
|
||||
});
|
||||
|
||||
res.json(result);
|
||||
} catch (error) {
|
||||
logAudit('USER_STATUS_CHANGE', {
|
||||
userId: req.user.role,
|
||||
ip: req.ip,
|
||||
resource: req.params.username,
|
||||
status: 'failed',
|
||||
details: {
|
||||
error: error.message,
|
||||
requestedStatus: req.body.status,
|
||||
},
|
||||
});
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
// GET /api/users/:username
|
||||
router.get('/:username',
|
||||
userValidation.usernameParam,
|
||||
handleValidationErrors,
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
const { username } = req.params;
|
||||
|
||||
// Get user info from list
|
||||
const users = await minioService.listUsers();
|
||||
const user = users.find(u => u.accessKey === username);
|
||||
|
||||
if (!user) {
|
||||
throw new AppError('User not found', 404);
|
||||
}
|
||||
|
||||
res.json({
|
||||
username: user.accessKey,
|
||||
status: user.status,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user