package handler import ( "net/http" "strings" "github.com/go-chi/chi/v5" "learning-service/internal/model" "learning-service/internal/repository" ) type CourseHandler struct { repo *repository.CourseRepository accessRepo *repository.AccessGrantRepository } func NewCourseHandler(repo *repository.CourseRepository, accessRepo *repository.AccessGrantRepository) *CourseHandler { return &CourseHandler{repo: repo, accessRepo: accessRepo} } // List — GET /courses?mine=true. Зеркально TestHandler.List: // mine=true → только мои; // без mine → published + видимые через access_grants. // Семантика union'а: published все + дозалив unpublished, выданных через grant. func (h *CourseHandler) List(w http.ResponseWriter, r *http.Request) { uid, ok := userIDFromHeader(r) if !ok { writeError(w, http.StatusUnauthorized, "unauthorized") return } mine := r.URL.Query().Get("mine") == "true" if mine { items, err := h.repo.List(r.Context(), &uid, false, nil) if err != nil { writeRepoError(w, r, err, "list courses") return } writeJSON(w, http.StatusOK, map[string]any{"items": items}) return } viewer := viewerContextFromHeaders(r, uid) grantIDs, err := h.accessRepo.ResolveVisibleResourceIDs(r.Context(), "course", viewer) if err != nil { writeRepoError(w, r, err, "resolve access") return } items, err := h.repo.List(r.Context(), nil, true, nil) if err != nil { writeRepoError(w, r, err, "list courses") return } if len(grantIDs) > 0 { extra, err := h.repo.List(r.Context(), nil, false, grantIDs) if err != nil { writeRepoError(w, r, err, "list granted courses") return } seen := map[string]struct{}{} for _, c := range items { seen[c.ID.String()] = struct{}{} } for _, c := range extra { if _, ok := seen[c.ID.String()]; ok { continue } items = append(items, c) } } writeJSON(w, http.StatusOK, map[string]any{"items": items}) } func (h *CourseHandler) Get(w http.ResponseWriter, r *http.Request) { id, err := parseUUID(chi.URLParam(r, "id")) if err != nil { writeError(w, http.StatusBadRequest, "invalid id") return } c, err := h.repo.Get(r.Context(), id) if err != nil { writeRepoError(w, r, err, "get course") return } writeJSON(w, http.StatusOK, c) } func (h *CourseHandler) Create(w http.ResponseWriter, r *http.Request) { uid, ok := userIDFromHeader(r) if !ok { writeError(w, http.StatusUnauthorized, "unauthorized") return } var req model.CreateCourseRequest if err := decodeJSON(r, &req); err != nil { writeError(w, http.StatusBadRequest, "invalid body") return } if strings.TrimSpace(req.Title) == "" { writeError(w, http.StatusBadRequest, "title is required") return } c, err := h.repo.Create(r.Context(), uid, req) if err != nil { writeRepoError(w, r, err, "create course") return } writeJSON(w, http.StatusCreated, c) } func (h *CourseHandler) Update(w http.ResponseWriter, r *http.Request) { uid, ok := userIDFromHeader(r) if !ok { writeError(w, http.StatusUnauthorized, "unauthorized") return } id, err := parseUUID(chi.URLParam(r, "id")) if err != nil { writeError(w, http.StatusBadRequest, "invalid id") return } existing, err := h.repo.Get(r.Context(), id) if err != nil { writeRepoError(w, r, err, "get course for update") return } if existing.OwnerUserID != uid { writeError(w, http.StatusForbidden, "only owner can edit") return } var req model.UpdateCourseRequest if err := decodeJSON(r, &req); err != nil { writeError(w, http.StatusBadRequest, "invalid body") return } c, err := h.repo.Update(r.Context(), id, req) if err != nil { writeRepoError(w, r, err, "update course") return } writeJSON(w, http.StatusOK, c) } func (h *CourseHandler) Delete(w http.ResponseWriter, r *http.Request) { uid, ok := userIDFromHeader(r) if !ok { writeError(w, http.StatusUnauthorized, "unauthorized") return } id, err := parseUUID(chi.URLParam(r, "id")) if err != nil { writeError(w, http.StatusBadRequest, "invalid id") return } existing, err := h.repo.Get(r.Context(), id) if err != nil { writeRepoError(w, r, err, "get course for delete") return } if existing.OwnerUserID != uid { writeError(w, http.StatusForbidden, "only owner can delete") return } if err := h.repo.Delete(r.Context(), id); err != nil { writeRepoError(w, r, err, "delete course") return } w.WriteHeader(http.StatusNoContent) }