Docs, mostly

pull/751/head
binwiederhier 2023-06-16 16:55:42 -04:00
parent 6e95d62726
commit c43a1166e2
7 changed files with 59 additions and 64 deletions

View File

@ -1333,7 +1333,6 @@ 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-enabled` | `NTFY_WEB_PUSH_ENABLED` | *boolean* (`true` or `false`) | - | Web Push: Enable/disable (requires private and public key below). |
| `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 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-private-key` | `NTFY_WEB_PUSH_PRIVATE_KEY` | *string* | - | Web Push: Private Key. Run `ntfy webpush generate-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 |
@ -1430,7 +1429,6 @@ OPTIONS:
--enable-metrics, --enable_metrics if set, Prometheus metrics are exposed via the /metrics endpoint (default: false) [$NTFY_ENABLE_METRICS] --enable-metrics, --enable_metrics if set, Prometheus metrics are exposed via the /metrics endpoint (default: false) [$NTFY_ENABLE_METRICS]
--metrics-listen-http value, --metrics_listen_http value ip:port used to expose the metrics endpoint (implicitly enables metrics) [$NTFY_METRICS_LISTEN_HTTP] --metrics-listen-http value, --metrics_listen_http value ip:port used to expose the metrics endpoint (implicitly enables metrics) [$NTFY_METRICS_LISTEN_HTTP]
--profile-listen-http value, --profile_listen_http value ip:port used to expose the profiling endpoints (implicitly enables profiling) [$NTFY_PROFILE_LISTEN_HTTP] --profile-listen-http value, --profile_listen_http value ip:port used to expose the profiling endpoints (implicitly enables profiling) [$NTFY_PROFILE_LISTEN_HTTP]
--web-push-enabled, --web_push_enabled enable web push (requires public and private key) (default: false) [$NTFY_WEB_PUSH_ENABLED]
--web-push-public-key value, --web_push_public_key value public key used for web push notifications [$NTFY_WEB_PUSH_PUBLIC_KEY] --web-push-public-key value, --web_push_public_key value public key used for web push notifications [$NTFY_WEB_PUSH_PUBLIC_KEY]
--web-push-private-key value, --web_push_private_key value private key used for web push notifications [$NTFY_WEB_PUSH_PRIVATE_KEY] --web-push-private-key value, --web_push_private_key value private key used for web push notifications [$NTFY_WEB_PUSH_PRIVATE_KEY]
--web-push-subscriptions-file value, --web_push_subscriptions_file value file used to store web push subscriptions [$NTFY_WEB_PUSH_SUBSCRIPTIONS_FILE] --web-push-subscriptions-file value, --web_push_subscriptions_file value file used to store web push subscriptions [$NTFY_WEB_PUSH_SUBSCRIPTIONS_FILE]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

After

Width:  |  Height:  |  Size: 96 KiB

View File

