(w http.ResponseWriter, r *http.Request)
| 91 | } |
| 92 | |
| 93 | func (a *userAPI) handleUsers(w http.ResponseWriter, r *http.Request) { |
| 94 | switch r.Method { |
| 95 | case http.MethodGet: |
| 96 | all, err := a.users.ListUsers() |
| 97 | if err != nil { |
| 98 | internalError(w, r, err) |
| 99 | return |
| 100 | } |
| 101 | items, total := filterUsersByQuery(all, r) |
| 102 | writeJSON(w, http.StatusOK, map[string]any{"users": items, "total": total}) |
| 103 | case http.MethodPost: |
| 104 | var req createUserRequest |
| 105 | if err := json.NewDecoder(r.Body).Decode(&req); err != nil { |
| 106 | writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid json body"}) |
| 107 | return |
| 108 | } |
| 109 | req.Username = strings.TrimSpace(req.Username) |
| 110 | req.Note = strings.TrimSpace(req.Note) |
| 111 | if req.Username == "" { |
| 112 | writeJSON(w, http.StatusBadRequest, map[string]any{"error": "username is required"}) |
| 113 | return |
| 114 | } |
| 115 | if strings.TrimSpace(req.ID) == "" { |
| 116 | req.ID = idgen.NextString() |
| 117 | } |
| 118 | if req.DataLimitResetStrategy == "" { |
| 119 | req.DataLimitResetStrategy = users.ResetStrategyNoReset |
| 120 | } |
| 121 | user, err := a.users.UpsertUser(users.User{ |
| 122 | ID: req.ID, |
| 123 | Username: req.Username, |
| 124 | Status: users.StatusActive, |
| 125 | Note: req.Note, |
| 126 | ExpireAt: req.ExpireAt, |
| 127 | DataLimitResetStrategy: req.DataLimitResetStrategy, |
| 128 | TrafficLimit: req.TrafficLimit, |
| 129 | PlanTrafficLimit: req.TrafficLimit, |
| 130 | SubToken: randomToken(16), |
| 131 | UUID: randomUUID(), |
| 132 | Secret: randomToken(16), |
| 133 | }) |
| 134 | if err != nil { |
| 135 | if errors.Is(err, users.ErrUsernameTaken) { |
| 136 | writeJSON(w, http.StatusConflict, map[string]any{"error": "username already exists"}) |
| 137 | return |
| 138 | } |
| 139 | internalError(w, r, err) |
| 140 | return |
| 141 | } |
| 142 | // Auto-associate with selected inbounds |
| 143 | if len(req.InboundIDs) > 0 { |
| 144 | affected, syncErr := a.syncUserInbounds(user.ID, req.InboundIDs) |
| 145 | if syncErr != nil { |
| 146 | // 回滚:删除刚创建的用户,保证原子性 |
| 147 | _ = a.users.DeleteUser(user.ID) |
| 148 | internalError(w, r, syncErr) |
| 149 | return |
| 150 | } |
nothing calls this directly
no test coverage detected