package api import ( "context" "encoding/json" "errors" "io" "net/http" "jukebox_maker/internal/config" "jukebox_maker/internal/copier" "jukebox_maker/internal/disk" ) func (s *Server) handleCopyStart(w http.ResponseWriter, r *http.Request) { diskID := r.PathValue("diskID") diskInfo, ok := s.deps.Watcher.DiskByID(diskID) if !ok || diskInfo.State != disk.DiskKnown { jsonErr(w, http.StatusUnprocessableEntity, "no initialized disk connected") return } cfg := s.deps.Config hasEnabledSources := false for _, src := range cfg.Sources { if src.Enabled { hasEnabledSources = true break } } if !hasEnabledSources { jsonErr(w, http.StatusUnprocessableEntity, "no source folders selected") return } var req struct { Mode string `json:"mode"` } if r.Body != nil { if err := json.NewDecoder(r.Body).Decode(&req); err != nil && !errors.Is(err, io.EOF) { jsonErr(w, http.StatusBadRequest, "invalid JSON: "+err.Error()) return } } overwriteMode := cfg.OverwriteMode switch req.Mode { case "", "add": overwriteMode = config.OverwriteSkip case "replace": overwriteMode = config.OverwriteDelete default: jsonErr(w, http.StatusBadRequest, "invalid copy mode") return } reserveBytes := int64(cfg.ReserveFreeGB * 1e9) if diskInfo.FreeBytes <= reserveBytes { jsonErr(w, http.StatusUnprocessableEntity, "free space is below reserve threshold") return } opts := copier.Options{ DiskID: diskInfo.DiskID, MountPath: diskInfo.MountPath, MediaPath: s.deps.MediaPath, DestFolder: cfg.DestFolder, SourceRules: cfg.Sources, ReserveFreeGB: cfg.ReserveFreeGB, OverwriteMode: overwriteMode, FileSelectMode: cfg.FileSelectMode, } taskID, err := s.deps.Copier.Start(context.Background(), opts) if err != nil { switch err.Error() { case "copy already running": jsonErr(w, http.StatusConflict, err.Error()) default: jsonErr(w, http.StatusUnprocessableEntity, err.Error()) } return } w.WriteHeader(http.StatusAccepted) jsonOK(w, map[string]string{"task_id": taskID}) } func (s *Server) handleCopyCancel(w http.ResponseWriter, r *http.Request) { diskID := r.PathValue("diskID") s.deps.Copier.Cancel(diskID) jsonOK(w, map[string]bool{"ok": true}) } func (s *Server) handleTaskGet(w http.ResponseWriter, r *http.Request) { id := r.PathValue("id") t, ok := s.deps.Tasks.Get(id) if !ok { jsonErr(w, http.StatusNotFound, "task not found") return } jsonOK(w, t) }