Skip to content

Latest commit

 

History

History
301 lines (225 loc) · 13.7 KB

notification.md

File metadata and controls

301 lines (225 loc) · 13.7 KB

Aam Digital - Notification API

for details about the internal implementation see README in module code folder

Overview

The Notification Module is watching for events that are configured to trigger (push) notifications to users. This module watches for database changes and creates notification events which can be sent through various channels:

  • Push Notifications through Firebase
  • "In-App" Notifications directly in the toolbar of our frontend UI
  • Email Notifications through SMTP (optional, active when the SMTP host, sender address and Keycloak access are configured, see Setup)

Dependencies

Push Notifications are sent through Firebase Cloud Messaging (FCM).

API Specification

notification-api-v1.yaml

Check if feature is enabled

You can make a request to the API to check if a certain feature is currently enabled and available:

> GET /actuator/features

// response:
{
  "notification": {
    "enabled": true,
    "email": { "enabled": true }
  }
}

notification.email is only reported when notification emails can actually be sent (see Setup), so the app can offer the email option only when it works.

If the aam-services backend is not deployed at all, such a request will usually return a HTTP 504 error. You should also account for that possibility.

Usage

notifications.drawio.png

Users configure their notification rules in the frontend. A rule's conditions follow MongoDB query syntax and semantics, including negations ($not, $nor, $ne, $nin), $in, $elemMatch, comparisons, $exists, $regex and dotted paths into nested fields. Note that values are compared strictly by type: a number stored as a string in a condition (e.g. {"age": {"$gte": "18"}}) does not match a numeric field.

A condition value can be the placeholder "$current_user" (e.g. {"assignedTo": {"$elemMatch": "$current_user"}}), which stands for the entity linked to the rule owner's user account (the Keycloak user attribute exact_username). This requires the Keycloak settings below (see Setup); without them, or for a user account without a linked entity, rules using the placeholder never trigger. Changes to the linked entity take effect within a day (NOTIFICATION_CONFIGCACHE_TTLMILLIS, default 86400000), or immediately when the user's notification settings are saved.


Setup

The following environment variables are required:

FEATURES_NOTIFICATIONAPI_ENABLED=true
FEATURES_NOTIFICATIONAPI_MODE=firebase    # delivery mode for push notifications

# Email notifications (optional): setting SPRING_MAIL_HOST switches them on, there is no separate flag.
# They are sent when the sender address, the SMTP host and the Keycloak access below are all set.
# Sender address only; the display name and subject prefix are managed in the
# templates/{locale}/notification/email-branding.properties template file.
NOTIFICATION_EMAIL_FROM=notifications@your-instance.org
SPRING_MAIL_HOST=<smtp-host>
SPRING_MAIL_PORT=587
SPRING_MAIL_USERNAME=<smtp-username>
SPRING_MAIL_PASSWORD=<smtp-password>
SPRING_MAIL_PROPERTIES_MAIL_SMTP_AUTH=true
SPRING_MAIL_PROPERTIES_MAIL_SMTP_STARTTLS_ENABLE=true

# Language of the email boilerplate (en, de, fr). Defaults to en.
# Keep in sync with the site default language.
NOTIFICATION_EMAIL_LOCALE=en

# Needed for email notifications (lookup recipient addresses in Keycloak)
# and for the "$current_user" placeholder in notification rules (lookup of the user's linked entity)
KEYCLOAK_SERVERURL=https://<your-keycloak-url>
KEYCLOAK_REALM=<your-realm>
KEYCLOAK_CLIENTID=<service-client-id>
KEYCLOAK_CLIENTSECRET=<service-client-secret>

# Firebase Configuration: Confidential (!)
NOTIFICATIONFIREBASECONFIGURATION_CREDENTIALFILEBASE64=<base-64-encoded-firebase-credential-file>

APPLICATION_BASEURL=<your-instance>.aam-digital.com

The Keycloak client for the lookup is the aam-backend client (KEYCLOAK_CLIENTID); its service account needs the realm-management role view-users. The definition of that client that ships with the backend (aam-backend-client.json, see its README) grants it.

