package web import ( "errors" "net/http" "inventree/internal/folders" "inventree/internal/models" ) func (a *App) handleFolderGet(w http.ResponseWriter, r *http.Request) { id, err := pathID(r, "id") if err != nil { jsonErr(w, http.StatusBadRequest, err) return } f, err := a.Folders.Get(r.Context(), id) if err != nil { jsonErr(w, http.StatusNotFound, err) return } jsonOK(w, f) } type folderCreateRequest struct { Name string `json:"name"` ParentID *int64 `json:"parent_id"` Description string `json:"description"` Icon string `json:"icon"` SortOrder int `json:"sort_order"` } func (a *App) handleFolderCreate(w http.ResponseWriter, r *http.Request) { var req folderCreateRequest if err := readJSON(r, &req); err != nil { jsonErr(w, http.StatusBadRequest, err) return } f := &models.Folder{Name: req.Name, ParentID: req.ParentID, Description: req.Description, Icon: req.Icon, SortOrder: req.SortOrder} if err := a.Folders.Create(r.Context(), f); err != nil { jsonErr(w, http.StatusBadRequest, err) return } jsonOK(w, f) } type folderUpdateRequest struct { Name string `json:"name"` Description string `json:"description"` Icon string `json:"icon"` SortOrder int `json:"sort_order"` } func (a *App) handleFolderUpdate(w http.ResponseWriter, r *http.Request) { id, err := pathID(r, "id") if err != nil { jsonErr(w, http.StatusBadRequest, err) return } var req folderUpdateRequest if err := readJSON(r, &req); err != nil { jsonErr(w, http.StatusBadRequest, err) return } f, err := a.Folders.Get(r.Context(), id) if err != nil { jsonErr(w, http.StatusNotFound, err) return } f.Name, f.Description, f.Icon, f.SortOrder = req.Name, req.Description, req.Icon, req.SortOrder if err := a.Folders.Update(r.Context(), f); err != nil { jsonErr(w, http.StatusBadRequest, err) return } jsonOK(w, f) } type folderMoveRequest struct { ParentID *int64 `json:"parent_id"` } func (a *App) handleFolderMove(w http.ResponseWriter, r *http.Request) { id, err := pathID(r, "id") if err != nil { jsonErr(w, http.StatusBadRequest, err) return } var req folderMoveRequest if err := readJSON(r, &req); err != nil { jsonErr(w, http.StatusBadRequest, err) return } if err := a.Folders.Move(r.Context(), id, req.ParentID); err != nil { status := http.StatusBadRequest if errors.Is(err, folders.ErrInvalidParent) { status = http.StatusConflict } jsonErr(w, status, err) return } jsonOK(w, map[string]any{"id": id, "parent_id": req.ParentID}) } func (a *App) handleFolderDeleteInfo(w http.ResponseWriter, r *http.Request) { ctx := r.Context() id, err := pathID(r, "id") if err != nil { jsonErr(w, http.StatusBadRequest, err) return } folderCount, assetCount, err := a.Folders.Counts(ctx, id) if err != nil { jsonErr(w, http.StatusInternalServerError, err) return } f, err := a.Folders.Get(ctx, id) if err != nil { jsonErr(w, http.StatusNotFound, err) return } all, err := a.Folders.All(ctx) if err != nil { jsonErr(w, http.StatusInternalServerError, err) return } descendants := map[int64]bool{id: true} changed := true for changed { changed = false for _, cand := range all { if cand.ParentID != nil && descendants[*cand.ParentID] && !descendants[cand.ID] { descendants[cand.ID] = true changed = true } } } byID := map[int64]models.Folder{} for _, cand := range all { byID[cand.ID] = cand } var pathOf func(int64) string pathOf = func(fid int64) string { ff, ok := byID[fid] if !ok { return "" } if ff.ParentID == nil { return ff.Name } return pathOf(*ff.ParentID) + " / " + ff.Name } type dest struct { ID int64 `json:"id"` Path string `json:"path"` } destinations := []dest{{ID: 0, Path: "Inventory (top level)"}} for _, cand := range all { if descendants[cand.ID] { continue } destinations = append(destinations, dest{ID: cand.ID, Path: pathOf(cand.ID)}) } jsonOK(w, map[string]any{ "folder_name": f.Name, "folder_count": folderCount, "asset_count": assetCount, "destinations": destinations, }) } func (a *App) handleFolderDelete(w http.ResponseWriter, r *http.Request) { ctx := r.Context() id, err := pathID(r, "id") if err != nil { jsonErr(w, http.StatusBadRequest, err) return } mode := r.URL.Query().Get("mode") switch mode { case "relocate": var dest *int64 if raw := r.URL.Query().Get("dest"); raw != "" && raw != "0" { d, err := queryOptionalID(r, "dest") if err != nil { jsonErr(w, http.StatusBadRequest, err) return } dest = d } if err := a.Folders.DeleteAndRelocate(ctx, id, dest); err != nil { jsonErr(w, http.StatusBadRequest, err) return } case "cascade": if err := a.Folders.CascadeDelete(ctx, id); err != nil { jsonErr(w, http.StatusBadRequest, err) return } default: if err := a.Folders.Delete(ctx, id); err != nil { status := http.StatusBadRequest if errors.Is(err, folders.ErrNotEmpty) { status = http.StatusConflict } jsonErr(w, status, err) return } } jsonOK(w, map[string]any{"id": id}) }