feat(lessons): ListVideos — плоский endpoint для раздела «Видео-уроки»
LessonRepository.ListVideos: SELECT с INNER JOIN courses, фильтр
video_key != '' + (course.is_published OR course.owner_user_id = viewer).
Возвращает LessonWithCourse — урок + denorm course_{title,slug,
is_published,owner_user_id} чтобы фронт сгруппировал по курсу
без N+1.
LessonHandler.ListVideos: GET /lessons?has_video=true. Гейт уже на
SQL-уровне, в коде только X-User-Id из headers.
Route регистрируется ДО /lessons/{id}, иначе chi бы заматчил
{id}="lessons".
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,23 @@ func (h *LessonHandler) ListByCourse(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, http.StatusOK, map[string]any{"items": items})
|
||||
}
|
||||
|
||||
// ListVideos — GET /lessons?has_video=true. Плоский список уроков с
|
||||
// видео для отдельной страницы «Видео-уроки» в портале. Гейтит на уровне
|
||||
// репо: только опубликованные курсы или мои.
|
||||
func (h *LessonHandler) ListVideos(w http.ResponseWriter, r *http.Request) {
|
||||
uid, ok := userIDFromHeader(r)
|
||||
if !ok {
|
||||
writeError(w, http.StatusUnauthorized, "unauthorized")
|
||||
return
|
||||
}
|
||||
items, err := h.repo.ListVideos(r.Context(), uid)
|
||||
if err != nil {
|
||||
writeRepoError(w, r, err, "list video lessons")
|
||||
return
|
||||
}
|
||||
writeJSON(w, http.StatusOK, map[string]any{"items": items})
|
||||
}
|
||||
|
||||
func (h *LessonHandler) Get(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := parseUUID(chi.URLParam(r, "id"))
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user