Files

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
}