Notification links are derived centrally from APPLICATION_BASEURL:

  • Email "manage notification settings": https://<base-url>/user-account?_tabIndex=1
  • Push notification link target (FCM webpush link and url custom data)

Database change-detection is activated automatically when notification (or reporting) is enabled; no separate flag needs to be set.

Email notifications are derived from the configuration

The email channel is on when the notification API is enabled and SPRING_MAIL_HOST, NOTIFICATION_EMAIL_FROM and KEYCLOAK_SERVERURL all have a non-blank value. An empty value, as in the deployment template (SPRING_MAIL_HOST=), counts as not set. Without them the service starts normally and only sends notifications through its other channels.

KEYCLOAK_SERVERURL (with the realm and client settings next to it) is needed because a notification only identifies the recipient by user id. The email address itself is looked up in the user's Keycloak account when the email is created.

On startup the service logs which state it is in. If an SMTP host is set but the sender address or the Keycloak access is missing, it logs an error naming the missing settings, so a half-finished configuration is not mistaken for a working one.

The former flag FEATURES_NOTIFICATIONAPI_EMAIL_ENABLED is no longer read, whatever its value. If it is still set, the service logs a warning at startup; remove it. In particular, a deployment that has an SMTP server configured now sends notification emails even if the flag is false; to stop this, clear SPRING_MAIL_HOST.

Notes:

  • Email notifications are only sent for users with channels.email=true in their NotificationConfig:* document.
  • If a user has no email address in Keycloak, that notification is skipped for email delivery.
  • Ensure the server firewall allows outgoing traffic for the SMTP port.

Email delivery retries and failure handling

If sending an email fails, the service automatically retries up to 3 times before giving up. Notifications that are still owed are held in the notification-outbox CouchDB database, so no notifications are silently lost.

When the service restarts, all held notifications are automatically retried once. This means the recovery path for any delivery problem is always the same: fix the root cause, then restart the service.

