diff --git a/backend/handlers/handle_message_upload.go b/backend/handlers/handle_message_upload.go new file mode 100644 index 0000000..b31086f --- /dev/null +++ b/backend/handlers/handle_message_upload.go @@ -0,0 +1,63 @@ +package handlers + +import ( + "crypto/rand" + "encoding/hex" + "net/http" + "os" + "path/filepath" + "strings" + + "github.com/gin-gonic/gin" +) + +var allowedExtensions = map[string]bool{ + ".jpg": true, ".jpeg": true, ".png": true, ".gif": true, ".webp": true, + ".mp4": true, ".webm": true, ".mp3": true, ".ogg": true, + ".pdf": true, ".txt": true, +} + +func (store *Store) UploadMessageFile(ctx *gin.Context) { + file, err := ctx.FormFile("file") + if err != nil { + ctx.JSON(http.StatusBadRequest, gin.H{"error": "file is required"}) + return + } + + const maxSize = 10 << 20 // 10MB + if file.Size > maxSize { + ctx.JSON(http.StatusBadRequest, gin.H{"error": "file too large"}) + return + } + + ext := strings.ToLower(filepath.Ext(file.Filename)) + if !allowedExtensions[ext] { + ctx.JSON(http.StatusBadRequest, gin.H{"error": "file type not allowed"}) + return + } + + b := make([]byte, 16) + if _, err := rand.Read(b); err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "failed to generate filename"}) + return + } + filename := hex.EncodeToString(b) + ext + + uploadDir := "/backend/uploads" + if err := os.MkdirAll(uploadDir, 0755); err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create upload directory"}) + return + } + + dest := filepath.Join(uploadDir, filename) + if err := ctx.SaveUploadedFile(file, dest); err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save file"}) + return + } + if err := os.Chmod(dest, 0644); err != nil { + ctx.JSON(http.StatusInternalServerError, gin.H{"error": "failed to set file permissions"}) + return + } + + ctx.JSON(http.StatusOK, gin.H{"url": "/uploads/" + filename}) +}