Skip to content

Mein Fach — Vollstaendige Implementation (Stand: 2026-04-29)

Dieses Dokument fasst den finalen Umsetzungsstand des Moduls "Mein Fach" zusammen. Loest den ersten Phase-1-Bericht nicht ab — beide gemeinsam dokumentieren den Weg.

Endnutzer-Beschreibung: Mein Fach Admin-Handbuch: Mein Fach (Admin) Architektur: Mein Fach (Architektur)


Was geliefert wurde

Phase 1 — Foundation

  • 3-Ebenen-DMS (Global / Space / Persoenlich)
  • Eigene Dokumente (PERSONAL scope) — Upload, Liste, Detail, Loeschen
  • Postfach (INBOX scope) — Drop empfangen, Lesen, Archivieren, Verschieben, Loeschen
  • Drop senden — POST /drops mit Sender-Asymmetrie, Privacy-Settings, Quota-Pruefung
  • Pro-Nutzer Settings (UserPostfachSettings) — wer darf reinlegen, Block-Liste, Aufbewahrung, Notification-Mode
  • Audit-Log (PostfachAuditLog) — alle Aktivitaeten werden mit Akteur+Grund nachvollziehbar gespeichert
  • Mein Fach Hub-UI mit 4 Sektionen
  • Quota-Anzeige mit Soft-Warning + Hard-Limit

Phase 2 — Verteiler + Anti-Abuse + Notification

  • Verteiler-Fach im Space — Reference-Dedup (1 Master, N Inbox-Refs)
  • Audience-Filter (staff/guardian/minor/external)
  • Replace + Distribute-To + Soft-Delete mit Vermerk
  • Aggregierte Read-Stats (anonym)
  • Anti-Spam-Throttle: 20 Drops/h pro (Sender, Empfaenger), Redis-Sliding-Window
  • Inbox-Unread-Badge auf Welt-Icon (60s polling)

Phase 3 — Polish + Production-Readiness

  • Auto-Cleanup-Cron personal-fach-cleanup (taeglich 03:15)
  • ClamAV MVP — post-upload-Scan, Graceful-Fallback wenn clamd offline
  • Tenant-Settings-UI im Admin-Portal
  • Drop-aus-Chat — Inbox-Icon auf File/PDF/Image/Video/Audio
  • Drag&Drop in 3 Stellen (Mein Fach Dokumente, SendDropDialog, Verteiler-Compose)
  • Mein-Fach-Icon in Header zwischen Favoriten und Avatar (statt Welten-Leiste)

Phase 9 — Nice-to-have

  • Tags — DocumentTag-Tabelle wiederverwendet, GET /personal-fach/tags + POST/DELETE pro Doc, Filter via ?tags=slug1,slug2
  • Volltextsuche?q= Param mit tsvector (deutsch), durchsucht Titel + Beschreibung + Inhalt
  • Datei-VersionierungDocument.parentId+version Chain, GET /documents/:id/versions + POST /versions/upload-url+confirm
  • Bulk-Actions — POST /documents/bulk-delete (max 200), POST /inbox/bulk mit action archive/move-to-docs/delete
  • Mobile Share-Sheet — PWA share_target manifest + Route /mein-fach/share-receive. Hinweis: Service-Worker-Bridge fuer File-Passing braucht noch injectManifest-Strategie statt generateSW. Manifest greift, IDB-Konsumieren ist vorbereitet.
  • Chat-Bot-DM bei Drop — Bot sendet m.notice in einer DM; pro (tenant, recipient) wird Room-ID in tenant_settings gecached → keine duplicate DMs
  • Webhook bei Drop — Per-Tenant-URL in tenant_settings.webhook_url + HMAC-Signatur (X-Prilog-Signature), 5s Timeout, best-effort
  • Save-from-Chat fuer Image/Video/Audio — AttachmentCaption mit Inbox-Icon
  • SvcAcct-Rotation als Cron — taeglich 04:00; rotiert MinIO-Service-Accounts pro Tenant alle 90 Tage

