feat(settings): DB-backed SMTP email config with live mailer swap (PMS-638) #433
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/PMS-638-email-db-settings"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
First slice of PMS-636 (move config from env vars into DB-backed admin settings): email/SMTP.
What
SMTP mailer config becomes editable at runtime through a DB-backed admin setting, resolved at boot and hot-swapped on change. Env stays the fallback.
MailerConfig::resolve(new,src/modules/settings/email.rs): env baseline (from_env) overridden per field by a single system-tenant (Uuid::from_u128(1)) row undertenant_settingscategoryemail. No DB row => identical to today's env-only behaviour.smtp_passwordis stored AES-256-GCM-encrypted (reusingcrypto::encrypt+ENCRYPTION_KEY, the same pattern aspayment_gateway_configs); decrypted only insideresolve, never returned by the read API.SharedMailer(new,src/utils/email.rs): a live-swappableMailerdecorator (RwLock<Arc<dyn Mailer>>). Built once at boot and distributed to every consumer (AppState, notifications worker, tickets automation, contacts service) unchanged; the admin handler swaps the inner in place, so a config change takes effect without a restart and without touching any consumer signature.RequireAdmin):GET /api/v1/settings/email(password masked,password_set: bool) andPUT /api/v1/settings/email(validates port / from-mailbox / tls, encrypts the password, upserts, then rebuilds + swaps the live mailer).main.rsresolves from DB-then-env at boot and wraps the result inSharedMailer..env.example: documents thatSMTP_*is now the fallback overridden by the DB config.Scope (per PMS-636 scoping)
Decisions taken with the issue owner: system-wide email config (default tenant), not per-tenant; app-wide mailer rebuilt in place on change;
OAUTH_SUPER_ADMIN_EMAILS+ bootstrapADMIN_*deliberately KEPT as env (they gate who becomes super_admin / first-run; an env var set at deploy is not admin-editable). During scoping the codebase showed the Stripe integration is already DB-backed + encrypted (payment_gateway_configs) and the OCI registry config named in the issue does not exist in mokosh-server, so those are not part of this slice.Access / RLS
tenant_settingsis fail-closed RLS (migration 038), so all reads/writes go throughbegin_with_tenant(SYSTEM_TENANT), matchingSettingsService. Boot resolution tolerates an absent system-tenant row (returns the env config).Tests
Unit (DB-free, run in
cargo test --lib):SharedMailerswap redirects sends to the new inner;SmtpTlsround-trips throughparse/as_str; password encrypt/mask (ciphertext != cleartext, decrypt recovers it, view masks it); stored JSON omits absent fields soresolvetreats them as env fallbacks. The DB-backedresolve/putprecedence + endpoint behaviour are integration-level (follow-up:tests/*.rs).#PMS-638