- Migration 20260924120000_dashboard_image_drop_data: Schutzpruefung
(bricht ab, solange eine Zeile ohne storagePath existiert; row_security
aus, damit ein Eigentuemer ohne BYPASSRLS nicht still 0 Zeilen sieht),
dann NOT NULL, DROP COLUMN data, DROP POLICY system_read_policy
- Dienst: Bootstrap-Umzug samt forSystem() und Selbstheilung aus data
entfernt; Upload vergibt die UUID selbst, Zeile gleich mit Pfad
- FORSYSTEM_ALLOWED_CALL_SITES, Tests, Zugriffsklassifikation (per
Gate-Schleife gemessen: 61/213/6) nachgezogen
- Betriebshandbuch Kap. 4: Hinweis und Wiederherstellungsweg bei Abbruch
- Todo 2026-09-22 nach completed/
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- proxmox-scheduler.service.ts: ein Cron-Auftrag je aktivem Mandant
(proxmox-poll:<tenantId>), onApplicationBootstrap (nicht onModuleInit,
Tender-Muster), Abfrageintervall = kleinstes pollIntervalMin der
aktiven Server, ein fehlgeschlagener Server bricht die Tick-Schleife
nicht ab, refreshTenant() zieht nach jedem Speichern sofort nach
- proxmox.service.ts: loadActiveServersForScheduler() als einziger
forSystem()-Aufruf des Moduls (Erlaubnisliste in
rls-access-inventory.spec.ts), testConnection() schreibt nicht ins
Zwischenlager, pollServer() bekommt eine Zehn-Sekunden-Sperre (T-DHH-06)
- proxmox.controller.ts: POST servers/:id/test, create() zieht den
Planer nach dem Anlegen sofort nach
- Zugriffsklassifikation: proxmoxServer wechselt auf system-gebunden
(Startpfad des Planers), proxmoxServerStatus bleibt gebunden
Tore: api 1306/1306 (>=1240), type-check 4/4, rls-access-inventory
und rls-coverage gruen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Bytes liegen unter user-files/dashboard-images/<userId>/<id>.<ext>, die
Zeile haelt nur noch storagePath (Muster User.avatarPath)
- Dateiname immer servergeneriert: UUID der Zeile + Endung aus dem
ERKANNTEN Mime-Typ, originalName kommt in keinem Pfad vor (T-HK4-01)
- Migration 20260922120000: storagePath dazu, data wird NULLbar, kein DROP
(zweistufig, T-HK4-03); system_read_policy fuer den Umzug
- onApplicationBootstrap zieht Altbestand automatisch um: systemgebunden
lesen, je Zeile mandantengebunden schreiben (Muster DKV-Planer)
- Upload nimmt die Zeile bei fehlgeschlagenem Schreiben zurueck, Loeschen
entfernt die Datei mit, fehlende Datei -> 404 (T-HK4-04)
- 11 neue Dienst-Tests gegen ein echtes Temp-Verzeichnis (kein fs-Mock)
- Zugriffsklassifikation: Stand system-gebunden, Zahlen nachgemessen
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
- Klassifikationsdokument: Klassen-Verteilung (35/21/14/2, 72 Paare),
Uebersichtsabsatz (zweite methodische Luecke, geschlossen) und
Hintergrunddienst-Nachtrag (ldap/getAllActiveConfigs reicht auch in
LdapFieldMapping hinein) aus Tabelle/Greps abgeleitet, nicht abgeschrieben
- Kritikschrift: Nachtrag (260911-mkj) unter (n4) im Bereich tenant, Vermerk
im Etappe-2-Abschluss dass #27 geschlossen ist
- WINDOWS.md: #27 fixed (Nachweis: vierte Erkennungsform, Proben,
Zwischenmessung 7/3/1); neuer Eintrag #33 fuer die Empfaenger ausserhalb
der vier Erkennungsformen (tenders.seed.ts, backfill-tender-source.ts)
- Spec: WINDOWS #TBD-MKJ-Platzhalter durch #33 ersetzt
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
- rls-access-inventory.spec.ts: analyzeFile in analyzeSource(source, relPath)
herausgeloest, parseSchemaRelations() liest schema.prisma zur Testzeit,
vierte Erkennung loest include:/select:/_count:/Relationsfilter ueber
SCHEMA_RELATIONS auf das Zielmodell auf und traegt es als eigene
Fundstelle (gebunden/ungebunden nach Empfaenger) ein
- drei Waechter (Rohzahl vs. erkannte Modellaufrufe, Stale-Check fuer
RELATION_SPEC_EXCEPTIONS, unresolvedRelationSpecValues leer), zwei
Schema-Tests, acht gepinnte Proben (WINDOWS #27 ungebunden/gebunden,
reale ldap-Form, verschachtelte where-Kette, Negativprobe, _count: true,
unbekannter Empfaenger, Konstantenaufloesung)
- Bestandsaufnahme (docs/mandantentrennung-zugriffsklassifikation.md): sieben
neue Paare, drei fortgeschriebene Staende (davon ldapFieldMapping mit
Klassenwechsel auf beides), Kopfabsatz "Erkennungsluecke GESCHLOSSEN"
ersetzt den alten "seit 260911-e2s vermessen"-Absatz, 65 -> 72 Paare
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
Die vorige Aufgabe-2-Teilcommit (11f5731) hatte nur die Loeschung von
tenant.middleware.ts und die neue tenant.guard.spec.ts erfasst — ein
`git add` mit mehreren Pfaden schlug wegen eines bereits entfernten
Pfads fataler fehl und liess die restlichen fuenf Dateien unstaged,
ohne dass das beim Commit auffiel (Rule 1 — Prozessfehler, hier
korrigiert). Dieser Commit traegt den eigentlichen Umbau nach:
tenant.guard.ts ohne Prisma-Abhaengigkeit, die geleerte
FORTENANT_ASSIGNMENT_EXCEPTIONS samt Wachhund-Test in
rls-access-inventory.spec.ts, und die drei berichtigten
Kommentarzeilen (app.module.ts, module.guard.ts, dkv.controller.ts).
Inhaltlich identisch mit dem, was bereits verifiziert wurde (891 Tests
gruen, Typpruefung sauber) — nur die Staging-Reihenfolge war fehlerhaft.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
prisma-tenant.extension.ts bekommt withTenantTransaction(prisma, tenantId, fn)
— die interaktive Transaktion auf dem UNgebundenen Client mit set_config als
erster Anweisung direkt auf tx, die in Aufgabe 1 als einzige der drei
gemessenen Formen sowohl die Einzelmessung als auch eine Lastprobe unter
echter Nebenlaeufigkeit bestand (die Array-Form auf dem gebundenen Client
verteilt jede Operation auf eine eigene Teiltransaktion; die interaktive Form
auf dem gebundenen Client brach unter 40 parallelen Aufrufen mit P2028 ab).
rls-access-inventory.spec.ts bekommt eine dritte Erkennung fuer
Modellzugriffe ueber den Rueckgabeparameter einer interaktiven Transaktion
(zwei Formen: direkter Empfaenger.$transaction(async...) und das neue
Hilfsmittel withTenantTransaction(...)) — macht das Paar
(groups.service.ts, tenantModuleActivation) erstmals sichtbar, das bislang
keine Pruefung dieses Projekts je gesehen hat.
groups.service.ts: alle zwoelf Methoden inklusive der drei Transaktionen
(update() isDefault:true, reassignDefaultBeforeDelete(), ensureDefaultGroup())
laufen jetzt ueber den Mandantenkontext. Zaehler und Transaktion in
ensureDefaultGroup() sind gemeinsam gebunden (T-JTS-05). addUserToDefaultGroup()
prueft neu, dass der Zielbenutzer zum Mandanten gehoert (Befund E, T-JTS-02) —
die Regel auf GroupMembership prueft nachweislich nur die Gruppenseite.
groups.service.spec.ts bekommt zwei unterscheidbare Clients ueber demselben
Speicher-Fake (Muster aus 260909-ipc, auf die interaktive Form uebertragen)
und 13 neue Bindungsnachweise; alle 42 Bestandstests bleiben gruen.
Klassifikationsdokument nachgezogen. 737 Tests und die Typpruefung gruen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
Aufgabe 2 der Etappe 2: getConfig/createConfig/updateConfig sowie
addFieldMapping/removeFieldMapping laufen jetzt ueber forTenant(), gebunden
an den aus der Anfrage bekannten Mandanten. removeFieldMapping nimmt den
Mandanten neu als Pflichtparameter entgegen und der Controller holt ihn aus
dem Sitzungsnachweis statt nur die URL-Kennung weiterzureichen (T-IPC-01) --
ein Administrator konnte bisher die Feldzuordnung eines fremden Mandanten
loeschen, wenn er ihre Kennung kannte. getAllActiveConfigs() und die
Start-Nachverschluesselung bleiben bewusst uebergreifend, mit ausgeschriebener
Begruendung im Code (Befund B).
rls-access-inventory.spec.ts erkennt jetzt neben `this.prisma.<Modell>` auch
gebundene `<Name>.<Modell>`-Zugriffe (Befund F/G) und prueft eine neue
Stand-Spalte (gebunden/ungebunden/gemischt) im Klassifikationsdokument gegen
den Quelltext. Das macht zwei bisher unsichtbare, weil schon laenger
gebundene Fundstellen sichtbar (auth.service.ts/passwordResetToken,
ldap.service.ts/groupMembership) und deckt auf, dass
(ldap-config.service.ts, ldapConfig) tatsaechlich "beides" ist, nicht
"muss-mandantengebunden" (Befund B).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
WINDOWS #18/#20, Aufgabe 3: docs/mandantentrennung-zugriffsklassifikation.md
haelt fuer jede der 227 this.prisma.*-Fundstellen (32 Dateien, zusammengefasst
zu 59 Datei-Modell-Paaren) eine Klasse fest — muss-mandantengebunden (31),
keine-mandantengebundene-tabelle (16), bewusst-uebergreifend (3, mit
ausgeschriebenem Grund) oder beides (9, der Hintergrunddienst-Sonderfall:
uebergreifend lesen, je Zeile mandantengebunden schreiben — betrifft
ldap.service.ts, tender-digest.scheduler.ts, tender-matching.service.ts).
rls-access-inventory.spec.ts ermittelt die Fundstellen bei jedem Testlauf neu
aus dem Quelltext und vergleicht sie gegen die Tabelle im Dokument — Datei und
Modellname als Schluessel, keine Zeilennummer. Scheitert nachweislich, sobald
eine Fundstelle fehlt oder ein Eintrag verwaist (per Testlauf geprueft, danach
zurueckgesetzt).
Zwei belegte Befunde im Dokument festgehalten: req.tenantPrisma wird gesetzt,
aber nirgends gelesen; WINDOWS #19 (nullbares tenantId bei SearchProvider/
TenderRssFeedSource) bleibt benannter Blocker fuer Etappe 3.
docs/mandantentrennung-datenbankrolle.md verweist jetzt auf das neue
Dokument und korrigiert die ueberholte Zahl 182 auf den nachgemessenen Stand
(227/59). WINDOWS.md #18/#19 um Nachtrag auf diesen Plan ergaenzt; #20 (der
in Aufgabe 1 gemessene und behobene forTenant()-Verbindungsdefekt) als
"fixed" markiert.
Deviation (Rule 3, blockierend fuer die Bestandsaufnahme-Pruefung):
auth.service.ts-Kommentar umformuliert, der zuvor woertlich
"this.prisma.user.findUnique" als erklaerenden Text enthielt und dadurch
einen Eigentreffer der grep-basierten Inventur-Pruefung erzeugte.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU