feat(settings): surface push.ntfyUrl in /public/settings for the app
All checks were successful
PR Checks / server-tests (pull_request) Successful in 9m39s
PR Checks / client-build (pull_request) Successful in 9m24s
PR Checks / bot-install (pull_request) Successful in 9m17s

The Android app's embedded push distributor (M7 Part 2) needs the shard's
client-facing ntfy relay URL to build its device topic endpoint, but the M7
Part 1 backend only used the NTFY_* vars server-side and never surfaced them.

Add a `push: { ntfyUrl }` block to settings.getPublic(), sourced from
NTFY_PUBLIC_URL or the first NTFY_ALLOWED_ORIGINS entry (never the possibly
internal NTFY_BASE_URL); null when unconfigured, so the app shows push as
unavailable for that shard. Additive, non-sensitive, forward-compatible.

- Extend the PublicSettings swagger schema; regenerate swagger-output.json.
- publicBrand.test.js: cover null / NTFY_PUBLIC_URL / NTFY_ALLOWED_ORIGINS.
- Document NTFY_PUBLIC_URL in .env.example and (docs PR) BACKEND_DESIGN.md.

Full server suite green (250 pass).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-20 15:25:40 -05:00
parent 4fa73d3ccf
commit a789ee3ac9
5 changed files with 123 additions and 5 deletions

View File

@@ -101,12 +101,20 @@ TOWNCRIER_DURATION_SEC=3600
# Push notifications (M7) — opt-in fan-out to the Android app via a self-hosted
# ntfy UnifiedPush relay (docs/android/PLAN.md §11). The publisher POSTs
# content-free tickles to each device's endpoint, so no publish token is required.
# NTFY_BASE_URL Public relay URL; also the backend's SSRF allow-set — a
# device may only register an endpoint on this origin.
# NTFY_ALLOWED_ORIGINS Optional comma-separated extra allowed origins.
# NTFY_BASE_URL Internal relay URL the publisher POSTs to; also part of the
# backend's SSRF allow-set — a device may only register an
# endpoint on an allowed origin.
# NTFY_PUBLIC_URL Client-facing relay URL surfaced to the app via
# /public/settings.push.ntfyUrl (the app registers its topic
# endpoint here). Defaults to the first NTFY_ALLOWED_ORIGINS
# entry; set when the public URL differs from NTFY_BASE_URL.
# NTFY_ALLOWED_ORIGINS Optional comma-separated allowed origins (the app's endpoint
# must sit on one). Also the default source for NTFY_PUBLIC_URL.
# NTFY_PUBLISH_TOKEN Optional bearer token for backend->ntfy publishes (off by default).
# Leave NTFY_BASE_URL unset in local dev to allow any public HTTPS endpoint
# (private/loopback hosts are always rejected).
# (private/loopback hosts are always rejected). Without NTFY_PUBLIC_URL /
# NTFY_ALLOWED_ORIGINS the app shows push as unavailable for the shard.
# NTFY_BASE_URL=https://ntfy.example.com
# NTFY_ALLOWED_ORIGINS=
# NTFY_PUBLIC_URL=https://ntfy.example.com
# NTFY_ALLOWED_ORIGINS=https://ntfy.example.com
# NTFY_PUBLISH_TOKEN=