diff --git a/.env.production.example b/.env.production.example index d53e7acd1..258db482e 100644 --- a/.env.production.example +++ b/.env.production.example @@ -84,6 +84,24 @@ API_TOKEN_HMAC_KEY="" # APNS_KEY_FILE="" +# ----------------------------------------------------------------------------- +# SMTP -- email notification channel (optional, all-or-none) +# ----------------------------------------------------------------------------- +# Operator SMTP transport for the email channel. SMTP_HOST + SMTP_PORT + +# SMTP_FROM together enable it; SMTP_USER/SMTP_PASS are optional (omit for an +# unauthenticated relay). SMTP_SECURE=true uses implicit TLS (port 465); +# unset/false uses STARTTLS (port 587). Per-user recipient address is set in +# Settings -> Notifications, not here. Missing the core trio disables the +# channel silently. Listed in docker-compose.yml's environment whitelist so +# these reach the container. +# SMTP_HOST="" +# SMTP_PORT="587" +# SMTP_FROM="HealthLog " +# SMTP_USER="" +# SMTP_PASS="" +# SMTP_SECURE="false" + + # ----------------------------------------------------------------------------- # Deploy webhook -- Coolify -> HealthLog auto-deploy feedback # ----------------------------------------------------------------------------- diff --git a/docker-compose.yml b/docker-compose.yml index 57e84f8b5..1c6b5798a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -107,6 +107,18 @@ services: # container (the `environment:` block is a whitelist). See # docs/ops/tls-cert-pin.md. TLS_LEAF_SPKI_PINS: "${TLS_LEAF_SPKI_PINS:-}" + # v1.17.1 — SMTP transport for the email notification channel (optional, + # all-or-none: HOST + PORT + FROM enable it; USER/PASS optional; SECURE + # toggles implicit TLS). Listed here so operator values in .env reach the + # container (the `environment:` block is a whitelist — vars not listed + # never propagate from .env, even with `${VAR}` substitution at + # compose-up). Per-user recipient is stored in the DB, not here. + SMTP_HOST: "${SMTP_HOST:-}" + SMTP_PORT: "${SMTP_PORT:-}" + SMTP_FROM: "${SMTP_FROM:-}" + SMTP_USER: "${SMTP_USER:-}" + SMTP_PASS: "${SMTP_PASS:-}" + SMTP_SECURE: "${SMTP_SECURE:-}" depends_on: db: condition: service_healthy diff --git a/messages/de.json b/messages/de.json index 504fbedc1..57f127fae 100644 --- a/messages/de.json +++ b/messages/de.json @@ -4133,6 +4133,17 @@ "ntfyTopic": "Topic", "ntfyAuthToken": "Auth-Token (optional)", "ntfyAuthTokenHint": "Nur nötig bei privaten Topics mit Zugangsschutz.", + "webhook": "Webhook", + "webhookDescription": "Benachrichtigungen per POST an eine beliebige URL — Gotify, Discord, Slack, Matrix oder Home Assistant.", + "webhookEnable": "Webhook aktivieren", + "webhookUrl": "Webhook-URL", + "webhookUrlHint": "Die vollständige URL für den POST. Muss öffentlich erreichbar sein.", + "webhookHeaderName": "Header-Name (optional)", + "webhookHeaderValue": "Header-Wert (optional)", + "email": "E-Mail", + "emailDescription": "Warnungen und Erinnerungen per E-Mail empfangen.", + "emailEnable": "E-Mail aktivieren", + "emailRecipient": "Empfängeradresse", "webPush": "Browser-Push", "webPushDescription": "Erhalte Benachrichtigungen direkt im Browser, auch wenn HealthLog nicht geöffnet ist.", "webPushNotSupported": "Dein Browser unterstützt keine Push-Benachrichtigungen.", @@ -5011,6 +5022,15 @@ "authTokenRevoke": "Token widerrufen", "medicationReminders": "Medikamentenerinnerungen", "medicationRemindersDescription": "Schwellenwerte für die Farbübergänge der Erinnerungs-Badges auf den Medikamentenkarten.", + "notificationHealth": { + "title": "Zustellungsstatus", + "description": "Zustellungsergebnisse aller Nutzer der letzten 24 Stunden.", + "empty": "In diesem Zeitraum wurden keine Benachrichtigungen gesendet.", + "ok": "OK", + "error": "Fehlgeschlagen", + "skipped": "Übersprungen", + "autoDisabled": "Automatisch deaktivierte Kanäle" + }, "reminderLateMinutes": "Spät-Schwellenwert (Minuten)", "reminderLateMinutesDescription": "Minuten nach Fenster-Ende bis der Badge von Gelb auf Orange wechselt.", "reminderMissedMinutes": "Verpasst-Schwellenwert (Minuten)", diff --git a/messages/en.json b/messages/en.json index 8477957ff..cc3e5d04d 100644 --- a/messages/en.json +++ b/messages/en.json @@ -4133,6 +4133,17 @@ "ntfyTopic": "Topic", "ntfyAuthToken": "Auth Token (optional)", "ntfyAuthTokenHint": "Only needed for private topics with access control.", + "webhook": "Webhook", + "webhookDescription": "POST notifications to any URL — Gotify, Discord, Slack, Matrix, or Home Assistant.", + "webhookEnable": "Enable webhook", + "webhookUrl": "Webhook URL", + "webhookUrlHint": "The full URL to POST to. Must be publicly reachable.", + "webhookHeaderName": "Header name (optional)", + "webhookHeaderValue": "Header value (optional)", + "email": "Email", + "emailDescription": "Receive alerts and reminders by email.", + "emailEnable": "Enable email", + "emailRecipient": "Recipient address", "webPush": "Browser Push", "webPushDescription": "Receive notifications directly in your browser, even when HealthLog is not open.", "webPushNotSupported": "Your browser does not support push notifications.", @@ -5011,6 +5022,15 @@ "authTokenRevoke": "Token revoked", "medicationReminders": "Medication Reminders", "medicationRemindersDescription": "Thresholds for reminder badge color transitions on medication cards.", + "notificationHealth": { + "title": "Notification health", + "description": "Delivery results across all users over the last 24 hours.", + "empty": "No notifications sent in this window.", + "ok": "OK", + "error": "Failed", + "skipped": "Skipped", + "autoDisabled": "Auto-disabled channels" + }, "reminderLateMinutes": "Late threshold (minutes)", "reminderLateMinutesDescription": "Minutes after window end until the badge changes from yellow to orange.", "reminderMissedMinutes": "Missed threshold (minutes)", diff --git a/messages/es.json b/messages/es.json index 4ac12939a..aa0316c06 100644 --- a/messages/es.json +++ b/messages/es.json @@ -4133,6 +4133,17 @@ "ntfyTopic": "Topic", "ntfyAuthToken": "Token de autenticación (opcional)", "ntfyAuthTokenHint": "Solo necesario para temas privados con control de acceso.", + "webhook": "Webhook", + "webhookDescription": "Envía notificaciones por POST a cualquier URL: Gotify, Discord, Slack, Matrix o Home Assistant.", + "webhookEnable": "Activar webhook", + "webhookUrl": "URL del webhook", + "webhookUrlHint": "La URL completa para el POST. Debe ser accesible públicamente.", + "webhookHeaderName": "Nombre de cabecera (opcional)", + "webhookHeaderValue": "Valor de cabecera (opcional)", + "email": "Correo electrónico", + "emailDescription": "Recibe alertas y recordatorios por correo electrónico.", + "emailEnable": "Activar correo electrónico", + "emailRecipient": "Dirección del destinatario", "webPush": "Push del navegador", "webPushDescription": "Recibe notificaciones directamente en tu navegador, incluso cuando HealthLog no está abierto.", "webPushNotSupported": "Tu navegador no admite notificaciones push.", @@ -5011,6 +5022,15 @@ "authTokenRevoke": "Token revocado", "medicationReminders": "Recordatorios de medicación", "medicationRemindersDescription": "Umbrales para los cambios de color de las insignias de recordatorio en las tarjetas de medicación.", + "notificationHealth": { + "title": "Estado de entrega", + "description": "Resultados de entrega de todos los usuarios en las últimas 24 horas.", + "empty": "No se enviaron notificaciones en este periodo.", + "ok": "OK", + "error": "Fallidas", + "skipped": "Omitidas", + "autoDisabled": "Canales desactivados automáticamente" + }, "reminderLateMinutes": "Umbral de retraso (minutos)", "reminderLateMinutesDescription": "Minutos tras el fin de la ventana hasta que la insignia cambia de amarillo a naranja.", "reminderMissedMinutes": "Umbral de omisión (minutos)", diff --git a/messages/fr.json b/messages/fr.json index a5d6fc975..9182df186 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -4133,6 +4133,17 @@ "ntfyTopic": "Topic", "ntfyAuthToken": "Jeton d’authentification (facultatif)", "ntfyAuthTokenHint": "Nécessaire uniquement pour les sujets privés avec contrôle d’accès.", + "webhook": "Webhook", + "webhookDescription": "Envoyer les notifications par POST vers n’importe quelle URL — Gotify, Discord, Slack, Matrix ou Home Assistant.", + "webhookEnable": "Activer le webhook", + "webhookUrl": "URL du webhook", + "webhookUrlHint": "L’URL complète pour le POST. Doit être accessible publiquement.", + "webhookHeaderName": "Nom d’en-tête (facultatif)", + "webhookHeaderValue": "Valeur d’en-tête (facultatif)", + "email": "E-mail", + "emailDescription": "Recevoir les alertes et les rappels par e-mail.", + "emailEnable": "Activer l’e-mail", + "emailRecipient": "Adresse du destinataire", "webPush": "Push navigateur", "webPushDescription": "Recevez des notifications directement dans votre navigateur, même lorsque HealthLog n’est pas ouvert.", "webPushNotSupported": "Votre navigateur ne prend pas en charge les notifications push.", @@ -5011,6 +5022,15 @@ "authTokenRevoke": "Jeton révoqué", "medicationReminders": "Rappels de médicaments", "medicationRemindersDescription": "Seuils des transitions de couleur des badges de rappel sur les fiches de médicaments.", + "notificationHealth": { + "title": "État de distribution", + "description": "Résultats de distribution de tous les utilisateurs sur les dernières 24 heures.", + "empty": "Aucune notification envoyée sur cette période.", + "ok": "OK", + "error": "Échecs", + "skipped": "Ignorées", + "autoDisabled": "Canaux désactivés automatiquement" + }, "reminderLateMinutes": "Seuil de retard (minutes)", "reminderLateMinutesDescription": "Minutes après la fin de la fenêtre avant que le badge passe du jaune à l'orange.", "reminderMissedMinutes": "Seuil d'oubli (minutes)", diff --git a/messages/it.json b/messages/it.json index 15cc4ed2e..7e3d685cb 100644 --- a/messages/it.json +++ b/messages/it.json @@ -4133,6 +4133,17 @@ "ntfyTopic": "Topic", "ntfyAuthToken": "Token di autenticazione (facoltativo)", "ntfyAuthTokenHint": "Necessario solo per topic privati con controllo degli accessi.", + "webhook": "Webhook", + "webhookDescription": "Invia le notifiche via POST a qualsiasi URL — Gotify, Discord, Slack, Matrix o Home Assistant.", + "webhookEnable": "Attiva webhook", + "webhookUrl": "URL del webhook", + "webhookUrlHint": "L’URL completo per il POST. Deve essere raggiungibile pubblicamente.", + "webhookHeaderName": "Nome intestazione (opzionale)", + "webhookHeaderValue": "Valore intestazione (opzionale)", + "email": "Email", + "emailDescription": "Ricevi avvisi e promemoria via email.", + "emailEnable": "Attiva email", + "emailRecipient": "Indirizzo del destinatario", "webPush": "Push del browser", "webPushDescription": "Ricevi notifiche direttamente nel browser, anche quando HealthLog non è aperto.", "webPushNotSupported": "Il tuo browser non supporta le notifiche push.", @@ -5011,6 +5022,15 @@ "authTokenRevoke": "Token revocato", "medicationReminders": "Promemoria dei farmaci", "medicationRemindersDescription": "Soglie per i cambi di colore dei badge di promemoria sulle schede dei farmaci.", + "notificationHealth": { + "title": "Stato di consegna", + "description": "Risultati di consegna di tutti gli utenti nelle ultime 24 ore.", + "empty": "Nessuna notifica inviata in questo periodo.", + "ok": "OK", + "error": "Non riuscite", + "skipped": "Saltate", + "autoDisabled": "Canali disattivati automaticamente" + }, "reminderLateMinutes": "Soglia di ritardo (minuti)", "reminderLateMinutesDescription": "Minuti dopo la fine della finestra prima che il badge passi da giallo ad arancione.", "reminderMissedMinutes": "Soglia di mancata assunzione (minuti)", diff --git a/messages/pl.json b/messages/pl.json index 5b865a424..72e762043 100644 --- a/messages/pl.json +++ b/messages/pl.json @@ -4133,6 +4133,17 @@ "ntfyTopic": "Topic", "ntfyAuthToken": "Token uwierzytelniania (opcjonalny)", "ntfyAuthTokenHint": "Wymagany tylko dla prywatnych tematów z kontrolą dostępu.", + "webhook": "Webhook", + "webhookDescription": "Wysyłaj powiadomienia metodą POST na dowolny URL — Gotify, Discord, Slack, Matrix lub Home Assistant.", + "webhookEnable": "Włącz webhook", + "webhookUrl": "Adres URL webhooka", + "webhookUrlHint": "Pełny adres URL dla żądania POST. Musi być publicznie dostępny.", + "webhookHeaderName": "Nazwa nagłówka (opcjonalnie)", + "webhookHeaderValue": "Wartość nagłówka (opcjonalnie)", + "email": "E-mail", + "emailDescription": "Otrzymuj alerty i przypomnienia e-mailem.", + "emailEnable": "Włącz e-mail", + "emailRecipient": "Adres odbiorcy", "webPush": "Push przeglądarki", "webPushDescription": "Otrzymuj powiadomienia bezpośrednio w przeglądarce, nawet gdy HealthLog nie jest otwarty.", "webPushNotSupported": "Twoja przeglądarka nie obsługuje powiadomień push.", @@ -5011,6 +5022,15 @@ "authTokenRevoke": "Token unieważniony", "medicationReminders": "Przypomnienia o lekach", "medicationRemindersDescription": "Progi zmian koloru oznaczeń przypomnień na kartach leków.", + "notificationHealth": { + "title": "Stan dostarczania", + "description": "Wyniki dostarczania wszystkich użytkowników z ostatnich 24 godzin.", + "empty": "W tym okresie nie wysłano żadnych powiadomień.", + "ok": "OK", + "error": "Nieudane", + "skipped": "Pominięte", + "autoDisabled": "Automatycznie wyłączone kanały" + }, "reminderLateMinutes": "Próg opóźnienia (minuty)", "reminderLateMinutesDescription": "Minuty po końcu okna, zanim oznaczenie zmieni kolor z żółtego na pomarańczowy.", "reminderMissedMinutes": "Próg pominięcia (minuty)", diff --git a/scripts/env-manifest.json b/scripts/env-manifest.json index 6b3d72f90..51f68af58 100644 --- a/scripts/env-manifest.json +++ b/scripts/env-manifest.json @@ -137,6 +137,26 @@ } ] }, + { + "name": "SMTP (email channel)", + "description": "Operator SMTP transport for the email notification channel (v1.17.1). All of SMTP_HOST, SMTP_PORT, SMTP_FROM together enable the channel; SMTP_USER/SMTP_PASS are optional (unauthenticated relay). Missing any of the three core vars disables email silently — the channel is never offered and the Settings card hides itself.", + "required": false, + "allOrNone": true, + "variables": [ + { + "name": "SMTP_HOST", + "purpose": "SMTP server hostname, e.g. smtp.example.com." + }, + { + "name": "SMTP_PORT", + "purpose": "SMTP port. 587 for STARTTLS (SMTP_SECURE unset/false), 465 for implicit TLS (SMTP_SECURE=true)." + }, + { + "name": "SMTP_FROM", + "purpose": "From address for outbound mail, e.g. \"HealthLog \"." + } + ] + }, { "name": "Off-host backups", "description": "Required ALL-OR-NONE: any of these without the rest disables backups silently.",