binwiederhier vor 2 Jahren
Ursprung
Commit
020996ea04

+ 8 - 6
docs/config.md

@@ -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), 
 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))
 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
@@ -816,7 +816,8 @@ To configure VAPID keys, first generate them:
 
 ```sh
 $ 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:
@@ -828,8 +829,9 @@ web-push-subscriptions-file: /var/cache/ntfy/webpush.db
 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
-gateway returns an error (e.g. 410 Gone when a user has unsubscribed).
+The `web-push-subscriptions-file` is used to store the push subscriptions. Unused subscriptions will send out a warning after 7 days,
+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
 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-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                                                                                                                                                     |
-| `web-push-public-key`                      | `NTFY_WEB_PUSH_PUBLIC_KEY`                      | *string*                                            | -                 | Web Push: Public 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 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 keys` to generate                                                                                                                                                                      |
 | `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                                                                                                                                                                                                  |
 

+ 2 - 3
docs/subscribe/desktop.md

@@ -1,8 +1,7 @@
 # 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)
-and thus can be installed on both desktop and mobile. 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.
+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.
 
 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**.

+ 2 - 2
server/server.yml

@@ -146,8 +146,8 @@
 
 # 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
-# can enable background notifications. Once enabled by the user, ntfy will forward published messages to the push
+# If enabled, allows ntfy to receive push notifications, even when the ntfy web app is closed. When enabled, users
+# 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.
 #
 # You must configure all settings below to enable Web Push.

+ 35 - 0
server/server_account_test.go

@@ -431,6 +431,41 @@ func TestAccount_Delete_Not_Allowed(t *testing.T) {
 	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) {
 	conf := newTestConfigWithAuthFile(t)
 	conf.EnableSignup = true

+ 8 - 2
server/server_webpush.go

@@ -120,7 +120,6 @@ func (s *Server) pruneAndNotifyWebPushSubscriptionsInternal() error {
 	}
 	payload, err := json.Marshal(newWebPushSubscriptionExpiringPayload())
 	if err != nil {
-		log.Tag(tagWebPush).Err(err).Warn("Unable to marshal expiring payload")
 		return err
 	}
 	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 {
 	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,
 		VAPIDPublicKey:  s.config.WebPushPublicKey,
 		VAPIDPrivateKey: s.config.WebPushPrivateKey,

+ 0 - 11
server/types.go

@@ -1,7 +1,6 @@
 package server
 
 import (
-	"github.com/SherClockHolmes/webpush-go"
 	"net/http"
 	"net/netip"
 	"time"
@@ -512,16 +511,6 @@ type webPushSubscription struct {
 	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 {
 	return map[string]any{
 		"web_push_subscription_id":       w.ID,

+ 6 - 2
server/webpush_store.go

@@ -63,8 +63,12 @@ const (
 		WHERE st.topic = ?
 		ORDER BY endpoint
 	`
-	selectWebPushSubscriptionsExpiringSoonQuery = `SELECT id, endpoint, key_auth, key_p256dh, user_id FROM subscription WHERE warned_at = 0 AND updated_at <= ?`
-	insertWebPushSubscriptionQuery              = `
+	selectWebPushSubscriptionsExpiringSoonQuery = `
+		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)
 		VALUES (?, ?, ?, ?, ?, ?, ?, ?)
 		ON CONFLICT (endpoint)