mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
@@ -158,8 +158,6 @@ async function canAccessUnit(user_id, unit_id) {
|
||||
const unit = await Unit.findOne({ where: { unit_id, ...notDeleted }, attributes: ['subscription'] });
|
||||
if (unit?.subscription && await hasItemGrant(user_id, 'unit', unit_id)) return true;
|
||||
|
||||
if (await hasActivePurchase(user_id, 'unit', unit_id)) return true;
|
||||
|
||||
// Only links to PUBLISHED courses count as a real course dependency — a unit
|
||||
// whose only link is to a draft/unpublished course behaves as if it had no
|
||||
// course link at all (falls through to the free/standalone branch below),
|
||||
@@ -182,8 +180,6 @@ async function canAccessLesson(user_id, lesson_id) {
|
||||
const lesson = await Lesson.findOne({ where: { lesson_id, ...notDeleted }, attributes: ['subscription'] });
|
||||
if (lesson?.subscription && await hasItemGrant(user_id, 'lesson', lesson_id)) return true;
|
||||
|
||||
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 || lesson.subscription === 'free';
|
||||
for (const link of unitLinks) {
|
||||
@@ -1311,12 +1307,11 @@ exports.getLessonsByUnitUuid = async (req, res) => {
|
||||
|
||||
if (!await canAccessUnit(req.user.user_id, unit.unit_id)) {
|
||||
const first = unit.courses?.[0] ?? null;
|
||||
const { product, has_purchased, purchase_eligible } = await buildCheckoutInfo(req.user.user_id, "unit", unit);
|
||||
return res.status(403).json({
|
||||
status: "error",
|
||||
message: "You do not have access to this unit.",
|
||||
course: first ? { title: first.title, subscription: first.subscription } : null,
|
||||
item: { uuid: unit.uuid, subscription: unit.subscription, product, has_purchased, purchase_eligible },
|
||||
item: { uuid: unit.uuid, subscription: unit.subscription },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1440,12 +1435,11 @@ exports.getLessonByUuid = async (req, res) => {
|
||||
|
||||
if (!await canAccessLesson(req.user.user_id, lesson.lesson_id)) {
|
||||
const firstCourse = lesson.units?.[0]?.courses?.[0] ?? null;
|
||||
const { product, has_purchased, purchase_eligible } = await buildCheckoutInfo(req.user.user_id, "lesson", lesson);
|
||||
return res.status(403).json({
|
||||
status: "error",
|
||||
message: "You do not have access to this lesson.",
|
||||
course: firstCourse ? { title: firstCourse.title, subscription: firstCourse.subscription } : null,
|
||||
item: { uuid: lesson.uuid, subscription: lesson.subscription, product, has_purchased, purchase_eligible },
|
||||
item: { uuid: lesson.uuid, subscription: lesson.subscription },
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1491,18 +1485,16 @@ exports.getLessonByUuid = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// ─── CHECKOUT INFO (course/unit/lesson) ───────────────────────────────────────
|
||||
// Deliberately does NOT hard-403 on locked content like getCourse/
|
||||
// getUnitByUuid/getLessonByUuid do — a locked-and-unpurchased item is exactly
|
||||
// who needs to land on this page and see title/description/product, so it
|
||||
// can't gate on the same canAccess*() check those content-serving routes use.
|
||||
// Auth-only; content stays fully protected behind the routes above.
|
||||
|
||||
const CHECKOUT_PK = { course: "course_id", unit: "unit_id", lesson: "lesson_id" };
|
||||
// ─── CHECKOUT INFO (course) ────────────────────────────────────────────────
|
||||
// Deliberately does NOT hard-403 on locked content like getCourse does — a
|
||||
// locked-and-unpurchased course is exactly who needs to land on this page and
|
||||
// see title/description/product, so it can't gate on the same canAccess*()
|
||||
// check those content-serving routes use. Auth-only; content stays fully
|
||||
// protected behind the routes above.
|
||||
|
||||
async function buildCheckoutInfo(user_id, purchasable_type, record) {
|
||||
const product = await mdl_Product.findOne({
|
||||
where: { purchasable_type, purchasable_id: record[CHECKOUT_PK[purchasable_type]], is_active: true },
|
||||
where: { purchasable_type, purchasable_id: record.course_id, is_active: true },
|
||||
attributes: ["id", "name", "price", "currency", "access_days"],
|
||||
});
|
||||
const hasPurchase = product && await mdl_CoursePurchase.findOne({
|
||||
@@ -1528,30 +1520,4 @@ exports.getCourseCheckoutInfo = async (req, res) => {
|
||||
console.error("[CLIENT][COURSES][CHECKOUT INFO]", err);
|
||||
return R.error(res, "Could not retrieve checkout info.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.getUnitCheckoutInfo = async (req, res) => {
|
||||
try {
|
||||
const { uuid } = req.params;
|
||||
const unit = await Unit.findOne({ where: { uuid, ...notDeleted }, attributes: ["unit_id", "uuid", "title", "description", "subscription"] });
|
||||
if (!unit) return R.error(res, "Unit not found.", 404);
|
||||
const { product, has_purchased, purchase_eligible } = await buildCheckoutInfo(req.user.user_id, "unit", unit);
|
||||
return R.success(res, "Checkout info retrieved.", { ...unit.toJSON(), product, has_purchased, purchase_eligible });
|
||||
} catch (err) {
|
||||
console.error("[CLIENT][UNITS][CHECKOUT INFO]", err);
|
||||
return R.error(res, "Could not retrieve checkout info.", 500);
|
||||
}
|
||||
};
|
||||
|
||||
exports.getLessonCheckoutInfo = async (req, res) => {
|
||||
try {
|
||||
const { uuid } = req.params;
|
||||
const lesson = await Lesson.findOne({ where: { uuid, ...notDeleted }, attributes: ["lesson_id", "uuid", "title", "description", "subscription"] });
|
||||
if (!lesson) return R.error(res, "Lesson not found.", 404);
|
||||
const { product, has_purchased, purchase_eligible } = await buildCheckoutInfo(req.user.user_id, "lesson", lesson);
|
||||
return R.success(res, "Checkout info retrieved.", { ...lesson.toJSON(), product, has_purchased, purchase_eligible });
|
||||
} catch (err) {
|
||||
console.error("[CLIENT][LESSONS][CHECKOUT INFO]", err);
|
||||
return R.error(res, "Could not retrieve checkout info.", 500);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user