35 lines
1.4 KiB
TypeScript
35 lines
1.4 KiB
TypeScript
// Large-course mode API — learner-facing course manifest list (L2, public read).
|
|
//
|
|
// GET /api/learn/courses?limit=N
|
|
|
|
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 { isPinnedCourseManifestModule } from '@/lib/course-manifest-repo/types';
|
|
|
|
const log = createLogger('Learner Courses API');
|
|
|
|
export async function GET(request: NextRequest) {
|
|
try {
|
|
const limitParam = Number(new URL(request.url).searchParams.get('limit') ?? 50);
|
|
const limit = Number.isFinite(limitParam) && limitParam > 0 ? Math.min(limitParam, 100) : 50;
|
|
const records = (await courseManifestRepo.listRecords(limit)).filter(
|
|
(record) => record.schemaVersion === 2 && record.modules.every(isPinnedCourseManifestModule),
|
|
);
|
|
return apiSuccess({
|
|
items: records.map((record) => ({
|
|
courseId: record.courseId,
|
|
version: record.version,
|
|
title: record.title,
|
|
summary: record.summary,
|
|
moduleCount: record.modules.length,
|
|
publishedAt: record.publishedAt,
|
|
})),
|
|
});
|
|
} catch (error) {
|
|
log.error('Learner course listing failed:', error);
|
|
return apiError(API_ERROR_CODES.INTERNAL_ERROR, 500, 'Failed to list courses');
|
|
}
|
|
}
|