Minor changes
parent
30a8f66db2
commit
020996ea04
|
@ -789,7 +789,7 @@ Note that the self-hosted server literally sends the message `New message` for e
|
||||||
may be `Some other message`. This is so that if iOS cannot talk to the self-hosted server (in time, or at all),
|
may be `Some other message`. This is so that if iOS cannot talk to the self-hosted server (in time, or at all),
|
||||||
it'll show `New message` as a popup.
|
it'll show `New message` as a popup.
|
||||||
|
|
||||||
## Web Push notifications
|
## Web Push
|
||||||
[Web Push](https://developer.mozilla.org/en-US/docs/Web/API/Push_API) ([RFC8030](https://datatracker.ietf.org/doc/html/rfc8030))
|
[Web Push](https://developer.mozilla.org/en-US/docs/Web/API/Push_API) ([RFC8030](https://datatracker.ietf.org/doc/html/rfc8030))
|
||||||
allows ntfy to receive push notifications, even when the ntfy web app (or even the browser, depending on the platform) is closed.
|
allows ntfy to receive push notifications, even when the ntfy web app (or even the browser, depending on the platform) is closed.
|
||||||
When enabled, the user can enable **background notifications** for their topics in the wep app under Settings. Once enabled by the
|
When enabled, the user can enable **background notifications** for their topics in the wep app under Settings. Once enabled by the
|
||||||
|
@ -817,6 +817,7 @@ To configure VAPID keys, first generate them:
|
||||||
```sh
|
```sh
|
||||||
$ ntfy webpush keys
|
$ ntfy webpush keys
|
||||||
Web Push keys generated.
|
Web Push keys generated.
|
||||||
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
Then copy the generated values into your `server.yml` or use the corresponding environment variables or command line arguments:
|
Then copy the generated values into your `server.yml` or use the corresponding environment variables or command line arguments:
|
||||||
|
@ -828,8 +829,9 @@ web-push-subscriptions-file: /var/cache/ntfy/webpush.db
|
||||||
web-push-email-address: sysadmin@example.com
|
web-push-email-address: sysadmin@example.com
|
||||||
```
|
```
|
||||||
|
|
||||||
The `web-push-subscriptions-file` is used to store the push subscriptions. Subscriptions do not ever expire automatically, unless the push
|
The `web-push-subscriptions-file` is used to store the push subscriptions. Unused subscriptions will send out a warning after 7 days,
|
||||||
gateway returns an error (e.g. 410 Gone when a user has unsubscribed).
|
and will automatically expire after 9 days (not configurable). If the gateway returns an error (e.g. 410 Gone when a user has unsubscribed),
|
||||||
|
subscriptions are also removed automatically.
|
||||||
|
|
||||||
The web app refreshes subscriptions on start and regularly on an interval, but this file should be persisted across restarts. If the subscription
|
The web app refreshes subscriptions on start and regularly on an interval, but this file should be persisted across restarts. If the subscription
|
||||||
file is deleted or lost, any web apps that aren't open will not receive new web push notifications until you open then.
|
file is deleted or lost, any web apps that aren't open will not receive new web push notifications until you open then.
|
||||||
|
@ -1333,8 +1335,8 @@ variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`).
|
||||||
| `stripe-secret-key` | `NTFY_STRIPE_SECRET_KEY` | *string* | - | Payments: Key used for the Stripe API communication, this enables payments |
|
| `stripe-secret-key` | `NTFY_STRIPE_SECRET_KEY` | *string* | - | Payments: Key used for the Stripe API communication, this enables payments |
|
||||||
| `stripe-webhook-key` | `NTFY_STRIPE_WEBHOOK_KEY` | *string* | - | Payments: Key required to validate the authenticity of incoming webhooks from Stripe |
|
| `stripe-webhook-key` | `NTFY_STRIPE_WEBHOOK_KEY` | *string* | - | Payments: Key required to validate the authenticity of incoming webhooks from Stripe |
|
||||||
| `billing-contact` | `NTFY_BILLING_CONTACT` | *email address* or *website* | - | Payments: Email or website displayed in Upgrade dialog as a billing contact |
|
| `billing-contact` | `NTFY_BILLING_CONTACT` | *email address* or *website* | - | Payments: Email or website displayed in Upgrade dialog as a billing contact |
|
||||||
| `web-push-public-key` | `NTFY_WEB_PUSH_PUBLIC_KEY` | *string* | - | Web Push: Public Key. Run `ntfy webpush generate-keys` to generate |
|
| `web-push-public-key` | `NTFY_WEB_PUSH_PUBLIC_KEY` | *string* | - | Web Push: Public Key. Run `ntfy webpush keys` to generate |
|
||||||
| `web-push-private-key` | `NTFY_WEB_PUSH_PRIVATE_KEY` | *string* | - | Web Push: Private Key. Run `ntfy webpush generate-keys` to generate |
|
| `web-push-private-key` | `NTFY_WEB_PUSH_PRIVATE_KEY` | *string* | - | Web Push: Private Key. Run `ntfy webpush keys` to generate |
|
||||||
| `web-push-subscriptions-file` | `NTFY_WEB_PUSH_SUBSCRIPTIONS_FILE` | *string* | - | Web Push: Subscriptions file |
|
| `web-push-subscriptions-file` | `NTFY_WEB_PUSH_SUBSCRIPTIONS_FILE` | *string* | - | Web Push: Subscriptions file |
|
||||||
| `web-push-email-address` | `NTFY_WEB_PUSH_EMAIL_ADDRESS` | *string* | - | Web Push: Sender email address |
|
| `web-push-email-address` | `NTFY_WEB_PUSH_EMAIL_ADDRESS` | *string* | - | Web Push: Sender email address |
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
# Using the web app as an installed web app
|
# Using the web app as an installed web app
|
||||||
|
|
||||||
While ntfy doesn't have a native desktop app, it is built as a [progressive web app](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) (PWA)
|
While ntfy doesn't have a native desktop app, it is built as a [progressive web app](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps) (PWA)
|
||||||
and thus can be installed on both desktop and mobile. This gives it its own launcher (e.g. shortcut on Windows, app on
|
and thus can be installed on both desktop and mobile devices. This gives it its own launcher (e.g. shortcut on Windows, app on
|
||||||
macOS, launcher shortcut on Linux) own window, push notifications, and an app badge with the unread notification count.
|
macOS, launcher shortcut on Linux), own window, push notifications, and an app badge with the unread notification count.
|
||||||
|
|
||||||
To install and register the web app in your operating system, click the "install app" icon in your browser (usually next to the
|
To install and register the web app in your operating system, click the "install app" icon in your browser (usually next to the
|
||||||
address bar). To receive background notifications, **either the browser or the installed web app must be open**.
|
address bar). To receive background notifications, **either the browser or the installed web app must be open**.
|
||||||
|
|
|
@ -146,8 +146,8 @@
|
||||||
|
|
||||||
# Web Push support (background notifications for browsers)
|
# Web Push support (background notifications for browsers)
|
||||||
#
|
#
|
||||||
# If enabled, allows ntfy to receive push notifications, even when the ntfy web app is closed. When enabled, the user
|
# If enabled, allows ntfy to receive push notifications, even when the ntfy web app is closed. When enabled, users
|
||||||
# can enable background notifications. Once enabled by the user, ntfy will forward published messages to the push
|
# can enable background notifications in the web app. Once enabled, ntfy will forward published messages to the push
|
||||||
# endpoint, which will then forward it to the browser.
|
# endpoint, which will then forward it to the browser.
|
||||||
#
|
#
|
||||||
# You must configure all settings below to enable Web Push.
|
# You must configure all settings below to enable Web Push.
|
||||||
|
|
|
@ -431,6 +431,41 @@ func TestAccount_Delete_Not_Allowed(t *testing.T) {
|
||||||
require.Equal(t, 40026, toHTTPError(t, rr.Body.String()).Code)
|
require.Equal(t, 40026, toHTTPError(t, rr.Body.String()).Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAccount_Delete_Success_WithWebPush(t *testing.T) {
|
||||||
|
conf := configureAuth(t, newTestConfigWithWebPush(t))
|
||||||
|
conf.EnableSignup = true
|
||||||
|
s := newTestServer(t, conf)
|
||||||
|
|
||||||
|
// Add account
|
||||||
|
rr := request(t, s, "POST", "/v1/account", `{"username":"phil", "password":"mypass"}`, nil)
|
||||||
|
require.Equal(t, 200, rr.Code)
|
||||||
|
|
||||||
|
// Add web push subscription
|
||||||
|
rr = request(t, s, "POST", "/v1/webpush", payloadForTopics(t, []string{"mytopic"}, testWebPushEndpoint), map[string]string{
|
||||||
|
"Authorization": util.BasicAuth("phil", "mypass"),
|
||||||
|
})
|
||||||
|
require.Equal(t, 200, rr.Code)
|
||||||
|
|
||||||
|
u, err := s.userManager.User("phil")
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
|
subs, err := s.webPush.SubscriptionsForTopic("mytopic")
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Len(t, subs, 1)
|
||||||
|
require.Equal(t, u.ID, subs[0].UserID)
|
||||||
|
|
||||||
|
// Delete account
|
||||||
|
rr = request(t, s, "DELETE", "/v1/account", `{"password":"mypass"}`, map[string]string{
|
||||||
|
"Authorization": util.BasicAuth("phil", "mypass"),
|
||||||
|
})
|
||||||
|
require.Equal(t, 200, rr.Code)
|
||||||
|
|
||||||
|
// Make sure web push subscription was deleted
|
||||||
|
subs, err = s.webPush.SubscriptionsForTopic("mytopic")
|
||||||
|
require.Nil(t, err)
|
||||||
|
require.Len(t, subs, 0)
|
||||||
|
}
|
||||||
|
|
||||||
func TestAccount_Reservation_AddWithoutTierFails(t *testing.T) {
|
func TestAccount_Reservation_AddWithoutTierFails(t *testing.T) {
|
||||||
conf := newTestConfigWithAuthFile(t)
|
conf := newTestConfigWithAuthFile(t)
|
||||||
conf.EnableSignup = true
|
conf.EnableSignup = true
|
||||||
|
|
|
@ -120,7 +120,6 @@ func (s *Server) pruneAndNotifyWebPushSubscriptionsInternal() error {
|
||||||
}
|
}
|
||||||
payload, err := json.Marshal(newWebPushSubscriptionExpiringPayload())
|
payload, err := json.Marshal(newWebPushSubscriptionExpiringPayload())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Tag(tagWebPush).Err(err).Warn("Unable to marshal expiring payload")
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
warningSent := make([]*webPushSubscription, 0)
|
warningSent := make([]*webPushSubscription, 0)
|
||||||
|
@ -140,7 +139,14 @@ func (s *Server) pruneAndNotifyWebPushSubscriptionsInternal() error {
|
||||||
|
|
||||||
func (s *Server) sendWebPushNotification(sub *webPushSubscription, message []byte, contexters ...log.Contexter) error {
|
func (s *Server) sendWebPushNotification(sub *webPushSubscription, message []byte, contexters ...log.Contexter) error {
|
||||||
log.Tag(tagWebPush).With(sub).With(contexters...).Debug("Sending web push message")
|
log.Tag(tagWebPush).With(sub).With(contexters...).Debug("Sending web push message")
|
||||||
resp, err := webpush.SendNotification(message, sub.ToSubscription(), &webpush.Options{
|
payload := &webpush.Subscription{
|
||||||
|
Endpoint: sub.Endpoint,
|
||||||
|
Keys: webpush.Keys{
|
||||||
|
Auth: sub.Auth,
|
||||||
|
P256dh: sub.P256dh,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
resp, err := webpush.SendNotification(message, payload, &webpush.Options{
|
||||||
Subscriber: s.config.WebPushEmailAddress,
|
Subscriber: s.config.WebPushEmailAddress,
|
||||||
VAPIDPublicKey: s.config.WebPushPublicKey,
|
VAPIDPublicKey: s.config.WebPushPublicKey,
|
||||||
VAPIDPrivateKey: s.config.WebPushPrivateKey,
|
VAPIDPrivateKey: s.config.WebPushPrivateKey,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/SherClockHolmes/webpush-go"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"time"
|
"time"
|
||||||
|
@ -512,16 +511,6 @@ type webPushSubscription struct {
|
||||||
UserID string
|
UserID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *webPushSubscription) ToSubscription() *webpush.Subscription {
|
|
||||||
return &webpush.Subscription{
|
|
||||||
Endpoint: w.Endpoint,
|
|
||||||
Keys: webpush.Keys{
|
|
||||||
Auth: w.Auth,
|
|
||||||
P256dh: w.P256dh,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *webPushSubscription) Context() log.Context {
|
func (w *webPushSubscription) Context() log.Context {
|
||||||
return map[string]any{
|
return map[string]any{
|
||||||
"web_push_subscription_id": w.ID,
|
"web_push_subscription_id": w.ID,
|
||||||
|
|
|
@ -63,8 +63,12 @@ const (
|
||||||
WHERE st.topic = ?
|
WHERE st.topic = ?
|
||||||
ORDER BY endpoint
|
ORDER BY endpoint
|
||||||
`
|
`
|
||||||
selectWebPushSubscriptionsExpiringSoonQuery = `SELECT id, endpoint, key_auth, key_p256dh, user_id FROM subscription WHERE warned_at = 0 AND updated_at <= ?`
|
selectWebPushSubscriptionsExpiringSoonQuery = `
|
||||||
insertWebPushSubscriptionQuery = `
|
SELECT id, endpoint, key_auth, key_p256dh, user_id
|
||||||
|
FROM subscription
|
||||||
|
WHERE warned_at = 0 AND updated_at <= ?
|
||||||
|
`
|
||||||
|
insertWebPushSubscriptionQuery = `
|
||||||
INSERT INTO subscription (id, endpoint, key_auth, key_p256dh, user_id, subscriber_ip, updated_at, warned_at)
|
INSERT INTO subscription (id, endpoint, key_auth, key_p256dh, user_id, subscriber_ip, updated_at, warned_at)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
ON CONFLICT (endpoint)
|
ON CONFLICT (endpoint)
|
||||||
|
|
Loading…
Reference in New Issue