feat(ntfy): support HTTP Basic auth alongside access tokens
CI / Build (windows/amd64) (pull_request) Successful in 22s
CI / Build (linux/amd64) (pull_request) Successful in 22s
CI / Build (linux/arm64) (pull_request) Successful in 21s
CI / Lint (pull_request) Successful in 1m12s
CI / Test (linux/amd64) (pull_request) Successful in 1m18s

Self-hosted ntfy that doesn't expose a token-mint endpoint can still
authenticate over HTTP Basic. Add Username + Password fields to
NtfyConfig; the channel sends 'Authorization: Basic …' when token is
empty and username is set. Token wins when both are configured.

Form-side: two new optional fields next to the access token, with
the same write-only placeholder treatment as smtp_password (blank
on edit means 'keep stored value'). Username is round-tripped on
edit; password is masked.
This commit is contained in:
2026-05-04 22:25:42 +01:00
parent cffad4b4f3
commit feaeff217d
3 changed files with 42 additions and 7 deletions
+11 -2
View File
@@ -3,6 +3,7 @@ package notification
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"io"
"net/http"
@@ -11,11 +12,15 @@ import (
)
// NtfyConfig is the per-channel JSON shape stored AEAD-encrypted in
// notification_channels.config.
// notification_channels.config. AccessToken takes precedence over
// (Username, Password) when both are set; supply one or the other for
// self-hosted ntfy that requires auth.
type NtfyConfig struct {
ServerURL string `json:"server_url"`
Topic string `json:"topic"`
AccessToken string `json:"access_token,omitempty"`
Username string `json:"username,omitempty"`
Password string `json:"password,omitempty"`
}
// NtfyChannel delivers alerts to an ntfy server using POST with
@@ -65,8 +70,12 @@ func (c *NtfyChannel) Send(ctx context.Context, p Payload) (int, time.Duration,
if p.Link != "" {
req.Header.Set("Click", p.Link)
}
if c.cfg.AccessToken != "" {
switch {
case c.cfg.AccessToken != "":
req.Header.Set("Authorization", "Bearer "+c.cfg.AccessToken)
case c.cfg.Username != "":
creds := c.cfg.Username + ":" + c.cfg.Password
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(creds)))
}
t0 := time.Now()