Per-Tenant S3 Architektur (Querschnitt zu allen Phasen)

  • Jeder Tenant hat eigenen MinIO-Service-Account auf eigenem Customer-Server (Pro) bzw. shared-host (Light)
  • Bucket tenant-{slug}, Service-Account-Policy nur auf den eigenen Bucket beschraenkt
  • AES-256-GCM-verschluesseltes Secret in TenantRuntime.s3SecretAccessKeyEncrypted
  • Schluessel STORAGE_ENCRYPTION_KEY env (64 Hex)
  • Backend: getS3ContextForTenant(tenantId) mit LRU-Cache (200 entries, 30 min TTL)
  • AWS-SDK v3.730+ CRC32-Checksums via requestChecksumCalculation: 'WHEN_REQUIRED' deaktiviert (sonst Sigv4-Mismatch mit MinIO)
  • Nginx-Pattern: location ~ ^/tenant- direkt zu MinIO (kein Path-Rewrite, sonst Signatur-Bruch)

Bewusst nicht implementiert

  • Begruendung: separater Spam-Vektor + Auth-Komplexitaet (Mailbox-zu-Tenant-Mapping). Phase 4-Thema.

Read-Receipts namentlich

  • Begruendung: bricht die zentrale Privacy-Asymmetrie auf, die das Postfach-Konzept ausmacht. Lehrer sieht nur Aggregat-% bei Verteiler-Drops — nicht "Schueler X hat geoeffnet".

Pre-Upload ClamAV-Scan (Variante A der vollen 3.5-Tage-Architektur)

  • Aktuell: post-upload-scan (MVP). Datei landet kurz in MinIO, wird gescannt, bei Treffer geloescht.
  • Vollausbau (geplant): nginx-pre-Hook + scanner-service. Kein Cleanup-Window.

Tenant-Admin-Notfallzugriff-Workflow

  • Audit-Log unterstuetzt ADMIN_ACCESS als Action — UI-Workflow zum Ausloesen/Bestaetigen mit Begruendung fehlt. Backend-Datenmodell ist bereit.

Live-Status

BereichStatus
Backend-Code✅ committed + deployed auf api.prilog.chat
Web-Client✅ Artifact gebaut + Tenants weser, demo, demo2, demo3 aktualisiert
Admin-Portal✅ Tenant-Settings-UI + Webhook-Field deployed
Migrations✅ 0015 (personal-fach), 0016 (distribution), 0017 (per-tenant-s3) live
Cron-Jobspersonal-fach-cleanup 03:15, storage-rotation 04:00
Per-Tenant S3✅ alle 4 Tenants haben Service-Accounts; E2E Upload+Download getestet
nginx/tenant-* Location auf weser + shared-1 + im Agent-Template
ClamAV-Code✅ deployed; clamav-daemon-Install auf api.prilog.chat ausstehend (apt install clamav-daemon mit sudo)
Mein-Fach-Doku✅ docs.prilog.chat

Endpoint-Inventar

Personal-Fach (User-facing)

