// Large-course mode API — regenerate the course framework. // // POST /api/courses/:courseId/framework/regenerate // // Re-runs Layer 1 from scratch: the existing framework and per-module state // are reset and the framework agent produces a new decomposition (status // `framework_generating` → `framework_ready`). Refused while any module is // generating. 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, regenerateCourseFramework, } from '@/lib/course-framework/runner'; import { requireOpsAccess } from '@/lib/server/ops-access'; import { CourseMutationInProgressError, isCoursePublishing, } from '@/lib/course-framework/publish-state'; import { proxyLargeCourseControlToEngine } from '@/lib/makelore-runtime/ops-engine-control'; import { assertLearningCourseMutable, FrozenLearningCourseMutationError, } from '@/lib/makelore-course/immutability'; const log = createLogger('Framework Regenerate API'); export async function POST( req: NextRequest, { params }: { params: Promise<{ courseId: string }> }, ) { const denied = await requireOpsAccess(req); if (denied) return denied; try { const { courseId } = await params; if (!isValidCourseId(courseId)) { return apiError(API_ERROR_CODES.INVALID_REQUEST, 400, `Invalid course id: ${courseId}`); } const record = await readCourseRecordReconciled(courseId); if (!record) { return apiError(API_ERROR_CODES.INVALID_REQUEST, 404, `Course not found: ${courseId}`); } const proxied = await proxyLargeCourseControlToEngine({ request: req, course: record, action: 'framework-regenerate', }); if (proxied) return proxied; await assertLearningCourseMutable(courseId, record); if ( isCourseGenerationRunning(courseId) || isCoursePublishing(courseId) || record.modules.some((m) => m.status === 'generating') ) { return apiError(API_ERROR_CODES.INVALID_REQUEST, 409, '模块正在生成中,无法重新生成框架'); } const baseUrl = buildRequestOrigin(req); const run = regenerateCourseFramework(courseId, baseUrl); after(() => run); return apiSuccess({ courseId, status: 'framework_generating', message: '框架重新生成已开始', detailUrl: `${baseUrl}/api/courses/${courseId}`, }); } catch (error) { if (error instanceof FrozenLearningCourseMutationError) { return apiError(API_ERROR_CODES.INVALID_REQUEST, 409, error.message); } if (error instanceof CourseMutationInProgressError) { return apiError(API_ERROR_CODES.INVALID_REQUEST, 409, error.message); } log.error('Framework regenerate failed:', error); return apiError(API_ERROR_CODES.INTERNAL_ERROR, 500, 'Failed to regenerate framework'); } }