diff --git a/controllers/client/courses.controller.js b/controllers/client/courses.controller.js index 548ba4e..00dc5a8 100644 --- a/controllers/client/courses.controller.js +++ b/controllers/client/courses.controller.js @@ -169,7 +169,7 @@ async function canAccessUnit(user_id, unit_id) { attributes: ['course_id'], include: [{ model: Course, as: 'course', attributes: [], where: { status: 'published', ...notDeleted }, required: true }], }); - if (!links.length) return !unit?.subscription; + if (!links.length) return !unit?.subscription || unit.subscription === 'free'; for (const link of links) { if (await canAccessCourse(user_id, link.course_id)) return true; } @@ -185,7 +185,7 @@ async function canAccessLesson(user_id, lesson_id) { if (await hasActivePurchase(user_id, 'lesson', lesson_id)) return true; const unitLinks = await UnitLesson.findAll({ where: { lesson_id }, attributes: ['unit_id'] }); - if (!unitLinks.length) return !lesson?.subscription; + if (!unitLinks.length) return !lesson?.subscription || lesson.subscription === 'free'; for (const link of unitLinks) { if (await canAccessUnit(user_id, link.unit_id)) return true; } diff --git a/controllers/client/media.controller.js b/controllers/client/media.controller.js index c1fe512..8a2eb5a 100644 --- a/controllers/client/media.controller.js +++ b/controllers/client/media.controller.js @@ -97,7 +97,17 @@ function pipeRemoteStream(remoteUrl, req, res) { let clientClosed = false; const proxyReq = transport.request(remoteUrl, { headers: proxyHeaders }, (proxyRes) => { - const status = proxyRes.statusCode === 206 ? 206 : 200; + const status = proxyRes.statusCode ?? 502; + + // Upstream (Garage/S3) returned something other than a successful + // content response — surface the real failure instead of piping its + // (often tiny XML/JSON) error body through as if it were the file. + if (status !== 200 && status !== 206) { + proxyRes.resume(); // drain so the socket can close cleanly + console.error(`[CLIENT][MEDIA][PROXY] Upstream returned ${status} for ${remoteUrl}`); + if (!res.headersSent) res.status(502).json({ message: "Stream unavailable." }); + return; + } [ "content-type", @@ -210,9 +220,10 @@ exports.issueToken = async (req, res) => { // 3. Requester IP matches the IP that issued the token // // Range requests for the same token are allowed (browser seeking). -// TEMPORARY: -// Same with problem from s3.service.js (Line 168-171) -// Investigate the issue ourselves. +// pipeRemoteStream() above forwards the real upstream status instead of +// collapsing everything to 200 — see its non-200/206 branch. A similar +// swallowed-status issue may still exist in s3.service.js (~line 168-171), +// not addressed here. exports.streamAsset = async (req, res) => { const { token } = req.params;