(w http.ResponseWriter, r *http.Request, nodeID string)
| 271 | } |
| 272 | |
| 273 | func (a *API) handleNode(w http.ResponseWriter, r *http.Request, nodeID string) { |
| 274 | switch r.Method { |
| 275 | case http.MethodGet: |
| 276 | node, err := a.store.Get(nodeID) |
| 277 | if err != nil { |
| 278 | writeNodeError(w, err) |
| 279 | return |
| 280 | } |
| 281 | writeJSON(w, http.StatusOK, a.nodeWithOnline(node)) |
| 282 | case http.MethodPut: |
| 283 | var req upsertNodeRequest |
| 284 | if err := json.NewDecoder(r.Body).Decode(&req); err != nil { |
| 285 | writeJSON(w, http.StatusBadRequest, map[string]any{"error": "invalid json body"}) |
| 286 | return |
| 287 | } |
| 288 | if req.Name == "" || req.BaseURL == "" { |
| 289 | writeJSON(w, http.StatusBadRequest, map[string]any{"error": "name and base_url are required"}) |
| 290 | return |
| 291 | } |
| 292 | node, err := a.store.Upsert(nodes.Node{ |
| 293 | ID: nodeID, |
| 294 | Name: req.Name, |
| 295 | BaseURL: strings.TrimRight(req.BaseURL, "/"), |
| 296 | ExpireAt: parseExpireAt(req.ExpireAt), |
| 297 | PanelURL: req.PanelURL, |
| 298 | Remark: req.Remark, |
| 299 | IPOverride: strings.TrimSpace(req.IPOverride), |
| 300 | Disabled: req.Disabled, |
| 301 | ACMEEmail: req.ACMEEmail, |
| 302 | PanelDomain: req.PanelDomain, |
| 303 | ExtraProxies: req.ExtraProxies, |
| 304 | HTTPSPort: req.HTTPSPort, |
| 305 | IsLanding: req.IsLanding, |
| 306 | }) |
| 307 | if err != nil { |
| 308 | writeNodeError(w, err) |
| 309 | return |
| 310 | } |
| 311 | // BaseURL 可能已变更,旧的缓存客户端不再有效 |
| 312 | a.evictClient(nodeID) |
| 313 | writeJSON(w, http.StatusOK, node) |
| 314 | case http.MethodDelete: |
| 315 | // 先级联删除该节点下的所有入站(及其关联的 user_inbounds、分组成员) |
| 316 | if a.inboundStore != nil { |
| 317 | ibs, err := a.inboundStore.ListInboundsByNode(nodeID) |
| 318 | if err != nil { |
| 319 | writeJSON(w, http.StatusInternalServerError, map[string]any{"error": "list inbounds: " + err.Error()}) |
| 320 | return |
| 321 | } |
| 322 | for _, ib := range ibs { |
| 323 | if err := a.inboundStore.DeleteInbound(ib.ID); err != nil { |
| 324 | writeJSON(w, http.StatusInternalServerError, map[string]any{"error": "delete inbound: " + err.Error()}) |
| 325 | return |
| 326 | } |
| 327 | } |
| 328 | } |
| 329 | if err := a.store.Delete(nodeID); err != nil { |
| 330 | writeNodeError(w, err) |
no test coverage detected