Add lesson retrieval functionality in VocabController and VocabService

- Introduced a new method in VocabService to fetch lesson details, including access control based on user ownership and lesson visibility.
- Updated VocabController to wrap the new method for user access.
- Added a new route in VocabRouter to handle requests for specific lessons.
- Enhanced VocabCourseListView to support navigation to individual lesson views, improving user experience in accessing lesson content.
This commit is contained in:
Torsten Schulz (local)
2026-01-19 15:07:52 +01:00
parent 33787ba796
commit c13cb40c7b
6 changed files with 238 additions and 2 deletions

View File

@@ -32,6 +32,7 @@ class VocabController {
this.deleteCourse = this._wrapWithUser((userId, req) => this.service.deleteCourse(userId, req.params.courseId));
// Lessons
this.getLesson = this._wrapWithUser((userId, req) => this.service.getLesson(userId, req.params.lessonId));
this.addLessonToCourse = this._wrapWithUser((userId, req) => this.service.addLessonToCourse(userId, req.params.courseId, req.body), { successStatus: 201 });
this.updateLesson = this._wrapWithUser((userId, req) => this.service.updateLesson(userId, req.params.lessonId, req.body));
this.deleteLesson = this._wrapWithUser((userId, req) => this.service.deleteLesson(userId, req.params.lessonId));

View File

@@ -43,6 +43,7 @@ router.delete('/courses/:courseId/enroll', vocabController.unenrollFromCourse);
// Progress
router.get('/courses/:courseId/progress', vocabController.getCourseProgress);
router.get('/lessons/:lessonId', vocabController.getLesson);
router.put('/lessons/:lessonId/progress', vocabController.updateLessonProgress);
// Grammar Exercises

View File

@@ -846,6 +846,33 @@ export default class VocabService {
return { success: true };
}
async getLesson(hashedUserId, lessonId) {
const user = await this._getUserByHashedId(hashedUserId);
const lesson = await VocabCourseLesson.findByPk(lessonId, {
include: [
{
model: VocabCourse,
as: 'course'
}
]
});
if (!lesson) {
const err = new Error('Lesson not found');
err.status = 404;
throw err;
}
// Prüfe Zugriff
if (lesson.course.ownerUserId !== user.id && !lesson.course.isPublic) {
const err = new Error('Access denied');
err.status = 403;
throw err;
}
return lesson.get({ plain: true });
}
async addLessonToCourse(hashedUserId, courseId, { chapterId, lessonNumber, title, description, weekNumber, dayNumber, lessonType, audioUrl, culturalNotes, targetMinutes, targetScorePercent, requiresReview }) {
const user = await this._getUserByHashedId(hashedUserId);
const course = await VocabCourse.findByPk(courseId);