mirror of
https://github.com/DeBrosOfficial/orama.git
synced 2026-06-16 23:14:13 +00:00
Migration 028: namespace_push_credentials
- Per-(namespace, provider) AES-256-GCM encrypted credential blob.
- Generic schema — apns/ntfy/expo/future plug in with zero migration.
- Separated from migration 026's namespace_push_config (preferences vs
credentials, different access patterns).
pkg/push/credentials
- Manager + Registry + RQLite store; HKDF purpose "namespace-push-credentials"
via pkg/secrets. Provider Validator interface for per-provider schema.
pkg/push/providers/apns
- Apple Push Notification service direct provider (no Expo proxy).
- Validator + dispatcher; credentials are p8 signing key + key_id + team_id.
pkg/push/providers/ntfy/credentials.go
- ntfy credential schema (auth_token + default topic). Used both with
the public ntfy.sh and our self-hosted instance.
pkg/environments/production/installers/ntfy.go
- Self-hosted ntfy server installer. Binary, system user, hardened
/etc/ntfy/server.yml, systemd unit. Listens on 127.0.0.1:NtfyListenPort
only — Caddy is the only public path.
pkg/environments/production/installers/caddy.go
- Emit reverse_proxy block for push.<dnsZone> -> 127.0.0.1:NtfyListenPort
when operator enables ntfy on a node.
CLI: install/upgrade orchestrators learn a new "ntfy" install/preserve
phase; flag gating in install/flags.go + upgrade/flags.go.
Gateway handlers/push/credentials_handler.go
- GET/PUT/DELETE /v1/namespace/push-credentials/{provider}.
- PUT validates against provider Validator before encrypting and storing.
- GET returns a redacted view (booleans + non-secret fields only).
Push manager: provider resolution now also consults
namespace_push_credentials before falling back to YAML defaults.
Docs: core/docs/PUSH_NOTIFICATIONS.md walks through end-to-end setup.
VERSION bumped to 0.122.14.
116 lines
4.5 KiB
Go
116 lines
4.5 KiB
Go
package gateway
|
|
|
|
// push_routes.go provides the always-registered push HTTP entrypoints.
|
|
// When the gateway has no push provider configured (no NtfyBaseURL,
|
|
// no ExpoAccessToken, etc. — and therefore no g.pushHandlers), these
|
|
// methods return a canonical 503 envelope instead of letting the route
|
|
// fall through to the default 404 handler.
|
|
//
|
|
// Bug #220: tenants saw `404 Page Not Found` on /v1/push/devices and
|
|
// couldn't tell whether the path was wrong or push wasn't enabled.
|
|
// 503 + a clear message points operators directly at the config knob.
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/DeBrosOfficial/network/pkg/httputil"
|
|
)
|
|
|
|
// pushNotConfiguredMessage is the human-readable 503 body. Same text
|
|
// reused by every push entrypoint so operators see consistent guidance.
|
|
const pushNotConfiguredMessage = "push notifications are not configured on this namespace gateway. " +
|
|
"Set `ntfy_base_url` or `expo_access_token` in the gateway config and restart, " +
|
|
"then call this endpoint again. See core/docs/SERVERLESS.md for details."
|
|
|
|
// pushDevicesHandler dispatches GET (list) / POST (register) on
|
|
// /v1/push/devices. Returns 503 when push isn't configured.
|
|
func (g *Gateway) pushDevicesHandler(w http.ResponseWriter, r *http.Request) {
|
|
if g.pushHandlers == nil {
|
|
httputil.WriteRPCError(w, http.StatusServiceUnavailable,
|
|
httputil.ErrCodeServiceUnavailable, pushNotConfiguredMessage)
|
|
return
|
|
}
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
g.pushHandlers.ListDevicesHandler(w, r)
|
|
case http.MethodPost:
|
|
g.pushHandlers.RegisterDeviceHandler(w, r)
|
|
default:
|
|
httputil.WriteRPCError(w, http.StatusMethodNotAllowed,
|
|
httputil.ErrCodeValidationFailed, "method not allowed: use GET to list or POST to register")
|
|
}
|
|
}
|
|
|
|
// pushDevicesByIDHandler handles DELETE /v1/push/devices/{id}. Returns
|
|
// 503 when push isn't configured.
|
|
func (g *Gateway) pushDevicesByIDHandler(w http.ResponseWriter, r *http.Request) {
|
|
if g.pushHandlers == nil {
|
|
httputil.WriteRPCError(w, http.StatusServiceUnavailable,
|
|
httputil.ErrCodeServiceUnavailable, pushNotConfiguredMessage)
|
|
return
|
|
}
|
|
g.pushHandlers.DeleteDeviceHandler(w, r)
|
|
}
|
|
|
|
// pushSendHandler handles POST /v1/push/send. Returns 503 when push
|
|
// isn't configured.
|
|
func (g *Gateway) pushSendHandler(w http.ResponseWriter, r *http.Request) {
|
|
if g.pushHandlers == nil {
|
|
httputil.WriteRPCError(w, http.StatusServiceUnavailable,
|
|
httputil.ErrCodeServiceUnavailable, pushNotConfiguredMessage)
|
|
return
|
|
}
|
|
g.pushHandlers.SendHandler(w, r)
|
|
}
|
|
|
|
// pushConfigHandler dispatches GET / PUT / DELETE on /v1/push/config — the
|
|
// tenant-self-service entrypoint for per-namespace push provider config
|
|
// (bug #220 follow-up). When push is fully disabled returns 503 with the
|
|
// same actionable message as the device endpoints.
|
|
func (g *Gateway) pushConfigHandler(w http.ResponseWriter, r *http.Request) {
|
|
if g.pushHandlers == nil {
|
|
httputil.WriteRPCError(w, http.StatusServiceUnavailable,
|
|
httputil.ErrCodeServiceUnavailable, pushNotConfiguredMessage)
|
|
return
|
|
}
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
g.pushHandlers.GetConfigHandler(w, r)
|
|
case http.MethodPut, http.MethodPost:
|
|
g.pushHandlers.PutConfigHandler(w, r)
|
|
case http.MethodDelete:
|
|
g.pushHandlers.DeleteConfigHandler(w, r)
|
|
default:
|
|
httputil.WriteRPCError(w, http.StatusMethodNotAllowed,
|
|
httputil.ErrCodeValidationFailed,
|
|
"method not allowed: use GET to read, PUT to update, or DELETE to clear")
|
|
}
|
|
}
|
|
|
|
// pushCredentialsSummaryHandler — GET /v1/namespace/push-credentials.
|
|
// Returns the list of providers with credentials stored AND the list of
|
|
// providers this gateway supports (feature #72). 503 when push isn't
|
|
// configured at all.
|
|
func (g *Gateway) pushCredentialsSummaryHandler(w http.ResponseWriter, r *http.Request) {
|
|
if g.pushHandlers == nil {
|
|
httputil.WriteRPCError(w, http.StatusServiceUnavailable,
|
|
httputil.ErrCodeServiceUnavailable, pushNotConfiguredMessage)
|
|
return
|
|
}
|
|
g.pushHandlers.CredentialsSummaryHandler(w, r)
|
|
}
|
|
|
|
// pushCredentialsByProviderHandler dispatches GET / PUT / DELETE on
|
|
// /v1/namespace/push-credentials/{provider} (feature #72 — generic
|
|
// per-provider credential storage). The {provider} segment is parsed
|
|
// inside the handler so unknown providers return a 400 with the list
|
|
// of supported ones rather than a bare 404.
|
|
func (g *Gateway) pushCredentialsByProviderHandler(w http.ResponseWriter, r *http.Request) {
|
|
if g.pushHandlers == nil {
|
|
httputil.WriteRPCError(w, http.StatusServiceUnavailable,
|
|
httputil.ErrCodeServiceUnavailable, pushNotConfiguredMessage)
|
|
return
|
|
}
|
|
g.pushHandlers.CredentialsByProviderHandler(w, r)
|
|
}
|