const router = require("express").Router(); const ctrl = require("../../controllers/admin/courses.controller"); const progressCtrl = require("../../controllers/admin/course_reading_progress.controller"); const requirementsCtrl = require("../../controllers/admin/completion_requirements.controller"); const { authenticate } = require("../../middleware/auth.middleware"); const { requireAdmin } = require("../../middleware/rbac.middleware"); const { adminLimiter } = require("../../middleware/rateLimiter.middleware"); router.use(authenticate, requireAdmin(), adminLimiter); // ══════════════════════════════════════════════════════════════════════════════ // COURSES // ══════════════════════════════════════════════════════════════════════════════ router.get("/", ctrl.getCourses); router.post("/", ctrl.createCourse); router.post("/full", ctrl.createCourseFull); // one-shot Add Course wizard submit (course + roadmap + rewards) // ── static segments first ──────────────────────────────────────────────────── router.get("/field-values", ctrl.getCourseFieldValues); router.delete("/bulk", ctrl.bulkArchiveCourses); router.delete("/bulk/permanent", ctrl.bulkPermanentlyDeleteCourses); router.get("/archives", ctrl.getArchivedCourses); router.patch("/restore/bulk", ctrl.bulkRestoreCourses); router.get("/flat", ctrl.getCoursesFlat); router.get("/by-subscription", ctrl.getCoursesBySubscription); router.get("/units-flat", ctrl.getUnitsFlat); router.get("/lessons-flat", ctrl.getLessonsFlat); router.get("/quizzes-flat", ctrl.getQuizzesFlat); router.put("/order", ctrl.reorderCourses); // persist top-level course catalog ordering { course_ids } // ── then :courseId ──────────────────────────────────────────────────────────── router.get("/archives/:courseId", ctrl.getArchivedCourse); router.patch("/:courseId/restore", ctrl.restoreCourse); router.get("/:courseId/archive-impact", ctrl.getCourseArchiveImpact); router.get("/:courseId/permanent-delete-impact", ctrl.getCoursePermanentDeleteImpact); router.get("/:courseId", ctrl.getCourse); router.put("/:courseId", ctrl.updateCourse); router.delete("/:courseId", ctrl.archiveCourse); router.delete("/:courseId/permanent", ctrl.permanentlyDeleteCourse); // ══════════════════════════════════════════════════════════════════════════════ // COURSE INSTRUCTORS // ══════════════════════════════════════════════════════════════════════════════ router.get("/:courseId/instructors", ctrl.getInstructors); router.put("/:courseId/instructors", ctrl.syncInstructors); // ══════════════════════════════════════════════════════════════════════════════ // COURSE ACHIEVEMENTS // ══════════════════════════════════════════════════════════════════════════════ router.get("/:courseId/achievements", ctrl.getCourseAchievements); router.put("/:courseId/achievements", ctrl.syncCourseAchievements); // ══════════════════════════════════════════════════════════════════════════════ // PREREQUISITES // ══════════════════════════════════════════════════════════════════════════════ router.get("/:courseId/prerequisites", ctrl.getPrerequisites); router.put("/:courseId/prerequisites", ctrl.syncPrerequisites); // ══════════════════════════════════════════════════════════════════════════════ // COMPLETION REQUIREMENTS // ══════════════════════════════════════════════════════════════════════════════ router.get("/:courseId/requirements", requirementsCtrl.getCourseRequirements); router.put("/:courseId/requirements", requirementsCtrl.syncCourseRequirements); // ══════════════════════════════════════════════════════════════════════════════ // COURSE ASSESSMENT // ══════════════════════════════════════════════════════════════════════════════ router.get("/:courseId/assessment", ctrl.getAssessment); router.post("/:courseId/assessment", ctrl.createAssessment); // ── static segments first ──────────────────────────────────────────────────── router.get("/:courseId/assessment/archives", ctrl.getArchivedAssessment); // ── then :assessmentId ──────────────────────────────────────────────────────── router.patch("/:courseId/assessment/:assessmentId", ctrl.updateAssessment); router.delete("/:courseId/assessment/:assessmentId", ctrl.deleteAssessment); router.patch("/:courseId/assessment/:assessmentId/restore", ctrl.restoreAssessment); // ── Assessment Questions ────────────────────────────────────────────────────── router.get("/:courseId/assessment/:assessmentId/questions", ctrl.getQuestions); router.post("/:courseId/assessment/:assessmentId/questions", ctrl.createQuestion); // static before :questionId router.delete("/:courseId/assessment/:assessmentId/questions/bulk", ctrl.bulkArchiveQuestions); router.patch("/:courseId/assessment/:assessmentId/questions/restore/bulk", ctrl.bulkRestoreQuestions); router.put("/:courseId/assessment/:assessmentId/questions/bulk-sync", ctrl.bulkSyncQuestions); router.get("/:courseId/assessment/:assessmentId/questions/archives/:questionId", ctrl.getArchivedQuestion); router.patch("/:courseId/assessment/:assessmentId/questions/:questionId", ctrl.updateQuestion); router.delete("/:courseId/assessment/:assessmentId/questions/:questionId", ctrl.deleteQuestion); router.patch("/:courseId/assessment/:assessmentId/questions/:questionId/restore", ctrl.restoreQuestion); // ── Assessment Completions & Sessions ───────────────────────────────────────── router.get("/:courseId/assessment/:assessmentId/completions", ctrl.getAssessmentCompletions); router.get("/:courseId/assessment/:assessmentId/sessions", ctrl.getAssessmentSessions); // ══════════════════════════════════════════════════════════════════════════════ // UNITS // ══════════════════════════════════════════════════════════════════════════════ router.get("/:courseId/field-values", ctrl.getUnitFieldValues); router.get("/:courseId/units", ctrl.getUnits); router.post("/:courseId/units", ctrl.createUnit); // create new unit + attach, or attach existing via body { unit_id } // static before :unitId router.post("/:courseId/units/attach", ctrl.attachUnits); // bulk-attach existing library units { unit_ids } router.put("/:courseId/units/order", ctrl.reorderUnits); // persist course-level unit ordering { unit_ids } router.delete("/:courseId/units/bulk", ctrl.bulkArchiveUnits); router.delete("/:courseId/units/bulk/permanent", ctrl.bulkPermanentlyDeleteUnits); router.get("/:courseId/units/archives", ctrl.getArchivedUnits); router.patch("/:courseId/units/restore/bulk", ctrl.bulkRestoreUnits); router.get("/:courseId/units/archives/:unitId", ctrl.getArchivedUnit); router.patch("/:courseId/units/:unitId/restore", ctrl.restoreUnit); router.get("/:courseId/units/:unitId/archive-impact", ctrl.getUnitArchiveImpact); router.get("/:courseId/units/:unitId/permanent-delete-impact", ctrl.getUnitPermanentDeleteImpact); router.get("/:courseId/units/:unitId", ctrl.getUnit); router.put("/:courseId/units/:unitId", ctrl.updateUnit); router.delete("/:courseId/units/:unitId", ctrl.archiveUnit); router.delete("/:courseId/units/:unitId/permanent", ctrl.permanentlyDeleteUnit); router.get("/:courseId/units/:unitId/requirements", requirementsCtrl.getUnitRequirements); router.put("/:courseId/units/:unitId/requirements", requirementsCtrl.syncUnitRequirements); // ══════════════════════════════════════════════════════════════════════════════ // UNIT QUIZ // ══════════════════════════════════════════════════════════════════════════════ router.get("/:courseId/units/:unitId/quiz", ctrl.getQuiz); router.post("/:courseId/units/:unitId/quiz", ctrl.createQuiz); // static before :quizId router.get("/:courseId/units/:unitId/quiz/archives", ctrl.getArchivedQuiz); router.patch("/:courseId/units/:unitId/quiz/:quizId", ctrl.updateQuiz); router.delete("/:courseId/units/:unitId/quiz/:quizId", ctrl.deleteQuiz); router.patch("/:courseId/units/:unitId/quiz/:quizId/restore", ctrl.restoreQuiz); // ── Quiz Questions ──────────────────────────────────────────────────────────── router.get("/:courseId/units/:unitId/quiz/:quizId/questions", ctrl.getQuestions); router.post("/:courseId/units/:unitId/quiz/:quizId/questions", ctrl.createQuestion); // static before :questionId router.delete("/:courseId/units/:unitId/quiz/:quizId/questions/bulk", ctrl.bulkArchiveQuestions); router.patch("/:courseId/units/:unitId/quiz/:quizId/questions/restore/bulk", ctrl.bulkRestoreQuestions); router.put("/:courseId/units/:unitId/quiz/:quizId/questions/bulk-sync", ctrl.bulkSyncQuestions); router.get("/:courseId/units/:unitId/quiz/:quizId/questions/archives/:questionId", ctrl.getArchivedQuestion); router.patch("/:courseId/units/:unitId/quiz/:quizId/questions/:questionId", ctrl.updateQuestion); router.delete("/:courseId/units/:unitId/quiz/:quizId/questions/:questionId", ctrl.deleteQuestion); router.patch("/:courseId/units/:unitId/quiz/:quizId/questions/:questionId/restore", ctrl.restoreQuestion); // ── Quiz Completions ────────────────────────────────────────────────────────── router.get("/:courseId/units/:unitId/quiz/:quizId/completions", ctrl.getQuizCompletions); // ══════════════════════════════════════════════════════════════════════════════ // LESSONS // ══════════════════════════════════════════════════════════════════════════════ router.get("/:courseId/units/:unitId/field-values", ctrl.getLessonFieldValues); router.get("/:courseId/units/:unitId/lessons", ctrl.getLessons); router.post("/:courseId/units/:unitId/lessons", ctrl.createLesson); // create new lesson + attach, or attach existing via body { lesson_id } // static before :lessonId router.put("/:courseId/units/:unitId/lessons/order", ctrl.reorderLessons); // persist unit-level lesson ordering { lesson_ids } router.delete("/:courseId/units/:unitId/lessons/bulk", ctrl.bulkArchiveLessons); router.delete("/:courseId/units/:unitId/lessons/bulk/permanent", ctrl.bulkPermanentlyDeleteLessons); router.get("/:courseId/units/:unitId/lessons/archives", ctrl.getArchivedLessons); router.patch("/:courseId/units/:unitId/lessons/restore/bulk", ctrl.bulkRestoreLessons); router.get("/:courseId/units/:unitId/lessons/archives/:lessonId", ctrl.getArchivedLesson); router.patch("/:courseId/units/:unitId/lessons/:lessonId/restore", ctrl.restoreLesson); router.get("/:courseId/units/:unitId/lessons/:lessonId", ctrl.getLesson); router.put("/:courseId/units/:unitId/lessons/:lessonId", ctrl.updateLesson); router.delete("/:courseId/units/:unitId/lessons/:lessonId", ctrl.archiveLesson); router.delete("/:courseId/units/:unitId/lessons/:lessonId/permanent", ctrl.permanentlyDeleteLesson); // ══════════════════════════════════════════════════════════════════════════════ // LESSON PAGE // ══════════════════════════════════════════════════════════════════════════════ router.get("/:courseId/units/:unitId/lessons/:lessonId/page", ctrl.getLessonPage); router.put("/:courseId/units/:unitId/lessons/:lessonId/page", ctrl.upsertLessonPage); // ══════════════════════════════════════════════════════════════════════════════ // LESSON COMPLETION REQUIREMENTS // ══════════════════════════════════════════════════════════════════════════════ router.get("/:courseId/units/:unitId/lessons/:lessonId/requirements", requirementsCtrl.getLessonRequirements); router.put("/:courseId/units/:unitId/lessons/:lessonId/requirements", requirementsCtrl.syncLessonRequirements); // ══════════════════════════════════════════════════════════════════════════════ // COURSE READING PROGRESS // ══════════════════════════════════════════════════════════════════════════════ // static segment before :userId to avoid param collision router.get("/:courseId/reading-progress/users/:userId", progressCtrl.getUserReadingProgress); router.get("/:courseId/reading-progress", progressCtrl.getCourseReadingProgress); module.exports = router;