package handlers import ( "fmt" "io" "net/http" "os" "path/filepath" "sheetless-server/database" "sheetless-server/models" "strconv" "time" "github.com/gin-gonic/gin" ) const uploadDir = "./uploads" func init() { // Create uploads directory if it doesn't exist if err := os.MkdirAll(uploadDir, 0755); err != nil { panic("Failed to create uploads directory: " + err.Error()) } } func UploadSheet(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "User not authenticated"}) return } // Get form data title := c.PostForm("title") if title == "" { c.JSON(http.StatusBadRequest, gin.H{"error": "Title is required"}) return } composer := c.PostForm("composer") description := c.PostForm("description") // Get uploaded file file, header, err := c.Request.FormFile("file") if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "File upload failed"}) return } defer file.Close() // Validate file type (should be PDF) if filepath.Ext(header.Filename) != ".pdf" { c.JSON(http.StatusBadRequest, gin.H{"error": "Only PDF files are allowed"}) return } // Generate unique filename filename := fmt.Sprintf("%d_%d%s", userID.(uint), time.Now().Unix(), filepath.Ext(header.Filename)) filePath := filepath.Join(uploadDir, filename) // Save file out, err := os.Create(filePath) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save file"}) return } defer out.Close() _, err = io.Copy(out, file) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save file"}) return } // Get file size fileInfo, err := out.Stat() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get file info"}) return } // Create database record sheet := models.MusicSheet{ Title: title, Composer: composer, Description: description, FilePath: filePath, FileSize: fileInfo.Size(), UserID: userID.(uint), } if err := database.DB.Create(&sheet).Error; err != nil { // Clean up file if database insert fails os.Remove(filePath) c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save sheet metadata"}) return } c.JSON(http.StatusCreated, gin.H{ "message": "Sheet uploaded successfully", "sheet": sheet, }) } func ListSheets(c *gin.Context) { var sheets []models.MusicSheet query := database.DB.Preload("User") // Filter by user if specified if userIDStr := c.Query("user_id"); userIDStr != "" { userID, err := strconv.ParseUint(userIDStr, 10, 32) if err == nil { query = query.Where("user_id = ?", uint(userID)) } } if err := query.Find(&sheets).Error; err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch sheets"}) return } c.JSON(http.StatusOK, gin.H{"sheets": sheets}) } func DownloadSheet(c *gin.Context) { idStr := c.Param("id") id, err := strconv.ParseUint(idStr, 10, 32) if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid sheet ID"}) return } var sheet models.MusicSheet if err := database.DB.First(&sheet, uint(id)).Error; err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Sheet not found"}) return } // Check if file exists if _, err := os.Stat(sheet.FilePath); os.IsNotExist(err) { c.JSON(http.StatusNotFound, gin.H{"error": "File not found"}) return } // Set headers for file download c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filepath.Base(sheet.FilePath))) c.Header("Content-Type", "application/pdf") c.File(sheet.FilePath) }