handleInboundUsers manages user assignments for an inbound. GET → list user IDs assigned to this inbound PUT → bulk-set user IDs (reconciles adds/removes)
(w http.ResponseWriter, r *http.Request, inboundID string)
| 304 | // GET → list user IDs assigned to this inbound |
| 305 | // PUT → bulk-set user IDs (reconciles adds/removes) |
| 306 | func (a *inboundAPI) handleInboundUsers(w http.ResponseWriter, r *http.Request, inboundID string) { |
| 307 | ib, err := a.store.GetInbound(inboundID) |
| 308 | if err != nil { |
| 309 | writeInboundError(w, err) |
| 310 | return |
| 311 | } |
| 312 | |
| 313 | switch r.Method { |
| 314 | case http.MethodGet: |
| 315 | existing, err := a.userStore.ListUserInboundsByInbound(inboundID) |
| 316 | if err != nil { |
| 317 | internalError(w, r, err) |
| 318 | return |
| 319 | } |
| 320 | userIDs := make([]string, 0, len(existing)) |
| 321 | for _, acc := range existing { |
| 322 | userIDs = append(userIDs, acc.UserID) |
| 323 | } |
| 324 | writeJSON(w, http.StatusOK, map[string]any{"user_ids": userIDs}) |
| 325 | |
| 326 | case http.MethodPut: |
| 327 | var req struct { |
| 328 | UserIDs []string `json:"user_ids"` |
| 329 | } |
| 330 | if err := json.NewDecoder(r.Body).Decode(&req); err != nil { |
| 331 | writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid json body"}) |
| 332 | return |
| 333 | } |
| 334 | |
| 335 | wanted := make(map[string]struct{}, len(req.UserIDs)) |
| 336 | for _, uid := range req.UserIDs { |
| 337 | wanted[uid] = struct{}{} |
| 338 | } |
| 339 | |
| 340 | existing, err := a.userStore.ListUserInboundsByInbound(inboundID) |
| 341 | if err != nil { |
| 342 | internalError(w, r, err) |
| 343 | return |
| 344 | } |
| 345 | existingByUser := make(map[string]users.UserInbound, len(existing)) |
| 346 | for _, acc := range existing { |
| 347 | existingByUser[acc.UserID] = acc |
| 348 | } |
| 349 | |
| 350 | // Add new user assignments |
| 351 | for _, uid := range req.UserIDs { |
| 352 | if _, ok := existingByUser[uid]; !ok { |
| 353 | secret := randomToken(12) |
| 354 | if ib.Protocol == "shadowsocks" && strings.HasPrefix(ib.Method, "2022-") { |
| 355 | secret = ssPassword(ib.Method) |
| 356 | } |
| 357 | acc := users.UserInbound{ |
| 358 | ID: idgen.NextString(), |
| 359 | UserID: uid, |
| 360 | InboundID: inboundID, |
| 361 | NodeID: ib.NodeID, |
| 362 | UUID: randomUUID(), |
| 363 | Secret: secret, |
no test coverage detected