2023-04-10 20:39:49 +00:00
|
|
|
package rest
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"github.com/multiformats/go-multiaddr"
|
|
|
|
"github.com/waku-org/go-waku/waku/v2/node"
|
|
|
|
"github.com/waku-org/go-waku/waku/v2/protocol/store"
|
|
|
|
"github.com/waku-org/go-waku/waku/v2/protocol/store/pb"
|
|
|
|
)
|
|
|
|
|
|
|
|
type StoreService struct {
|
|
|
|
node *node.WakuNode
|
|
|
|
mux *chi.Mux
|
|
|
|
}
|
|
|
|
|
|
|
|
type StoreResponse struct {
|
|
|
|
Messages []StoreWakuMessage `json:"messages"`
|
|
|
|
Cursor *HistoryCursor `json:"cursor,omitempty"`
|
|
|
|
ErrorMessage string `json:"error_message,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type HistoryCursor struct {
|
2023-11-10 18:31:36 +00:00
|
|
|
PubsubTopic string `json:"pubsubTopic"`
|
|
|
|
SenderTime string `json:"senderTime"`
|
|
|
|
StoreTime string `json:"storeTime"`
|
2023-04-10 20:39:49 +00:00
|
|
|
Digest []byte `json:"digest"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type StoreWakuMessage struct {
|
2023-11-10 18:31:36 +00:00
|
|
|
Payload []byte `json:"payload"`
|
|
|
|
ContentTopic string `json:"contentTopic"`
|
|
|
|
Version *uint32 `json:"version,omitempty"`
|
|
|
|
Timestamp *int64 `json:"timestamp,omitempty"`
|
|
|
|
Meta []byte `json:"meta,omitempty"`
|
2023-04-10 20:39:49 +00:00
|
|
|
}
|
|
|
|
|
2023-09-11 14:24:05 +00:00
|
|
|
const routeStoreMessagesV1 = "/store/v1/messages"
|
2023-04-10 20:39:49 +00:00
|
|
|
|
|
|
|
func NewStoreService(node *node.WakuNode, m *chi.Mux) *StoreService {
|
|
|
|
s := &StoreService{
|
|
|
|
node: node,
|
|
|
|
mux: m,
|
|
|
|
}
|
|
|
|
|
2023-09-11 14:24:05 +00:00
|
|
|
m.Get(routeStoreMessagesV1, s.getV1Messages)
|
2023-04-10 20:39:49 +00:00
|
|
|
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2023-11-13 22:52:46 +00:00
|
|
|
func getStoreParams(r *http.Request) (*store.Query, []store.HistoryRequestOption, error) {
|
2023-04-10 20:39:49 +00:00
|
|
|
query := &store.Query{}
|
|
|
|
var options []store.HistoryRequestOption
|
2023-11-13 22:52:46 +00:00
|
|
|
var err error
|
2023-04-10 20:39:49 +00:00
|
|
|
peerAddrStr := r.URL.Query().Get("peerAddr")
|
2023-11-13 22:52:46 +00:00
|
|
|
var m multiaddr.Multiaddr
|
|
|
|
if peerAddrStr != "" {
|
|
|
|
m, err = multiaddr.NewMultiaddr(peerAddrStr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
options = append(options, store.WithPeerAddr(m))
|
2023-04-10 20:39:49 +00:00
|
|
|
}
|
2023-11-13 22:52:46 +00:00
|
|
|
query.PubsubTopic = r.URL.Query().Get("pubsubTopic")
|
2023-04-10 20:39:49 +00:00
|
|
|
|
|
|
|
contentTopics := r.URL.Query().Get("contentTopics")
|
|
|
|
if contentTopics != "" {
|
|
|
|
query.ContentTopics = strings.Split(contentTopics, ",")
|
|
|
|
}
|
|
|
|
|
|
|
|
startTimeStr := r.URL.Query().Get("startTime")
|
|
|
|
if startTimeStr != "" {
|
2023-11-07 19:48:43 +00:00
|
|
|
startTime, err := strconv.ParseInt(startTimeStr, 10, 64)
|
2023-04-10 20:39:49 +00:00
|
|
|
if err != nil {
|
2023-11-13 22:52:46 +00:00
|
|
|
return nil, nil, err
|
2023-04-10 20:39:49 +00:00
|
|
|
}
|
2023-11-07 19:48:43 +00:00
|
|
|
query.StartTime = &startTime
|
2023-04-10 20:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
endTimeStr := r.URL.Query().Get("endTime")
|
|
|
|
if endTimeStr != "" {
|
2023-11-07 19:48:43 +00:00
|
|
|
endTime, err := strconv.ParseInt(endTimeStr, 10, 64)
|
2023-04-10 20:39:49 +00:00
|
|
|
if err != nil {
|
2023-11-13 22:52:46 +00:00
|
|
|
return nil, nil, err
|
2023-04-10 20:39:49 +00:00
|
|
|
}
|
2023-11-07 19:48:43 +00:00
|
|
|
query.EndTime = &endTime
|
2023-04-10 20:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var cursor *pb.Index
|
|
|
|
|
|
|
|
senderTimeStr := r.URL.Query().Get("senderTime")
|
|
|
|
storeTimeStr := r.URL.Query().Get("storeTime")
|
|
|
|
digestStr := r.URL.Query().Get("digest")
|
|
|
|
|
|
|
|
if senderTimeStr != "" || storeTimeStr != "" || digestStr != "" {
|
|
|
|
cursor = &pb.Index{}
|
|
|
|
|
|
|
|
if senderTimeStr != "" {
|
|
|
|
cursor.SenderTime, err = strconv.ParseInt(senderTimeStr, 10, 64)
|
|
|
|
if err != nil {
|
2023-11-13 22:52:46 +00:00
|
|
|
return nil, nil, err
|
2023-04-10 20:39:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if storeTimeStr != "" {
|
|
|
|
cursor.ReceiverTime, err = strconv.ParseInt(storeTimeStr, 10, 64)
|
|
|
|
if err != nil {
|
2023-11-13 22:52:46 +00:00
|
|
|
return nil, nil, err
|
2023-04-10 20:39:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if digestStr != "" {
|
|
|
|
cursor.Digest, err = base64.URLEncoding.DecodeString(digestStr)
|
|
|
|
if err != nil {
|
2023-11-13 22:52:46 +00:00
|
|
|
return nil, nil, err
|
2023-04-10 20:39:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-11-13 22:52:46 +00:00
|
|
|
cursor.PubsubTopic = query.PubsubTopic
|
2023-04-10 20:39:49 +00:00
|
|
|
|
|
|
|
options = append(options, store.WithCursor(cursor))
|
|
|
|
}
|
|
|
|
|
|
|
|
pageSizeStr := r.URL.Query().Get("pageSize")
|
|
|
|
ascendingStr := r.URL.Query().Get("ascending")
|
|
|
|
if ascendingStr != "" || pageSizeStr != "" {
|
|
|
|
ascending := true
|
2023-12-06 13:02:05 +00:00
|
|
|
pageSize := uint64(store.DefaultPageSize)
|
2023-04-10 20:39:49 +00:00
|
|
|
if ascendingStr != "" {
|
|
|
|
ascending, err = strconv.ParseBool(ascendingStr)
|
|
|
|
if err != nil {
|
2023-11-13 22:52:46 +00:00
|
|
|
return nil, nil, err
|
2023-04-10 20:39:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if pageSizeStr != "" {
|
|
|
|
pageSize, err = strconv.ParseUint(pageSizeStr, 10, 64)
|
|
|
|
if err != nil {
|
2023-11-13 22:52:46 +00:00
|
|
|
return nil, nil, err
|
2023-04-10 20:39:49 +00:00
|
|
|
}
|
2023-12-06 13:02:05 +00:00
|
|
|
if pageSize > store.MaxPageSize {
|
|
|
|
pageSize = store.MaxPageSize
|
|
|
|
}
|
2023-04-10 20:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
options = append(options, store.WithPaging(ascending, pageSize))
|
|
|
|
}
|
|
|
|
|
2023-11-13 22:52:46 +00:00
|
|
|
return query, options, nil
|
2023-04-10 20:39:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func writeStoreError(w http.ResponseWriter, code int, err error) {
|
|
|
|
writeResponse(w, StoreResponse{ErrorMessage: err.Error()}, code)
|
|
|
|
}
|
|
|
|
|
|
|
|
func toStoreResponse(result *store.Result) StoreResponse {
|
|
|
|
response := StoreResponse{}
|
|
|
|
|
|
|
|
cursor := result.Cursor()
|
|
|
|
if cursor != nil {
|
|
|
|
response.Cursor = &HistoryCursor{
|
|
|
|
PubsubTopic: cursor.PubsubTopic,
|
|
|
|
SenderTime: fmt.Sprintf("%d", cursor.SenderTime),
|
|
|
|
StoreTime: fmt.Sprintf("%d", cursor.ReceiverTime),
|
|
|
|
Digest: cursor.Digest,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, m := range result.Messages {
|
|
|
|
response.Messages = append(response.Messages, StoreWakuMessage{
|
|
|
|
Payload: m.Payload,
|
|
|
|
ContentTopic: m.ContentTopic,
|
2023-11-10 18:31:36 +00:00
|
|
|
Version: m.Version,
|
|
|
|
Timestamp: m.Timestamp,
|
2023-04-10 20:39:49 +00:00
|
|
|
Meta: m.Meta,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return response
|
|
|
|
}
|
|
|
|
|
|
|
|
func (d *StoreService) getV1Messages(w http.ResponseWriter, r *http.Request) {
|
2023-11-13 22:52:46 +00:00
|
|
|
query, options, err := getStoreParams(r)
|
2023-04-10 20:39:49 +00:00
|
|
|
if err != nil {
|
|
|
|
writeStoreError(w, http.StatusBadRequest, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
|
|
|
|
defer cancel()
|
|
|
|
|
|
|
|
result, err := d.node.Store().Query(ctx, *query, options...)
|
|
|
|
if err != nil {
|
|
|
|
writeStoreError(w, http.StatusInternalServerError, err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
writeErrOrResponse(w, nil, toStoreResponse(result))
|
|
|
|
}
|