Delivery providers & Web Push
Acest conținut nu este încă disponibil în limba selectată.
This document covers two gNotify capabilities added on top of the core notification pipeline:
- Delivery providers (Furnizori) — configurable per-channel backends for sending email and SMS, chosen individually for each channel of a notification.
- Web Push — real browser push notifications over the open Web Push protocol (VAPID), with no Firebase / FCM account required.
1. Delivery providers (Furnizori)
Section titled “1. Delivery providers (Furnizori)”Concept
Section titled “Concept”A Provider is a persisted, admin-managed delivery backend. Instead of hard-wiring a
single SMTP server or one SMS gateway, operators register any number of providers and pick
one per channel when sending a notification (“this email via the first SMTP, that email
via the second; this SMS via AWS SNS, that one via the third-party gateway”).
Each provider has:
| Field | Meaning |
|---|---|
cheie | unique key, referenced at send time (e.g. gstackgov, aws-sns) |
nume | human label shown in the UI |
tip | provider type — one of the values below |
config | JSON configuration (shape depends on tip) |
activ | only active providers are selectable when sending |
Provider types
Section titled “Provider types”tip | Channel | config keys |
|---|---|---|
EMAIL_SMTP | from (required). Optional host, port, username, password, starttls for a dedicated SMTP server. If host is omitted, the shared spring.mail.* transport (e.g. Amazon SES) is reused and only the From identity differs. | |
SMS_SNS | sms | region (default eu-north-1), accessKey, secretKey, optional senderId, smsType (Transactional/Promotional). Sends via Amazon SNS Publish. |
SMS_HTTP | sms | url (required), method (default POST), contentType (form/json), headers (JSON object), params (JSON object; values may contain {to} and {message} placeholders). Optional successField/idField/errorField to parse a JSON response (so gateways that return HTTP 200 on failure — like TextBelt — are handled correctly). Generic third-party HTTP gateway. |
Choosing a provider per channel
Section titled “Choosing a provider per channel”On the Trimite notificare (notify console) screen, each channel row has a Furnizor dropdown:
- email channels list the active
EMAIL_SMTPproviders; - sms channels list the active
SMS_SNS+SMS_HTTPproviders; - push / telegram have no provider (single built-in transport).
Leaving the dropdown on (implicit) keeps the previous behaviour:
- email → default SES
From(gnotify.mail.default-sender); - sms → the fallback provider from
gnotify.sms.provider(stub/twilio/textbelt).
The chosen provider key travels end-to-end: ChannelSpec.provider → NotificationJob.provider
→ resolved at delivery by ProviderRegistry inside EmailChannelSender / SmsChannelSender.
Managing providers — the Furnizori page
Section titled “Managing providers — the Furnizori page”Administrare → Furnizori (/providers, ADMIN only) is a type-aware CRUD screen:
selecting a type shows the right config fields, SMS_HTTP exposes free-form headers/params
JSON, and the config is serialized to the provider’s config column on save.
Backend endpoint: /api/providers (standard JHipster resource, locked to ROLE_ADMIN).
Seeded providers
Section titled “Seeded providers”On startup, ProviderSeeder materializes the delivery backends that were previously
configured only through properties, so they appear on the Furnizori page and in the
per-channel selector. Upsert by key — existing rows (including your UI edits) are never
overwritten.
| Key | Seeded from | Type | Active | Notes |
|---|---|---|---|---|
gstackgov | gnotify.mail.senders.gstackgov | EMAIL_SMTP | yes | SES From identity; shares the SES transport |
gstackdev | gnotify.mail.senders.gstackdev | EMAIL_SMTP | yes | SES From identity; shares the SES transport |
textbelt | gnotify.sms.textbelt | SMS_HTTP | yes | the “third-party server”; success-field parsing wired |
aws-sns | (template) | SMS_SNS | no | placeholder — fill in credentials in the UI and enable |
The two SES senders differ only by From; they are two identities on one SES server, not two
separate SMTP servers. To add a genuinely separate second SMTP server, create a new
EMAIL_SMTP provider on the Furnizori page and fill in its own host/port/username/password.
Adding real credentials
Section titled “Adding real credentials”- AWS SNS: edit the seeded
aws-snsrow → setregion,accessKey,secretKey→ tick Activ. The AWS SDK v2 (software.amazon.awssdk:sns, url-connection HTTP client) is already bundled. - A second/other SMTP: add an
EMAIL_SMTPprovider withhost/port/username/passwordand afrom. - Another HTTP SMS gateway: add an
SMS_HTTPprovider with the gateway’surl,params(using{to}/{message}), and — if it returns a JSON body —successField.
2. Web Push (VAPID)
Section titled “2. Web Push (VAPID)”How it works — and why there is no Firebase
Section titled “How it works — and why there is no Firebase”gNotify sends browser notifications with the standard Web Push protocol using VAPID
(Voluntary Application Server Identification), via the nl.martijndwars:web-push Java
library. There is no Firebase project, no FCM app, and no server key:
- Each browser subscribes to its own push service. gNotify posts the encrypted payload directly there.
- Firefox subscriptions terminate on Mozilla’s push service
(
updates.push.services.mozilla.com); Chrome/Edge subscriptions terminate on Google’s endpoints (fcm.googleapis.com/wp/…). The latter is just the standardized Web Push transport — it does not require a Firebase account. - The only server-side secret is the VAPID keypair, owned by the deployment.
Configuration
Section titled “Configuration”Set on the backend (env / gnotify.push.vapid.*):
GNOTIFY_PUSH_VAPID_PUBLIC_KEY=<base64url public key>GNOTIFY_PUSH_VAPID_PRIVATE_KEY=<base64url private key>GNOTIFY_PUSH_VAPID_SUBJECT=mailto:admin@exampleGenerate a keypair with npx web-push generate-vapid-keys --json. If the keys are unset,
the PUSH channel silently falls back to a stub (logs “sent”, delivers nothing) — the
provider_message_id will read fake-push-… instead of webpush-…. On startup the log
prints Web Push (VAPID) not configured … when the keys are missing.
Subscription model
Section titled “Subscription model”PUSH is keyed by the recipient’s identifier (IDNP/IDNO), not by an address — a person can
have several devices. When a citizen enables push from the portal (Contul meu), the
browser subscription is stored as a PushSubscription under their identifier, and a PUSH
CanalContact (value = identifier) is auto-created so the send-by-IDNP flow can route to it.
- Public key for the browser:
GET /api/registry/push-public-key(authenticated). - Subscribe:
POST /api/registry/me/push-subscription. - Sending to
PUSHfans out to all the identifier’s subscriptions; a404/410from the push service means the subscription expired and it is auto-removed.
Testing it end to end
Section titled “Testing it end to end”- Subscribe a browser — open the portal (
https://portalgnotify.gstack.esempla.systems), log in as a directory user, go to Contul meu, enable push and Allow the browser prompt. Note the user’s IDNP. - Send — from the admin UI (
https://gnotify.gstack.esempla.systems):- Trimite după ID / grup → enter the IDNP → send; or
- Trimite notificare → Fără șablon → add a push channel with recipient = the IDNP.
- Verify — the OS notification appears (even with the portal tab closed, as long as the
browser runs). Server-side, the
Livrarerow flips toPUSH / SENTwith aprovider_message_idstartingwebpush-(a real send;fake-push-would mean the stub).
Requirements: HTTPS, the user must click Allow, desktop Chrome/Edge/Firefox (Safari/iOS is restricted).
Regeneration safety
Section titled “Regeneration safety”All of the above lives in separately-named, hand-written classes so jhipster jdl never
clobbers it: service/channel/{ProviderRegistry,ProviderSeeder,EmailChannelSender,SmsChannelSender,WebPushService,PushChannelSender},
repository/lookup/*, web/rest/RegistryPushResource, and the standalone app/providers/
Angular screen. The Provider entity itself is generated from registry-providers.jdl
(@skipClient, backend-only) — re-run jhipster jdl registry-providers.jdl --force after JDL
changes, then keep the security matcher for /api/providers/**.