Docs and Matrix tests
This commit is contained in:
		
							parent
							
								
									0ff8e968ca
								
							
						
					
					
						commit
						18bd3c0e55
					
				
					 12 changed files with 172 additions and 140 deletions
				
			
		|  | @ -9,7 +9,9 @@ those out, too. | |||
|     [create a pull request](https://github.com/binwiederhier/ntfy/pulls), and I'll happily include it. Also note, that | ||||
|     I cannot guarantee that all of these examples are functional. Many of them I have not tried myself. | ||||
| 
 | ||||
| ## A long process is done: backups, copying data, pipelines, ... | ||||
| ## Cronjobs | ||||
| ntfy is perfect for any kind of cronjobs or just when long processes are done (backups, pipelines, rsync copy commands, ...). | ||||
| 
 | ||||
| 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: | ||||
|  | @ -21,6 +23,15 @@ rsync -a root@laptop /backups/laptop \ | |||
|   || curl -H tags:warning -H prio:high -d "Laptop backup failed" ntfy.sh/backups | ||||
| ``` | ||||
| 
 | ||||
| Here's one for the history books. I desperately want the `github.com/ntfy` organization, but all my tickets with | ||||
| GitHub have been hopeless. In case it ever becomes available, I want to know immediately. | ||||
| 
 | ||||
| ``` cron | ||||
| # Check github/ntfy user | ||||
| */6 * * * * if curl -s https://api.github.com/users/ntfy | grep "Not Found"; then curl -d "github.com/ntfy is available" -H "Tags: tada" -H "Prio: high" ntfy.sh/my-alerts; fi | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ## Low disk space alerts | ||||
| Here's a simple cronjob that I use to alert me when the disk space on the root disk is running low. It's simple, but  | ||||
| effective.  | ||||
|  | @ -42,11 +53,7 @@ if [ -n "$avail" ]; then | |||
| fi | ||||
| ``` | ||||
| 
 | ||||
| ## Server-sent messages in your web app | ||||
| Just as you can [subscribe to topics in the Web UI](subscribe/web.md), you can use ntfy in your own | ||||
| web application. Check out the <a href="/example.html">live example</a>. | ||||
| 
 | ||||
| ## Notify on SSH login | ||||
| ## SSH login alerts | ||||
| 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. | ||||
|  | @ -102,7 +109,7 @@ One of my co-workers uses the following Ansible task to let him know when things | |||
|     body: "{{ inventory_hostname }} reseeding complete" | ||||
| ``` | ||||
| 
 | ||||
| ## Watchtower notifications (shoutrrr) | ||||
| ## Watchtower (shoutrrr) | ||||
| You can use [shoutrrr](https://github.com/containrrr/shoutrrr) generic webhook support to send  | ||||
| [Watchtower](https://github.com/containrrr/watchtower/) notifications to your ntfy topic. | ||||
| 
 | ||||
|  | @ -121,16 +128,7 @@ Or, if you only want to send notifications using shoutrrr: | |||
| shoutrrr send -u "generic+https://ntfy.sh/my_watchtower_topic?title=WatchtowerUpdates" -m "testMessage" | ||||
| ``` | ||||
| 
 | ||||
| ## Random cronjobs | ||||
| Alright, here's one for the history books. I desperately want the `github.com/ntfy` organization, but all my tickets with | ||||
| GitHub have been hopeless. In case it ever becomes available, I want to know immediately. | ||||
| 
 | ||||
| ``` cron | ||||
| # Check github/ntfy user | ||||
| */6 * * * * if curl -s https://api.github.com/users/ntfy | grep "Not Found"; then curl -d "github.com/ntfy is available" -H "Tags: tada" -H "Prio: high" ntfy.sh/my-alerts; fi | ||||
| ``` | ||||
| 
 | ||||
| ## Download notifications (Sonarr, Radarr, Lidarr, Readarr, Prowlarr, SABnzbd) | ||||
| ## Sonarr, Radarr, Lidarr, Readarr, Prowlarr, SABnzbd | ||||
| It's possible to use custom scripts for all the *arr services, plus SABnzbd. Notifications for downloads, warnings, grabs etc. | ||||
| Some simple bash scripts to achieve this are kindly provided in [nickexyz's repository](https://github.com/nickexyz/ntfy-shellscripts).  | ||||
| 
 | ||||
|  | @ -343,7 +341,7 @@ You can use the HTTP request node to send messages with [Node-RED](https://noder | |||
| 
 | ||||
|  | ||||
| 
 | ||||
| ## Gatus service health check | ||||
| ## Gatus | ||||
| 
 | ||||
| An example for a custom alert with [Gatus](https://github.com/TwiN/gatus): | ||||
| ``` yaml | ||||
|  | @ -435,11 +433,38 @@ notify: | |||
| ``` | ||||
| 
 | ||||
| ## Uptime Kuma | ||||
| - Go to your [Uptime Kuma](https://github.com/louislam/uptime-kuma) Settings > Notifications, click on **Setup Notification** | ||||
| -  | ||||
| - Set your desired **title** (e.g. "Uptime Kuma"), **ntfy topic**, **Server URL** and **priority (1-5)** | ||||
| -  | ||||
| - You can now test the notifications and apply them to monitors. | ||||
| -  | ||||
| -  | ||||
| -  | ||||
| Go to your [Uptime Kuma](https://github.com/louislam/uptime-kuma) Settings > Notifications, click on **Setup Notification**. | ||||
| Then set your desired **title** (e.g. "Uptime Kuma"), **ntfy topic**, **Server URL** and **priority (1-5)**: | ||||
| 
 | ||||
| <div id="uptimekuma-screenshots" class="screenshots"> | ||||
|     <a href="../../static/img/uptimekuma-settings.png"><img src="../../static/img/uptimekuma-settings.png"/></a> | ||||
|     <a href="../../static/img/uptimekuma-setup.png"><img src="../../static/img/uptimekuma-setup.png"/></a> | ||||
| </div> | ||||
| 
 | ||||
| 
 | ||||
| You can now test the notifications and apply them to monitors: | ||||
| 
 | ||||
| <div id="uptimekuma-monitor-screenshots" class="screenshots"> | ||||
|     <a href="../../static/img/uptimekuma-ios-test.jpg"><img src="../../static/img/uptimekuma-ios-test.jpg"/></a> | ||||
|     <a href="../../static/img/uptimekuma-ios-down.jpg"><img src="../../static/img/uptimekuma-ios-down.jpg"/></a> | ||||
|     <a href="../../static/img/uptimekuma-ios-up.jpg"><img src="../../static/img/uptimekuma-ios-up.jpg"/></a> | ||||
| </div> | ||||
| 
 | ||||
| ## Apprise | ||||
| ntfy is integrated natively into [Apprise](https://github.com/caronc/apprise) (also check out the  | ||||
| [Apprise/ntfy wiki page](https://github.com/caronc/apprise/wiki/Notify_ntfy)). | ||||
| 
 | ||||
| You can use it like this: | ||||
| 
 | ||||
| ``` | ||||
| apprise -vv -t "Test Message Title" -b "Test Message Body" \ | ||||
|    ntfy://mytopic | ||||
| ``` | ||||
| 
 | ||||
| Or with your own server like this: | ||||
| 
 | ||||
| ``` | ||||
| apprise -vv -t "Test Message Title" -b "Test Message Body" \ | ||||
|    ntfy://ntfy.example.com/mytopic | ||||
| ``` | ||||
| 
 | ||||
|  |  | |||
|  | @ -2735,6 +2735,22 @@ parameter (or any of its aliases `unifiedpush` or `up`) to `1` to [disable Fireb | |||
| option is mostly equivalent to `Firebase: no`, but was introduced to allow future flexibility. The flag additionally  | ||||
| enables auto-detection of the message encoding. If the message is binary, it'll be encoded as base64. | ||||
| 
 | ||||
| ### Matrix Gateway | ||||
| The ntfy server implements a [Matrix Push Gateway](https://spec.matrix.org/v1.2/push-gateway-api/) (in combination with | ||||
| [UnifiedPush](https://unifiedpush.org) as the [Provider Push Protocol](https://unifiedpush.org/developers/gateway/)). This makes it easier to integrate | ||||
| with self-hosted [Matrix](https://matrix.org/) servers (such as [synapse](https://github.com/matrix-org/synapse)), since  | ||||
| you don't have to set up a separate push proxy (such as [common-proxies](https://github.com/UnifiedPush/common-proxies)). | ||||
| 
 | ||||
| In short, ntfy accepts Matrix messages on the `/_matrix/push/v1/notify` endpoint (see [Push Gateway API](https://spec.matrix.org/v1.2/push-gateway-api/)),  | ||||
| and forwards them to the ntfy topic defined in the `pushkey` of the message. The message will then be forwarded to the | ||||
| ntfy Android app, and passed on to the Matrix client there. | ||||
| 
 | ||||
| There is a nice diagram in the [Push Gateway docs](https://spec.matrix.org/v1.2/push-gateway-api/). In this diagram, the | ||||
| ntfy server plays the role of the Push Gateway, as well as the Push Provider. UnifiedPush is the Provider Push Protocol. | ||||
| 
 | ||||
| !!! info | ||||
|     This is not a generic Matrix Push Gateway. It only works in combination with UnifiedPush and ntfy. | ||||
| 
 | ||||
| ## Public topics | ||||
| Obviously all topics on ntfy.sh are public, but there are a few designated topics that are used in examples, and topics | ||||
| that you can use to try out what [authentication and access control](#authentication) looks like. | ||||
|  |  | |||
|  | @ -2,6 +2,27 @@ | |||
| Binaries for all releases can be found on the GitHub releases pages for the [ntfy server](https://github.com/binwiederhier/ntfy/releases) | ||||
| and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/releases). | ||||
| 
 | ||||
| ## ntfy server v1.26.0 (UNRELEASED) | ||||
| 
 | ||||
| **Features:** | ||||
| 
 | ||||
| * ntfy now is a [Matrix Push Gateway](https://spec.matrix.org/v1.2/push-gateway-api/) (in combination with [UnifiedPush](https://unifiedpush.org) as the [Provider Push Protocol](https://unifiedpush.org/developers/gateway/), [#319](https://github.com/binwiederhier/ntfy/issues/319)/[#326](https://github.com/binwiederhier/ntfy/pull/326), thanks to [@MayeulC](https://github.com/MayeulC) for reporting) | ||||
| * Windows CLI is now available via [Scoop](https://scoop.sh) ([ScoopInstaller#3594](https://github.com/ScoopInstaller/Main/pull/3594), [#311](https://github.com/binwiederhier/ntfy/pull/311), [#269](https://github.com/binwiederhier/ntfy/issues/269), thanks to [@kzshantonu](https://github.com/kzshantonu)) | ||||
| * [Uptime Kuma](https://github.com/louislam/uptime-kuma) now allows publishing to ntfy ([uptime-kuma#1674](https://github.com/louislam/uptime-kuma/pull/1674), thanks to [@philippdormann](https://github.com/philippdormann)) | ||||
| * Display ntfy version in `ntfy serve` command  ([#314](https://github.com/binwiederhier/ntfy/issues/314), thanks to [@poblabs](https://github.com/poblabs)) | ||||
| 
 | ||||
| **Bugs:** | ||||
| 
 | ||||
| * Web app: Show "notifications not supported" alert on HTTP ([#323](https://github.com/binwiederhier/ntfy/issues/323), thanks to [@milksteakjellybeans](https://github.com/milksteakjellybeans) for reporting) | ||||
| 
 | ||||
| **Documentation** | ||||
| 
 | ||||
| * Added [example](examples.md) for [Uptime Kuma](https://github.com/louislam/uptime-kuma) integration ([#315](https://github.com/binwiederhier/ntfy/pull/315), thanks to [@philippdormann](https://github.com/philippdormann)) | ||||
| * Fix Docker install instructions  ([#320](https://github.com/binwiederhier/ntfy/issues/320), thanks to [@milksteakjellybeans](https://github.com/milksteakjellybeans) for reporting) | ||||
| * Add clarifying comments to base-url ([#322](https://github.com/binwiederhier/ntfy/issues/322), thanks to [@milksteakjellybeans](https://github.com/milksteakjellybeans) for reporting) | ||||
| * Update FAQ for iOS app ([#321](https://github.com/binwiederhier/ntfy/issues/321), thanks to [@milksteakjellybeans](https://github.com/milksteakjellybeans) for reporting) | ||||
| 
 | ||||
| 
 | ||||
| <!-- | ||||
| 
 | ||||
| ## ntfy Android app v1.14.0 (UNRELEASED) | ||||
|  | @ -11,26 +32,6 @@ and the [ntfy Android app](https://github.com/binwiederhier/ntfy-android/release | |||
| * Italian (thanks to [@Genio2003](https://hosted.weblate.org/user/Genio2003/)) | ||||
| 
 | ||||
| 
 | ||||
| ## ntfy server v1.26.0 (UNRELEASED) | ||||
| 
 | ||||
| **Bugs:** | ||||
| 
 | ||||
| * Web app: Show "notifications not supported" alert on HTTP ([#323](https://github.com/binwiederhier/ntfy/issues/323), thanks to [@milksteakjellybeans](https://github.com/milksteakjellybeans) for reporting) | ||||
| 
 | ||||
| **Features:** | ||||
| 
 | ||||
| * Windows CLI is now available via [Scoop](https://scoop.sh) ([ScoopInstaller#3594](https://github.com/ScoopInstaller/Main/pull/3594), [#311](https://github.com/binwiederhier/ntfy/pull/311), [#269](https://github.com/binwiederhier/ntfy/issues/269), thanks to [@kzshantonu](https://github.com/kzshantonu)) | ||||
| * [Uptime Kuma](https://github.com/louislam/uptime-kuma) now allows publishing to ntfy ([uptime-kuma#1674](https://github.com/louislam/uptime-kuma/pull/1674), thanks to [@philippdormann](https://github.com/philippdormann)) | ||||
| * Display ntfy version in `ntfy serve` command  ([#314](https://github.com/binwiederhier/ntfy/issues/314), thanks to [@poblabs](https://github.com/poblabs)) | ||||
| 
 | ||||
| **Documentation** | ||||
| 
 | ||||
| * Added [example](examples.md) for [Uptime Kuma](https://github.com/louislam/uptime-kuma) integration ([#315](https://github.com/binwiederhier/ntfy/pull/315), thanks to [@philippdormann](https://github.com/philippdormann)) | ||||
| * Fix Docker install instructions  ([#320](https://github.com/binwiederhier/ntfy/issues/320), thanks to [@milksteakjellybeans](https://github.com/milksteakjellybeans) for reporting) | ||||
| * Add clarifying comments to base-url ([#322](https://github.com/binwiederhier/ntfy/issues/322), thanks to [@milksteakjellybeans](https://github.com/milksteakjellybeans) for reporting) | ||||
| * Update FAQ for iOS app ([#321](https://github.com/binwiederhier/ntfy/issues/321), thanks to [@milksteakjellybeans](https://github.com/milksteakjellybeans) for reporting) | ||||
| 
 | ||||
| 
 | ||||
| ## ntfy iOS app v1.2 (UNRELEASED) | ||||
| 
 | ||||
| This release adds support for authentication/authorization for self-hosted servers. It also allows you to | ||||
|  |  | |||
							
								
								
									
										3
									
								
								docs/static/css/extra.css
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								docs/static/css/extra.css
									
										
									
									
										vendored
									
									
								
							|  | @ -60,7 +60,8 @@ figure video { | |||
| } | ||||
| 
 | ||||
| .screenshots img { | ||||
|     height: 230px; | ||||
|     max-height: 230px; | ||||
|     max-width: 300px; | ||||
|     margin: 3px; | ||||
|     border-radius: 5px; | ||||
|     filter: drop-shadow(2px 2px 2px #ddd); | ||||
|  |  | |||
|  | @ -87,7 +87,7 @@ recommended way to subscribe to a topic**. The notable exception is JavaScript, | |||
| ### Subscribe as SSE stream | ||||
| Using [EventSource](https://developer.mozilla.org/en-US/docs/Web/API/EventSource) in JavaScript, you can consume | ||||
| notifications via a [Server-Sent Events (SSE)](https://en.wikipedia.org/wiki/Server-sent_events) stream. It's incredibly  | ||||
| easy to use. Here's what it looks like. You may also want to check out the [live example](/example.html). | ||||
| easy to use. Here's what it looks like. You may also want to check out the [full example on GitHub](https://github.com/binwiederhier/ntfy/tree/main/examples/web-example-eventsource). | ||||
| 
 | ||||
| === "Command line (curl)" | ||||
|     ``` | ||||
|  |  | |||
|  | @ -1,56 +0,0 @@ | |||
| <!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> | ||||
|  | @ -75,9 +75,6 @@ var ( | |||
| 	disallowedTopics = []string{"docs", "static", "file", "app", "settings"} // If updated, also update in Android app | ||||
| 	attachURLRegex   = regexp.MustCompile(`^https?://`) | ||||
| 
 | ||||
| 	//go:embed "example.html" | ||||
| 	exampleSource string | ||||
| 
 | ||||
| 	//go:embed site | ||||
| 	webFs        embed.FS | ||||
| 	webFsCached  = &util.CachingEmbedFS{ModTime: time.Now(), FS: webFs} | ||||
|  | @ -283,8 +280,6 @@ func (s *Server) handle(w http.ResponseWriter, r *http.Request) { | |||
| func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visitor) error { | ||||
| 	if r.Method == http.MethodGet && r.URL.Path == "/" { | ||||
| 		return s.ensureWebEnabled(s.handleHome)(w, r, v) | ||||
| 	} else if r.Method == http.MethodGet && r.URL.Path == "/example.html" { | ||||
| 		return s.ensureWebEnabled(s.handleExample)(w, r, v) | ||||
| 	} else if r.Method == http.MethodHead && r.URL.Path == "/" { | ||||
| 		return s.ensureWebEnabled(s.handleEmpty)(w, r, v) | ||||
| 	} else if r.Method == http.MethodGet && r.URL.Path == webConfigPath { | ||||
|  | @ -357,11 +352,6 @@ func (s *Server) handleTopicAuth(w http.ResponseWriter, _ *http.Request, _ *visi | |||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (s *Server) handleExample(w http.ResponseWriter, _ *http.Request, _ *visitor) error { | ||||
| 	_, err := io.WriteString(w, exampleSource) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visitor) error { | ||||
| 	appRoot := "/" | ||||
| 	if !s.config.WebRootIsApp { | ||||
|  | @ -435,7 +425,7 @@ func (s *Server) handleFile(w http.ResponseWriter, r *http.Request, v *visitor) | |||
| } | ||||
| 
 | ||||
| func (s *Server) handleMatrixDiscovery(w http.ResponseWriter) error { | ||||
| 	return handleMatrixDiscovery(w) | ||||
| 	return writeMatrixDiscoveryResponse(w) | ||||
| } | ||||
| 
 | ||||
| func (s *Server) handlePublishWithoutResponse(r *http.Request, v *visitor) (*message, error) { | ||||
|  |  | |||
|  | @ -11,9 +11,36 @@ import ( | |||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	matrixPushKeyHeader = "X-Matrix-Pushkey" | ||||
| ) | ||||
| // Matrix Push Gateway / UnifiedPush / ntfy integration: | ||||
| // | ||||
| // ntfy implements a Matrix Push Gateway (as defined in https://spec.matrix.org/v1.2/push-gateway-api/), | ||||
| // in combination with UnifiedPush as the Provider Push Protocol (as defined in https://unifiedpush.org/developers/gateway/). | ||||
| // | ||||
| // In the picture below, ntfy is the Push Gateway (mostly in this file), as well as the Push Provider (ntfy's | ||||
| // main functionality). UnifiedPush is the Provider Push Protocol, as implemented by the ntfy server and the | ||||
| // ntfy Android app. | ||||
| // | ||||
| //                                    +--------------------+  +-------------------+ | ||||
| //                  Matrix HTTP      |                    |  |                   | | ||||
| //             Notification Protocol |   App Developer    |  |   Device Vendor   | | ||||
| //                                   |                    |  |                   | | ||||
| //           +-------------------+   | +----------------+ |  | +---------------+ | | ||||
| //           |                   |   | |                | |  | |               | | | ||||
| //           | Matrix homeserver +----->  Push Gateway  +------> Push Provider | | | ||||
| //           |                   |   | |                | |  | |               | | | ||||
| //           +-^-----------------+   | +----------------+ |  | +----+----------+ | | ||||
| //             |                     |                    |  |      |            | | ||||
| //    Matrix   |                     |                    |  |      |            | | ||||
| // Client/Server API  +              |                    |  |      |            | | ||||
| //             |      |              +--------------------+  +-------------------+ | ||||
| //             |   +--+-+                                           | | ||||
| //             |   |    <-------------------------------------------+ | ||||
| //             +---+    | | ||||
| //                 |    |          Provider Push Protocol | ||||
| //                 +----+ | ||||
| // | ||||
| //         Mobile Device or Client | ||||
| // | ||||
| 
 | ||||
| // matrixRequest represents a Matrix message, as it is sent to a Push Gateway (as per | ||||
| // this spec: https://spec.matrix.org/v1.2/push-gateway-api/). | ||||
|  | @ -30,6 +57,7 @@ const ( | |||
| //        ] | ||||
| //      } | ||||
| //    } | ||||
| // | ||||
| type matrixRequest struct { | ||||
| 	Notification *struct { | ||||
| 		Devices []*struct { | ||||
|  | @ -38,10 +66,13 @@ type matrixRequest struct { | |||
| 	} `json:"notification"` | ||||
| } | ||||
| 
 | ||||
| // matrixResponse represents the response to a Matrix push gateway message, as defined | ||||
| // in the spec (https://spec.matrix.org/v1.2/push-gateway-api/). | ||||
| type matrixResponse struct { | ||||
| 	Rejected []string `json:"rejected"` | ||||
| } | ||||
| 
 | ||||
| // errMatrix represents an error when handing Matrix gateway messages | ||||
| type errMatrix struct { | ||||
| 	pushKey string | ||||
| 	err     error | ||||
|  | @ -54,6 +85,12 @@ func (e errMatrix) Error() string { | |||
| 	return fmt.Sprintf("message with push key %s rejected", e.pushKey) | ||||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	// matrixPushKeyHeader is a header that's used internally to pass the Matrix push key (from the matrixRequest) | ||||
| 	// along with the request. The push key is only used if an error occurs down the line. | ||||
| 	matrixPushKeyHeader = "X-Matrix-Pushkey" | ||||
| ) | ||||
| 
 | ||||
| // newRequestFromMatrixJSON reads the request body as a Matrix JSON message, parses the "pushkey", and creates a new | ||||
| // HTTP request that looks like a normal ntfy request from it. | ||||
| // | ||||
|  | @ -82,7 +119,7 @@ func newRequestFromMatrixJSON(r *http.Request, baseURL string, messageLimit int) | |||
| 	} else if m.Notification == nil || len(m.Notification.Devices) == 0 || m.Notification.Devices[0].PushKey == "" { | ||||
| 		return nil, errHTTPBadRequestMatrixMessageInvalid | ||||
| 	} | ||||
| 	pushKey := m.Notification.Devices[0].PushKey | ||||
| 	pushKey := m.Notification.Devices[0].PushKey // We ignore other devices for now, see discussion in #316 | ||||
| 	if !strings.HasPrefix(pushKey, baseURL+"/") { | ||||
| 		return nil, &errMatrix{pushKey: pushKey, err: errHTTPBadRequestMatrixPushkeyBaseURLMismatch} | ||||
| 	} | ||||
|  | @ -94,21 +131,27 @@ func newRequestFromMatrixJSON(r *http.Request, baseURL string, messageLimit int) | |||
| 	return newRequest, nil | ||||
| } | ||||
| 
 | ||||
| func handleMatrixDiscovery(w http.ResponseWriter) error { | ||||
| // writeMatrixDiscoveryResponse writes the UnifiedPush Matrix Gateway Discovery response to the given http.ResponseWriter, | ||||
| // as per the spec (https://unifiedpush.org/developers/gateway/). | ||||
| func writeMatrixDiscoveryResponse(w http.ResponseWriter) error { | ||||
| 	w.Header().Set("Content-Type", "application/json") | ||||
| 	_, err := io.WriteString(w, `{"unifiedpush":{"gateway":"matrix"}}`+"\n") | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| // writeMatrixError logs and writes the errMatrix to the given http.ResponseWriter as a matrixResponse | ||||
| func writeMatrixError(w http.ResponseWriter, r *http.Request, v *visitor, err *errMatrix) error { | ||||
| 	log.Debug("%s Matrix gateway error: %s", logHTTPPrefix(v, r), err.Error()) | ||||
| 	return writeMatrixResponse(w, err.pushKey) | ||||
| } | ||||
| 
 | ||||
| // writeMatrixSuccess writes a successful matrixResponse (no rejected push key) to the given http.ResponseWriter | ||||
| func writeMatrixSuccess(w http.ResponseWriter) error { | ||||
| 	return writeMatrixResponse(w, "") | ||||
| } | ||||
| 
 | ||||
| // writeMatrixResponse writes a matrixResponse to the given http.ResponseWriter, as defined in | ||||
| // the spec (https://spec.matrix.org/v1.2/push-gateway-api/) | ||||
| func writeMatrixResponse(w http.ResponseWriter, rejectedPushKey string) error { | ||||
| 	rejected := make([]string, 0) | ||||
| 	if rejectedPushKey != "" { | ||||
|  |  | |||
							
								
								
									
										21
									
								
								server/server_matrix_test.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								server/server_matrix_test.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,21 @@ | |||
| package server | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| 	"testing" | ||||
| ) | ||||
| 
 | ||||
| func TestMatrix_NewRequestFromMatrixJSON_Success(t *testing.T) { | ||||
| 	baseURL := "https://ntfy.sh" | ||||
| 	maxLength := 4096 | ||||
| 	body := `{"notification":{"content":{"body":"I'm floating in a most peculiar way.","msgtype":"m.text"},"counts":{"missed_calls":1,"unread":2},"devices":[{"app_id":"org.matrix.matrixConsole.ios","data":{},"pushkey":"https://ntfy.sh/upABCDEFGHI?up=1","pushkey_ts":12345678,"tweaks":{"sound":"bing"}}],"event_id":"$3957tyerfgewrf384","prio":"high","room_alias":"#exampleroom:matrix.org","room_id":"!slw48wfj34rtnrf:example.com","room_name":"Mission Control","sender":"@exampleuser:matrix.org","sender_display_name":"Major Tom","type":"m.room.message"}}` | ||||
| 	r, _ := http.NewRequest("POST", "http://ntfy.example.com/_matrix/push/v1/notify", strings.NewReader(body)) | ||||
| 	newRequest, err := newRequestFromMatrixJSON(r, baseURL, maxLength) | ||||
| 	require.Nil(t, err) | ||||
| 	require.Equal(t, "POST", newRequest.Method) | ||||
| 	require.Equal(t, "https://ntfy.sh/upABCDEFGHI?up=1", newRequest.URL.String()) | ||||
| 	require.Equal(t, "https://ntfy.sh/upABCDEFGHI?up=1", newRequest.Header.Get("X-Matrix-Pushkey")) | ||||
| 	require.Equal(t, body, readAll(t, newRequest.Body)) | ||||
| } | ||||
|  | @ -6,6 +6,7 @@ import ( | |||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math/rand" | ||||
| 	"net/http" | ||||
| 	"net/http/httptest" | ||||
|  | @ -171,10 +172,6 @@ func TestServer_StaticSites(t *testing.T) { | |||
| 	require.Equal(t, 301, rr.Code) | ||||
| 
 | ||||
| 	// Docs test removed, it was failing annoyingly. | ||||
| 
 | ||||
| 	rr = request(t, s, "GET", "/example.html", "", nil) | ||||
| 	require.Equal(t, 200, rr.Code) | ||||
| 	require.Contains(t, rr.Body.String(), "</html>") | ||||
| } | ||||
| 
 | ||||
| func TestServer_WebEnabled(t *testing.T) { | ||||
|  | @ -185,9 +182,6 @@ func TestServer_WebEnabled(t *testing.T) { | |||
| 	rr := request(t, s, "GET", "/", "", nil) | ||||
| 	require.Equal(t, 404, rr.Code) | ||||
| 
 | ||||
| 	rr = request(t, s, "GET", "/example.html", "", nil) | ||||
| 	require.Equal(t, 404, rr.Code) | ||||
| 
 | ||||
| 	rr = request(t, s, "GET", "/config.js", "", nil) | ||||
| 	require.Equal(t, 404, rr.Code) | ||||
| 
 | ||||
|  | @ -201,9 +195,6 @@ func TestServer_WebEnabled(t *testing.T) { | |||
| 	rr = request(t, s2, "GET", "/", "", nil) | ||||
| 	require.Equal(t, 200, rr.Code) | ||||
| 
 | ||||
| 	rr = request(t, s2, "GET", "/example.html", "", nil) | ||||
| 	require.Equal(t, 200, rr.Code) | ||||
| 
 | ||||
| 	rr = request(t, s2, "GET", "/config.js", "", nil) | ||||
| 	require.Equal(t, 200, rr.Code) | ||||
| 
 | ||||
|  | @ -1390,3 +1381,11 @@ func toHTTPError(t *testing.T, s string) *errHTTP { | |||
| func basicAuth(s string) string { | ||||
| 	return fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(s))) | ||||
| } | ||||
| 
 | ||||
| func readAll(t *testing.T, rc io.ReadCloser) string { | ||||
| 	b, err := io.ReadAll(rc) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	return string(b) | ||||
| } | ||||
|  |  | |||
|  | @ -3,7 +3,6 @@ package server | |||
| import ( | ||||
| 	"github.com/emersion/go-smtp" | ||||
| 	"github.com/stretchr/testify/require" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
|  | @ -304,14 +303,6 @@ func newTestBackend(t *testing.T, handler func(http.ResponseWriter, *http.Reques | |||
| 	return conf, backend | ||||
| } | ||||
| 
 | ||||
| func readAll(t *testing.T, rc io.ReadCloser) string { | ||||
| 	b, err := io.ReadAll(rc) | ||||
| 	if err != nil { | ||||
| 		t.Fatal(err) | ||||
| 	} | ||||
| 	return string(b) | ||||
| } | ||||
| 
 | ||||
| func fakeConnState(t *testing.T, remoteAddr string) *smtp.ConnectionState { | ||||
| 	ip, err := net.ResolveIPAddr("ip", remoteAddr) | ||||
| 	if err != nil { | ||||
|  |  | |||
|  | @ -18,7 +18,8 @@ type PeekedReadCloser struct { | |||
| 	closed       bool | ||||
| } | ||||
| 
 | ||||
| // Peek reads the underlying ReadCloser into memory up until the limit and returns a PeekedReadCloser | ||||
| // Peek reads the underlying ReadCloser into memory up until the limit and returns a PeekedReadCloser. | ||||
| // It does not return an error if limit is reached. Instead, LimitReached will be set to true. | ||||
| func Peek(underlying io.ReadCloser, limit int) (*PeekedReadCloser, error) { | ||||
| 	if underlying == nil { | ||||
| 		underlying = io.NopCloser(strings.NewReader("")) | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue