chore: establish learning module baseline

This commit is contained in:
inman committed 2026-08-16 14:58:47 +08:00
commit 2d04197f3f
2409 files changed
+533050

No files matched your search

@@ -0,0 +1,77 @@
// Large-course mode API — regenerate a module and its dependent tail.
//
// POST /api/courses/:courseId/modules/:index/regenerate
//
// Re-runs the given module through the single-courseware agent, invalidating
// and rebuilding every later module because their continuity input depends on
// the target's actual output. Refused while any course generation is active.
import { after, type NextRequest } from 'next/server';
import { apiError, apiSuccess, API_ERROR_CODES } from '@/lib/server/api-response';
import { buildRequestOrigin } from '@/lib/server/classroom-storage';
import { createLogger } from '@/lib/logger';
import { isValidCourseId, readCourseRecordReconciled } from '@/lib/course-framework/store';
import { isCourseGenerationRunning, runCourseGenerationJob } from '@/lib/course-framework/runner';
import { requireOpsAccess } from '@/lib/server/ops-access';
import {
CourseMutationInProgressError,
isCoursePublishing,
} from '@/lib/course-framework/publish-state';
const log = createLogger('Module Regenerate API');
export async function POST(
req: NextRequest,
{ params }: { params: Promise<{ courseId: string; index: string }> },
) {
const denied = requireOpsAccess(req);
if (denied) return denied;
try {
const { courseId, index: indexParam } = await params;
const index = Number(indexParam);
if (!isValidCourseId(courseId) || !Number.isInteger(index) || index < 1) {
return apiError(API_ERROR_CODES.INVALID_REQUEST, 400, 'Invalid course id or module index');
}
const record = await readCourseRecordReconciled(courseId);
if (!record) {
return apiError(API_ERROR_CODES.INVALID_REQUEST, 404, `Course not found: ${courseId}`);
}
if (!record.framework) {
return apiError(
API_ERROR_CODES.INVALID_REQUEST,
409,
'Course framework not generated yet; cannot regenerate a module',
);
}
const moduleRec = record.modules.find((m) => m.index === index);
if (!moduleRec) {
return apiError(API_ERROR_CODES.INVALID_REQUEST, 404, `Module ${index} not found`);
}
if (
isCourseGenerationRunning(courseId) ||
isCoursePublishing(courseId) ||
record.modules.some((module) => module.status === 'generating')
) {
return apiError(API_ERROR_CODES.INVALID_REQUEST, 409, '该课程已有生成任务正在运行');
}
const baseUrl = buildRequestOrigin(req);
const run = runCourseGenerationJob(courseId, baseUrl, { onlyModuleIndex: index });
after(() => run);
return apiSuccess({
courseId,
moduleIndex: index,
status: 'generating',
message: `Module ${index} and downstream regeneration started`,
detailUrl: `${baseUrl}/api/courses/${courseId}`,
});
} catch (error) {
if (error instanceof CourseMutationInProgressError) {
return apiError(API_ERROR_CODES.INVALID_REQUEST, 409, error.message);
}
log.error('Module regenerate failed:', error);
return apiError(API_ERROR_CODES.INTERNAL_ERROR, 500, 'Failed to regenerate module');
}
}