188 lines
4.4 KiB
Go
188 lines
4.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"sheetless-server/database"
|
|
"sheetless-server/models"
|
|
"sheetless-server/utils"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
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) {
|
|
// Get form data
|
|
title := c.PostForm("title")
|
|
if title == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Title is required"})
|
|
return
|
|
}
|
|
|
|
composerUUIDStr := c.PostForm("composer_uuid")
|
|
if composerUUIDStr == "" {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Composer UUID is required"})
|
|
return
|
|
}
|
|
|
|
composerUUID, err := uuid.Parse(composerUUIDStr)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid composer UUID"})
|
|
return
|
|
}
|
|
|
|
var composer models.Composer
|
|
if err := database.DB.First(&composer, composerUUID).Error; err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Composer not found"})
|
|
return
|
|
}
|
|
|
|
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%s", 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
|
|
}
|
|
|
|
fileHash, err := utils.FileHashFromUpload(out)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed calculating hash"})
|
|
return
|
|
}
|
|
|
|
uuid, err := GenerateNonexistentSheetUuid()
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed generating sheet uuid"})
|
|
return
|
|
}
|
|
|
|
// Create database record
|
|
sheet := models.Sheet{
|
|
Uuid: *uuid,
|
|
Title: title,
|
|
Description: description,
|
|
FilePath: filePath,
|
|
FileSize: fileInfo.Size(),
|
|
FileHash: fileHash,
|
|
ComposerId: composer.Uuid,
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}
|
|
|
|
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.Sheet
|
|
|
|
if err := database.DB.Preload("Composer").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) {
|
|
uuid, err := uuid.Parse(c.Param("uuid"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid sheet uuid"})
|
|
return
|
|
}
|
|
|
|
var sheet models.Sheet
|
|
if err := database.DB.First(&sheet, uuid).Error; err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Sheet not found"})
|
|
return
|
|
}
|
|
|
|
if _, err := os.Stat(sheet.FilePath); os.IsNotExist(err) {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "File not found"})
|
|
return
|
|
}
|
|
|
|
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filepath.Base(sheet.FilePath)))
|
|
c.Header("Content-Type", "application/pdf")
|
|
c.File(sheet.FilePath)
|
|
}
|
|
|
|
func GenerateNonexistentSheetUuid() (*uuid.UUID, error) {
|
|
for i := 0; i < 10; i++ {
|
|
uuid := uuid.New()
|
|
|
|
var exists bool
|
|
err := database.DB.Model(&models.Sheet{}).
|
|
Select("count(*) > 0").
|
|
Where("uuid = ?", uuid).
|
|
Find(&exists).
|
|
Error
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !exists {
|
|
return &uuid, nil
|
|
}
|
|
}
|
|
return nil, errors.New("Somehow unable to generate new uuid for sheet.")
|
|
}
|