7.2 KiB
Phase 17 — Kontext: Eigene Ausschreibungs-Quellen je Nutzer
Quelle dieses Dokuments: Gespraech mit dem User am 2026-08-12. Kein
/gsd-discuss-phase-Lauf — die Entscheidungen fielen im Gespraech beim
Durchsprechen des Backlog-Punkts
2026-08-11-tender-radar-einstellungen-mischen-rollen.md.
Warum diese Phase existiert
Die Einstellungsseite /modules/tender-radar/settings traegt vier Abschnitte,
die drei verschiedenen Zustaendigkeiten gehoeren. Der Backlog-Punkt sah darin
urspruenglich nur ein Anzeigeproblem ("Admin-Bloecke hinter eine Rollenpruefung
legen"). Beim Durchsprechen kam heraus, dass zwei der drei Admin-Abschnitte
inhaltlich falsch zugeschnitten sind:
"Wenn Person A ihre Ausschreibungen auf kontoa@mail.de und Person B von kontob@mail.de holt, macht eine gemeinsame Einstellung keinen Sinn. Ebenso die RSS-Feeds. Wieso kann eine Person nicht mehrere Portale abfragen als eine andere?"
Der User hat ausdruecklich festgehalten, dass seine urspruengliche Zustimmung zur gemeinsamen Konfiguration auf einer missverstaendlichen Erklaerung meinerseits beruhte. Das ist keine Meinungsaenderung, sondern eine Korrektur.
Heutiger Zustand, konkret: TenderEmailConfig.tenantId ist @unique — es
gibt genau ein Alert-Postfach pro Mandant. Ein zweiter Nutzer mit eigenem
Portal-Konto kann seine Alerts gar nicht anbinden. TenderRssFeedSource.url ist
@unique ohne Besitzer — die Feed-Liste gilt fuer alle, und wer einen Feed
loescht, nimmt ihn allen weg.
Entscheidungen
D-01 — Alert-Postfach gehoert dem Nutzer
TenderEmailConfig wechselt von tenantId @unique auf userId @unique. Jeder
Nutzer bindet sein eigenes Postfach an. tenantId bleibt als denormalisiertes
Feld erhalten (SMTP-Aufloesung, gleiche Rolle wie in TenderMatch).
D-02 — RSS-Feeds bekommen einen Besitzer, plattformweite Feeds bleiben moeglich
TenderRssFeedSource bekommt ein nullable userId:
userId = null→ plattformweiter Feed, von der Administration gepflegt, gilt fuer alle. Der in Phase 14 gesetzteservice.bund.de-Feed bleibt damit unveraendert fuer jeden aktiv.userIdgesetzt → persoenlicher Feed, nur dieser Nutzer bekommt ihn.
url @unique faellt weg und wird @@unique([userId, url]), sonst koennten nicht
zwei Nutzer denselben Feed verfolgen.
Begruendung: Die Alternative — alle Feeds persoenlich, niemand startet mit etwas — haette den seit Phase 14 aktiven Standard-Feed fuer alle bestehenden Nutzer stillgelegt. Diese Entscheidung ist im Gespraech nicht ausdruecklich abgefragt worden; sie ist der am wenigsten zerstoererische Weg und sollte beim Planen gegengelesen werden.
D-03 — Abrufintervall bleibt Administration
TenderSourcePollConfig bleibt unveraendert plattformweit. Es gibt genau eine
oeffentliche Quelle (oeffentlichevergabe.de), die fuer alle dieselben Daten
liefert. Pro Nutzer abzurufen hiesse, dieselben Daten mehrfach zu holen. Der
User hat dem im Gespraech ausdruecklich zugestimmt.
D-04 — Digest-Intervall ist bereits nutzereigen, muss nur sichtbar werden
TenderNotificationPref haengt schon an userId (NOTIFY-01/D-03 aus Phase 12).
Die Einstellung steht heute nur an der falschen Stelle: als letzter Abschnitt
unter drei Admin-Bloecken. Kein Datenmodell-Aenderung noetig, nur Platzierung.
D-05 — Ausschreibungsdaten bleiben plattform-global (HARTE GRENZE)
Tender bleibt ohne tenantId, ohne RLS, ohne forTenant() — Entscheidung
D-03 aus Phase 10 bleibt unangetastet. Geaendert wird ausschliesslich, wer
Quellen einspeist, nicht wer Treffer sieht.
Der User wurde darueber aufgeklaert und hat zugestimmt:
"Reicht dir 'jeder speist seine eigenen Quellen ein, gesehen wird alles gemeinsam'?" → "Ja, plane"
Praktische Folge, die in der UI ehrlich benannt werden muss: Was ueber das Postfach von Nutzer A hereinkommt, steht danach auch in der Trefferliste von Nutzer B. Eine Sichtbarkeitstrennung waere ein deutlich groesserer Umbau (Tender mandanten-/nutzerscoped, RLS, Dedup ueber Sichtbarkeitsgrenzen) und ist hier bewusst ausgeklammert.
Offene Punkte fuer die Planung
- Migration der bestehenden Zeilen. Das eine vorhandene
TenderEmailConfig-Row (falls auf dem Testserver konfiguriert) braucht einen Besitzer. Vorschlag: dem aeltesten ADMIN des Mandanten zuordnen, nicht loeschen. Zu pruefen, ob ueberhaupt eine Zeile existiert. - Bestehende RSS-Feeds werden zu
userId = null(plattformweit) — das ist der Ist-Zustand und aendert fuer niemanden etwas. email-alert.adapter.ts:143liest heute bewusst mandantenuebergreifend (findMany({ where: { isActive: true }})) und darf nicht inforTenant()gewickelt werden — der Kommentar dort ist ausdruecklich. Der Wechsel aufuserIdaendert daran nichts: der Scheduler loest weiterhin alle aktiven Postfaecher in einem Durchlauf auf. Diecatch-pro-Postfach-Disziplin muss erhalten bleiben.- Wohin mit den nutzereigenen Abschnitten? Es gibt bereits
/settings/general/account. Drei Varianten standen im Backlog-Punkt; die Frage betrifft auch DKV-Fleet (gleiche Bauform) und jedes kuenftige Modul. Beim Planen zu entscheiden — eine eigene nutzerseitige Modulseite scheint am tragfaehigsten, weil modulfremde Einstellungen sich sonst in einer allgemeinen Seite sammeln. - Rollenpruefung der verbleibenden Admin-Abschnitte. Das urspruengliche Anliegen des Backlog-Punkts: die Seite selbst prueft keine Rolle, ein normaler Nutzer sieht die Bedienelemente und laeuft beim Speichern in eine Fehlermeldung. Nach dem Umbau bleibt nur noch das Abrufintervall Admin-Sache — das gehoert hinter eine Rollenpruefung.
Betroffene Stellen (Bestandsaufnahme 2026-08-12)
| Datei | Rolle |
|---|---|
apps/api/prisma/schema.prisma:278 |
TenderEmailConfig (tenantId @unique) |
apps/api/prisma/schema.prisma:550 |
TenderRssFeedSource (url @unique) |
apps/api/prisma/schema.prisma:512 |
TenderNotificationPref (bereits userId @unique) |
apps/api/prisma/schema.prisma:524 |
TenderSourcePollConfig (bleibt) |
apps/api/src/tenders/tender-email-config.service.ts |
4 Zugriffe auf tenantId |
apps/api/src/tenders/tender-rss-feed.service.ts |
3 Zugriffe, kein Besitzer-Scoping |
apps/api/src/tenders/adapters/email-alert.adapter.ts:143 |
Fan-out ueber alle aktiven Postfaecher |
apps/api/src/tenders/adapters/rss.adapter.ts:79 |
Fan-out ueber alle aktiven Feeds |
apps/api/src/tenders/tenders.controller.ts:241-301 |
5 Endpunkte, heute @Roles(ADMIN, SUPER_ADMIN) |
apps/api/src/tenders/tenders.module.ts:288 |
Seed des service.bund.de-Feeds |
apps/web/src/app/(portal)/modules/tender-radar/settings/page.tsx |
die vier Abschnitte |
Guenstig: Beide Adapter holen heute schon alle aktiven Konfigurationen in einem Durchlauf. Mehrere Postfaecher und Feeds sind keine neue Mechanik, nur mehr Zeilen in der Tabelle. Der Scheduler muss dafuer nicht angefasst werden.
Route-Order-Falle (Bestandswissen)
tenders.controller.ts traegt mehrere Kommentare dazu: statische Routen
(rss-feeds, email-config, source-config) MUESSEN vor @Get(':id')
deklariert werden, sonst schattet die Parameter-Route sie mit 404 ab. Unit-Tests
fangen das nicht. Beim Umbau der Endpunkte erhalten bleiben.