2025-02-28 21:18:07 +01:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"database/sql"
|
|
|
|
"net/http"
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
"github.com/labstack/echo/v4"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
|
2025-02-28 21:40:40 +01:00
|
|
|
db "git.entr0py.de/garionion/inventory/database/sqlite/generated"
|
2025-02-28 21:18:07 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
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()
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
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",
|
|
|
|
})
|
|
|
|
}
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
// 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,
|
2025-02-28 22:03:18 +01:00
|
|
|
"name": space.Name,
|
2025-02-28 21:18:07 +01:00
|
|
|
}
|
|
|
|
}
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
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()
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
// 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",
|
|
|
|
})
|
|
|
|
}
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
// 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",
|
|
|
|
})
|
|
|
|
}
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
// 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",
|
|
|
|
})
|
|
|
|
}
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
// 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,
|
|
|
|
}
|
|
|
|
}
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
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()
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
// 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",
|
|
|
|
})
|
|
|
|
}
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
// 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",
|
|
|
|
})
|
|
|
|
}
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
// 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",
|
|
|
|
})
|
|
|
|
}
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
// Build a list of all storage IDs in the hierarchy
|
|
|
|
storageIDs := []int64{rootStorageID}
|
|
|
|
storageIDs = append(storageIDs, h.getChildStorageIDs(allStorageSpaces, rootStorageID)...)
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
// Get objects for all storage spaces in the hierarchy
|
|
|
|
var allObjects []map[string]interface{}
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
for _, storageID := range storageIDs {
|
|
|
|
// Get objects in this storage
|
|
|
|
objects, err := h.db.GetObjectsByStorageID(ctx, storageID)
|
|
|
|
if err != nil {
|
2025-02-28 21:40:40 +01:00
|
|
|
h.logger.Error("Failed to fetch objects in storage",
|
2025-02-28 21:18:07 +01:00
|
|
|
zap.Int64("storageID", storageID),
|
|
|
|
zap.Error(err))
|
|
|
|
continue // Skip this storage if there's an error
|
|
|
|
}
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
// 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,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
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
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
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)...)
|
|
|
|
}
|
|
|
|
}
|
2025-02-28 21:40:40 +01:00
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
return childIDs
|
|
|
|
}
|
|
|
|
|
2025-02-28 21:46:13 +01:00
|
|
|
// CreateStorageSpace creates a new storage space
|
|
|
|
func (h *StorageHandler) CreateStorageSpace(c echo.Context) error {
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
// Parse request body
|
|
|
|
type StorageSpaceRequest struct {
|
2025-02-28 22:03:18 +01:00
|
|
|
Name string `json:"name"`
|
2025-02-28 21:46:13 +01:00
|
|
|
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,
|
2025-02-28 22:17:29 +01:00
|
|
|
Name: req.Name,
|
2025-02-28 21:46:13 +01:00
|
|
|
})
|
|
|
|
|
|
|
|
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",
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2025-02-28 21:18:07 +01:00
|
|
|
// RegisterRoutes registers all storage-related routes
|
|
|
|
func (h *StorageHandler) RegisterRoutes(g *echo.Group) {
|
|
|
|
g.GET("/storageSpaces", h.GetStorageSpaces)
|
2025-02-28 21:46:13 +01:00
|
|
|
g.POST("/storageSpaces", h.CreateStorageSpace)
|
2025-02-28 21:18:07 +01:00
|
|
|
g.GET("/storageSpaces/:id/objects", h.GetObjectsInStorage)
|
|
|
|
g.GET("/storageSpaces/:id/hierarchy/objects", h.GetStorageHierarchyObjects)
|
|
|
|
}
|