MethodePfadFunktion
GET/personal-fach/documents?q=&tags=Liste + Volltext + Tag-Filter
POST/personal-fach/documents/upload-urlPresigned PUT
POST/personal-fach/documents/confirmUpload finalisieren + ClamAV-Scan
GET/personal-fach/documents/:idDetail
GET/personal-fach/documents/:id/downloadPresigned GET
DELETE/personal-fach/documents/:idSoft-Delete
POST/personal-fach/documents/bulk-deleteMehrere loeschen
POST/personal-fach/documents/:id/tagsTag zuweisen
DELETE/personal-fach/documents/:id/tags/:tagIdTag entfernen
GET/personal-fach/documents/:id/versionsVersions-Chain
POST/personal-fach/documents/:id/versions/upload-urlNeue Version: Upload-URL
POST/personal-fach/documents/:id/versions/confirmNeue Version: finalisieren
GET/personal-fach/tagsAlle Tenant-Tags
GET/personal-fach/inboxPostfach-Drops
GET/personal-fach/inbox/:idDrop-Detail (markiert als gelesen)
GET/personal-fach/inbox/:id/downloadDrop-Download
POST/personal-fach/inbox/:id/archiveArchivieren
POST/personal-fach/inbox/:id/move-to-docsINBOX → PERSONAL
DELETE/personal-fach/inbox/:idSoft-Delete
POST/personal-fach/inbox/bulkBulk: archive / move / delete
POST/personal-fach/drops/upload-urlDrop senden: Upload-URL
POST/personal-fach/dropsDrop senden: persistieren + ClamAV + Bot-DM + Webhook
GET/personal-fach/settingsPostfach-Settings
PATCH/personal-fach/settingsSettings aendern
GET/personal-fach/quotaQuota-Status
GET/personal-fach/auditAudit-Log (eigenes)

Verteiler

MethodePfadFunktion
POST/spaces/:spaceId/distributions/previewEmpfaenger-Vorschau
POST/spaces/:spaceId/distributions/upload-urlMaster-Upload-URL
POST/spaces/:spaceId/distributionsVerteilen + ClamAV + Bot-DM (alle Empfaenger)
GET/spaces/:spaceId/distributionsListe
GET/spaces/:spaceId/distributions/:id/statsAggregierte Read-Stats
GET/spaces/:spaceId/distributions/:id/downloadMaster-Download
POST/spaces/:spaceId/distributions/:id/replaceNeue Version
POST/spaces/:spaceId/distributions/:id/distribute-toNachversand
DELETE/spaces/:spaceId/distributions/:idSoft-Delete

Admin

MethodePfadFunktion
POST/admin/tenants/:tid/storage/initializeSvcAcct-Backfill
GET/admin/tenants/:tid/personal-fach-settingsSettings lesen
PATCH/admin/tenants/:tid/personal-fach-settingsSettings schreiben

Naechste sinnvolle Schritte

  1. clamav-daemon installieren (sudo) — aktiviert den ClamAV-Scan vollstaendig
  2. PWA share-target Service-Worker-Bridge (injectManifest-Strategie) — derzeit zeigt /share-receive nur Hinweis-Text wenn keine Datei in IDB
  3. Pre-Upload ClamAV-Variante A — Nginx-Hook + Scanner-Service, kein Cleanup-Window
  4. Tenant-Admin-Notfall-Workflow-UI — DSGVO-konformer Postfach-Einsicht
  5. Email-zu-Postfach — externer Magic-Link-Empfang (Phase 4)

Bekannte Stolperfallen (im Memory-Eintrag project_per_tenant_s3.md)

  • Agent-Env-File-Pfade unterscheiden sich (weser=/opt/prilog-agent/.env, shared-1=/etc/prilog/agent.env)
  • mc-Aliasname local (nicht prilog-local)
  • mc admin user svcacct add MUSS --disable-pager (sonst execFile-Hang)
  • WS-Result-Listener vor sendCommand registrieren (Race-Condition bei Sub-1s-Responses)
  • Shared-Hosting → Agent unter SHARED-HOST-{sharedHostId} anstelle des Tenant-orderId
  • AWS-SDK v3.730+ CRC32-Checksums brechen MinIO-Sigv4 → requestChecksumCalculation: 'WHEN_REQUIRED'
  • nginx-Rewrite /s3/ bricht Sigv4 → ~ ^/tenant- ohne Rewrite
  • MinIO-Root-Password-Rotation invalidiert Service-Accounts → SvcAccts neu provisionieren
  • Storage-Provision-Endpoint nutzt runtime.matrixDomain (nicht Agent-Output) sonst zeigen Shared-Tenants alle auf shared-1.prilog.team