Files
tessera-ctl/.planning/quick/261008-dts-modul-domains-autodns-anbindung-kontakte/261008-dts-SUMMARY.md
T
2026-10-08 11:34:16 +02:00

173 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
phase: quick-261008-dts
plan: 01
subsystem: domains
tags: [autodns, domains, nestjs, nextjs, prisma, rls, money-safety]
status: complete
requires:
- module-registry (ModuleGuard, UseModule, ModuleManage)
- crypto (CryptoService, global)
- prisma tenant extension (forTenant)
provides:
- Modul Domains (Slug domains) mit AutoDNS-Anbindung Demo/Live
- Kunden, Kontakt-Zuordnung, Domainliste, Verfügbarkeit, Registrierung, Aufträge
affects:
- docs/mandantentrennung-zugriffsklassifikation.md
- CHANGELOG.md, docs/anleitung-anwender.md, docs/anleitung-administration.md
tech-stack:
added: []
patterns:
- atomarer Anspruch (updateMany where status DRAFT, count === 1) vor dem Netzaufruf
- ein Client für alle AutoDNS-Aufrufe (feste Hosts, Takt 350 ms, kein Retry)
- Pull-Abgleich offener Aufträge statt Cron
key-files:
created:
- apps/api/src/domains/autodns-client.ts
- apps/api/src/domains/autodns-parse.ts
- apps/api/src/domains/domain-name.ts
- apps/api/src/domains/domains-settings.service.ts
- apps/api/src/domains/domains-directory.service.ts
- apps/api/src/domains/domains-orders.service.ts
- apps/api/prisma/migrations/20261008120000_domains_autodns/migration.sql
- apps/web/src/app/(portal)/modules/domains/page.tsx
- apps/web/src/app/(portal)/modules/domains/components/RegisterTab.tsx
- apps/web/src/app/(portal)/modules/domains/components/OrdersTab.tsx
modified:
- apps/api/src/domains/domains.controller.ts
- CHANGELOG.md
decisions:
- "Aufträge des nicht aktiven Systems werden beim Abgleich nicht angefasst (Zugangsdaten gehören zum aktiven System); nur der SUBMITTING-zu-UNKNOWN-Übergang gilt für alle."
- "Beim Abschicken wird der Entwurf VOR dem Anspruch gelesen, damit genau der bestätigte Inhalt hinausgeht; der Zugang wird vor dem Anspruch geladen, damit ein fehlender Zugang nie einen Auftrag in SUBMITTING stehen lässt."
- "HTTP-Antwort ohne Umschlag (Gateway 502/504) zählt als UNKNOWN, nicht als FAILED; FAILED nur bei ausdrücklicher AutoDNS-Ablehnung (auth, forbidden, rate-limit, business)."
- "UNKNOWN wird beim Abschicken ohne lastCheckedAt gespeichert; erst ein Abgleich mit Ergebnis setzt es und schaltet das Verwerfen frei. Ein Fehler beim Nachsehen setzt es nicht."
- "Auftragssuche beim Abgleich nimmt nur Aufträge mit lesbarer Anlagezeit ab 5 Minuten vor der Bestätigung; ohne Zeit zählt ein Auftrag nicht (lieber UNKNOWN als ein falscher Treffer)."
metrics:
tasks: 3
completed: 2026-10-08
actuals:
tokens: 109600
tasks: 3
commits: 3
plan_head_before: 6b0f840c1cf1ad09d593e9f607b5230541fdfb2b
plan_head_after: d332246bdead2b5ec87156dd610945687c672016
---
# Phase quick-261008-dts Plan 01: Modul Domains (AutoDNS) Summary
Neues Modul „Domains“ mit verschlüsselter Demo-/Live-Anbindung an AutoDNS, Kunden- und Kontaktzuordnung, Domainliste und einer Registrierung, die pro Auftrag höchstens einmal an AutoDNS geht und bei unklarem Ausgang auf „Ergebnis ungeklärt“ stehen bleibt, statt zu raten.
## Commits
| Aufgabe | Commit | Inhalt |
|---|---|---|
| 1 (Tracer) | 1f1c984 | Migration (4 Tabellen mit Zeilenschutz), AutoDNS-Client, Einstellungen, Verbindungstest, Modulseite mit Reiter Einstellungen, Registrierungen (Loader, Icon, Navigationstitel) |
| 2 | 450218f | Kunden, Kontakte live aus AutoDNS (Seiten, 60-s-Zwischenspeicher), Zuordnung, Kontakt anlegen, Domainliste, Reiter Domains/Kontakte/Kunden |
| 3 | d332246 | Verfügbarkeit, Entwurf, verbindliches Abschicken mit atomarem Anspruch, Auftragsverfolgung und Abgleich, Reiter Registrieren/Aufträge, Changelog, Anwender- und Administrationshandbuch, Mandantenschutz-Inventar |
Nichts gepusht. Docs-Artefakte (PLAN, RESEARCH, SUMMARY, STATE) sind nicht committet.
## Aufgabe 3 im Detail (Geldsicherheit)
- **Anspruch vor dem Netz:** `submitOrder` führt `updateMany` mit `id, tenantId, status: 'DRAFT', environment: <aktiv>` aus. Nur bei `count === 1` geht genau ein `POST /domain` (ohne Query, ohne Wiederholung) hinaus. Wer `count !== 1` bekommt, lädt die Zeile neu und erhält 404, 409 `environmentChanged` oder 409 `alreadySubmitted`.
- **Ausgang:** Auftragsnummer vorhanden gleich SUBMITTED. Ausdrückliche AutoDNS-Ablehnung (auth, forbidden, rate-limit, business, auch HTTP 200 mit `status.type` ERROR) gleich FAILED mit `openKey` null. Zeitablauf, Netz, TLS, unlesbare oder umschlaglose Antwort gleich UNKNOWN, nie zurück auf DRAFT.
- **Eindeutigkeit:** `openKey` (Domain) mit `@@unique([tenantId, environment, openKey])`; FAILED und CANCELED geben ihn frei, SUCCESS behält ihn bewusst.
- **Abgleich:** Pull beim Öffnen des Reiters „Aufträge“, bei „Aktualisieren“ und alle 30 s bei offenen Aufträgen und sichtbarer Seite; höchstens 20 Aufträge je Lauf, am längsten ungeprüfte zuerst; ein Fehler bei einem Auftrag hält die anderen nicht auf. SUBMITTING älter als 120 s wird UNKNOWN. UNKNOWN: erst `GET /domain/{name}`, dann `POST /job/_search`.
- **Verwerfen:** Entwurf jederzeit; UNKNOWN erst nach einem Abgleich mit Ergebnis (`lastCheckedAt` gesetzt), im Browser mit zusätzlicher Warnung.
- **Browser:** `useRef`-Sperre gegen Doppelklick; nach einem Fehler beim Abschicken bleibt der Knopf gesperrt, bis „Zurück“ gewählt wird (der Auftrag könnte angekommen sein).
## Messwerte der Prüfkette (Aufgabe 3)
| Segment | Ergebnis |
|---|---|
| `pnpm --filter @tessera/api test` | 133 Dateien, 2423 Tests grün |
| `pnpm --filter @tessera/web test` | 132 Dateien, 1455 Tests grün |
| `tsc --noEmit` api und web | grün |
| `biome check` (alle angefassten Dateien) | grün |
| rls-coverage, rls-access-inventory | 35 Tests grün (domains: 0/30/0, Paare 104) |
| Rollen-Decorator im Controller | keiner |
| de/en-Schlüsselgleichheit `domains.*`, Wortprüfung (Mandant, Lizenz) | 207 Schlüssel, gleich, ohne Treffer |
| CHANGELOG-/Handbuch-Greps | ok |
| Neuaufbau `docker compose up -d --build api web` | api, web, db laufen; „Domains module seeded in registry“; Routen `orders/refresh` vor `orders/:id/submit`; keine ausstehenden Migrationen; `GET customers` und `GET orders` geben `[]`; `POST availability` ohne Zugang gibt 409 `notConfigured` |
| Gegenprobe Doppelbestellung | Anspruchsprüfung testweise ausgebaut: Test „zwei gleichzeitige Bestätigungen“ und Test „Systemwechsel zwischen Lesen und Anspruch“ schlugen fehl; danach zurückgebaut |
## Abweichungen vom Plan
### Automatisch behoben und ergänzt
**1. [Regel 2 - Fehlende Absicherung] Kontakt-Prüfung beim Entwurf**
- **Ort:** `createOrder`
- **Problem:** Ohne Prüfung könnte ein Entwurf Kontaktnummern enthalten, die bei AutoDNS nicht (mehr) existieren.
- **Lösung:** Alle vier Kontaktnummern werden gegen die Kontaktliste geprüft (`findContactsByIds`), sonst 400 `contactNotFound`. Grenze: bei mehr als 2000 Kontakten kann ein vorhandener Kontakt außerhalb der gelesenen Liste fälschlich abgelehnt werden; die Meldung rät zum Neu-Einlesen.
**2. [Regel 3 - Blockierend] Zeitlimit im Test einstellbar**
- `DomainsSettingsService.transport` bekam das Feld `timeoutMs` (nur für Tests), damit der Zeitablauf-Fall mit 20 ms getestet werden kann; im Betrieb bleibt es leer (20 s). `HOSTNAME_PATTERN` wird exportiert und im Auftragsdienst wiederverwendet.
**3. [Regel 1 - Fehler im eigenen Entwurf] Zusätzliche Absicherungen im Abgleich**
- Ein Fehler beim Nachsehen (Anmeldung, Zeitablauf, Gateway) setzt `lastCheckedAt` nicht, damit „Verwerfen“ nie auf einer Prüfung beruht, die nichts geprüft hat. Nur eine ausdrückliche AutoDNS-Antwort „gibt es nicht“ (Fehlerart business) zählt als Ergebnis.
- Alle Schreibzugriffe des Abgleichs sind an den erwarteten Zustand gebunden (`status` in der `where`-Klausel), damit zwei gleichzeitige Abgleiche sich nicht überschreiben.
**4. [Hinweis] Reiter Aufträge für alle, auch ohne Einrichtung**
- Der Reiter ist immer sichtbar (Liste kommt aus der Datenbank); der Abgleich überspringt ihn still, wenn das aktive System nicht eingerichtet ist.
**5. [Aufräumen] Versehentliche Fremdformatierung rückgängig gemacht**
- Ein Formatierungslauf hatte `module-manage-handlers.spec.ts` in unbeteiligten Zeilen umgebrochen; die Datei wurde zurückgesetzt und nur um die sechs Handlernamen ergänzt.
### Nicht behoben (vorbestehend)
Keine fehlschlagenden Tests. `biome organizeImports` meldet in `module-manage-handlers.spec.ts` eine bereits vor dieser Arbeit vorhandene Importreihenfolge; bewusst nicht angefasst.
## Threat-Status
| ID | Stand |
|---|---|
| T-dts-01 Passwort | umgesetzt (Aufgabe 1); Antworten tragen nur `hasPassword` |
| T-dts-02 Rechte | alle Schreib- und Verwalten-Wege tragen `@ModuleManage('domains')`, kein Rollen-Decorator; Controller- und Handler-Spec grün |
| T-dts-03 Doppelbestellung | umgesetzt und mit Gegenprobe belegt (siehe oben) |
| T-dts-04 SSRF | feste Hosts, `redirect: 'error'`, Pfadprüfung |
| T-dts-05 Organisationstrennung | `forTenant` überall, RLS-Gates grün |
| T-dts-06 Abfragegrenze | Takt 350 ms, höchstens 20 Aufträge je Abgleich, Verbindungstest ein Aufruf |
| T-dts-07 Demo/Live | System in Anspruch und Zuordnung, Wechsel nur mit Bestätigung |
| T-dts-08 Routen-Reihenfolge | `orders/refresh` vor `orders/:id/...`, Spec prüft |
| T-dts-09 Eingaben | `normalizeDomainName`, Validatoren, `ParseUUIDPipe` |
| T-dts-10 Fehlertexte | nur `messages[].text`, gekürzt; AutoDNS 401/403 als 502 |
## Bekannte Stubs
Keine. Die Anzeige „Preis nicht ermittelbar“ ist gewollt (AutoDNS nennt keinen Preis).
## Prüfung gegen AutoDNS-Demo
Voraussetzung: Demo-Zugangsdaten eintragen (Domains, Einstellungen, Zugang Demo-System, Benutzername, Passwort, Kontext). System bleibt auf „Demo-System“. Jeder Punkt schließt eine offene Annahme aus der Recherche.
| Nr. | Annahme | Prüfung in der Oberfläche | Wenn es abweicht |
|---|---|---|---|
| 1 | A1: Welcher Demo-Kontext funktioniert (1 oder 4) | Einstellungen, Zugang Demo-System: Kontext 4 eintragen, speichern, „Verbindung testen“; bei Fehler 1 versuchen. Je Versuch genau einmal klicken (Sperrgefahr) | Wert merken und in die Handbücher übernehmen |
| 2 | A6/A9: Anmeldung ohne Zwei-Faktor, Test gelingt | „Verbindung testen“ zeigt „Verbindung erfolgreich“ | bei Anmeldefehler Benutzer ohne 2FA verwenden |
| 3 | A2: Kontaktfelder und Telefonformat | Reiter Kontakte, „Neuer Kontakt“: erst Person, dann Organisation anlegen (Telefon +49 30 123456). Meldung „Der Kontakt wurde bei AutoDNS angelegt“, Kontakt erscheint nach „Aus AutoDNS neu einlesen“ | Fehlermeldung von AutoDNS lesen (steht im Formular); Pflichtfelder oder Telefonformat anpassen |
| 4 | A3: Verfügbarkeitsprüfung liefert genau die gefragte Domain mit WHOIS-Status und Preis | Reiter Registrieren: eine sicher freie Domain (zufälliger Name) und eine belegte (zum Beispiel denic.de) prüfen. Frei zeigt „… ist frei“ mit Preis; belegt zeigt „nicht frei“ mit Status. Außerdem eine Umlautdomain prüfen | Steht immer „nicht frei“ mit Status „NO_RESULT“, liefert DomainStudio den Namen anders (Suchwort, Endung) und `parseDomainStudio` oder die Anfrage ist anzupassen. Fehlt nur der Preis, erscheint „Preis nicht ermittelbar“: Preisform prüfen |
| 5 | A4/A5: Registrierung liefert Auftragsnummer, `{ id }` genügt als Kontaktverweis | Zusammenfassung erstellen, Häkchen setzen, „Jetzt verbindlich registrieren“. Erwartet: „wird bearbeitet“, Reiter Aufträge zeigt „In Bearbeitung“ | „Fehlgeschlagen“ mit Text von AutoDNS: Text lesen (Kontaktverweis, Nameserver, Pflichtfelder). Steht ein Auftrag ohne Nummer auf „In Bearbeitung“ und ändert sich nie, ist die Fundstelle der Nummer anders (`extractJobFromSubmit`) |
| 6 | Auftrag erreicht SUCCESS, Tessera folgt | Reiter Aufträge, „Aktualisieren“ (oder 30 s warten): Stand wechselt auf „Registriert“ | bleibt er „In Bearbeitung“, in AutoDNS den Auftrag ansehen; „Rückfrage nötig“ heißt Status SUPPORT |
| 7 | D-J: Domainliste mit Inhaber, Ablaufdatum, `registryStatus` | Reiter Domains, „Aus AutoDNS neu laden“: die neue Domain erscheint mit Inhaber, Ablaufdatum und Status (Aktiv, In Bearbeitung …). Kunde nach Zuordnung des Inhaber-Kontakts | Status als Rohwert angezeigt: `registryStatus` hat andere Werte, Beschriftungen nachziehen. Ablaufdatum „–“: Feld `expire` fehlt in der Antwort |
| 8 | A8: `.de` mit Standard-Nameservern besteht die Nameserver-Prüfung | Standard-Nameserver in den Einstellungen eintragen (bei AutoDNS eingerichtete), eine freie `.de`-Domain registrieren | „Fehlgeschlagen“ mit Nameserver-Meldung: Nameserver einrichten oder korrigieren |
| 9 | Neu in Aufgabe 3: Auftragssuche (`POST /job/_search`, Filter `object`, Anlagezeit) und `GET /domain/{name}` für nicht Gefundenes | Schwer künstlich auszulösen. Wenn ein Auftrag je auf „Ergebnis ungeklärt“ steht: „Aktualisieren“ klicken. Erwartet: Tessera findet die Domain (Registriert), einen Auftrag (In Bearbeitung) oder setzt die Prüfung (dann ist „Verwerfen“ möglich) | Bleibt „Verwerfen“ trotz Abgleich gesperrt, liefert `GET /domain/{name}` für Unbekanntes keinen business-Fehler; Antwortform prüfen |
| 10 | Neu in Aufgabe 3: Umlautdomain wird als Punycode gesucht und bestellt | Umlautdomain prüfen; Anzeige zeigt die Umlautform | DomainStudio erwartet ggf. die Unicode-Form im Suchwort |
| 11 | D-J: Wert für „Kündigung vorgemerkt“ | Eine Domain mit vorgemerkter Kündigung in der Liste ansehen (falls vorhanden) | Werte von `cancelationStatus` prüfen (NONE und NOT_SET gelten als „keine“) |
## Browser-Prüfschritte für den Orchestrator (dunkler Modus bevorzugt)
Lokal: `http://localhost:3000`, Anmeldung `admin` / `admin123`. Playwright-Prüfung mit Theme-Knopf auf dunkel.
1. **Aktivieren:** Marktplatz, Modul „Domains“ aktivieren. Erwartet: Modul in der Seitenleiste (Gruppe Domains), Erde-Symbol.
2. **Freigabe Benutzen gegen Verwalten:** Mit einem normalen Benutzer mit „Benutzen“ anmelden: Reiter Domains, Kontakte, Kunden, Aufträge sichtbar; kein Registrieren, kein Einstellungen, kein „Neuer Kontakt“, keine Zuordnung, keine Kunden-Knöpfe. Mit Admin oder „Verwalten“: zusätzlich Registrieren und Einstellungen.
3. **Einstellungen:** Ohne Zugang zeigt die Kopfzeile „AutoDNS nicht eingerichtet“ mit Hinweis und Knopf „Zu den Einstellungen“. Passwort eintragen, speichern, Seite neu laden: Passwortfeld leer mit Platzhalter „Gespeichert – leer lassen …“. Live-Kontext ist mit 4 vorbelegt. Auswahl „Live-System“: Bestätigungsfeld erscheint; „Abbrechen“ stellt Demo zurück; erst „Live-System verwenden“ speichert, Kopfzeile zeigt dann „Live-System – Registrierungen kosten Geld“. Wieder auf Demo zurückstellen. Nameserver (zwei) eintragen und speichern.
4. **Kontakte:** Liste, Suche, Filter „Nicht zugeordnet“, „Nach Kunde gruppieren“; Kontakte ankreuzen, Kunden wählen, „Zuordnen“; „Zuordnung entfernen“; „Neuer Kontakt“ mit ungültiger E-Mail und mit Telefon ohne „+“ (Fehler sichtbar), dann gültig speichern.
5. **Kunden:** Kunden anlegen, einen als „Eigene Firma“ markieren (Kennzeichnung erscheint, bei einem zweiten wandert sie); Kunden mit Kontakten löschen: Meldung „Diesem Kunden sind noch Kontakte zugeordnet …“.
6. **Domains:** Liste, Gruppen pro Kunde mit „Nicht zugeordnet“ zuletzt, Kundenfilter blendet andere Gruppen aus, Datum als TT.MM.JJJJ.
7. **Registrieren:** Ungültiger Name zeigt Meldung; freie und belegte Domain; Kontaktauswahl gruppiert nach Kunde, Voreinstellung Admin gleich Inhaber, Technik/Zone gleich eigene Firma; Nameserver hinzufügen/entfernen (2 bis 6); „Zusammenfassung anzeigen“ zeigt System-Kennzeichnung, Preis oder „Preis nicht ermittelbar“; „Jetzt verbindlich registrieren“ bleibt gesperrt bis zum Häkchen; **Doppelklick auf den Knopf** darf nur einen Auftrag erzeugen (Reiter Aufträge zeigt eine Zeile, Server-Log zeigt ein einziges „Registrierung abgeschickt“). Vorsicht: Mit Demo-Zugang ohne echte Daten antwortet AutoDNS mit einem Fehler, die Zeile steht dann auf „Fehlgeschlagen“ oder „Ergebnis ungeklärt“; beides ist ein gültiger Ausgang.
8. **Aufträge:** Reiter zeigt Zeilen mit System (Demo/Live), Stand, Bestätigt von/am, Fehlertext; „Aktualisieren“; „Verwerfen“ nur für Verwalter bei Entwurf und geprüftem unklarem Auftrag, mit Rückfrage.
## Self-Check: PASSED
- Dateien vorhanden: domain-name.ts, domains-orders.service.ts, dto/domains-order.dto.ts, RegisterTab.tsx, OrdersTab.tsx, order-status.ts (alle von `git status` als hinzugefügt bestätigt).
- Commits vorhanden: 1f1c984, 450218f, d332246 liegen in der Historie von HEAD (`git rev-list --count 6b0f840..HEAD` gleich 3).