import { NextResponse } from 'next/server'; import { spawn } from 'node:child_process'; import { mkdir, stat, unlink, rename } from 'node:fs/promises'; import { createWriteStream } from 'node:fs'; import path from 'node:path'; import crypto from 'node:crypto'; import { checkSystemBin } from '@/lib/system-bins'; export const dynamic = 'force-dynamic'; export const maxDuration = 600; const PROJECT_ROOT = process.cwd(); const DATA_DIR = path.join(PROJECT_ROOT, 'data'); const FACTORY_DIR = path.join(PROJECT_ROOT, 'factory'); const PRESET_PATH = path.join(FACTORY_DIR, 'preset.zip'); // GET — stato del preset (per UI: presente o no, dimensione, data). export async function GET() { try { const s = await stat(PRESET_PATH); return NextResponse.json({ exists: true, sizeBytes: s.size, modifiedAt: s.mtime.toISOString(), }); } catch { return NextResponse.json({ exists: false }); } } // POST — crea il preset zippando lo stato corrente di data/. export async function POST() { if (!(await checkSystemBin('zip', '-v'))) { return NextResponse.json( { error: "Cannot create the preset: the 'zip' command is not installed on the server. Ask the administrator to install it." }, { status: 503 }, ); } await mkdir(FACTORY_DIR, { recursive: true }); // Scrive prima su .tmp poi rename atomico → mai un preset.zip parziale leggibile. const tmpPath = path.join(FACTORY_DIR, `preset.zip.${crypto.randomUUID()}.tmp`); const child = spawn( 'zip', ['-r', '-', 'cards.txt', 'portals.txt', 'uploads', 'fonts', '-x', 'uploads/.tmp/*'], { cwd: DATA_DIR }, ); const out = createWriteStream(tmpPath); child.stdout.pipe(out); const exit: number = await new Promise(resolve => { let exitCode = -1; child.on('error', () => resolve(-1)); child.on('exit', code => { exitCode = code ?? -1; }); out.on('finish', () => resolve(exitCode)); out.on('error', () => resolve(-1)); }); // 0 = ok, 12 = "nothing to do" (data/ vuoto: accettiamo, sarà uno zip minimale) if (exit !== 0 && exit !== 12) { try { await unlink(tmpPath); } catch { /* ignore */ } return NextResponse.json( { error: `Failed to create the preset archive: the 'zip' command exited with code ${exit}. Check the server logs for details. The previous preset (if any) has not been modified.` }, { status: 500 }, ); } await rename(tmpPath, PRESET_PATH); const s = await stat(PRESET_PATH); return NextResponse.json({ ok: true, sizeBytes: s.size, modifiedAt: s.mtime.toISOString(), }); }