feat(settings): surface push.ntfyUrl in /public/settings for the app
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:
@@ -105,9 +105,28 @@ async function getPublic() {
|
||||
hero: brand.hero,
|
||||
favicon: brand.favicon,
|
||||
}
|
||||
// Push-notification relay (M7). The client-facing ntfy base URL the app's
|
||||
// embedded distributor registers its device topic against; null when push is
|
||||
// not configured for this shard, in which case the app simply shows push as
|
||||
// unavailable. The publisher's own NTFY_BASE_URL may be an internal
|
||||
// compose-network address, so a distinct NTFY_PUBLIC_URL is preferred; failing
|
||||
// that we use the first NTFY_ALLOWED_ORIGINS entry (a device endpoint must sit
|
||||
// on an allowed origin anyway). Never NTFY_BASE_URL — it may be internal-only.
|
||||
out.push = { ntfyUrl: publicNtfyUrl() }
|
||||
return out
|
||||
}
|
||||
|
||||
// The client-facing ntfy base URL (no trailing slash), or null when unset.
|
||||
function publicNtfyUrl() {
|
||||
const explicit = (process.env.NTFY_PUBLIC_URL || '').trim()
|
||||
if (explicit) return explicit.replace(/\/+$/, '')
|
||||
const firstOrigin = (process.env.NTFY_ALLOWED_ORIGINS || '')
|
||||
.split(',')
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean)[0]
|
||||
return firstOrigin ? firstOrigin.replace(/\/+$/, '') : null
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
get,
|
||||
set,
|
||||
|
||||
Reference in New Issue
Block a user