Commit Graph

6 Commits

Author SHA1 Message Date
schalli 636fe0df8f refactor(quick-260921-bi2): maschinelle Lint-Fixe und toten Code abbauen
- Aufgabe 2: vier sichere Biome-Regeln (useImportType pfadgebunden auf
  apps/web+packages, noUselessEscapeInRegex, useConst,
  useExponentiationOperator) sowie fuenf ungesicherte Regeln
  (useNodejsImportProtocol, useLiteralKeys, useOptionalChain, useTemplate,
  useParseIntRadix) angewendet und den gesamten Diff von Hand gelesen
  (ldap.service.ts zeichenweise gegen Gross-/Kleinschreibung der
  AD-Merkmale, auth.service.ts/jwt.strategy.ts gegen Durchwinken bei
  fehlender Sitzung geprueft)
- noUselessSwitchCase bleibt bewusst stehen (tender-normalizer.service.ts:60,
  die Fallmarke dokumentiert Absicht)
- Toter Code (D-03): fuenf folgenlose Auffangvariablen entfernt, eine
  nicht benutzte Funktion (forSystemQuery, Pruefskript) entfernt, ein
  positionsgebundener Dekoratorparameter umbenannt (current-user.decorator.ts),
  fuenf Symptomfunde entfernt und als Folgeaufgaben zu melden (siehe unten)
- Sechs weitere, im Plan nicht namentlich gelistete aber
  gleich-kategorische Dead-Code-Fundstellen in Testdateien zusaetzlich
  bereinigt (groups.service.spec.ts, cert-manager.test.tsx,
  ldap.service.spec.ts, prisma-tenant.extension.spec.ts x3) — noetig, um
  die vom Plan selbst verlangten Nullstaende bei noUnusedVariables/
  noUnusedImports/noUnusedFunctionParameters zu erreichen

Dekoratordaten aus apps/api unveraendert (593 Zeilen, sha256 6e1583f1...).
Endstand 620 Befunde (541 echt, 79 Test) statt der im Plan geschaetzten
621/542 — eine Differenz von 1, weil das Streichen des Namens aus
`catch (e: any)` in calendar.service.ts (Symptom-Fix) den dort ebenfalls
gemeldeten noExplicitAny-Befund miteliminiert; das ist eine erwuenschte
Nebenwirkung, keine Regression. Fehlerstufe 0, beide Testlaeufe
punktgleich gruen (69/1124, 66/459), pnpm type-check 4/4, pnpm lint
--force 5/5.

Folgeaufgaben aus D-03 (nicht in diesem Vorgang behoben):
- force-password-change.interceptor.ts: Freigabeliste prueft nur den Pfad,
  nicht die HTTP-Methode
- change-password/page.tsx: nach erzwungenem Wechsel bleibt die Person auf
  der Seite stehen (keine Weiterleitung, keine Aktualisierung der
  Benutzerablage)
- VehicleTable.tsx: Loeschschaltflaeche hat keinen Besetztzustand, laesst
  sich doppelt ausloesen
