Files
XgridConverter/app/api/convert/route.ts
2026-03-09 15:18:50 +01:00

101 lines
3.0 KiB
TypeScript

import { spawn } from "child_process";
import { writeFile, readFile, mkdir, unlink } from "fs/promises";
import { NextRequest, NextResponse } from "next/server";
import path from "path";
import os from "os";
export async function POST(req: NextRequest) {
try {
const formData = await req.formData();
const file = formData.get("file") as File;
if (!file) {
return NextResponse.json({ error: "No file provided" }, { status: 400 });
}
// 1. Setup workspace
const tempDir = path.join(os.tmpdir(), "xgrids-pipeline");
await mkdir(tempDir, { recursive: true });
// FIX: Sanitize filename to avoid shell/path issues with spaces
const safeName = file.name.replace(/[^a-z0-8.]/gi, "_").toLowerCase();
const timestamp = Date.now();
const inputPath = path.join(tempDir, `${timestamp}_${safeName}`);
// Ensure we replace the extension correctly for the output
const outputPath = inputPath.replace(/\.(lcc|lci)$/i, ".ply");
const scriptPath = path.join(
process.cwd(),
"scripts",
"preprocess",
"convert_lci_to_ply.py",
);
// 2. Write the file
const buffer = Buffer.from(await file.arrayBuffer());
await writeFile(inputPath, buffer);
// 3. Execute Python
return new Promise<NextResponse>((resolve) => {
// spawn handles arguments as an array, which is safer than exec for spaces
const pythonProcess = spawn("python3", [
scriptPath,
inputPath,
outputPath,
]);
let errorOutput = "";
pythonProcess.stderr.on("data", (data) => {
errorOutput += data.toString();
});
pythonProcess.on("close", async (code) => {
if (code !== 0) {
console.error("Python Error:", errorOutput);
// Cleanup input even on failure
await unlink(inputPath).catch(() => {});
return resolve(
NextResponse.json(
{
error: `Python script failed with code ${code}. ${errorOutput}`,
},
{ status: 500 },
),
);
}
try {
const plyBuffer = await readFile(outputPath);
// Cleanup
await Promise.all([
unlink(inputPath).catch(() => {}),
unlink(outputPath).catch(() => {}),
]);
resolve(
new NextResponse(plyBuffer, {
status: 200,
headers: {
"Content-Type": "application/octet-stream",
"Content-Disposition": `attachment; filename="${file.name.replace(/\.[^/.]+$/, "")}.ply"`,
},
}),
);
} catch (e) {
resolve(
NextResponse.json(
{ error: "Failed to read generated PLY file" },
{ status: 500 },
),
);
}
});
});
} catch (error: any) {
console.error("API Route Error:", error);
return NextResponse.json({ error: error.message }, { status: 500 });
}
}