Minor changes

pull/751/head
binwiederhier 2023-06-17 21:51:04 -04:00
parent 30a8f66db2
commit 020996ea04
7 changed files with 61 additions and 26 deletions

View File

@ -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
@ -816,7 +816,8 @@ 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 |

View File

@ -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**.

View File

@ -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.

View File

@ -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

View File

@ -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,

View File

@ -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,

View File

@ -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)