From 8a8a4669153409223442d94bc7a58f9cb736f2de Mon Sep 17 00:00:00 2001 From: Keiran Date: Thu, 7 Aug 2025 18:16:16 +0100 Subject: [PATCH] update the upload handler to use bucket service --- internal/handlers/handlers.go | 228 ++++++++++++++++++++++++++++------ 1 file changed, 188 insertions(+), 40 deletions(-) diff --git a/internal/handlers/handlers.go b/internal/handlers/handlers.go index 841a713..1846a23 100644 --- a/internal/handlers/handlers.go +++ b/internal/handlers/handlers.go @@ -1,75 +1,223 @@ package handlers import ( - "io" - "os" + "context" + "fmt" + "net/http" + "strconv" + "strings" + "git.keircn.com/keiran/termcloud/internal/db" "github.com/labstack/echo/v4" ) -type FileInfo struct { - FileName string `json:"fileName"` - FileSize int64 `json:"fileSize"` - FileType string `json:"fileType"` - FileURL string `json:"fileURL"` +type Handlers struct { + bucketService *db.BucketService } -func RootHandler(c echo.Context) error { +func NewHandlers(bucketService *db.BucketService) *Handlers { + return &Handlers{bucketService: bucketService} +} + +func (h *Handlers) RootHandler(c echo.Context) error { return c.JSON(200, map[string]string{ "status": "😺", "docs": "https://illfillthisoutlater.com", }) } -func UploadHandler(c echo.Context) error { - file, err := c.FormFile("file") - if err != nil { - c.Logger().Errorf("Error retrieving file from request: %v", err) - return c.JSON(400, map[string]string{"error": "Failed to retrieve file"}) +func (h *Handlers) AuthMiddleware(next echo.HandlerFunc) echo.HandlerFunc { + return func(c echo.Context) error { + apiKey := c.Request().Header.Get("X-API-Key") + if apiKey == "" { + return c.JSON(401, map[string]string{"error": "API key required"}) + } + + user, err := h.bucketService.GetUserByAPIKey(context.Background(), apiKey) + if err != nil { + return c.JSON(401, map[string]string{"error": "Invalid API key"}) + } + + c.Set("user", user) + return next(c) + } +} + +func (h *Handlers) CreateBucketHandler(c echo.Context) error { + user := c.Get("user").(*db.User) + + var req struct { + Name string `json:"name"` + } + if err := c.Bind(&req); err != nil { + return c.JSON(400, map[string]string{"error": "Invalid request body"}) } - if file == nil { - c.Logger().Error("No file provided in the request") - return c.JSON(400, map[string]string{"error": "No file provided"}) + if req.Name == "" { + return c.JSON(400, map[string]string{"error": "Bucket name is required"}) + } + + bucket, err := h.bucketService.CreateBucket(context.Background(), req.Name, user.ID) + if err != nil { + if strings.Contains(err.Error(), "duplicate key") { + return c.JSON(409, map[string]string{"error": "Bucket name already exists"}) + } + return c.JSON(500, map[string]string{"error": "Failed to create bucket"}) + } + + return c.JSON(201, bucket) +} + +func (h *Handlers) ListBucketsHandler(c echo.Context) error { + user := c.Get("user").(*db.User) + + buckets, err := h.bucketService.GetUserBuckets(context.Background(), user.ID) + if err != nil { + return c.JSON(500, map[string]string{"error": "Failed to list buckets"}) + } + + return c.JSON(200, map[string]interface{}{ + "buckets": buckets, + }) +} + +func (h *Handlers) DeleteBucketHandler(c echo.Context) error { + user := c.Get("user").(*db.User) + bucketName := c.Param("bucket") + + if err := h.bucketService.DeleteBucket(context.Background(), bucketName, user.ID); err != nil { + if strings.Contains(err.Error(), "no rows") { + return c.JSON(404, map[string]string{"error": "Bucket not found"}) + } + return c.JSON(500, map[string]string{"error": "Failed to delete bucket"}) + } + + return c.JSON(200, map[string]string{"message": "Bucket deleted successfully"}) +} + +func (h *Handlers) UploadObjectHandler(c echo.Context) error { + user := c.Get("user").(*db.User) + bucketName := c.Param("bucket") + objectKey := c.Param("key") + + bucket, err := h.bucketService.GetBucket(context.Background(), bucketName, user.ID) + if err != nil { + if strings.Contains(err.Error(), "no rows") { + return c.JSON(404, map[string]string{"error": "Bucket not found"}) + } + return c.JSON(500, map[string]string{"error": "Failed to get bucket"}) + } + + file, err := c.FormFile("file") + if err != nil { + return c.JSON(400, map[string]string{"error": "Failed to retrieve file"}) } src, err := file.Open() if err != nil { - c.Logger().Errorf("Error opening uploaded file: %v", err) return c.JSON(500, map[string]string{"error": "Failed to open file"}) } defer src.Close() - fileName := file.Filename - fileSize := file.Size - fileType := file.Header.Get("Content-Type") + contentType := file.Header.Get("Content-Type") + if contentType == "" { + contentType = "application/octet-stream" + } - c.Logger().Infof("Received file: %s, Size: %d bytes, Type: %s", fileName, fileSize, fileType) - - err = os.MkdirAll("uploads", 0o755) + object, err := h.bucketService.UploadObject(context.Background(), bucket.ID, objectKey, file.Size, contentType, src) if err != nil { - c.Logger().Errorf("Error creating uploads directory: %v", err) - return c.JSON(500, map[string]string{"error": "Failed to create upload directory"}) + if strings.Contains(err.Error(), "storage limit exceeded") { + return c.JSON(413, map[string]string{"error": "Storage limit exceeded"}) + } + return c.JSON(500, map[string]string{"error": "Failed to upload object"}) } - dst, err := os.Create("uploads/" + fileName) + return c.JSON(201, object) +} + +func (h *Handlers) ListObjectsHandler(c echo.Context) error { + user := c.Get("user").(*db.User) + bucketName := c.Param("bucket") + + bucket, err := h.bucketService.GetBucket(context.Background(), bucketName, user.ID) if err != nil { - c.Logger().Errorf("Error creating file on server: %v", err) - return c.JSON(500, map[string]string{"error": "Failed to save file"}) - } - defer dst.Close() - - if _, err := io.Copy(dst, src); err != nil { - c.Logger().Errorf("Error saving uploaded file: %v", err) - return c.JSON(500, map[string]string{"error": "Failed to save file"}) + if strings.Contains(err.Error(), "no rows") { + return c.JSON(404, map[string]string{"error": "Bucket not found"}) + } + return c.JSON(500, map[string]string{"error": "Failed to get bucket"}) } - fileURL := "http://localhost:8080/uploads/" + fileName + objects, err := h.bucketService.ListObjects(context.Background(), bucket.ID) + if err != nil { + return c.JSON(500, map[string]string{"error": "Failed to list objects"}) + } - return c.JSON(200, FileInfo{ - FileName: fileName, - FileSize: fileSize, - FileType: fileType, - FileURL: fileURL, + return c.JSON(200, map[string]interface{}{ + "objects": objects, + }) +} + +func (h *Handlers) GetObjectHandler(c echo.Context) error { + user := c.Get("user").(*db.User) + bucketName := c.Param("bucket") + objectKey := c.Param("key") + + file, err := h.bucketService.GetObjectFile(context.Background(), bucketName, objectKey, user.ID) + if err != nil { + if strings.Contains(err.Error(), "no rows") { + return c.JSON(404, map[string]string{"error": "Object not found"}) + } + return c.JSON(500, map[string]string{"error": "Failed to get object"}) + } + defer file.Close() + + stat, err := file.Stat() + if err != nil { + return c.JSON(500, map[string]string{"error": "Failed to get file info"}) + } + + c.Response().Header().Set("Content-Length", strconv.FormatInt(stat.Size(), 10)) + c.Response().Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", objectKey)) + + return c.Stream(http.StatusOK, "application/octet-stream", file) +} + +func (h *Handlers) DeleteObjectHandler(c echo.Context) error { + user := c.Get("user").(*db.User) + bucketName := c.Param("bucket") + objectKey := c.Param("key") + + bucket, err := h.bucketService.GetBucket(context.Background(), bucketName, user.ID) + if err != nil { + if strings.Contains(err.Error(), "no rows") { + return c.JSON(404, map[string]string{"error": "Bucket not found"}) + } + return c.JSON(500, map[string]string{"error": "Failed to get bucket"}) + } + + if err := h.bucketService.DeleteObject(context.Background(), bucket.ID, objectKey); err != nil { + return c.JSON(500, map[string]string{"error": "Failed to delete object"}) + } + + return c.JSON(200, map[string]string{"message": "Object deleted successfully"}) +} + +func (h *Handlers) GetUserInfoHandler(c echo.Context) error { + user := c.Get("user").(*db.User) + + buckets, err := h.bucketService.GetUserBuckets(context.Background(), user.ID) + if err != nil { + return c.JSON(500, map[string]string{"error": "Failed to get user buckets"}) + } + + var totalUsage int64 + for _, bucket := range buckets { + totalUsage += bucket.StorageUsedBytes + } + + return c.JSON(200, map[string]interface{}{ + "user": user, + "totalUsage": totalUsage, + "bucketCount": len(buckets), }) }