package upload import ( "encoding/json" "fmt" "io" "net/http" "os" "path/filepath" "time" "api_yal/internal/logger" "api_yal/internal/middleware" "api_yal/internal/models" "go.uber.org/zap" "gorm.io/gorm" ) type UploadHandler struct { db *gorm.DB uploadPath string } func NewHandler(db *gorm.DB, uploadPath string) *UploadHandler { if err := os.MkdirAll(uploadPath, 0755); err != nil { logger.Get().Warn("failed to create upload directory", zap.String("path", uploadPath), zap.Error(err)) } return &UploadHandler{db: db, uploadPath: uploadPath} } func (h *UploadHandler) Upload(w http.ResponseWriter, r *http.Request) { userID, ok := r.Context().Value(middleware.UserIDKey).(uint) if !ok { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } if err := r.ParseMultipartForm(32 << 20); err != nil { http.Error(w, "Failed to parse form", http.StatusBadRequest) return } file, header, err := r.FormFile("file") if err != nil { http.Error(w, "File is required", http.StatusBadRequest) return } defer file.Close() objectIDStr := r.FormValue("object_id") if objectIDStr == "" { http.Error(w, "object_id is required", http.StatusBadRequest) return } var objectID uint if _, err := fmt.Sscan(objectIDStr, &objectID); err != nil { http.Error(w, "Invalid object_id", http.StatusBadRequest) return } ext := filepath.Ext(header.Filename) filename := fmt.Sprintf("%d_%d%s", userID, time.Now().UnixNano(), ext) filePath := filepath.Join(h.uploadPath, filename) dst, err := os.Create(filePath) if err != nil { logger.Get().Error("failed to create file", zap.Error(err)) http.Error(w, "Failed to save file", http.StatusInternalServerError) return } defer dst.Close() if _, err := io.Copy(dst, file); err != nil { logger.Get().Error("failed to write file", zap.Error(err)) http.Error(w, "Failed to save file", http.StatusInternalServerError) return } isPrimary := false if r.FormValue("is_primary") == "true" { isPrimary = true } image := models.ObjectImage{ ObjectID: objectID, URL: "/uploads/" + filename, IsPrimary: isPrimary, } if err := h.db.Create(&image).Error; err != nil { logger.Get().Error("failed to save image record", zap.Error(err)) http.Error(w, "Failed to save image", http.StatusInternalServerError) return } w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(map[string]interface{}{ "id": image.ID, "url": image.URL, "object_id": image.ObjectID, "is_primary": image.IsPrimary, }) } func (h *UploadHandler) DeleteImage(w http.ResponseWriter, r *http.Request) { userID, ok := r.Context().Value(middleware.UserIDKey).(uint) if !ok { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } imageIDStr := r.URL.Query().Get("id") if imageIDStr == "" { http.Error(w, "id is required", http.StatusBadRequest) return } var imageID uint if _, err := fmt.Sscan(imageIDStr, &imageID); err != nil { http.Error(w, "Invalid id", http.StatusBadRequest) return } var image models.ObjectImage if err := h.db.First(&image, imageID).Error; err != nil { http.Error(w, "Image not found", http.StatusNotFound) return } var object models.Object if err := h.db.First(&object, image.ObjectID).Error; err != nil { http.Error(w, "Object not found", http.StatusNotFound) return } if object.OwnerID != userID { http.Error(w, "Forbidden", http.StatusForbidden) return } filePath := filepath.Join(h.uploadPath, filepath.Base(image.URL)) os.Remove(filePath) h.db.Delete(&image) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(map[string]string{"message": "Image deleted"}) }