// Package handlers package handlers import ( "encoding/json" "net/http" "strconv" "github.com/go-chi/chi/v5" "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/middleware" "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/models" "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/repository" "gogs.fxtmmsk.ru/foxtime/photoplaces/backend/internal/validator" ) type ServiceHandler struct { serviceRepo *repository.ServiceRepo tagRepo *repository.TagRepo } func NewServiceHandler(serviceRepo *repository.ServiceRepo, tagRepo *repository.TagRepo) *ServiceHandler { return &ServiceHandler{serviceRepo: serviceRepo, tagRepo: tagRepo} } func (h *ServiceHandler) List(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() filter := models.ServiceFilter{Status: "published"} if tags := q["tags"]; len(tags) > 0 { filter.Tags = tags } if mr := q.Get("min_rating"); mr != "" { if v, err := strconv.ParseFloat(mr, 64); err == nil { filter.MinRating = v } } if pmin := q.Get("price_min"); pmin != "" { if v, err := strconv.Atoi(pmin); err == nil { filter.PriceMin = &v } } if pmax := q.Get("price_max"); pmax != "" { if v, err := strconv.Atoi(pmax); err == nil { filter.PriceMax = &v } } if limit := q.Get("limit"); limit != "" { if v, err := strconv.Atoi(limit); err == nil { filter.Limit_ = v } } services, err := h.serviceRepo.List(r.Context(), filter) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } if services == nil { services = []*models.Service{} } writeJSON(w, http.StatusOK, map[string]interface{}{"data": services}) } func (h *ServiceHandler) GetByID(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") svc, err := h.serviceRepo.GetByID(r.Context(), id) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } if svc == nil { writeError(w, http.StatusNotFound, "service not found") return } tags, err := h.serviceRepo.GetTags(r.Context(), id) if err == nil { svc.Tags = tags } writeJSON(w, http.StatusOK, svc) } type createServiceRequest struct { Title string `json:"title" validate:"required,min=1,max=255"` Description *string `json:"description" validate:"omitempty,max=5000"` Price int `json:"price" validate:"required,min=1"` Currency string `json:"currency" validate:"required,currency,len=3"` DurationMinutes *int `json:"duration_minutes" validate:"omitempty,min=1,max=10080"` Tags []string `json:"tags" validate:"dive,slug,max=50"` } type updateServiceRequest struct { Title *string `json:"title" validate:"omitempty,min=1,max=255"` Description *string `json:"description" validate:"omitempty,max=5000"` Price *int `json:"price" validate:"omitempty,min=1"` Currency *string `json:"currency" validate:"omitempty,currency,len=3"` DurationMinutes *int `json:"duration_minutes" validate:"omitempty,min=1,max=10080"` Tags []string `json:"tags" validate:"dive,slug,max=50"` } func (h *ServiceHandler) Create(w http.ResponseWriter, r *http.Request) { userID := middleware.GetUserID(r.Context()) var req createServiceRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } if err := validator.Validate(req); err != nil { writeValidationError(w, err) return } tags := make([]models.Tag, len(req.Tags)) for i, t := range req.Tags { tags[i] = models.Tag{ID: t} } svc := &models.Service{ ExecutorID: userID, Title: req.Title, Description: req.Description, Price: req.Price, Currency: req.Currency, DurationMinutes: req.DurationMinutes, Status: "published", Tags: tags, } if err := h.serviceRepo.Create(r.Context(), svc); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusCreated, svc) } func (h *ServiceHandler) Update(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") userID := middleware.GetUserID(r.Context()) role := middleware.GetUserRole(r.Context()) var req updateServiceRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, "invalid request body") return } if err := validator.Validate(req); err != nil { writeValidationError(w, err) return } svc, err := h.serviceRepo.GetByID(r.Context(), id) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } if svc == nil { writeError(w, http.StatusNotFound, "service not found") return } if svc.ExecutorID != userID && role != "moderator" && role != "superadmin" { writeError(w, http.StatusForbidden, "not your service") return } if req.Title != nil { svc.Title = *req.Title } if req.Description != nil { svc.Description = req.Description } if req.Price != nil { svc.Price = *req.Price } if req.Currency != nil { svc.Currency = *req.Currency } if req.DurationMinutes != nil { svc.DurationMinutes = req.DurationMinutes } if err := h.serviceRepo.Update(r.Context(), svc); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } writeJSON(w, http.StatusOK, svc) } func (h *ServiceHandler) Delete(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") userID := middleware.GetUserID(r.Context()) role := middleware.GetUserRole(r.Context()) svc, err := h.serviceRepo.GetByID(r.Context(), id) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } if svc == nil { writeError(w, http.StatusNotFound, "service not found") return } if svc.ExecutorID != userID && role != "moderator" && role != "superadmin" { writeError(w, http.StatusForbidden, "not your service") return } if err := h.serviceRepo.SoftDelete(r.Context(), id); err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } w.WriteHeader(http.StatusNoContent) }