feat: productionize learning engine and classroom

This commit is contained in:
inman committed 2026-08-16 21:44:52 +08:00
1 parent 2d04197f3f
commit ba35adfbfa
124 files changed
+9071 -796

No files matched your search

@@ -0,0 +1,69 @@
import type { NextRequest } from 'next/server';
import { readCourseRecordReconciled } from '@/lib/course-framework/store';
import { authorizeLearningRuntime, runtimeUnauthorized } from '@/lib/makelore-runtime/auth';
import { isValidClassroomJobId, readClassroomGenerationJob } from '@/lib/server/classroom-job-store';
import { buildRequestOrigin, readClassroom } from '@/lib/server/classroom-storage';
import {
finalizeLargeLearningCourse,
finalizeSingleLearningCourse,
frozenLearningFinalizeResponse,
} from '@/lib/makelore-course/finalize';
import { FrozenLearningCourseMutationError } from '@/lib/makelore-course/immutability';
export async function POST(request: NextRequest, context: { params: Promise<{ jobId: string }> }) {
if (!authorizeLearningRuntime(request)) return runtimeUnauthorized();
const { jobId } = await context.params;
const owner = request.headers.get('x-owner-user-id')?.trim();
if (!owner || !isValidClassroomJobId(jobId)) return Response.json({ error: 'invalid_request' }, { status: 400 });
const job = await readClassroomGenerationJob(jobId);
try {
if (job) {
if (job.ownerPrincipalId !== owner) {
return Response.json({ error: 'job_not_found' }, { status: 404 });
}
if (job.status !== 'succeeded' || !job.result?.classroomId) {
return Response.json({ error: 'job_not_ready' }, { status: 409 });
}
const classroom = await readClassroom(job.result.classroomId);
if (!classroom) return Response.json({ error: 'classroom_not_found' }, { status: 404 });
const record = await finalizeSingleLearningCourse({
jobId,
classroom,
baseUrl: buildRequestOrigin(request),
requireInteractiveHtml: job.input.interactiveMode === true,
requireNarrationAudio: job.input.enableTTS === true,
});
return Response.json(frozenLearningFinalizeResponse(record));
}
const course = await readCourseRecordReconciled(jobId);
if (!course || course.ownerPrincipalId !== owner) {
return Response.json({ error: 'job_not_found' }, { status: 404 });
}
if (course.status !== 'completed') {
return Response.json(
{
error: course.status === 'framework_ready' ? 'review_required' : 'job_not_ready',
...(course.status === 'framework_ready'
? { reviewUrl: `/learning-ops/courses/${course.id}` }
: {}),
},
{ status: 409 },
);
}
const record = await finalizeLargeLearningCourse({
jobId,
course,
baseUrl: buildRequestOrigin(request),
});
return Response.json(frozenLearningFinalizeResponse(record));
} catch (error) {
if (error instanceof FrozenLearningCourseMutationError) {
return Response.json({ error: 'course_frozen', message: error.message }, { status: 409 });
}
return Response.json({
error: 'course_not_offline_complete',
message: error instanceof Error ? error.message : 'Course finalization failed',
}, { status: 422 });
}
}