|
- 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(),
- });
- }
|