diff --git a/.gitignore b/.gitignore index a88775f3..c777cdf8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ dist/ .idea/ +site/ *.iml diff --git a/config/config.yml b/config/config.yml index 210df071..695d41d2 100644 --- a/config/config.yml +++ b/config/config.yml @@ -6,7 +6,7 @@ # listen-http: ":80" # If set, also publish messages to a Firebase Cloud Messaging (FCM) topic for your app. -# This is optional and only required to support Android apps (which don't allow background services anymore). +# This is optional and only required to save battery when using the Android app. # # firebase-key-file: @@ -23,6 +23,8 @@ # Interval in which keepalive messages are sent to the client. This is to prevent # intermediaries closing the connection for inactivity. # +# Note that the Android app has a hardcoded timeout at 77s, so it should be less than that. +# # keepalive-interval: 30s # Interval in which the manager prunes old messages, deletes topics diff --git a/docs/config.md b/docs/config.md new file mode 100644 index 00000000..96196a5e --- /dev/null +++ b/docs/config.md @@ -0,0 +1,108 @@ +# Configuring the ntfy server +The ntfy server can be configured in three ways: using a config file (typically at `/etc/ntfy/config.yml`, +see [config.yml](https://github.com/binwiederhier/ntfy/blob/main/config/config.yml)), via command line arguments +or using environment variables. + +## Quick start +By default, simply running `ntfy` will start the server at port 80. No configuration needed. Batteries included 😀. +If everything works as it should, you'll see something like this: +``` +$ ntfy +2021/11/30 19:59:08 Listening on :80 +``` + +You can immediately start [publishing messages](publish/index.md), or subscribe via the [Android app](subscribe/phone.md), +[the web UI](subscribe/web.md), or simply via [curl or your favorite HTTP client](subscribe/api.md). To configure +the server further, check out the [config options table](#config-options) or simply type `ntfy --help` to +get a list of [command line options](#command-line-options). + +## Config options +Each config options can be set in the config file `/etc/ntfy/config.yml` (e.g. `listen-http: :80`) or as a +CLI option (e.g. `--listen-http :80`. Here's a list of all available options. Alternatively, you can set an environment +variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`). + +| Config option | Env variable | Format | Default | Description | +|---|---|---|---|---| +| `listen-http` | `NTFY_LISTEN_HTTP` | `[host]:port` | `:80` | Listen address for the HTTP web server | +| `firebase-key-file` | `NTFY_FIREBASE_KEY_FILE` | *filename* | - | If set, also publish messages to a Firebase Cloud Messaging (FCM) topic for your app. This is optional and only required to save battery when using the Android app. | +| `cache-file` | `NTFY_CACHE_FILE` | *filename* | - | If set, messages are cached in a local SQLite database instead of only in-memory. This allows for service restarts without losing messages in support of the since= parameter. | +| `cache-duration` | `NTFY_CACHE_DURATION` | *duration* | 12h | Duration for which messages will be buffered before they are deleted. This is required to support the `since=...` and `poll=1` parameter. | +| `keepalive-interval` | `NTFY_KEEPALIVE_INTERVAL` | *duration* | 30s | Interval in which keepalive messages are sent to the client. This is to prevent intermediaries closing the connection for inactivity. Note that the Android app has a hardcoded timeout at 77s, so it should be less than that. | +| `manager-interval` | `$NTFY_MANAGER_INTERVAL` | *duration* | 1m | Interval in which the manager prunes old messages, deletes topics and prints the stats. | +| `global-topic-limit` | `NTFY_GLOBAL_TOPIC_LIMIT` | *number* | 5000 | Rate limiting: Total number of topics before the server rejects new topics. | +| `visitor-subscription-limit` | `NTFY_VISITOR_SUBSCRIPTION_LIMIT` | *number* | 30 | Rate limiting: Number of subscriptions per visitor (IP address) | +| `visitor-request-limit-burst` | `NTFY_VISITOR_REQUEST_LIMIT_BURST` | *number* | 60 | Allowed GET/PUT/POST requests per second, per visitor. This setting is the initial bucket of requests each visitor has | +| `visitor-request-limit-replenish` | `NTFY_VISITOR_REQUEST_LIMIT_REPLENISH` | *duration* | 10s | Strongly related to `visitor-request-limit-burst`: The rate at which the bucket is refilled | +| `behind-proxy` | `NTFY_BEHIND_PROXY` | *bool* | false | If set, the X-Forwarded-For header is used to determine the visitor IP address instead of the remote address of the connection. | + +The format for a *duration* is: `(smh)`, e.g. 30s, 20m or 1h. + +## Firebase (FCM) +!!! info + Using Firebase is **optional** and only works if you modify and build your own Android .apk. + For a self-hosted instance, it's easier to just not bother with FCM. + +[Firebase Cloud Messaging (FCM)](https://firebase.google.com/docs/cloud-messaging) is the Google approved way to send +push messages to Android devices. FCM is the only method that an Android app can receive messages without having to run a +[foreground service](https://developer.android.com/guide/components/foreground-services). + +For the main host [ntfy.sh](https://ntfy.sh), the [ntfy Android App](subscribe/phone.md) uses Firebase to send messages +to the device. For other hosts, instant delivery is used and FCM is not involved. + +To configure FCM for your self-hosted instance of the ntfy server, follow these steps: + +1. Sign up for a [Firebase account](https://console.firebase.google.com/) +2. Create an app and download the key file (e.g. `myapp-firebase-adminsdk-ahnce-....json`) +3. Place the key file in `/etc/ntfy`, set the `firebase-key-file` in `config.yml` accordingly and restart the ntfy server +4. Build your own Android .apk following [these instructions]() + +Example: +``` +# If set, also publish messages to a Firebase Cloud Messaging (FCM) topic for your app. +# This is optional and only required to support Android apps (which don't allow background services anymore). +# +firebase-key-file: "/etc/ntfy/ntfy-sh-firebase-adminsdk-ahnce-9f4d6f14b5.json" +``` + +## Behind a proxy (TLS, etc.) + +!! warn +If you are behind a proxy, you must set the `behind-proxy` flag. Otherwise all visitors are rate limited +as if they are one. + + +## Rate limiting +Rate limiting: Allowed GET/PUT/POST requests per second, per visitor: +- visitor-request-limit-burst is the initial bucket of requests each visitor has +- visitor-request-limit-replenish is the rate at which the bucket is refilled + + +## Command line options +``` +$ ntfy --help +NAME: + ntfy - Simple pub-sub notification service + +USAGE: + ntfy [OPTION..] + +GLOBAL OPTIONS: + --config value, -c value config file (default: /etc/ntfy/config.yml) [$NTFY_CONFIG_FILE] + --listen-http value, -l value ip:port used to as listen address (default: ":80") [$NTFY_LISTEN_HTTP] + --firebase-key-file value, -F value Firebase credentials file; if set additionally publish to FCM topic [$NTFY_FIREBASE_KEY_FILE] + --cache-file value, -C value cache file used for message caching [$NTFY_CACHE_FILE] + --cache-duration since, -b since buffer messages for this time to allow since requests (default: 12h0m0s) [$NTFY_CACHE_DURATION] + --keepalive-interval value, -k value interval of keepalive messages (default: 30s) [$NTFY_KEEPALIVE_INTERVAL] + --manager-interval value, -m value interval of for message pruning and stats printing (default: 1m0s) [$NTFY_MANAGER_INTERVAL] + --global-topic-limit value, -T value total number of topics allowed (default: 5000) [$NTFY_GLOBAL_TOPIC_LIMIT] + --visitor-subscription-limit value, -V value number of subscriptions per visitor (default: 30) [$NTFY_VISITOR_SUBSCRIPTION_LIMIT] + --visitor-request-limit-burst value, -B value initial limit of requests per visitor (default: 60) [$NTFY_VISITOR_REQUEST_LIMIT_BURST] + --visitor-request-limit-replenish value, -R value interval at which burst limit is replenished (one per x) (default: 10s) [$NTFY_VISITOR_REQUEST_LIMIT_REPLENISH] + --behind-proxy, -P if set, use X-Forwarded-For header to determine visitor IP address (for rate limiting) (default: false) [$NTFY_BEHIND_PROXY] + +Try 'ntfy COMMAND --help' for more information. + +ntfy v1.4.8 (7b8185c), runtime go1.17, built at 1637872539 +Copyright (C) 2021 Philipp C. Heckel, distributed under the Apache License 2.0 +``` + diff --git a/docs/develop.md b/docs/develop.md new file mode 100644 index 00000000..1d5b2a72 --- /dev/null +++ b/docs/develop.md @@ -0,0 +1,5 @@ +# Building + +## ntfy server + +## Android app diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 00000000..df635b4e --- /dev/null +++ b/docs/examples.md @@ -0,0 +1 @@ +# Examples diff --git a/docs/faq.md b/docs/faq.md new file mode 100644 index 00000000..947fcce6 --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,47 @@ +# Frequently asked questions (FAQ) + +## Isn't this like ...? +Who knows. I didn't do a lot of research before making this. It was fun making it. + +## Can I use this in my app? Will it stay free? +Yes. As long as you don't abuse it, it'll be available and free of charge. I do not plan on monetizing +the service. + +## What are the uptime guarantees? +Best effort. + +## What happens if there are multiple subscribers to the same topic? +As per usual with pub-sub, all subscribers receive notifications if they are +subscribed to a topic. + +## Will you know what topics exist, can you spy on me? +If you don't trust me or your messages are sensitive, run your own server. It's open source. +That said, the logs do not contain any topic names or other details about you. +Messages are cached for the duration configured in `config.yml` (12h by default) to facilitate service restarts, message polling and to overcome +client network disruptions. + +## Can I self-host it? +Yes. The server (including this Web UI) can be self-hosted, and the Android app supports adding topics from +your own server as well. There are install instructions +on GitHub. + +## Why is Firebase used? +In addition to caching messages locally and delivering them to long-polling subscribers, all messages are also +published to Firebase Cloud Messaging (FCM) (if `FirebaseKeyFile` is set, which it is on ntfy.sh). This +is to facilitate instant notifications on Android. +

