handleGroupMemberByUID 处理 /v1/user-groups/{id}/members/{uid} 的 DELETE 请求。
(w http.ResponseWriter, r *http.Request, groupID, userID string)
| 262 | |
| 263 | // handleGroupMemberByUID 处理 /v1/user-groups/{id}/members/{uid} 的 DELETE 请求。 |
| 264 | func (a *userGroupAPI) handleGroupMemberByUID(w http.ResponseWriter, r *http.Request, groupID, userID string) { |
| 265 | if r.Method != http.MethodDelete { |
| 266 | writeMethodNotAllowed(w, http.MethodDelete) |
| 267 | return |
| 268 | } |
| 269 | // 删除前先收集该用户在此组的节点 ID,用于删除后触发下发 |
| 270 | var affectedNodes []string |
| 271 | if accesses, err := a.userStore.ListUserInboundsByUser(userID); err != nil { |
| 272 | log.Printf("handleGroupMemberByUID: list inbounds user=%s: %v (node push may be incomplete)", userID, err) |
| 273 | } else { |
| 274 | seen := make(map[string]struct{}) |
| 275 | for _, acc := range accesses { |
| 276 | if acc.GroupID == groupID { |
| 277 | if _, dup := seen[acc.NodeID]; !dup { |
| 278 | seen[acc.NodeID] = struct{}{} |
| 279 | affectedNodes = append(affectedNodes, acc.NodeID) |
| 280 | } |
| 281 | } |
| 282 | } |
| 283 | } |
| 284 | if err := a.ugStore.RemoveMember(groupID, userID); err != nil { |
| 285 | internalError(w, r, err) |
| 286 | return |
| 287 | } |
| 288 | // 清除该用户在此组的 inbound 记录 |
| 289 | if err := a.userStore.DeleteGroupUserInbounds(userID, groupID); err != nil { |
| 290 | internalError(w, r, err) |
| 291 | return |
| 292 | } |
| 293 | a.applyNodes(affectedNodes) |
| 294 | writeJSON(w, http.StatusOK, map[string]any{"removed": true}) |
| 295 | } |
| 296 | |
| 297 | // handleGroupSync 同步用户组所有成员的 inbound 记录。 |
| 298 | func (a *userGroupAPI) handleGroupSync(w http.ResponseWriter, r *http.Request, groupID string) { |
no test coverage detected