Add multi-disk copy workflow

This commit is contained in:
2026-04-23 22:24:32 +03:00
parent 5b3cb9e393
commit 31bac2b5d8
10 changed files with 468 additions and 204 deletions

View File

@@ -9,8 +9,9 @@ import (
)
func (s *Server) handleCopyStart(w http.ResponseWriter, r *http.Request) {
diskInfo := s.deps.Watcher.CurrentDisk()
if diskInfo.State != disk.DiskKnown {
diskID := r.PathValue("diskID")
diskInfo, ok := s.deps.Watcher.DiskByID(diskID)
if !ok || diskInfo.State != disk.DiskKnown {
jsonErr(w, http.StatusUnprocessableEntity, "no known disk connected")
return
}
@@ -54,7 +55,8 @@ func (s *Server) handleCopyStart(w http.ResponseWriter, r *http.Request) {
}
func (s *Server) handleCopyCancel(w http.ResponseWriter, r *http.Request) {
s.deps.Copier.Cancel()
diskID := r.PathValue("diskID")
s.deps.Copier.Cancel(diskID)
jsonOK(w, map[string]bool{"ok": true})
}

View File

@@ -1,14 +1,13 @@
package api
import (
"encoding/json"
"net/http"
"jukebox_maker/internal/disk"
)
func (s *Server) handleDiskStatus(w http.ResponseWriter, r *http.Request) {
info := s.deps.Watcher.CurrentDisk()
type response struct {
State disk.DiskState `json:"state"`
DiskID string `json:"disk_id"`
@@ -18,17 +17,57 @@ func (s *Server) handleDiskStatus(w http.ResponseWriter, r *http.Request) {
ActiveTaskID string `json:"active_task_id,omitempty"`
}
resp := response{
State: info.State,
DiskID: info.DiskID,
TotalBytes: info.TotalBytes,
FreeBytes: info.FreeBytes,
MountPath: info.MountPath,
disks := s.deps.Watcher.ListDisks()
resp := make([]response, 0, len(disks))
for _, info := range disks {
item := response{
State: info.State,
DiskID: info.DiskID,
TotalBytes: info.TotalBytes,
FreeBytes: info.FreeBytes,
MountPath: info.MountPath,
}
if info.DiskID != "" {
if t, ok := s.deps.Tasks.ActiveTaskByDisk(info.DiskID); ok {
item.ActiveTaskID = t.ID
}
}
resp = append(resp, item)
}
if t, ok := s.deps.Tasks.ActiveTask(); ok {
resp.ActiveTaskID = t.ID
}
jsonOK(w, resp)
jsonOK(w, map[string]any{"items": resp})
}
func (s *Server) handleDiskInit(w http.ResponseWriter, r *http.Request) {
var req struct {
MountPath string `json:"mount_path"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
jsonErr(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
return
}
info, ok := s.deps.Watcher.DiskByMountPath(req.MountPath)
if !ok {
jsonErr(w, http.StatusNotFound, "disk not found")
return
}
if info.State == disk.DiskAbsent {
jsonErr(w, http.StatusUnprocessableEntity, "no disk connected")
return
}
if info.State == disk.DiskKnown {
jsonErr(w, http.StatusConflict, "disk already initialized")
return
}
diskID, err := disk.InitDisk(info.MountPath)
if err != nil {
jsonErr(w, http.StatusInternalServerError, "init disk: "+err.Error())
return
}
s.deps.OnDiskInit(info.MountPath, diskID)
s.deps.Watcher.ProbeNow()
jsonOK(w, map[string]string{"disk_id": diskID})
}

View File

@@ -21,6 +21,8 @@ type Deps struct {
Tasks *task.Store
MediaPath string
MountPath string
// OnDiskInit вызывается при ручной инициализации диска через UI.
OnDiskInit func(mountPath, diskID string)
}
type Server struct {
@@ -56,12 +58,13 @@ func (s *Server) routes() {
s.mux.HandleFunc("GET /settings", s.handleSettings)
s.mux.HandleFunc("GET /health", s.handleHealth)
s.mux.HandleFunc("GET /api/disk", s.handleDiskStatus)
s.mux.HandleFunc("GET /api/disks", s.handleDiskStatus)
s.mux.HandleFunc("POST /api/disks/init", s.handleDiskInit)
s.mux.HandleFunc("GET /api/sources", s.handleSources)
s.mux.HandleFunc("GET /api/config", s.handleGetConfig)
s.mux.HandleFunc("PUT /api/config", s.handlePutConfig)
s.mux.HandleFunc("POST /api/copy/start", s.handleCopyStart)
s.mux.HandleFunc("POST /api/copy/cancel", s.handleCopyCancel)
s.mux.HandleFunc("POST /api/disks/{diskID}/copy/start", s.handleCopyStart)
s.mux.HandleFunc("POST /api/disks/{diskID}/copy/cancel", s.handleCopyCancel)
s.mux.HandleFunc("GET /api/tasks/{id}", s.handleTaskGet)
}