Features: - PDF service for generating task reports with ReportLab-style formatting - Storage service for file uploads (local and S3-compatible) - Admin authentication middleware with JWT support - Admin user model and repository Infrastructure: - Updated Docker configuration for admin panel builds - Email service enhancements for task notifications - Updated router with admin and file upload routes - Environment configuration updates Tests: - Unit tests for handlers (auth, residence, task) - Unit tests for models (user, residence, task) - Unit tests for repositories (user, residence, task) - Unit tests for services (residence, task) - Integration test setup - Test utilities for mocking database and services Database: - Admin user seed data - Updated test data seeds 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
97 lines
2.4 KiB
Go
97 lines
2.4 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/treytartt/mycrib-api/internal/services"
|
|
)
|
|
|
|
// UploadHandler handles file upload endpoints
|
|
type UploadHandler struct {
|
|
storageService *services.StorageService
|
|
}
|
|
|
|
// NewUploadHandler creates a new upload handler
|
|
func NewUploadHandler(storageService *services.StorageService) *UploadHandler {
|
|
return &UploadHandler{storageService: storageService}
|
|
}
|
|
|
|
// UploadImage handles POST /api/uploads/image
|
|
// Accepts multipart/form-data with "file" field
|
|
func (h *UploadHandler) UploadImage(c *gin.Context) {
|
|
file, err := c.FormFile("file")
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "No file provided"})
|
|
return
|
|
}
|
|
|
|
// Get category from query param (default: images)
|
|
category := c.DefaultQuery("category", "images")
|
|
|
|
result, err := h.storageService.Upload(file, category)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, result)
|
|
}
|
|
|
|
// UploadDocument handles POST /api/uploads/document
|
|
// Accepts multipart/form-data with "file" field
|
|
func (h *UploadHandler) UploadDocument(c *gin.Context) {
|
|
file, err := c.FormFile("file")
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "No file provided"})
|
|
return
|
|
}
|
|
|
|
result, err := h.storageService.Upload(file, "documents")
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, result)
|
|
}
|
|
|
|
// UploadCompletion handles POST /api/uploads/completion
|
|
// For task completion photos
|
|
func (h *UploadHandler) UploadCompletion(c *gin.Context) {
|
|
file, err := c.FormFile("file")
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "No file provided"})
|
|
return
|
|
}
|
|
|
|
result, err := h.storageService.Upload(file, "completions")
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, result)
|
|
}
|
|
|
|
// DeleteFile handles DELETE /api/uploads
|
|
// Expects JSON body with "url" field
|
|
func (h *UploadHandler) DeleteFile(c *gin.Context) {
|
|
var req struct {
|
|
URL string `json:"url" binding:"required"`
|
|
}
|
|
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
if err := h.storageService.Delete(req.URL); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "File deleted successfully"})
|
|
}
|