mirror of
https://github.com/binwiederhier/ntfy.git
synced 2024-12-26 19:52:30 +01:00
Examples and anchors on website
This commit is contained in:
parent
ba2f6e08cd
commit
6d7fec5337
8 changed files with 159 additions and 20 deletions
|
@ -3,6 +3,7 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>ntfy.sh: EventSource Example</title>
|
<title>ntfy.sh: EventSource Example</title>
|
||||||
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<style>
|
<style>
|
||||||
body { font-size: 1.2em; line-height: 130%; }
|
body { font-size: 1.2em; line-height: 130%; }
|
||||||
#events { font-family: monospace; }
|
#events { font-family: monospace; }
|
||||||
|
@ -13,6 +14,7 @@
|
||||||
<p>
|
<p>
|
||||||
This is an example showing how to use <a href="https://ntfy.sh">ntfy.sh</a> with
|
This is an example showing how to use <a href="https://ntfy.sh">ntfy.sh</a> with
|
||||||
<a href="https://developer.mozilla.org/en-US/docs/Web/API/EventSource">EventSource</a>.<br/>
|
<a href="https://developer.mozilla.org/en-US/docs/Web/API/EventSource">EventSource</a>.<br/>
|
||||||
|
This example doesn't need a server. You can just save the HTML page and run it from anywhere.
|
||||||
</p>
|
</p>
|
||||||
<button id="publishButton">Send test notification</button>
|
<button id="publishButton">Send test notification</button>
|
||||||
<p><b>Log:</b></p>
|
<p><b>Log:</b></p>
|
||||||
|
|
56
server/example.html
Normal file
56
server/example.html
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>ntfy.sh: EventSource Example</title>
|
||||||
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
|
<style>
|
||||||
|
body { font-size: 1.2em; line-height: 130%; }
|
||||||
|
#events { font-family: monospace; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>ntfy.sh: EventSource Example</h1>
|
||||||
|
<p>
|
||||||
|
This is an example showing how to use <a href="https://ntfy.sh">ntfy.sh</a> with
|
||||||
|
<a href="https://developer.mozilla.org/en-US/docs/Web/API/EventSource">EventSource</a>.<br/>
|
||||||
|
This example doesn't need a server. You can just save the HTML page and run it from anywhere.
|
||||||
|
</p>
|
||||||
|
<button id="publishButton">Send test notification</button>
|
||||||
|
<p><b>Log:</b></p>
|
||||||
|
<div id="events"></div>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
const publishURL = `https://ntfy.sh/example`;
|
||||||
|
const subscribeURL = `https://ntfy.sh/example/sse`;
|
||||||
|
const events = document.getElementById('events');
|
||||||
|
const eventSource = new EventSource(subscribeURL);
|
||||||
|
|
||||||
|
// Publish button
|
||||||
|
document.getElementById("publishButton").onclick = () => {
|
||||||
|
fetch(publishURL, {
|
||||||
|
method: 'POST', // works with PUT as well, though that sends an OPTIONS request too!
|
||||||
|
body: `It is ${new Date().toString()}. This is a test.`
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// Incoming events
|
||||||
|
eventSource.onopen = () => {
|
||||||
|
let event = document.createElement('div');
|
||||||
|
event.innerHTML = `EventSource connected to ${subscribeURL}`;
|
||||||
|
events.appendChild(event);
|
||||||
|
};
|
||||||
|
eventSource.onerror = (e) => {
|
||||||
|
let event = document.createElement('div');
|
||||||
|
event.innerHTML = `EventSource error: Failed to connect to ${subscribeURL}`;
|
||||||
|
events.appendChild(event);
|
||||||
|
};
|
||||||
|
eventSource.onmessage = (e) => {
|
||||||
|
let event = document.createElement('div');
|
||||||
|
event.innerHTML = e.data;
|
||||||
|
events.appendChild(event);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -38,7 +38,7 @@
|
||||||
<h1><img src="static/img/ntfy.png" alt="ntfy"/><br/>ntfy.sh | simple HTTP-based pub-sub</h1>
|
<h1><img src="static/img/ntfy.png" alt="ntfy"/><br/>ntfy.sh | simple HTTP-based pub-sub</h1>
|
||||||
<p>
|
<p>
|
||||||
<b>Ntfy</b> (pronounce: <i>notify</i>) is a simple HTTP-based <a href="https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern">pub-sub</a> notification service.
|
<b>Ntfy</b> (pronounce: <i>notify</i>) is a simple HTTP-based <a href="https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern">pub-sub</a> notification service.
|
||||||
It allows you to send notifications <a href="https://play.google.com/store/apps/details?id=io.heckel.ntfy">to your phone</a> or desktop via scripts from any computer,
|
It allows you to send notifications <a href="#subscribe-phone">to your phone</a> or desktop via scripts from any computer,
|
||||||
entirely <b>without signup or cost</b>. It's also <a href="https://github.com/binwiederhier/ntfy">open source</a> if you want to run your own.
|
entirely <b>without signup or cost</b>. It's also <a href="https://github.com/binwiederhier/ntfy">open source</a> if you want to run your own.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -53,9 +53,9 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
There are many ways to use Ntfy. You can send yourself messages for all sorts of things: When a long process finishes or fails (a backup, a long rsync job, ...),
|
There are many ways to use Ntfy. You can send yourself messages for all sorts of things: When a long process finishes or fails,
|
||||||
or to notify yourself when somebody logs into your server(s). Or you may want to use it in your own app to distribute messages to subscribed clients.
|
or to notify yourself when somebody logs into your server(s). Or you may want to use it in your own app to distribute messages to subscribed clients.
|
||||||
Endless possibilities 😀. Be sure to check out the <a href="https://github.com/binwiederhier/ntfy/tree/main/examples">example on GitHub</a>!
|
Endless possibilities 😀. Be sure to check out the <a href="#examples">examples below</a>.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 id="publish" class="anchor">Publishing messages</h2>
|
<h2 id="publish" class="anchor">Publishing messages</h2>
|
||||||
|
@ -104,16 +104,21 @@
|
||||||
<audio id="notifySound" src="static/sound/mixkit-message-pop-alert-2354.mp3"></audio>
|
<audio id="notifySound" src="static/sound/mixkit-message-pop-alert-2354.mp3"></audio>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 id="android-app" class="anchor">Subscribe via Android App</h3>
|
<h3 id="subscribe-phone" class="anchor">Subscribe from your phone</h3>
|
||||||
<p>
|
<p>
|
||||||
You can use the <a href="https://play.google.com/store/apps/details?id=io.heckel.ntfy">Ntfy Android App</a>
|
You can use the <a href="https://play.google.com/store/apps/details?id=io.heckel.ntfy">Ntfy Android App</a>
|
||||||
to receive notifications directly on your phone. Just like the server, this app is also <a href="https://github.com/binwiederhier/ntfy-android">open source</a>.
|
to receive notifications directly on your phone. Just like the server, this app is also <a href="https://github.com/binwiederhier/ntfy-android">open source</a>.
|
||||||
|
Since I don't have an iPhone or a Mac, I didn't make an iOS app yet. I'd be awesome if <a href="https://github.com/binwiederhier/ntfy/issues/4">someone else could help out</a>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="https://play.google.com/store/apps/details?id=io.heckel.ntfy"><img src="static/img/badge-googleplay.png"></a>
|
||||||
|
<a href="https://github.com/binwiederhier/ntfy/issues/4"><img src="static/img/badge-appstore.png"></a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3 id="subscribe-api" class="anchor">Subscribe via your app, or via the CLI</h3>
|
<h3 id="subscribe-api" class="anchor">Subscribe via your app, or via the CLI</h3>
|
||||||
<p class="smallMarginBottom">
|
<p class="smallMarginBottom">
|
||||||
Using <a href="https://developer.mozilla.org/en-US/docs/Web/API/EventSource">EventSource</a> in JS, you can consume
|
Using <a href="https://developer.mozilla.org/en-US/docs/Web/API/EventSource">EventSource</a> in JS, you can consume
|
||||||
notifications like this (see <a href="https://github.com/binwiederhier/ntfy/tree/main/examples">full example</a>):
|
notifications like this (see <a href="example.html">live example</a>):
|
||||||
</p>
|
</p>
|
||||||
<code>
|
<code>
|
||||||
const eventSource = new EventSource('https://ntfy.sh/mytopic/sse');<br/>
|
const eventSource = new EventSource('https://ntfy.sh/mytopic/sse');<br/>
|
||||||
|
@ -149,19 +154,12 @@
|
||||||
<code>
|
<code>
|
||||||
$ curl -s ntfy.sh/mytopic/raw<br/>
|
$ curl -s ntfy.sh/mytopic/raw<br/>
|
||||||
<br/>
|
<br/>
|
||||||
This is a notification
|
This is a notification<br/>
|
||||||
</code>
|
And another one with a smiley face 😀
|
||||||
<p class="smallMarginBottom">
|
|
||||||
Here's an example of how to use this endpoint to send desktop notifications for every incoming message:
|
|
||||||
</p>
|
|
||||||
<code>
|
|
||||||
while read msg; do<br/>
|
|
||||||
[ -n "$msg" ] && notify-send "$msg"<br/>
|
|
||||||
done < <(stdbuf -i0 -o0 curl -s ntfy.sh/mytopic/raw)
|
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<h2 id="other-features" class="anchor">Other features</h2>
|
<h2 id="other-features" class="anchor">Other features</h2>
|
||||||
<h3 id="fetching-cached-messages" class="anchor">Fetching cached messages</h3>
|
<h3 id="fetching-cached-messages" class="anchor">Fetching cached messages (<tt>since=</tt>)</h3>
|
||||||
<p class="smallMarginBottom">
|
<p class="smallMarginBottom">
|
||||||
Messages are cached on disk for {{.CacheDuration}} to account for network interruptions of subscribers.
|
Messages are cached on disk for {{.CacheDuration}} to account for network interruptions of subscribers.
|
||||||
You can read back what you missed by using the <tt>since=</tt> query parameter. It takes either a
|
You can read back what you missed by using the <tt>since=</tt> query parameter. It takes either a
|
||||||
|
@ -172,7 +170,7 @@
|
||||||
curl -s "ntfy.sh/mytopic/json?since=10m"
|
curl -s "ntfy.sh/mytopic/json?since=10m"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<h3 id="polling" class="anchor">Fetching cached messages</h3>
|
<h3 id="polling" class="anchor">Polling (<tt>poll=1</tt>)</h3>
|
||||||
<p class="smallMarginBottom">
|
<p class="smallMarginBottom">
|
||||||
You can also just poll for messages if you don't like the long-standing connection using the <tt>poll=1</tt>
|
You can also just poll for messages if you don't like the long-standing connection using the <tt>poll=1</tt>
|
||||||
query parameter. The connection will end after all available messages have been read. This parameter can be
|
query parameter. The connection will end after all available messages have been read. This parameter can be
|
||||||
|
@ -182,7 +180,7 @@
|
||||||
curl -s "ntfy.sh/mytopic/json?poll=1"
|
curl -s "ntfy.sh/mytopic/json?poll=1"
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
<h3 id="multiple-topics" class="anchor">Subscribing to multiple topics</h3>
|
<h3 id="multiple-topics" class="anchor">Subscribing to multiple topics (<tt>topic1,topic2,...</tt>)</h3>
|
||||||
<p class="smallMarginBottom">
|
<p class="smallMarginBottom">
|
||||||
It's possible to subscribe to multiple topics in one HTTP call by providing a
|
It's possible to subscribe to multiple topics in one HTTP call by providing a
|
||||||
comma-separated list of topics in the URL. This allows you to reduce the number of connections you have to maintain:
|
comma-separated list of topics in the URL. This allows you to reduce the number of connections you have to maintain:
|
||||||
|
@ -194,6 +192,65 @@
|
||||||
{"id":"Cm02DsxUHb","time":1637182643,"event":"message","topic":"mytopic2","message":"for topic 2"}
|
{"id":"Cm02DsxUHb","time":1637182643,"event":"message","topic":"mytopic2","message":"for topic 2"}
|
||||||
</code>
|
</code>
|
||||||
|
|
||||||
|
<h2 id="examples" class="anchor">Examples</h2>
|
||||||
|
<p>
|
||||||
|
There are a million ways to use Ntfy, but here are some inspirations. I try to collect
|
||||||
|
<a href="https://github.com/binwiederhier/ntfy/tree/main/examples">examples on GitHub</a>, so be sure to check
|
||||||
|
those out, too.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="example-alerts" class="anchor">Example: A long process is done: backups, copying data, pipelines, ...</h3>
|
||||||
|
<p class="smallMarginBottom">
|
||||||
|
I started adding notifications pretty much all of my scripts. Typically, I just chain the <tt>curl</tt> call
|
||||||
|
directly to the command I'm running. The following example will either send <i>Laptop backup succeeded</i>
|
||||||
|
or ⚠️ <i>Laptop backup failed</i> directly to my phone:
|
||||||
|
</p>
|
||||||
|
<code>
|
||||||
|
rsync -a root@laptop /backups/laptop \<br/>
|
||||||
|
&& zfs snapshot ... \<br/>
|
||||||
|
&& curl -d "Laptop backup succeeded" ntfy.sh/backups \<br/>
|
||||||
|
|| echo -en "\u26A0\uFE0F Laptop backup failed" | curl -sT- ntfy.sh/backups
|
||||||
|
</code>
|
||||||
|
|
||||||
|
<h3 id="example-web" class="anchor">Example: Server-sent messages in your web app</h3>
|
||||||
|
<p>
|
||||||
|
Just as you can <a href="#subscribe-web">subscribe to topics in this Web UI</a>, you can use Ntfy in your own
|
||||||
|
web application. Check out the <a href="example.html">live example</a> or just look the source of this page.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3 id="example-notify-ssh" class="anchor">Example: Notify on SSH login</h3>
|
||||||
|
<p>
|
||||||
|
Years ago my home server was broken into. That shook me hard, so every time someone logs into any machine that I
|
||||||
|
own, I now message myself. Here's an example of how to use <a href="https://en.wikipedia.org/wiki/Linux_PAM">PAM</a>
|
||||||
|
to notify yourself on SSH login.
|
||||||
|
</p>
|
||||||
|
<p class="smallMarginBottom">
|
||||||
|
<b>/etc/pam.d/sshd</b> (at the end of the file):
|
||||||
|
</p>
|
||||||
|
<code>
|
||||||
|
session optional pam_exec.so /usr/local/bin/ntfy-ssh-login.sh
|
||||||
|
</code>
|
||||||
|
<p class="smallMarginBottom">
|
||||||
|
<b>/usr/local/bin/ntfy-ssh-login.sh</b>:
|
||||||
|
</p>
|
||||||
|
<code>
|
||||||
|
#!/bin/bash<br/>
|
||||||
|
if [ "${PAM_TYPE}" = "open_session" ]; then<br/>
|
||||||
|
echo -en "\u26A0\uFE0F SSH login: ${PAM_USER} from ${PAM_RHOST}" | curl -T- ntfy.sh/alerts<br/>
|
||||||
|
fi
|
||||||
|
</code>
|
||||||
|
|
||||||
|
<h3 id="example-collect-data" class="anchor">Example: Collect data from multiple machines</h3>
|
||||||
|
<p>
|
||||||
|
The other day I was running tasks on 20 servers and I wanted to collect the interim results
|
||||||
|
as a CSV in one place. Here's the script I wrote:
|
||||||
|
</p>
|
||||||
|
<code>
|
||||||
|
while read result; do<br/>
|
||||||
|
[ -n "$result" ] && echo "result" >> results.csv<br/>
|
||||||
|
done < <(stdbuf -i0 -o0 curl -s ntfy.sh/results/raw)
|
||||||
|
</code>
|
||||||
|
|
||||||
<h2 id="faq" class="anchor">FAQ</h2>
|
<h2 id="faq" class="anchor">FAQ</h2>
|
||||||
<p>
|
<p>
|
||||||
<b id="isnt-this-like" class="anchor">Isn't this like ...?</b><br/>
|
<b id="isnt-this-like" class="anchor">Isn't this like ...?</b><br/>
|
||||||
|
@ -225,6 +282,13 @@
|
||||||
client network disruptions.
|
client network disruptions.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<b id="selfhosted" class="anchor">Can I self-host it?</b><br/>
|
||||||
|
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 <a href="https://github.com/binwiederhier/ntfy#installation">install instructions</a>
|
||||||
|
on GitHub.
|
||||||
|
</p>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<b id="why-firebase" class="anchor">Why is Firebase used?</b><br/>
|
<b id="why-firebase" class="anchor">Why is Firebase used?</b><br/>
|
||||||
In addition to caching messages locally and delivering them to long-polling subscribers, all messages are also
|
In addition to caching messages locally and delivering them to long-polling subscribers, all messages are also
|
||||||
|
@ -232,7 +296,13 @@
|
||||||
is to facilitate instant notifications on Android.
|
is to facilitate instant notifications on Android.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h2 id="#privacy" class="anchor">Privacy policy</h2>
|
<p>
|
||||||
|
<b id="why-no-ios" class="anchor">Why is there no iOS app (yet)?</b><br/>
|
||||||
|
I don't have an iPhone or a Mac, so I didn't make an iOS app yet. I'd be awesome if
|
||||||
|
<a href="https://github.com/binwiederhier/ntfy/issues/4">someone else could help out</a>.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h2 id="privacy" class="anchor">Privacy policy</h2>
|
||||||
<p>
|
<p>
|
||||||
Neither the server nor the app record any personal information, or share any of the messages and topics with
|
Neither the server nor the app record any personal information, or share any of the messages and topics with
|
||||||
any outside service. All data is exclusively used to make the service function properly. The one exception
|
any outside service. All data is exclusively used to make the service function properly. The one exception
|
||||||
|
|
|
@ -88,6 +88,9 @@ var (
|
||||||
indexSource string
|
indexSource string
|
||||||
indexTemplate = template.Must(template.New("index").Parse(indexSource))
|
indexTemplate = template.Must(template.New("index").Parse(indexSource))
|
||||||
|
|
||||||
|
//go:embed "example.html"
|
||||||
|
exampleSource string
|
||||||
|
|
||||||
//go:embed static
|
//go:embed static
|
||||||
webStaticFs embed.FS
|
webStaticFs embed.FS
|
||||||
|
|
||||||
|
@ -188,6 +191,8 @@ func (s *Server) handle(w http.ResponseWriter, r *http.Request) {
|
||||||
func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request) error {
|
func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request) error {
|
||||||
if r.Method == http.MethodGet && (r.URL.Path == "/" || topicRegex.MatchString(r.URL.Path)) {
|
if r.Method == http.MethodGet && (r.URL.Path == "/" || topicRegex.MatchString(r.URL.Path)) {
|
||||||
return s.handleHome(w, r)
|
return s.handleHome(w, r)
|
||||||
|
} else if r.Method == http.MethodGet && r.URL.Path == "/example.html" {
|
||||||
|
return s.handleExample(w, r)
|
||||||
} else if r.Method == http.MethodHead && r.URL.Path == "/" {
|
} else if r.Method == http.MethodHead && r.URL.Path == "/" {
|
||||||
return s.handleEmpty(w, r)
|
return s.handleEmpty(w, r)
|
||||||
} else if r.Method == http.MethodGet && staticRegex.MatchString(r.URL.Path) {
|
} else if r.Method == http.MethodGet && staticRegex.MatchString(r.URL.Path) {
|
||||||
|
@ -217,6 +222,11 @@ func (s *Server) handleEmpty(w http.ResponseWriter, r *http.Request) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleExample(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
_, err := io.WriteString(w, exampleSource)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) handleStatic(w http.ResponseWriter, r *http.Request) error {
|
func (s *Server) handleStatic(w http.ResponseWriter, r *http.Request) error {
|
||||||
http.FileServer(http.FS(webStaticFs)).ServeHTTP(w, r)
|
http.FileServer(http.FS(webStaticFs)).ServeHTTP(w, r)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -28,13 +28,13 @@ h1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
margin-top: 20px;
|
margin-top: 30px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
font-size: 1.8em;
|
font-size: 1.8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
margin-top: 20px;
|
margin-top: 25px;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
font-size: 1.3em;
|
font-size: 1.3em;
|
||||||
}
|
}
|
||||||
|
|
BIN
server/static/img/badge-appstore.png
Normal file
BIN
server/static/img/badge-appstore.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
BIN
server/static/img/badge-googleplay.png
Normal file
BIN
server/static/img/badge-googleplay.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.7 KiB |
|
@ -338,6 +338,7 @@ if (match) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add anchor links
|
||||||
document.querySelectorAll('.anchor').forEach((el) => {
|
document.querySelectorAll('.anchor').forEach((el) => {
|
||||||
if (el.hasAttribute('id')) {
|
if (el.hasAttribute('id')) {
|
||||||
const id = el.getAttribute('id');
|
const id = el.getAttribute('id');
|
||||||
|
|
Loading…
Reference in a new issue