import { NextRequest, NextResponse } from 'next/server' import { validateUploadSecret } from '@/lib/auth' import { getRemoteFolder, pushAllToGitHub } from '@/lib/github' import { buildCommitMessage } from '@/lib/commit-message' import { classifyFileChanges } from '@/lib/diff-files' import { getModelFolderPath } from '@/lib/model-paths' import { cleanupStagingUpload, ensurePreparedStagingAssets, readStagedManifest } from '@/lib/upload-staging' import { acquireUploadLock, releaseUploadLock } from '@/lib/upload-lock' import { parseStagingRequestBody } from '@/lib/upload-request' import { getErrorMessage } from '@/lib/guards' export const runtime = 'nodejs' export const dynamic = 'force-dynamic' /** * POST /api/upload/git * Upload prepared files and push to GitHub via Octokit. */ export async function POST(req: NextRequest) { const authError = validateUploadSecret(req) if (authError) return authError let folderName: string let stagingId: string try { const body: unknown = await req.json() stagingId = parseStagingRequestBody(body).stagingId const manifest = await readStagedManifest(stagingId) folderName = manifest.folderName } catch (err) { const message = getErrorMessage(err) return NextResponse.json({ success: false, error: message }, { status: 400 }) } if (!acquireUploadLock(folderName)) { return NextResponse.json( { success: false, error: 'Un upload est deja en cours pour ce dossier. Patientez quelques secondes.' }, { status: 409 }, ) } try { const { filesToPush, modelFilename, compressed, compressionError, assetSummaries, } = await ensurePreparedStagingAssets(stagingId) const folderPath = getModelFolderPath(folderName) const remote = await getRemoteFolder(folderPath) const remoteFileMap = new Map(remote.files.map((f) => [f.name.toLowerCase(), f.size])) const isReplace = remoteFileMap.size > 0 const { fileChanges, changedFilesToPush, deletedFileNames, deletePaths } = classifyFileChanges(filesToPush, remoteFileMap, folderPath) if (changedFilesToPush.length === 0 && deletePaths.length === 0) { await cleanupStagingUpload(stagingId).catch(() => {}) return NextResponse.json({ success: true, folderName, filesCount: 0, compressed, compressionError: compressionError || undefined, message: 'Aucun fichier modifie — rien a envoyer.', }) } const commitMessage = buildCommitMessage( folderName, modelFilename, assetSummaries, isReplace, fileChanges, deletedFileNames, ) try { const { commitUrl } = await pushAllToGitHub(changedFilesToPush, deletePaths, commitMessage) await cleanupStagingUpload(stagingId).catch(() => {}) return NextResponse.json({ success: true, folderName, filesCount: changedFilesToPush.length, compressed, compressionError: compressionError || undefined, message: `${changedFilesToPush.length} fichier(s) modifie(s) envoye(s) sur GitHub en un seul commit.`, commitUrl, }) } catch (err) { const message = getErrorMessage(err, 'Erreur GitHub inconnue') return NextResponse.json( { success: false, error: `Push GitHub echoue: ${message}` }, { status: 500 }, ) } } finally { releaseUploadLock(folderName) } }