mirror of
https://github.com/rgrgogu/new_starr.git
synced 2026-09-27 00:12:54 +08:00
added: course func()
Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
@@ -0,0 +1,441 @@
|
|||||||
|
const { Op } = require("sequelize");
|
||||||
|
const sequelize = require("../../config/db.config");
|
||||||
|
const R = require("../../utils/response.util");
|
||||||
|
const { paginate } = require("../../utils/paginate.util");
|
||||||
|
|
||||||
|
const Course = require("../../models/courses/courses.mdl");
|
||||||
|
const Unit = require("../../models/courses/units.mdl");
|
||||||
|
const Lesson = require("../../models/courses/lessons.mdl");
|
||||||
|
const LessonPage = require("../../models/courses/lesson-page.mdl");
|
||||||
|
const mdl_Users = require("../../models/users/users.mdl");
|
||||||
|
|
||||||
|
const notDeleted = { deletedAt: null };
|
||||||
|
|
||||||
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
const auditByFields = ["createdBy", "updatedBy", "deletedBy"];
|
||||||
|
const adminExclude = [];
|
||||||
|
|
||||||
|
// ─── COURSE ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
exports.getCourses = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const result = await paginate(Course, req, {
|
||||||
|
excludeAttributes: adminExclude,
|
||||||
|
auditOptions: { mdl_Users, parentAlias: "Course" },
|
||||||
|
context: "list",
|
||||||
|
findOptions: { where: { ...notDeleted } },
|
||||||
|
});
|
||||||
|
return R.success(res, "Courses retrieved.", result);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[COURSE][GET ALL]", err);
|
||||||
|
return R.error(res, "Could not retrieve courses.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getCourse = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { courseId } = req.params;
|
||||||
|
|
||||||
|
const course = await Course.findOne({
|
||||||
|
where: { course_id: courseId, ...notDeleted },
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: Unit,
|
||||||
|
as: "units",
|
||||||
|
where: notDeleted,
|
||||||
|
required: false,
|
||||||
|
order_index: [["order", "ASC"]],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
order_index: [[{ model: Unit, as: "units" }, "order_index", "ASC"]],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!course) return R.error(res, "Course not found.", 404);
|
||||||
|
|
||||||
|
return R.success(res, "Course retrieved.", { data: course });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[COURSE][GET ONE]", err);
|
||||||
|
return R.error(res, "Could not retrieve course.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.createCourse = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { title, description, order_index } = req.body;
|
||||||
|
if (!title) return R.error(res, "Title is required.", 400);
|
||||||
|
|
||||||
|
const course = await Course.create({
|
||||||
|
title,
|
||||||
|
description: description ?? null,
|
||||||
|
order_index: order_index ?? 0,
|
||||||
|
createdBy: req.body.createdBy ?? null,
|
||||||
|
});
|
||||||
|
|
||||||
|
return R.success(res, "Course created.", { data: course }, 201);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[COURSE][CREATE]", err);
|
||||||
|
return R.error(res, "Could not create course.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.updateCourse = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { courseId } = req.params;
|
||||||
|
|
||||||
|
const course = await Course.findOne({ where: { course_id: courseId, ...notDeleted } });
|
||||||
|
if (!course) return R.error(res, "Course not found.", 404);
|
||||||
|
|
||||||
|
const { title, description, order_index } = req.body;
|
||||||
|
|
||||||
|
if (title !== undefined) course.title = title;
|
||||||
|
if (description !== undefined) course.description = description;
|
||||||
|
if (order_index !== undefined) course.order_index = order_index;
|
||||||
|
course.updatedBy = req.body.updatedBy ?? null;
|
||||||
|
|
||||||
|
await course.save();
|
||||||
|
|
||||||
|
return R.success(res, "Course updated.", { data: course });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[COURSE][UPDATE]", err);
|
||||||
|
return R.error(res, "Could not update course.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.deleteCourse = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { courseId } = req.params;
|
||||||
|
|
||||||
|
const course = await Course.findOne({ where: { course_id: courseId, ...notDeleted } });
|
||||||
|
if (!course) return R.error(res, "Course not found.", 404);
|
||||||
|
|
||||||
|
await course.update({ deletedBy: req.body.deletedBy ?? null });
|
||||||
|
await course.destroy();
|
||||||
|
|
||||||
|
return R.success(res, "Course deleted.");
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[COURSE][DELETE]", err);
|
||||||
|
return R.error(res, "Could not delete course.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─── UNIT ─────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
exports.getUnits = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { courseId } = req.params;
|
||||||
|
|
||||||
|
const course = await Course.findOne({ where: { course_id: courseId, ...notDeleted } });
|
||||||
|
if (!course) return R.error(res, "Course not found.", 404);
|
||||||
|
|
||||||
|
const result = await paginate(Unit, req, {
|
||||||
|
excludeAttributes: adminExclude,
|
||||||
|
auditOptions: { mdl_Users, parentAlias: "Unit" },
|
||||||
|
context: "list",
|
||||||
|
findOptions: {
|
||||||
|
where: { course_id: courseId, ...notDeleted },
|
||||||
|
order: [["order_index", "ASC"]],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return R.success(res, "Units retrieved.", result);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[UNIT][GET ALL]", err);
|
||||||
|
return R.error(res, "Could not retrieve units.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getUnit = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { courseId, unitId } = req.params;
|
||||||
|
|
||||||
|
const unit = await Unit.findOne({
|
||||||
|
where: { unit_id: unitId, course_id: courseId, ...notDeleted },
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: Lesson,
|
||||||
|
as: "lessons",
|
||||||
|
where: notDeleted,
|
||||||
|
required: false,
|
||||||
|
order: [["order_index", "ASC"]],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
order: [[{ model: Lesson, as: "lessons" }, "order_index", "ASC"]],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!unit) return R.error(res, "Unit not found.", 404);
|
||||||
|
|
||||||
|
return R.success(res, "Unit retrieved.", { data: unit });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[UNIT][GET ONE]", err);
|
||||||
|
return R.error(res, "Could not retrieve unit.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.createUnit = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { courseId } = req.params;
|
||||||
|
const { title, description, order_index } = req.body;
|
||||||
|
|
||||||
|
if (!title) return R.error(res, "Title is required.", 400);
|
||||||
|
|
||||||
|
const course = await Course.findOne({ where: { course_id: courseId, ...notDeleted } });
|
||||||
|
if (!course) return R.error(res, "Course not found.", 404);
|
||||||
|
|
||||||
|
const unit = await Unit.create({
|
||||||
|
course_id: courseId,
|
||||||
|
title,
|
||||||
|
description: description ?? null,
|
||||||
|
order_index: order_index ?? 0,
|
||||||
|
createdBy: req.body.createdBy ?? null,
|
||||||
|
});
|
||||||
|
|
||||||
|
return R.success(res, "Unit created.", { data: unit }, 201);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[UNIT][CREATE]", err);
|
||||||
|
return R.error(res, "Could not create unit.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.updateUnit = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { courseId, unitId } = req.params;
|
||||||
|
|
||||||
|
const unit = await Unit.findOne({
|
||||||
|
where: { unit_id: unitId, course_id: courseId, ...notDeleted },
|
||||||
|
});
|
||||||
|
if (!unit) return R.error(res, "Unit not found.", 404);
|
||||||
|
|
||||||
|
const { title, description, order_index } = req.body;
|
||||||
|
|
||||||
|
if (title !== undefined) unit.title = title;
|
||||||
|
if (description !== undefined) unit.description = description;
|
||||||
|
if (order !== undefined) unit.order = order;
|
||||||
|
unit.updatedBy = req.body.updatedBy ?? null;
|
||||||
|
|
||||||
|
await unit.save();
|
||||||
|
|
||||||
|
return R.success(res, "Unit updated.", { data: unit });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[UNIT][UPDATE]", err);
|
||||||
|
return R.error(res, "Could not update unit.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.deleteUnit = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { courseId, unitId } = req.params;
|
||||||
|
|
||||||
|
const unit = await Unit.findOne({
|
||||||
|
where: { unit_id: unitId, course_id: courseId, ...notDeleted },
|
||||||
|
});
|
||||||
|
if (!unit) return R.error(res, "Unit not found.", 404);
|
||||||
|
|
||||||
|
await unit.update({ deletedBy: req.body.deletedBy ?? null });
|
||||||
|
await unit.destroy();
|
||||||
|
|
||||||
|
return R.success(res, "Unit deleted.");
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[UNIT][DELETE]", err);
|
||||||
|
return R.error(res, "Could not delete unit.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─── LESSON ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
exports.getLessons = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { courseId, unitId } = req.params;
|
||||||
|
|
||||||
|
const unit = await Unit.findOne({
|
||||||
|
where: { unit_id: unitId, course_id: courseId, ...notDeleted },
|
||||||
|
});
|
||||||
|
if (!unit) return R.error(res, "Unit not found.", 404);
|
||||||
|
|
||||||
|
const result = await paginate(Lesson, req, {
|
||||||
|
excludeAttributes: adminExclude,
|
||||||
|
auditOptions: { mdl_Users, parentAlias: "Lesson" },
|
||||||
|
context: "list",
|
||||||
|
findOptions: {
|
||||||
|
where: { unit_id: unitId, ...notDeleted },
|
||||||
|
order: [["order_index", "ASC"]],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return R.success(res, "Lessons retrieved.", result);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[LESSON][GET ALL]", err);
|
||||||
|
return R.error(res, "Could not retrieve lessons.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getLesson = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { courseId, unitId, lessonId } = req.params;
|
||||||
|
|
||||||
|
const lesson = await Lesson.findOne({
|
||||||
|
where: { lesson_id: lessonId, unit_id: unitId, ...notDeleted },
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: LessonPage,
|
||||||
|
as: "page",
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: Unit,
|
||||||
|
as: "unit",
|
||||||
|
where: { course_id: courseId, ...notDeleted },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!lesson) return R.error(res, "Lesson not found.", 404);
|
||||||
|
|
||||||
|
return R.success(res, "Lesson retrieved.", { data: lesson });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[LESSON][GET ONE]", err);
|
||||||
|
return R.error(res, "Could not retrieve lesson.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.createLesson = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { courseId, unitId } = req.params;
|
||||||
|
const { title, description, order_index } = req.body;
|
||||||
|
|
||||||
|
if (!title) return R.error(res, "Title is required.", 400);
|
||||||
|
|
||||||
|
const unit = await Unit.findOne({
|
||||||
|
where: { unit_id: unitId, course_id: courseId, ...notDeleted },
|
||||||
|
});
|
||||||
|
if (!unit) return R.error(res, "Unit not found.", 404);
|
||||||
|
|
||||||
|
const lesson = await Lesson.create({
|
||||||
|
unit_id: unitId,
|
||||||
|
title,
|
||||||
|
description: description ?? null,
|
||||||
|
order_index: order_index ?? 0,
|
||||||
|
createdBy: req.body.createdBy ?? null,
|
||||||
|
});
|
||||||
|
|
||||||
|
// ── Auto-create an empty page for this lesson ─────────────────────────────
|
||||||
|
await LessonPage.create({
|
||||||
|
lesson_id: lesson.lesson_id,
|
||||||
|
blocks: [],
|
||||||
|
createdBy: req.body.createdBy ?? null,
|
||||||
|
});
|
||||||
|
|
||||||
|
return R.success(res, "Lesson created.", { data: lesson }, 201);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[LESSON][CREATE]", err);
|
||||||
|
return R.error(res, "Could not create lesson.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.updateLesson = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { courseId, unitId, lessonId } = req.params;
|
||||||
|
|
||||||
|
const lesson = await Lesson.findOne({
|
||||||
|
where: {
|
||||||
|
lesson_id: lessonId,
|
||||||
|
unit_id: unitId,
|
||||||
|
...notDeleted,
|
||||||
|
},
|
||||||
|
include: [{
|
||||||
|
model: Unit,
|
||||||
|
as: "unit",
|
||||||
|
where: { course_id: courseId, ...notDeleted },
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
if (!lesson) return R.error(res, "Lesson not found.", 404);
|
||||||
|
|
||||||
|
const { title, description, order_index } = req.body;
|
||||||
|
|
||||||
|
if (title !== undefined) lesson.title = title;
|
||||||
|
if (description !== undefined) lesson.description = description;
|
||||||
|
if (order_index !== undefined) lesson.order_index = order_index;
|
||||||
|
lesson.updatedBy = req.body.updatedBy ?? null;
|
||||||
|
|
||||||
|
await lesson.save();
|
||||||
|
|
||||||
|
return R.success(res, "Lesson updated.", { data: lesson });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[LESSON][UPDATE]", err);
|
||||||
|
return R.error(res, "Could not update lesson.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.deleteLesson = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { courseId, unitId, lessonId } = req.params;
|
||||||
|
|
||||||
|
const lesson = await Lesson.findOne({
|
||||||
|
where: { lesson_id: lessonId, unit_id: unitId, ...notDeleted },
|
||||||
|
include: [{
|
||||||
|
model: Unit,
|
||||||
|
as: "unit",
|
||||||
|
where: { course_id: courseId, ...notDeleted },
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
if (!lesson) return R.error(res, "Lesson not found.", 404);
|
||||||
|
|
||||||
|
await lesson.update({ deletedBy: req.body.deletedBy ?? null });
|
||||||
|
await lesson.destroy();
|
||||||
|
|
||||||
|
return R.success(res, "Lesson deleted.");
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[LESSON][DELETE]", err);
|
||||||
|
return R.error(res, "Could not delete lesson.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─── LESSON PAGE ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
exports.getLessonPage = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { lessonId } = req.params;
|
||||||
|
|
||||||
|
const page = await LessonPage.findOne({
|
||||||
|
where: { lesson_id: lessonId },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!page) return R.error(res, "Lesson page not found.", 404);
|
||||||
|
|
||||||
|
return R.success(res, "Lesson page retrieved.", { data: page });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[LESSON PAGE][GET]", err);
|
||||||
|
return R.error(res, "Could not retrieve lesson page.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.upsertLessonPage = async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { lessonId } = req.params;
|
||||||
|
const { blocks } = req.body;
|
||||||
|
|
||||||
|
if (!Array.isArray(blocks)) return R.error(res, "blocks must be an array.", 400);
|
||||||
|
|
||||||
|
const lesson = await Lesson.findOne({ where: { lesson_id: lessonId, ...notDeleted } });
|
||||||
|
if (!lesson) return R.error(res, "Lesson not found.", 404);
|
||||||
|
|
||||||
|
const [page, created] = await LessonPage.upsert({
|
||||||
|
lesson_id: lessonId,
|
||||||
|
blocks,
|
||||||
|
updatedBy: req.body.updatedBy ?? null,
|
||||||
|
createdBy: req.body.updatedBy ?? null,
|
||||||
|
}, {
|
||||||
|
returning: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return R.success(
|
||||||
|
res,
|
||||||
|
created ? "Lesson page created." : "Lesson page updated.",
|
||||||
|
{ data: page },
|
||||||
|
created ? 201 : 200,
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[LESSON PAGE][UPSERT]", err);
|
||||||
|
return R.error(res, "Could not save lesson page.", 500);
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
const { DataTypes } = require("sequelize");
|
||||||
|
const sequelize = require("../../config/db.config");
|
||||||
|
const mdl_Users = require("../users/users.mdl");
|
||||||
|
|
||||||
|
const Course = sequelize.define("Course", {
|
||||||
|
|
||||||
|
course_id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
|
||||||
|
uuid: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, allowNull: false, unique: true },
|
||||||
|
|
||||||
|
title: { type: DataTypes.STRING(255), allowNull: false, label: "Title" },
|
||||||
|
description: { type: DataTypes.TEXT, allowNull: true, label: "Description" },
|
||||||
|
order_index: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0, label: "Order" },
|
||||||
|
|
||||||
|
createdBy: { type: DataTypes.BIGINT, allowNull: true, label: "Created By" },
|
||||||
|
updatedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Updated By" },
|
||||||
|
deletedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Deleted By" },
|
||||||
|
|
||||||
|
}, {
|
||||||
|
tableName: "courses",
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
indexes: [
|
||||||
|
{ fields: ["uuid"] },
|
||||||
|
{ fields: ["order_index"] },
|
||||||
|
{ fields: ["deletedAt"] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Course.belongsTo(mdl_Users, { as: "creator", foreignKey: "createdBy" });
|
||||||
|
Course.belongsTo(mdl_Users, { as: "updater", foreignKey: "updatedBy" });
|
||||||
|
|
||||||
|
module.exports = Course;
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
const { DataTypes } = require("sequelize");
|
||||||
|
const sequelize = require("../../config/db.config");
|
||||||
|
const mdl_Users = require("../users/users.mdl");
|
||||||
|
const Lesson = require("./lessons.mdl");
|
||||||
|
|
||||||
|
const LessonPage = sequelize.define("LessonPage", {
|
||||||
|
page_id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
|
||||||
|
lesson_id: { type: DataTypes.BIGINT, allowNull: false, unique: true },
|
||||||
|
blocks: { type: DataTypes.JSONB, allowNull: false, defaultValue: [] },
|
||||||
|
createdBy: { type: DataTypes.BIGINT, allowNull: true, label: "Created By" },
|
||||||
|
updatedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Updated By" },
|
||||||
|
}, {
|
||||||
|
tableName: "lesson_pages",
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
LessonPage.belongsTo(Lesson, { as: "lesson", foreignKey: "lesson_id" });
|
||||||
|
LessonPage.belongsTo(mdl_Users, { as: "creator", foreignKey: "createdBy" });
|
||||||
|
LessonPage.belongsTo(mdl_Users, { as: "updater", foreignKey: "updatedBy" });
|
||||||
|
Lesson.hasOne(LessonPage, { as: "page", foreignKey: "lesson_id" });
|
||||||
|
|
||||||
|
module.exports = LessonPage;
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
const { DataTypes } = require("sequelize");
|
||||||
|
const sequelize = require("../../config/db.config");
|
||||||
|
const mdl_Users = require("../users/users.mdl");
|
||||||
|
const Unit = require("./units.mdl");
|
||||||
|
|
||||||
|
const Lesson = sequelize.define("Lesson", {
|
||||||
|
lesson_id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
|
||||||
|
uuid: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, allowNull: false, unique: true },
|
||||||
|
unit_id: { type: DataTypes.BIGINT, allowNull: false },
|
||||||
|
title: { type: DataTypes.STRING(255), allowNull: false, label: "Title" },
|
||||||
|
description: { type: DataTypes.TEXT, allowNull: true, label: "Description" },
|
||||||
|
order_index: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0, label: "Order" },
|
||||||
|
createdBy: { type: DataTypes.BIGINT, allowNull: true, label: "Created By" },
|
||||||
|
updatedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Updated By" },
|
||||||
|
deletedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Deleted By" },
|
||||||
|
}, {
|
||||||
|
tableName: "lessons",
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
indexes: [
|
||||||
|
{ fields: ["uuid"] },
|
||||||
|
{ fields: ["unit_id"] },
|
||||||
|
{ fields: ["order_index"] },
|
||||||
|
{ fields: ["deletedAt"] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Lesson.belongsTo(Unit, { as: "unit", foreignKey: "unit_id" });
|
||||||
|
Lesson.belongsTo(mdl_Users, { as: "creator", foreignKey: "createdBy" });
|
||||||
|
Lesson.belongsTo(mdl_Users, { as: "updater", foreignKey: "updatedBy" });
|
||||||
|
Unit.hasMany(Lesson, { as: "lessons", foreignKey: "unit_id" });
|
||||||
|
|
||||||
|
module.exports = Lesson;
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
const { DataTypes } = require("sequelize");
|
||||||
|
const sequelize = require("../../config/db.config");
|
||||||
|
const mdl_Users = require("../users/users.mdl");
|
||||||
|
const Course = require("./courses.mdl");
|
||||||
|
|
||||||
|
const Unit = sequelize.define("Unit", {
|
||||||
|
unit_id: { type: DataTypes.BIGINT, primaryKey: true, autoIncrement: true },
|
||||||
|
uuid: { type: DataTypes.UUID, defaultValue: DataTypes.UUIDV4, allowNull: false, unique: true },
|
||||||
|
course_id: { type: DataTypes.BIGINT, allowNull: false },
|
||||||
|
title: { type: DataTypes.STRING(255), allowNull: false, label: "Title" },
|
||||||
|
description: { type: DataTypes.TEXT, allowNull: true, label: "Description" },
|
||||||
|
order_index: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0, label: "Order" },
|
||||||
|
createdBy: { type: DataTypes.BIGINT, allowNull: true, label: "Created By" },
|
||||||
|
updatedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Updated By" },
|
||||||
|
deletedBy: { type: DataTypes.BIGINT, allowNull: true, label: "Deleted By" },
|
||||||
|
}, {
|
||||||
|
tableName: "units",
|
||||||
|
timestamps: true,
|
||||||
|
paranoid: true,
|
||||||
|
indexes: [
|
||||||
|
{ fields: ["uuid"] },
|
||||||
|
{ fields: ["course_id"] },
|
||||||
|
{ fields: ["order_index"] },
|
||||||
|
{ fields: ["deletedAt"] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
Unit.belongsTo(Course, { as: "course", foreignKey: "course_id" });
|
||||||
|
Unit.belongsTo(mdl_Users, { as: "creator", foreignKey: "createdBy" });
|
||||||
|
Unit.belongsTo(mdl_Users, { as: "updater", foreignKey: "updatedBy" });
|
||||||
|
Course.hasMany(Unit, { as: "units", foreignKey: "course_id" });
|
||||||
|
|
||||||
|
module.exports = Unit;
|
||||||
Generated
-4
@@ -53,7 +53,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz",
|
||||||
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
|
"integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cluster-key-slot": "1.1.2",
|
"cluster-key-slot": "1.1.2",
|
||||||
"generic-pool": "3.9.0",
|
"generic-pool": "3.9.0",
|
||||||
@@ -766,7 +765,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
|
||||||
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
|
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"accepts": "~1.3.8",
|
"accepts": "~1.3.8",
|
||||||
"array-flatten": "1.1.1",
|
"array-flatten": "1.1.1",
|
||||||
@@ -813,7 +811,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.11.2.tgz",
|
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.11.2.tgz",
|
||||||
"integrity": "sha512-a7uwwfNTh1U60ssiIkuLFWHt4hAC5yxlLGU2VP0X4YNlyEDZAqF4tK3GD3NSitVBrCQmQ0++0uOyFOgC2y4DDw==",
|
"integrity": "sha512-a7uwwfNTh1U60ssiIkuLFWHt4hAC5yxlLGU2VP0X4YNlyEDZAqF4tK3GD3NSitVBrCQmQ0++0uOyFOgC2y4DDw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
},
|
},
|
||||||
@@ -1830,7 +1827,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz",
|
||||||
"integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==",
|
"integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pg-connection-string": "^2.12.0",
|
"pg-connection-string": "^2.12.0",
|
||||||
"pg-pool": "^3.13.0",
|
"pg-pool": "^3.13.0",
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
const router = require("express").Router();
|
||||||
|
const controller = require("../../controllers/admin/courses.controller");
|
||||||
|
const { sensitiveOpsLimiter } = require("../../middleware/rateLimiter.middleware");
|
||||||
|
|
||||||
|
// ─── Courses ──────────────────────────────────────────────────────────────────
|
||||||
|
router.get("/", controller.getCourses);
|
||||||
|
router.post("/", sensitiveOpsLimiter, controller.createCourse);
|
||||||
|
router.get("/:courseId", controller.getCourse);
|
||||||
|
router.patch("/:courseId", sensitiveOpsLimiter, controller.updateCourse);
|
||||||
|
router.delete("/:courseId", sensitiveOpsLimiter, controller.deleteCourse);
|
||||||
|
|
||||||
|
// ─── Units ────────────────────────────────────────────────────────────────────
|
||||||
|
router.get("/:courseId/units", controller.getUnits);
|
||||||
|
router.post("/:courseId/units", sensitiveOpsLimiter, controller.createUnit);
|
||||||
|
router.get("/:courseId/units/:unitId", controller.getUnit);
|
||||||
|
router.patch("/:courseId/units/:unitId", sensitiveOpsLimiter, controller.updateUnit);
|
||||||
|
router.delete("/:courseId/units/:unitId", sensitiveOpsLimiter, controller.deleteUnit);
|
||||||
|
|
||||||
|
// ─── Lessons ──────────────────────────────────────────────────────────────────
|
||||||
|
router.get("/:courseId/units/:unitId/lessons", controller.getLessons);
|
||||||
|
router.post("/:courseId/units/:unitId/lessons", sensitiveOpsLimiter, controller.createLesson);
|
||||||
|
router.get("/:courseId/units/:unitId/lessons/:lessonId", controller.getLesson);
|
||||||
|
router.patch("/:courseId/units/:unitId/lessons/:lessonId", sensitiveOpsLimiter, controller.updateLesson);
|
||||||
|
router.delete("/:courseId/units/:unitId/lessons/:lessonId", sensitiveOpsLimiter, controller.deleteLesson);
|
||||||
|
|
||||||
|
// ─── Lesson Page ──────────────────────────────────────────────────────────────
|
||||||
|
router.get("/:courseId/units/:unitId/lessons/:lessonId/page", controller.getLessonPage);
|
||||||
|
router.put("/:courseId/units/:unitId/lessons/:lessonId/page", sensitiveOpsLimiter, controller.upsertLessonPage);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -33,6 +33,7 @@ const authRoutes = require('./routes/auth.routes');
|
|||||||
const clientRoutes = require('./routes/client/client.routes');
|
const clientRoutes = require('./routes/client/client.routes');
|
||||||
const staffRoutes = require('./routes/staff/staff.routes');
|
const staffRoutes = require('./routes/staff/staff.routes');
|
||||||
const adminRoutes = require('./routes/admin/admin.routes');
|
const adminRoutes = require('./routes/admin/admin.routes');
|
||||||
|
const courseRoutes = require('./routes/admin/courses.routes');
|
||||||
|
|
||||||
// ── Models (ensure associations are loaded) ────────────────────────────────────
|
// ── Models (ensure associations are loaded) ────────────────────────────────────
|
||||||
require('./models/users/users.mdl');
|
require('./models/users/users.mdl');
|
||||||
@@ -96,6 +97,7 @@ app.use('/api/auth', authRoutes);
|
|||||||
app.use('/api/client', clientRoutes);
|
app.use('/api/client', clientRoutes);
|
||||||
app.use('/api/staff', staffRoutes);
|
app.use('/api/staff', staffRoutes);
|
||||||
app.use('/api/admin', adminRoutes);
|
app.use('/api/admin', adminRoutes);
|
||||||
|
app.use('/api/admin/courses', courseRoutes);
|
||||||
|
|
||||||
// Health check
|
// Health check
|
||||||
app.get('/api/health', (req, res) => {
|
app.get('/api/health', (req, res) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user