feat: add message mutation sync
This commit is contained in:
+95
-8
@@ -4074,6 +4074,8 @@ func serveIMTopicMessageAction(wrt http.ResponseWriter, req *http.Request) {
|
||||
serveIMTopicDraft(wrt, req)
|
||||
case strings.HasSuffix(req.URL.Path, "/calls"):
|
||||
serveIMTopicCalls(wrt, req)
|
||||
case strings.HasSuffix(req.URL.Path, "/message-mutations"):
|
||||
serveIMMessageMutations(wrt, req)
|
||||
case strings.HasSuffix(req.URL.Path, "/messages/search"):
|
||||
serveIMMessageSearch(wrt, req)
|
||||
case strings.Contains(req.URL.Path, "/messages/") && strings.HasSuffix(req.URL.Path, "/context"):
|
||||
@@ -4198,6 +4200,40 @@ func serveIMMessageSearch(wrt http.ResponseWriter, req *http.Request) {
|
||||
writeIMSuccess(wrt, req, map[string]any{"topic": topic, "items": items, "next_cursor": nextCursor})
|
||||
}
|
||||
|
||||
func serveIMMessageMutations(wrt http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
writeIMError(wrt, req, http.StatusMethodNotAllowed, 30002, imReasonMethodNotAllowed, "method not allowed", nil)
|
||||
return
|
||||
}
|
||||
principal, topic, ok := authenticateTopicReader(wrt, req, topicFromMessageMutationsPath(req.URL.Path))
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
sinceVersion, err := parseIMMutationSince(req.URL.Query().Get("since_version"), req.URL.Query().Get("since"))
|
||||
if err != nil {
|
||||
writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "since_version"})
|
||||
return
|
||||
}
|
||||
limit := parseIMLimit(req.URL.Query().Get("limit"), 50, 100)
|
||||
mutations, nextCursor, hasMore, mutationVersion, err := store.Messages.Mutations(principal.TenantID, topic, principal.Uid, sinceVersion, limit)
|
||||
if err != nil {
|
||||
writeIMRelationError(wrt, req, err)
|
||||
return
|
||||
}
|
||||
items := make([]map[string]any, 0, len(mutations))
|
||||
for _, mutation := range mutations {
|
||||
items = append(items, messageMutationEventPayload(mutation))
|
||||
}
|
||||
writeIMSuccess(wrt, req, map[string]any{
|
||||
"topic": topic,
|
||||
"items": items,
|
||||
"next_cursor": nextCursor,
|
||||
"has_more": hasMore,
|
||||
"mutation_version": mutationVersion,
|
||||
"expired": false,
|
||||
})
|
||||
}
|
||||
|
||||
func serveIMMessageContext(wrt http.ResponseWriter, req *http.Request) {
|
||||
if req.Method != http.MethodGet {
|
||||
writeIMError(wrt, req, http.StatusMethodNotAllowed, 30002, imReasonMethodNotAllowed, "method not allowed", nil)
|
||||
@@ -6324,19 +6360,34 @@ func serveIMGroupMutes(wrt http.ResponseWriter, req *http.Request, topic, tail s
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if !(selfSub.ModeGiven & selfSub.ModeWant).IsAdmin() {
|
||||
writeIMError(wrt, req, http.StatusForbidden, 30005, imReasonPermissionDenied, "permission denied", nil)
|
||||
return
|
||||
}
|
||||
isAdmin := (selfSub.ModeGiven & selfSub.ModeWant).IsAdmin()
|
||||
switch {
|
||||
case tail == "mutes" && req.Method == http.MethodGet:
|
||||
items, nextCursor, err := store.GroupBusiness.ListMutes(principal.TenantID, topic, strings.TrimSpace(req.URL.Query().Get("cursor")), parseIMLimit(req.URL.Query().Get("limit"), 50, 100))
|
||||
if err != nil && !errors.Is(err, types.ErrNotFound) {
|
||||
writeIMRelationError(wrt, req, err)
|
||||
return
|
||||
var items []types.GroupMute
|
||||
nextCursor := ""
|
||||
if isAdmin {
|
||||
var err error
|
||||
items, nextCursor, err = store.GroupBusiness.ListMutes(principal.TenantID, topic, strings.TrimSpace(req.URL.Query().Get("cursor")), parseIMLimit(req.URL.Query().Get("limit"), 50, 100))
|
||||
if err != nil && !errors.Is(err, types.ErrNotFound) {
|
||||
writeIMRelationError(wrt, req, err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
item, err := store.GroupBusiness.GetMute(principal.TenantID, topic, principal.Uid)
|
||||
if err != nil && !errors.Is(err, types.ErrNotFound) {
|
||||
writeIMRelationError(wrt, req, err)
|
||||
return
|
||||
}
|
||||
if item != nil {
|
||||
items = append(items, *item)
|
||||
}
|
||||
}
|
||||
writeIMSuccess(wrt, req, map[string]any{"topic": topic, "items": groupMutePayloads(items), "next_cursor": nextCursor})
|
||||
case (tail == "mutes" || tail == "mutes/batch") && req.Method == http.MethodPost:
|
||||
if !isAdmin {
|
||||
writeIMError(wrt, req, http.StatusForbidden, 30005, imReasonPermissionDenied, "permission denied", nil)
|
||||
return
|
||||
}
|
||||
var body imGroupMuteBatchRequest
|
||||
if !decodeIMJSONBody(wrt, req, &body) {
|
||||
return
|
||||
@@ -6377,6 +6428,10 @@ func serveIMGroupMutes(wrt http.ResponseWriter, req *http.Request, topic, tail s
|
||||
map[string]any{"topic": topic, "items": groupMutePayloads(mutes)})
|
||||
writeIMSuccess(wrt, req, map[string]any{"topic": topic, "items": groupMutePayloads(mutes)})
|
||||
case strings.HasPrefix(tail, "mutes/") && req.Method == http.MethodDelete:
|
||||
if !isAdmin {
|
||||
writeIMError(wrt, req, http.StatusForbidden, 30005, imReasonPermissionDenied, "permission denied", nil)
|
||||
return
|
||||
}
|
||||
uid := types.ParseUserId(strings.TrimSpace(strings.TrimPrefix(tail, "mutes/")))
|
||||
if uid.IsZero() {
|
||||
writeIMError(wrt, req, http.StatusBadRequest, 30000, imReasonMalformedRequest, "malformed request", map[string]any{"field": "user_id"})
|
||||
@@ -8689,6 +8744,17 @@ func messageSearchPayload(msg types.Message, query string) map[string]any {
|
||||
}
|
||||
}
|
||||
|
||||
func messageMutationEventPayload(mutation types.MessageMutation) map[string]any {
|
||||
return map[string]any{
|
||||
"seq": mutation.SeqId,
|
||||
"version": mutation.Version,
|
||||
"event": mutation.Event,
|
||||
"operator": mutation.Operator,
|
||||
"mutated_at": mutation.MutatedAt.UTC().Format(time.RFC3339Nano),
|
||||
"mutation_version": mutation.MutationVersion,
|
||||
}
|
||||
}
|
||||
|
||||
func messageContextPayload(msg types.Message) map[string]any {
|
||||
return map[string]any{
|
||||
"seq": msg.SeqId,
|
||||
@@ -11701,6 +11767,12 @@ func topicFromMessagesSearchPath(path string) string {
|
||||
return topicFromPathWithSuffix(path, prefix, suffix)
|
||||
}
|
||||
|
||||
func topicFromMessageMutationsPath(path string) string {
|
||||
const prefix = "/v1/im/topics/"
|
||||
const suffix = "/message-mutations"
|
||||
return topicFromPathWithSuffix(path, prefix, suffix)
|
||||
}
|
||||
|
||||
func topicFromMediaPath(path string) string {
|
||||
const prefix = "/v1/im/topics/"
|
||||
const suffix = "/media"
|
||||
@@ -11728,6 +11800,21 @@ func topicFromPathWithSuffix(path, prefix, suffix string) string {
|
||||
return strings.Trim(rest, "/")
|
||||
}
|
||||
|
||||
func parseIMMutationSince(primary, fallback string) (int64, error) {
|
||||
value := strings.TrimSpace(primary)
|
||||
if value == "" {
|
||||
value = strings.TrimSpace(fallback)
|
||||
}
|
||||
if value == "" {
|
||||
return 0, nil
|
||||
}
|
||||
parsed, err := strconv.ParseInt(value, 10, 64)
|
||||
if err != nil || parsed < 0 {
|
||||
return 0, strconv.ErrSyntax
|
||||
}
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func messageContextPath(path string) (string, int64, bool) {
|
||||
const prefix = "/v1/im/topics/"
|
||||
idx := strings.Index(path, prefix)
|
||||
|
||||
Reference in New Issue
Block a user