+ +## How much battery does the Android app use? +If you use the ntfy.sh server and you don't use the instant delivery feature, the Android app uses no +additional battery, since Firebase Cloud Messaging (FCM) is used. If you use your own server, or you use +instant delivery, the app has to maintain a constant connection to the server, which consumes about 4% of +battery in 17h of use (on my phone). I use it and it makes no difference to me. + +## What is instant delivery? +Instant delivery is a feature in the Android app. If turned on, the app maintains a constant connection to the +server and listens for incoming notifications. This consumes additional battery, +but delivers notifications instantly. + +## Why is there no iOS app (yet)? +I don't have an iPhone or a Mac, so I didn't make an iOS app yet. It'd be awesome if +someone else could help out. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..bf6d425a --- /dev/null +++ b/docs/index.md @@ -0,0 +1,10 @@ +# ntfy.sh | simple HTTP-based pub-sub + +**ntfy** (pronounce: *notify*) is a simple HTTP-based [pub-sub](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) +notification service. It allows you to send notifications to your phone or desktop via scripts from any computer, +entirely **without signup, cost or setup**. It's also [open source](https://github.com/binwiederhier/ntfy) if you want +to run your own. + +(pub sub diagram) + +(screenshot / video / gif) diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 00000000..6da2e179 --- /dev/null +++ b/docs/install.md @@ -0,0 +1,94 @@ +# Install your own ntfy server +The following steps are only required if you want to **self-host your own ntfy server**. If you just want to +[send messages using ntfy.sh](publish/index.md), you don't need to install anything. Just use `curl` +or your favorite HTTP client. + +## General steps +The ntfy server comes as a statically linked binary and is shipped as tarball, deb/rpm packages and as a Docker image. +We support amd64, armv7 and arm64. + +1. Install ntfy using one of the methods described below +2. Then (optionally) edit `/etc/ntfy/config.yml` (see [configuration](config.md)) +3. Then just run it with `ntfy` (or `systemctl start ntfy` when using the deb/rpm). + + +## Binaries and packages +Please check out the [releases page](https://github.com/binwiederhier/ntfy/releases) for binaries and +deb/rpm packages. + +x86_64/amd64: +``` +wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_x86_64.tar.gz +sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy +``` + +armv7: +``` +wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_armv7.tar.gz +sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy +``` + +arm64/v8: +``` +wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_linux_arm64.tar.gz +sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy +``` + +## Debian/Ubuntu repository +Installation via Debian repository: +```bash +curl -sSL https://archive.heckel.io/apt/pubkey.txt | sudo apt-key add - +sudo apt install apt-transport-https +sudo sh -c "echo 'deb [arch=amd64] https://archive.heckel.io/apt debian main' > /etc/apt/sources.list.d/archive.heckel.io.list" +sudo apt update +sudo apt install ntfy +``` + +Manually installing the .deb file: +```bash +wget https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_amd64.deb +dpkg -i ntfy_1.5.0_amd64.deb +``` + +## Fedora/RHEL/CentOS +```bash +rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.5.0/ntfy_1.5.0_amd64.rpm +``` + +## Docker +The ntfy server exposes its web UI and the API on port 80, so you need to expose that in Docker. To use the persistent +message cache, you also need to map a volume to `/var/cache/ntfy`. To change other settings, you should map `/etc/ntfy`, +so you can edit `/etc/ntfy/config.yml`. + +Basic usage (no cache or additional config): +``` +docker run -p 80:80 -it binwiederhier/ntfy +``` + +With persistent cache (configured as command line arguments): +```bash +docker run \ + -v /var/cache/ntfy:/var/cache/ntfy \ + -p 80:80 \ + -it \ + binwiederhier/ntfy \ + --cache-file /var/cache/ntfy/cache.db +``` + +With other config options (configured via `/etc/ntfy/config.yml`, see [configuration](config.md) for details): +```bash +docker run \ + -v /etc/ntfy:/etc/ntfy \ + -p 80:80 \ + -it \ + binwiederhier/ntfy +``` + +## Go +To install via Go, simply run: +```bash +go install heckel.io/ntfy@latest +``` +!!! info + Please [let me know](https://github.com/binwiederhier/ntfy/issues) if there are any issues with this installation + method. The SQLite bindings require CGO and it works for me, but I have the feeling it may not work for everyone. diff --git a/docs/publish/index.md b/docs/publish/index.md new file mode 100644 index 00000000..986dec3c --- /dev/null +++ b/docs/publish/index.md @@ -0,0 +1,207 @@ +# Publishing + +Publishing messages can be done via PUT or POST. Topics are created on the fly by subscribing or publishing to them. +Because there is no sign-up, the topic is essentially a password, so pick something that's not easily guessable. + +Here's an example showing how to publish a simple message using a POST request: +=== "Command line (curl)" + ``` + curl -d "Backup successful 😀" ntfy.sh/mytopic + ``` + +=== "HTTP" + ``` http + POST /mytopic HTTP/1.1 + Host: ntfy.sh + + Backup successful 😀 + ``` +=== "JavaScript" + ``` javascript + fetch('https://ntfy.sh/mytopic', { + method: 'POST', // PUT works too + body: 'Backup successful 😀' + }) + ``` + +=== "Go" + ``` go + http.Post("https://ntfy.sh/mytopic", "text/plain", + strings.NewReader("Backup successful 😀")) + ``` + +=== "PHP" + ``` php + file_get_contents('https://ntfy.sh/mytopic', false, stream_context_create([ + 'http' => [ + 'method' => 'POST', // PUT also works + 'header' => 'Content-Type: text/plain', + 'content' => 'Backup successful 😀' + ] + ])); + ``` + +If you have the [Android app](../subscribe/phone.md) installed on your phone, this will create a notification that looks like this: + +
+ ![basic notification](../static/img/basic-notification.png){ width=500 } +
Android notification
+
+ +There are more features related to publishing messages: You can set a [notification priority](#message-priority), +a [title](#message-title), and [tag messages](#tags-emojis) 🥳 🎉. Here's an example that uses all of them at once: + +=== "Command line (curl)" + ``` + curl \ + -H "Title: Unauthorized access detected" \ + -H "Priority: urgent" \ + -H "Tags: warning,skull" \ + -d "Remote access to phils-laptop detected. Act right away." \ + ntfy.sh/phil_alerts + ``` + +=== "HTTP" + ``` http + POST /phil_alerts HTTP/1.1 + Host: ntfy.sh + Title: Unauthorized access detected + Priority: urgent + Tags: warning,skull + + Remote access to phils-laptop detected. Act right away. + ``` + +=== "JavaScript" + ``` javascript + fetch('https://ntfy.sh/phil_alerts', { + method: 'POST', // PUT works too + body: 'Remote access to phils-laptop detected. Act right away.', + headers: { + 'Title': 'Unauthorized access detected', + 'Priority': 'urgent', + 'Tags': 'warning,skull' + } + }) + ``` + +=== "Go" + ``` go + req, _ := http.NewRequest("POST", "https://ntfy.sh/phil_alerts", + strings.NewReader("Remote access to phils-laptop detected. Act right away.")) + req.Header.Set("Title", "Unauthorized access detected") + req.Header.Set("Priority", "urgent") + req.Header.Set("Tags", "warning,skull") + http.DefaultClient.Do(req) + ``` + +=== "PHP" + ``` php + file_get_contents('https://ntfy.sh/phil_alerts', false, stream_context_create([ + 'http' => [ + 'method' => 'POST', // PUT also works + 'header' => + "Content-Type: text/plain\r\n" . + "Title: Unauthorized access detected\r\n" . + "Priority: urgent\r\n" . + "Tags: warning,skull", + 'content' => 'Remote access to phils-laptop detected. Act right away.' + ] + ])); + ``` + +
+ ![priority notification](../static/img/priority-notification.png){ width=500 } +
Urgent notification with tags and title
+
+ +## Message priority +All messages have a priority, which defines how urgently your phone notifies you. You can set custom +notification sounds and vibration patterns on your phone to map to these priorities (see [Android config](../subscribe/phone.md)). + +The following priorities exist: + +| Priority | Icon | ID | Name | Description | +|---|---|---|---|---| +| Max priority | ![min priority](../static/img/priority-5.svg) | `5` | `max`/`urgent` | Really long vibration bursts, default notification sound with a pop-over notification. | +| High priority | ![min priority](../static/img/priority-4.svg) | `4` | `high` | Long vibration burst, default notification sound with a pop-over notification. | +| **Default priority** | *(none)* | `3` | `default` | Short default vibration and sound. Default notification behavior. | +| Low priority | ![min priority](../static/img/priority-2.svg) |`2` | `low` | No vibration or sound. Notification will not visibly show up until notification drawer is pulled down. | +| Min priority | ![min priority](../static/img/priority-1.svg) | `1` | `min` | No vibration or sound. The notification will be under the fold in "Other notifications". | + +You can set the priority with the header `X-Priority` (or any of its aliases: `Priority`, `prio`, or `p`). + +=== "Command line (curl)" + ``` + curl -H "X-Priority: 5" -d "An urgent message" ntfy.sh/phil_alerts + curl -H "Priority: low" -d "Low priority message" ntfy.sh/phil_alerts + curl -H p:4 -d "A high priority message" ntfy.sh/phil_alerts + ``` + +=== "HTTP" + ``` http + POST /phil_alerts HTTP/1.1 + Host: ntfy.sh + Priority: 5 + + An urgent message + ``` + +=== "JavaScript" + ``` javascript + fetch('https://ntfy.sh/phil_alerts', { + method: 'POST', + body: 'An urgent message', + headers: { 'Priority': '5' } + }) + ``` + +=== "Go" + ``` go + req, _ := http.NewRequest("POST", "https://ntfy.sh/phil_alerts", strings.NewReader("An urgent message")) + req.Header.Set("Priority", "5") + http.DefaultClient.Do(req) + ``` + +=== "PHP" + ``` php + file_get_contents('https://ntfy.sh/phil_alerts', false, stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'header' => + "Content-Type: text/plain\r\n" . + "Priority: 5", + 'content' => 'An urgent message' + ] + ])); + ``` + +
+ ![priority notification](../static/img/priority-detail-overview.png){ width=500 } +
Detail view of priority notifications
+
+ +## Tags & emojis 🥳 🎉 +You can tag messages with emojis (or other relevant strings). If a tag matches a known emoji short code, +it will be converted to an emoji. If it doesn't match, it will be listed below the notification. This is useful +for things like warnings and such (⚠️, ️🚨, or 🚩), but also to simply tag messages otherwise (e.g. which script the +message came from, ...). + +You can set tags with the `X-Tags` header (or any of its aliases: `Tags`, or `ta`). +Use this reference +to figure out what tags can be converted to emojis. In the example below, the tag "warning" matches the emoji ⚠️, +the tag "ssh-login" doesn't match and will be displayed below the message. + +``` +$ curl -H "Tags: warning,ssh-login" -d "Unauthorized SSH access" ntfy.sh/mytopic +{"id":"ZEIwjfHlSS",...,"tags":["warning","ssh-login"],"message":"Unauthorized SSH access"} +``` + +## Message title +The notification title is typically set to the topic short URL (e.g. `ntfy.sh/mytopic`. +To override it, you can set the `X-Title` header (or any of its aliases: `Title`, `ti`, or `t`). + +``` +curl -H "Title: Dogs are better than cats" -d "Oh my ..." ntfy.sh/mytopic< +``` + diff --git a/docs/static/css/extra.css b/docs/static/css/extra.css new file mode 100644 index 00000000..b834efce --- /dev/null +++ b/docs/static/css/extra.css @@ -0,0 +1,4 @@ +figure img { + border-radius: 7px; + filter: drop-shadow(3px 3px 5px #ccc); +} diff --git a/docs/static/img/basic-notification.png b/docs/static/img/basic-notification.png new file mode 100644 index 00000000..3a8a245d Binary files /dev/null and b/docs/static/img/basic-notification.png differ diff --git a/docs/static/img/favicon.png b/docs/static/img/favicon.png new file mode 100644 index 00000000..92312fea Binary files /dev/null and b/docs/static/img/favicon.png differ diff --git a/docs/static/img/ntfy.png b/docs/static/img/ntfy.png new file mode 100644 index 00000000..6b969a84 Binary files /dev/null and b/docs/static/img/ntfy.png differ diff --git a/docs/static/img/priority-1.svg b/docs/static/img/priority-1.svg new file mode 100644 index 00000000..df6a0a49 --- /dev/null +++ b/docs/static/img/priority-1.svg @@ -0,0 +1,47 @@ + + + + + + + + diff --git a/docs/static/img/priority-2.svg b/docs/static/img/priority-2.svg new file mode 100644 index 00000000..10a89ad1 --- /dev/null +++ b/docs/static/img/priority-2.svg @@ -0,0 +1,43 @@ + + + + + + + diff --git a/docs/static/img/priority-4.svg b/docs/static/img/priority-4.svg new file mode 100644 index 00000000..a1723cf8 --- /dev/null +++ b/docs/static/img/priority-4.svg @@ -0,0 +1,43 @@ + + + + + + + diff --git a/docs/static/img/priority-5.svg b/docs/static/img/priority-5.svg new file mode 100644 index 00000000..2e2c4447 --- /dev/null +++ b/docs/static/img/priority-5.svg @@ -0,0 +1,47 @@ + + + + + + + + diff --git a/docs/static/img/priority-detail-overview.png b/docs/static/img/priority-detail-overview.png new file mode 100644 index 00000000..c9321aa7 Binary files /dev/null and b/docs/static/img/priority-detail-overview.png differ diff --git a/docs/static/img/priority-notification.png b/docs/static/img/priority-notification.png new file mode 100644 index 00000000..31d15152 Binary files /dev/null and b/docs/static/img/priority-notification.png differ diff --git a/docs/static/js/extra.js b/docs/static/js/extra.js new file mode 100644 index 00000000..d588866a --- /dev/null +++ b/docs/static/js/extra.js @@ -0,0 +1,31 @@ +// Link tabs, as per https://facelessuser.github.io/pymdown-extensions/extensions/tabbed/#linked-tabs + +const savedTab = localStorage.getItem('savedTab') +const tabs = document.querySelectorAll(".tabbed-set > input") +for (const tab of tabs) { + tab.addEventListener("click", () => { + const current = document.querySelector(`label[for=${tab.id}]`) + const pos = current.getBoundingClientRect().top + const labelContent = current.innerHTML + const labels = document.querySelectorAll('.tabbed-set > label, .tabbed-alternate > .tabbed-labels > label') + for (const label of labels) { + if (label.innerHTML === labelContent) { + document.querySelector(`input[id=${label.getAttribute('for')}]`).checked = true + } + } + + // Preserve scroll position + const delta = (current.getBoundingClientRect().top) - pos + window.scrollBy(0, delta) + + // Save + localStorage.setItem('savedTab', labelContent) + }) + + // Select saved tab + const current = document.querySelector(`label[for=${tab.id}]`) + const labelContent = current.innerHTML + if (savedTab === labelContent) { + tab.checked = true + } +} diff --git a/docs/subscribe/api.md b/docs/subscribe/api.md new file mode 100644 index 00000000..8a77f13c --- /dev/null +++ b/docs/subscribe/api.md @@ -0,0 +1 @@ +# Subscribe from your phone diff --git a/docs/subscribe/phone.md b/docs/subscribe/phone.md new file mode 100644 index 00000000..8a77f13c --- /dev/null +++ b/docs/subscribe/phone.md @@ -0,0 +1 @@ +# Subscribe from your phone diff --git a/docs/subscribe/poll.md b/docs/subscribe/poll.md new file mode 100644 index 00000000..8a77f13c --- /dev/null +++ b/docs/subscribe/poll.md @@ -0,0 +1 @@ +# Subscribe from your phone diff --git a/docs/subscribe/since.md b/docs/subscribe/since.md new file mode 100644 index 00000000..8a77f13c --- /dev/null +++ b/docs/subscribe/since.md @@ -0,0 +1 @@ +# Subscribe from your phone diff --git a/docs/subscribe/web.md b/docs/subscribe/web.md new file mode 100644 index 00000000..7da6add2 --- /dev/null +++ b/docs/subscribe/web.md @@ -0,0 +1 @@ +# Subscribe from the web UI diff --git a/examples/publish-go/main.go b/examples/publish-go/main.go new file mode 100644 index 00000000..35fac138 --- /dev/null +++ b/examples/publish-go/main.go @@ -0,0 +1,27 @@ +package main + +import ( + "log" + "net/http" + "strings" +) + +func main() { + // Without additional headers (priority, tags, title), it's a one liner. + // Check out https://ntfy.sh/mytopic in your browser after running this. + http.Post("https://ntfy.sh/mytopic", "text/plain", strings.NewReader("Backup successful 😀")) + + // If you'd like to add title, priority, or tags, it's a little harder. + // Check out https://ntfy.sh/phil_alerts in your browser. + req, err := http.NewRequest("POST", "https://ntfy.sh/phil_alerts", + strings.NewReader("Remote access to phils-laptop detected. Act right away.")) + if err != nil { + log.Fatal(err) + } + req.Header.Set("Title", "Unauthorized access detected") + req.Header.Set("Priority", "urgent") + req.Header.Set("Tags", "warning,skull") + if _, err := http.DefaultClient.Do(req); err != nil { + log.Fatal(err) + } +} diff --git a/examples/publish-php/publish.php b/examples/publish-php/publish.php new file mode 100644 index 00000000..c7c6eefc --- /dev/null +++ b/examples/publish-php/publish.php @@ -0,0 +1,14 @@ + [ + 'method' => 'POST', // PUT also works + 'header' => + "Content-Type: text/plain\r\n" . + "Title: Unauthorized access detected\r\n" . + "Priority: urgent\r\n" . + "Tags: warning,skull", + 'content' => 'Remote access to phils-laptop detected. Act right away.' + ] +])); diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 00000000..a877bfb4 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,85 @@ +site_name: ntfy.sh +site_url: https://ntfy.sh +site_description: simple HTTP-based pub-sub +copyright: Made with ❤️ by Philipp C. Heckel +repo_name: binwiederhier/ntfy +repo_url: https://github.com/binwiederhier/ntfy +edit_uri: edit/main/docs/ + +theme: + name: material +# custom_dir: docs/overrides + language: en + logo: static/img/ntfy.png + favicon: static/img/favicon.png + include_search_page: false + search_index_only: true + palette: + - media: "(prefers-color-scheme: light)" # Light mode + scheme: default + primary: teal + toggle: + icon: material/lightbulb-outline + name: Switch to light mode + - media: "(prefers-color-scheme: dark)" # Dark mode + scheme: slate + primary: teal + accent: indigo + toggle: + icon: material/lightbulb + name: Switch to dark mode + features: + - search.suggest + - search.highlight + - search.share + - navigation.sections + - toc.integrate + - content.tabs.link +extra_javascript: + - static/js/extra.js +extra_css: + - static/css/extra.css + +markdown_extensions: + - admonition + - codehilite + - meta + - toc: + permalink: true + - pymdownx.tabbed: + alternate_style: true + - pymdownx.superfences + - pymdownx.highlight + - pymdownx.tasklist: + custom_checkbox: true + - footnotes + - attr_list + - md_in_html + +plugins: + - search + +extra: + social: + - icon: fontawesome/brands/github-alt + link: https://github.com/binwiederhier + +nav: +- "Getting started": index.md +- "Installation": install.md +- "Configuration": config.md +- "Publishing": + - "Sending messages": publish/index.md +- "Subscribing": + - "From the Android/iOS app": subscribe/phone.md + - "From the Web UI": subscribe/web.md + - "Using the API": + - "Basic API usage": subscribe/api.md + - "Fetching cached messages": subscribe/since.md + - "Polling": subscribe/poll.md +- "Other things": + - "Examples": examples.md + - "FAQs": faq.md + - "Development": develop.md + +