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,233 @@
|
||||
const cron = require('node-cron');
|
||||
const nodemailer = require('nodemailer');
|
||||
const MinIOService = require('./minio.service');
|
||||
const config = require('../config');
|
||||
const { logger } = require('../utils/logger');
|
||||
|
||||
class ReportService {
|
||||
constructor() {
|
||||
this.minioService = new MinIOService();
|
||||
this.transporter = null;
|
||||
this.scheduledTask = null;
|
||||
|
||||
if (config.email.host && config.email.auth.user) {
|
||||
this.initializeMailer();
|
||||
this.setupSchedule();
|
||||
} else {
|
||||
logger.warn('Email configuration missing. Report scheduling disabled.');
|
||||
}
|
||||
}
|
||||
|
||||
initializeMailer() {
|
||||
this.transporter = nodemailer.createTransport({
|
||||
host: config.email.host,
|
||||
port: config.email.port,
|
||||
secure: config.email.secure,
|
||||
auth: {
|
||||
user: config.email.auth.user,
|
||||
pass: config.email.auth.pass,
|
||||
},
|
||||
});
|
||||
|
||||
// Verify connection
|
||||
this.transporter.verify((error) => {
|
||||
if (error) {
|
||||
logger.error('Email transporter verification failed:', error);
|
||||
} else {
|
||||
logger.info('Email transporter ready');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setupSchedule() {
|
||||
if (!cron.validate(config.reports.schedule)) {
|
||||
logger.error(`Invalid cron expression: ${config.reports.schedule}`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.scheduledTask = cron.schedule(config.reports.schedule, async () => {
|
||||
logger.info('Running scheduled storage report...');
|
||||
try {
|
||||
await this.generateAndSendReport();
|
||||
} catch (error) {
|
||||
logger.error('Scheduled report failed:', error);
|
||||
}
|
||||
});
|
||||
|
||||
logger.info(`Report scheduled with cron: ${config.reports.schedule}`);
|
||||
}
|
||||
|
||||
async generateReport() {
|
||||
const [bucketSizes, users] = await Promise.all([
|
||||
this.minioService.getBucketSizes(),
|
||||
this.minioService.listUsers(),
|
||||
]);
|
||||
|
||||
const totalSize = bucketSizes.reduce((sum, b) => sum + b.size, 0);
|
||||
const date = new Date().toISOString().split('T')[0];
|
||||
|
||||
const report = {
|
||||
date,
|
||||
summary: {
|
||||
totalBuckets: bucketSizes.length,
|
||||
totalUsers: users.length,
|
||||
totalSize,
|
||||
totalSizeFormatted: this.minioService.formatBytes(totalSize),
|
||||
},
|
||||
buckets: bucketSizes,
|
||||
users: users.map(u => u.accessKey),
|
||||
};
|
||||
|
||||
return report;
|
||||
}
|
||||
|
||||
formatReportText(report) {
|
||||
let text = `MinIO Speicherauswertung\n`;
|
||||
text += `Datum: ${report.date}\n`;
|
||||
text += `----------------------------------------\n\n`;
|
||||
|
||||
text += `Zusammenfassung:\n`;
|
||||
text += `- Buckets: ${report.summary.totalBuckets}\n`;
|
||||
text += `- Benutzer: ${report.summary.totalUsers}\n`;
|
||||
text += `- Gesamtspeicher: ${report.summary.totalSizeFormatted}\n\n`;
|
||||
|
||||
text += `Alle MinIO-User:\n`;
|
||||
report.users.forEach(user => {
|
||||
text += `- ${user}\n`;
|
||||
});
|
||||
|
||||
text += `\nAlle Buckets:\n`;
|
||||
report.buckets.forEach(bucket => {
|
||||
text += `\nBucket: ${bucket.name}\n`;
|
||||
text += ` Größe: ${bucket.sizeFormatted}\n`;
|
||||
text += ` Objekte: ${bucket.objects}\n`;
|
||||
text += ` Letzte Änderung: ${bucket.lastModified}\n`;
|
||||
});
|
||||
|
||||
text += `\n----------------------------------------\n`;
|
||||
text += `Gesamtspeicher: ${report.summary.totalSizeFormatted}\n`;
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
formatReportHTML(report) {
|
||||
let html = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
|
||||
h1 { color: #2c3e50; }
|
||||
.summary { background: #f4f4f4; padding: 15px; border-radius: 5px; margin: 20px 0; }
|
||||
table { border-collapse: collapse; width: 100%; margin: 20px 0; }
|
||||
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
||||
th { background-color: #2c3e50; color: white; }
|
||||
tr:nth-child(even) { background-color: #f2f2f2; }
|
||||
.footer { margin-top: 30px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 0.9em; color: #666; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>MinIO Speicherauswertung</h1>
|
||||
<p>Datum: ${report.date}</p>
|
||||
|
||||
<div class="summary">
|
||||
<h2>Zusammenfassung</h2>
|
||||
<ul>
|
||||
<li>Anzahl Buckets: ${report.summary.totalBuckets}</li>
|
||||
<li>Anzahl Benutzer: ${report.summary.totalUsers}</li>
|
||||
<li>Gesamtspeicher: <strong>${report.summary.totalSizeFormatted}</strong></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<h2>Bucket-Details</h2>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Bucket Name</th>
|
||||
<th>Größe</th>
|
||||
<th>Objekte</th>
|
||||
<th>Letzte Änderung</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${report.buckets.map(bucket => `
|
||||
<tr>
|
||||
<td>${bucket.name}</td>
|
||||
<td>${bucket.sizeFormatted}</td>
|
||||
<td>${bucket.objects}</td>
|
||||
<td>${bucket.lastModified}</td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Benutzer</h2>
|
||||
<ul>
|
||||
${report.users.map(user => `<li>${user}</li>`).join('')}
|
||||
</ul>
|
||||
|
||||
<div class="footer">
|
||||
<p>Dieser Bericht wurde automatisch von MinIO WebUI generiert.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
async sendEmail(report, recipients = null) {
|
||||
if (!this.transporter) {
|
||||
throw new Error('Email service not configured');
|
||||
}
|
||||
|
||||
const to = recipients || config.email.to;
|
||||
if (!to) {
|
||||
throw new Error('No recipients configured');
|
||||
}
|
||||
|
||||
const mailOptions = {
|
||||
from: config.email.from,
|
||||
to: Array.isArray(to) ? to.join(', ') : to,
|
||||
subject: `MinIO Speicherauswertung ${report.date}`,
|
||||
text: this.formatReportText(report),
|
||||
html: this.formatReportHTML(report),
|
||||
};
|
||||
|
||||
const info = await this.transporter.sendMail(mailOptions);
|
||||
logger.info(`Report email sent: ${info.messageId}`);
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
async generateAndSendReport(recipients = null) {
|
||||
const report = await this.generateReport();
|
||||
await this.sendEmail(report, recipients);
|
||||
return report;
|
||||
}
|
||||
|
||||
getScheduleInfo() {
|
||||
return {
|
||||
enabled: !!this.scheduledTask,
|
||||
schedule: config.reports.schedule,
|
||||
nextRun: this.scheduledTask ? cron.getTasks()[0]?.nextDates(1)[0] : null,
|
||||
recipients: config.email.to,
|
||||
};
|
||||
}
|
||||
|
||||
stopSchedule() {
|
||||
if (this.scheduledTask) {
|
||||
this.scheduledTask.stop();
|
||||
this.scheduledTask = null;
|
||||
logger.info('Report schedule stopped');
|
||||
}
|
||||
}
|
||||
|
||||
startSchedule() {
|
||||
if (!this.scheduledTask && this.transporter) {
|
||||
this.setupSchedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = new ReportService();
|
||||
Reference in New Issue
Block a user