package routes import ( "os" "path/filepath" "strings" "time" "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" "stereo.cat/backend/internal/auth" "stereo.cat/backend/internal/types" ) func RegisterFileRoutes(cfg *types.StereoConfig, api *gin.RouterGroup) { api.POST("/upload", auth.JwtMiddleware(cfg.JWTSecret), func(c *gin.Context) { claims := c.MustGet("claims").(jwt.MapClaims) user := claims["user"].(auth.User) uid := user.ID if uid == "" { c.JSON(401, gin.H{"error": "unauthorized"}) return } file, err := c.FormFile("file") if err != nil { c.JSON(400, gin.H{"error": "file is required"}) return } filePath := filepath.Join(cfg.ImagePath, uid, file.Filename) if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil { c.JSON(500, gin.H{"error": "failed to create directory"}) return } if err := c.SaveUploadedFile(file, filePath); err != nil { c.JSON(500, gin.H{"error": "failed to save file"}) return } if file.Size <= 0 { c.JSON(400, gin.H{"error": "file size must be greater than zero"}) return } fileMeta := types.File{ Name: file.Filename, Owner: uid, CreatedAt: time.Now(), Size: file.Size, } if err := cfg.Database.Create(&fileMeta).Error; err != nil { c.JSON(500, gin.H{"error": "failed to save file metadata"}) return } c.JSON(200, gin.H{"message": "file uploaded successfully", "name": fileMeta.Name}) }) api.DELETE("/:uid/:name", auth.JwtMiddleware(cfg.JWTSecret), func(c *gin.Context) { claims := c.MustGet("claims").(jwt.MapClaims) user := claims["user"].(auth.User) uid := c.Param("uid") uid = strings.TrimSpace(uid) if uid == "" { c.JSON(400, gin.H{"error": "uid is required"}) return } if uid != user.ID { c.JSON(403, gin.H{"error": "you can only delete your own files"}) return } filename := c.Param("name") filename = strings.TrimSpace(filename) if filename == "" { c.JSON(400, gin.H{"error": "filename is required"}) return } path := filepath.Join(cfg.ImagePath, uid, filename) if _, err := os.Stat(path); os.IsNotExist(err) { c.JSON(404, gin.H{"error": "file not found"}) return } if err := os.Remove(path); err != nil { c.JSON(500, gin.H{"error": "failed to delete file"}) return } if err := cfg.Database.Where("owner = ? AND name = ?", uid, filename).Delete(&types.File{}).Error; err != nil { c.JSON(500, gin.H{"error": "failed to delete file metadata"}) return } c.JSON(200, gin.H{"message": "file deleted successfully"}) }) api.GET("/:uid/:name", func(c *gin.Context) { uid := c.Param("uid") uid = strings.TrimSpace(uid) if uid == "" { c.JSON(400, gin.H{"error": "uid is required"}) return } filename := c.Param("name") filename = strings.TrimSpace(filename) safe := "" for _, r := range filename { if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '.' || r == '-' { safe += string(r) } else { safe += "_" } } filename = safe // parts := strings.SplitN(name, "_", 2) // if len(parts) != 2 { // c.JSON(400, gin.H{"error": "invalid file name"}) // return // } // uid, filename := parts[0], parts[1] // if uid == "" || filename == "" { // c.JSON(400, gin.H{"error": "invalid file name"}) // return // } path := filepath.Join(cfg.ImagePath, uid, filename) if _, err := os.Stat(path); err != nil { c.JSON(404, gin.H{"error": "file not found"}) return } c.File(path) }) api.GET("/list", auth.JwtMiddleware(cfg.JWTSecret), func(c *gin.Context) { claims := c.MustGet("claims").(jwt.MapClaims) user := claims["user"].(auth.User) var files []types.File if err := cfg.Database.Where("owner = ?", user.ID).Find(&files).Error; err != nil { c.JSON(500, gin.H{"error": "failed to retrieve files"}) return } c.JSON(200, files) }) }