Files
minio-webui/backend/src/api/users/index.js
T
paul bb44b143ec 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
2025-07-22 16:29:53 +02:00

193 lines
4.8 KiB
JavaScript

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;