87 lines
2.7 KiB
Go
87 lines
2.7 KiB
Go
package controlplane
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
const pageCursorVersion = 1
|
|
|
|
type pageCursorPayload struct {
|
|
Version int `json:"v"`
|
|
Kind string `json:"kind"`
|
|
ChatID string `json:"chat_id,omitempty"`
|
|
SortTime string `json:"sort_time"`
|
|
MessageID string `json:"message_id,omitempty"`
|
|
}
|
|
|
|
type MessagePageCursor struct {
|
|
ChatID string
|
|
SourceTime string
|
|
MessageID string
|
|
}
|
|
|
|
type ConversationPageCursor struct {
|
|
SortTime string
|
|
ChatID string
|
|
}
|
|
|
|
func EncodeMessagePageCursor(chatID string, sourceTime time.Time, messageID string) string {
|
|
return encodePageCursor(pageCursorPayload{
|
|
Version: pageCursorVersion, Kind: "messages", ChatID: chatID,
|
|
SortTime: sourceTime.UTC().Format(time.RFC3339Nano), MessageID: messageID,
|
|
})
|
|
}
|
|
|
|
func DecodeMessagePageCursor(raw, chatID string) (*MessagePageCursor, error) {
|
|
payload, err := decodePageCursor(raw, "messages")
|
|
if err != nil || payload.ChatID != chatID || payload.MessageID == "" {
|
|
return nil, errors.New("invalid message cursor")
|
|
}
|
|
if _, err := time.Parse(time.RFC3339Nano, payload.SortTime); err != nil {
|
|
return nil, errors.New("invalid message cursor")
|
|
}
|
|
return &MessagePageCursor{ChatID: payload.ChatID, SourceTime: payload.SortTime, MessageID: payload.MessageID}, nil
|
|
}
|
|
|
|
func EncodeConversationPageCursor(sortTime time.Time, chatID string) string {
|
|
return encodePageCursor(pageCursorPayload{
|
|
Version: pageCursorVersion, Kind: "conversations", ChatID: chatID,
|
|
SortTime: sortTime.UTC().Format(time.RFC3339Nano),
|
|
})
|
|
}
|
|
|
|
func DecodeConversationPageCursor(raw string) (*ConversationPageCursor, error) {
|
|
payload, err := decodePageCursor(raw, "conversations")
|
|
if err != nil || payload.ChatID == "" {
|
|
return nil, errors.New("invalid conversation cursor")
|
|
}
|
|
if _, err := time.Parse(time.RFC3339Nano, payload.SortTime); err != nil {
|
|
return nil, errors.New("invalid conversation cursor")
|
|
}
|
|
return &ConversationPageCursor{SortTime: payload.SortTime, ChatID: payload.ChatID}, nil
|
|
}
|
|
|
|
func encodePageCursor(payload pageCursorPayload) string {
|
|
encoded, _ := json.Marshal(payload)
|
|
return base64.RawURLEncoding.EncodeToString(encoded)
|
|
}
|
|
|
|
func decodePageCursor(raw, kind string) (pageCursorPayload, error) {
|
|
if strings.TrimSpace(raw) == "" {
|
|
return pageCursorPayload{}, errors.New("cursor is empty")
|
|
}
|
|
decoded, err := base64.RawURLEncoding.DecodeString(raw)
|
|
if err != nil {
|
|
return pageCursorPayload{}, errors.New("cursor is not base64url")
|
|
}
|
|
var payload pageCursorPayload
|
|
if err := json.Unmarshal(decoded, &payload); err != nil || payload.Version != pageCursorVersion || payload.Kind != kind || payload.SortTime == "" {
|
|
return pageCursorPayload{}, errors.New("cursor payload is invalid")
|
|
}
|
|
return payload, nil
|
|
}
|