- SplitTab.tsx: downloadAllAsZip erhielt eine ungenutzte
  Uebersetzungsfunktion, Hinweis auf fest verdrahtete Texte im Zip-Pfad

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TPPB4ApQxzSU1rwV2Ffj9J
2026-09-21 08:58:32 +02:00
schalli 6e2a641d76 feat(quick-260914-eym): Mail-Transport je Versand nach Mandant (WINDOWS #30), ldap/digest/matching ueber Systemkontext, vier Tabellen im Werkzeug, Erlaubnisliste vollstaendig
- mail: MailerModule-Fabrik und DB-Startpfad (findFirst beim Boot) ersatzlos
  entfernt; MailService baut je Versand einen nodemailer-Transport aus
  getDecryptedSmtpConfig(tenantId) des Empfaenger-Mandanten, Umgebungs-Kette
  (MAIL_* -> TESSERA_SMTP_* -> localhost:1025) nur als Rueckfall; Fehler
  weiter verschluckt (T-02-12), close() im finally; neue mail.service.spec.ts
  (4 Tests, T-GWH-03 geschlossen)
- settings: Startpfad-Methode samt vier Spec-Tests geloescht;
  auth: requestPasswordReset reicht user.tenantId durch (Spec-Zusicherung)
- ldap: getAllActiveConfigs und Nachverschluesselung lesen ueber forSystem
  (zwei Zuweisungen), Schreibzeile je Altzeile ueber forTenant(config.tenantId);
  Tests 301/306 umgedreht, neuer Altzeilen-Test
- tender-digest: Kandidatenabfrage ueber forSystem, Schleife gebunden (+1 Test)
- tender-matching: Profilabfrage ueber forSystem, Katalog (D-03) ungebunden (+1 Test)
- tender-notifications.integration.spec: Mock um forSystem
- Werkzeug: LdapConfig (15 Spalten), LdapFieldMapping (6), TenderMatch (8),
  TenderSavedSearch (8) je neun Kennungen plus Relations-Kennung
  ldapconfig-systemkontext-include-fieldmappings-beider-mandanten
  -> Alle 253 Pruefungen bestanden
- Detektor: FORSYSTEM_ALLOWED_CALL_SITES auf 4 Dateien / 5 Aufrufe;
  Proben-Empfaenger sysPrisma (Gate-Zaehlung, Name nicht hartkodiert)
- Klassifikation: 6 Zeilen system-gebunden, settings/smtpConfig gebunden
- Falsifizierung durch Rueckbau ausgefuehrt und zurueckgenommen:
  (a) FOR SELECT bei TenderMatch entfernt -> 5 von 253 rot (Insert gelingt,
  cmd ALL); (b) Regel TenderSavedSearch aus der Datei entfernt -> 1 von 245
  rot (Extraktion), lebende DB bleibt bei 34; (c) local=false -> gruen, plus
  Reset entfernt -> 5 rot (Erben sichtbar); (d) Zahl 0 -> 2 rot, Fremddatei
  admin-seed -> 3 rot
- Baseline: 64 Dateien / 1054 Tests, tsc 0, Werkzeug 253

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018N9CD3ebPKm1b32bPpBknY
2026-09-14 11:46:08 +02:00
schalli 07fc653f52 feat(quick-260911-nke): Benutzer an 34 Aufrufstellen gesetzt, zehn Tabellen gemessen, sechs Pruefungen umgedreht
- 30 verbleibende forTenant()-Aufrufstellen in sieben Diensten (calendar 6,
  dashboard 9, favorites 5, tender-email-config 3, tender-notification-pref 2,
  tender-rss-feed 2, tender-triage 3) reichen userId als drittes Argument
  durch. tender-digest.scheduler.ts bleibt zweistellig (Hintergrunddienst,
  Etappe 3c), mit Begruendung im Kommentar. Keine Methodensignatur, kein
  Controller angefasst, keine anwendungsseitige userId-Filterung entfernt.
- rls-scratch-check.mjs: zwoelf Extraktionsstellen auf die neue Migration
  umgeleitet (TenderEmailConfig/TenderNotificationPref/TenderSavedSearch/
  TenderTriage/TenderRssFeedSource in runTendersAreaChecks, SearchProvider in
  runSearchProviderAreaChecks/runDashboardAreaChecks, DashboardLayout/
  WidgetInstance, CalendarSource/FavoriteLink samt regelstand-eindeutig-Gates).
  SearchProvider/TenderRssFeedSource jetzt mit extractAllPolicySql (4 Regeln).
  runUserDimensionChecks() um die uebrigen neun Tabellen erweitert (neue
  Routine runCommandSeparatedPersonalTableCheck fuer die zwei NULL-faehigen
  Tabellen inkl. gemeinsame-Zeile-Pruefungen).
- Sechs Loch-Pruefungen umgedreht (dashboardlayout, widgetinstance,
  searchprovider, calendarsource, favoritelink-Doppelaussage getrennt) —
  alte Messung ohne Benutzer bleibt unter neuem Namen, Umkehrung MIT
  Benutzer erwartet das Gegenteil; kein alter Name mehr als Kennung.
- Baseline: 1020/62 Tests weiterhin gruen, Typpruefung sauber, Werkzeug
  203/203 bestanden (vorher 146).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
2026-09-11 17:39:17 +02:00
schalli df5c5b728b feat(laa-03): binde die Je-Treffer-Haelften der Hintergrunddienste und schliesse die Dokumente
tender-digest.scheduler.ts: die uebergreifende Kandidatenabfrage
(tenderMatch.findMany mit distinct:['userId']) bleibt bewusst ungebunden
und waehlt zusaetzlich das denormalisierte tenantId der Treffer-Zeile mit
aus; innerhalb der Schleife binden tenderNotificationPref.findUnique,
tenderMatch.findMany/updateMany und user.findUnique an den Mandanten
DIESER Kandidatenzeile.

tender-matching.service.ts: die Profilabfrage (tenderSavedSearch.findMany)
und der Lesezugriff auf den plattformweiten Tender-Katalog (D-03) bleiben
ungebunden; innerhalb der Profilschleife bindet die Treffer-Anlage
(tenderMatch.upsert), im nachgelagerten Instant-Dispatch binden
tenderMatch.findMany/updateMany und user.findUnique — je EIN gebundener
Client pro Profil, nicht neu je Treffer.

Beide Dateien tragen Codekommentare, die die uebergreifenden Abfragen
ausdruecklich als Etappe-3-Uebergabe benennen — die Trennlinie zwischen
Etappe 2 und Etappe 3 wird hier gezogen, nicht verwischt.

Alle drei betroffenen Testdateien (inkl. der gemeinsamen
Integrationsdatei) bekommen den Zwei-Client-Nachweis, Tests fuer
Zwei-Mandanten-Laeufe und die lautlose Fehlerform (kein Versand,
notifiedAt bleibt NULL). Falsifiziert: ein probeweiser Rueckbau der
user.findUnique-Bindung im Instant-Dispatch von tender-matching.service.ts
machte genau den erwarteten Test rot, danach zurueckgenommen.

rls-access-inventory.spec.ts gemessen und docs/mandantentrennung-zugriffsklassifikation.md
nachgezogen: Stand der fuenf betroffenen Paare (tender-digest.scheduler.ts/
tenderMatch,tenderNotificationPref,user; tender-matching.service.ts/
tenderMatch,user) auf gemischt bzw. gebunden; Bereichsuebersicht und
Klassen-Verteilung neu gemessen (tenders jetzt 36 ungebunden/26 gebunden);
"Der Hintergrunddienst als Falle" um den Abschluss beider Dateien ergaenzt.

docs/mandantentrennung-etappe2-fehlerrichtung.md: (t4) um den Nachtrag
ergaenzt, dass die Je-Treffer-Haelften geschlossen sind und die
uebergreifenden Haelften an Etappe 3 uebergeben bleiben.

770 Tests gruen, Typpruefung sauber, Wegwerf-Werkzeug 32/32, kein
Schema-/Migrations-/Compose-/Umgebungsdatei-Diff.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AMASaSxv5QMY7RncqZriRR
2026-09-09 16:02:40 +02:00
schalli 1222951af6 fix(quick-260909-ab3): kollidierende AD-Konten werden angelegt, nur ohne Adresse
- User.email auf optional gestellt (Migration geschrieben, NICHT
  ausgefuehrt); Eindeutigkeitsindex unangetastet, NULL bleibt in Postgres
  je verschieden
- Neuer Kollisionsentscheider (resolveEmailForWrite) in ldap.service.ts:
  eine bereits vergebene Adresse wird nie umgehaengt (T-Q3-01) — das
  zuerst angelegte Konto behaelt sie, jedes weitere Konto entsteht ohne
  Adresse (gesperrte Nutzerentscheidung 2026-09-09, WINDOWS #15)
- Entscheider in upsertMappedUser (Sync) UND importUsersByDn (Handimport)
  verdrahtet, damit der zweite Anlageweg nicht als Luecke bestehen bleibt
- LdapSyncResult um emailConflicts/skippedNoLogin/entryFailures erweitert;
  rohe ORM-Ausnahmetexte gehen nur noch an logger.error, nie in den
  Bericht (T-Q3-02)
- UserService.create nimmt die Adresse optional entgegen; Tender-Digest
  und Instant-Alert ueberspringen Empfaenger ohne Adresse (continue)
- Fuenf neue Testfaelle vorab gegen den unveraenderten Bestand rot
  gelaufen (erwartete Ursachen bestaetigt); 651/651 API-Tests gruen,
  prisma validate und type-check sauber

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FYZcd3SSmo14QTqWx2KKzU
2026-09-09 07:48:37 +02:00
schalli c0a8906d6a feat(12-02): TenderDigestScheduler — ein globaler Cron, findMany über fällige Nutzer
A single platform-wide @nestjs/schedule cron (daily 07:00), registered
via SchedulerRegistry exactly like TenderSchedulerService — NOT the
DkvSchedulerService single-tenant pattern (Pitfall 1). Selects
candidate users as distinct userId with an open TenderMatch
(notifiedAt IS NULL) via findMany across all tenants, resolves each
user's TenderNotificationPref.digestInterval (missing row -> daily
default, D-01: daily always due, weekly only on Monday Europe/Berlin,
off never), groups their un-notified matches by saved-search profile
name into one TenderMailService.sendDigest call per user (D-02), and
stamps notifiedAt+channel='digest' ONLY after a successful send — the
shared notifiedAt-IS-NULL eligibility gate that guarantees no
double-send with instant alerts (D-06).

Each candidate user is processed in its own try/catch: a missing SMTP
config, a send failure, or an unexpected thrown error for one
user/tenant leaves that user's matches notifiedAt=NULL (retried next
run) and never aborts the run for the rest (Pitfall 6).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 09:13:56 +02:00