Files
sheetless-server/src/handlers/sheets.go
2026-01-23 22:47:21 +01:00

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.")
}