| 388 | } |
| 389 | |
| 390 | func handleUpdateTask(dp *DataProvider, readonly bool) http.HandlerFunc { |
| 391 | return func(w http.ResponseWriter, r *http.Request) { |
| 392 | dp := effectiveDP(r, dp) |
| 393 | if readonly { |
| 394 | writeError(w, http.StatusForbidden, "server is in read-only mode", nil) |
| 395 | return |
| 396 | } |
| 397 | |
| 398 | taskID := r.PathValue("id") |
| 399 | if taskID == "" { |
| 400 | writeError(w, http.StatusBadRequest, "task ID is required", nil) |
| 401 | return |
| 402 | } |
| 403 | |
| 404 | var body TaskUpdateRequest |
| 405 | if err := json.NewDecoder(r.Body).Decode(&body); err != nil { |
| 406 | writeError(w, http.StatusBadRequest, "invalid JSON body", []string{err.Error()}) |
| 407 | return |
| 408 | } |
| 409 | |
| 410 | req := toUpdateRequest(body) |
| 411 | |
| 412 | if errs := taskfile.ValidateUpdateRequest(req); len(errs) > 0 { |
| 413 | writeError(w, http.StatusBadRequest, "validation failed", errs) |
| 414 | return |
| 415 | } |
| 416 | |
| 417 | tasks, err := dp.GetTasks() |
| 418 | if err != nil { |
| 419 | writeError(w, http.StatusInternalServerError, "failed to load tasks", nil) |
| 420 | return |
| 421 | } |
| 422 | |
| 423 | found := findTaskByID(tasks, taskID) |
| 424 | if found == nil { |
| 425 | writeError(w, http.StatusNotFound, "task not found: "+taskID, nil) |
| 426 | return |
| 427 | } |
| 428 | |
| 429 | if err := taskfile.UpdateTaskFile(found.FilePath, req); err != nil { |
| 430 | handleFileUpdateError(w, err) |
| 431 | return |
| 432 | } |
| 433 | |
| 434 | dp.Invalidate() |
| 435 | |
| 436 | updated, err := reloadTask(dp, taskID) |
| 437 | if err != nil { |
| 438 | writeError(w, http.StatusInternalServerError, "failed to reload tasks", nil) |
| 439 | return |
| 440 | } |
| 441 | |
| 442 | writeJSON(w, TaskDetail{Task: updated, Body: updated.Body}) |
| 443 | } |
| 444 | } |
| 445 | |
| 446 | func toUpdateRequest(body TaskUpdateRequest) taskfile.UpdateRequest { |
| 447 | return taskfile.UpdateRequest{ |