mirror of
https://github.com/binwiederhier/ntfy.git
synced 2026-01-18 16:17:26 +01:00
Rename to sequence_id
This commit is contained in:
@@ -125,7 +125,7 @@ var (
|
|||||||
errHTTPBadRequestInvalidUsername = &errHTTP{40046, http.StatusBadRequest, "invalid request: invalid username", "", nil}
|
errHTTPBadRequestInvalidUsername = &errHTTP{40046, http.StatusBadRequest, "invalid request: invalid username", "", nil}
|
||||||
errHTTPBadRequestTemplateFileNotFound = &errHTTP{40047, http.StatusBadRequest, "invalid request: template file not found", "https://ntfy.sh/docs/publish/#message-templating", nil}
|
errHTTPBadRequestTemplateFileNotFound = &errHTTP{40047, http.StatusBadRequest, "invalid request: template file not found", "https://ntfy.sh/docs/publish/#message-templating", nil}
|
||||||
errHTTPBadRequestTemplateFileInvalid = &errHTTP{40048, http.StatusBadRequest, "invalid request: template file invalid", "https://ntfy.sh/docs/publish/#message-templating", nil}
|
errHTTPBadRequestTemplateFileInvalid = &errHTTP{40048, http.StatusBadRequest, "invalid request: template file invalid", "https://ntfy.sh/docs/publish/#message-templating", nil}
|
||||||
errHTTPBadRequestSIDInvalid = &errHTTP{40049, http.StatusBadRequest, "invalid request: SID invalid", "https://ntfy.sh/docs/publish/#TODO", nil}
|
errHTTPBadRequestSIDInvalid = &errHTTP{40049, http.StatusBadRequest, "invalid request: sequence ID invalid", "https://ntfy.sh/docs/publish/#TODO", nil}
|
||||||
errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", "", nil}
|
errHTTPNotFound = &errHTTP{40401, http.StatusNotFound, "page not found", "", nil}
|
||||||
errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication", nil}
|
errHTTPUnauthorized = &errHTTP{40101, http.StatusUnauthorized, "unauthorized", "https://ntfy.sh/docs/publish/#authentication", nil}
|
||||||
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication", nil}
|
errHTTPForbidden = &errHTTP{40301, http.StatusForbidden, "forbidden", "https://ntfy.sh/docs/publish/#authentication", nil}
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ const (
|
|||||||
CREATE TABLE IF NOT EXISTS messages (
|
CREATE TABLE IF NOT EXISTS messages (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
mid TEXT NOT NULL,
|
mid TEXT NOT NULL,
|
||||||
sid TEXT NOT NULL,
|
sequence_id TEXT NOT NULL,
|
||||||
time INT NOT NULL,
|
time INT NOT NULL,
|
||||||
expires INT NOT NULL,
|
expires INT NOT NULL,
|
||||||
topic TEXT NOT NULL,
|
topic TEXT NOT NULL,
|
||||||
@@ -54,7 +54,7 @@ const (
|
|||||||
deleted INT NOT NULL
|
deleted INT NOT NULL
|
||||||
);
|
);
|
||||||
CREATE INDEX IF NOT EXISTS idx_mid ON messages (mid);
|
CREATE INDEX IF NOT EXISTS idx_mid ON messages (mid);
|
||||||
CREATE INDEX IF NOT EXISTS idx_sid ON messages (sid);
|
CREATE INDEX IF NOT EXISTS idx_sequence_id ON messages (sequence_id);
|
||||||
CREATE INDEX IF NOT EXISTS idx_time ON messages (time);
|
CREATE INDEX IF NOT EXISTS idx_time ON messages (time);
|
||||||
CREATE INDEX IF NOT EXISTS idx_topic ON messages (topic);
|
CREATE INDEX IF NOT EXISTS idx_topic ON messages (topic);
|
||||||
CREATE INDEX IF NOT EXISTS idx_expires ON messages (expires);
|
CREATE INDEX IF NOT EXISTS idx_expires ON messages (expires);
|
||||||
@@ -69,50 +69,50 @@ const (
|
|||||||
COMMIT;
|
COMMIT;
|
||||||
`
|
`
|
||||||
insertMessageQuery = `
|
insertMessageQuery = `
|
||||||
INSERT INTO messages (mid, sid, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, attachment_deleted, sender, user, content_type, encoding, published, deleted)
|
INSERT INTO messages (mid, sequence_id, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, attachment_deleted, sender, user, content_type, encoding, published, deleted)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
`
|
`
|
||||||
deleteMessageQuery = `DELETE FROM messages WHERE mid = ?`
|
deleteMessageQuery = `DELETE FROM messages WHERE mid = ?`
|
||||||
updateMessagesForTopicExpiryQuery = `UPDATE messages SET expires = ? WHERE topic = ?`
|
updateMessagesForTopicExpiryQuery = `UPDATE messages SET expires = ? WHERE topic = ?`
|
||||||
selectRowIDFromMessageID = `SELECT id FROM messages WHERE mid = ?` // Do not include topic, see #336 and TestServer_PollSinceID_MultipleTopics
|
selectRowIDFromMessageID = `SELECT id FROM messages WHERE mid = ?` // Do not include topic, see #336 and TestServer_PollSinceID_MultipleTopics
|
||||||
selectMessagesByIDQuery = `
|
selectMessagesByIDQuery = `
|
||||||
SELECT mid, sid, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
SELECT mid, sequence_id, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE mid = ?
|
WHERE mid = ?
|
||||||
`
|
`
|
||||||
selectMessagesSinceTimeQuery = `
|
selectMessagesSinceTimeQuery = `
|
||||||
SELECT mid, sid, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
SELECT mid, sequence_id, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE topic = ? AND time >= ? AND published = 1
|
WHERE topic = ? AND time >= ? AND published = 1
|
||||||
ORDER BY time, id
|
ORDER BY time, id
|
||||||
`
|
`
|
||||||
selectMessagesSinceTimeIncludeScheduledQuery = `
|
selectMessagesSinceTimeIncludeScheduledQuery = `
|
||||||
SELECT mid, sid, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
SELECT mid, sequence_id, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE topic = ? AND time >= ?
|
WHERE topic = ? AND time >= ?
|
||||||
ORDER BY time, id
|
ORDER BY time, id
|
||||||
`
|
`
|
||||||
selectMessagesSinceIDQuery = `
|
selectMessagesSinceIDQuery = `
|
||||||
SELECT mid, sid, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
SELECT mid, sequence_id, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE topic = ? AND id > ? AND published = 1
|
WHERE topic = ? AND id > ? AND published = 1
|
||||||
ORDER BY time, id
|
ORDER BY time, id
|
||||||
`
|
`
|
||||||
selectMessagesSinceIDIncludeScheduledQuery = `
|
selectMessagesSinceIDIncludeScheduledQuery = `
|
||||||
SELECT mid, sid, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
SELECT mid, sequence_id, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE topic = ? AND (id > ? OR published = 0)
|
WHERE topic = ? AND (id > ? OR published = 0)
|
||||||
ORDER BY time, id
|
ORDER BY time, id
|
||||||
`
|
`
|
||||||
selectMessagesLatestQuery = `
|
selectMessagesLatestQuery = `
|
||||||
SELECT mid, sid, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
SELECT mid, sequence_id, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE topic = ? AND published = 1
|
WHERE topic = ? AND published = 1
|
||||||
ORDER BY time DESC, id DESC
|
ORDER BY time DESC, id DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
`
|
`
|
||||||
selectMessagesDueQuery = `
|
selectMessagesDueQuery = `
|
||||||
SELECT mid, sid, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
SELECT mid, sequence_id, time, expires, topic, message, title, priority, tags, click, icon, actions, attachment_name, attachment_type, attachment_size, attachment_expires, attachment_url, sender, user, content_type, encoding, deleted
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE time <= ? AND published = 0
|
WHERE time <= ? AND published = 0
|
||||||
ORDER BY time, id
|
ORDER BY time, id
|
||||||
@@ -267,9 +267,9 @@ const (
|
|||||||
|
|
||||||
//13 -> 14
|
//13 -> 14
|
||||||
migrate13To14AlterMessagesTableQuery = `
|
migrate13To14AlterMessagesTableQuery = `
|
||||||
ALTER TABLE messages ADD COLUMN sid TEXT NOT NULL DEFAULT('');
|
ALTER TABLE messages ADD COLUMN sequence_id TEXT NOT NULL DEFAULT('');
|
||||||
ALTER TABLE messages ADD COLUMN deleted INT NOT NULL DEFAULT('0');
|
ALTER TABLE messages ADD COLUMN deleted INT NOT NULL DEFAULT('0');
|
||||||
CREATE INDEX IF NOT EXISTS idx_sid ON messages (sid);
|
CREATE INDEX IF NOT EXISTS idx_sequence_id ON messages (sequence_id);
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -409,7 +409,7 @@ func (c *messageCache) addMessages(ms []*message) error {
|
|||||||
}
|
}
|
||||||
_, err := stmt.Exec(
|
_, err := stmt.Exec(
|
||||||
m.ID,
|
m.ID,
|
||||||
m.SID,
|
m.SequenceID,
|
||||||
m.Time,
|
m.Time,
|
||||||
m.Expires,
|
m.Expires,
|
||||||
m.Topic,
|
m.Topic,
|
||||||
@@ -720,11 +720,11 @@ func readMessages(rows *sql.Rows) ([]*message, error) {
|
|||||||
func readMessage(rows *sql.Rows) (*message, error) {
|
func readMessage(rows *sql.Rows) (*message, error) {
|
||||||
var timestamp, expires, attachmentSize, attachmentExpires int64
|
var timestamp, expires, attachmentSize, attachmentExpires int64
|
||||||
var priority int
|
var priority int
|
||||||
var id, sid, topic, msg, title, tagsStr, click, icon, actionsStr, attachmentName, attachmentType, attachmentURL, sender, user, contentType, encoding string
|
var id, sequenceID, topic, msg, title, tagsStr, click, icon, actionsStr, attachmentName, attachmentType, attachmentURL, sender, user, contentType, encoding string
|
||||||
var deleted bool
|
var deleted bool
|
||||||
err := rows.Scan(
|
err := rows.Scan(
|
||||||
&id,
|
&id,
|
||||||
&sid,
|
&sequenceID,
|
||||||
×tamp,
|
×tamp,
|
||||||
&expires,
|
&expires,
|
||||||
&topic,
|
&topic,
|
||||||
@@ -773,13 +773,13 @@ func readMessage(rows *sql.Rows) (*message, error) {
|
|||||||
URL: attachmentURL,
|
URL: attachmentURL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Clear SID if it equals ID (we do not want the SID in the message output)
|
// Clear SequenceID if it equals ID (we do not want the SequenceID in the message output)
|
||||||
if sid == id {
|
if sequenceID == id {
|
||||||
sid = ""
|
sequenceID = ""
|
||||||
}
|
}
|
||||||
return &message{
|
return &message{
|
||||||
ID: id,
|
ID: id,
|
||||||
SID: sid,
|
SequenceID: sequenceID,
|
||||||
Time: timestamp,
|
Time: timestamp,
|
||||||
Expires: expires,
|
Expires: expires,
|
||||||
Event: messageEvent,
|
Event: messageEvent,
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ func testCacheAttachments(t *testing.T, c *messageCache) {
|
|||||||
expires1 := time.Now().Add(-4 * time.Hour).Unix() // Expired
|
expires1 := time.Now().Add(-4 * time.Hour).Unix() // Expired
|
||||||
m := newDefaultMessage("mytopic", "flower for you")
|
m := newDefaultMessage("mytopic", "flower for you")
|
||||||
m.ID = "m1"
|
m.ID = "m1"
|
||||||
m.SID = "m1"
|
m.SequenceID = "m1"
|
||||||
m.Sender = netip.MustParseAddr("1.2.3.4")
|
m.Sender = netip.MustParseAddr("1.2.3.4")
|
||||||
m.Attachment = &attachment{
|
m.Attachment = &attachment{
|
||||||
Name: "flower.jpg",
|
Name: "flower.jpg",
|
||||||
@@ -333,7 +333,7 @@ func testCacheAttachments(t *testing.T, c *messageCache) {
|
|||||||
expires2 := time.Now().Add(2 * time.Hour).Unix() // Future
|
expires2 := time.Now().Add(2 * time.Hour).Unix() // Future
|
||||||
m = newDefaultMessage("mytopic", "sending you a car")
|
m = newDefaultMessage("mytopic", "sending you a car")
|
||||||
m.ID = "m2"
|
m.ID = "m2"
|
||||||
m.SID = "m2"
|
m.SequenceID = "m2"
|
||||||
m.Sender = netip.MustParseAddr("1.2.3.4")
|
m.Sender = netip.MustParseAddr("1.2.3.4")
|
||||||
m.Attachment = &attachment{
|
m.Attachment = &attachment{
|
||||||
Name: "car.jpg",
|
Name: "car.jpg",
|
||||||
@@ -347,7 +347,7 @@ func testCacheAttachments(t *testing.T, c *messageCache) {
|
|||||||
expires3 := time.Now().Add(1 * time.Hour).Unix() // Future
|
expires3 := time.Now().Add(1 * time.Hour).Unix() // Future
|
||||||
m = newDefaultMessage("another-topic", "sending you another car")
|
m = newDefaultMessage("another-topic", "sending you another car")
|
||||||
m.ID = "m3"
|
m.ID = "m3"
|
||||||
m.SID = "m3"
|
m.SequenceID = "m3"
|
||||||
m.User = "u_BAsbaAa"
|
m.User = "u_BAsbaAa"
|
||||||
m.Sender = netip.MustParseAddr("5.6.7.8")
|
m.Sender = netip.MustParseAddr("5.6.7.8")
|
||||||
m.Attachment = &attachment{
|
m.Attachment = &attachment{
|
||||||
@@ -403,13 +403,13 @@ func TestMemCache_Attachments_Expired(t *testing.T) {
|
|||||||
func testCacheAttachmentsExpired(t *testing.T, c *messageCache) {
|
func testCacheAttachmentsExpired(t *testing.T, c *messageCache) {
|
||||||
m := newDefaultMessage("mytopic", "flower for you")
|
m := newDefaultMessage("mytopic", "flower for you")
|
||||||
m.ID = "m1"
|
m.ID = "m1"
|
||||||
m.SID = "m1"
|
m.SequenceID = "m1"
|
||||||
m.Expires = time.Now().Add(time.Hour).Unix()
|
m.Expires = time.Now().Add(time.Hour).Unix()
|
||||||
require.Nil(t, c.AddMessage(m))
|
require.Nil(t, c.AddMessage(m))
|
||||||
|
|
||||||
m = newDefaultMessage("mytopic", "message with attachment")
|
m = newDefaultMessage("mytopic", "message with attachment")
|
||||||
m.ID = "m2"
|
m.ID = "m2"
|
||||||
m.SID = "m2"
|
m.SequenceID = "m2"
|
||||||
m.Expires = time.Now().Add(2 * time.Hour).Unix()
|
m.Expires = time.Now().Add(2 * time.Hour).Unix()
|
||||||
m.Attachment = &attachment{
|
m.Attachment = &attachment{
|
||||||
Name: "car.jpg",
|
Name: "car.jpg",
|
||||||
@@ -422,7 +422,7 @@ func testCacheAttachmentsExpired(t *testing.T, c *messageCache) {
|
|||||||
|
|
||||||
m = newDefaultMessage("mytopic", "message with external attachment")
|
m = newDefaultMessage("mytopic", "message with external attachment")
|
||||||
m.ID = "m3"
|
m.ID = "m3"
|
||||||
m.SID = "m3"
|
m.SequenceID = "m3"
|
||||||
m.Expires = time.Now().Add(2 * time.Hour).Unix()
|
m.Expires = time.Now().Add(2 * time.Hour).Unix()
|
||||||
m.Attachment = &attachment{
|
m.Attachment = &attachment{
|
||||||
Name: "car.jpg",
|
Name: "car.jpg",
|
||||||
@@ -434,7 +434,7 @@ func testCacheAttachmentsExpired(t *testing.T, c *messageCache) {
|
|||||||
|
|
||||||
m = newDefaultMessage("mytopic2", "message with expired attachment")
|
m = newDefaultMessage("mytopic2", "message with expired attachment")
|
||||||
m.ID = "m4"
|
m.ID = "m4"
|
||||||
m.SID = "m4"
|
m.SequenceID = "m4"
|
||||||
m.Expires = time.Now().Add(2 * time.Hour).Unix()
|
m.Expires = time.Now().Add(2 * time.Hour).Unix()
|
||||||
m.Attachment = &attachment{
|
m.Attachment = &attachment{
|
||||||
Name: "expired-car.jpg",
|
Name: "expired-car.jpg",
|
||||||
|
|||||||
@@ -917,13 +917,13 @@ func (s *Server) handleDelete(w http.ResponseWriter, r *http.Request, v *visitor
|
|||||||
if !util.ContainsIP(s.config.VisitorRequestExemptPrefixes, v.ip) && !vrate.MessageAllowed() {
|
if !util.ContainsIP(s.config.VisitorRequestExemptPrefixes, v.ip) && !vrate.MessageAllowed() {
|
||||||
return errHTTPTooManyRequestsLimitMessages.With(t)
|
return errHTTPTooManyRequestsLimitMessages.With(t)
|
||||||
}
|
}
|
||||||
sid, e := s.sidFromPath(r.URL.Path)
|
sequenceID, e := s.sequenceIDFromPath(r.URL.Path)
|
||||||
if e != nil {
|
if e != nil {
|
||||||
return e.With(t)
|
return e.With(t)
|
||||||
}
|
}
|
||||||
// Create a delete message: empty body, same SID, deleted flag set
|
// Create a delete message: empty body, same SequenceID, deleted flag set
|
||||||
m := newDefaultMessage(t.ID, deletedMessageBody)
|
m := newDefaultMessage(t.ID, deletedMessageBody)
|
||||||
m.SID = sid
|
m.SequenceID = sequenceID
|
||||||
m.Deleted = true
|
m.Deleted = true
|
||||||
m.Sender = v.IP()
|
m.Sender = v.IP()
|
||||||
m.User = v.MaybeUserID()
|
m.User = v.MaybeUserID()
|
||||||
@@ -944,7 +944,7 @@ func (s *Server) handleDelete(w http.ResponseWriter, r *http.Request, v *visitor
|
|||||||
if err := s.messageCache.AddMessage(m); err != nil {
|
if err := s.messageCache.AddMessage(m); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logvrm(v, r, m).Tag(tagPublish).Debug("Deleted message with SID %s", sid)
|
logvrm(v, r, m).Tag(tagPublish).Debug("Deleted message with sequence ID %s", sequenceID)
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
s.messages++
|
s.messages++
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
@@ -1009,21 +1009,21 @@ func (s *Server) forwardPollRequest(v *visitor, m *message) {
|
|||||||
|
|
||||||
func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, firebase bool, email, call string, template templateMode, unifiedpush bool, err *errHTTP) {
|
func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, firebase bool, email, call string, template templateMode, unifiedpush bool, err *errHTTP) {
|
||||||
if r.Method != http.MethodGet && updatePathRegex.MatchString(r.URL.Path) {
|
if r.Method != http.MethodGet && updatePathRegex.MatchString(r.URL.Path) {
|
||||||
pathSID, err := s.sidFromPath(r.URL.Path)
|
pathSequenceID, err := s.sequenceIDFromPath(r.URL.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, false, "", "", "", false, err
|
return false, false, "", "", "", false, err
|
||||||
}
|
}
|
||||||
m.SID = pathSID
|
m.SequenceID = pathSequenceID
|
||||||
} else {
|
} else {
|
||||||
sid := readParam(r, "x-sequence-id", "sequence-id", "sid")
|
sequenceID := readParam(r, "x-sequence-id", "sequence-id", "sid")
|
||||||
if sid != "" {
|
if sequenceID != "" {
|
||||||
if sidRegex.MatchString(sid) {
|
if sidRegex.MatchString(sequenceID) {
|
||||||
m.SID = sid
|
m.SequenceID = sequenceID
|
||||||
} else {
|
} else {
|
||||||
return false, false, "", "", "", false, errHTTPBadRequestSIDInvalid
|
return false, false, "", "", "", false, errHTTPBadRequestSIDInvalid
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
m.SID = m.ID
|
m.SequenceID = m.ID
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cache = readBoolParam(r, true, "x-cache", "cache")
|
cache = readBoolParam(r, true, "x-cache", "cache")
|
||||||
@@ -1764,8 +1764,8 @@ func (s *Server) topicsFromPath(path string) ([]*topic, string, error) {
|
|||||||
return topics, parts[1], nil
|
return topics, parts[1], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// sidFromPath returns the SID from a POST path like /mytopic/sidHere
|
// sequenceIDFromPath returns the sequence ID from a POST path like /mytopic/sequenceIdHere
|
||||||
func (s *Server) sidFromPath(path string) (string, *errHTTP) {
|
func (s *Server) sequenceIDFromPath(path string) (string, *errHTTP) {
|
||||||
parts := strings.Split(path, "/")
|
parts := strings.Split(path, "/")
|
||||||
if len(parts) != 3 {
|
if len(parts) != 3 {
|
||||||
return "", errHTTPBadRequestSIDInvalid
|
return "", errHTTPBadRequestSIDInvalid
|
||||||
|
|||||||
@@ -684,7 +684,7 @@ func TestServer_PublishWithSIDInPath(t *testing.T) {
|
|||||||
response := request(t, s, "POST", "/mytopic/sid", "message", nil)
|
response := request(t, s, "POST", "/mytopic/sid", "message", nil)
|
||||||
msg := toMessage(t, response.Body.String())
|
msg := toMessage(t, response.Body.String())
|
||||||
require.NotEmpty(t, msg.ID)
|
require.NotEmpty(t, msg.ID)
|
||||||
require.Equal(t, "sid", msg.SID)
|
require.Equal(t, "sid", msg.SequenceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_PublishWithSIDInHeader(t *testing.T) {
|
func TestServer_PublishWithSIDInHeader(t *testing.T) {
|
||||||
@@ -695,7 +695,7 @@ func TestServer_PublishWithSIDInHeader(t *testing.T) {
|
|||||||
})
|
})
|
||||||
msg := toMessage(t, response.Body.String())
|
msg := toMessage(t, response.Body.String())
|
||||||
require.NotEmpty(t, msg.ID)
|
require.NotEmpty(t, msg.ID)
|
||||||
require.Equal(t, "sid", msg.SID)
|
require.Equal(t, "sid", msg.SequenceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_PublishWithSIDInPathAndHeader(t *testing.T) {
|
func TestServer_PublishWithSIDInPathAndHeader(t *testing.T) {
|
||||||
@@ -706,7 +706,7 @@ func TestServer_PublishWithSIDInPathAndHeader(t *testing.T) {
|
|||||||
})
|
})
|
||||||
msg := toMessage(t, response.Body.String())
|
msg := toMessage(t, response.Body.String())
|
||||||
require.NotEmpty(t, msg.ID)
|
require.NotEmpty(t, msg.ID)
|
||||||
require.Equal(t, "sid1", msg.SID) // SID in path has priority over SID in header
|
require.Equal(t, "sid1", msg.SequenceID) // Sequence ID in path has priority over header
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_PublishWithSIDInQuery(t *testing.T) {
|
func TestServer_PublishWithSIDInQuery(t *testing.T) {
|
||||||
@@ -715,7 +715,7 @@ func TestServer_PublishWithSIDInQuery(t *testing.T) {
|
|||||||
response := request(t, s, "PUT", "/mytopic?sid=sid1", "message", nil)
|
response := request(t, s, "PUT", "/mytopic?sid=sid1", "message", nil)
|
||||||
msg := toMessage(t, response.Body.String())
|
msg := toMessage(t, response.Body.String())
|
||||||
require.NotEmpty(t, msg.ID)
|
require.NotEmpty(t, msg.ID)
|
||||||
require.Equal(t, "sid1", msg.SID)
|
require.Equal(t, "sid1", msg.SequenceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_PublishWithSIDViaGet(t *testing.T) {
|
func TestServer_PublishWithSIDViaGet(t *testing.T) {
|
||||||
@@ -724,7 +724,7 @@ func TestServer_PublishWithSIDViaGet(t *testing.T) {
|
|||||||
response := request(t, s, "GET", "/mytopic/publish?sid=sid1", "message", nil)
|
response := request(t, s, "GET", "/mytopic/publish?sid=sid1", "message", nil)
|
||||||
msg := toMessage(t, response.Body.String())
|
msg := toMessage(t, response.Body.String())
|
||||||
require.NotEmpty(t, msg.ID)
|
require.NotEmpty(t, msg.ID)
|
||||||
require.Equal(t, "sid1", msg.SID)
|
require.Equal(t, "sid1", msg.SequenceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_PublishWithInvalidSIDInPath(t *testing.T) {
|
func TestServer_PublishWithInvalidSIDInPath(t *testing.T) {
|
||||||
|
|||||||
@@ -24,11 +24,11 @@ const (
|
|||||||
|
|
||||||
// message represents a message published to a topic
|
// message represents a message published to a topic
|
||||||
type message struct {
|
type message struct {
|
||||||
ID string `json:"id"` // Random message ID
|
ID string `json:"id"` // Random message ID
|
||||||
SID string `json:"sid,omitempty"` // Message sequence ID for updating message contents (omitted if same as ID)
|
SequenceID string `json:"sequence_id,omitempty"` // Message sequence ID for updating message contents (omitted if same as ID)
|
||||||
Time int64 `json:"time"` // Unix time in seconds
|
Time int64 `json:"time"` // Unix time in seconds
|
||||||
Expires int64 `json:"expires,omitempty"` // Unix time in seconds (not required for open/keepalive)
|
Expires int64 `json:"expires,omitempty"` // Unix time in seconds (not required for open/keepalive)
|
||||||
Event string `json:"event"` // One of the above
|
Event string `json:"event"` // One of the above
|
||||||
Topic string `json:"topic"`
|
Topic string `json:"topic"`
|
||||||
Title string `json:"title,omitempty"`
|
Title string `json:"title,omitempty"`
|
||||||
Message string `json:"message"` // Allow empty message body
|
Message string `json:"message"` // Allow empty message body
|
||||||
@@ -48,12 +48,12 @@ type message struct {
|
|||||||
|
|
||||||
func (m *message) Context() log.Context {
|
func (m *message) Context() log.Context {
|
||||||
fields := map[string]any{
|
fields := map[string]any{
|
||||||
"topic": m.Topic,
|
"topic": m.Topic,
|
||||||
"message_id": m.ID,
|
"message_id": m.ID,
|
||||||
"message_sid": m.SID,
|
"message_sequence_id": m.SequenceID,
|
||||||
"message_time": m.Time,
|
"message_time": m.Time,
|
||||||
"message_event": m.Event,
|
"message_event": m.Event,
|
||||||
"message_body_size": len(m.Message),
|
"message_body_size": len(m.Message),
|
||||||
}
|
}
|
||||||
if m.Sender.IsValid() {
|
if m.Sender.IsValid() {
|
||||||
fields["message_sender"] = m.Sender.String()
|
fields["message_sender"] = m.Sender.String()
|
||||||
@@ -94,23 +94,23 @@ func newAction() *action {
|
|||||||
|
|
||||||
// publishMessage is used as input when publishing as JSON
|
// publishMessage is used as input when publishing as JSON
|
||||||
type publishMessage struct {
|
type publishMessage struct {
|
||||||
Topic string `json:"topic"`
|
Topic string `json:"topic"`
|
||||||
SID string `json:"sid"`
|
SequenceID string `json:"sequence_id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Priority int `json:"priority"`
|
Priority int `json:"priority"`
|
||||||
Tags []string `json:"tags"`
|
Tags []string `json:"tags"`
|
||||||
Click string `json:"click"`
|
Click string `json:"click"`
|
||||||
Icon string `json:"icon"`
|
Icon string `json:"icon"`
|
||||||
Actions []action `json:"actions"`
|
Actions []action `json:"actions"`
|
||||||
Attach string `json:"attach"`
|
Attach string `json:"attach"`
|
||||||
Markdown bool `json:"markdown"`
|
Markdown bool `json:"markdown"`
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Call string `json:"call"`
|
Call string `json:"call"`
|
||||||
Cache string `json:"cache"` // use string as it defaults to true (or use &bool instead)
|
Cache string `json:"cache"` // use string as it defaults to true (or use &bool instead)
|
||||||
Firebase string `json:"firebase"` // use string as it defaults to true (or use &bool instead)
|
Firebase string `json:"firebase"` // use string as it defaults to true (or use &bool instead)
|
||||||
Delay string `json:"delay"`
|
Delay string `json:"delay"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// messageEncoder is a function that knows how to encode a message
|
// messageEncoder is a function that knows how to encode a message
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { dbAsync } from "../src/app/db";
|
|||||||
|
|
||||||
import { toNotificationParams, icon, badge } from "../src/app/notificationUtils";
|
import { toNotificationParams, icon, badge } from "../src/app/notificationUtils";
|
||||||
import initI18n from "../src/app/i18n";
|
import initI18n from "../src/app/i18n";
|
||||||
import { messageWithSID } from "../src/app/utils";
|
import { messageWithSequenceId } from "../src/app/utils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General docs for service workers and PWAs:
|
* General docs for service workers and PWAs:
|
||||||
@@ -27,14 +27,15 @@ const addNotification = async ({ subscriptionId, message }) => {
|
|||||||
|
|
||||||
// Note: SubscriptionManager duplicates this logic, so if you change it here, change it there too
|
// Note: SubscriptionManager duplicates this logic, so if you change it here, change it there too
|
||||||
|
|
||||||
// Delete existing notification with same SID (if any)
|
// Delete existing notification with same sequence ID (if any)
|
||||||
if (message.sid) {
|
const sequenceId = message.sequence_id || message.id;
|
||||||
await db.notifications.where({ subscriptionId, sid: message.sid }).delete();
|
if (sequenceId) {
|
||||||
|
await db.notifications.where({ subscriptionId, sequenceId }).delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add notification to database
|
// Add notification to database
|
||||||
await db.notifications.add({
|
await db.notifications.add({
|
||||||
...messageWithSID(message),
|
...messageWithSequenceId(message),
|
||||||
subscriptionId,
|
subscriptionId,
|
||||||
new: 1, // New marker (used for bubble indicator); cannot be boolean; Dexie index limitation
|
new: 1, // New marker (used for bubble indicator); cannot be boolean; Dexie index limitation
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -47,22 +47,25 @@ class Poller {
|
|||||||
// Filter out notifications older than the prune threshold
|
// Filter out notifications older than the prune threshold
|
||||||
const deleteAfterSeconds = await prefs.deleteAfter();
|
const deleteAfterSeconds = await prefs.deleteAfter();
|
||||||
const pruneThresholdTimestamp = deleteAfterSeconds > 0 ? Math.round(Date.now() / 1000) - deleteAfterSeconds : 0;
|
const pruneThresholdTimestamp = deleteAfterSeconds > 0 ? Math.round(Date.now() / 1000) - deleteAfterSeconds : 0;
|
||||||
const recentNotifications = pruneThresholdTimestamp > 0 ? notifications.filter((n) => n.time >= pruneThresholdTimestamp) : notifications;
|
const recentNotifications =
|
||||||
|
pruneThresholdTimestamp > 0 ? notifications.filter((n) => n.time >= pruneThresholdTimestamp) : notifications;
|
||||||
|
|
||||||
// Find the latest notification for each sequence ID
|
// Find the latest notification for each sequence ID
|
||||||
const latestBySid = this.latestNotificationsBySid(recentNotifications);
|
const latestBySequenceId = this.latestNotificationsBySequenceId(recentNotifications);
|
||||||
|
|
||||||
// Delete all existing notifications for which the latest notification is marked as deleted
|
// Delete all existing notifications for which the latest notification is marked as deleted
|
||||||
const deletedSids = Object.entries(latestBySid)
|
const deletedSequenceIds = Object.entries(latestBySequenceId)
|
||||||
.filter(([, notification]) => notification.deleted)
|
.filter(([, notification]) => notification.deleted)
|
||||||
.map(([sid]) => sid);
|
.map(([sequenceId]) => sequenceId);
|
||||||
if (deletedSids.length > 0) {
|
if (deletedSequenceIds.length > 0) {
|
||||||
console.log(`[Poller] Deleting notifications with deleted sequence IDs for ${subscription.id}`, deletedSids);
|
console.log(`[Poller] Deleting notifications with deleted sequence IDs for ${subscription.id}`, deletedSequenceIds);
|
||||||
await Promise.all(deletedSids.map((sid) => subscriptionManager.deleteNotificationBySid(subscription.id, sid)));
|
await Promise.all(
|
||||||
|
deletedSequenceIds.map((sequenceId) => subscriptionManager.deleteNotificationBySequenceId(subscription.id, sequenceId))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add only the latest notification for each non-deleted sequence
|
// Add only the latest notification for each non-deleted sequence
|
||||||
const notificationsToAdd = Object.values(latestBySid).filter((n) => !n.deleted);
|
const notificationsToAdd = Object.values(latestBySequenceId).filter((n) => !n.deleted);
|
||||||
if (notificationsToAdd.length > 0) {
|
if (notificationsToAdd.length > 0) {
|
||||||
console.log(`[Poller] Adding ${notificationsToAdd.length} notification(s) for ${subscription.id}`);
|
console.log(`[Poller] Adding ${notificationsToAdd.length} notification(s) for ${subscription.id}`);
|
||||||
await subscriptionManager.addNotifications(subscription.id, notificationsToAdd);
|
await subscriptionManager.addNotifications(subscription.id, notificationsToAdd);
|
||||||
@@ -82,18 +85,18 @@ class Poller {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Groups notifications by sid and returns only the latest (highest time) for each sequence.
|
* Groups notifications by sequenceId and returns only the latest (highest time) for each sequence.
|
||||||
* Returns an object mapping sid -> latest notification.
|
* Returns an object mapping sequenceId -> latest notification.
|
||||||
*/
|
*/
|
||||||
latestNotificationsBySid(notifications) {
|
latestNotificationsBySequenceId(notifications) {
|
||||||
const latestBySid = {};
|
const latestBySequenceId = {};
|
||||||
notifications.forEach((notification) => {
|
notifications.forEach((notification) => {
|
||||||
const sid = notification.sid || notification.id;
|
const sequenceId = notification.sequence_id || notification.id;
|
||||||
if (!(sid in latestBySid) || notification.time >= latestBySid[sid].time) {
|
if (!(sequenceId in latestBySequenceId) || notification.time >= latestBySequenceId[sequenceId].time) {
|
||||||
latestBySid[sid] = notification;
|
latestBySequenceId[sequenceId] = notification;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return latestBySid;
|
return latestBySequenceId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import api from "./Api";
|
|||||||
import notifier from "./Notifier";
|
import notifier from "./Notifier";
|
||||||
import prefs from "./Prefs";
|
import prefs from "./Prefs";
|
||||||
import db from "./db";
|
import db from "./db";
|
||||||
import { messageWithSID, topicUrl } from "./utils";
|
import { messageWithSequenceId, topicUrl } from "./utils";
|
||||||
|
|
||||||
class SubscriptionManager {
|
class SubscriptionManager {
|
||||||
constructor(dbImpl) {
|
constructor(dbImpl) {
|
||||||
@@ -15,7 +15,7 @@ class SubscriptionManager {
|
|||||||
return Promise.all(
|
return Promise.all(
|
||||||
subscriptions.map(async (s) => ({
|
subscriptions.map(async (s) => ({
|
||||||
...s,
|
...s,
|
||||||
new: await this.db.notifications.where({ subscriptionId: s.id, new: 1 }).count()
|
new: await this.db.notifications.where({ subscriptionId: s.id, new: 1 }).count(),
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ class SubscriptionManager {
|
|||||||
baseUrl,
|
baseUrl,
|
||||||
topic,
|
topic,
|
||||||
mutedUntil: 0,
|
mutedUntil: 0,
|
||||||
last: null
|
last: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
await this.db.subscriptions.put(subscription);
|
await this.db.subscriptions.put(subscription);
|
||||||
@@ -101,7 +101,7 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
const local = await this.add(remote.base_url, remote.topic, {
|
const local = await this.add(remote.base_url, remote.topic, {
|
||||||
displayName: remote.display_name, // May be undefined
|
displayName: remote.display_name, // May be undefined
|
||||||
reservation // May be null!
|
reservation, // May be null!
|
||||||
});
|
});
|
||||||
|
|
||||||
return local.id;
|
return local.id;
|
||||||
@@ -183,15 +183,15 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
// Add notification to database
|
// Add notification to database
|
||||||
await this.db.notifications.add({
|
await this.db.notifications.add({
|
||||||
...messageWithSID(notification),
|
...messageWithSequenceId(notification),
|
||||||
subscriptionId,
|
subscriptionId,
|
||||||
new: 1 // New marker (used for bubble indicator); cannot be boolean; Dexie index limitation
|
new: 1, // New marker (used for bubble indicator); cannot be boolean; Dexie index limitation
|
||||||
});
|
});
|
||||||
|
|
||||||
// FIXME consider put() for double tab
|
// FIXME consider put() for double tab
|
||||||
// Update subscription last message id (for ?since=... queries)
|
// Update subscription last message id (for ?since=... queries)
|
||||||
await this.db.subscriptions.update(subscriptionId, {
|
await this.db.subscriptions.update(subscriptionId, {
|
||||||
last: notification.id
|
last: notification.id,
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(`[SubscriptionManager] Error adding notification`, e);
|
console.error(`[SubscriptionManager] Error adding notification`, e);
|
||||||
@@ -202,12 +202,12 @@ class SubscriptionManager {
|
|||||||
/** Adds/replaces notifications, will not throw if they exist */
|
/** Adds/replaces notifications, will not throw if they exist */
|
||||||
async addNotifications(subscriptionId, notifications) {
|
async addNotifications(subscriptionId, notifications) {
|
||||||
const notificationsWithSubscriptionId = notifications.map((notification) => {
|
const notificationsWithSubscriptionId = notifications.map((notification) => {
|
||||||
return { ...messageWithSID(notification), subscriptionId };
|
return { ...messageWithSequenceId(notification), subscriptionId };
|
||||||
});
|
});
|
||||||
const lastNotificationId = notifications.at(-1).id;
|
const lastNotificationId = notifications.at(-1).id;
|
||||||
await this.db.notifications.bulkPut(notificationsWithSubscriptionId);
|
await this.db.notifications.bulkPut(notificationsWithSubscriptionId);
|
||||||
await this.db.subscriptions.update(subscriptionId, {
|
await this.db.subscriptions.update(subscriptionId, {
|
||||||
last: lastNotificationId
|
last: lastNotificationId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,8 +228,8 @@ class SubscriptionManager {
|
|||||||
await this.db.notifications.delete(notificationId);
|
await this.db.notifications.delete(notificationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteNotificationBySid(subscriptionId, sid) {
|
async deleteNotificationBySequenceId(subscriptionId, sequenceId) {
|
||||||
await this.db.notifications.where({ subscriptionId, sid }).delete();
|
await this.db.notifications.where({ subscriptionId, sequenceId }).delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteNotifications(subscriptionId) {
|
async deleteNotifications(subscriptionId) {
|
||||||
@@ -240,8 +240,8 @@ class SubscriptionManager {
|
|||||||
await this.db.notifications.where({ id: notificationId }).modify({ new: 0 });
|
await this.db.notifications.where({ id: notificationId }).modify({ new: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
async markNotificationReadBySid(subscriptionId, sid) {
|
async markNotificationReadBySequenceId(subscriptionId, sequenceId) {
|
||||||
await this.db.notifications.where({ subscriptionId, sid }).modify({ new: 0 });
|
await this.db.notifications.where({ subscriptionId, sequenceId }).modify({ new: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
async markNotificationsRead(subscriptionId) {
|
async markNotificationsRead(subscriptionId) {
|
||||||
@@ -250,19 +250,19 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
async setMutedUntil(subscriptionId, mutedUntil) {
|
async setMutedUntil(subscriptionId, mutedUntil) {
|
||||||
await this.db.subscriptions.update(subscriptionId, {
|
await this.db.subscriptions.update(subscriptionId, {
|
||||||
mutedUntil
|
mutedUntil,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setDisplayName(subscriptionId, displayName) {
|
async setDisplayName(subscriptionId, displayName) {
|
||||||
await this.db.subscriptions.update(subscriptionId, {
|
await this.db.subscriptions.update(subscriptionId, {
|
||||||
displayName
|
displayName,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async setReservation(subscriptionId, reservation) {
|
async setReservation(subscriptionId, reservation) {
|
||||||
await this.db.subscriptions.update(subscriptionId, {
|
await this.db.subscriptions.update(subscriptionId, {
|
||||||
reservation
|
reservation,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,12 +11,11 @@ const createDatabase = (username) => {
|
|||||||
const dbName = username ? `ntfy-${username}` : "ntfy"; // IndexedDB database is based on the logged-in user
|
const dbName = username ? `ntfy-${username}` : "ntfy"; // IndexedDB database is based on the logged-in user
|
||||||
const db = new Dexie(dbName);
|
const db = new Dexie(dbName);
|
||||||
|
|
||||||
db.version(6).stores({
|
db.version(3).stores({
|
||||||
// FIXME Should be 3
|
|
||||||
subscriptions: "&id,baseUrl,[baseUrl+mutedUntil]",
|
subscriptions: "&id,baseUrl,[baseUrl+mutedUntil]",
|
||||||
notifications: "&id,sid,subscriptionId,time,new,deleted,[subscriptionId+new],[subscriptionId+sid]",
|
notifications: "&id,sequenceId,subscriptionId,time,new,deleted,[subscriptionId+new],[subscriptionId+sequenceId]",
|
||||||
users: "&baseUrl,username",
|
users: "&baseUrl,username",
|
||||||
prefs: "&key",
|
prefs: "&key"
|
||||||
});
|
});
|
||||||
|
|
||||||
return db;
|
return db;
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ export const toNotificationParams = ({ subscriptionId, message, defaultTitle, to
|
|||||||
icon,
|
icon,
|
||||||
image,
|
image,
|
||||||
timestamp: message.time * 1000,
|
timestamp: message.time * 1000,
|
||||||
tag: message.sid || message.id, // Update notification if there is a sequence ID
|
tag: message.sequence_id || message.id, // Update notification if there is a sequence ID
|
||||||
renotify: true,
|
renotify: true,
|
||||||
silent: false,
|
silent: false,
|
||||||
// This is used by the notification onclick event
|
// This is used by the notification onclick event
|
||||||
|
|||||||
@@ -103,9 +103,9 @@ export const maybeActionErrors = (notification) => {
|
|||||||
return actionErrors;
|
return actionErrors;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const messageWithSID = (message) => {
|
export const messageWithSequenceId = (message) => {
|
||||||
if (!message.sid) {
|
if (!message.sequenceId) {
|
||||||
message.sid = message.id;
|
message.sequenceId = message.sequence_id || message.id;
|
||||||
}
|
}
|
||||||
return message;
|
return message;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -53,9 +53,10 @@ export const useConnectionListeners = (account, subscriptions, users, webPushTop
|
|||||||
// Note: This logic is duplicated in the Android app in SubscriberService::onNotificationReceived()
|
// Note: This logic is duplicated in the Android app in SubscriberService::onNotificationReceived()
|
||||||
// and FirebaseService::handleMessage().
|
// and FirebaseService::handleMessage().
|
||||||
|
|
||||||
// Delete existing notification with same sid, if any
|
// Delete existing notification with same sequenceId, if any
|
||||||
if (notification.sid) {
|
const sequenceId = notification.sequence_id || notification.id;
|
||||||
await subscriptionManager.deleteNotificationBySid(subscriptionId, notification.sid);
|
if (sequenceId) {
|
||||||
|
await subscriptionManager.deleteNotificationBySequenceId(subscriptionId, sequenceId);
|
||||||
}
|
}
|
||||||
// Add notification to database
|
// Add notification to database
|
||||||
if (!notification.deleted) {
|
if (!notification.deleted) {
|
||||||
|
|||||||
Reference in New Issue
Block a user