mirror of
https://github.com/Noooste/garage-ui.git
synced 2026-07-26 07:48:13 +00:00
feat: add Docker support with Dockerfile, docker-compose.yml, and .dockerignore
This commit is contained in:
@@ -0,0 +1,341 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"Noooste/garage-ui/internal/models"
|
||||
"Noooste/garage-ui/internal/services"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
)
|
||||
|
||||
// UserHandler handles user/key management operations using Garage Admin API
|
||||
type UserHandler struct {
|
||||
adminService *services.GarageAdminService
|
||||
}
|
||||
|
||||
// NewUserHandler creates a new user handler
|
||||
func NewUserHandler(adminService *services.GarageAdminService) *UserHandler {
|
||||
return &UserHandler{
|
||||
adminService: adminService,
|
||||
}
|
||||
}
|
||||
|
||||
// ListUsers lists all users/access keys
|
||||
//
|
||||
// @Summary List all users
|
||||
// @Description Retrieves a list of all users/access keys
|
||||
// @Tags Users
|
||||
// @Produce json
|
||||
// @Success 200 {object} models.APIResponse{data=models.UserListResponse} "List of users retrieved successfully"
|
||||
// @Failure 500 {object} models.APIResponse{error=models.APIError} "Failed to list users"
|
||||
// @Router /api/v1/users [get]
|
||||
func (h *UserHandler) ListUsers(c fiber.Ctx) error {
|
||||
ctx := c.Context()
|
||||
|
||||
keys, err := h.adminService.ListKeys(ctx)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(
|
||||
models.ErrorResponse(models.ErrCodeInternalError, "Failed to list users: "+err.Error()),
|
||||
)
|
||||
}
|
||||
|
||||
// Convert to UserInfo format
|
||||
users := make([]models.UserInfo, 0, len(keys))
|
||||
for _, key := range keys {
|
||||
// Get full key info to retrieve bucket permissions
|
||||
keyInfo, err := h.adminService.GetKeyInfo(ctx, key.ID, false)
|
||||
if err != nil {
|
||||
// If we can't get full info, skip this key or use basic info
|
||||
continue
|
||||
}
|
||||
|
||||
// Convert bucket permissions to frontend format
|
||||
bucketPermissions := convertBucketPermissionsToBucketPermissions(keyInfo.Buckets)
|
||||
|
||||
// Determine status based on expiration
|
||||
status := "active"
|
||||
if keyInfo.Expired {
|
||||
status = "inactive"
|
||||
}
|
||||
|
||||
users = append(users, models.UserInfo{
|
||||
AccessKeyID: keyInfo.AccessKeyID,
|
||||
Name: keyInfo.Name,
|
||||
CreatedAt: keyInfo.Created,
|
||||
Status: status,
|
||||
BucketPermissions: bucketPermissions,
|
||||
Expiration: keyInfo.Expiration,
|
||||
Expired: keyInfo.Expired,
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(models.SuccessResponse(models.UserListResponse{
|
||||
Users: users,
|
||||
Count: len(users),
|
||||
}))
|
||||
}
|
||||
|
||||
// convertBucketPermissionsToBucketPermissions converts Garage bucket permissions to frontend BucketPermission format
|
||||
func convertBucketPermissionsToBucketPermissions(buckets []models.KeyBucketInfo) []models.BucketPermission {
|
||||
permissions := make([]models.BucketPermission, 0, len(buckets))
|
||||
|
||||
for _, bucket := range buckets {
|
||||
// Get bucket name from aliases
|
||||
var bucketName string
|
||||
if len(bucket.GlobalAliases) > 0 {
|
||||
bucketName = bucket.GlobalAliases[0]
|
||||
} else if len(bucket.LocalAliases) > 0 {
|
||||
bucketName = bucket.LocalAliases[0]
|
||||
} else {
|
||||
bucketName = bucket.ID
|
||||
}
|
||||
|
||||
// Create bucket permission with simple read/write/owner flags
|
||||
permissions = append(permissions, models.BucketPermission{
|
||||
BucketID: bucket.ID,
|
||||
BucketName: bucketName,
|
||||
Read: bucket.Permissions.Read,
|
||||
Write: bucket.Permissions.Write,
|
||||
Owner: bucket.Permissions.Owner,
|
||||
})
|
||||
}
|
||||
|
||||
return permissions
|
||||
}
|
||||
|
||||
// CreateUser creates a new user/access key
|
||||
//
|
||||
// @Summary Create a new user
|
||||
// @Description Creates a new user/access key with optional name
|
||||
// @Tags Users
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param request body models.CreateUserRequest true "User creation request"
|
||||
// @Success 201 {object} models.APIResponse{data=models.UserInfo} "User created successfully"
|
||||
// @Failure 400 {object} models.APIResponse{error=models.APIError} "Invalid request body"
|
||||
// @Failure 500 {object} models.APIResponse{error=models.APIError} "Failed to create user"
|
||||
// @Router /api/v1/users [post]
|
||||
func (h *UserHandler) CreateUser(c fiber.Ctx) error {
|
||||
ctx := c.Context()
|
||||
|
||||
var req models.CreateUserRequest
|
||||
if err := c.Bind().JSON(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(
|
||||
models.ErrorResponse(models.ErrCodeBadRequest, "Invalid request body: "+err.Error()),
|
||||
)
|
||||
}
|
||||
|
||||
// Prepare create key request
|
||||
createReq := models.CreateKeyRequest{}
|
||||
if req.Name != "" {
|
||||
createReq.Name = &req.Name
|
||||
}
|
||||
|
||||
// Create the key
|
||||
keyInfo, err := h.adminService.CreateKey(ctx, createReq)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(
|
||||
models.ErrorResponse(models.ErrCodeInternalError, "Failed to create user: "+err.Error()),
|
||||
)
|
||||
}
|
||||
|
||||
// Convert bucket permissions to frontend format
|
||||
bucketPermissions := convertBucketPermissionsToBucketPermissions(keyInfo.Buckets)
|
||||
|
||||
// Determine status
|
||||
status := "active"
|
||||
if keyInfo.Expired {
|
||||
status = "inactive"
|
||||
}
|
||||
|
||||
// Convert to UserInfo format
|
||||
userInfo := models.UserInfo{
|
||||
AccessKeyID: keyInfo.AccessKeyID,
|
||||
SecretKey: keyInfo.SecretAccessKey,
|
||||
Name: keyInfo.Name,
|
||||
CreatedAt: keyInfo.Created,
|
||||
Status: status,
|
||||
BucketPermissions: bucketPermissions,
|
||||
Expiration: keyInfo.Expiration,
|
||||
Expired: keyInfo.Expired,
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(models.SuccessResponse(userInfo))
|
||||
}
|
||||
|
||||
// DeleteUser deletes a user/access key
|
||||
//
|
||||
// @Summary Delete a user
|
||||
// @Description Deletes a specific user/access key
|
||||
// @Tags Users
|
||||
// @Produce json
|
||||
// @Param access_key path string true "Access key of the user to delete"
|
||||
// @Success 200 {object} models.APIResponse{data=map[string]interface{}} "User deleted successfully"
|
||||
// @Failure 400 {object} models.APIResponse{error=models.APIError} "Access key is required"
|
||||
// @Failure 500 {object} models.APIResponse{error=models.APIError} "Failed to delete user"
|
||||
// @Router /api/v1/users/{access_key} [delete]
|
||||
func (h *UserHandler) DeleteUser(c fiber.Ctx) error {
|
||||
ctx := c.Context()
|
||||
accessKey := c.Params("access_key")
|
||||
|
||||
if accessKey == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(
|
||||
models.ErrorResponse(models.ErrCodeBadRequest, "Access key is required"),
|
||||
)
|
||||
}
|
||||
|
||||
// Delete the key
|
||||
err := h.adminService.DeleteKey(ctx, accessKey)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(
|
||||
models.ErrorResponse(models.ErrCodeInternalError, "Failed to delete user: "+err.Error()),
|
||||
)
|
||||
}
|
||||
|
||||
return c.JSON(models.SuccessResponse(map[string]interface{}{
|
||||
"access_key": accessKey,
|
||||
"deleted": true,
|
||||
}))
|
||||
}
|
||||
|
||||
// GetUser retrieves information about a specific user/access key
|
||||
//
|
||||
// @Summary Get user information
|
||||
// @Description Retrieves information about a specific user/access key
|
||||
// @Tags Users
|
||||
// @Produce json
|
||||
// @Param access_key path string true "Access key of the user to retrieve"
|
||||
// @Success 200 {object} models.APIResponse{data=models.UserInfo} "User information retrieved successfully"
|
||||
// @Failure 400 {object} models.APIResponse{error=models.APIError} "Access key is required"
|
||||
// @Failure 500 {object} models.APIResponse{error=models.APIError} "Failed to get user info"
|
||||
// @Router /api/v1/users/{access_key} [get]
|
||||
func (h *UserHandler) GetUser(c fiber.Ctx) error {
|
||||
ctx := c.Context()
|
||||
accessKey := c.Params("access_key")
|
||||
|
||||
if accessKey == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(
|
||||
models.ErrorResponse(models.ErrCodeBadRequest, "Access key is required"),
|
||||
)
|
||||
}
|
||||
|
||||
// Get key information (without secret key)
|
||||
keyInfo, err := h.adminService.GetKeyInfo(ctx, accessKey, false)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(
|
||||
models.ErrorResponse(models.ErrCodeInternalError, "Failed to get user info: "+err.Error()),
|
||||
)
|
||||
}
|
||||
|
||||
// Convert bucket permissions to frontend format
|
||||
bucketPermissions := convertBucketPermissionsToBucketPermissions(keyInfo.Buckets)
|
||||
|
||||
// Determine status
|
||||
status := "active"
|
||||
if keyInfo.Expired {
|
||||
status = "inactive"
|
||||
}
|
||||
|
||||
// Convert to UserInfo format
|
||||
userInfo := models.UserInfo{
|
||||
AccessKeyID: keyInfo.AccessKeyID,
|
||||
Name: keyInfo.Name,
|
||||
CreatedAt: keyInfo.Created,
|
||||
Status: status,
|
||||
BucketPermissions: bucketPermissions,
|
||||
Expiration: keyInfo.Expiration,
|
||||
Expired: keyInfo.Expired,
|
||||
}
|
||||
|
||||
return c.JSON(models.SuccessResponse(userInfo))
|
||||
}
|
||||
|
||||
// UpdateUserPermissions updates user permissions
|
||||
//
|
||||
// @Summary Update user permissions
|
||||
// @Description Updates the permissions and settings for a specific user/access key
|
||||
// @Tags Users
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param access_key path string true "Access key of the user to update"
|
||||
// @Param request body models.UpdateUserRequest true "User update request with new permissions"
|
||||
// @Success 200 {object} models.APIResponse{data=models.UserInfo} "User updated successfully"
|
||||
// @Failure 400 {object} models.APIResponse{error=models.APIError} "Access key is required or invalid request body"
|
||||
// @Failure 500 {object} models.APIResponse{error=models.APIError} "Failed to update user"
|
||||
// @Router /api/v1/users/{access_key} [patch]
|
||||
func (h *UserHandler) UpdateUserPermissions(c fiber.Ctx) error {
|
||||
ctx := c.Context()
|
||||
accessKey := c.Params("access_key")
|
||||
|
||||
if accessKey == "" {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(
|
||||
models.ErrorResponse(models.ErrCodeBadRequest, "Access key is required"),
|
||||
)
|
||||
}
|
||||
|
||||
var req models.UpdateUserRequest
|
||||
if err := c.Bind().JSON(&req); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(
|
||||
models.ErrorResponse(models.ErrCodeBadRequest, "Invalid request body: "+err.Error()),
|
||||
)
|
||||
}
|
||||
|
||||
// Prepare update request
|
||||
updateReq := models.UpdateKeyRequest{}
|
||||
|
||||
// Handle status change (activate/deactivate)
|
||||
if req.Status != nil {
|
||||
if *req.Status == "inactive" {
|
||||
// Deactivate by setting expiration to the past
|
||||
pastTime := time.Now().Add(-24 * time.Hour)
|
||||
updateReq.Expiration = &pastTime
|
||||
updateReq.NeverExpires = false
|
||||
} else if *req.Status == "active" {
|
||||
// Activate by removing expiration (set to never expire)
|
||||
updateReq.NeverExpires = true
|
||||
}
|
||||
}
|
||||
|
||||
// Handle explicit expiration date setting
|
||||
if req.Expiration != nil && *req.Expiration != "" {
|
||||
expirationTime, err := time.Parse(time.RFC3339, *req.Expiration)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(
|
||||
models.ErrorResponse(models.ErrCodeBadRequest, "Invalid expiration date format: "+err.Error()),
|
||||
)
|
||||
}
|
||||
updateReq.Expiration = &expirationTime
|
||||
updateReq.NeverExpires = false
|
||||
}
|
||||
|
||||
// Update the key
|
||||
keyInfo, err := h.adminService.UpdateKey(ctx, accessKey, updateReq)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(
|
||||
models.ErrorResponse(models.ErrCodeInternalError, "Failed to update user: "+err.Error()),
|
||||
)
|
||||
}
|
||||
|
||||
// Convert bucket permissions to frontend format
|
||||
bucketPermissions := convertBucketPermissionsToBucketPermissions(keyInfo.Buckets)
|
||||
|
||||
// Determine status
|
||||
status := "active"
|
||||
if keyInfo.Expired {
|
||||
status = "inactive"
|
||||
}
|
||||
|
||||
// Convert to UserInfo format
|
||||
userInfo := models.UserInfo{
|
||||
AccessKeyID: keyInfo.AccessKeyID,
|
||||
Name: keyInfo.Name,
|
||||
CreatedAt: keyInfo.Created,
|
||||
Status: status,
|
||||
BucketPermissions: bucketPermissions,
|
||||
Expiration: keyInfo.Expiration,
|
||||
Expired: keyInfo.Expired,
|
||||
}
|
||||
|
||||
return c.JSON(models.SuccessResponse(userInfo))
|
||||
}
|
||||
Reference in New Issue
Block a user