@ -1,20 +1,17 @@
# 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) 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. 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.
This gives it its own launcher (e.g. shortcut on Windows, app on macOS, launcher shortcut on Linux) own window, To install and register the web app in your operating system, click the "install app" icon in your browser (usually next to the
push notifications, and an app badge with the unread notification count. address bar). To receive background notifications, **either the browser or the installed web app must be open**.
To receive background notifications, either the browser or the installed web app must be open.
<!-- TODO: (Q4 2023) Safari 17 / macOS 14 Sonoma supports installable PWAs too --> <!-- TODO: (Q4 2023) Safari 17 / macOS 14 Sonoma supports installable PWAs too -->
Web app installation is supported on Chrome and Edge on desktop, as well as Chrome on Android and Safari on iOS. Web app installation is supported on Chrome and Edge on desktop, as well as Chrome on Android and Safari on iOS.
Look at the [compatibility table](https://caniuse.com/web-app-manifest) for more info. Look at the [compatibility table](https://caniuse.com/web-app-manifest) for more info.
<div id="pwa-screenshots" class="screenshots"> <div id="pwa-screenshots" class="screenshots">
<a href="../../static/img/pwa.png"><img src="../../static/img/pwa.png"/></a> <a href="../../static/img/pwa.png"><img src="../../static/img/pwa.png"/></a>
<a href="../../static/img/pwa-install.png"><img src="../../static/img/pwa-install.png"/></a> <a href="../../static/img/pwa-install.png"><img src="../../static/img/pwa-install.png"/></a>

View File

@ -1,49 +1,21 @@
# Subscribe from the Web UI # Subscribe from the web app
The web app lets you subscribe and publish messages to ntfy topics. For ntfy.sh, the web app is available at [ntfy.sh/app](https://ntfy.sh/app).
To subscribe, simply type in the topic name and click the *Subscribe* button. **After subscribing, messages published to the topic
will appear in the web app, and pop up as a notification.**
You can use the Web UI to subscribe to topics as well. Simply type in the topic name and click the *Subscribe* button. <div id="subscribe-screenshots" class="screenshots">
<a href="../../static/img/web-subscribe.png"><img src="../../static/img/web-subscribe.png"/></a>
While subscribing, you have the option to enable background notifications on supported browsers. </div>
- If background notifications are off:
This requires an active ntfy tab to be open to receive notifications. These are typically instantaneous, and will
appear as a system notification. If you don't see these, check that your browser is allowed to show notifications
(for example in System Settings on macOS).
If you don't want to enable background notifications, pinning the ntfy tab on your browser is a good solution to leave
it running.
- If background notifications are on:
This uses the [Web Push API](https://caniuse.com/push-api). You don't need an active ntfy tab open, but in some
cases you may need to keep your browser open.
Background notifications are only supported on the same server hosting the web app. You cannot use another server,
but can instead subscribe on the other server itself.
If the ntfy app is not opened for more than a week, background notifications will be paused. You can resume them
by opening the app again, and will get a warning notification before they are paused.
| Browser | Platform | Browser Running | Browser Not Running | Restrictions |
| ------- | -------- | --------------- | ------------------- | ------------------------------------------------------- |
| Chrome | Desktop | ✅ | ❌ | |
| Firefox | Desktop | ✅ | ❌ | |
| Edge | Desktop | ✅ | ❌ | |
| Opera | Desktop | ✅ | ❌ | |
| Safari | Desktop | ✅ | ✅ | requires Safari 16.1, macOS 13 Ventura |
| Chrome | Android | ✅ | ✅ | |
| Safari | iOS | ⚠️ | ⚠️ | requires iOS 16.4, only when app is added to homescreen |
(Browsers below 1% usage not shown, look at the [Push API](https://caniuse.com/push-api) for more info)
## Publish messages
To learn how to send messages, check out the [publishing page](../publish.md). To learn how to send messages, check out the [publishing page](../publish.md).
<div id="web-screenshots" class="screenshots"> <div id="web-screenshots" class="screenshots">
<a href="../../static/img/web-detail.png"><img src="../../static/img/web-detail.png"/></a> <a href="../../static/img/web-detail.png"><img src="../../static/img/web-detail.png"/></a>
<a href="../../static/img/web-notification.png"><img src="../../static/img/web-notification.png"/></a> <a href="../../static/img/web-notification.png"><img src="../../static/img/web-notification.png"/></a>
<a href="../../static/img/web-subscribe.png"><img src="../../static/img/web-subscribe.png"/></a>
</div> </div>
## Topic reservations
If topic reservations are enabled, you can claim ownership over topics and define access to it: If topic reservations are enabled, you can claim ownership over topics and define access to it:
<div id="reserve-screenshots" class="screenshots"> <div id="reserve-screenshots" class="screenshots">
@ -51,9 +23,29 @@ If topic reservations are enabled, you can claim ownership over topics and defin
<a href="../../static/img/web-reserve-topic-dialog.png"><img src="../../static/img/web-reserve-topic-dialog.png"/></a> <a href="../../static/img/web-reserve-topic-dialog.png"><img src="../../static/img/web-reserve-topic-dialog.png"/></a>
</div> </div>
You can set your default choice for new subscriptions (for example synced account subscriptions and the default toggle state) ## Background notifications
in the settings page: While subscribing, you have the option to enable background notifications on supported browsers (see "Settings" tab).
<div id="push-settings-screenshots" class="screenshots"> **If background notifications are off (default):** This requires an active ntfy tab to be open to receive notifications.
<a href="../../static/img/web-push-settings.png"><img src="../../static/img/web-push-settings.png"/></a> These are typically instantaneous, and will appear as a system notification. If you don't see these, check that your browser
</div> is allowed to show notifications (for example in System Settings on macOS). If you don't want to enable background notifications,
**pinning the ntfy tab on your browser** is a good solution to leave it running.
**If background notifications are on:** This uses the [Web Push API](https://caniuse.com/push-api). You don't need an active
ntfy tab open, but in some cases you may need to keep your browser open. Background notifications are only supported on the
same server hosting the web app. You cannot use another server, but can instead subscribe on the other server itself.
If the ntfy app is not opened for more than a week, background notifications will be paused. You can resume them
by opening the app again, and will get a warning notification before they are paused.
| Browser | Platform | Browser Running | Browser Not Running | Restrictions |
|---------|----------|-----------------|---------------------|---------------------------------------------------------|
| Chrome | Desktop | ✅ | ❌ | |
| Firefox | Desktop | ✅ | ❌ | |
| Edge | Desktop | ✅ | ❌ | |
| Opera | Desktop | ✅ | ❌ | |
| Safari | Desktop | ✅ | ✅ | requires Safari 16.1, macOS 13 Ventura |
| Chrome | Android | ✅ | ✅ | |
| Safari | iOS | ⚠️ | ⚠️ | requires iOS 16.4, only when app is added to homescreen |
(Browsers below 1% usage not shown, look at the [Push API](https://caniuse.com/push-api) for more info)

View File

@ -79,18 +79,18 @@ func (s *Server) handleWebPushDelete(w http.ResponseWriter, r *http.Request, _ *
func (s *Server) publishToWebPushEndpoints(v *visitor, m *message) { func (s *Server) publishToWebPushEndpoints(v *visitor, m *message) {
subscriptions, err := s.webPush.SubscriptionsForTopic(m.Topic) subscriptions, err := s.webPush.SubscriptionsForTopic(m.Topic)
if err != nil { if err != nil {
logvm(v, m).Err(err).Warn("Unable to publish web push messages") logvm(v, m).Err(err).With(v, m).Warn("Unable to publish web push messages")
return return
} }
log.Tag(tagWebPush).With(v, m).Debug("Publishing web push message to %d subscribers", len(subscriptions))
payload, err := json.Marshal(newWebPushPayload(fmt.Sprintf("%s/%s", s.config.BaseURL, m.Topic), m)) payload, err := json.Marshal(newWebPushPayload(fmt.Sprintf("%s/%s", s.config.BaseURL, m.Topic), m))
if err != nil { if err != nil {
log.Tag(tagWebPush).Err(err).Warn("Unable to marshal expiring payload") log.Tag(tagWebPush).Err(err).With(v, m).Warn("Unable to marshal expiring payload")
return return
} }
for _, subscription := range subscriptions { for _, subscription := range subscriptions {
ctx := log.Context{"endpoint": subscription.Endpoint, "username": subscription.UserID, "topic": m.Topic, "message_id": m.ID} if err := s.sendWebPushNotification(subscription, payload, v, m); err != nil {
if err := s.sendWebPushNotification(payload, subscription, &ctx); err != nil { log.Tag(tagWebPush).Err(err).With(v, m, subscription).Warn("Unable to publish web push message")
log.Tag(tagWebPush).Err(err).Fields(ctx).Warn("Unable to publish web push message")
} }
} }
} }
@ -125,9 +125,8 @@ func (s *Server) pruneAndNotifyWebPushSubscriptionsInternal() error {
} }
warningSent := make([]*webPushSubscription, 0) warningSent := make([]*webPushSubscription, 0)
for _, subscription := range subscriptions { for _, subscription := range subscriptions {
ctx := log.Context{"endpoint": subscription.Endpoint} if err := s.sendWebPushNotification(subscription, payload); err != nil {
if err := s.sendWebPushNotification(payload, subscription, &ctx); err != nil { log.Tag(tagWebPush).Err(err).With(subscription).Warn("Unable to publish expiry imminent warning")
log.Tag(tagWebPush).Err(err).Fields(ctx).Warn("Unable to publish expiry imminent warning")
continue continue
} }
warningSent = append(warningSent, subscription) warningSent = append(warningSent, subscription)
@ -139,7 +138,8 @@ func (s *Server) pruneAndNotifyWebPushSubscriptionsInternal() error {
return nil return nil
} }
func (s *Server) sendWebPushNotification(message []byte, sub *webPushSubscription, ctx *log.Context) 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{ resp, err := webpush.SendNotification(message, sub.ToSubscription(), &webpush.Options{
Subscriber: s.config.WebPushEmailAddress, Subscriber: s.config.WebPushEmailAddress,
VAPIDPublicKey: s.config.WebPushPublicKey, VAPIDPublicKey: s.config.WebPushPublicKey,
@ -148,18 +148,18 @@ func (s *Server) sendWebPushNotification(message []byte, sub *webPushSubscriptio
TTL: int(s.config.CacheDuration.Seconds()), TTL: int(s.config.CacheDuration.Seconds()),
}) })
if err != nil { if err != nil {
log.Tag(tagWebPush).Err(err).Fields(*ctx).Debug("Unable to publish web push message, removing endpoint") log.Tag(tagWebPush).With(sub).With(contexters...).Err(err).Debug("Unable to publish web push message, removing endpoint")
if err := s.webPush.RemoveSubscriptionsByEndpoint(sub.Endpoint); err != nil { if err := s.webPush.RemoveSubscriptionsByEndpoint(sub.Endpoint); err != nil {
return err return err
} }
return err return err
} }
if (resp.StatusCode < 200 || resp.StatusCode > 299) && resp.StatusCode != 429 { if (resp.StatusCode < 200 || resp.StatusCode > 299) && resp.StatusCode != 429 {
log.Tag(tagWebPush).Fields(*ctx).Field("response_code", resp.StatusCode).Debug("Unable to publish web push message, unexpected response") log.Tag(tagWebPush).With(sub).With(contexters...).Field("response_code", resp.StatusCode).Debug("Unable to publish web push message, unexpected response")
if err := s.webPush.RemoveSubscriptionsByEndpoint(sub.Endpoint); err != nil { if err := s.webPush.RemoveSubscriptionsByEndpoint(sub.Endpoint); err != nil {
return err return err
} }
return errHTTPInternalErrorWebPushUnableToPublish.Fields(*ctx) return errHTTPInternalErrorWebPushUnableToPublish.With(sub).With(contexters...)
} }
return nil return nil
} }

View File

@ -521,3 +521,11 @@ func (w *webPushSubscription) ToSubscription() *webpush.Subscription {
}, },
} }
} }
func (w *webPushSubscription) Context() log.Context {
return map[string]any{
"web_push_subscription_id": w.ID,
"web_push_subscription_user_id": w.UserID,
"web_push_subscription_endpoint": w.Endpoint,
}
}