package api import ( "context" "database/sql" "net/http" "strconv" "github.com/labstack/echo/v4" "go.uber.org/zap" db "git.entr0py.de/garionion/inventory/database/sqlite/generated" ) type StorageHandler struct { db *db.Queries logger *zap.Logger } func NewStorageHandler(db *db.Queries, logger *zap.Logger) *StorageHandler { return &StorageHandler{ db: db, logger: logger, } } // GetStorageSpaces returns all storage spaces func (h *StorageHandler) GetStorageSpaces(c echo.Context) error { ctx := context.Background() storageSpaces, err := h.db.GetAllStorageSpaces(ctx) if err != nil { h.logger.Error("Failed to fetch storage spaces", zap.Error(err)) return c.JSON(http.StatusInternalServerError, map[string]string{ "error": "Failed to fetch storage spaces", }) } // Convert to a format suitable for the frontend result := make([]map[string]interface{}, len(storageSpaces)) for i, space := range storageSpaces { result[i] = map[string]interface{}{ "id": space.ID, "parent": space.Parent, "location": space.Location, "name": space.Name, } } return c.JSON(http.StatusOK, result) } // GetObjectsInStorage returns all objects in a specific storage space func (h *StorageHandler) GetObjectsInStorage(c echo.Context) error { ctx := context.Background() // Parse storage ID from URL storageIDStr := c.Param("id") storageID, err := strconv.ParseInt(storageIDStr, 10, 64) if err != nil { h.logger.Warn("Invalid storage ID", zap.String("id", storageIDStr)) return c.JSON(http.StatusBadRequest, map[string]string{ "error": "Invalid storage ID", }) } // Check if storage exists _, err = h.db.GetStorageSpaceByID(ctx, storageID) if err != nil { if err == sql.ErrNoRows { return c.JSON(http.StatusNotFound, map[string]string{ "error": "Storage space not found", }) } h.logger.Error("Failed to fetch storage space", zap.Error(err)) return c.JSON(http.StatusInternalServerError, map[string]string{ "error": "Failed to fetch storage space", }) } // Get objects in this storage objects, err := h.db.GetObjectsByStorageID(ctx, storageID) if err != nil { h.logger.Error("Failed to fetch objects in storage", zap.Error(err)) return c.JSON(http.StatusInternalServerError, map[string]string{ "error": "Failed to fetch objects in storage", }) } // Convert to a format suitable for the frontend result := make([]map[string]interface{}, len(objects)) for i, obj := range objects { result[i] = map[string]interface{}{ "id": obj.ID, "name": obj.Name, "storagespaceId": obj.StoragespaceID, "description": obj.Description.String, "serialnumber": obj.Serialnumber.String, } } return c.JSON(http.StatusOK, result) } // GetStorageHierarchyObjects returns all objects in a storage hierarchy func (h *StorageHandler) GetStorageHierarchyObjects(c echo.Context) error { ctx := context.Background() // Parse root storage ID from URL rootStorageIDStr := c.Param("id") rootStorageID, err := strconv.ParseInt(rootStorageIDStr, 10, 64) if err != nil { h.logger.Warn("Invalid storage ID", zap.String("id", rootStorageIDStr)) return c.JSON(http.StatusBadRequest, map[string]string{ "error": "Invalid storage ID", }) } // Check if root storage exists _, err = h.db.GetStorageSpaceByID(ctx, rootStorageID) if err != nil { if err == sql.ErrNoRows { return c.JSON(http.StatusNotFound, map[string]string{ "error": "Storage space not found", }) } h.logger.Error("Failed to fetch storage space", zap.Error(err)) return c.JSON(http.StatusInternalServerError, map[string]string{ "error": "Failed to fetch storage space", }) } // Get all storage spaces to build the hierarchy allStorageSpaces, err := h.db.GetAllStorageSpaces(ctx) if err != nil { h.logger.Error("Failed to fetch all storage spaces", zap.Error(err)) return c.JSON(http.StatusInternalServerError, map[string]string{ "error": "Failed to fetch storage hierarchy", }) } // Build a list of all storage IDs in the hierarchy storageIDs := []int64{rootStorageID} storageIDs = append(storageIDs, h.getChildStorageIDs(allStorageSpaces, rootStorageID)...) // Get objects for all storage spaces in the hierarchy var allObjects []map[string]interface{} for _, storageID := range storageIDs { // Get objects in this storage objects, err := h.db.GetObjectsByStorageID(ctx, storageID) if err != nil { h.logger.Error("Failed to fetch objects in storage", zap.Int64("storageID", storageID), zap.Error(err)) continue // Skip this storage if there's an error } // Convert to a format suitable for the frontend for _, obj := range objects { allObjects = append(allObjects, map[string]interface{}{ "id": obj.ID, "name": obj.Name, "storagespaceId": obj.StoragespaceID, "description": obj.Description.String, "serialnumber": obj.Serialnumber.String, }) } } return c.JSON(http.StatusOK, allObjects) } // Helper function to recursively get all child storage IDs func (h *StorageHandler) getChildStorageIDs(spaces []db.Storagespace, parentID int64) []int64 { var childIDs []int64 for _, space := range spaces { if space.Parent.Valid && space.Parent.Int64 == parentID { childIDs = append(childIDs, space.ID) // Recursively get children of this child childIDs = append(childIDs, h.getChildStorageIDs(spaces, space.ID)...) } } return childIDs } // CreateStorageSpace creates a new storage space func (h *StorageHandler) CreateStorageSpace(c echo.Context) error { ctx := context.Background() // Parse request body type StorageSpaceRequest struct { Name string `json:"name"` Location string `json:"location"` Parent *struct { Valid bool `json:"valid"` Int64 int64 `json:"int64"` } `json:"parent"` } var req StorageSpaceRequest if err := c.Bind(&req); err != nil { h.logger.Warn("Invalid request body", zap.Error(err)) return c.JSON(http.StatusBadRequest, map[string]string{ "error": "Invalid request body", }) } // Convert parent to sql.NullInt64 var parent sql.NullInt64 if req.Parent != nil && req.Parent.Valid { parent.Valid = true parent.Int64 = req.Parent.Int64 // Verify parent exists _, err := h.db.GetStorageSpaceByID(ctx, parent.Int64) if err != nil { if err == sql.ErrNoRows { return c.JSON(http.StatusBadRequest, map[string]string{ "error": "Parent storage space not found", }) } h.logger.Error("Failed to fetch parent storage space", zap.Error(err)) return c.JSON(http.StatusInternalServerError, map[string]string{ "error": "Failed to verify parent storage space", }) } } // Convert location to sql.NullString var location sql.NullString if req.Location != "" { location.Valid = true location.String = req.Location } // Create storage space err := h.db.CreateStorageSpace(ctx, db.CreateStorageSpaceParams{ Parent: parent, Location: location, Name: req.Name, }) if err != nil { h.logger.Error("Failed to create storage space", zap.Error(err)) return c.JSON(http.StatusInternalServerError, map[string]string{ "error": "Failed to create storage space", }) } return c.JSON(http.StatusCreated, map[string]string{ "message": "Storage space created successfully", }) } // RegisterRoutes registers all storage-related routes func (h *StorageHandler) RegisterRoutes(g *echo.Group) { g.GET("/storageSpaces", h.GetStorageSpaces) g.POST("/storageSpaces", h.CreateStorageSpace) g.GET("/storageSpaces/:id/objects", h.GetObjectsInStorage) g.GET("/storageSpaces/:id/hierarchy/objects", h.GetStorageHierarchyObjects) }