docs(17-01): complete eigenes Alert-Postfach je Nutzer plan
This commit is contained in:
@@ -609,7 +609,7 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10
|
|||||||
| 14. RSS, Email-Alert Ingestion & Module Rollout | 5/5 | In Progress| |
|
| 14. RSS, Email-Alert Ingestion & Module Rollout | 5/5 | In Progress| |
|
||||||
| 15. Modul-Berechtigungen: Gruppen & User-Grants | 8/8 | Complete | 2026-08-04 (Bericht 15-04 am 2026-08-11 nachgezogen) |
|
| 15. Modul-Berechtigungen: Gruppen & User-Grants | 8/8 | Complete | 2026-08-04 (Bericht 15-04 am 2026-08-11 nachgezogen) |
|
||||||
| 16. AD-Gruppen-Synchronisation | 5/5 | Complete | 2026-08-11 |
|
| 16. AD-Gruppen-Synchronisation | 5/5 | Complete | 2026-08-11 |
|
||||||
| 17. Eigene Ausschreibungs-Quellen je Nutzer | 0/3 | Planned | - |
|
| 17. Eigene Ausschreibungs-Quellen je Nutzer | 1/3 | In Progress| |
|
||||||
|
|
||||||
### Phase 17: Eigene Ausschreibungs-Quellen je Nutzer
|
### Phase 17: Eigene Ausschreibungs-Quellen je Nutzer
|
||||||
|
|
||||||
@@ -628,7 +628,7 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10
|
|||||||
- **SRC-05**: Die verbleibenden Administrationseinstellungen sind fuer normale Nutzer nicht sichtbar; die Rollenpruefung greift in der Oberflaeche wie in der API (D-03).
|
- **SRC-05**: Die verbleibenden Administrationseinstellungen sind fuer normale Nutzer nicht sichtbar; die Rollenpruefung greift in der Oberflaeche wie in der API (D-03).
|
||||||
|
|
||||||
**Depends on:** Phase 16
|
**Depends on:** Phase 16
|
||||||
**Plans:** 3 plans
|
**Plans:** 1/3 plans executed
|
||||||
|
|
||||||
**Success Criteria** (what must be TRUE):
|
**Success Criteria** (what must be TRUE):
|
||||||
|
|
||||||
@@ -642,6 +642,6 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10
|
|||||||
|
|
||||||
Plans (Wellenstruktur — streng nacheinander, alle drei fassen Schema, Controller oder die Zugriffsschicht gemeinsam an):
|
Plans (Wellenstruktur — streng nacheinander, alle drei fassen Schema, Controller oder die Zugriffsschicht gemeinsam an):
|
||||||
|
|
||||||
- [ ] 17-01-PLAN.md (Welle 1) — Alert-Postfach wechselt vom Mandanten zum Nutzer: Schema, handgeschriebene Migration mit Besitzer-Zuordnung, Dienst und Endpunkt, erste Fassung der Seite "Meine Quellen". Enthaelt den Entscheidungspunkt fuer beide Datenbank-Umbauten der Phase.
|
- [x] 17-01-PLAN.md (Welle 1) — Alert-Postfach wechselt vom Mandanten zum Nutzer: Schema, handgeschriebene Migration mit Besitzer-Zuordnung, Dienst und Endpunkt, erste Fassung der Seite "Meine Quellen". Enthaelt den Entscheidungspunkt fuer beide Datenbank-Umbauten der Phase.
|
||||||
- [ ] 17-02-PLAN.md (Welle 2, nach 17-01) — RSS-Feeds bekommen einen Besitzer: Schema und Migration, Besitzerlogik, Schutz gegen fremdes Loeschen, Mengenbegrenzung, Herkunftsmarkierung im Abruf, angepasste Startbestueckung.
|
- [ ] 17-02-PLAN.md (Welle 2, nach 17-01) — RSS-Feeds bekommen einen Besitzer: Schema und Migration, Besitzerlogik, Schutz gegen fremdes Loeschen, Mengenbegrenzung, Herkunftsmarkierung im Abruf, angepasste Startbestueckung.
|
||||||
- [ ] 17-03-PLAN.md (Welle 3, nach 17-01 und 17-02) — Oberflaeche nach Zustaendigkeit trennen: "Meine Quellen" vollstaendig, Administrationsseite reduziert und rollengeprueft, Zahnrad umgehaengt, Beschriftungen in beiden Sprachen, Backlog-Punkt geschlossen.
|
- [ ] 17-03-PLAN.md (Welle 3, nach 17-01 und 17-02) — Oberflaeche nach Zustaendigkeit trennen: "Meine Quellen" vollstaendig, Administrationsseite reduziert und rollengeprueft, Zahnrad umgehaengt, Beschriftungen in beiden Sprachen, Backlog-Punkt geschlossen.
|
||||||
|
|||||||
+23
-18
@@ -2,18 +2,18 @@
|
|||||||
gsd_state_version: 1.0
|
gsd_state_version: 1.0
|
||||||
milestone: v1.1
|
milestone: v1.1
|
||||||
milestone_name: Ausschreibungs-Radar
|
milestone_name: Ausschreibungs-Radar
|
||||||
current_phase: 16
|
current_phase: 17
|
||||||
current_phase_name: AD-Gruppen-Synchronisation
|
current_phase_name: eigene-ausschreibungs-quellen-je-nutzer
|
||||||
status: complete
|
status: executing
|
||||||
stopped_at: Phase 16 abgeschlossen — UAT durchgefuehrt, kritischer Sweep-Fehler gefunden und behoben (260811-f9i), Rest-Annahme A1 bewusst als offen akzeptiert
|
stopped_at: "17-01 abgeschlossen: TenderEmailConfig-Besitz auf userId umgezogen, Migration lokal angewendet (alpha noch nicht ausgerollt), Adapter-Fanout getestet. Naechster Schritt: Plan 17-02 (RSS-Feed-Besitzer)."
|
||||||
last_updated: "2026-08-11T13:17:48.766Z"
|
last_updated: "2026-08-12T09:27:12.562Z"
|
||||||
last_activity: 2026-08-11
|
last_activity: 2026-08-12
|
||||||
last_activity_desc: LDAP-Bind-Passwort verschluesselt (4f687ea)
|
last_activity_desc: Phase 17 execution started
|
||||||
progress:
|
progress:
|
||||||
total_phases: 16
|
total_phases: 17
|
||||||
completed_phases: 15
|
completed_phases: 15
|
||||||
total_plans: 80
|
total_plans: 83
|
||||||
completed_plans: 79
|
completed_plans: 80
|
||||||
---
|
---
|
||||||
|
|
||||||
# Project State
|
# Project State
|
||||||
@@ -23,21 +23,21 @@ progress:
|
|||||||
See: .planning/PROJECT.md (updated 2026-07-17)
|
See: .planning/PROJECT.md (updated 2026-07-17)
|
||||||
|
|
||||||
**Core value:** Eine zentrale Plattform, in der beliebige Workflow-Tools als Module lizenziert, aktiviert und genutzt werden koennen -- ohne zwischen verschiedenen Anwendungen wechseln zu muessen.
|
**Core value:** Eine zentrale Plattform, in der beliebige Workflow-Tools als Module lizenziert, aktiviert und genutzt werden koennen -- ohne zwischen verschiedenen Anwendungen wechseln zu muessen.
|
||||||
**Current focus:** Phase 16 — AD-Gruppen-Synchronisation
|
**Current focus:** Phase 17 — eigene-ausschreibungs-quellen-je-nutzer
|
||||||
|
|
||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Phase: 16 (AD-Gruppen-Synchronisation) — COMPLETE
|
Phase: 17 (eigene-ausschreibungs-quellen-je-nutzer) — EXECUTING
|
||||||
Plan: 5 of 5
|
Plan: 2 of 3
|
||||||
Status: Abgeschlossen. UAT 2/5/6 bestanden, 7 teilweise, 1/3/4/8 bewusst
|
Status: Ready to execute
|
||||||
uebersprungen (kein AD-Schreibzugriff, Entscheidung des Users 2026-08-11).
|
uebersprungen (kein AD-Schreibzugriff, Entscheidung des Users 2026-08-11).
|
||||||
UAT-Test 2 hat einen kritischen Fehler in der Loescherkennung aufgedeckt, der
|
UAT-Test 2 hat einen kritischen Fehler in der Loescherkennung aufgedeckt, der
|
||||||
beim ersten echten Sync alle AD-gebundenen Gruppen entfernt haette — behoben in
|
beim ersten echten Sync alle AD-gebundenen Gruppen entfernt haette — behoben in
|
||||||
260811-f9i. Offene Annahme A1 (objectGUID uebersteht Umbenennung) steht
|
260811-f9i. Offene Annahme A1 (objectGUID uebersteht Umbenennung) steht
|
||||||
dokumentiert in 16-VERIFICATION.md.
|
dokumentiert in 16-VERIFICATION.md.
|
||||||
Last activity: 2026-08-11 — UAT abgeschlossen, Sweep-Fix ausgeliefert
|
Last activity: 2026-08-12 — Phase 17 execution started
|
||||||
|
|
||||||
Progress: [██████████] 100%
|
Progress: [██████████] 96%
|
||||||
|
|
||||||
## Performance Metrics
|
## Performance Metrics
|
||||||
|
|
||||||
@@ -117,6 +117,7 @@ Progress: [██████████] 100%
|
|||||||
| Phase 16-ad-gruppen-synchronisation P03 | 12min | 2 tasks | 3 files |
|
| Phase 16-ad-gruppen-synchronisation P03 | 12min | 2 tasks | 3 files |
|
||||||
| Phase 16-ad-gruppen-synchronisation P04 | 3min | 3 tasks | 5 files |
|
| Phase 16-ad-gruppen-synchronisation P04 | 3min | 3 tasks | 5 files |
|
||||||
| Phase 16 P05 | 6min | 2 tasks | 4 files |
|
| Phase 16 P05 | 6min | 2 tasks | 4 files |
|
||||||
|
| Phase 17 P01 | 76min | 3 tasks | 12 files |
|
||||||
|
|
||||||
## Accumulated Context
|
## Accumulated Context
|
||||||
|
|
||||||
@@ -278,6 +279,10 @@ Recent decisions affecting current work:
|
|||||||
- [Phase ?]: [16-04]: PERM-02 bleibt in REQUIREMENTS.md bewusst auf [ ] - schliesst erst mit Plan 16-05
|
- [Phase ?]: [16-04]: PERM-02 bleibt in REQUIREMENTS.md bewusst auf [ ] - schliesst erst mit Plan 16-05
|
||||||
- [Phase ?]: [16-05]: PERM-02 in REQUIREMENTS.md auf [x] gesetzt - alle 5 Phase-16-Erfolgskriterien code-vollstaendig ueber 16-01..16-03; dieser Plan liefert die Sichtbarkeitsschicht (Sync-Bericht D-05/D-06) und die dritte D-04-Anzeigestelle (Freigabe-Matrix)
|
- [Phase ?]: [16-05]: PERM-02 in REQUIREMENTS.md auf [x] gesetzt - alle 5 Phase-16-Erfolgskriterien code-vollstaendig ueber 16-01..16-03; dieser Plan liefert die Sichtbarkeitsschicht (Sync-Bericht D-05/D-06) und die dritte D-04-Anzeigestelle (Freigabe-Matrix)
|
||||||
- [Phase ?]: [16-05]: A1/A2-Live-Pruefung gegen echtes AD (WINDOWS.md #4) bleibt trotz PERM-02-Abschluss offen - Korrektheitsannahme unter SC-3/SC-4, kein eigenes Erfolgskriterium; negatives Ergebnis waere Stopp-Grund fuer D-05-Loeschsemantik
|
- [Phase ?]: [16-05]: A1/A2-Live-Pruefung gegen echtes AD (WINDOWS.md #4) bleibt trotz PERM-02-Abschluss offen - Korrektheitsannahme unter SC-3/SC-4, kein eigenes Erfolgskriterium; negatives Ergebnis waere Stopp-Grund fuer D-05-Loeschsemantik
|
||||||
|
- [Phase ?]: [17-01]: Checkpoint 1 (gate=blocking) 'weiter' — beide Datenbank-Umbauten der Phase freigegeben, gestuetzt auf gemessene 0 Bestandszeilen (lokal + alpha)
|
||||||
|
- [Phase ?]: [17-01]: TenderEmailConfig.userId @unique ersetzt tenantId @unique (D-01); tenantId bleibt denormalisiert, wird auf create UND update mitgeschrieben
|
||||||
|
- [Phase ?]: [17-01]: email-config-Routen von @Roles(ADMIN,SUPER_ADMIN) auf @UseModule('tender-radar') umgestellt — Postfach ist jetzt Nutzereinstellung (D-01, T-17-06 accept)
|
||||||
|
- [Phase ?]: [17-01]: eigene Seite /modules/tender-radar/my-sources statt Erweiterung von /settings/general/account (D-01 offener Punkt 4)
|
||||||
|
|
||||||
### Pending Todos
|
### Pending Todos
|
||||||
|
|
||||||
@@ -326,7 +331,7 @@ Items acknowledged and carried forward from previous milestone close:
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-08-11T13:10:00.000Z
|
Last session: 2026-08-12T09:27:12.530Z
|
||||||
Stopped at: Sitzung fortgesetzt nach v1.2 — HANDOFF.json gelesen und abgeraeumt; User waehlt Backlog-Punkt "Verschluesselungsschluessel: Vorgabewert entfernen + Beispiel-Umgebungsdateien dokumentieren" als naechstes (Quick Task)
|
Stopped at: 17-01 abgeschlossen: TenderEmailConfig-Besitz auf userId umgezogen, Migration lokal angewendet (alpha noch nicht ausgerollt), Adapter-Fanout getestet. Naechster Schritt: Plan 17-02 (RSS-Feed-Besitzer).
|
||||||
Resume file: None
|
Resume file: None
|
||||||
Last activity: 2026-07-14 - Built LDAP per-user exclude/denylist filter (9d1323f), migration applied on live DB, verified via Playwright: sync deactivated 4 excluded service accounts (administrator/krbtgt/guest/dns-ldap), 2 real LDAP users stay active, 0 wrongly created
|
Last activity: 2026-07-14 - Built LDAP per-user exclude/denylist filter (9d1323f), migration applied on live DB, verified via Playwright: sync deactivated 4 excluded service accounts (administrator/krbtgt/guest/dns-ldap), 2 real LDAP users stay active, 0 wrongly created
|
||||||
|
|||||||
+17
-4
@@ -1,15 +1,15 @@
|
|||||||
---
|
---
|
||||||
schema_version: 1
|
schema_version: 1
|
||||||
open_count: 6
|
open_count: 7
|
||||||
waived_count: 0
|
waived_count: 0
|
||||||
fixed_count: 0
|
fixed_count: 0
|
||||||
total_count: 6
|
total_count: 7
|
||||||
last_updated: 2026-08-06T14:39:03.905Z
|
last_updated: 2026-08-12T09:24:35.289Z
|
||||||
---
|
---
|
||||||
|
|
||||||
# Broken Windows Ledger
|
# Broken Windows Ledger
|
||||||
|
|
||||||
> Cross-phase defect register. `/gsd-ship` blocks while `open_count > 0`.
|
> Cross-phase defect register. With `workflow.windows_enforce` enabled, `/gsd-ship` blocks while `open_count > 0`.
|
||||||
> Waive with `gsd-tools windows waive <id> "<reason>"` (reason required).
|
> Waive with `gsd-tools windows waive <id> "<reason>"` (reason required).
|
||||||
> Mark fixed with `gsd-tools windows fixed <id>`.
|
> Mark fixed with `gsd-tools windows fixed <id>`.
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ last_updated: 2026-08-06T14:39:03.905Z
|
|||||||
| 4 | 16 | unrun-verify | .planning/phases/16-ad-gruppen-synchronisation/16-03-PLAN.md | | 16-03 Task 2 human-check: RESEARCH.md Annahmen A1 (objectGUID uebersteht AD-Umbenennung) und A2 (byteweise Hex-Filter-Syntax) sind gegen kein echtes Active Directory geprueft — kein erreichbares AD in dieser Sandbox. Negatives Ergebnis bei A1 oder A2 ist Stopp-Grund fuer die Loeschsemantik (D-05). | open | | 2026-08-06T14:21:59.987Z | |
|
| 4 | 16 | unrun-verify | .planning/phases/16-ad-gruppen-synchronisation/16-03-PLAN.md | | 16-03 Task 2 human-check: RESEARCH.md Annahmen A1 (objectGUID uebersteht AD-Umbenennung) und A2 (byteweise Hex-Filter-Syntax) sind gegen kein echtes Active Directory geprueft — kein erreichbares AD in dieser Sandbox. Negatives Ergebnis bei A1 oder A2 ist Stopp-Grund fuer die Loeschsemantik (D-05). | open | | 2026-08-06T14:21:59.987Z | |
|
||||||
| 5 | 16 | unrun-verify | .planning/phases/16-ad-gruppen-synchronisation/16-04-PLAN.md | | Manueller Browser-Durchklick aus dem Plan-Verification-Block (drei GroupFormModal-Zustaende: Anlegen mit Hinweis-Link, lokale Gruppe umbenennen, importierte Gruppe mit gesperrtem Namen + internem Namen speichern; Namensanzeige mit Tooltip in der Liste nach dem Speichern) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar. | open | | 2026-08-06T14:31:14.336Z | |
|
| 5 | 16 | unrun-verify | .planning/phases/16-ad-gruppen-synchronisation/16-04-PLAN.md | | Manueller Browser-Durchklick aus dem Plan-Verification-Block (drei GroupFormModal-Zustaende: Anlegen mit Hinweis-Link, lokale Gruppe umbenennen, importierte Gruppe mit gesperrtem Namen + internem Namen speichern; Namensanzeige mit Tooltip in der Liste nach dem Speichern) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar. | open | | 2026-08-06T14:31:14.336Z | |
|
||||||
| 6 | 16 | unrun-verify | .planning/phases/16-ad-gruppen-synchronisation/16-05-PLAN.md | | 16-05 <verification> Manuell nachzuholen (Browser): Sync ausloesen und pruefen, dass alle drei Zahlenzeilen erscheinen; Lauf mit verschobener Standardmarkierung provozieren und Amber-Zeile pruefen; Freigabe-Matrix-Spaltensuche unter beiden Namen probieren - nicht ausgefuehrt, kein Browser-Tool in dieser Session verfuegbar. | open | | 2026-08-06T14:39:03.905Z | |
|
| 6 | 16 | unrun-verify | .planning/phases/16-ad-gruppen-synchronisation/16-05-PLAN.md | | 16-05 <verification> Manuell nachzuholen (Browser): Sync ausloesen und pruefen, dass alle drei Zahlenzeilen erscheinen; Lauf mit verschobener Standardmarkierung provozieren und Amber-Zeile pruefen; Freigabe-Matrix-Spaltensuche unter beiden Namen probieren - nicht ausgefuehrt, kein Browser-Tool in dieser Session verfuegbar. | open | | 2026-08-06T14:39:03.905Z | |
|
||||||
|
| 7 | 17 | unrun-verify | .planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/17-01-PLAN.md | | 17-01 Task 2 human-check: Browser-Gegenprobe (normaler Nutzer oeffnet /modules/tender-radar/my-sources, speichert Postfach, zweites Konto desselben Mandanten sieht leeres Formular) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar. Automatisierte Pruefungen (prisma validate/migrate status, Index-Liste, beide Typpruefungen, 335/335 src/tenders-Tests) sind gelaufen und gruen. | open | | 2026-08-12T09:24:35.289Z | |
|
||||||
|
|
||||||
````json
|
````json
|
||||||
[
|
[
|
||||||
@@ -95,6 +96,18 @@ last_updated: 2026-08-06T14:39:03.905Z
|
|||||||
"reason": "",
|
"reason": "",
|
||||||
"recorded_at": "2026-08-06T14:39:03.905Z",
|
"recorded_at": "2026-08-06T14:39:03.905Z",
|
||||||
"resolved_at": null
|
"resolved_at": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"kind": "unrun-verify",
|
||||||
|
"phase": "17",
|
||||||
|
"file": ".planning/phases/17-eigene-ausschreibungs-quellen-je-nutzer/17-01-PLAN.md",
|
||||||
|
"line": null,
|
||||||
|
"description": "17-01 Task 2 human-check: Browser-Gegenprobe (normaler Nutzer oeffnet /modules/tender-radar/my-sources, speichert Postfach, zweites Konto desselben Mandanten sieht leeres Formular) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar. Automatisierte Pruefungen (prisma validate/migrate status, Index-Liste, beide Typpruefungen, 335/335 src/tenders-Tests) sind gelaufen und gruen.",
|
||||||
|
"status": "open",
|
||||||
|
"reason": "",
|
||||||
|
"recorded_at": "2026-08-12T09:24:35.289Z",
|
||||||
|
"resolved_at": null
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
````
|
````
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
---
|
||||||
|
phase: 17-eigene-ausschreibungs-quellen-je-nutzer
|
||||||
|
plan: 01
|
||||||
|
subsystem: api
|
||||||
|
tags: [prisma, postgresql, nestjs, nextjs, multi-tenancy, migration]
|
||||||
|
|
||||||
|
# Dependency graph
|
||||||
|
requires: []
|
||||||
|
provides:
|
||||||
|
- "TenderEmailConfig.userId @unique (Besitz beim Nutzer statt Mandant, D-01)"
|
||||||
|
- "Handgeschriebene Migration mit Bestandsdaten-Umzug (aeltester aktiver Admin uebernimmt, sonst Loeschung)"
|
||||||
|
- "TenderEmailConfigService per-user (getConfigForApi/saveConfig)"
|
||||||
|
- "GET/PUT /modules/tender-radar/email-config @UseModule('tender-radar') statt @Roles(ADMIN,SUPER_ADMIN)"
|
||||||
|
- "/modules/tender-radar/my-sources ('Meine Quellen') — neue nutzerseitige Modulseite"
|
||||||
|
- "EmailAlertAdapter-Fanout jetzt pro Postfach-Zeile statt pro Mandant (Mechanik unveraendert)"
|
||||||
|
affects: [17-02-rss-feed-owner, 17-03-ui-aufteilung]
|
||||||
|
|
||||||
|
# Actuals (#2632)
|
||||||
|
actuals:
|
||||||
|
tokens: 13600
|
||||||
|
tasks: 3
|
||||||
|
commits: 3
|
||||||
|
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns:
|
||||||
|
- "Handgeschriebene Prisma-Migration mit Backfill-Regel (aeltester aktiver Admin/SUPER_ADMIN je Mandant) vor Pflichtsetzung + Eindeutigkeits-Wechsel — gleiche Werkzeugkette wie 16-01/20260811140000 (migrate diff + Handdatei + migrate deploy)"
|
||||||
|
- "Migrations-SQL wird per Text-Spec (kein DB-Zugriff) auf Reihenfolge geprueft, gleiches Muster wie doe-url-migration-sql.spec.ts"
|
||||||
|
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- apps/api/prisma/migrations/20260812100000_tender_email_config_per_user/migration.sql
|
||||||
|
- apps/api/src/tenders/email-config-migration-sql.spec.ts
|
||||||
|
- "apps/web/src/app/(portal)/modules/tender-radar/my-sources/page.tsx"
|
||||||
|
modified:
|
||||||
|
- apps/api/prisma/schema.prisma
|
||||||
|
- apps/api/src/tenders/tender-email-config.service.ts
|
||||||
|
- apps/api/src/tenders/tenders.controller.ts
|
||||||
|
- apps/api/src/tenders/adapters/email-alert.adapter.ts
|
||||||
|
|
||||||
|
key-decisions:
|
||||||
|
- "Checkpoint 1 (17-01 Task 1, gate=blocking): 'weiter' — Nutzer hat beide Datenbank-Umbauten der Phase (Postfach je Nutzer 17-01, RSS-Feed-Besitzer 17-02) freigegeben am 2026-08-12, nachdem die Zaehlung 0 Bestandszeilen sowohl lokal als auch auf dem alpha-Testserver ergab (lesend per SSH gemessen, keine Aenderung dort vorgenommen)"
|
||||||
|
- "tenantId bleibt denormalisiert auf TenderEmailConfig und wird bei saveConfig auf CREATE UND UPDATE neu geschrieben, nicht nur beim Anlegen — der Mandant eines Nutzers kann sich aendern"
|
||||||
|
- "email-config-Routen von @Roles(ADMIN,SUPER_ADMIN) auf @UseModule('tender-radar') umgestellt: das Postfach ist jetzt eine Nutzereinstellung, Zugang haengt an der Modulfreigabe aus Phase 15 (T-17-06, disposition=accept, beabsichtigt laut D-01)"
|
||||||
|
- "Eigene Seite /modules/tender-radar/my-sources statt Erweiterung von /settings/general/account (D-01 offener Punkt 4) — Modul-Einstellungen bleiben beim Modul"
|
||||||
|
- "EmailAlertAdapter-Warnmeldung im Fehlerfall nennt jetzt Zeilen-id + Besitzer-userId statt tenantId (T-17-03) — ein Mandant kann seit dieser Migration mehrere Postfaecher haben, die alte Meldung waere mehrdeutig"
|
||||||
|
|
||||||
|
requirements-completed: [SRC-01, SRC-03]
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
- id: D1
|
||||||
|
description: "Migration verschiebt TenderEmailConfig-Besitz von tenantId auf userId; Bestandszeilen werden dem aeltesten aktiven Admin/SUPER_ADMIN ihres Mandanten zugeordnet, unbesetzte Zeilen entfernt, alte tenantId-Eindeutigkeit faellt, neue userId-Eindeutigkeit entsteht"
|
||||||
|
requirement: "SRC-01"
|
||||||
|
verification:
|
||||||
|
- kind: unit
|
||||||
|
ref: "apps/api/src/tenders/email-config-migration-sql.spec.ts (6 Tests, reiner Textabgleich der Reihenfolge)"
|
||||||
|
status: pass
|
||||||
|
- kind: integration
|
||||||
|
ref: "prisma migrate deploy gegen lokale Dev-DB (172.19.0.2) + prisma migrate status + Index-Abfrage (TenderEmailConfig_userId_key vorhanden, TenderEmailConfig_tenantId_key entfernt, TenderEmailConfig_tenantId_idx bleibt)"
|
||||||
|
status: pass
|
||||||
|
human_judgment: false
|
||||||
|
- id: D2
|
||||||
|
description: "TenderEmailConfigService liest/schreibt ausschliesslich nach userId; zwei Nutzer desselben Mandanten erhalten zwei unabhaengige Zeilen; Passwort verlaesst den Server nie (nur hasPassword)"
|
||||||
|
requirement: "SRC-01"
|
||||||
|
verification:
|
||||||
|
- kind: unit
|
||||||
|
ref: "apps/api/src/tenders/tender-email-config.service.spec.ts (8 Tests)"
|
||||||
|
status: pass
|
||||||
|
human_judgment: false
|
||||||
|
- id: D3
|
||||||
|
description: "GET/PUT /modules/tender-radar/email-config sind @UseModule-gated statt @Roles-gated; userId/tenantId kommen ausschliesslich aus dem Auth-Kontext, nie aus Query/Body; zwei verschiedene Nutzer loesen unterschiedliche userIds auf (IDOR-Schutz, T-17-01)"
|
||||||
|
requirement: "SRC-01"
|
||||||
|
verification:
|
||||||
|
- kind: unit
|
||||||
|
ref: "apps/api/src/tenders/tenders.controller.spec.ts (email-config describe block, 3 Tests inkl. neuem IDOR-Test)"
|
||||||
|
status: pass
|
||||||
|
human_judgment: false
|
||||||
|
- id: D4
|
||||||
|
description: "EmailAlertAdapter holt mehrere aktive Postfaecher DESSELBEN Mandanten in einem Durchlauf ab, mit je eigenen Zugangsdaten; ein kaputtes Postfach blockiert die anderen nicht; Herkunftsmarkierung folgt weiterhin dem tenantId-Feld der jeweiligen Zeile"
|
||||||
|
requirement: "SRC-01"
|
||||||
|
verification:
|
||||||
|
- kind: unit
|
||||||
|
ref: "apps/api/src/tenders/adapters/email-alert.adapter.spec.ts (23 Tests, davon 3 neu fuer Phase 17: Mehrfach-Postfach-Fanout, Katalog-per-Mailbox-Fehlerbehandlung mit Warnmeldung, ownerTenantId-Divergenz)"
|
||||||
|
status: pass
|
||||||
|
human_judgment: false
|
||||||
|
- id: D5
|
||||||
|
description: "/modules/tender-radar/my-sources ist fuer jeden Nutzer mit Modulzugang erreichbar, zeigt sein eigenes Postfach-Formular (EmailAlertConfigForm unveraendert wiederverwendet) und benennt D-05 (Sichtbarkeit bleibt mandantenweit geteilt) im Hinweistext"
|
||||||
|
requirement: "SRC-03"
|
||||||
|
verification: []
|
||||||
|
human_judgment: true
|
||||||
|
rationale: "Browser-Gegenprobe mit zwei Konten desselben Mandanten (laut Plan-<verify> human-check) nicht ausgefuehrt — kein Browser-Tool in dieser Session verfuegbar. Als WINDOWS.md #7 (unrun-verify) festgehalten. Automatisierte Pruefungen (Typprüfung web+api, Komponente kompiliert, i18n-Keys vorhanden) sind gelaufen und gruen."
|
||||||
|
|
||||||
|
duration: 76min
|
||||||
|
completed: 2026-08-12
|
||||||
|
status: complete
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 17 Plan 01: Eigenes Alert-Postfach je Nutzer Summary
|
||||||
|
|
||||||
|
**TenderEmailConfig-Besitz per Handmigration von tenantId auf userId umgezogen (Bestandszeilen dem aeltesten aktiven Admin zugeordnet), Service/Controller/Adapter auf Nutzer-Scoping umgestellt, neue Seite `/modules/tender-radar/my-sources` mit dem unveraenderten Postfach-Formular.**
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
- **Duration:** 76 min (inkl. Checkpoint-Wartezeit auf Nutzerfreigabe)
|
||||||
|
- **Started:** 2026-08-12T08:08:41Z
|
||||||
|
- **Completed:** 2026-08-12T09:25:00Z
|
||||||
|
- **Tasks:** 3 (Checkpoint-Entscheidung, Tracer-Task durchgehende Bahn, Test-Erweiterung Adapter)
|
||||||
|
- **Files modified:** 12 (3 neu, 9 geaendert)
|
||||||
|
|
||||||
|
## Bestandsaufnahme aus dem Checkpoint (Task 1)
|
||||||
|
|
||||||
|
Vor der Freigabe wurde gezaehlt, wie viele `TenderEmailConfig`-Zeilen tatsaechlich existieren:
|
||||||
|
|
||||||
|
- **Lokale Entwicklungsdatenbank:** 0 Zeilen
|
||||||
|
- **Testserver alpha.tessera.ctl.de (192.168.13.12):** 0 Zeilen (lesend per SSH/psql gezaehlt, keine Aenderung dort vorgenommen)
|
||||||
|
|
||||||
|
Die im Plan beschriebene Umzugsregel ("aeltester aktiver Administrator uebernimmt die Bestandszeile, sonst wird sie geloescht") hatte damit **keine echten Daten zu bewegen** — sie ist als Logik in der Migrationsdatei angelegt und per Text-Spec abgesichert, griff aber bei der tatsaechlichen Anwendung ins Leere. Der Nutzer hat auf dieser Grundlage mit "weiter" beide Datenbank-Umbauten der Phase (dieser Plan + Plan 17-02) freigegeben.
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- `TenderEmailConfig.userId @unique` ersetzt `tenantId @unique` — zwei Nutzer desselben Mandanten koennen jetzt gleichzeitig je ein eigenes Alert-Postfach hinterlegen (D-01)
|
||||||
|
- Handgeschriebene Migration mit korrekter Backfill-Reihenfolge (Zuordnung vor Loeschung, Pflicht erst nach Befuellung), lokal angewendet und per Index-Abfrage verifiziert
|
||||||
|
- `TenderEmailConfigService` und die `GET`/`PUT /email-config`-Endpunkte lesen/schreiben ausschliesslich nach `userId` aus dem Auth-Kontext — nie aus Query/Body (IDOR-Schutz)
|
||||||
|
- `EmailAlertAdapter` holt weiterhin alle aktiven Postfaecher mandantenuebergreifend in einem Zug ab (Mechanik unveraendert), jetzt nachweislich auch mehrere Postfaecher desselben Mandanten; ein kaputtes Postfach blockiert die anderen nicht
|
||||||
|
- Neue Seite `/modules/tender-radar/my-sources` ("Meine Quellen") mit dem unveraenderten `EmailAlertConfigForm` und einem Hinweistext, der D-05 ehrlich benennt (eingelesene Ausschreibungen bleiben mandantenweit sichtbar)
|
||||||
|
- `Tender` bleibt unveraendert plattform-global — keine Sichtbarkeitstrennung eingefuehrt (D-05, harte Grenze eingehalten)
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
Jede Aufgabe wurde einzeln committet:
|
||||||
|
|
||||||
|
1. **Task 1: Freigabe fuer die beiden Datenbank-Umbauten dieser Phase** — Checkpoint, kein eigener Commit (Entscheidung "weiter" durch den Nutzer, dokumentiert oben und in STATE.md)
|
||||||
|
2. **Task 2: Ein eigenes Postfach je Nutzer — durchgehend von der Datenbank bis zur Seite** — `05b1d29` (feat)
|
||||||
|
3. **Task 3: Der Abruf holt alle Postfaecher — und beweist es mit Tests** — `55ceb24` (test)
|
||||||
|
|
||||||
|
**Plan metadata:** wird mit diesem SUMMARY committet (docs)
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
- `apps/api/prisma/schema.prisma` — `TenderEmailConfig.userId @unique` ergaenzt, `tenantId` auf gewoehnliches Feld zurueckgestuft, `@@index([userId])` ergaenzt
|
||||||
|
- `apps/api/prisma/migrations/20260812100000_tender_email_config_per_user/migration.sql` — Handmigration: Spalte anlegen, Bestandszeilen dem aeltesten aktiven Admin zuordnen, unbesetzte Zeilen loeschen, Eindeutigkeit von tenantId auf userId umziehen
|
||||||
|
- `apps/api/src/tenders/email-config-migration-sql.spec.ts` — neuer Text-Spec (kein DB-Zugriff), prueft die Reihenfolge der Handmigration
|
||||||
|
- `apps/api/src/tenders/tender-email-config.service.ts` — `getConfigForApi(userId)`, `saveConfig({userId, tenantId}, dto)`, tenantId auf create UND update mitgeschrieben
|
||||||
|
- `apps/api/src/tenders/tender-email-config.service.spec.ts` — auf userId-Schluessel umgestellt, plus zwei neue Faelle (tenantId beim Anlegen, zwei Nutzer = zwei Zeilen)
|
||||||
|
- `apps/api/src/tenders/tenders.controller.ts` — email-config-Routen von `@Roles(ADMIN,SUPER_ADMIN)` auf `@UseModule('tender-radar')`, `userId`/`tenantId` aus `extractTriageContext(req)`, Route-Reihenfolge vor `@Get(':id')` unveraendert
|
||||||
|
- `apps/api/src/tenders/tenders.controller.spec.ts` — Fake-Service-Signaturen + Tests an userId-Schluessel angepasst (Rule 3, nicht im Plan gelistet), plus neuer IDOR-Test
|
||||||
|
- `apps/api/src/tenders/adapters/email-alert.adapter.ts` — Warnmeldung nennt Zeilen-id + Besitzer statt Mandant (T-17-03); Sammelabfrage-Mechanik und Fehlerbehandlung je Zeile unangetastet
|
||||||
|
- `apps/api/src/tenders/adapters/email-alert.adapter.spec.ts` — Config-Fixtures um `id`/`userId` ergaenzt, drei neue Faelle (Mehrfach-Postfach-Fanout, Warnmeldungs-Inhalt, ownerTenantId-Divergenz)
|
||||||
|
- `apps/web/src/app/(portal)/modules/tender-radar/my-sources/page.tsx` — neue Seite "Meine Quellen"
|
||||||
|
- `apps/web/src/messages/de.json`, `apps/web/src/messages/en.json` — `tenderRadar.mySources.*` Schluessel ergaenzt
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
- Checkpoint 1 (gate=blocking): "weiter" — Freigabe fuer beide Datenbank-Umbauten der Phase, gestuetzt auf gemessene 0 Bestandszeilen (lokal und alpha)
|
||||||
|
- tenantId bleibt denormalisiert, wird bei jedem Save (create UND update) neu geschrieben
|
||||||
|
- email-config-Endpunkte wechseln von rollenbasiert auf modulbasiert (D-01 beabsichtigt, T-17-06 disposition=accept)
|
||||||
|
- Eigene Seite statt Erweiterung von `/settings/general/account` (D-01 offener Punkt 4)
|
||||||
|
- Adapter-Warnmeldung nennt Zeilen-id + userId statt tenantId, da ein Mandant jetzt mehrere Postfaecher haben kann
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Auto-fixed Issues
|
||||||
|
|
||||||
|
**1. [Rule 3 - Blocking] `tenders.controller.spec.ts` an neue Service-Signatur angepasst**
|
||||||
|
- **Found during:** Task 2 (Type-Check nach Controller-Umstellung)
|
||||||
|
- **Issue:** Die bestehenden Controller-Tests riefen `getConfigForApi(tenantId)`/`saveConfig(tenantId, dto)` mit der alten Signatur auf — nicht im Plan gelistet, aber ohne Anpassung kompiliert das Projekt nicht und die Tests schlagen fehl
|
||||||
|
- **Fix:** Fake-Service-Signaturen und die zwei email-config-Tests auf `getConfigForApi(userId)`/`saveConfig({userId,tenantId}, dto)` umgestellt; einen zusaetzlichen IDOR-Test ergaenzt (zwei Nutzer loesen unterschiedliche userIds auf)
|
||||||
|
- **Files modified:** apps/api/src/tenders/tenders.controller.spec.ts
|
||||||
|
- **Verification:** `pnpm --filter @tessera/api type-check` fehlerfrei, 49/49 Tests in der Datei gruen
|
||||||
|
- **Committed in:** 05b1d29 (Task 2 commit)
|
||||||
|
|
||||||
|
**2. [Rule 3 - Blocking] `tender-email-config.service.spec.ts` vollstaendig auf userId umgeschrieben, bereits in Task 2**
|
||||||
|
- **Found during:** Task 2 (Type-Check — `saveConfig` erwartet jetzt ein Objekt statt eines Strings)
|
||||||
|
- **Issue:** Die Plan-Zuordnung dieser Datei zu Task 3 haette den Type-Check von Task 2 nicht bestehen lassen — der Compiler kennt keine Task-Grenzen
|
||||||
|
- **Fix:** Datei bereits in Task 2 auf die neue Signatur umgeschrieben (inkl. der beiden von Task 3 verlangten neuen Faelle: tenantId beim Anlegen, zwei Nutzer = zwei Zeilen), damit beide Tasks durchgaengig kompilieren
|
||||||
|
- **Files modified:** apps/api/src/tenders/tender-email-config.service.spec.ts
|
||||||
|
- **Verification:** 8/8 Tests gruen in Task 2 wie in Task 3
|
||||||
|
- **Committed in:** 05b1d29 (Task 2 commit, vorgezogen aus Task 3)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Total deviations:** 2 auto-fixed (beide Rule 3 — Kompilierfaehigkeit)
|
||||||
|
**Impact on plan:** Beide Anpassungen waren fuer die Korrektheit der eigentlichen Plan-Aenderung zwingend notwendig (der Controller/Service-Vertrag aendert sich, angrenzende Tests muessen mitziehen). Kein Scope Creep — beide Dateien testen ausschliesslich das, was der Plan ohnehin verlangt.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
- Lokaler Postgres-Container (`tessera-ctl-db-1`) lief zu Sessionbeginn nicht — gestartet und ueber die dokumentierte Container-IP (172.19.0.2, `tessera:tessera_dev`) angebunden, wie in den Projektnotizen vorgesehen
|
||||||
|
- Zwei bereits vorhandene, noch nicht angewendete Migrationen (`20260811120000_doe_notice_url_backfill`, `20260811140000_encrypt_ldap_bind_password`) mussten vor der neuen Migration per `migrate deploy` nachgezogen werden — kein Blocker, nur Reihenfolge
|
||||||
|
|
||||||
|
## User Setup Required
|
||||||
|
|
||||||
|
None — keine externe Dienstkonfiguration erforderlich. Die Migration ist bisher nur lokal angewendet; das Ausrollen auf dem Testserver (192.168.13.12) bleibt bewusst Nutzeraktion beim naechsten Deploy (Projektregel: kein `docker compose pull/up/rebuild` durch Claude auf dem Testserver).
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
- Die duenne Bahn (Datenbank -> Migration -> Dienst -> Endpunkt -> Seite) steht und ist bewiesen (335/335 `src/tenders`-Tests, 603/603 API gesamt, 192/192 Web gesamt, beide Typpruefungen fehlerfrei)
|
||||||
|
- Plan 17-02 (RSS-Feeds, Besitzer-Feld) kann auf denselben Checkpoint-Freigaben aufbauen — die Nutzerfreigabe deckte explizit beide Datenbank-Umbauten der Phase ab
|
||||||
|
- Offen: Browser-Gegenprobe fuer `/modules/tender-radar/my-sources` mit zwei Konten desselben Mandanten (WINDOWS.md #7, unrun-verify) — vor `/gsd-ship` nachzuholen
|
||||||
|
- Offen: Rollout auf dem Testserver bleibt Nutzeraktion; die Migration liegt bereit, ist aber dort noch nicht angewendet
|
||||||
|
|
||||||
|
## Self-Check: PASSED
|
||||||
|
|
||||||
|
Alle im SUMMARY genannten Dateien existieren auf der Festplatte; beide Task-Commit-Hashes (`05b1d29`, `55ceb24`) sind im Git-Log auffindbar.
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 17-eigene-ausschreibungs-quellen-je-nutzer*
|
||||||
|
*Completed: 2026-08-12*
|
||||||
Reference in New Issue
Block a user