1
1
<!DOCTYPE html>
2
2
< html lang ="en ">
3
3
< head >
4
- < title > ntfy.sh</ title >
5
- < style >
6
- body { font-size : 1.2em ; line-height : 130% ; }
7
- # error { color : darkred; font-style : italic; }
8
- # main { max-width : 900px ; margin : 0 auto 50px auto; }
9
- </ style >
4
+ < meta charset ="UTF-8 ">
5
+
6
+ < title > ntfy.sh | simple HTTP-based pub-sub</ title >
7
+ < link rel ="stylesheet " href ="static/css/app.css " type ="text/css ">
8
+
9
+ <!-- Mobile view -->
10
+ < meta name ="viewport " content ="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no ">
11
+ < meta http-equiv ="X-UA-Compatible " content ="IE=edge,chrome=1 ">
12
+ < meta name ="HandheldFriendly " content ="true ">
13
+
14
+ <!-- Mobile browsers, background color -->
15
+ < meta name ="theme-color " content ="#004c79 ">
16
+ < meta name ="msapplication-navbutton-color " content ="#004c79 ">
17
+ < meta name ="apple-mobile-web-app-status-bar-style " content ="#004c79 ">
18
+
19
+ <!-- Favicon, see favicon.io -->
20
+ < link rel ="icon " type ="image/png " href ="static/img/favicon.png ">
21
+
22
+ <!-- Previews in Google, Slack, WhatsApp, etc. -->
23
+ < meta property ="og:type " content ="website " />
24
+ < meta property ="og:locale " content ="en_US " />
25
+ < meta property ="og:site_name " content ="ntfy.sh " />
26
+ < meta property ="og:title " content ="ntfy.sh | simple HTTP-based pub-sub " />
27
+ < meta property ="og:description " content ="ntfy is a simple HTTP-based pub-sub notification service. It allows you to send desktop notifications via scripts from any computer, entirely without signup or cost. Made with ❤ by Philipp C. Heckel, Apache License 2.0, source at https://heckel.io/ntfy. " />
28
+ < meta property ="og:image " content ="/static/img/ntfy.png " />
29
+ < meta property ="og:url " content ="https://ntfy.sh " />
10
30
</ head >
11
31
< body >
12
32
< div id ="main ">
13
33
< h1 > ntfy.sh - simple HTTP-based pub-sub</ h1 >
14
34
< p >
15
- < b > ntfy</ b > (pronounce: < i > notify</ i > ) is a simple < b > HTTP-based pub-sub notification service and tool</ b > .
16
- It allows you to send < b > desktop notifications via scripts</ b > , entirely < b > without signup or cost</ b > .
35
+ < b > ntfy</ b > (pronounce: < i > notify</ i > ) is a simple HTTP-based pub-sub notification service and tool.
36
+ It allows you to send < b > desktop notifications via scripts from any computer </ b > , entirely < b > without signup or cost</ b > .
17
37
It's also < a href ="https://github.com/binwiederhier/ntfy "> open source</ a > if you want to run your own.
18
38
</ p >
19
39
< p id ="error "> </ p >
@@ -37,151 +57,31 @@ <h3>Subscribe via web</h3>
37
57
< p >
38
58
< label for ="topicField "> Topic ID:</ label >
39
59
< input type ="text " id ="topicField " placeholder ="Letters, numbers, _ and - " pattern ="[-_A-Za-z]{1,64} " autofocus />
40
- < input type ="submit " id ="subscribeButton " value ="Subscribe topic " />
60
+ < input type ="submit " id ="subscribeButton " value ="Subscribe " />
41
61
</ p >
42
62
</ form >
43
63
< p id ="topicsHeader "> Subscribed topics:</ p >
44
64
< ul id ="topicsList "> </ ul >
45
65
46
66
< h3 > Subscribe via your app, or via the CLI</ h3 >
47
- < tt >
67
+ < code >
48
68
curl -s ntfy.sh/mytopic/raw # one message per line (\n are replaced with a space)< br />
49
69
curl -s ntfy.sh/mytopic/json # one JSON message per line< br />
50
70
curl -s ntfy.sh/mytopic/sse # server-sent events (SSE) stream
51
- </ tt >
71
+ </ code >
52
72
53
- < h3 > Publishing messages</ h3 >
73
+ < h2 > Publishing messages</ h2 >
54
74
< p >
55
75
Publishing messages can be done via PUT or POST using. Here's an example using < tt > curl</ tt > :
56
76
</ p >
57
- < tt >
77
+ < code >
58
78
curl -d "long process is done" ntfy.sh/mytopic
59
- </ tt >
79
+ </ code >
60
80
< p >
61
81
Messages published to a non-existing topic or a topic without subscribers will not be delivered later.
62
82
There is (currently) no buffering of any kind. If you're not listening, the message won't be delivered.
63
83
</ p >
64
84
</ div >
65
-
66
- < script type ="text/javascript ">
67
- let topics = { } ;
68
-
69
- const topicsHeader = document . getElementById ( "topicsHeader" ) ;
70
- const topicsList = document . getElementById ( "topicsList" ) ;
71
- const topicField = document . getElementById ( "topicField" ) ;
72
- const subscribeButton = document . getElementById ( "subscribeButton" ) ;
73
- const subscribeForm = document . getElementById ( "subscribeForm" ) ;
74
- const errorField = document . getElementById ( "error" ) ;
75
-
76
- const subscribe = ( topic ) => {
77
- if ( Notification . permission !== "granted" ) {
78
- Notification . requestPermission ( ) . then ( ( permission ) => {
79
- if ( permission === "granted" ) {
80
- subscribeInternal ( topic , 0 ) ;
81
- } else {
82
- showNotificationDeniedError ( ) ;
83
- }
84
- } ) ;
85
- } else {
86
- subscribeInternal ( topic , 0 ) ;
87
- }
88
- } ;
89
-
90
- const subscribeInternal = ( topic , delaySec ) => {
91
- setTimeout ( ( ) => {
92
- // Render list entry
93
- let topicEntry = document . getElementById ( `topic-${ topic } ` ) ;
94
- if ( ! topicEntry ) {
95
- topicEntry = document . createElement ( 'li' ) ;
96
- topicEntry . id = `topic-${ topic } ` ;
97
- topicEntry . innerHTML = `${ topic } <button onclick="test('${ topic } ')">Test</button> <button onclick="unsubscribe('${ topic } ')">Unsubscribe</button>` ;
98
- topicsList . appendChild ( topicEntry ) ;
99
- }
100
- topicsHeader . style . display = '' ;
101
-
102
- // Open event source
103
- let eventSource = new EventSource ( `${ topic } /sse` ) ;
104
- eventSource . onopen = ( ) => {
105
- topicEntry . innerHTML = `${ topic } <button onclick="test('${ topic } ')">Test</button> <button onclick="unsubscribe('${ topic } ')">Unsubscribe</button>` ;
106
- delaySec = 0 ; // Reset on successful connection
107
- } ;
108
- eventSource . onerror = ( e ) => {
109
- const newDelaySec = ( delaySec + 5 <= 15 ) ? delaySec + 5 : 15 ;
110
- topicEntry . innerHTML = `${ topic } <i>(Reconnecting in ${ newDelaySec } s ...)</i> <button disabled="disabled">Test</button> <button onclick="unsubscribe('${ topic } ')">Unsubscribe</button>` ;
111
- eventSource . close ( )
112
- subscribeInternal ( topic , newDelaySec ) ;
113
- } ;
114
- eventSource . onmessage = ( e ) => {
115
- const event = JSON . parse ( e . data ) ;
116
- new Notification ( event . message ) ;
117
- } ;
118
- topics [ topic ] = eventSource ;
119
- localStorage . setItem ( 'topics' , JSON . stringify ( Object . keys ( topics ) ) ) ;
120
- } , delaySec * 1000 ) ;
121
- } ;
122
-
123
- const unsubscribe = ( topic ) => {
124
- topics [ topic ] . close ( ) ;
125
- delete topics [ topic ] ;
126
- localStorage . setItem ( 'topics' , JSON . stringify ( Object . keys ( topics ) ) ) ;
127
- document . getElementById ( `topic-${ topic } ` ) . remove ( ) ;
128
- if ( Object . keys ( topics ) . length === 0 ) {
129
- topicsHeader . style . display = 'none' ;
130
- }
131
- } ;
132
-
133
- const test = ( topic ) => {
134
- fetch ( `/${ topic } ` , {
135
- method : 'PUT' ,
136
- body : `This is a test notification for topic ${ topic } !`
137
- } )
138
- } ;
139
-
140
- const showError = ( msg ) => {
141
- errorField . innerHTML = msg ;
142
- topicField . disabled = true ;
143
- subscribeButton . disabled = true ;
144
- } ;
145
-
146
- const showBrowserIncompatibleError = ( ) => {
147
- showError ( "Your browser is not compatible to use the web-based desktop notifications." ) ;
148
- } ;
149
-
150
- const showNotificationDeniedError = ( ) => {
151
- showError ( "You have blocked desktop notifications for this website. Please unblock them and refresh to use the web-based desktop notifications." ) ;
152
- } ;
153
-
154
- subscribeForm . onsubmit = function ( ) {
155
- if ( ! topicField . value ) {
156
- return false ;
157
- }
158
- subscribe ( topicField . value ) ;
159
- topicField . value = "" ;
160
- return false ;
161
- } ;
162
-
163
- // Disable Web UI if notifications of EventSource are not available
164
- if ( ! window [ "Notification" ] || ! window [ "EventSource" ] ) {
165
- showBrowserIncompatibleError ( ) ;
166
- } else if ( Notification . permission === "denied" ) {
167
- showNotificationDeniedError ( ) ;
168
- }
169
-
170
- // Reset UI
171
- topicField . value = "" ;
172
-
173
- // Restore topics
174
- const storedTopics = localStorage . getItem ( 'topics' ) ;
175
- if ( storedTopics && Notification . permission === "granted" ) {
176
- const storedTopicsArray = JSON . parse ( storedTopics )
177
- storedTopicsArray . forEach ( ( topic ) => { subscribeInternal ( topic , 0 ) ; } ) ;
178
- if ( storedTopicsArray . length === 0 ) {
179
- topicsHeader . style . display = 'none' ;
180
- }
181
- } else {
182
- topicsHeader . style . display = 'none' ;
183
- }
184
- </ script >
185
-
85
+ < script src ="static/js/app.js "> </ script >
186
86
</ body >
187
87
</ html >
0 commit comments