From 6907e9bb2d2d2a3c4786b1ad5647250d6b06c72b Mon Sep 17 00:00:00 2001 From: Kenneth Obsequio Date: Sat, 16 May 2026 11:04:36 +0800 Subject: [PATCH] added: course func() Signed-off-by: Kenneth Obsequio --- controllers/admin/courses.controller.js | 441 ++++++++++++++++++++++++ models/courses/courses.mdl.js | 32 ++ models/courses/lesson-page.mdl.js | 23 ++ models/courses/lessons.mdl.js | 33 ++ models/courses/units.mdl.js | 33 ++ package-lock.json | 4 - routes/admin/courses.routes.js | 30 ++ server.js | 6 +- 8 files changed, 596 insertions(+), 6 deletions(-) create mode 100644 controllers/admin/courses.controller.js create mode 100644 models/courses/courses.mdl.js create mode 100644 models/courses/lesson-page.mdl.js create mode 100644 models/courses/lessons.mdl.js create mode 100644 models/courses/units.mdl.js create mode 100644 routes/admin/courses.routes.js diff --git a/controllers/admin/courses.controller.js b/controllers/admin/courses.controller.js new file mode 100644 index 0000000..4fd1178 --- /dev/null +++ b/controllers/admin/courses.controller.js @@ -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); + } +}; diff --git a/models/courses/courses.mdl.js b/models/courses/courses.mdl.js new file mode 100644 index 0000000..08af8b4 --- /dev/null +++ b/models/courses/courses.mdl.js @@ -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; diff --git a/models/courses/lesson-page.mdl.js b/models/courses/lesson-page.mdl.js new file mode 100644 index 0000000..4bac65d --- /dev/null +++ b/models/courses/lesson-page.mdl.js @@ -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; \ No newline at end of file diff --git a/models/courses/lessons.mdl.js b/models/courses/lessons.mdl.js new file mode 100644 index 0000000..5aac3df --- /dev/null +++ b/models/courses/lessons.mdl.js @@ -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; \ No newline at end of file diff --git a/models/courses/units.mdl.js b/models/courses/units.mdl.js new file mode 100644 index 0000000..f3b6ed3 --- /dev/null +++ b/models/courses/units.mdl.js @@ -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; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 5265f7b..d5494e0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,7 +53,6 @@ "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", "license": "MIT", - "peer": true, "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -766,7 +765,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz", "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -813,7 +811,6 @@ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.11.2.tgz", "integrity": "sha512-a7uwwfNTh1U60ssiIkuLFWHt4hAC5yxlLGU2VP0X4YNlyEDZAqF4tK3GD3NSitVBrCQmQ0++0uOyFOgC2y4DDw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 14" }, @@ -1830,7 +1827,6 @@ "resolved": "https://registry.npmjs.org/pg/-/pg-8.20.0.tgz", "integrity": "sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==", "license": "MIT", - "peer": true, "dependencies": { "pg-connection-string": "^2.12.0", "pg-pool": "^3.13.0", diff --git a/routes/admin/courses.routes.js b/routes/admin/courses.routes.js new file mode 100644 index 0000000..2c7b579 --- /dev/null +++ b/routes/admin/courses.routes.js @@ -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; diff --git a/server.js b/server.js index c5a34be..ed9d330 100644 --- a/server.js +++ b/server.js @@ -33,6 +33,7 @@ const authRoutes = require('./routes/auth.routes'); const clientRoutes = require('./routes/client/client.routes'); const staffRoutes = require('./routes/staff/staff.routes'); const adminRoutes = require('./routes/admin/admin.routes'); +const courseRoutes = require('./routes/admin/courses.routes'); // ── Models (ensure associations are loaded) ──────────────────────────────────── require('./models/users/users.mdl'); @@ -64,7 +65,7 @@ app.use(cors({ 'Origin', 'X-Requested-With', 'csrf-token', - 'x-csrf-token', + 'x-csrf-token', 'CSRF-Token', 'X-CSRF-Token', ], @@ -96,6 +97,7 @@ app.use('/api/auth', authRoutes); app.use('/api/client', clientRoutes); app.use('/api/staff', staffRoutes); app.use('/api/admin', adminRoutes); +app.use('/api/admin/courses', courseRoutes); // Health check app.get('/api/health', (req, res) => { @@ -132,4 +134,4 @@ app.use((err, req, res, next) => { console.error('❌ Failed to start server:', err); process.exit(1); } -})(); \ No newline at end of file +})();