feat(notificaciones): edit summary recipients in the UI
NOTIFICATION_ADMIN_EMAILS made "add Beto to the summaries" a redeploy — the wrong unit of work for a list that changes when office staff change. Adds `app_settings`, a key/value table for the configuration staff must be able to change without a deploy, and `SettingsService`, which resolves every key db -> env -> default and reports which of the three a value came from. That ladder is what makes the move safe: a deployment behaves exactly as before until somebody saves in the UI, and the screen can say "this is still coming from the deployment" rather than implying somebody chose it. - new ability `setting:manage` (ADMIN) — deliberately above `notification:send`, since redirecting the audit summaries is how someone would quietly stop them being read - GET/PUT /notifications/settings/admin-emails; read is open to any logged-in user so the UI can display the list, write is gated - resolved per job, not cached at boot, or we would reintroduce exactly the restart-to-apply behaviour being removed - a saved empty list means "nobody" and does NOT fall through to the env, or clearing the field would keep mailing the people just removed Credentials stay in env — see the model doc for where the line is drawn. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -131,9 +131,14 @@ SES_SECRET_KEY=...
|
||||
SES_FROM=mail@jorgecuadros.com
|
||||
SES_FROM_NAME=Information Server
|
||||
SES_CONFIGURATION_SET=... # optional
|
||||
NOTIFICATION_ADMIN_EMAILS=rmancinas@freakma.net,mpulido@freakma.net
|
||||
NOTIFICATION_ADMIN_EMAILS=rmancinas@freakma.net,mpulido@freakma.net # fallback only
|
||||
```
|
||||
|
||||
`NOTIFICATION_ADMIN_EMAILS` is no longer the source of truth. The summary
|
||||
recipients are edited in the UI and stored in `app_settings`; the env var
|
||||
is the fallback for a deployment where nobody has saved them yet. See
|
||||
"Operator settings" below.
|
||||
|
||||
Without SES_* the API still boots and `MailService` falls back to stdout
|
||||
in dev (`NODE_ENV !== "production"`). In production every send throws
|
||||
`ServiceUnavailableException` and the row is recorded as `FAILED`.
|
||||
@@ -164,6 +169,25 @@ Both render the same `NotificationLogPanel` ("Registro de envíos"), which
|
||||
filters by servicio and by view (todos / enviados / fallidos / omitidos).
|
||||
STAFF users see the Servicios log read-only.
|
||||
|
||||
## Operator settings
|
||||
|
||||
`app_settings` holds the configuration staff change without a redeploy.
|
||||
`SettingsService` resolves every key **db → env → default**, and reports
|
||||
which of the three a value came from so the UI can say so. Adding a key
|
||||
means adding a typed accessor there, not a generic getter.
|
||||
|
||||
Currently one key: `notification.adminEmails` (summary recipients), edited
|
||||
on the Servicios tab, gated on `setting:manage` (ADMIN — above
|
||||
`notification:send`, because redirecting the audit summaries is how someone
|
||||
would stop them being read). Read on every job rather than cached, so an
|
||||
edit takes effect on the next sweep with no restart. An empty saved list
|
||||
means "nobody" and deliberately does **not** fall through to the env.
|
||||
|
||||
Credentials do not belong here. SES keys, `DATABASE_URL` and S3 config stay
|
||||
in the environment: they are deployment identity, they must exist before
|
||||
the app can reach its own database, and a table only widens who can read
|
||||
them.
|
||||
|
||||
## Cron (future)
|
||||
|
||||
The four service methods (`runOutstandingPayments`, `runPaymentConfirmation`,
|
||||
|
||||
Reference in New Issue
Block a user