mirror of
https://github.com/binwiederhier/ntfy.git
synced 2024-11-22 03:13:33 +01:00
implement scheduled message unpublishing via /topic/{messageId}
This commit is contained in:
parent
9d3fc20e58
commit
37468f38c2
2 changed files with 83 additions and 2 deletions
|
@ -77,6 +77,7 @@ var (
|
|||
wsPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/ws$`)
|
||||
authPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}(,[-_A-Za-z0-9]{1,64})*/auth$`)
|
||||
publishPathRegex = regexp.MustCompile(`^/[-_A-Za-z0-9]{1,64}/(publish|send|trigger)$`)
|
||||
messagePathRegex = regexp.MustCompile(`^/[-A-Za-z-0-9]{1,64}/([A-Za-z0-9]{12})$`)
|
||||
|
||||
webConfigPath = "/config.js"
|
||||
webManifestPath = "/manifest.webmanifest"
|
||||
|
@ -537,6 +538,8 @@ func (s *Server) handleInternal(w http.ResponseWriter, r *http.Request, v *visit
|
|||
return s.limitRequests(s.authorizeTopicRead(s.handleTopicAuth))(w, r, v)
|
||||
} else if r.Method == http.MethodGet && (topicPathRegex.MatchString(r.URL.Path) || externalTopicPathRegex.MatchString(r.URL.Path)) {
|
||||
return s.ensureWebEnabled(s.handleTopic)(w, r, v)
|
||||
} else if r.Method == http.MethodDelete && (messagePathRegex.MatchString(r.URL.Path)) {
|
||||
return s.limitRequestsWithTopic(s.authorizeTopicWrite(s.handleMessageUnpublish))(w, r, v)
|
||||
}
|
||||
return errHTTPNotFound
|
||||
}
|
||||
|
@ -558,6 +561,25 @@ func (s *Server) handleTopic(w http.ResponseWriter, r *http.Request, v *visitor)
|
|||
return s.handleStatic(w, r, v)
|
||||
}
|
||||
|
||||
func (s *Server) handleMessageUnpublish(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||
segments := strings.Split(r.URL.Path, "/")
|
||||
msg, err := s.messageCache.Message(segments[len(segments)-1])
|
||||
if err != nil {
|
||||
if err == errMessageNotFound {
|
||||
return errHTTPNotFound
|
||||
}
|
||||
return err
|
||||
}
|
||||
if time.Now().Unix() >= msg.Time {
|
||||
httpErr := errHTTPBadRequest
|
||||
alreadyPublished := httpErr.Wrap("Message \"%s\" has already been published", msg.ID)
|
||||
s.handleError(w, r, v, alreadyPublished)
|
||||
return alreadyPublished
|
||||
}
|
||||
s.messageCache.DeleteMessages(msg.ID)
|
||||
return s.writeJSON(w, msg)
|
||||
}
|
||||
|
||||
func (s *Server) handleEmpty(_ http.ResponseWriter, _ *http.Request, _ *visitor) error {
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -7,8 +7,6 @@ import (
|
|||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"heckel.io/ntfy/v2/user"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
@ -22,6 +20,9 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"heckel.io/ntfy/v2/user"
|
||||
|
||||
"github.com/SherClockHolmes/webpush-go"
|
||||
"github.com/stretchr/testify/require"
|
||||
"heckel.io/ntfy/v2/log"
|
||||
|
@ -2853,6 +2854,64 @@ template ""}}`,
|
|||
}
|
||||
}
|
||||
|
||||
func TestServer_Message_Unpublish_Existing(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := newTestServer(t, newTestConfig(t))
|
||||
response1 := request(t, s, "PUT", "/mytopic", "hello universe", map[string]string{
|
||||
"In": "1m",
|
||||
})
|
||||
msg1 := toMessage(t, response1.Body.String())
|
||||
time.Sleep(500)
|
||||
|
||||
response2 := request(t, s, "DELETE", fmt.Sprintf("/mytopic/%s", msg1.ID), "", nil)
|
||||
require.Equal(t, 200, response2.Code)
|
||||
msg2 := toMessage(t, response2.Body.String())
|
||||
require.Equal(t, msg1.ID, msg2.ID)
|
||||
}
|
||||
|
||||
func TestServer_Message_Unpublish_Nonexistent(t *testing.T) {
|
||||
t.Parallel()
|
||||
s := newTestServer(t, newTestConfig(t))
|
||||
|
||||
response2 := request(t, s, "DELETE", "/mytopic/n0nexist3nt1", "", nil)
|
||||
require.Equal(t, 404, response2.Code)
|
||||
}
|
||||
|
||||
func TestServer_Message_Unpublish_Protected_Fail_Unauthorized(t *testing.T) {
|
||||
c := newTestConfigWithAuthFile(t)
|
||||
s := newTestServer(t, c)
|
||||
require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin))
|
||||
require.Nil(t, s.userManager.AllowAccess(user.Everyone, "announcements", user.PermissionRead))
|
||||
|
||||
response1 := request(t, s, "PUT", "/announcements", "hello universe", map[string]string{
|
||||
"In": "1m",
|
||||
"Authorization": util.BasicAuth("phil", "phil"),
|
||||
})
|
||||
msg1 := toMessage(t, response1.Body.String())
|
||||
|
||||
response2 := request(t, s, "DELETE", fmt.Sprintf("/announcements/%s", msg1.ID), "", nil)
|
||||
require.Equal(t, 403, response2.Code)
|
||||
}
|
||||
func TestServer_Message_Unpublish_Private(t *testing.T) {
|
||||
c := newTestConfigWithAuthFile(t)
|
||||
c.AuthDefault = user.PermissionDenyAll
|
||||
s := newTestServer(t, c)
|
||||
require.Nil(t, s.userManager.AddUser("phil", "phil", user.RoleAdmin))
|
||||
require.Nil(t, s.userManager.AllowAccess("phil", "announcements", user.PermissionReadWrite))
|
||||
|
||||
h := map[string]string{
|
||||
"In": "1m",
|
||||
"Authorization": util.BasicAuth("phil", "phil"),
|
||||
}
|
||||
response1 := request(t, s, "PUT", "/announcements", "hello universe", h)
|
||||
msg1 := toMessage(t, response1.Body.String())
|
||||
|
||||
response2 := request(t, s, "DELETE", fmt.Sprintf("/announcements/%s", msg1.ID), "", h)
|
||||
msg2 := toMessage(t, response2.Body.String())
|
||||
require.Equal(t, 200, response2.Code)
|
||||
require.Equal(t, msg1.ID, msg2.ID)
|
||||
}
|
||||
|
||||
func newTestConfig(t *testing.T) *Config {
|
||||
conf := NewConfig()
|
||||
conf.BaseURL = "http://127.0.0.1:12345"
|
||||
|
|
Loading…
Reference in a new issue