package handlers import ( "github.com/gofiber/fiber/v2" "github.com/google/uuid" "github.com/buhapp/backend/internal/audit" "github.com/buhapp/backend/internal/auth" "github.com/buhapp/backend/internal/storage" "github.com/buhapp/backend/internal/users" ) type PhotoHandlers struct { Users *users.Repo Storage *storage.Storage Audit *audit.Repo } func NewPhotoHandlers(u *users.Repo, s *storage.Storage, au *audit.Repo) *PhotoHandlers { return &PhotoHandlers{Users: u, Storage: s, Audit: au} } const maxPhotoSize = 8 * 1024 * 1024 // 8 MB var allowedPhotoTypes = map[string]bool{ "image/jpeg": true, "image/jpg": true, "image/png": true, "image/webp": true, } // UploadPhoto — POST /me/photo (multipart, поле "photo") func (h *PhotoHandlers) UploadPhoto(c *fiber.Ctx) error { uid, err := auth.UserID(c) if err != nil { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "no user"}) } uuidUser, err := uuid.Parse(uid) if err != nil { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "bad user id"}) } if h.Storage == nil { return c.Status(fiber.StatusServiceUnavailable).JSON(fiber.Map{"error": "storage not configured"}) } file, err := c.FormFile("photo") if err != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "photo field required"}) } if file.Size > maxPhotoSize { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "file too large (max 8MB)"}) } contentType := file.Header.Get("Content-Type") if !allowedPhotoTypes[contentType] { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid image type"}) } src, err := file.Open() if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "open failed"}) } defer src.Close() url, err := h.Storage.UploadAvatar(c.UserContext(), uuidUser, contentType, src, file.Size) if err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "upload failed: " + err.Error()}) } // обновим photo_url в users sql := `UPDATE users SET photo_url=$1, updated_at=NOW() WHERE id=$2` if _, err := h.Users.UpdateRaw(c.UserContext(), sql, url, uuidUser); err != nil { return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "db update failed"}) } _ = h.Audit.Log(c.UserContext(), &audit.Event{ UserID: &uuidUser, Action: "user.photo_upload", IP: c.IP(), UserAgent: c.Get("User-Agent"), }) return c.JSON(fiber.Map{"ok": true, "photo_url": url}) }