feat(domains): Modul Domains mit AutoDNS-Zugang, Verbindungstest und Einstellungen

- Migration mit vier Tabellen (Einstellungen, Kunden, Kontaktzuordnung, Bestellungen), Zeilenschutz je Organisation
- AutoDNS-Client mit festen Demo-/Live-Adressen, Takt-Begrenzer, 20 s Zeitlimit, ohne Wiederholung
- Zugang je System verschluesselt gespeichert, Live-Wechsel nur mit Bestaetigung, Verbindungstest
- Modulseite mit Systemkennzeichnung und Reiter Einstellungen (nur Verwalten)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-08 10:35:21 +02:00
parent 6b0f840c1c
commit 1f1c984184
29 changed files with 3123 additions and 2 deletions
+102
View File
@@ -841,6 +841,108 @@ model NextcloudAlertSubscription {
@@index([tenantId, userId])
}
// Domains (quick-261008-dts): Anbindung an AutoDNS / InterNetX (Domainrobot).
// AutoDNS bleibt die Quelle der Wahrheit fuer Kontakte und Domains (werden live
// gelesen, nie gespiegelt); lokal liegt nur, was AutoDNS nicht kennt (Zuordnung
// Kontakt -> Kunde) oder was Geldsicherheit braucht (Bestellungen). AutoDNS-
// Kontakt- und Job-Ids sind opake Dezimalzahlen und stehen als String (kein
// Int32-Ueberlauf). Zeilenschutz nach Muster ProxmoxServer (tenantId, keine
// Relation zu Tenant).
enum AutodnsEnvironment {
DEMO
LIVE
}
enum DomainOrderStatus {
DRAFT
SUBMITTING
SUBMITTED
SUCCESS
FAILED
UNKNOWN
CANCELED
}
// Einstellungen je Organisation (Singleton). Getrennte Zugaenge fuer das
// Demo- und das Live-System; Passwoerter AES-verschluesselt (CryptoService),
// nie an den Client zurueckgegeben.
model DomainsConfig {
id String @id @default(uuid())
tenantId String @unique
environment AutodnsEnvironment @default(DEMO)
demoUser String?
demoEncryptedPassword String?
demoContext Int?
liveUser String?
liveEncryptedPassword String?
liveContext Int?
defaultNameServers String[] @default([])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([tenantId])
}
// Kunden, denen AutoDNS-Kontakte zugeordnet werden. Hoechstens einer ist die
// eigene Firma (Pruefung im Dienst). Kein Firmenname als Vorgabe.
model DomainsCustomer {
id String @id @default(uuid())
tenantId String
name String
isOwnCompany Boolean @default(false)
assignments DomainsContactAssignment[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([tenantId, name])
@@index([tenantId])
}
// Zuordnung AutoDNS-Kontakt -> Kunde. Die Umgebung ist Teil des Schluessels,
// weil Kontakt-Ids von Demo- und Live-System zwangslaeufig kollidieren.
model DomainsContactAssignment {
id String @id @default(uuid())
tenantId String
environment AutodnsEnvironment
autodnsContactId String
customerId String
customer DomainsCustomer @relation(fields: [customerId], references: [id], onDelete: Restrict)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([tenantId, environment, autodnsContactId])
@@index([tenantId])
@@index([customerId])
}
// Domain-Bestellungen (Geldsicherheit). `openKey` traegt den Domainnamen,
// solange die Bestellung offen ist (DRAFT, SUBMITTING, SUBMITTED, UNKNOWN,
// SUCCESS), und ist bei FAILED/CANCELED null; die Unique-Regel darauf
// verhindert zwei offene Bestellungen derselben Domain (NULLs wiederholen
// sich in Postgres).
model DomainsOrder {
id String @id @default(uuid())
tenantId String
environment AutodnsEnvironment
domainName String
openKey String?
status DomainOrderStatus @default(DRAFT)
payload Json
jobId String?
jobStatus String?
errorText String?
createdByUserId String
confirmedByUserId String?
confirmedByUsername String?
confirmedAt DateTime?
lastCheckedAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@unique([tenantId, environment, openKey])
@@index([tenantId])
}
// Eigene Module (quick-260929-9wc): vom Administrator angelegte Seitenleisten-
// Eintraege, die eine externe https-Seite im Rahmen zeigen. Sichtbar fuer alle
// Benutzer des Mandanten. Zeilenschutz nach Muster ProxmoxServer (tenantId,