/*********************************************************************************************************************************************************************** * File Name: groups.routes.js (client) * Type of Program: Router * Description: Client-facing task routes — groups, task lists, tasks, completions, * file uploads, and progress tracking. * * Route Map: * GET /client/groups → my groups * GET /client/groups/:groupId → single group * GET /client/groups/:groupId/task-lists → task lists (?status=ongoing|completed|overdue) * GET /client/groups/:groupId/task-lists/:taskListId → single task list * GET /client/groups/:groupId/task-lists/:taskListId/tasks/:taskId → task + requirements + latest completion * GET /client/groups/:groupId/task-lists/:taskListId/tasks/:taskId/progress → full progress snapshot * GET /client/groups/:groupId/task-lists/:taskListId/tasks/:taskId/completions/latest → latest completion only * GET /client/groups/:groupId/task-lists/:taskListId/tasks/:taskId/completions → full completion history * POST /client/groups/:groupId/task-lists/:taskListId/tasks/:taskId/completions → submit completion * POST /client/groups/:groupId/task-lists/:taskListId/tasks/:taskId/upload → upload file to S3 (returns file metadata) * POST /client/groups/:groupId/task-lists/:taskListId/tasks/:taskId/requirements/:requirementId/visit → UPSERT link visit * POST /client/groups/:groupId/task-lists/:taskListId/tasks/:taskId/requirements/:requirementId/progress → UPSERT lesson progress * * Guards: authenticate + requireClient() applied in client.routes.js * * Author: rgrgogu * Date Created: Jun. 13, 2026 ***********************************************************************************************************************************************************************/ const router = require('express').Router(); const taskController = require('../../controllers/client/task.controller'); const uploadController = require('../../controllers/client/task_upload.controller'); const progressController = require('../../controllers/client/task_progress.controller'); const downloadController = require('../../controllers/client/task_download.controller'); const { handleUpload } = require('../../middleware/task_upload.middleware'); const { sensitiveOpsLimiter } = require('../../middleware/rateLimiter.middleware'); // ─── Groups ─────────────────────────────────────────────────────────────────── router.get('/', taskController.getMyGroups); router.get('/:groupId', taskController.getMyGroup); // ─── Task Lists ─────────────────────────────────────────────────────────────── router.get('/:groupId/task-lists', taskController.getGroupTaskLists); router.get('/:groupId/task-lists/:taskListId', taskController.getGroupTaskList); // ─── Tasks ──────────────────────────────────────────────────────────────────── router.get('/:groupId/task-lists/:taskListId/tasks/:taskId', taskController.getTask); // ─── Progress snapshot ──────────────────────────────────────────────────────── router.get( '/:groupId/task-lists/:taskListId/tasks/:taskId/progress', progressController.getTaskProgress ); // ─── Completions ────────────────────────────────────────────────────────────── // NOTE: /latest must be before / to avoid Express treating "latest" as an ID router.get( '/:groupId/task-lists/:taskListId/tasks/:taskId/completions/latest', taskController.getLatestCompletion ); router.get( '/:groupId/task-lists/:taskListId/tasks/:taskId/completions', taskController.getMySubmissions ); router.post( '/:groupId/task-lists/:taskListId/tasks/:taskId/completions', sensitiveOpsLimiter, taskController.submitTask ); router.get( '/:groupId/task-lists/:taskListId/tasks/:taskId/completions/:completionId/files/:fileId/stream', downloadController.streamCompletionFile ); router.get( '/:groupId/task-lists/:taskListId/tasks/:taskId/completions/:completionId/files/:fileId/download', downloadController.downloadCompletionFile ); // ─── File upload → S3 ──────────────────────────────────────────────────────── // Client uploads one file at a time, gets back file metadata, // then includes all file references in the completion submit payload. router.post( '/:groupId/task-lists/:taskListId/tasks/:taskId/upload', sensitiveOpsLimiter, handleUpload, uploadController.uploadTaskFile ); // ─── Requirement-level progress (UPSERT) ───────────────────────────────────── router.post( '/:groupId/task-lists/:taskListId/tasks/:taskId/requirements/:requirementId/visit', sensitiveOpsLimiter, progressController.visitLink ); router.delete( '/:groupId/task-lists/:taskListId/tasks/:taskId/requirements/:requirementId/visit', sensitiveOpsLimiter, progressController.unvisitLink ); router.post( '/:groupId/task-lists/:taskListId/tasks/:taskId/requirements/:requirementId/progress', sensitiveOpsLimiter, progressController.updateProgress ); module.exports = router;