Files
minio-webui/backend/src/services/report.service.js
T
paul b4fc144cd3 fix: Support Exchange servers without authentication
- Make email authentication optional in transporter config
- Only add auth object if both username and password are provided
- Update initialization to only require SMTP_HOST
- Add logging to show whether auth is enabled
- Update documentation with no-auth configuration examples
- Fix "Unrecognized authentication type" error for open relays

Exchange servers configured as internal relays often don't
require authentication. This fix allows using them by leaving
SMTP_USER and SMTP_PASS empty in the .env file.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-24 08:58:45 +02:00

251 lines
7.2 KiB
JavaScript

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) {
this.initializeMailer();
this.setupSchedule();
} else {
logger.warn('Email host not configured. Report scheduling disabled.');
}
}
initializeMailer() {
const transportConfig = {
host: config.email.host,
port: config.email.port,
secure: config.email.secure,
// For Exchange Server on port 25 without TLS
tls: {
rejectUnauthorized: false, // Accept self-signed certificates
// If you want to disable TLS completely when secure is false:
...(config.email.port === 25 && !config.email.secure ? { enabled: false } : {})
},
// Disable STARTTLS for port 25 if not using secure
requireTLS: config.email.secure,
ignoreTLS: config.email.port === 25 && !config.email.secure,
};
// Only add auth if username and password are provided
if (config.email.auth.user && config.email.auth.pass) {
transportConfig.auth = {
user: config.email.auth.user,
pass: config.email.auth.pass,
};
logger.info('Email configured with authentication');
} else {
logger.info('Email configured without authentication (open relay)');
}
this.transporter = nodemailer.createTransport(transportConfig);
// 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();