// Courseware detail — GET /api/coursewares/[id] // // Returns the latest published record for one courseware (or a specific // version via `?version=N`). Public read path for the learner player. import { type NextRequest } from 'next/server'; import { apiError, apiSuccess, API_ERROR_CODES } from '@/lib/server/api-response'; import { COURSEWARES_DIR, createFileCoursewareRepo } from '@/lib/courseware-repo/store'; import { toSummary } from '@/lib/courseware-repo'; import { filterLearnerVisibleCoursewares, isCoursewareLearnerVisible, } from '@/lib/courseware-repo/learner-visibility'; import { createLogger } from '@/lib/logger'; const log = createLogger('Courseware Detail API'); export async function GET(request: NextRequest, context: { params: Promise<{ id: string }> }) { try { const { id } = await context.params; if (!/^[a-zA-Z0-9_-]+$/.test(id)) { return apiError(API_ERROR_CODES.INVALID_REQUEST, 400, 'Invalid courseware id'); } const repo = createFileCoursewareRepo(COURSEWARES_DIR); const versionParam = new URL(request.url).searchParams.get('version'); let record; if (versionParam) { const version = Number(versionParam); if (!Number.isInteger(version) || version < 1) { return apiError(API_ERROR_CODES.INVALID_REQUEST, 400, 'Invalid version'); } record = await repo.getRecord(id, version); if (record && !(await isCoursewareLearnerVisible(record))) record = null; } else { const candidates = (await repo.listRecords({ status: 'published' })).filter( (candidate) => candidate.coursewareId === id, ); const visible = await filterLearnerVisibleCoursewares(candidates); record = visible.sort((a, b) => b.version - a.version)[0] ?? null; } if (!record || record.status !== 'published') { return apiError(API_ERROR_CODES.INVALID_REQUEST, 404, 'Courseware not found'); } return apiSuccess({ courseware: toSummary(record) }); } catch (error) { log.error('Courseware detail failed:', error); return apiError(API_ERROR_CODES.INTERNAL_ERROR, 500, 'Courseware detail failed'); } }