Examples:

  • SMTP server temporarily unreachable → retried up to 3 times with a growing pause, then held until next restart
  • Wrong SMTP credentials → held immediately (retrying wouldn't help), fixed after updating credentials and restarting

To see what is currently held, list the outbox and look at attempts and lastError:

GET /notification-outbox/_all_docs?include_docs=true

An entry whose attempts has reached the maximum is waiting for a restart. Entries are deleted as soon as they are delivered, so an empty database means nothing is outstanding.

Multi-language Email Templates & Runtime Override

The email language is selected by NOTIFICATION_EMAIL_LOCALE (defaults to en). Templates are organized in a per-language folder ({locale}/notification/...) so a translator can copy a whole language folder (e.g. en/ → de/) and translate the bundled templates. A region suffix in the configured locale is ignored (de-DE → de).

For the configured locale, the backend resolves the template in this order and uses the first that exists:

  1. Mounted override, localized: /opt/app/templates/{locale}/notification/create-notification-email-template.html
  2. Mounted override, legacy unsuffixed (kept for backward compatibility): /opt/app/templates/notification/create-notification-email-template.html
  3. Bundled classpath, localized: src/main/resources/templates/{locale}/notification/create-notification-email-template.html
  4. Bundled classpath, English fallback: src/main/resources/templates/en/notification/create-notification-email-template.html

Bundled languages: en, de, fr.

Example volume mount and override layout:

volumes:
  - ./config/aam-backend-service/templates:/opt/app/templates:ro
config/aam-backend-service/templates/
  de/notification/create-notification-email-template.html
  de/notification/email-branding.properties
  fr/notification/create-notification-email-template.html

Important:

  • Template changes require a container restart.
  • The backend does not manage logo files or inline image attachments. Template authors can embed images directly in the HTML template (for example as data URIs or remote image URLs).

Email Branding (sender name & subject prefix)

The sender display name and the subject prefix are not environment variables. They are kept together with the email templates in {locale}/notification/email-branding.properties, so all template-related text is managed in one place:

# Display name shown in front of the sender address, e.g. "Aam Digital <notifications@example.org>".
from-name=Aam Digital
# Prefix prepended to every notification email subject, e.g. "Aam Digital: A new record was added".
subject-prefix=Aam Digital
  • from-name is combined with the configured address (NOTIFICATION_EMAIL_FROM, which holds the bare address only) to form the final sender header from-name <NOTIFICATION_EMAIL_FROM>. If from-name is empty, the bare address is used as-is.
  • subject-prefix is prepended to every email subject.

The file is resolved per locale (NOTIFICATION_EMAIL_LOCALE) using the same order as the HTML template, using the first that exists:

  1. Mounted override, localized: /opt/app/templates/{locale}/notification/email-branding.properties
  2. Mounted override, legacy unsuffixed: /opt/app/templates/notification/email-branding.properties
  3. Bundled classpath, localized: src/main/resources/templates/{locale}/notification/email-branding.properties
  4. Bundled classpath, English fallback: src/main/resources/templates/en/notification/email-branding.properties

Only the English (en) branding file is bundled; other locales fall back to it unless you provide a localized copy (e.g. to translate the subject prefix). If the file — or an individual key — is missing, the subject prefix falls back to Aam Digital and no display name is added.

Permission-Aware Notifications (optional)

To filter notifications based on entity-level permissions (so users only receive notifications for entities they can access), configure the connection to the replication-backend:

AAMREPLICATIONBACKENDCLIENTCONFIGURATION_BASEPATH=http://replication-backend:5984
AAMREPLICATIONBACKENDCLIENTCONFIGURATION_BASICAUTHUSERNAME=<admin-username>
AAMREPLICATIONBACKENDCLIENTCONFIGURATION_BASICAUTHPASSWORD=<admin-password>

If these variables are not set, notifications are sent without permission filtering (all users matching a notification rule receive the notification regardless of entity access). This is the expected default for systems without access control (no Config:Permissions document configured). To explicitly disable permission filtering set AAMREPLICATIONBACKENDCLIENTCONFIGURATION_BASEPATH= (empty value).

When the replication-backend is configured but temporarily unavailable, notifications are not sent (fail-closed) to avoid leaking data to unauthorized users. Each such failure is logged at ERROR.

A permission check that gets no answer is abandoned after AAMREPLICATIONBACKENDCLIENTCONFIGURATION_RESPONSETIMEOUTINSECONDS (default 10) and treated as unavailable, so an unresponsive replication-backend delays notifications instead of stopping them.

Firebase Configuration (for Push Notifications)

If you want to send Push Notifications, we rely on Firebase Cloud Messaging (FCM).

  1. Create a Firebase Account
  2. Create a Firebase Project
  3. Select "Cloud Messaging" and "Add a web app" (you can skip the second step of setup through npm)
  4. Open the "Project Settings" page
  5. Under "Your apps" copy to JSON config object
    1. store this as assets/firebase-config.json in the Aam Digital frontend (or overwrite the empty sample file in your ndb-setup folder in your deployment)
    2. make sure this is proper json format (i.e. the keys are also in double quotes)
  6. Create a Service Account or new key for it
    1. ... through the firebase interface as described here
    2. download the firebase-credentials.json with its key
    3. Encode it as base64 run this to print the encoded file to the console:
      base64 -i firebase-credentials.json
    4. Copy the output (remove line breaks to make it a single line of encoded text)
    5. Add this as an environment variable: NOTIFICATIONFIREBASECONFIGURATION_CREDENTIALFILEBASE64 as described above
  7. To apply the config restart the container, if necessary

Config:Permissions

Users need to have certain Entity Permissions in the frontend to be able to access and configure Notifications:

In the User Permissions doc Config:Permissions in the CouchDB, make sure the following rules are present:

{
  "default": [
    {
      "subject": ["NotificationConfig", "NotificationEvent"],
      "action": "manage"
    }
  ]
}