package object_storage import ( "net/http" "time" "github.com/atlasos/calypso/internal/common/database" "github.com/atlasos/calypso/internal/common/logger" "github.com/gin-gonic/gin" ) // Handler handles HTTP requests for object storage type Handler struct { service *Service setupService *SetupService logger *logger.Logger } // NewHandler creates a new object storage handler func NewHandler(service *Service, db *database.DB, log *logger.Logger) *Handler { return &Handler{ service: service, setupService: NewSetupService(db, log), logger: log, } } // ListBuckets lists all buckets func (h *Handler) ListBuckets(c *gin.Context) { buckets, err := h.service.ListBuckets(c.Request.Context()) if err != nil { h.logger.Error("Failed to list buckets", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list buckets: " + err.Error()}) return } c.JSON(http.StatusOK, gin.H{"buckets": buckets}) } // GetBucket gets bucket information func (h *Handler) GetBucket(c *gin.Context) { bucketName := c.Param("name") bucket, err := h.service.GetBucketStats(c.Request.Context(), bucketName) if err != nil { h.logger.Error("Failed to get bucket", "bucket", bucketName, "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get bucket: " + err.Error()}) return } c.JSON(http.StatusOK, bucket) } // CreateBucketRequest represents a request to create a bucket type CreateBucketRequest struct { Name string `json:"name" binding:"required"` } // CreateBucket creates a new bucket func (h *Handler) CreateBucket(c *gin.Context) { var req CreateBucketRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Invalid create bucket request", "error", err) c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()}) return } if err := h.service.CreateBucket(c.Request.Context(), req.Name); err != nil { h.logger.Error("Failed to create bucket", "bucket", req.Name, "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create bucket: " + err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"message": "bucket created successfully", "name": req.Name}) } // DeleteBucket deletes a bucket func (h *Handler) DeleteBucket(c *gin.Context) { bucketName := c.Param("name") if err := h.service.DeleteBucket(c.Request.Context(), bucketName); err != nil { h.logger.Error("Failed to delete bucket", "bucket", bucketName, "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete bucket: " + err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "bucket deleted successfully"}) } // GetAvailableDatasets gets all available pools and datasets for object storage setup func (h *Handler) GetAvailableDatasets(c *gin.Context) { datasets, err := h.setupService.GetAvailableDatasets(c.Request.Context()) if err != nil { h.logger.Error("Failed to get available datasets", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get available datasets: " + err.Error()}) return } c.JSON(http.StatusOK, gin.H{"pools": datasets}) } // SetupObjectStorageRequest represents a request to setup object storage type SetupObjectStorageRequest struct { PoolName string `json:"pool_name" binding:"required"` DatasetName string `json:"dataset_name" binding:"required"` CreateNew bool `json:"create_new"` } // SetupObjectStorage configures object storage with a ZFS dataset func (h *Handler) SetupObjectStorage(c *gin.Context) { var req SetupObjectStorageRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Invalid setup request", "error", err) c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()}) return } setupReq := SetupRequest{ PoolName: req.PoolName, DatasetName: req.DatasetName, CreateNew: req.CreateNew, } result, err := h.setupService.SetupObjectStorage(c.Request.Context(), setupReq) if err != nil { h.logger.Error("Failed to setup object storage", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to setup object storage: " + err.Error()}) return } c.JSON(http.StatusOK, result) } // GetCurrentSetup gets the current object storage configuration func (h *Handler) GetCurrentSetup(c *gin.Context) { setup, err := h.setupService.GetCurrentSetup(c.Request.Context()) if err != nil { h.logger.Error("Failed to get current setup", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get current setup: " + err.Error()}) return } if setup == nil { c.JSON(http.StatusOK, gin.H{"configured": false}) return } c.JSON(http.StatusOK, gin.H{"configured": true, "setup": setup}) } // UpdateObjectStorage updates the object storage configuration func (h *Handler) UpdateObjectStorage(c *gin.Context) { var req SetupObjectStorageRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Invalid update request", "error", err) c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()}) return } setupReq := SetupRequest{ PoolName: req.PoolName, DatasetName: req.DatasetName, CreateNew: req.CreateNew, } result, err := h.setupService.UpdateObjectStorage(c.Request.Context(), setupReq) if err != nil { h.logger.Error("Failed to update object storage", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update object storage: " + err.Error()}) return } c.JSON(http.StatusOK, result) } // ListUsers lists all IAM users func (h *Handler) ListUsers(c *gin.Context) { users, err := h.service.ListUsers(c.Request.Context()) if err != nil { h.logger.Error("Failed to list users", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list users: " + err.Error()}) return } c.JSON(http.StatusOK, gin.H{"users": users}) } // CreateUserRequest represents a request to create a user type CreateUserRequest struct { AccessKey string `json:"access_key" binding:"required"` SecretKey string `json:"secret_key" binding:"required"` } // CreateUser creates a new IAM user func (h *Handler) CreateUser(c *gin.Context) { var req CreateUserRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Invalid create user request", "error", err) c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()}) return } if err := h.service.CreateUser(c.Request.Context(), req.AccessKey, req.SecretKey); err != nil { h.logger.Error("Failed to create user", "access_key", req.AccessKey, "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create user: " + err.Error()}) return } c.JSON(http.StatusCreated, gin.H{"message": "user created successfully", "access_key": req.AccessKey}) } // DeleteUser deletes an IAM user func (h *Handler) DeleteUser(c *gin.Context) { accessKey := c.Param("access_key") if err := h.service.DeleteUser(c.Request.Context(), accessKey); err != nil { h.logger.Error("Failed to delete user", "access_key", accessKey, "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete user: " + err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "user deleted successfully"}) } // ListServiceAccounts lists all service accounts (access keys) func (h *Handler) ListServiceAccounts(c *gin.Context) { accounts, err := h.service.ListServiceAccounts(c.Request.Context()) if err != nil { h.logger.Error("Failed to list service accounts", "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list service accounts: " + err.Error()}) return } c.JSON(http.StatusOK, gin.H{"service_accounts": accounts}) } // CreateServiceAccountRequest represents a request to create a service account type CreateServiceAccountRequest struct { ParentUser string `json:"parent_user" binding:"required"` Policy string `json:"policy,omitempty"` Expiration *string `json:"expiration,omitempty"` // ISO 8601 format } // CreateServiceAccount creates a new service account (access key) func (h *Handler) CreateServiceAccount(c *gin.Context) { var req CreateServiceAccountRequest if err := c.ShouldBindJSON(&req); err != nil { h.logger.Error("Invalid create service account request", "error", err) c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request: " + err.Error()}) return } var expiration *time.Time if req.Expiration != nil { exp, err := time.Parse(time.RFC3339, *req.Expiration) if err != nil { h.logger.Error("Invalid expiration format", "error", err) c.JSON(http.StatusBadRequest, gin.H{"error": "invalid expiration format, use ISO 8601 (RFC3339)"}) return } expiration = &exp } account, err := h.service.CreateServiceAccount(c.Request.Context(), req.ParentUser, req.Policy, expiration) if err != nil { h.logger.Error("Failed to create service account", "parent_user", req.ParentUser, "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create service account: " + err.Error()}) return } c.JSON(http.StatusCreated, account) } // DeleteServiceAccount deletes a service account func (h *Handler) DeleteServiceAccount(c *gin.Context) { accessKey := c.Param("access_key") if err := h.service.DeleteServiceAccount(c.Request.Context(), accessKey); err != nil { h.logger.Error("Failed to delete service account", "access_key", accessKey, "error", err) c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete service account: " + err.Error()}) return } c.JSON(http.StatusOK, gin.H{"message": "service account deleted successfully"}) }