feat(260929-if2): Erinnerungen anlegen und zur Faelligkeit benachrichtigen (Tracer)
- Reminder-Tabelle mit Zeilenschutz (Mandant+Benutzer, Systemlesen fuer den E-Mail-Planer), API reminders (Liste, Anlegen) - Kachel "Erinnerungen", globaler Melder im Portalrahmen (Browser und Desktop, je Faelligkeit einmal) - Desktop: Laufzeit-Berechtigung fuer Benachrichtigungen nur fuer die gespeicherte Server-Adresse - Zugriffsklassifikation nachgemessen Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
-- 260929-if2 — Erinnerungen: persoenliche, einmalige Erinnerungen je Benutzer.
|
||||
--
|
||||
-- Zweck: die Tabelle "Reminder" traegt die Erinnerungen des Dashboard-Widgets
|
||||
-- „Erinnerungen“ (Titel, Beschreibung, Faelligkeit, optional E-Mail). Es gibt
|
||||
-- keine Wiederholung (D-01) und keine Historie: „Erledigt“ loescht die Zeile.
|
||||
--
|
||||
-- Besitz: eine Erinnerung gehoert genau einem Benutzer (gleicher Mandant UND
|
||||
-- gleicher Benutzer, D-05). Faellt der Benutzer weg, fallen seine Erinnerungen
|
||||
-- mit (ON DELETE CASCADE). Die Anwendung antwortet fuer fremde Kennungen mit
|
||||
-- 404 (nie 403).
|
||||
--
|
||||
-- Spuren des E-Mail-Planers: "emailSentAt" ist der ANSPRUCH auf den Versand
|
||||
-- (wird vor dem Senden gesetzt, damit mehrere API-Instanzen nicht doppelt
|
||||
-- senden), "emailAttempts" zaehlt die Versuche (hoechstens 3). Ein Verschieben
|
||||
-- der Faelligkeit setzt beide zurueck.
|
||||
--
|
||||
-- Zeilenschutz, zwei Regeln:
|
||||
-- tenant_isolation_policy — Mandant UND Benutzer (Form aus DashboardImage,
|
||||
-- 20260921120000_dashboard_image): ohne gesetzten Benutzer (Hintergrund-
|
||||
-- dienst, der je Mandant gebunden schreibt) gilt nur der Mandant, mit
|
||||
-- Benutzer zusaetzlich "userId".
|
||||
-- system_read_policy — NUR FOR SELECT, Form aus 20260914120000_rls_system_
|
||||
-- context_read. Sie bedient allein die Kandidatenabfrage des E-Mail-
|
||||
-- Planers (reminder-mail.scheduler.ts), der einmal ueber ALLE Mandanten
|
||||
-- liest und dann je Zeile gebunden anspricht. Schreiben bleibt der
|
||||
-- Mandantenregel vorbehalten.
|
||||
--
|
||||
-- Rechte fuer die Anwendungsrolle tessera_app: kommen ueber ALTER DEFAULT
|
||||
-- PRIVILEGES aus 20260909130000_rls_app_role automatisch — hier nichts zu tun.
|
||||
--
|
||||
-- WICHTIG: wie alle RLS-Regeln dieses Schemas wirken diese erst, wenn die
|
||||
-- Anwendung als Rolle ohne Umgehungsrecht verbindet (Schalter heute AUS, siehe
|
||||
-- docs/mandantentrennung-datenbankrolle.md). Bis dahin tragen die
|
||||
-- Anwendungspruefungen im Dienst den Schutz allein.
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Reminder" (
|
||||
"id" TEXT NOT NULL,
|
||||
"tenantId" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"description" TEXT NOT NULL DEFAULT '',
|
||||
"dueAt" TIMESTAMP(3) NOT NULL,
|
||||
"emailEnabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"emailSentAt" TIMESTAMP(3),
|
||||
"emailAttempts" INTEGER NOT NULL DEFAULT 0,
|
||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Reminder_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Reminder_tenantId_userId_dueAt_idx" ON "Reminder"("tenantId", "userId", "dueAt");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "Reminder_dueAt_idx" ON "Reminder"("dueAt");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Reminder" ADD CONSTRAINT "Reminder_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- Zeilenschutz: Mandant UND Benutzer (Muster 20260921120000)
|
||||
ALTER TABLE "Reminder" ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE "Reminder" FORCE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_policy ON "Reminder"
|
||||
USING (
|
||||
"tenantId" = current_tenant_id()
|
||||
AND (current_user_id() IS NULL OR "userId" = current_user_id())
|
||||
);
|
||||
|
||||
-- Systemkontext: nur Lesen, fuer die Kandidatenabfrage des E-Mail-Planers
|
||||
CREATE POLICY system_read_policy ON "Reminder"
|
||||
FOR SELECT USING (is_system_context());
|
||||
Reference in New Issue
Block a user