Basis: Certvia dev@a48c5fb als Fundament für Craftvia
CI / build-and-check (push) Canceled after 0s
CI / audit (push) Canceled after 0s
CI / sbom (push) Canceled after 0s

Unveränderter Stand von certvia/dev (a48c5fb) plus Craftvia-Spezifikation
und Brandbook unter docs/craftvia/. ISMS-Module werden im Folgecommit entfernt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-14 11:05:39 +02:00
co-authored by Claude Opus 5
commit c8e6f30a27
720 changed files with 140143 additions and 0 deletions
@@ -0,0 +1,84 @@
-- CreateTable
CREATE TABLE "mail_logs" (
"id" TEXT NOT NULL,
"tenant_id" TEXT,
"scope" TEXT NOT NULL DEFAULT 'tenant',
"to" TEXT NOT NULL,
"template" TEXT NOT NULL,
"locale" TEXT NOT NULL DEFAULT 'de',
"status" TEXT NOT NULL DEFAULT 'pending',
"provider_message_id" TEXT,
"error" TEXT,
"attempts" INTEGER NOT NULL DEFAULT 0,
"dedupe_key" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"sent_at" TIMESTAMP(3),
CONSTRAINT "mail_logs_pkey" PRIMARY KEY ("id")
);
-- CreateTable
CREATE TABLE "notification_preferences" (
"id" TEXT NOT NULL,
"tenant_id" TEXT NOT NULL,
"user_id" TEXT NOT NULL,
"event_type" TEXT NOT NULL,
"email" BOOLEAN NOT NULL DEFAULT true,
"locale" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "notification_preferences_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX "mail_logs_dedupe_key_key" ON "mail_logs"("dedupe_key");
-- CreateIndex
CREATE INDEX "mail_logs_tenant_id_created_at_idx" ON "mail_logs"("tenant_id", "created_at");
-- CreateIndex
CREATE INDEX "mail_logs_status_idx" ON "mail_logs"("status");
-- CreateIndex
CREATE INDEX "notification_preferences_tenant_id_idx" ON "notification_preferences"("tenant_id");
-- CreateIndex
CREATE UNIQUE INDEX "notification_preferences_user_id_event_type_key" ON "notification_preferences"("user_id", "event_type");
-- ============================================================================
-- Row Level Security (SEC1)
--
-- Muster wie seit F-04 (20260730160000_rls_enforce): ENABLE + FORCE + Policy mit
-- USING *und* WITH CHECK. Ohne WITH CHECK koennte die eingeschraenkte App-Rolle
-- `isms_app` unter FORCE keine Zeilen mehr anlegen.
--
-- mail_logs.tenant_id ist NULLABLE (Plattform-Mails ohne Mandantenbezug). Die
-- Policy ist trotzdem identisch zu audit_logs, das dieselbe Situation hat:
-- Zeilen mit tenant_id = NULL erfuellen die Bedingung nie und sind fuer `isms_app`
-- weder les- noch schreibbar. Das ist beabsichtigt — Plattform-Zeilen werden
-- ausschliesslich ueber den rohen `prisma`-Client (Owner-Rolle, BYPASSRLS)
-- geschrieben, genau wie writePlatformAudit().
--
-- GRANT ist noetig, weil die pauschalen Rechte aus 20260702095816 nur fuer die
-- damals existierenden Tabellen galten; ALTER DEFAULT PRIVILEGES greift nur fuer
-- Objekte, die von der dort gesetzten Rolle erzeugt werden.
-- ============================================================================
GRANT SELECT, INSERT, UPDATE, DELETE ON "mail_logs" TO isms_app;
GRANT SELECT, INSERT, UPDATE, DELETE ON "notification_preferences" TO isms_app;
ALTER TABLE "mail_logs" ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS tenant_isolation ON "mail_logs";
CREATE POLICY tenant_isolation ON "mail_logs"
USING ("tenant_id" = current_setting('app.tenant_id', true))
WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true));
ALTER TABLE "mail_logs" FORCE ROW LEVEL SECURITY;
ALTER TABLE "notification_preferences" ENABLE ROW LEVEL SECURITY;
DROP POLICY IF EXISTS tenant_isolation ON "notification_preferences";
CREATE POLICY tenant_isolation ON "notification_preferences"
USING ("tenant_id" = current_setting('app.tenant_id', true))
WITH CHECK ("tenant_id" = current_setting('app.tenant_id', true));
ALTER TABLE "notification_preferences" FORCE ROW LEVEL SECURITY;