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:
@@ -0,0 +1,165 @@
|
||||
-- 261008-dts — Modul "Domains" (AutoDNS / InterNetX Domainrobot).
|
||||
--
|
||||
-- Zweck: vier neue Tabellen und zwei Aufzaehlungen.
|
||||
-- * "DomainsConfig": Einstellungen der Organisation (Singleton), getrennte
|
||||
-- Zugaenge fuer das Demo- und das Live-System, Passwoerter AES-verschluesselt
|
||||
-- (CryptoService), Standard-Nameserver.
|
||||
-- * "DomainsCustomer": Kunden, denen AutoDNS-Kontakte zugeordnet werden;
|
||||
-- eine Zeile darf als "Eigene Firma" markiert sein (Pruefung im Dienst).
|
||||
-- * "DomainsContactAssignment": Zuordnung AutoDNS-Kontakt -> Kunde. Die
|
||||
-- Umgebung (DEMO/LIVE) gehoert zum Schluessel, weil Kontakt-Ids beider
|
||||
-- Systeme kollidieren. Kontakte und Domains selbst werden NICHT gespiegelt,
|
||||
-- AutoDNS bleibt die Quelle der Wahrheit.
|
||||
-- * "DomainsOrder": 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 (tenantId, environment, openKey) verhindert zwei offene
|
||||
-- Bestellungen derselben Domain; NULLs wiederholen sich in Postgres
|
||||
-- beliebig. SUCCESS behaelt den Schluessel mit Absicht: kurz nach der
|
||||
-- Registrierung kann ein verzoegerter WHOIS noch "frei" melden.
|
||||
--
|
||||
-- AutoDNS-Kontakt- und Job-Ids stehen als TEXT (opake Dezimalzahlen).
|
||||
--
|
||||
-- Von Hand geschrieben (Vorbild 20261002130000_handelsware_datev); der
|
||||
-- DDL-Teil stammt aus `prisma migrate diff`, damit der Stand ohne Abweichung
|
||||
-- zum Schema passt.
|
||||
--
|
||||
-- Zeilenschutz (Pflicht — sonst schlaegt rls-coverage.spec.ts fehl): alle
|
||||
-- Tabellen tragen `tenantId` und `tenant_isolation_policy` OHNE
|
||||
-- Benutzerdimension (`USING ("tenantId" = current_tenant_id())`) — das sind
|
||||
-- Verwaltungsdaten der Organisation, keine persoenlichen Daten eines
|
||||
-- Benutzers. Keine `system_read_policy`: es gibt keinen Hintergrunddienst, der
|
||||
-- diese Tabellen ueber alle Organisationen liest (Auftragsstatus wird beim
|
||||
-- Oeffnen der Seite abgefragt, nicht per Zeitplan).
|
||||
--
|
||||
-- Rechte fuer die Anwendungsrolle tessera_app kommen automatisch ueber
|
||||
-- ALTER DEFAULT PRIVILEGES aus 20260909130000_rls_app_role — hier nichts zu
|
||||
-- tun.
|
||||
--
|
||||
-- WICHTIG: wie alle bisherigen RLS-Migrationen wirken diese Regeln erst,
|
||||
-- wenn die Anwendung als Rolle ohne Umgehungsrecht verbindet (Schalter
|
||||
-- heute AUS, siehe docs/mandantentrennung-datenbankrolle.md).
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "AutodnsEnvironment" AS ENUM ('DEMO', 'LIVE');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "DomainOrderStatus" AS ENUM ('DRAFT', 'SUBMITTING', 'SUBMITTED', 'SUCCESS', 'FAILED', 'UNKNOWN', 'CANCELED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "DomainsConfig" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"environment" "AutodnsEnvironment" NOT NULL DEFAULT 'DEMO',
|
||||
"demoUser" TEXT,
|
||||
"demoEncryptedPassword" TEXT,
|
||||
"demoContext" INTEGER,
|
||||
"liveUser" TEXT,
|
||||
"liveEncryptedPassword" TEXT,
|
||||
"liveContext" INTEGER,
|
||||
"defaultNameServers" TEXT[] DEFAULT ARRAY[]::TEXT[],
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "DomainsConfig_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "DomainsCustomer" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"isOwnCompany" BOOLEAN NOT NULL DEFAULT false,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "DomainsCustomer_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "DomainsContactAssignment" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"environment" "AutodnsEnvironment" NOT NULL,
|
||||
"autodnsContactId" TEXT NOT NULL,
|
||||
"customerId" TEXT NOT NULL,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "DomainsContactAssignment_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "DomainsOrder" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"environment" "AutodnsEnvironment" NOT NULL,
|
||||
"domainName" TEXT NOT NULL,
|
||||
"openKey" TEXT,
|
||||
"status" "DomainOrderStatus" NOT NULL DEFAULT 'DRAFT',
|
||||
"payload" JSONB NOT NULL,
|
||||
"jobId" TEXT,
|
||||
"jobStatus" TEXT,
|
||||
"errorText" TEXT,
|
||||
"createdByUserId" TEXT NOT NULL,
|
||||
"confirmedByUserId" TEXT,
|
||||
"confirmedByUsername" TEXT,
|
||||
"confirmedAt" TIMESTAMP(3),
|
||||
"lastCheckedAt" TIMESTAMP(3),
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "DomainsOrder_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "DomainsConfig_tenantId_key" ON "DomainsConfig"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DomainsConfig_tenantId_idx" ON "DomainsConfig"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DomainsCustomer_tenantId_idx" ON "DomainsCustomer"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "DomainsCustomer_tenantId_name_key" ON "DomainsCustomer"("tenantId", "name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DomainsContactAssignment_tenantId_idx" ON "DomainsContactAssignment"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DomainsContactAssignment_customerId_idx" ON "DomainsContactAssignment"("customerId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "DomainsContactAssignment_tenantId_environment_autodnsContac_key" ON "DomainsContactAssignment"("tenantId", "environment", "autodnsContactId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "DomainsOrder_tenantId_idx" ON "DomainsOrder"("tenantId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "DomainsOrder_tenantId_environment_openKey_key" ON "DomainsOrder"("tenantId", "environment", "openKey");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "DomainsContactAssignment" ADD CONSTRAINT "DomainsContactAssignment_customerId_fkey" FOREIGN KEY ("customerId") REFERENCES "DomainsCustomer"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
|
||||
-- Zeilenschutz
|
||||
ALTER TABLE "DomainsConfig" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "DomainsConfig" FORCE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_policy ON "DomainsConfig"
|
||||
USING ("tenantId" = current_tenant_id());
|
||||
|
||||
ALTER TABLE "DomainsCustomer" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "DomainsCustomer" FORCE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_policy ON "DomainsCustomer"
|
||||
USING ("tenantId" = current_tenant_id());
|
||||
|
||||
ALTER TABLE "DomainsContactAssignment" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "DomainsContactAssignment" FORCE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_policy ON "DomainsContactAssignment"
|
||||
USING ("tenantId" = current_tenant_id());
|
||||
|
||||
ALTER TABLE "DomainsOrder" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "DomainsOrder" FORCE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_policy ON "DomainsOrder"
|
||||
USING ("tenantId" = current_tenant_id());
|
||||
Reference in New Issue
Block a user