Add 'Firebase: no' header, closes #42

This commit is contained in:
Philipp Heckel 2021-12-09 12:15:17 -05:00
parent d6fbccab55
commit f966b2f9d7
3 changed files with 88 additions and 14 deletions

View file

@ -64,5 +64,14 @@ It looked something like this:
curl -d "$(hostname),$count,$time" ntfy.sh/results 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"
```

View file

@ -332,7 +332,14 @@ them with a comma, e.g. `tag1,tag2,tag3`.
<figcaption>Detail view of notifications with tags</figcaption> <figcaption>Detail view of notifications with tags</figcaption>
</figure> </figure>
## 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 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 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. 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'
]
]));
```

View file

@ -128,11 +128,6 @@ func New(conf *config.Config) (*Server, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
for _, t := range topics {
if firebaseSubscriber != nil {
t.Subscribe(firebaseSubscriber)
}
}
return &Server{ return &Server{
config: conf, config: conf,
cache: cache, cache: cache,
@ -284,13 +279,20 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, _ *visito
if m.Message == "" { if m.Message == "" {
return errHTTPBadRequest return errHTTPBadRequest
} }
title, priority, tags, cache := parseHeaders(r.Header) title, priority, tags, cache, firebase := parseHeaders(r.Header)
m.Title = title m.Title = title
m.Priority = priority m.Priority = priority
m.Tags = tags m.Tags = tags
if err := t.Publish(m); err != nil { if err := t.Publish(m); err != nil {
return err 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 cache {
if err := s.cache.AddMessage(m); err != nil { if err := s.cache.AddMessage(m); err != nil {
return err return err
@ -306,7 +308,7 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, _ *visito
return nil 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") title = readHeader(header, "x-title", "title", "ti", "t")
priorityStr := readHeader(header, "x-priority", "priority", "prio", "p") priorityStr := readHeader(header, "x-priority", "priority", "prio", "p")
if priorityStr != "" { if priorityStr != "" {
@ -333,7 +335,8 @@ func parseHeaders(header http.Header) (title string, priority int, tags []string
} }
} }
cache = readHeader(header, "x-cache", "cache") != "no" 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 { func readHeader(header http.Header, names ...string) string {
@ -512,9 +515,6 @@ func (s *Server) topicsFromIDs(ids ...string) ([]*topic, error) {
return nil, errHTTPTooManyRequests return nil, errHTTPTooManyRequests
} }
s.topics[id] = newTopic(id) s.topics[id] = newTopic(id)
if s.firebase != nil {
s.topics[id].Subscribe(s.firebase)
}
} }
topics = append(topics, s.topics[id]) 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()) log.Printf("cannot get stats for topic %s: %s", t.ID, err.Error())
continue 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) delete(s.topics, t.ID)
continue continue
} }