244 lines
6.2 KiB
Go
244 lines
6.2 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"project-dashboard/api/utils"
|
|
"project-dashboard/models"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// UploadFile handles file uploads
|
|
func UploadFile(c *gin.Context) {
|
|
user, exists := c.Get("user")
|
|
if !exists {
|
|
utils.ErrorResponse(c, http.StatusUnauthorized, "User not authenticated")
|
|
return
|
|
}
|
|
|
|
userObj := user.(*models.User)
|
|
project, _ := c.Get("project")
|
|
projectObj := project.(*models.Project)
|
|
|
|
// Get task ID if provided
|
|
taskIDStr := c.PostForm("task_id")
|
|
var taskID *uint
|
|
if taskIDStr != "" {
|
|
if id, err := strconv.ParseUint(taskIDStr, 10, 32); err == nil {
|
|
taskIDUint := uint(id)
|
|
taskID = &taskIDUint
|
|
}
|
|
}
|
|
|
|
// Get file from form
|
|
file, header, err := c.Request.FormFile("file")
|
|
if err != nil {
|
|
utils.ErrorResponse(c, http.StatusBadRequest, "No file provided")
|
|
return
|
|
}
|
|
defer file.Close()
|
|
|
|
// Validate file size (10MB limit)
|
|
const maxFileSize = 10 << 20 // 10MB
|
|
if header.Size > maxFileSize {
|
|
utils.ErrorResponse(c, http.StatusBadRequest, "File size too large (max 10MB)")
|
|
return
|
|
}
|
|
|
|
// Generate unique filename
|
|
ext := filepath.Ext(header.Filename)
|
|
fileName := uuid.New().String() + ext
|
|
|
|
// Create uploads directory if it doesn't exist
|
|
uploadDir := "./uploads"
|
|
if err := os.MkdirAll(uploadDir, 0755); err != nil {
|
|
utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to create upload directory")
|
|
return
|
|
}
|
|
|
|
// Save file
|
|
filePath := filepath.Join(uploadDir, fileName)
|
|
dst, err := os.Create(filePath)
|
|
if err != nil {
|
|
utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to create file")
|
|
return
|
|
}
|
|
defer dst.Close()
|
|
|
|
if _, err := io.Copy(dst, file); err != nil {
|
|
utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to save file")
|
|
return
|
|
}
|
|
|
|
// Get MIME type
|
|
mimeType := header.Header.Get("Content-Type")
|
|
if mimeType == "" {
|
|
mimeType = "application/octet-stream"
|
|
}
|
|
|
|
// Create file record
|
|
fileUpload := models.FileUpload{
|
|
FileName: fileName,
|
|
OriginalName: header.Filename,
|
|
FilePath: filePath,
|
|
FileSize: header.Size,
|
|
MimeType: mimeType,
|
|
UploadedByID: userObj.ID,
|
|
ProjectID: &projectObj.ID,
|
|
TaskID: taskID,
|
|
}
|
|
|
|
if err := models.GetDB().Create(&fileUpload).Error; err != nil {
|
|
// Clean up file if database save fails
|
|
os.Remove(filePath)
|
|
utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to save file record")
|
|
return
|
|
}
|
|
|
|
// Load file with relationships
|
|
models.GetDB().
|
|
Preload("UploadedBy").
|
|
Preload("Project").
|
|
Preload("Task").
|
|
First(&fileUpload, fileUpload.ID)
|
|
|
|
utils.SuccessResponse(c, http.StatusCreated, "File uploaded successfully", fileUpload)
|
|
}
|
|
|
|
// GetFiles returns all files for a project
|
|
func GetFiles(c *gin.Context) {
|
|
project, exists := c.Get("project")
|
|
if !exists {
|
|
utils.ErrorResponse(c, http.StatusNotFound, "Project not found")
|
|
return
|
|
}
|
|
|
|
projectObj := project.(*models.Project)
|
|
|
|
var files []models.FileUpload
|
|
|
|
if err := models.GetDB().
|
|
Preload("UploadedBy").
|
|
Preload("Project").
|
|
Preload("Task").
|
|
Where("project_id = ?", projectObj.ID).
|
|
Order("created_at DESC").
|
|
Find(&files).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to fetch files")
|
|
return
|
|
}
|
|
|
|
utils.SuccessResponse(c, http.StatusOK, "Files retrieved successfully", files)
|
|
}
|
|
|
|
// GetTaskFiles returns all files for a specific task
|
|
func GetTaskFiles(c *gin.Context) {
|
|
project, _ := c.Get("project")
|
|
projectObj := project.(*models.Project)
|
|
|
|
taskID := c.Param("task_id")
|
|
if taskID == "" {
|
|
utils.ErrorResponse(c, http.StatusBadRequest, "Task ID required")
|
|
return
|
|
}
|
|
|
|
// Verify task exists and belongs to project
|
|
var task models.Task
|
|
if err := models.GetDB().
|
|
Where("id = ? AND project_id = ?", taskID, projectObj.ID).
|
|
First(&task).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusNotFound, "Task not found")
|
|
return
|
|
}
|
|
|
|
var files []models.FileUpload
|
|
|
|
if err := models.GetDB().
|
|
Preload("UploadedBy").
|
|
Preload("Project").
|
|
Preload("Task").
|
|
Where("task_id = ?", taskID).
|
|
Order("created_at DESC").
|
|
Find(&files).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to fetch task files")
|
|
return
|
|
}
|
|
|
|
utils.SuccessResponse(c, http.StatusOK, "Task files retrieved successfully", files)
|
|
}
|
|
|
|
// DownloadFile serves a file for download
|
|
func DownloadFile(c *gin.Context) {
|
|
project, _ := c.Get("project")
|
|
projectObj := project.(*models.Project)
|
|
|
|
fileID := c.Param("file_id")
|
|
if fileID == "" {
|
|
utils.ErrorResponse(c, http.StatusBadRequest, "File ID required")
|
|
return
|
|
}
|
|
|
|
var fileUpload models.FileUpload
|
|
if err := models.GetDB().
|
|
Where("id = ? AND project_id = ?", fileID, projectObj.ID).
|
|
First(&fileUpload).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusNotFound, "File not found")
|
|
return
|
|
}
|
|
|
|
// Check if file exists on disk
|
|
if _, err := os.Stat(fileUpload.FilePath); os.IsNotExist(err) {
|
|
utils.ErrorResponse(c, http.StatusNotFound, "File not found on disk")
|
|
return
|
|
}
|
|
|
|
// Set headers for file download
|
|
c.Header("Content-Description", "File Transfer")
|
|
c.Header("Content-Transfer-Encoding", "binary")
|
|
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", fileUpload.OriginalName))
|
|
c.Header("Content-Type", "application/octet-stream")
|
|
|
|
// Serve file
|
|
c.File(fileUpload.FilePath)
|
|
}
|
|
|
|
// DeleteFile deletes a file
|
|
func DeleteFile(c *gin.Context) {
|
|
project, _ := c.Get("project")
|
|
projectObj := project.(*models.Project)
|
|
|
|
fileID := c.Param("file_id")
|
|
if fileID == "" {
|
|
utils.ErrorResponse(c, http.StatusBadRequest, "File ID required")
|
|
return
|
|
}
|
|
|
|
var fileUpload models.FileUpload
|
|
if err := models.GetDB().
|
|
Where("id = ? AND project_id = ?", fileID, projectObj.ID).
|
|
First(&fileUpload).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusNotFound, "File not found")
|
|
return
|
|
}
|
|
|
|
// Delete file from disk
|
|
if err := os.Remove(fileUpload.FilePath); err != nil {
|
|
// Log error but continue with database deletion
|
|
fmt.Printf("Failed to delete file from disk: %v\n", err)
|
|
}
|
|
|
|
// Delete file record from database
|
|
if err := models.GetDB().Delete(&fileUpload).Error; err != nil {
|
|
utils.ErrorResponse(c, http.StatusInternalServerError, "Failed to delete file record")
|
|
return
|
|
}
|
|
|
|
utils.SuccessResponse(c, http.StatusOK, "File deleted successfully", nil)
|
|
}
|