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 = `

MinIO Speicherauswertung

Datum: ${report.date}

Zusammenfassung

Bucket-Details

${report.buckets.map(bucket => ` `).join('')}
Bucket Name Größe Objekte Letzte Änderung
${bucket.name} ${bucket.sizeFormatted} ${bucket.objects} ${bucket.lastModified}

Benutzer

`; 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();