Fix ntfy bearer token usage
This commit is contained in:
parent
a7ab445281
commit
0c2cc6b081
4 changed files with 9 additions and 20 deletions
|
|
@ -37,7 +37,7 @@ A web app that visualizes DMR repeaters on an interactive map. Filter by band an
|
||||||
| [BrandMeister WebSocket](https://wiki.brandmeister.network/index.php/API/Last_Heard) | Real-time Last Heard feed for heatmap via socket.io | None |
|
| [BrandMeister WebSocket](https://wiki.brandmeister.network/index.php/API/Last_Heard) | Real-time Last Heard feed for heatmap via socket.io | None |
|
||||||
| [Nominatim](https://nominatim.openstreetmap.org) | Address geocoding and autocomplete (client-side) | None |
|
| [Nominatim](https://nominatim.openstreetmap.org) | Address geocoding and autocomplete (client-side) | None |
|
||||||
| [OSRM](https://project-osrm.org) | Driving route calculation (client-side) | None |
|
| [OSRM](https://project-osrm.org) | Driving route calculation (client-side) | None |
|
||||||
| [ntfy](https://ntfy.sh) | Push notification when a user report is submitted (optional) | Access token (bearer or basic auth) |
|
| [ntfy](https://ntfy.sh) | Push notification when a user report is submitted (optional) | Access token (bearer) |
|
||||||
|
|
||||||
The BrandMeister talk group registry is fetched once at Docker build time and bundled as `frontend/static/talkgroups.json`. It is used for talk group name resolution and autocomplete in CPS Studio.
|
The BrandMeister talk group registry is fetched once at Docker build time and bundled as `frontend/static/talkgroups.json`. It is used for talk group name resolution and autocomplete in CPS Studio.
|
||||||
|
|
||||||
|
|
@ -79,8 +79,7 @@ docker compose -f compose.test.yml run --rm test
|
||||||
| `ADMIN_TOKEN` | *(disabled)* | Set to enable the admin interface at `/admin/` |
|
| `ADMIN_TOKEN` | *(disabled)* | Set to enable the admin interface at `/admin/` |
|
||||||
| `NTFY_URL` | *(disabled)* | ntfy server base URL (e.g. `https://ntfy.example.com`). Together with `NTFY_TOPIC` it enables push notifications for new user reports |
|
| `NTFY_URL` | *(disabled)* | ntfy server base URL (e.g. `https://ntfy.example.com`). Together with `NTFY_TOPIC` it enables push notifications for new user reports |
|
||||||
| `NTFY_TOPIC` | *(disabled)* | ntfy topic that report notifications are published to |
|
| `NTFY_TOPIC` | *(disabled)* | ntfy topic that report notifications are published to |
|
||||||
| `NTFY_USER` | *(none)* | ntfy username. When set, the token is sent as HTTP basic auth (`user:token`) |
|
| `NTFY_TOKEN` | *(none)* | ntfy access token, sent as `Authorization: Bearer`. Required for protected topics |
|
||||||
| `NTFY_TOKEN` | *(none)* | ntfy access token. Sent as `Authorization: Bearer` when no username is set |
|
|
||||||
| `PUBLIC_BASE_URL` | *(none)* | Public base URL of the site (e.g. `https://dmrmap.de`). Used to add a click-through link to the admin view in ntfy notifications |
|
| `PUBLIC_BASE_URL` | *(none)* | Public base URL of the site (e.g. `https://dmrmap.de`). Used to add a click-through link to the admin view in ntfy notifications |
|
||||||
|
|
||||||
Both `NTFY_URL` and `NTFY_TOPIC` must be set for notifications; otherwise reports are stored but nothing is pushed. On startup the app logs either `ntfy notifications enabled: <topic URL> (auth: ...)` or `ntfy notifications disabled`. The **Test push** button in the admin Reports view publishes a test notification and shows the upstream error if it fails.
|
Both `NTFY_URL` and `NTFY_TOPIC` must be set for notifications; otherwise reports are stored but nothing is pushed. On startup the app logs either `ntfy notifications enabled: <topic URL> (auth: ...)` or `ntfy notifications disabled`. The **Test push** button in the admin Reports view publishes a test notification and shows the upstream error if it fails.
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -14,14 +13,13 @@ import (
|
||||||
// required environment variables are not set, in which case notify is a no-op.
|
// required environment variables are not set, in which case notify is a no-op.
|
||||||
type ntfyNotifier struct {
|
type ntfyNotifier struct {
|
||||||
url string // full topic URL, e.g. https://ntfy.example.com/dmrmap
|
url string // full topic URL, e.g. https://ntfy.example.com/dmrmap
|
||||||
user string
|
|
||||||
token string
|
token string
|
||||||
clickBase string
|
clickBase string
|
||||||
httpClient *http.Client
|
httpClient *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
// newNtfyNotifierFromEnv builds a notifier from NTFY_URL / NTFY_TOPIC /
|
// newNtfyNotifierFromEnv builds a notifier from NTFY_URL / NTFY_TOPIC /
|
||||||
// NTFY_USER / NTFY_TOKEN. Returns nil when notifications are not configured.
|
// NTFY_TOKEN. Returns nil when notifications are not configured.
|
||||||
func newNtfyNotifierFromEnv() *ntfyNotifier {
|
func newNtfyNotifierFromEnv() *ntfyNotifier {
|
||||||
base := strings.TrimSpace(os.Getenv("NTFY_URL"))
|
base := strings.TrimSpace(os.Getenv("NTFY_URL"))
|
||||||
topic := strings.TrimSpace(os.Getenv("NTFY_TOPIC"))
|
topic := strings.TrimSpace(os.Getenv("NTFY_TOPIC"))
|
||||||
|
|
@ -32,17 +30,14 @@ func newNtfyNotifierFromEnv() *ntfyNotifier {
|
||||||
|
|
||||||
n := &ntfyNotifier{
|
n := &ntfyNotifier{
|
||||||
url: strings.TrimRight(base, "/") + "/" + strings.TrimLeft(topic, "/"),
|
url: strings.TrimRight(base, "/") + "/" + strings.TrimLeft(topic, "/"),
|
||||||
user: strings.TrimSpace(os.Getenv("NTFY_USER")),
|
|
||||||
token: strings.TrimSpace(os.Getenv("NTFY_TOKEN")),
|
token: strings.TrimSpace(os.Getenv("NTFY_TOKEN")),
|
||||||
clickBase: strings.TrimRight(strings.TrimSpace(os.Getenv("PUBLIC_BASE_URL")), "/"),
|
clickBase: strings.TrimRight(strings.TrimSpace(os.Getenv("PUBLIC_BASE_URL")), "/"),
|
||||||
httpClient: &http.Client{Timeout: 10 * time.Second},
|
httpClient: &http.Client{Timeout: 10 * time.Second},
|
||||||
}
|
}
|
||||||
|
|
||||||
auth := "none"
|
auth := "none"
|
||||||
if n.user != "" && n.token != "" {
|
if n.token != "" {
|
||||||
auth = "basic (" + n.user + ")"
|
auth = "access token"
|
||||||
} else if n.token != "" {
|
|
||||||
auth = "bearer token"
|
|
||||||
}
|
}
|
||||||
log.Printf("ntfy notifications enabled: %s (auth: %s)", n.url, auth)
|
log.Printf("ntfy notifications enabled: %s (auth: %s)", n.url, auth)
|
||||||
|
|
||||||
|
|
@ -110,14 +105,11 @@ func (n *ntfyNotifier) send(rep UserReport) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// setAuth uses basic auth when a username is configured (ntfy accepts an
|
// setAuth applies the ntfy access token. It is a bearer credential: ntfy does
|
||||||
// access token as the password), and bearer auth for a token on its own.
|
// NOT accept it as the password half of a username:password pair (that yields
|
||||||
|
// HTTP 401), only as a bearer token or as basic auth with an empty username.
|
||||||
func (n *ntfyNotifier) setAuth(req *http.Request) {
|
func (n *ntfyNotifier) setAuth(req *http.Request) {
|
||||||
switch {
|
if n.token != "" {
|
||||||
case n.user != "" && n.token != "":
|
|
||||||
creds := base64.StdEncoding.EncodeToString([]byte(n.user + ":" + n.token))
|
|
||||||
req.Header.Set("Authorization", "Basic "+creds)
|
|
||||||
case n.token != "":
|
|
||||||
req.Header.Set("Authorization", "Bearer "+n.token)
|
req.Header.Set("Authorization", "Bearer "+n.token)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ services:
|
||||||
- MIGRATIONS_DIR=/app/backend/migrations
|
- MIGRATIONS_DIR=/app/backend/migrations
|
||||||
- NTFY_URL=${NTFY_URL:-}
|
- NTFY_URL=${NTFY_URL:-}
|
||||||
- NTFY_TOPIC=${NTFY_TOPIC:-}
|
- NTFY_TOPIC=${NTFY_TOPIC:-}
|
||||||
- NTFY_USER=${NTFY_USER:-}
|
|
||||||
- NTFY_TOKEN=${NTFY_TOKEN:-}
|
- NTFY_TOKEN=${NTFY_TOKEN:-}
|
||||||
- PUBLIC_BASE_URL=${PUBLIC_BASE_URL:-http://localhost:8080}
|
- PUBLIC_BASE_URL=${PUBLIC_BASE_URL:-http://localhost:8080}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,6 @@ services:
|
||||||
- ADMIN_TOKEN=${ADMIN_TOKEN:-}
|
- ADMIN_TOKEN=${ADMIN_TOKEN:-}
|
||||||
- NTFY_URL=${NTFY_URL:-}
|
- NTFY_URL=${NTFY_URL:-}
|
||||||
- NTFY_TOPIC=${NTFY_TOPIC:-}
|
- NTFY_TOPIC=${NTFY_TOPIC:-}
|
||||||
- NTFY_USER=${NTFY_USER:-}
|
|
||||||
- NTFY_TOKEN=${NTFY_TOKEN:-}
|
- NTFY_TOKEN=${NTFY_TOKEN:-}
|
||||||
- PUBLIC_BASE_URL=${PUBLIC_BASE_URL:-}
|
- PUBLIC_BASE_URL=${PUBLIC_BASE_URL:-}
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue