chore: establish learning module baseline
This commit is contained in:
commit
2d04197f3f
2409 files changed
+533050
No files matched your search
@@ -0,0 +1,70 @@
|
||||
// Large-course mode API — learner-facing course manifest (L2, public read).
|
||||
//
|
||||
// GET /api/learn/courses/:courseId
|
||||
//
|
||||
// Static data: the published course framework + per-module courseware ids.
|
||||
// Zero LLM — the learner course page uses this to render the module list and
|
||||
// link each module into the existing /learn/:coursewareId player.
|
||||
|
||||
import { type NextRequest } from 'next/server';
|
||||
import { apiError, apiSuccess, API_ERROR_CODES } from '@/lib/server/api-response';
|
||||
import { createLogger } from '@/lib/logger';
|
||||
import { courseManifestRepo } from '@/lib/course-manifest-repo/store';
|
||||
import { isValidCourseManifestId } from '@/lib/course-manifest-repo/store';
|
||||
import { isPinnedCourseManifestModule } from '@/lib/course-manifest-repo/types';
|
||||
|
||||
const log = createLogger('Learner Course API');
|
||||
|
||||
export async function GET(
|
||||
request: NextRequest,
|
||||
{ params }: { params: Promise<{ courseId: string }> },
|
||||
) {
|
||||
try {
|
||||
const { courseId } = await params;
|
||||
if (!isValidCourseManifestId(courseId)) {
|
||||
return apiError(API_ERROR_CODES.INVALID_REQUEST, 400, `Invalid course id: ${courseId}`);
|
||||
}
|
||||
const versionParam = new URL(request.url).searchParams.get('version');
|
||||
let record;
|
||||
if (versionParam !== null) {
|
||||
const version = Number(versionParam);
|
||||
if (!Number.isInteger(version) || version < 1) {
|
||||
return apiError(API_ERROR_CODES.INVALID_REQUEST, 400, 'Invalid course manifest version');
|
||||
}
|
||||
record = await courseManifestRepo.getRecord(courseId, version);
|
||||
} else {
|
||||
record = await courseManifestRepo.getLatestRecord(courseId);
|
||||
}
|
||||
if (!record) {
|
||||
return apiError(API_ERROR_CODES.INVALID_REQUEST, 404, `Course not found: ${courseId}`);
|
||||
}
|
||||
if (record.schemaVersion !== 2 || !record.modules.every(isPinnedCourseManifestModule)) {
|
||||
return apiError(
|
||||
API_ERROR_CODES.INVALID_REQUEST,
|
||||
409,
|
||||
'This legacy course manifest must be republished before learners can open it',
|
||||
);
|
||||
}
|
||||
return apiSuccess({
|
||||
course: {
|
||||
courseId: record.courseId,
|
||||
version: record.version,
|
||||
title: record.title,
|
||||
summary: record.summary,
|
||||
language: record.language,
|
||||
publishedAt: record.publishedAt,
|
||||
modules: record.modules.map((m) => ({
|
||||
index: m.index,
|
||||
title: m.title,
|
||||
description: m.description,
|
||||
coursewareId: m.coursewareId,
|
||||
coursewareVersion: m.coursewareVersion,
|
||||
contentHash: m.contentHash,
|
||||
})),
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
log.error('Learner course manifest failed:', error);
|
||||
return apiError(API_ERROR_CODES.INTERNAL_ERROR, 500, 'Failed to load course');
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user