feat(#278): Mail→Paperless Archivierungs-Service mit Tests #312

Merged
admin-mrrm merged 1 commit from feat/278-mail-archive-service into main 2026-05-16 15:34:35 +02:00
Owner

Summary

Neuer MailArchiveService im Mail-Modul, der eine einzelne Mail nach Paperless archiviert (Closes #278).

Ablauf in archive(ownerSub, accountId, folder, uid):

  1. Konto-Besitz verifizieren (NotFoundException sonst)
  2. Idempotenz: existiert ein mail_archive_link für (account, folder, uid)? → existierende Document-ID zurück, kein erneuter Upload
  3. Raw EML via neuem ImapService.fetchRawByUid (bodies: [''], Header-Parse für Subject/From/Date)
  4. Tags ["email", "<account-label>"] und Korrespondent (From-Adresse) über neue PaperlessClient-Helper findOrCreateTag / findOrCreateCorrespondent auflösen
  5. uploadDocument → Task-UUID; via getTask pollen bis SUCCESS (Intervall/Timeout configurable, default 1s / 60s); bei FAILURE/REVOKED oder Timeout → ServiceUnavailableException
  6. mail_archive_link mit echter Paperless-Document-ID anlegen

Warum Polling? uploadDocument liefert nur eine Task-UUID; die echte Document-ID gibt es erst nach OCR/Klassifizierung. Schema verlangt paperless_document_id integer NOT NULL, also muss vor dem Insert auf den fertigen Task gewartet werden.

Erweiterungen:

  • ImapService.fetchRawByUid(config, folder, uid): Promise<RawMessage> mit Headern
  • PaperlessClient.getTask(uuid){ status, relatedDocument, taskId }
  • PaperlessClient.findOrCreateTag(name) / findOrCreateCorrespondent(name) — Search-by-name__iexact + POST bei Miss
  • MailModule importiert DocumentsModule für PaperlessClient DI

Tests: 326 grün gesamt (+5 IMAP, +8 PaperlessClient, +9 MailArchiveService).

Test plan

  • CI grün
  • Nach Deploy: Manueller Smoketest gegen paperless.dev.mrrm.de — Mail via API archivieren, prüfen ob Eintrag in Paperless + mail_archive_link landet
  • Idempotenz prüfen: zweiter Archiv-Call → kein neuer Upload, gleiche Document-ID
## Summary Neuer `MailArchiveService` im Mail-Modul, der eine einzelne Mail nach Paperless archiviert (Closes #278). **Ablauf in `archive(ownerSub, accountId, folder, uid)`:** 1. Konto-Besitz verifizieren (`NotFoundException` sonst) 2. **Idempotenz**: existiert ein `mail_archive_link` für (account, folder, uid)? → existierende Document-ID zurück, kein erneuter Upload 3. Raw EML via neuem `ImapService.fetchRawByUid` (`bodies: ['']`, Header-Parse für Subject/From/Date) 4. Tags `["email", "<account-label>"]` und Korrespondent (From-Adresse) über neue PaperlessClient-Helper `findOrCreateTag` / `findOrCreateCorrespondent` auflösen 5. `uploadDocument` → Task-UUID; via `getTask` pollen bis `SUCCESS` (Intervall/Timeout configurable, default `1s` / `60s`); bei `FAILURE`/`REVOKED` oder Timeout → `ServiceUnavailableException` 6. `mail_archive_link` mit echter Paperless-Document-ID anlegen **Warum Polling?** `uploadDocument` liefert nur eine Task-UUID; die echte Document-ID gibt es erst nach OCR/Klassifizierung. Schema verlangt `paperless_document_id integer NOT NULL`, also muss vor dem Insert auf den fertigen Task gewartet werden. **Erweiterungen:** - `ImapService.fetchRawByUid(config, folder, uid): Promise<RawMessage>` mit Headern - `PaperlessClient.getTask(uuid)` → `{ status, relatedDocument, taskId }` - `PaperlessClient.findOrCreateTag(name)` / `findOrCreateCorrespondent(name)` — Search-by-`name__iexact` + POST bei Miss - `MailModule` importiert `DocumentsModule` für `PaperlessClient` DI **Tests:** 326 grün gesamt (+5 IMAP, +8 PaperlessClient, +9 MailArchiveService). ## Test plan - [ ] CI grün - [ ] Nach Deploy: Manueller Smoketest gegen `paperless.dev.mrrm.de` — Mail via API archivieren, prüfen ob Eintrag in Paperless + `mail_archive_link` landet - [ ] Idempotenz prüfen: zweiter Archiv-Call → kein neuer Upload, gleiche Document-ID
feat(#278): Mail→Paperless Archivierungs-Service mit Tests
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
43e3437b93
Orchestriert das Archivieren einer einzelnen Mail nach Paperless:

1. Konto-Besitz prüfen
2. Idempotenz via mail_archive_link (folder + uid)
3. Raw EML via IMAP (neue ImapService.fetchRawByUid)
4. Tags ("email" + Konto-Label) und Korrespondent (From-Adresse)
   über findOrCreate-Helper in Paperless auflösen
5. uploadDocument liefert Task-UUID; mit getTask pollen bis SUCCESS
   (configurable Intervall/Timeout, default 1s/60s)
6. mail_archive_link mit echter Paperless-Document-ID anlegen

Erweiterungen:
- ImapService.fetchRawByUid: bodies: [''] → Raw RFC822 + Header-Parse
- PaperlessClient: getTask, findOrCreateTag, findOrCreateCorrespondent
- MailModule importiert DocumentsModule (PaperlessClient DI)

Tests: 326 grün gesamt (+5 imap, +8 paperless.client, +9 mail-archive)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
admin-mrrm/mrrmlabapp!312
No description provided.