From 26bc28ae2471b5d36bd58b48d2faaffed41e0461 Mon Sep 17 00:00:00 2001 From: Hunter Kehoe Date: Sat, 6 Jun 2026 10:38:09 -0600 Subject: [PATCH] support clear|read via GET --- docs/publish.md | 17 ++++++++++++++++- server/server.go | 2 +- server/server_test.go | 27 +++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/docs/publish.md b/docs/publish.md index 5149a232..2150fed7 100644 --- a/docs/publish.md +++ b/docs/publish.md @@ -4104,26 +4104,41 @@ field the response. A sequence of updates may look like this (first example from ### Clearing notifications Clearing a notification means **marking it as read and dismissing it from the notification drawer**. -To do this, send a PUT request to the `///clear` endpoint (or `///read` as an alias). +To do this, send a `PUT` request to the `///clear` endpoint (or `///read` as an alias). This will then emit a `message_clear` event that is used by the clients (web app and Android app) to update the read status and dismiss the notification. +Alternatively, if your client has limited HTTP support, you can send a `GET` request to the same endpoints: +`GET ///clear` or `GET ///read`. + === "Command line (curl)" ```bash + # Via PUT method curl -X PUT ntfy.sh/mytopic/my-download-123/clear + + # Via GET method + curl ntfy.sh/mytopic/my-download-123/clear ``` === "HTTP" ``` http PUT /mytopic/my-download-123/clear HTTP/1.1 Host: ntfy.sh + + # Or using GET + GET /mytopic/my-download-123/clear HTTP/1.1 + Host: ntfy.sh ``` === "JavaScript" ``` javascript + // Via PUT method await fetch('https://ntfy.sh/mytopic/my-download-123/clear', { method: 'PUT' }); + + // Via GET method + await fetch('https://ntfy.sh/mytopic/my-download-123/clear'); ``` === "Go" diff --git a/server/server.go b/server/server.go index d4586728..31977dff 100644 --- a/server/server.go +++ b/server/server.go @@ -648,7 +648,7 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visit return s.limitRequestsWithTopic(s.authorizeTopicWrite(s.handleDelete))(w, r, v) } else if r.Method == http.MethodGet && deletePathRegex.MatchString(r.URL.Path) { return s.limitRequestsWithTopic(s.authorizeTopicWrite(s.handleDelete))(w, r, v) - } else if r.Method == http.MethodPut && clearPathRegex.MatchString(r.URL.Path) { + } else if (r.Method == http.MethodGet || r.Method == http.MethodPut) && clearPathRegex.MatchString(r.URL.Path) { return s.limitRequestsWithTopic(s.authorizeTopicWrite(s.handleClear))(w, r, v) } else if r.Method == http.MethodGet && publishPathRegex.MatchString(r.URL.Path) { return s.limitRequestsWithTopic(s.authorizeTopicWrite(s.handlePublish))(w, r, v) diff --git a/server/server_test.go b/server/server_test.go index e1d4afe8..d768203f 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -4113,6 +4113,33 @@ func TestServer_ClearMessage_ReadEndpoint(t *testing.T) { }) } +func TestServer_ClearMessage_GET(t *testing.T) { + forEachBackend(t, func(t *testing.T, databaseURL string) { + t.Parallel() + s := newTestServer(t, newTestConfig(t, databaseURL)) + + // 1. Test GET /topic/seq-id/clear + response := request(t, s, "PUT", "/mytopic/seq456", "original message 1", nil) + require.Equal(t, 200, response.Code) + + response = request(t, s, "GET", "/mytopic/seq456/clear", "", nil) + require.Equal(t, 200, response.Code) + clearMsg1 := toMessage(t, response.Body.String()) + require.Equal(t, "seq456", clearMsg1.SequenceID) + require.Equal(t, "message_clear", clearMsg1.Event) + + // 2. Test GET /topic/seq-id/read + response = request(t, s, "PUT", "/mytopic/seq789", "original message 2", nil) + require.Equal(t, 200, response.Code) + + response = request(t, s, "GET", "/mytopic/seq789/read", "", nil) + require.Equal(t, 200, response.Code) + clearMsg2 := toMessage(t, response.Body.String()) + require.Equal(t, "seq789", clearMsg2.SequenceID) + require.Equal(t, "message_clear", clearMsg2.Event) + }) +} + func TestServer_UpdateMessage(t *testing.T) { forEachBackend(t, func(t *testing.T, databaseURL string) { t.Parallel()