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-Versionierung —
Document.parentId+versionChain, GET/documents/:id/versions+ POST/versions/upload-url+confirm - Bulk-Actions — POST
/documents/bulk-delete(max 200), POST/inbox/bulkmit action archive/move-to-docs/delete - Mobile Share-Sheet — PWA
share_targetmanifest + Route/mein-fach/share-receive. Hinweis: Service-Worker-Bridge fuer File-Passing braucht nochinjectManifest-Strategie stattgenerateSW. 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_KEYenv (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
Email-zu-Postfach (externe Sender via Magic-Link)
- 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_ACCESSals Action — UI-Workflow zum Ausloesen/Bestaetigen mit Begruendung fehlt. Backend-Datenmodell ist bereit.
Live-Status
| Bereich | Status |
|---|---|
| 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-Jobs | ✅ personal-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)
| Methode | Pfad | Funktion |
|---|---|---|
| GET | /personal-fach/documents?q=&tags= | Liste + Volltext + Tag-Filter |
| POST | /personal-fach/documents/upload-url | Presigned PUT |
| POST | /personal-fach/documents/confirm | Upload finalisieren + ClamAV-Scan |
| GET | /personal-fach/documents/:id | Detail |
| GET | /personal-fach/documents/:id/download | Presigned GET |
| DELETE | /personal-fach/documents/:id | Soft-Delete |
| POST | /personal-fach/documents/bulk-delete | Mehrere loeschen |
| POST | /personal-fach/documents/:id/tags | Tag zuweisen |
| DELETE | /personal-fach/documents/:id/tags/:tagId | Tag entfernen |
| GET | /personal-fach/documents/:id/versions | Versions-Chain |
| POST | /personal-fach/documents/:id/versions/upload-url | Neue Version: Upload-URL |
| POST | /personal-fach/documents/:id/versions/confirm | Neue Version: finalisieren |
| GET | /personal-fach/tags | Alle Tenant-Tags |
| GET | /personal-fach/inbox | Postfach-Drops |
| GET | /personal-fach/inbox/:id | Drop-Detail (markiert als gelesen) |
| GET | /personal-fach/inbox/:id/download | Drop-Download |
| POST | /personal-fach/inbox/:id/archive | Archivieren |
| POST | /personal-fach/inbox/:id/move-to-docs | INBOX → PERSONAL |
| DELETE | /personal-fach/inbox/:id | Soft-Delete |
| POST | /personal-fach/inbox/bulk | Bulk: archive / move / delete |
| POST | /personal-fach/drops/upload-url | Drop senden: Upload-URL |
| POST | /personal-fach/drops | Drop senden: persistieren + ClamAV + Bot-DM + Webhook |
| GET | /personal-fach/settings | Postfach-Settings |
| PATCH | /personal-fach/settings | Settings aendern |
| GET | /personal-fach/quota | Quota-Status |
| GET | /personal-fach/audit | Audit-Log (eigenes) |
Verteiler
| Methode | Pfad | Funktion |
|---|---|---|
| POST | /spaces/:spaceId/distributions/preview | Empfaenger-Vorschau |
| POST | /spaces/:spaceId/distributions/upload-url | Master-Upload-URL |
| POST | /spaces/:spaceId/distributions | Verteilen + ClamAV + Bot-DM (alle Empfaenger) |
| GET | /spaces/:spaceId/distributions | Liste |
| GET | /spaces/:spaceId/distributions/:id/stats | Aggregierte Read-Stats |
| GET | /spaces/:spaceId/distributions/:id/download | Master-Download |
| POST | /spaces/:spaceId/distributions/:id/replace | Neue Version |
| POST | /spaces/:spaceId/distributions/:id/distribute-to | Nachversand |
| DELETE | /spaces/:spaceId/distributions/:id | Soft-Delete |
Admin
| Methode | Pfad | Funktion |
|---|---|---|
| POST | /admin/tenants/:tid/storage/initialize | SvcAcct-Backfill |
| GET | /admin/tenants/:tid/personal-fach-settings | Settings lesen |
| PATCH | /admin/tenants/:tid/personal-fach-settings | Settings schreiben |
Naechste sinnvolle Schritte
- clamav-daemon installieren (sudo) — aktiviert den ClamAV-Scan vollstaendig
- PWA share-target Service-Worker-Bridge (injectManifest-Strategie) — derzeit zeigt /share-receive nur Hinweis-Text wenn keine Datei in IDB
- Pre-Upload ClamAV-Variante A — Nginx-Hook + Scanner-Service, kein Cleanup-Window
- Tenant-Admin-Notfall-Workflow-UI — DSGVO-konformer Postfach-Einsicht
- 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(nichtprilog-local) mc admin user svcacct addMUSS--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