Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-07-11 12:12:29 +08:00
parent e1ffdab190
commit 82ea9c77c4
19 changed files with 530 additions and 122 deletions
+13 -24
View File
@@ -17,10 +17,11 @@
* be able to access at least one attached course. Lessons resolve through
* their parent units the same way.
*
* Discovery rule (getUnits/getLessons only): only course-free content is
* listed at all — a unit with any course affiliation, or a lesson with any
* unit that has a course affiliation, is excluded outright rather than
* listed-but-locked. This does not affect the single-item endpoints above
* Discovery rule (getUnits/getLessons only): ALL non-deleted units/lessons
* are listed, whether or not they're attached to a course — course_count/
* courses[] (published courses only) and is_locked tell the learner whether
* a given item is standalone or bound, and if bound, whether they already
* have access. This does not affect the single-item endpoints above
* (:uuid) — those still enforce access normally for direct links, and
* course-scoped consumption runs through a separate controller entirely.
*
@@ -61,12 +62,11 @@ function sanitizeQuestions(questions = []) {
// ─── UNIT LIBRARY (learner view) ──────────────────────────────────────────────
// Client-side Units/Lessons browsing only ever shows INDEPENDENT content —
// anything affiliated with a course (directly, or for a lesson, through any
// of its attached units) is excluded from these listings entirely, not just
// flagged locked. This does not affect course-scoped consumption (which runs
// through ClientCoursesContext/getCourse, a separate path) or direct-link
// access to UnitDetails/LessonDetails, which still enforce access normally.
// Client-side Units/Lessons browsing shows ALL content, bound to a course or
// not — course_count/courses[] + is_locked below tell the learner which is
// which. This does not affect course-scoped consumption (which runs through
// ClientCoursesContext/getCourse, a separate path) or direct-link access to
// UnitDetails/LessonDetails, which still enforce access normally.
exports.getUnits = async (req, res) => {
try {
const rows = await sequelize.query(`
@@ -76,17 +76,12 @@ exports.getUnits = async (req, res) => {
JOIN lessons l ON l.lesson_id = ul.lesson_id AND l."deletedAt" IS NULL
WHERE ul.unit_id = u.unit_id) AS lesson_count,
(SELECT CAST(COUNT(*) AS INTEGER) FROM course_units cu
JOIN courses c ON c.course_id = cu.course_id AND c."deletedAt" IS NULL
JOIN courses c ON c.course_id = cu.course_id AND c."deletedAt" IS NULL AND c.status = 'published'
WHERE cu.unit_id = u.unit_id) AS course_count,
(SELECT quiz_id FROM unit_quizzes q
WHERE q.unit_id = u.unit_id AND q."deletedAt" IS NULL LIMIT 1) AS quiz_id
FROM units u
WHERE u."deletedAt" IS NULL
AND NOT EXISTS (
SELECT 1 FROM course_units cu
JOIN courses c ON c.course_id = cu.course_id AND c."deletedAt" IS NULL
WHERE cu.unit_id = u.unit_id
)
ORDER BY u.title ASC
`, { type: sequelize.QueryTypes.SELECT });
@@ -97,7 +92,7 @@ exports.getUnits = async (req, res) => {
const courseLinkRows = unitIds.length ? await sequelize.query(`
SELECT cu.unit_id, c.course_id, c.uuid, c.title, c.subscription
FROM course_units cu
JOIN courses c ON c.course_id = cu.course_id AND c."deletedAt" IS NULL
JOIN courses c ON c.course_id = cu.course_id AND c."deletedAt" IS NULL AND c.status = 'published'
WHERE cu.unit_id IN (:unitIds)
`, { replacements: { unitIds }, type: sequelize.QueryTypes.SELECT }) : [];
@@ -141,12 +136,6 @@ exports.getLessons = async (req, res) => {
WHERE ul.lesson_id = l.lesson_id) AS unit_count
FROM lessons l
WHERE l."deletedAt" IS NULL
AND NOT EXISTS (
SELECT 1 FROM unit_lessons ul
JOIN course_units cu ON cu.unit_id = ul.unit_id
JOIN courses c ON c.course_id = cu.course_id AND c."deletedAt" IS NULL
WHERE ul.lesson_id = l.lesson_id
)
ORDER BY l.title ASC
`, { type: sequelize.QueryTypes.SELECT });
@@ -157,7 +146,7 @@ exports.getLessons = async (req, res) => {
SELECT DISTINCT ul.lesson_id, c.course_id, c.uuid, c.title, c.subscription
FROM unit_lessons ul
JOIN course_units cu ON cu.unit_id = ul.unit_id
JOIN courses c ON c.course_id = cu.course_id AND c."deletedAt" IS NULL
JOIN courses c ON c.course_id = cu.course_id AND c."deletedAt" IS NULL AND c.status = 'published'
WHERE ul.lesson_id IN (:lessonIds)
`, { replacements: { lessonIds }, type: sequelize.QueryTypes.SELECT }) : [];