-- Editierbare E-Mail-Vorlagen, zweistufig wie der Werkzeugkatalog: -- account_id NULL = plattformweiter Standard (nur vom Betreiber -- editierbar, z. B. künftige E-Mails, die die Plattform selbst an -- Mandanten-Admins schickt), account_id gesetzt = mandantenspezifische -- Übersteuerung (vom Mandanten-Admin editierbar, z. B. eigener Ton/ -- Branding für eine E-Mail, die an die eigenen Mitarbeiter geht). -- "typ" identifiziert, welche vom System versendete E-Mail gemeint ist -- (aktuell nur "passwort_zuruecksetzen" — die einzige E-Mail, die das -- System bisher verschickt, siehe internal/mail). Text darf Platzhalter -- wie "{{link}}" enthalten, die beim Versand ersetzt werden (siehe -- internal/web/email_vorlage_handlers.go). CREATE TABLE email_vorlage ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), account_id UUID REFERENCES account(id), typ TEXT NOT NULL, betreff TEXT NOT NULL, text TEXT NOT NULL, updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- ACHTUNG: ein einfaches UNIQUE (account_id, typ) würde NICHT reichen — -- SQL behandelt NULL nie als gleich zu NULL, ein normaler UNIQUE- -- Constraint hätte also beliebig viele Plattform-Standard-Zeilen -- (account_id IS NULL) je typ zugelassen. Zwei partielle Unique-Indizes -- statt eines gemeinsamen Constraints, dafür braucht UpsertEmailVorlage -- zwei unterschiedliche ON-CONFLICT-Ziele (siehe internal/store/email_vorlage.go). CREATE UNIQUE INDEX email_vorlage_plattform_uidx ON email_vorlage (typ) WHERE account_id IS NULL; CREATE UNIQUE INDEX email_vorlage_mandant_uidx ON email_vorlage (account_id, typ) WHERE account_id IS NOT NULL; -- Plattformweiter Standard für die einzige aktuell existierende -- E-Mail — ohne diese Zeile gäbe es nichts, worauf ResolveEmailVorlage -- zurückfallen könnte, solange ein Mandant keine eigene Vorlage hat. INSERT INTO email_vorlage (account_id, typ, betreff, text) VALUES ( NULL, 'passwort_zuruecksetzen', 'Deklarix — Passwort zurücksetzen', 'Hallo,' || E'\n\n' || 'über diesen Link kannst du dein Deklarix-Passwort zurücksetzen (gültig 1 Stunde):' || E'\n' || '{{link}}' || E'\n\n' || 'Falls du das nicht angefordert hast, ignoriere diese E-Mail.' ); -- Wie werkzeug (account_id NULLABLE): NULL-Zeilen sind für alle lesbar, -- aber nur vom Betreiber schreibbar; ein Mandant darf nur seine eigene -- account_id-Zeile anlegen/ändern. CREATE POLICY tenant_isolation ON email_vorlage FOR ALL USING ( account_id IS NULL OR account_id = NULLIF(current_setting('app.account_id', true), '')::uuid OR current_setting('app.is_betreiber', true) = 'true' ) WITH CHECK ( (account_id IS NULL AND current_setting('app.is_betreiber', true) = 'true') OR account_id = NULLIF(current_setting('app.account_id', true), '')::uuid ); ALTER TABLE email_vorlage ENABLE ROW LEVEL SECURITY; ALTER TABLE email_vorlage FORCE ROW LEVEL SECURITY;