From f966b2f9d743b8b6ff45cdac999a04173dd41aab Mon Sep 17 00:00:00 2001 From: Philipp Heckel Date: Thu, 9 Dec 2021 12:15:17 -0500 Subject: [PATCH] Add 'Firebase: no' header, closes #42 --- docs/examples.md | 11 +++++++- docs/publish.md | 67 +++++++++++++++++++++++++++++++++++++++++++++++- server/server.go | 24 ++++++++--------- 3 files changed, 88 insertions(+), 14 deletions(-) diff --git a/docs/examples.md b/docs/examples.md index 1359331d..f7c85d70 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -64,5 +64,14 @@ It looked something like this: curl -d "$(hostname),$count,$time" ntfy.sh/results ``` +## Ansible, Salt and Puppet +You can easily integrate ntfy into Ansible, Salt, or Puppet to notify you when runs are done or are highstated. +One of my co-workers uses the following Ansible task to let him know when things are done: - +```yml +- name: Send ntfy.sh update + uri: + url: "https://ntfy.sh/{{ ntfy_channel }}" + method: POST + body: "{{ inventory_hostname }} reseeding complete" +``` diff --git a/docs/publish.md b/docs/publish.md index 4f86fe81..5ffaaf78 100644 --- a/docs/publish.md +++ b/docs/publish.md @@ -332,7 +332,14 @@ them with a comma, e.g. `tag1,tag2,tag3`.
Detail view of notifications with tags
-## Message caching +## Advanced features + +### Message caching +!!! info + If `Cache: no` is used, messages will only be delivered to connected subscribers, and won't be re-delivered if a + client re-connects. If a subscriber has (temporary) network issues or is reconnecting momentarily, + **messages might be missed**. + By default, the ntfy server caches messages on disk for 12 hours (see [message caching](config.md#message-cache)), so all messages you publish are stored server-side for a little while. The reason for this is to overcome temporary client-side network disruptions, but arguably this feature also may raise privacy concerns. @@ -385,3 +392,61 @@ are still delivered to connected subscribers, but [`since=`](subscribe/api.md#fe ] ])); ``` + +### Firebase +!!! info + If `Firebase: no` is used and [instant delivery](subscribe/phone.md#instant-delivery) isn't enabled in the Android + app (Google Play variant only), **message delivery will be significantly delayed (up to 15 minutes)**. To overcome + this delay, simply enable instant delivery. + +The ntfy server can be configured to use [Firebase Cloud Messaging (FCM)](https://firebase.google.com/docs/cloud-messaging) +(see [Firebase config](config.md#firebase-fcm)) for message delivery on Android (to minimize the app's battery footprint). +The ntfy.sh server is configured this way, meaning that all messages published to ntfy.sh are also published to corresponding +FCM topics. + +If you'd like to avoid forwarding messages to Firebase, you can set the `X-Firebase` header (or its alias: `Firebase`) +to `no`. This will instruct the server not to forward messages to Firebase. + +=== "Command line (curl)" + ``` + curl -H "X-Firebase: no" -d "This message won't be forwarded to FCM" ntfy.sh/mytopic + curl -H "Firebase: no" -d "This message won't be forwarded to FCM" ntfy.sh/mytopic + ``` + +=== "HTTP" + ``` http + POST /mytopic HTTP/1.1 + Host: ntfy.sh + Firebase: no + + This message won't be forwarded to FCM + ``` + +=== "JavaScript" + ``` javascript + fetch('https://ntfy.sh/mytopic', { + method: 'POST', + body: 'This message won't be forwarded to FCM', + headers: { 'Firebase': 'no' } + }) + ``` + +=== "Go" + ``` go + req, _ := http.NewRequest("POST", "https://ntfy.sh/mytopic", strings.NewReader("This message won't be forwarded to FCM")) + req.Header.Set("Firebase", "no") + http.DefaultClient.Do(req) + ``` + +=== "PHP" + ``` php-inline + file_get_contents('https://ntfy.sh/mytopic', false, stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'header' => + "Content-Type: text/plain\r\n" . + "Firebase: no", + 'content' => 'This message won't be stored server-side' + ] + ])); + ``` diff --git a/server/server.go b/server/server.go index 8b6c364d..7ee039c4 100644 --- a/server/server.go +++ b/server/server.go @@ -128,11 +128,6 @@ func New(conf *config.Config) (*Server, error) { if err != nil { return nil, err } - for _, t := range topics { - if firebaseSubscriber != nil { - t.Subscribe(firebaseSubscriber) - } - } return &Server{ config: conf, cache: cache, @@ -284,13 +279,20 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, _ *visito if m.Message == "" { return errHTTPBadRequest } - title, priority, tags, cache := parseHeaders(r.Header) + title, priority, tags, cache, firebase := parseHeaders(r.Header) m.Title = title m.Priority = priority m.Tags = tags if err := t.Publish(m); err != nil { return err } + if s.firebase != nil && firebase { + go func() { + if err := s.firebase(m); err != nil { + log.Printf("Unable to publish to Firebase: %v", err.Error()) + } + }() + } if cache { if err := s.cache.AddMessage(m); err != nil { return err @@ -306,7 +308,7 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, _ *visito return nil } -func parseHeaders(header http.Header) (title string, priority int, tags []string, cache bool) { +func parseHeaders(header http.Header) (title string, priority int, tags []string, cache bool, firebase bool) { title = readHeader(header, "x-title", "title", "ti", "t") priorityStr := readHeader(header, "x-priority", "priority", "prio", "p") if priorityStr != "" { @@ -333,7 +335,8 @@ func parseHeaders(header http.Header) (title string, priority int, tags []string } } cache = readHeader(header, "x-cache", "cache") != "no" - return title, priority, tags, cache + firebase = readHeader(header, "x-firebase", "firebase") != "no" + return title, priority, tags, cache, firebase } func readHeader(header http.Header, names ...string) string { @@ -512,9 +515,6 @@ func (s *Server) topicsFromIDs(ids ...string) ([]*topic, error) { return nil, errHTTPTooManyRequests } s.topics[id] = newTopic(id) - if s.firebase != nil { - s.topics[id].Subscribe(s.firebase) - } } topics = append(topics, s.topics[id]) } @@ -547,7 +547,7 @@ func (s *Server) updateStatsAndExpire() { log.Printf("cannot get stats for topic %s: %s", t.ID, err.Error()) continue } - if msgs == 0 && (subs == 0 || (s.firebase != nil && subs == 1)) { // Firebase is a subscriber! + if msgs == 0 && subs == 0 { delete(s.topics, t.ID) continue }