ready to test

Testing

Signed-off-by: Kenneth Obsequio <k80308392@gmail.com>
This commit is contained in:
2026-06-22 10:06:58 +08:00
parent bf48c95467
commit 439bb33f77
189 changed files with 17559 additions and 686 deletions
+25 -7
View File
@@ -23,22 +23,40 @@ const { authenticate } = require('../../middleware/auth.middleware');
const { requireAdmin } = require('../../middleware/rbac.middleware');
const { adminLimiter } = require('../../middleware/rateLimiter.middleware');
const dashboardRoutes = require('./dashboard.routes');
const usersRoutes = require('./users.routes');
const groupsRoutes = require('./groups.routes');
const assetsRoutes = require('./assets.routes');
const coursesRoutes = require('./courses.routes')
const taskRoutes = require('./task.routes');
const dashboardRoutes = require('./dashboard.routes');
const profileRoutes = require('./profile.routes');
const usersRoutes = require('./users.routes');
const groupsRoutes = require('./groups.routes');
const assetsRoutes = require('./assets.routes');
const coursesRoutes = require('./courses.routes');
const taskRoutes = require('./task.routes');
const tiersRoutes = require('./tiers.routes');
const categoriesRoutes = require('./categories.routes');
const productsRoutes = require('./products.routes');
const advertisementRoutes = require('./advertisements.routes');
const notificationRoutes = require('./notifications.routes');
const mediaRoutes = require('./media.routes');
const activityCtrl = require('../../controllers/admin/user_activity.controller');
// ── Guards — applied to ALL admin routes ──────────────────────────────────────
router.use(authenticate, requireAdmin(), adminLimiter);
// ── Mount ─────────────────────────────────────────────────────────────────────
router.use('/dashboard', dashboardRoutes);
router.use('/users', usersRoutes);
router.use('/profile', profileRoutes);
router.use('/users', usersRoutes);
router.use('/groups', groupsRoutes);
router.use('/assets', assetsRoutes);
router.use('/courses', coursesRoutes);
router.use('/task-lists', taskRoutes);
router.use('/tiers', tiersRoutes);
router.use('/categories', categoriesRoutes);
router.use('/products', productsRoutes);
router.use('/advertisements', advertisementRoutes);
router.use('/notifications', notificationRoutes);
router.use('/media', mediaRoutes);
// ── Activity feed — global ────────────────────────────────────────────────────
router.get('/activity', activityCtrl.getActivity);
module.exports = router;
+22
View File
@@ -0,0 +1,22 @@
// routes/admin/advertisements.routes.js
const router = require('express').Router();
const controller = require('../../controllers/admin/advertisements.controller');
const { sensitiveOpsLimiter } = require('../../middleware/rateLimiter.middleware');
// ─── Static routes first ──────────────────────────────────────────────────────
router.get('/archived', controller.getArchivedAdvertisements);
router.get('/field-values', controller.getAdvertisementFieldValues);
router.delete('/bulk', sensitiveOpsLimiter, controller.archiveAdvertisements);
router.patch('/bulk-restore', controller.restoreAdvertisements);
// ─── Collection ───────────────────────────────────────────────────────────────
router.get('/', controller.getAdvertisements);
router.post('/', controller.createAdvertisement);
// ─── Dynamic routes last ──────────────────────────────────────────────────────
router.get('/:advertisementId', controller.getAdvertisement);
router.patch('/:advertisementId', sensitiveOpsLimiter, controller.updateAdvertisement);
router.patch('/:advertisementId/restore', sensitiveOpsLimiter, controller.restoreAdvertisement);
router.delete('/:advertisementId', sensitiveOpsLimiter, controller.archiveAdvertisement);
module.exports = router;
+9 -9
View File
@@ -1,25 +1,25 @@
const express = require('express');
// routes/admin/assets.routes.js
const router = require('express').Router();
const multer = require('multer');
const upload = multer({ storage: multer.memoryStorage() });
const controller = require('../../controllers/admin/assets.controller');
const { sensitiveOpsLimiter } = require('../../middleware/rateLimiter.middleware');
// ─── Static routes first ──────────────────────────────────────────────────────
router.get('/archived', controller.getArchivedAssets);
router.get('/field-values', controller.getAssetFieldValues); // ← must be before /:assetId
router.delete('/bulk', sensitiveOpsLimiter, controller.archiveAssets);
router.get('/archived', controller.getArchivedAssets);
router.get('/field-values', controller.getAssetFieldValues);
router.delete('/bulk', sensitiveOpsLimiter, controller.archiveAssets);
router.patch('/bulk-restore', controller.restoreAssets);
// ─── Collection ───────────────────────────────────────────────────────────────
router.get('/', controller.getAssets);
router.get('/', controller.getAssets);
router.post('/', upload.fields([{ name: 'file', maxCount: 1 }, { name: 'thumbnail', maxCount: 1 }]), controller.uploadAsset);
// ─── Dynamic routes last ──────────────────────────────────────────────────────
router.get('/:assetId', controller.getAsset);
router.patch('/:assetId', sensitiveOpsLimiter, upload.fields([{ name: 'file', maxCount: 1 }]), controller.updateAsset);
router.get('/:assetId', controller.getAsset);
router.patch('/:assetId', sensitiveOpsLimiter, upload.fields([{ name: 'file', maxCount: 1 }]), controller.updateAsset);
router.patch('/:assetId/restore', sensitiveOpsLimiter, controller.restoreAsset);
router.delete('/:assetId', sensitiveOpsLimiter, controller.archiveAsset);
router.delete('/:assetId', sensitiveOpsLimiter, controller.archiveAsset);
module.exports = router;
+12
View File
@@ -0,0 +1,12 @@
const express = require('express');
const router = express.Router();
const ctrl = require('../../controllers/admin/categories.controller');
router.get ('/', ctrl.getCategories);
router.post ('/', ctrl.createCategory);
router.get ('/:id', ctrl.getCategory);
router.put ('/:id', ctrl.updateCategory);
router.delete('/:id', ctrl.archiveCategory);
router.post ('/:id/restore', ctrl.restoreCategory);
module.exports = router;
+24 -5
View File
@@ -1,5 +1,6 @@
const router = require("express").Router();
const ctrl = require("../../controllers/admin/courses.controller");
const ctrl = require("../../controllers/admin/courses.controller");
const progressCtrl = require("../../controllers/admin/course_reading_progress.controller");
const { authenticate } = require("../../middleware/auth.middleware");
const { requireAdmin } = require("../../middleware/rbac.middleware");
@@ -19,6 +20,9 @@ router.get("/field-values", ctrl.getCourseFieldValues);
router.delete("/bulk", ctrl.bulkArchiveCourses);
router.get("/archives", ctrl.getArchivedCourses);
router.patch("/restore/bulk", ctrl.bulkRestoreCourses);
router.get("/flat", ctrl.getCoursesFlat);
router.get("/units-flat", ctrl.getUnitsFlat);
router.get("/lessons-flat", ctrl.getLessonsFlat);
// ── then :courseId ────────────────────────────────────────────────────────────
router.get("/archives/:courseId", ctrl.getArchivedCourse);
@@ -27,6 +31,13 @@ router.get("/:courseId", ctrl.getCourse);
router.put("/:courseId", ctrl.updateCourse);
router.delete("/:courseId", ctrl.archiveCourse);
// ══════════════════════════════════════════════════════════════════════════════
// COURSE INSTRUCTORS
// ══════════════════════════════════════════════════════════════════════════════
router.get("/:courseId/instructors", ctrl.getInstructors);
router.put("/:courseId/instructors", ctrl.syncInstructors);
// ══════════════════════════════════════════════════════════════════════════════
// PREREQUISITES
// ══════════════════════════════════════════════════════════════════════════════
@@ -45,7 +56,7 @@ router.post("/:courseId/assessment", ctrl.createAssessment);
router.get("/:courseId/assessment/archives", ctrl.getArchivedAssessment);
// ── then :assessmentId ────────────────────────────────────────────────────────
router.put("/:courseId/assessment/:assessmentId", ctrl.updateAssessment);
router.patch("/:courseId/assessment/:assessmentId", ctrl.updateAssessment);
router.delete("/:courseId/assessment/:assessmentId", ctrl.deleteAssessment);
router.patch("/:courseId/assessment/:assessmentId/restore", ctrl.restoreAssessment);
@@ -58,7 +69,7 @@ router.delete("/:courseId/assessment/:assessmentId/questions/bulk", ctrl.bulkArc
router.patch("/:courseId/assessment/:assessmentId/questions/restore/bulk", ctrl.bulkRestoreQuestions);
router.get("/:courseId/assessment/:assessmentId/questions/archives/:questionId", ctrl.getArchivedQuestion);
router.put("/:courseId/assessment/:assessmentId/questions/:questionId", ctrl.updateQuestion);
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);
@@ -91,7 +102,7 @@ router.post("/:courseId/units/:unitId/quiz", ctrl.createQuiz);
// static before :quizId
router.get("/:courseId/units/:unitId/quiz/archives", ctrl.getArchivedQuiz);
router.put("/:courseId/units/:unitId/quiz/:quizId", ctrl.updateQuiz);
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);
@@ -104,7 +115,7 @@ router.delete("/:courseId/units/:unitId/quiz/:quizId/questions/bulk", ctrl.bulkA
router.patch("/:courseId/units/:unitId/quiz/:quizId/questions/restore/bulk", ctrl.bulkRestoreQuestions);
router.get("/:courseId/units/:unitId/quiz/:quizId/questions/archives/:questionId", ctrl.getArchivedQuestion);
router.put("/:courseId/units/:unitId/quiz/:quizId/questions/:questionId", ctrl.updateQuestion);
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);
@@ -134,4 +145,12 @@ router.delete("/:courseId/units/:unitId/lessons/:lessonId", ctrl.archiveLesson);
router.get("/:courseId/units/:unitId/lessons/:lessonId/page", ctrl.getLessonPage);
router.put("/:courseId/units/:unitId/lessons/:lessonId/page", ctrl.upsertLessonPage);
// ══════════════════════════════════════════════════════════════════════════════
// 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;
+15
View File
@@ -0,0 +1,15 @@
/***********************************************************************************************************************************************************************
* File Name: media.routes.js (admin)
* Type of Program: Routes
* Author: Kenneth Obsequio (@lash0000)
* Date Created: Jun. 22, 2026
***********************************************************************************************************************************************************************/
"use strict";
const router = require("express").Router();
const ctrl = require("../../controllers/admin/media.controller");
// Auth + requireAdmin are already applied by admin.routes.js before reaching here.
router.post("/token", ctrl.issueToken);
module.exports = router;
+25
View File
@@ -0,0 +1,25 @@
/***********************************************************************************************************************************************************************
* File Name : notifications.routes.js
* Type : Router (Admin)
* Description : Admin notification endpoints.
*
* GET /api/admin/notifications — paginated list
* GET /api/admin/notifications/unseen — unseen count
* PATCH /api/admin/notifications/seen-all — mark all seen
* PATCH /api/admin/notifications/:id/seen — mark one seen
*
* Guards: inherited from admin.routes.js (authenticate → requireAdmin → adminLimiter)
*
* Author: Kenneth Obsequio (@lash0000)
* Date Created: Jun. 19, 2026
***********************************************************************************************************************************************************************/
const express = require('express');
const router = express.Router();
const { list, unseenCount, markSeen, markAllSeen } = require('../../controllers/admin/notification.controller');
router.get('/', list);
router.get('/unseen', unseenCount);
router.patch('/seen-all', markAllSeen);
router.patch('/:id/seen', markSeen);
module.exports = router;
+14
View File
@@ -0,0 +1,14 @@
const express = require('express');
const router = express.Router();
const ctrl = require('../../controllers/admin/products.controller');
// Product per course
router.get ('/courses/:courseId/product', ctrl.getCourseProduct);
router.put ('/courses/:courseId/product', ctrl.upsertCourseProduct);
router.delete('/courses/:courseId/product', ctrl.removeCourseProduct);
// Categories per course
router.get ('/courses/:courseId/categories', ctrl.getCourseCategories);
router.post ('/courses/:courseId/categories', ctrl.syncCourseCategories);
module.exports = router;
+14
View File
@@ -0,0 +1,14 @@
const express = require('express');
const router = express.Router();
const ctrl = require('../../controllers/admin/profile.controller');
const { validate } = require('../../middleware/validate.middleware');
const { updateProfileValidator } = require('../../validators/profile.validator');
const { handleAvatarUpload } = require('../../middleware/avatar_upload.middleware');
router.get('/', ctrl.getProfile);
router.put('/', ...updateProfileValidator, validate, ctrl.updateProfile);
router.post('/avatar', handleAvatarUpload, ctrl.uploadAvatar);
router.delete('/avatar', ctrl.deleteAvatar);
module.exports = router;
+10
View File
@@ -1,5 +1,6 @@
const router = require("express").Router();
const controller = require("../../controllers/admin/task.controller");
const completionController = require("../../controllers/admin/task_completion.controller");
const { sensitiveOpsLimiter } = require("../../middleware/rateLimiter.middleware");
// ─── Task Lists ───────────────────────────────────────────────────────────────
@@ -35,4 +36,13 @@ router.patch("/:taskListId/tasks/:taskId", sensitiveOpsLimiter, controller.updat
router.delete("/:taskListId/tasks/:taskId", sensitiveOpsLimiter, controller.archiveTask);
router.patch("/:taskListId/tasks/:taskId/restore", sensitiveOpsLimiter, controller.restoreTask);
// ─── Completions (nested under task) ─────────────────────────────────────────
router.get("/:taskListId/tasks/:taskId/completions", completionController.getCompletions);
router.get("/:taskListId/tasks/:taskId/completions/user/:userId", completionController.getCompletionsByUser);
router.post("/:taskListId/tasks/:taskId/completions/bulk-archive", sensitiveOpsLimiter, completionController.bulkArchiveCompletions);
router.post("/:taskListId/tasks/:taskId/completions/bulk-restore", sensitiveOpsLimiter, completionController.bulkRestoreCompletions);
router.get("/:taskListId/tasks/:taskId/completions/:completionId", completionController.getCompletion);
router.delete("/:taskListId/tasks/:taskId/completions/:completionId", sensitiveOpsLimiter, completionController.archiveCompletion);
router.patch("/:taskListId/tasks/:taskId/completions/:completionId/restore", sensitiveOpsLimiter, completionController.restoreCompletion);
module.exports = router;
+28
View File
@@ -0,0 +1,28 @@
const express = require('express');
const router = express.Router();
const ctrl = require('../../controllers/admin/tiers.controller');
router.get ('/', ctrl.getPlans);
router.post ('/', ctrl.createPlan);
router.get ('/field-values', ctrl.getPlanFieldValues);
router.post ('/bulk/archive', ctrl.bulkArchivePlans);
router.post ('/bulk/restore', ctrl.bulkRestorePlans);
router.get ('/payments/field-values', ctrl.getPaymentFieldValues);
router.get ('/payments', ctrl.getPayments);
router.get ('/payments/:id', ctrl.getPayment);
router.get ('/users/:id/tiers', ctrl.getUserTiers);
router.post ('/users/tiers/grant', ctrl.grantTier);
router.patch ('/users/tiers/:tid/revoke', ctrl.revokeTier);
// ← course routes before /:id
router.get ('/:id/courses', ctrl.getPlanCourses);
router.post ('/:id/courses', ctrl.syncPlanCourses);
router.get ('/:id', ctrl.getPlan);
router.put ('/:id', ctrl.updatePlan);
router.delete('/:id', ctrl.archivePlan);
router.post ('/:id/restore', ctrl.restorePlan);
module.exports = router;
+8 -1
View File
@@ -1,7 +1,8 @@
const express = require('express');
const router = express.Router();
const usersCtrl = require('../../controllers/admin/users.controller');
const usersCtrl = require('../../controllers/admin/users.controller');
const activityCtrl = require('../../controllers/admin/user_activity.controller');
const { sensitiveOpsLimiter } = require('../../middleware/rateLimiter.middleware');
router.post('/bulk/restore', sensitiveOpsLimiter, usersCtrl.bulkRestoreUsers);
@@ -19,4 +20,10 @@ router.post('/:id/restore', sensitiveOpsLimiter, usersCtrl.restoreUser);
router.get('/:id/sessions', usersCtrl.getUserSessions);
router.delete('/:id/sessions/:sid', sensitiveOpsLimiter, usersCtrl.terminateSession);
// ── Achievements ──────────────────────────────────────────────────────────────
router.get('/:id/achievements', usersCtrl.getUserAchievements);
// ── Activity ──────────────────────────────────────────────────────────────────
router.get('/:id/activity', activityCtrl.getUserActivity);
module.exports = router;