handleUpdateSettings updates settings and persists them to config file. Note: Output directories cannot be changed via API for security.
(w http.ResponseWriter, r *http.Request)
| 417 | // handleUpdateSettings updates settings and persists them to config file. |
| 418 | // Note: Output directories cannot be changed via API for security. |
| 419 | func (s *Server) handleUpdateSettings(w http.ResponseWriter, r *http.Request) { |
| 420 | var req struct { |
| 421 | Token *string `json:"token,omitempty"` |
| 422 | Concurrency *int `json:"connections,omitempty"` |
| 423 | MaxActive *int `json:"maxActive,omitempty"` |
| 424 | MultipartThreshold *string `json:"multipartThreshold,omitempty"` |
| 425 | Verify *string `json:"verify,omitempty"` |
| 426 | Retries *int `json:"retries,omitempty"` |
| 427 | Endpoint *string `json:"endpoint,omitempty"` |
| 428 | // Proxy settings |
| 429 | Proxy *struct { |
| 430 | URL *string `json:"url,omitempty"` |
| 431 | Username *string `json:"username,omitempty"` |
| 432 | Password *string `json:"password,omitempty"` |
| 433 | NoProxy *string `json:"noProxy,omitempty"` |
| 434 | NoEnvProxy *bool `json:"noEnvProxy,omitempty"` |
| 435 | InsecureSkipVerify *bool `json:"insecureSkipVerify,omitempty"` |
| 436 | } `json:"proxy,omitempty"` |
| 437 | // Note: ModelsDir and DatasetsDir are NOT updatable via API for security |
| 438 | } |
| 439 | |
| 440 | if err := json.NewDecoder(r.Body).Decode(&req); err != nil { |
| 441 | writeError(w, http.StatusBadRequest, "Invalid request body", err.Error()) |
| 442 | return |
| 443 | } |
| 444 | |
| 445 | // Apply updates under the write lock. Only the API-mutable fields are |
| 446 | // touched; startup-only fields (dirs, auth, origins) are left untouched so |
| 447 | // the middleware that reads them lock-free never races this writer. |
| 448 | s.configMu.Lock() |
| 449 | if req.Token != nil { |
| 450 | s.config.Token = *req.Token |
| 451 | } |
| 452 | if req.Concurrency != nil && *req.Concurrency > 0 { |
| 453 | s.config.Concurrency = *req.Concurrency |
| 454 | } |
| 455 | if req.MaxActive != nil && *req.MaxActive > 0 { |
| 456 | s.config.MaxActive = *req.MaxActive |
| 457 | } |
| 458 | if req.MultipartThreshold != nil && *req.MultipartThreshold != "" { |
| 459 | s.config.MultipartThreshold = *req.MultipartThreshold |
| 460 | } |
| 461 | if req.Verify != nil && *req.Verify != "" { |
| 462 | s.config.Verify = *req.Verify |
| 463 | } |
| 464 | if req.Retries != nil && *req.Retries > 0 { |
| 465 | s.config.Retries = *req.Retries |
| 466 | } |
| 467 | if req.Endpoint != nil { |
| 468 | s.config.Endpoint = *req.Endpoint |
| 469 | } |
| 470 | |
| 471 | // Update proxy settings using copy-on-write: build a fresh ProxyConfig and |
| 472 | // publish the new pointer rather than mutating the existing struct in |
| 473 | // place. In-flight downloads (and any snapshot) keep pointing at the old, |
| 474 | // now-immutable ProxyConfig, so there is no torn read of proxy fields. |
| 475 | if req.Proxy != nil { |
| 476 | var np hfdownloader.ProxyConfig |