Docs, mostly
parent
6e95d62726
commit
c43a1166e2
|
@ -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 |
|
@ -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>
|
||||||
|
|
|
@ -1,31 +1,45 @@
|
||||||
# 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>
|
||||||
|
</div>
|
||||||
|
|
||||||
While subscribing, you have the option to enable background notifications on supported browsers.
|
## Publish messages
|
||||||
|
To learn how to send messages, check out the [publishing page](../publish.md).
|
||||||
|
|
||||||
- If background notifications are off:
|
<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-notification.png"><img src="../../static/img/web-notification.png"/></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
This requires an active ntfy tab to be open to receive notifications. These are typically instantaneous, and will
|
## Topic reservations
|
||||||
appear as a system notification. If you don't see these, check that your browser is allowed to show notifications
|
If topic reservations are enabled, you can claim ownership over topics and define access to it:
|
||||||
(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
|
<div id="reserve-screenshots" class="screenshots">
|
||||||
it running.
|
<a href="../../static/img/web-reserve-topic.png"><img src="../../static/img/web-reserve-topic.png"/></a>
|
||||||
|
<a href="../../static/img/web-reserve-topic-dialog.png"><img src="../../static/img/web-reserve-topic-dialog.png"/></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
- If background notifications are on:
|
## Background notifications
|
||||||
|
While subscribing, you have the option to enable background notifications on supported browsers (see "Settings" tab).
|
||||||
|
|
||||||
This uses the [Web Push API](https://caniuse.com/push-api). You don't need an active ntfy tab open, but in some
|
**If background notifications are off (default):** This requires an active ntfy tab to be open to receive notifications.
|
||||||
cases you may need to keep your browser open.
|
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.
|
||||||
|
|
||||||
Background notifications are only supported on the same server hosting the web app. You cannot use another server,
|
**If background notifications are on:** This uses the [Web Push API](https://caniuse.com/push-api). You don't need an active
|
||||||
but can instead subscribe on the other server itself.
|
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
|
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.
|
by opening the app again, and will get a warning notification before they are paused.
|
||||||
|
|
||||||
| Browser | Platform | Browser Running | Browser Not Running | Restrictions |
|
| Browser | Platform | Browser Running | Browser Not Running | Restrictions |
|
||||||
| ------- | -------- | --------------- | ------------------- | ------------------------------------------------------- |
|
|---------|----------|-----------------|---------------------|---------------------------------------------------------|
|
||||||
| Chrome | Desktop | ✅ | ❌ | |
|
| Chrome | Desktop | ✅ | ❌ | |
|
||||||
| Firefox | Desktop | ✅ | ❌ | |
|
| Firefox | Desktop | ✅ | ❌ | |
|
||||||
| Edge | Desktop | ✅ | ❌ | |
|
| Edge | Desktop | ✅ | ❌ | |
|
||||||
|
@ -35,25 +49,3 @@ While subscribing, you have the option to enable background notifications on sup
|
||||||
| Safari | iOS | ⚠️ | ⚠️ | requires iOS 16.4, only when app is added to homescreen |
|
| 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)
|
(Browsers below 1% usage not shown, look at the [Push API](https://caniuse.com/push-api) for more info)
|
||||||
|
|
||||||
To learn how to send messages, check out the [publishing page](../publish.md).
|
|
||||||
|
|
||||||
<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-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>
|
|
||||||
|
|
||||||
If topic reservations are enabled, you can claim ownership over topics and define access to it:
|
|
||||||
|
|
||||||
<div id="reserve-screenshots" class="screenshots">
|
|
||||||
<a href="../../static/img/web-reserve-topic.png"><img src="../../static/img/web-reserve-topic.png"/></a>
|
|
||||||
<a href="../../static/img/web-reserve-topic-dialog.png"><img src="../../static/img/web-reserve-topic-dialog.png"/></a>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
You can set your default choice for new subscriptions (for example synced account subscriptions and the default toggle state)
|
|
||||||
in the settings page:
|
|
||||||
|
|
||||||
<div id="push-settings-screenshots" class="screenshots">
|
|
||||||
<a href="../../static/img/web-push-settings.png"><img src="../../static/img/web-push-settings.png"/></a>
|
|
||||||
</div>
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue