import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Cron } from '@nestjs/schedule'; import { ImapFlow } from 'imapflow'; @Injectable() export class ImapFolderService { private readonly logger = new Logger(ImapFolderService.name); constructor(private readonly configService: ConfigService) {} private createClient(): ImapFlow { return new ImapFlow({ host: this.configService.get('IMAP_HOST', ''), port: this.configService.get('IMAP_PORT', 993), secure: this.configService.get('IMAP_USE_SSL', 'true') === 'true', auth: { user: this.configService.get('IMAP_USERNAME', ''), pass: this.configService.get('IMAP_PASSWORD', ''), }, logger: false, }); } @Cron('0 3 * * *', { timeZone: 'Europe/Berlin' }) async cleanupImportedEmails(): Promise { if (!this.configService.get('IMAP_HOST')) return; const importedFolder = this.configService.get( 'IMAP_IMPORTED_FOLDER', 'importiert', ); const trashFolder = this.configService.get( 'IMAP_TRASH_FOLDER', 'Trash', ); const client = this.createClient(); try { await client.connect(); // E-Mails älter als 90 Tage in Papierkorb verschieben try { await client.mailboxOpen(importedFolder); const cutoff = new Date(); cutoff.setDate(cutoff.getDate() - 90); const oldUids = await client.search({ before: cutoff }, { uid: true }); if (Array.isArray(oldUids) && oldUids.length > 0) { await client.messageMove(oldUids, trashFolder, { uid: true }); this.logger.log( `${oldUids.length} alte E-Mail(s) aus "${importedFolder}" in "${trashFolder}" verschoben.`, ); } } catch (err: any) { this.logger.warn( `Bereinigung "${importedFolder}" nicht möglich: ${err.message}`, ); } // Papierkorb leeren try { await client.mailboxOpen(trashFolder); const trashUids = await client.search({ all: true }, { uid: true }); if (Array.isArray(trashUids) && trashUids.length > 0) { await client.messageDelete(trashUids, { uid: true }); this.logger.log( `${trashUids.length} E-Mail(s) aus "${trashFolder}" gelöscht.`, ); } } catch (err: any) { this.logger.warn( `Papierkorb "${trashFolder}" konnte nicht geleert werden: ${err.message}`, ); } } catch (err: any) { this.logger.error(`IMAP-Cleanup fehlgeschlagen: ${err.message}`); } finally { await client.logout().catch(() => {}); } } async moveProcessedInboxToImportiert(messageIds: string[]): Promise { if (!this.configService.get('IMAP_HOST') || messageIds.length === 0) return 0; const importedFolder = this.configService.get( 'IMAP_IMPORTED_FOLDER', 'importiert', ); const client = this.createClient(); let movedCount = 0; try { await client.connect(); const mailboxes = await client.list(); if (!mailboxes.some((m) => m.path === importedFolder)) { await client.mailboxCreate(importedFolder); this.logger.log(`IMAP-Ordner "${importedFolder}" erstellt.`); } const status = await client.mailboxOpen('INBOX'); if (status.exists === 0) return 0; const normalize = (id: string) => id.replace(/^<|>$/g, '').toLowerCase(); const idSet = new Set(messageIds.map(normalize)); const uidsToMove: number[] = []; for await (const msg of client.fetch('1:*', { uid: true, envelope: true, })) { const msgId = msg.envelope?.messageId; if (msgId && idSet.has(normalize(msgId))) { uidsToMove.push(msg.uid); } } if (uidsToMove.length > 0) { await client.messageMove(uidsToMove, importedFolder, { uid: true }); movedCount = uidsToMove.length; this.logger.log( `${movedCount} E-Mail(s) aus INBOX → "${importedFolder}" verschoben.`, ); } } catch (err: any) { this.logger.error( `moveProcessedInboxToImportiert fehlgeschlagen: ${err.message}`, ); } finally { await client.logout().catch(() => {}); } return movedCount; } async moveToImportiert(messageId: string): Promise { if (!this.configService.get('IMAP_HOST')) return; const importedFolder = this.configService.get( 'IMAP_IMPORTED_FOLDER', 'importiert', ); const client = this.createClient(); try { await client.connect(); const mailboxes = await client.list(); if (!mailboxes.some((m) => m.path === importedFolder)) { await client.mailboxCreate(importedFolder); this.logger.log(`IMAP-Ordner "${importedFolder}" erstellt.`); } await client.mailboxOpen('INBOX'); const uids = await client.search( { header: { 'message-id': messageId } }, { uid: true }, ); if (Array.isArray(uids) && uids.length > 0) { await client.messageMove(uids, importedFolder, { uid: true }); this.logger.log( `E-Mail ${messageId} → "${importedFolder}" verschoben.`, ); } else { this.logger.warn( `E-Mail ${messageId} nicht in INBOX gefunden (bereits verschoben?).`, ); } } catch (err: any) { this.logger.error(`IMAP moveToImportiert fehlgeschlagen: ${err.message}`); } finally { await client.logout().catch(() => {}); } } }