-- Admin Email-Templates editor (t-paliad-072). -- -- Two tables backing the /admin/email-templates UI: -- * paliad.email_templates — exactly one active row per (key, lang) pair. -- Absence of a row means "use the embedded default shipped with the -- binary". Saves UPSERT into this table; resets DELETE the row. -- * paliad.email_template_versions — append-only history. One row per -- save (and per reset / restore). The service GCs to the most recent -- 20 rows per (key, lang) inside the same tx as the save, so this table -- stays at most 3 templates × 2 languages × 20 = 120 rows steady-state. -- -- RLS enabled with no policies: same shape as paliad.invitations and -- paliad.reminder_log. The Go server bypasses RLS via its direct DB pool; -- this denies all PostgREST access (no PostgREST surface today, but kept -- closed by default). -- -- key is one of: 'invitation', 'deadline_digest', 'base'. Not enforced via -- CHECK because the canonical key set lives in code (internal/services/ -- email_template_service.go) and changing it shouldn't require a migration. CREATE TABLE paliad.email_templates ( key text NOT NULL, lang text NOT NULL CHECK (lang IN ('de', 'en')), subject text NOT NULL DEFAULT '', body text NOT NULL, updated_at timestamptz NOT NULL DEFAULT now(), updated_by uuid REFERENCES auth.users(id) ON DELETE SET NULL, PRIMARY KEY (key, lang) ); CREATE TABLE paliad.email_template_versions ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), key text NOT NULL, lang text NOT NULL CHECK (lang IN ('de', 'en')), subject text NOT NULL DEFAULT '', body text NOT NULL, saved_at timestamptz NOT NULL DEFAULT now(), saved_by uuid REFERENCES auth.users(id) ON DELETE SET NULL, note text NOT NULL DEFAULT '' ); CREATE INDEX email_template_versions_key_lang_idx ON paliad.email_template_versions (key, lang, saved_at DESC); ALTER TABLE paliad.email_templates ENABLE ROW LEVEL SECURITY; ALTER TABLE paliad.email_template_versions ENABLE ROW LEVEL SECURITY;