Remove mtime
This commit is contained in:
@@ -31,7 +31,6 @@ const (
|
|||||||
mid TEXT NOT NULL,
|
mid TEXT NOT NULL,
|
||||||
sid TEXT NOT NULL,
|
sid TEXT NOT NULL,
|
||||||
time INT NOT NULL,
|
time INT NOT NULL,
|
||||||
mtime INT NOT NULL,
|
|
||||||
expires INT NOT NULL,
|
expires INT NOT NULL,
|
||||||
topic TEXT NOT NULL,
|
topic TEXT NOT NULL,
|
||||||
message TEXT NOT NULL,
|
message TEXT NOT NULL,
|
||||||
@@ -57,7 +56,6 @@ const (
|
|||||||
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_sid ON messages (sid);
|
||||||
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_mtime ON messages (mtime);
|
|
||||||
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);
|
||||||
CREATE INDEX IF NOT EXISTS idx_sender ON messages (sender);
|
CREATE INDEX IF NOT EXISTS idx_sender ON messages (sender);
|
||||||
@@ -71,53 +69,53 @@ const (
|
|||||||
COMMIT;
|
COMMIT;
|
||||||
`
|
`
|
||||||
insertMessageQuery = `
|
insertMessageQuery = `
|
||||||
INSERT INTO messages (mid, sid, time, mtime, 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, 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)
|
||||||
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, mtime, 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, 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
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE mid = ?
|
WHERE mid = ?
|
||||||
`
|
`
|
||||||
selectMessagesSinceTimeQuery = `
|
selectMessagesSinceTimeQuery = `
|
||||||
SELECT mid, sid, time, mtime, 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, 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
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE topic = ? AND time >= ? AND published = 1
|
WHERE topic = ? AND time >= ? AND published = 1
|
||||||
ORDER BY mtime, id
|
ORDER BY time, id
|
||||||
`
|
`
|
||||||
selectMessagesSinceTimeIncludeScheduledQuery = `
|
selectMessagesSinceTimeIncludeScheduledQuery = `
|
||||||
SELECT mid, sid, time, mtime, 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, 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
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE topic = ? AND time >= ?
|
WHERE topic = ? AND time >= ?
|
||||||
ORDER BY mtime, id
|
ORDER BY time, id
|
||||||
`
|
`
|
||||||
selectMessagesSinceIDQuery = `
|
selectMessagesSinceIDQuery = `
|
||||||
SELECT mid, sid, time, mtime, 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, 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
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE topic = ? AND id > ? AND published = 1
|
WHERE topic = ? AND id > ? AND published = 1
|
||||||
ORDER BY mtime, id
|
ORDER BY time, id
|
||||||
`
|
`
|
||||||
selectMessagesSinceIDIncludeScheduledQuery = `
|
selectMessagesSinceIDIncludeScheduledQuery = `
|
||||||
SELECT mid, sid, time, mtime, 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, 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
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE topic = ? AND (id > ? OR published = 0)
|
WHERE topic = ? AND (id > ? OR published = 0)
|
||||||
ORDER BY mtime, id
|
ORDER BY time, id
|
||||||
`
|
`
|
||||||
selectMessagesLatestQuery = `
|
selectMessagesLatestQuery = `
|
||||||
SELECT mid, sid, time, mtime, 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, 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
|
||||||
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, mtime, 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, 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
|
||||||
FROM messages
|
FROM messages
|
||||||
WHERE time <= ? AND published = 0
|
WHERE time <= ? AND published = 0
|
||||||
ORDER BY mtime, id
|
ORDER BY time, id
|
||||||
`
|
`
|
||||||
selectMessagesExpiredQuery = `SELECT mid FROM messages WHERE expires <= ? AND published = 1`
|
selectMessagesExpiredQuery = `SELECT mid FROM messages WHERE expires <= ? AND published = 1`
|
||||||
updateMessagePublishedQuery = `UPDATE messages SET published = 1 WHERE mid = ?`
|
updateMessagePublishedQuery = `UPDATE messages SET published = 1 WHERE mid = ?`
|
||||||
@@ -270,10 +268,8 @@ const (
|
|||||||
//13 -> 14
|
//13 -> 14
|
||||||
migrate13To14AlterMessagesTableQuery = `
|
migrate13To14AlterMessagesTableQuery = `
|
||||||
ALTER TABLE messages ADD COLUMN sid TEXT NOT NULL DEFAULT('');
|
ALTER TABLE messages ADD COLUMN sid TEXT NOT NULL DEFAULT('');
|
||||||
ALTER TABLE messages ADD COLUMN mtime INT NOT NULL DEFAULT('0');
|
|
||||||
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_sid ON messages (sid);
|
||||||
CREATE INDEX IF NOT EXISTS idx_mtime ON messages (mtime);
|
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -415,7 +411,6 @@ func (c *messageCache) addMessages(ms []*message) error {
|
|||||||
m.ID,
|
m.ID,
|
||||||
m.SID,
|
m.SID,
|
||||||
m.Time,
|
m.Time,
|
||||||
m.MTime,
|
|
||||||
m.Expires,
|
m.Expires,
|
||||||
m.Topic,
|
m.Topic,
|
||||||
m.Message,
|
m.Message,
|
||||||
@@ -723,14 +718,13 @@ func readMessages(rows *sql.Rows) ([]*message, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func readMessage(rows *sql.Rows) (*message, error) {
|
func readMessage(rows *sql.Rows) (*message, error) {
|
||||||
var timestamp, mtimestamp, expires, attachmentSize, attachmentExpires int64
|
var timestamp, expires, attachmentSize, attachmentExpires int64
|
||||||
var priority, deleted int
|
var priority, deleted int
|
||||||
var id, sid, topic, msg, title, tagsStr, click, icon, actionsStr, attachmentName, attachmentType, attachmentURL, sender, user, contentType, encoding string
|
var id, sid, topic, msg, title, tagsStr, click, icon, actionsStr, attachmentName, attachmentType, attachmentURL, sender, user, contentType, encoding string
|
||||||
err := rows.Scan(
|
err := rows.Scan(
|
||||||
&id,
|
&id,
|
||||||
&sid,
|
&sid,
|
||||||
×tamp,
|
×tamp,
|
||||||
&mtimestamp,
|
|
||||||
&expires,
|
&expires,
|
||||||
&topic,
|
&topic,
|
||||||
&msg,
|
&msg,
|
||||||
@@ -782,7 +776,6 @@ func readMessage(rows *sql.Rows) (*message, error) {
|
|||||||
ID: id,
|
ID: id,
|
||||||
SID: sid,
|
SID: sid,
|
||||||
Time: timestamp,
|
Time: timestamp,
|
||||||
MTime: mtimestamp,
|
|
||||||
Expires: expires,
|
Expires: expires,
|
||||||
Event: messageEvent,
|
Event: messageEvent,
|
||||||
Topic: topic,
|
Topic: topic,
|
||||||
|
|||||||
@@ -24,11 +24,9 @@ func TestMemCache_Messages(t *testing.T) {
|
|||||||
func testCacheMessages(t *testing.T, c *messageCache) {
|
func testCacheMessages(t *testing.T, c *messageCache) {
|
||||||
m1 := newDefaultMessage("mytopic", "my message")
|
m1 := newDefaultMessage("mytopic", "my message")
|
||||||
m1.Time = 1
|
m1.Time = 1
|
||||||
m1.MTime = 1000
|
|
||||||
|
|
||||||
m2 := newDefaultMessage("mytopic", "my other message")
|
m2 := newDefaultMessage("mytopic", "my other message")
|
||||||
m2.Time = 2
|
m2.Time = 2
|
||||||
m2.MTime = 2000
|
|
||||||
|
|
||||||
require.Nil(t, c.AddMessage(m1))
|
require.Nil(t, c.AddMessage(m1))
|
||||||
require.Nil(t, c.AddMessage(newDefaultMessage("example", "my example message")))
|
require.Nil(t, c.AddMessage(newDefaultMessage("example", "my example message")))
|
||||||
@@ -126,13 +124,10 @@ func testCacheMessagesScheduled(t *testing.T, c *messageCache) {
|
|||||||
m1 := newDefaultMessage("mytopic", "message 1")
|
m1 := newDefaultMessage("mytopic", "message 1")
|
||||||
m2 := newDefaultMessage("mytopic", "message 2")
|
m2 := newDefaultMessage("mytopic", "message 2")
|
||||||
m2.Time = time.Now().Add(time.Hour).Unix()
|
m2.Time = time.Now().Add(time.Hour).Unix()
|
||||||
m2.MTime = time.Now().Add(time.Hour).UnixMilli()
|
|
||||||
m3 := newDefaultMessage("mytopic", "message 3")
|
m3 := newDefaultMessage("mytopic", "message 3")
|
||||||
m3.Time = time.Now().Add(time.Minute).Unix() // earlier than m2!
|
m3.Time = time.Now().Add(time.Minute).Unix() // earlier than m2!
|
||||||
m3.MTime = time.Now().Add(time.Minute).UnixMilli() // earlier than m2!
|
|
||||||
m4 := newDefaultMessage("mytopic2", "message 4")
|
m4 := newDefaultMessage("mytopic2", "message 4")
|
||||||
m4.Time = time.Now().Add(time.Minute).Unix()
|
m4.Time = time.Now().Add(time.Minute).Unix()
|
||||||
m4.MTime = time.Now().Add(time.Minute).UnixMilli()
|
|
||||||
require.Nil(t, c.AddMessage(m1))
|
require.Nil(t, c.AddMessage(m1))
|
||||||
require.Nil(t, c.AddMessage(m2))
|
require.Nil(t, c.AddMessage(m2))
|
||||||
require.Nil(t, c.AddMessage(m3))
|
require.Nil(t, c.AddMessage(m3))
|
||||||
@@ -206,25 +201,18 @@ func TestMemCache_MessagesSinceID(t *testing.T) {
|
|||||||
func testCacheMessagesSinceID(t *testing.T, c *messageCache) {
|
func testCacheMessagesSinceID(t *testing.T, c *messageCache) {
|
||||||
m1 := newDefaultMessage("mytopic", "message 1")
|
m1 := newDefaultMessage("mytopic", "message 1")
|
||||||
m1.Time = 100
|
m1.Time = 100
|
||||||
m1.MTime = 100000
|
|
||||||
m2 := newDefaultMessage("mytopic", "message 2")
|
m2 := newDefaultMessage("mytopic", "message 2")
|
||||||
m2.Time = 200
|
m2.Time = 200
|
||||||
m2.MTime = 200000
|
|
||||||
m3 := newDefaultMessage("mytopic", "message 3")
|
m3 := newDefaultMessage("mytopic", "message 3")
|
||||||
m3.Time = time.Now().Add(time.Hour).Unix() // Scheduled, in the future, later than m7 and m5
|
m3.Time = time.Now().Add(time.Hour).Unix() // Scheduled, in the future, later than m7 and m5
|
||||||
m3.MTime = time.Now().Add(time.Hour).UnixMilli() // Scheduled, in the future, later than m7 and m5
|
|
||||||
m4 := newDefaultMessage("mytopic", "message 4")
|
m4 := newDefaultMessage("mytopic", "message 4")
|
||||||
m4.Time = 400
|
m4.Time = 400
|
||||||
m4.MTime = 400000
|
|
||||||
m5 := newDefaultMessage("mytopic", "message 5")
|
m5 := newDefaultMessage("mytopic", "message 5")
|
||||||
m5.Time = time.Now().Add(time.Minute).Unix() // Scheduled, in the future, later than m7
|
m5.Time = time.Now().Add(time.Minute).Unix() // Scheduled, in the future, later than m7
|
||||||
m5.MTime = time.Now().Add(time.Minute).UnixMilli() // Scheduled, in the future, later than m7
|
|
||||||
m6 := newDefaultMessage("mytopic", "message 6")
|
m6 := newDefaultMessage("mytopic", "message 6")
|
||||||
m6.Time = 600
|
m6.Time = 600
|
||||||
m6.MTime = 600000
|
|
||||||
m7 := newDefaultMessage("mytopic", "message 7")
|
m7 := newDefaultMessage("mytopic", "message 7")
|
||||||
m7.Time = 700
|
m7.Time = 700
|
||||||
m7.MTime = 700000
|
|
||||||
|
|
||||||
require.Nil(t, c.AddMessage(m1))
|
require.Nil(t, c.AddMessage(m1))
|
||||||
require.Nil(t, c.AddMessage(m2))
|
require.Nil(t, c.AddMessage(m2))
|
||||||
@@ -285,17 +273,14 @@ func testCachePrune(t *testing.T, c *messageCache) {
|
|||||||
|
|
||||||
m1 := newDefaultMessage("mytopic", "my message")
|
m1 := newDefaultMessage("mytopic", "my message")
|
||||||
m1.Time = now - 10
|
m1.Time = now - 10
|
||||||
m1.MTime = (now - 10) * 1000
|
|
||||||
m1.Expires = now - 5
|
m1.Expires = now - 5
|
||||||
|
|
||||||
m2 := newDefaultMessage("mytopic", "my other message")
|
m2 := newDefaultMessage("mytopic", "my other message")
|
||||||
m2.Time = now - 5
|
m2.Time = now - 5
|
||||||
m2.MTime = (now - 5) * 1000
|
|
||||||
m2.Expires = now + 5 // In the future
|
m2.Expires = now + 5 // In the future
|
||||||
|
|
||||||
m3 := newDefaultMessage("another_topic", "and another one")
|
m3 := newDefaultMessage("another_topic", "and another one")
|
||||||
m3.Time = now - 12
|
m3.Time = now - 12
|
||||||
m3.MTime = (now - 12) * 1000
|
|
||||||
m3.Expires = now - 2
|
m3.Expires = now - 2
|
||||||
|
|
||||||
require.Nil(t, c.AddMessage(m1))
|
require.Nil(t, c.AddMessage(m1))
|
||||||
@@ -546,7 +531,6 @@ func TestSqliteCache_Migration_From1(t *testing.T) {
|
|||||||
// Add delayed message
|
// Add delayed message
|
||||||
delayedMessage := newDefaultMessage("mytopic", "some delayed message")
|
delayedMessage := newDefaultMessage("mytopic", "some delayed message")
|
||||||
delayedMessage.Time = time.Now().Add(time.Minute).Unix()
|
delayedMessage.Time = time.Now().Add(time.Minute).Unix()
|
||||||
delayedMessage.MTime = time.Now().Add(time.Minute).UnixMilli()
|
|
||||||
require.Nil(t, c.AddMessage(delayedMessage))
|
require.Nil(t, c.AddMessage(delayedMessage))
|
||||||
|
|
||||||
// 10, not 11!
|
// 10, not 11!
|
||||||
|
|||||||
@@ -874,7 +874,7 @@ func (s *Server) handlePublish(w http.ResponseWriter, r *http.Request, v *visito
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
minc(metricMessagesPublishedSuccess)
|
minc(metricMessagesPublishedSuccess)
|
||||||
return s.writeJSON(w, m)
|
return s.writeJSON(w, m.forJSON())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handlePublishMatrix(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
func (s *Server) handlePublishMatrix(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
@@ -1291,7 +1291,7 @@ func (s *Server) handleBodyAsAttachment(r *http.Request, v *visitor, m *message,
|
|||||||
func (s *Server) handleSubscribeJSON(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
func (s *Server) handleSubscribeJSON(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
encoder := func(msg *message) (string, error) {
|
encoder := func(msg *message) (string, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if err := json.NewEncoder(&buf).Encode(&msg); err != nil {
|
if err := json.NewEncoder(&buf).Encode(msg.forJSON()); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return buf.String(), nil
|
return buf.String(), nil
|
||||||
@@ -1302,7 +1302,7 @@ func (s *Server) handleSubscribeJSON(w http.ResponseWriter, r *http.Request, v *
|
|||||||
func (s *Server) handleSubscribeSSE(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
func (s *Server) handleSubscribeSSE(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
encoder := func(msg *message) (string, error) {
|
encoder := func(msg *message) (string, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
if err := json.NewEncoder(&buf).Encode(&msg); err != nil {
|
if err := json.NewEncoder(&buf).Encode(msg.forJSON()); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
if msg.Event != messageEvent {
|
if msg.Event != messageEvent {
|
||||||
|
|||||||
@@ -24,10 +24,9 @@ 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"` // Message sequence ID for updating message contents
|
SID string `json:"sid,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
|
||||||
MTime int64 `json:"mtime"` // Unix time in milliseconds
|
|
||||||
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"`
|
||||||
@@ -53,7 +52,6 @@ func (m *message) Context() log.Context {
|
|||||||
"message_id": m.ID,
|
"message_id": m.ID,
|
||||||
"message_sid": m.SID,
|
"message_sid": m.SID,
|
||||||
"message_time": m.Time,
|
"message_time": m.Time,
|
||||||
"message_mtime": m.MTime,
|
|
||||||
"message_event": m.Event,
|
"message_event": m.Event,
|
||||||
"message_body_size": len(m.Message),
|
"message_body_size": len(m.Message),
|
||||||
}
|
}
|
||||||
@@ -66,6 +64,16 @@ func (m *message) Context() log.Context {
|
|||||||
return fields
|
return fields
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// forJSON returns a copy of the message prepared for JSON output.
|
||||||
|
// It clears SID if it equals ID (to avoid redundant output).
|
||||||
|
func (m *message) forJSON() *message {
|
||||||
|
msg := *m
|
||||||
|
if msg.SID == msg.ID {
|
||||||
|
msg.SID = "" // Will be omitted due to omitempty
|
||||||
|
}
|
||||||
|
return &msg
|
||||||
|
}
|
||||||
|
|
||||||
type attachment struct {
|
type attachment struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Type string `json:"type,omitempty"`
|
Type string `json:"type,omitempty"`
|
||||||
@@ -123,7 +131,6 @@ func newMessage(event, topic, msg string) *message {
|
|||||||
return &message{
|
return &message{
|
||||||
ID: util.RandomString(messageIDLength),
|
ID: util.RandomString(messageIDLength),
|
||||||
Time: time.Now().Unix(),
|
Time: time.Now().Unix(),
|
||||||
MTime: time.Now().UnixMilli(),
|
|
||||||
Event: event,
|
Event: event,
|
||||||
Topic: topic,
|
Topic: topic,
|
||||||
Message: msg,
|
Message: msg,
|
||||||
@@ -162,11 +169,7 @@ type sinceMarker struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newSinceTime(timestamp int64) sinceMarker {
|
func newSinceTime(timestamp int64) sinceMarker {
|
||||||
return newSinceMTime(timestamp * 1000)
|
return sinceMarker{time.Unix(timestamp, 0), ""}
|
||||||
}
|
|
||||||
|
|
||||||
func newSinceMTime(mtimestamp int64) sinceMarker {
|
|
||||||
return sinceMarker{time.UnixMilli(mtimestamp), ""}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSinceID(id string) sinceMarker {
|
func newSinceID(id string) sinceMarker {
|
||||||
@@ -557,7 +560,7 @@ func newWebPushPayload(subscriptionID string, message *message) *webPushPayload
|
|||||||
return &webPushPayload{
|
return &webPushPayload{
|
||||||
Event: webPushMessageEvent,
|
Event: webPushMessageEvent,
|
||||||
SubscriptionID: subscriptionID,
|
SubscriptionID: subscriptionID,
|
||||||
Message: message,
|
Message: message.forJSON(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,8 +70,7 @@
|
|||||||
"notifications_delete": "Delete",
|
"notifications_delete": "Delete",
|
||||||
"notifications_copied_to_clipboard": "Copied to clipboard",
|
"notifications_copied_to_clipboard": "Copied to clipboard",
|
||||||
"notifications_tags": "Tags",
|
"notifications_tags": "Tags",
|
||||||
"notifications_sid": "Sequence ID",
|
"notifications_modified": "modified {{date}}",
|
||||||
"notifications_revisions": "Revisions",
|
|
||||||
"notifications_priority_x": "Priority {{priority}}",
|
"notifications_priority_x": "Priority {{priority}}",
|
||||||
"notifications_new_indicator": "New notification",
|
"notifications_new_indicator": "New notification",
|
||||||
"notifications_attachment_image": "Attachment image",
|
"notifications_attachment_image": "Attachment image",
|
||||||
|
|||||||
@@ -25,9 +25,6 @@ const addNotification = async ({ subscriptionId, message }) => {
|
|||||||
const db = await dbAsync();
|
const db = await dbAsync();
|
||||||
const populatedMessage = message;
|
const populatedMessage = message;
|
||||||
|
|
||||||
if (!("mtime" in populatedMessage)) {
|
|
||||||
populatedMessage.mtime = message.time * 1000;
|
|
||||||
}
|
|
||||||
if (!("sid" in populatedMessage)) {
|
if (!("sid" in populatedMessage)) {
|
||||||
populatedMessage.sid = message.id;
|
populatedMessage.sid = message.id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,7 +157,7 @@ class SubscriptionManager {
|
|||||||
// killing performance. See https://dexie.org/docs/Collection/Collection.offset()#a-better-paging-approach
|
// killing performance. See https://dexie.org/docs/Collection/Collection.offset()#a-better-paging-approach
|
||||||
|
|
||||||
const notifications = await this.db.notifications
|
const notifications = await this.db.notifications
|
||||||
.orderBy("mtime") // Sort by time first
|
.orderBy("time") // Sort by time
|
||||||
.filter((n) => n.subscriptionId === subscriptionId)
|
.filter((n) => n.subscriptionId === subscriptionId)
|
||||||
.reverse()
|
.reverse()
|
||||||
.toArray();
|
.toArray();
|
||||||
@@ -167,30 +167,39 @@ class SubscriptionManager {
|
|||||||
|
|
||||||
async getAllNotifications() {
|
async getAllNotifications() {
|
||||||
const notifications = await this.db.notifications
|
const notifications = await this.db.notifications
|
||||||
.orderBy("mtime") // Efficient, see docs
|
.orderBy("time") // Efficient, see docs
|
||||||
.reverse()
|
.reverse()
|
||||||
.toArray();
|
.toArray();
|
||||||
|
|
||||||
return this.groupNotificationsBySID(notifications);
|
return this.groupNotificationsBySID(notifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collapse notification updates based on sids
|
// Collapse notification updates based on sids, keeping only the latest version
|
||||||
|
// Also tracks the original time (earliest) for each sequence
|
||||||
groupNotificationsBySID(notifications) {
|
groupNotificationsBySID(notifications) {
|
||||||
const results = {};
|
const latestBySid = {};
|
||||||
|
const originalTimeBySid = {};
|
||||||
|
|
||||||
notifications.forEach((notification) => {
|
notifications.forEach((notification) => {
|
||||||
const key = `${notification.subscriptionId}:${notification.sid}`;
|
const key = `${notification.subscriptionId}:${notification.sid}`;
|
||||||
if (key in results) {
|
|
||||||
if ("history" in results[key]) {
|
// Track the latest notification for each sid (first one since sorted DESC)
|
||||||
results[key].history.push(notification);
|
if (!(key in latestBySid)) {
|
||||||
} else {
|
latestBySid[key] = notification;
|
||||||
results[key].history = [notification];
|
}
|
||||||
}
|
|
||||||
} else {
|
// Track the original (earliest) time for each sid
|
||||||
results[key] = notification;
|
const currentOriginal = originalTimeBySid[key];
|
||||||
|
if (currentOriginal === undefined || notification.time < currentOriginal) {
|
||||||
|
originalTimeBySid[key] = notification.time;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return Object.values(results);
|
// Return latest notifications with originalTime set
|
||||||
|
return Object.entries(latestBySid).map(([key, notification]) => ({
|
||||||
|
...notification,
|
||||||
|
originalTime: originalTimeBySid[key],
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds notification, or returns false if it already exists */
|
/** Adds notification, or returns false if it already exists */
|
||||||
@@ -201,9 +210,6 @@ class SubscriptionManager {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const populatedNotification = notification;
|
const populatedNotification = notification;
|
||||||
if (!("mtime" in populatedNotification)) {
|
|
||||||
populatedNotification.mtime = notification.time * 1000;
|
|
||||||
}
|
|
||||||
if (!("sid" in populatedNotification)) {
|
if (!("sid" in populatedNotification)) {
|
||||||
populatedNotification.sid = notification.id;
|
populatedNotification.sid = notification.id;
|
||||||
}
|
}
|
||||||
@@ -227,9 +233,6 @@ class SubscriptionManager {
|
|||||||
async addNotifications(subscriptionId, notifications) {
|
async addNotifications(subscriptionId, notifications) {
|
||||||
const notificationsWithSubscriptionId = notifications.map((notification) => {
|
const notificationsWithSubscriptionId = notifications.map((notification) => {
|
||||||
const populatedNotification = notification;
|
const populatedNotification = notification;
|
||||||
if (!("mtime" in populatedNotification)) {
|
|
||||||
populatedNotification.mtime = notification.time * 1000;
|
|
||||||
}
|
|
||||||
if (!("sid" in populatedNotification)) {
|
if (!("sid" in populatedNotification)) {
|
||||||
populatedNotification.sid = notification.id;
|
populatedNotification.sid = notification.id;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ 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(3).stores({
|
db.version(4).stores({
|
||||||
subscriptions: "&id,baseUrl,[baseUrl+mutedUntil]",
|
subscriptions: "&id,baseUrl,[baseUrl+mutedUntil]",
|
||||||
notifications: "&id,sid,subscriptionId,time,mtime,new,[subscriptionId+new]", // compound key for query performance
|
notifications: "&id,sid,subscriptionId,time,new,[subscriptionId+new]", // compound key for query performance
|
||||||
users: "&baseUrl,username",
|
users: "&baseUrl,username",
|
||||||
prefs: "&key",
|
prefs: "&key",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export const toNotificationParams = ({ subscriptionId, message, defaultTitle, to
|
|||||||
badge,
|
badge,
|
||||||
icon,
|
icon,
|
||||||
image,
|
image,
|
||||||
timestamp: message.mtime,
|
timestamp: message.time * 1000,
|
||||||
tag,
|
tag,
|
||||||
renotify: true,
|
renotify: true,
|
||||||
silent: false,
|
silent: false,
|
||||||
|
|||||||
@@ -236,7 +236,9 @@ const NotificationItem = (props) => {
|
|||||||
const { t, i18n } = useTranslation();
|
const { t, i18n } = useTranslation();
|
||||||
const { notification } = props;
|
const { notification } = props;
|
||||||
const { attachment } = notification;
|
const { attachment } = notification;
|
||||||
const date = formatShortDateTime(notification.time, i18n.language);
|
const isModified = notification.originalTime && notification.originalTime !== notification.time;
|
||||||
|
const originalDate = formatShortDateTime(notification.originalTime || notification.time, i18n.language);
|
||||||
|
const modifiedDate = isModified ? formatShortDateTime(notification.time, i18n.language) : null;
|
||||||
const otherTags = unmatchedTags(notification.tags);
|
const otherTags = unmatchedTags(notification.tags);
|
||||||
const tags = otherTags.length > 0 ? otherTags.join(", ") : null;
|
const tags = otherTags.length > 0 ? otherTags.join(", ") : null;
|
||||||
const handleDelete = async () => {
|
const handleDelete = async () => {
|
||||||
@@ -267,8 +269,6 @@ const NotificationItem = (props) => {
|
|||||||
const hasUserActions = notification.actions && notification.actions.length > 0;
|
const hasUserActions = notification.actions && notification.actions.length > 0;
|
||||||
const showActions = hasAttachmentActions || hasClickAction || hasUserActions;
|
const showActions = hasAttachmentActions || hasClickAction || hasUserActions;
|
||||||
|
|
||||||
const showSid = notification.id !== notification.sid || notification.history;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card sx={{ padding: 1 }} role="listitem" aria-label={t("notifications_list_item")}>
|
<Card sx={{ padding: 1 }} role="listitem" aria-label={t("notifications_list_item")}>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
@@ -289,7 +289,8 @@ const NotificationItem = (props) => {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
<Typography sx={{ fontSize: 14 }} color="text.secondary">
|
<Typography sx={{ fontSize: 14 }} color="text.secondary">
|
||||||
{date}
|
{originalDate}
|
||||||
|
{modifiedDate && ` (${t("notifications_modified", { date: modifiedDate })})`}
|
||||||
{[1, 2, 4, 5].includes(notification.priority) && (
|
{[1, 2, 4, 5].includes(notification.priority) && (
|
||||||
<img
|
<img
|
||||||
src={priorityFiles[notification.priority]}
|
src={priorityFiles[notification.priority]}
|
||||||
@@ -325,16 +326,6 @@ const NotificationItem = (props) => {
|
|||||||
{t("notifications_tags")}: {tags}
|
{t("notifications_tags")}: {tags}
|
||||||
</Typography>
|
</Typography>
|
||||||
)}
|
)}
|
||||||
{showSid && (
|
|
||||||
<Typography sx={{ fontSize: 14 }} color="text.secondary">
|
|
||||||
{t("notifications_sid")}: {notification.sid}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
{notification.history && (
|
|
||||||
<Typography sx={{ fontSize: 14 }} color="text.secondary">
|
|
||||||
{t("notifications_revisions")}: {notification.history.length + 1}
|
|
||||||
</Typography>
|
|
||||||
)}
|
|
||||||
</CardContent>
|
</CardContent>
|
||||||
{showActions && (
|
{showActions && (
|
||||||
<CardActions sx={{ paddingTop: 0 }}>
|
<CardActions sx={{ paddingTop: 0 }}>
|
||||||
|
|||||||
Reference in New Issue
Block a user