75f3dd141c
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CI and Release / lint-frontend (push) Has been cancelled
CI and Release / dockerfile-scan (push) Has been cancelled
CI and Release / test-frontend (push) Has been cancelled
CI and Release / lint-verification-agent (push) Has been cancelled
CI and Release / test-verification-agent (push) Has been cancelled
CI and Release / e2e-verification-agent (push) Has been cancelled
CI and Release / test-backend (push) Has been cancelled
CI and Release / build-dev-image (push) Has been cancelled
CI and Release / push-dev-image (push) Has been cancelled
CI and Release / build-image (push) Has been cancelled
CI and Release / build-verification-image (push) Has been cancelled
CI and Release / determine-version (push) Has been cancelled
CI and Release / push-image (push) Has been cancelled
CI and Release / push-verification-image (12) (push) Has been cancelled
CI and Release / lint-backend (push) Has been cancelled
CI and Release / push-verification-image (17) (push) Has been cancelled
CI and Release / push-verification-image (18) (push) Has been cancelled
CI and Release / release (push) Has been cancelled
CI and Release / publish-helm-chart (push) Has been cancelled
CI and Release / push-verification-image (13) (push) Has been cancelled
CI and Release / push-verification-image (14) (push) Has been cancelled
CI and Release / push-verification-image (15) (push) Has been cancelled
CI and Release / push-verification-image (16) (push) Has been cancelled
340 lines
9.5 KiB
Go
340 lines
9.5 KiB
Go
package storages
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
|
|
users_middleware "databasus-backend/internal/features/users/middleware"
|
|
workspaces_services "databasus-backend/internal/features/workspaces/services"
|
|
)
|
|
|
|
type StorageController struct {
|
|
storageService *StorageService
|
|
workspaceService *workspaces_services.WorkspaceService
|
|
}
|
|
|
|
func (c *StorageController) RegisterRoutes(router *gin.RouterGroup) {
|
|
router.POST("/storages", c.SaveStorage)
|
|
router.GET("/storages", c.GetStorages)
|
|
router.GET("/storages/:id", c.GetStorage)
|
|
router.DELETE("/storages/:id", c.DeleteStorage)
|
|
router.POST("/storages/:id/test", c.TestStorageConnection)
|
|
router.POST("/storages/:id/transfer", c.TransferStorageToWorkspace)
|
|
router.POST("/storages/direct-test", c.TestStorageConnectionDirect)
|
|
}
|
|
|
|
// SaveStorage
|
|
// @Summary Save a storage
|
|
// @Description Create or update a storage
|
|
// @Tags storages
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param Authorization header string true "JWT token"
|
|
// @Param request body Storage true "Storage data with workspaceId"
|
|
// @Success 200 {object} Storage
|
|
// @Failure 400
|
|
// @Failure 401
|
|
// @Failure 403
|
|
// @Router /storages [post]
|
|
func (c *StorageController) SaveStorage(ctx *gin.Context) {
|
|
user, ok := users_middleware.GetUserFromContext(ctx)
|
|
if !ok {
|
|
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
|
return
|
|
}
|
|
|
|
var request Storage
|
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
if request.WorkspaceID == uuid.Nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "workspaceId is required"})
|
|
return
|
|
}
|
|
|
|
if err := c.storageService.SaveStorage(user, request.WorkspaceID, &request); err != nil {
|
|
if errors.Is(err, ErrInsufficientPermissionsToManageStorage) ||
|
|
errors.Is(err, ErrRcloneStorageRequiresAdmin) {
|
|
ctx.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, request)
|
|
}
|
|
|
|
// GetStorage
|
|
// @Summary Get a storage by ID
|
|
// @Description Get a specific storage by ID
|
|
// @Tags storages
|
|
// @Produce json
|
|
// @Param Authorization header string true "JWT token"
|
|
// @Param id path string true "Storage ID"
|
|
// @Success 200 {object} Storage
|
|
// @Failure 400
|
|
// @Failure 401
|
|
// @Failure 403
|
|
// @Router /storages/{id} [get]
|
|
func (c *StorageController) GetStorage(ctx *gin.Context) {
|
|
user, ok := users_middleware.GetUserFromContext(ctx)
|
|
if !ok {
|
|
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
|
return
|
|
}
|
|
|
|
id, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid storage ID"})
|
|
return
|
|
}
|
|
|
|
storage, err := c.storageService.GetStorage(user, id)
|
|
if err != nil {
|
|
if errors.Is(err, ErrInsufficientPermissionsToViewStorage) {
|
|
ctx.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, storage)
|
|
}
|
|
|
|
// GetStorages
|
|
// @Summary Get all storages
|
|
// @Description Get all storages for a workspace
|
|
// @Tags storages
|
|
// @Produce json
|
|
// @Param Authorization header string true "JWT token"
|
|
// @Param workspace_id query string true "Workspace ID"
|
|
// @Success 200 {array} Storage
|
|
// @Failure 400
|
|
// @Failure 401
|
|
// @Failure 403
|
|
// @Router /storages [get]
|
|
func (c *StorageController) GetStorages(ctx *gin.Context) {
|
|
user, ok := users_middleware.GetUserFromContext(ctx)
|
|
if !ok {
|
|
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
|
return
|
|
}
|
|
|
|
workspaceIDStr := ctx.Query("workspace_id")
|
|
if workspaceIDStr == "" {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "workspace_id query parameter is required"})
|
|
return
|
|
}
|
|
|
|
workspaceID, err := uuid.Parse(workspaceIDStr)
|
|
if err != nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid workspace_id"})
|
|
return
|
|
}
|
|
|
|
storages, err := c.storageService.GetStorages(user, workspaceID)
|
|
if err != nil {
|
|
if errors.Is(err, ErrInsufficientPermissionsToViewStorages) {
|
|
ctx.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, storages)
|
|
}
|
|
|
|
// DeleteStorage
|
|
// @Summary Delete a storage
|
|
// @Description Delete a storage by ID
|
|
// @Tags storages
|
|
// @Produce json
|
|
// @Param Authorization header string true "JWT token"
|
|
// @Param id path string true "Storage ID"
|
|
// @Success 200
|
|
// @Failure 400
|
|
// @Failure 401
|
|
// @Failure 403
|
|
// @Router /storages/{id} [delete]
|
|
func (c *StorageController) DeleteStorage(ctx *gin.Context) {
|
|
user, ok := users_middleware.GetUserFromContext(ctx)
|
|
if !ok {
|
|
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
|
return
|
|
}
|
|
|
|
id, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid storage ID"})
|
|
return
|
|
}
|
|
|
|
if err := c.storageService.DeleteStorage(user, id); err != nil {
|
|
if errors.Is(err, ErrInsufficientPermissionsToManageStorage) {
|
|
ctx.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, gin.H{"message": "storage deleted successfully"})
|
|
}
|
|
|
|
// TestStorageConnection
|
|
// @Summary Test storage connection
|
|
// @Description Test the connection to the storage
|
|
// @Tags storages
|
|
// @Produce json
|
|
// @Param Authorization header string true "JWT token"
|
|
// @Param id path string true "Storage ID"
|
|
// @Success 200
|
|
// @Failure 400
|
|
// @Failure 401
|
|
// @Failure 403
|
|
// @Router /storages/{id}/test [post]
|
|
func (c *StorageController) TestStorageConnection(ctx *gin.Context) {
|
|
user, ok := users_middleware.GetUserFromContext(ctx)
|
|
if !ok {
|
|
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
|
return
|
|
}
|
|
|
|
id, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid storage ID"})
|
|
return
|
|
}
|
|
|
|
if err := c.storageService.TestStorageConnection(user, id); err != nil {
|
|
if errors.Is(err, ErrInsufficientPermissionsToTestStorage) {
|
|
ctx.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, gin.H{"message": "storage connection test successful"})
|
|
}
|
|
|
|
// TransferStorageToWorkspace
|
|
// @Summary Transfer storage to another workspace
|
|
// @Description Transfer a storage from one workspace to another
|
|
// @Tags storages
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param Authorization header string true "JWT token"
|
|
// @Param id path string true "Storage ID"
|
|
// @Param request body TransferStorageRequest true "Target workspace ID"
|
|
// @Success 200
|
|
// @Failure 400
|
|
// @Failure 401
|
|
// @Failure 403
|
|
// @Router /storages/{id}/transfer [post]
|
|
func (c *StorageController) TransferStorageToWorkspace(ctx *gin.Context) {
|
|
user, ok := users_middleware.GetUserFromContext(ctx)
|
|
if !ok {
|
|
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
|
return
|
|
}
|
|
|
|
id, err := uuid.Parse(ctx.Param("id"))
|
|
if err != nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "invalid storage ID"})
|
|
return
|
|
}
|
|
|
|
var request TransferStorageRequest
|
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
if request.TargetWorkspaceID == uuid.Nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "targetWorkspaceId is required"})
|
|
return
|
|
}
|
|
|
|
if err := c.storageService.TransferStorageToWorkspace(
|
|
user,
|
|
id,
|
|
request.TargetWorkspaceID,
|
|
nil,
|
|
); err != nil {
|
|
if errors.Is(err, ErrInsufficientPermissionsInSourceWorkspace) ||
|
|
errors.Is(err, ErrInsufficientPermissionsInTargetWorkspace) {
|
|
ctx.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, gin.H{"message": "storage transferred successfully"})
|
|
}
|
|
|
|
// TestStorageConnectionDirect
|
|
// @Summary Test storage connection directly
|
|
// @Description Test the connection to a storage object provided in the request
|
|
// @Tags storages
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param Authorization header string true "JWT token"
|
|
// @Param request body Storage true "Storage data with workspaceId"
|
|
// @Success 200
|
|
// @Failure 400
|
|
// @Failure 401
|
|
// @Failure 403
|
|
// @Router /storages/direct-test [post]
|
|
func (c *StorageController) TestStorageConnectionDirect(ctx *gin.Context) {
|
|
user, ok := users_middleware.GetUserFromContext(ctx)
|
|
if !ok {
|
|
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"})
|
|
return
|
|
}
|
|
|
|
var request Storage
|
|
if err := ctx.ShouldBindJSON(&request); err != nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
if request.WorkspaceID == uuid.Nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": "workspaceId is required"})
|
|
return
|
|
}
|
|
|
|
canView, _, err := c.workspaceService.CanUserAccessWorkspace(request.WorkspaceID, user)
|
|
if err != nil {
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if !canView {
|
|
ctx.JSON(
|
|
http.StatusForbidden,
|
|
gin.H{"error": "insufficient permissions to test storage in this workspace"},
|
|
)
|
|
return
|
|
}
|
|
|
|
if err := c.storageService.TestStorageConnectionDirect(user, &request); err != nil {
|
|
if errors.Is(err, ErrRcloneStorageRequiresAdmin) {
|
|
ctx.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
ctx.JSON(http.StatusOK, gin.H{"message": "storage connection test successful"})
|
|
}
|