From 18bd3c0e55c6bb1fddb128af8ecf25df37d14808 Mon Sep 17 00:00:00 2001 From: Philipp Heckel <pheckel@datto.com> Date: Thu, 16 Jun 2022 11:40:56 -0400 Subject: [PATCH] Docs and Matrix tests --- docs/examples.md | 77 ++++++++++++++++++++++++------------ docs/publish.md | 16 ++++++++ docs/releases.md | 41 +++++++++---------- docs/static/css/extra.css | 3 +- docs/subscribe/api.md | 2 +- server/example.html | 56 -------------------------- server/server.go | 12 +----- server/server_matrix.go | 53 ++++++++++++++++++++++--- server/server_matrix_test.go | 21 ++++++++++ server/server_test.go | 19 +++++---- server/smtp_server_test.go | 9 ----- util/peek.go | 3 +- 12 files changed, 172 insertions(+), 140 deletions(-) delete mode 100644 server/example.html create mode 100644 server/server_matrix_test.go diff --git a/docs/examples.md b/docs/examples.md index 6183b670..bbda91a8 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -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. --  --  --  \ No newline at end of file +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 +``` + diff --git a/docs/publish.md b/docs/publish.md index 5f0ae585..82f7beb0 100644 --- a/docs/publish.md +++ b/docs/publish.md @@ -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. diff --git a/docs/releases.md b/docs/releases.md index 56e7fe1f..9c0c6203 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -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 diff --git a/docs/static/css/extra.css b/docs/static/css/extra.css index a7370399..6ac0b7e7 100644 --- a/docs/static/css/extra.css +++ b/docs/static/css/extra.css @@ -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); diff --git a/docs/subscribe/api.md b/docs/subscribe/api.md index 7eb3a95e..4c46d221 100644 --- a/docs/subscribe/api.md +++ b/docs/subscribe/api.md @@ -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)" ``` diff --git a/server/example.html b/server/example.html deleted file mode 100644 index e558ef12..00000000 --- a/server/example.html +++ /dev/null @@ -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> diff --git a/server/server.go b/server/server.go index 2aa114a2..21a6c584 100644 --- a/server/server.go +++ b/server/server.go @@ -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) { diff --git a/server/server_matrix.go b/server/server_matrix.go index 3bc59d35..42c9f037 100644 --- a/server/server_matrix.go +++ b/server/server_matrix.go @@ -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 != "" { diff --git a/server/server_matrix_test.go b/server/server_matrix_test.go new file mode 100644 index 00000000..d8263775 --- /dev/null +++ b/server/server_matrix_test.go @@ -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)) +} diff --git a/server/server_test.go b/server/server_test.go index 343d502b..c9af9459 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -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) +} diff --git a/server/smtp_server_test.go b/server/smtp_server_test.go index 8e9d5892..c0de7079 100644 --- a/server/smtp_server_test.go +++ b/server/smtp_server_test.go @@ -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 { diff --git a/util/peek.go b/util/peek.go index f7219253..40150cbc 100644 --- a/util/peek.go +++ b/util/peek.go @@ -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(""))