This commit is contained in:
Austin
2026-09-20 16:39:46 +08:00
3 changed files with 85 additions and 2 deletions
+40 -1
View File
@@ -4138,7 +4138,7 @@ func serveIMGroupMembers(wrt http.ResponseWriter, req *http.Request, topic strin
}
func serveIMGroupMember(wrt http.ResponseWriter, req *http.Request, topic string, memberID types.Uid) {
if req.Method != http.MethodGet {
if req.Method != http.MethodGet && req.Method != http.MethodDelete {
writeIMError(wrt, req, http.StatusMethodNotAllowed, 30002, imReasonMethodNotAllowed, "method not allowed", nil)
return
}
@@ -4163,6 +4163,20 @@ func serveIMGroupMember(wrt http.ResponseWriter, req *http.Request, topic string
writeIMError(wrt, req, http.StatusNotFound, 30004, imReasonMalformedRequest, "member not found", nil)
return
}
if req.Method == http.MethodDelete {
if ok, reason := canRemoveIMGroupMember(selfSub, principal.Uid, *member); !ok {
writeIMError(wrt, req, http.StatusForbidden, 30005, imReasonPermissionDenied, "permission denied", map[string]any{"user_id": memberID.UserId(), "reason": reason})
return
}
if err := store.GroupBusiness.Leave(principal.TenantID, topic, memberID, time.Now().UTC().Round(time.Millisecond)); err != nil {
writeIMRelationError(wrt, req, err)
return
}
publishIMGroupRealtimeEvent(principal.TenantID, topic, principal.Uid, "group.member.left",
map[string]any{"user_id": memberID.UserId(), "removed_by": principal.Uid.UserId(), "member_sync": "single"})
writeIMSuccess(wrt, req, map[string]any{"topic": topic, "user_id": memberID.UserId(), "status": "left"})
return
}
memberSub, err := store.Subs.Get(principal.TenantID, topic, memberID, false)
if err != nil {
writeIMRelationError(wrt, req, err)
@@ -7111,6 +7125,31 @@ func canMuteIMGroupMember(tenantID types.TenantID, topic string, selfSub *types.
return true, ""
}
func canRemoveIMGroupMember(selfSub *types.Subscription, actor types.Uid, target types.GroupMember) (bool, string) {
if actor.IsZero() || target.UserID.IsZero() {
return false, "invalid_user"
}
if actor == target.UserID {
return false, "self"
}
actorAccess := types.ModeNone
if selfSub != nil {
actorAccess = selfSub.ModeGiven & selfSub.ModeWant
}
if !actorAccess.IsAdmin() {
return false, "not_admin"
}
switch groupMemberRole(groupMemberAccess(target)) {
case "owner":
return false, "owner"
case "admin":
if !actorAccess.IsOwner() {
return false, "admin"
}
}
return true, ""
}
func groupMemberProfilePayload(topic string, sub *types.Subscription, user *types.User, ctx *types.RelationshipContext) map[string]any {
data := groupMemberPayload(*sub, true, ctx.Remark)
data["topic"] = topic