feat: verarbeitete/ignorierte E-Mails beim Prüfen in IMAP-Ordner verschieben
Build and Push Multi-Platform Images / build-and-push (push) Successful in 38s

- Cleanup-Cron von EmailDownloadService in ImapFolderService verschoben,
  damit er auch aus EmailController aufrufbar ist (zirkuläre Abhängigkeit vermieden)
- Beim Klick auf „Anhänge prüfen" wird der IMAP-Cleanup fire-and-forget gestartet
- Beim Klick auf „Bereits verarbeitete Anhänge prüfen" werden zusätzlich alle
  E-Mails im IMAP-Posteingang, die in der DB als verarbeitet (Status 1) oder
  ignoriert (Status 3) markiert sind, in den Ordner „importiert" verschoben
- Erfolgsmeldung zeigt Anzahl verschobener E-Mails an

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-18 13:04:03 +02:00
parent 41eed1871e
commit 969f0ae0b1
5 changed files with 105 additions and 43 deletions
@@ -1,5 +1,6 @@
import { Injectable, Logger } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Cron } from '@nestjs/schedule';
import { ImapFlow } from 'imapflow';
@Injectable()
@@ -21,6 +22,91 @@ export class ImapFolderService {
});
}
@Cron('0 3 * * *', { timeZone: 'Europe/Berlin' })
async cleanupImportedEmails(): Promise<void> {
if (!this.configService.get<string>('IMAP_HOST')) return;
const importedFolder = this.configService.get<string>('IMAP_IMPORTED_FOLDER', 'importiert');
const trashFolder = this.configService.get<string>('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<number> {
if (!this.configService.get<string>('IMAP_HOST') || messageIds.length === 0) return 0;
const importedFolder = this.configService.get<string>('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<void> {
if (!this.configService.get<string>('IMAP_HOST')) return;