66 lines
1.8 KiB
TypeScript
66 lines
1.8 KiB
TypeScript
import { mkdir, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { spawn } from "node:child_process";
|
|
import { NextResponse } from "next/server";
|
|
|
|
const PYTHON = process.env.PYTHON_EXECUTABLE || "python";
|
|
const PROJECT_ROOT = join(process.cwd(), "..");
|
|
const RUNTIME_DIR = join(process.cwd(), ".runtime-crosswords");
|
|
|
|
function runPython(requestPayload: unknown): Promise<string> {
|
|
return new Promise((resolve, reject) => {
|
|
const child = spawn(PYTHON, ["crossword_service.py"], {
|
|
cwd: PROJECT_ROOT,
|
|
stdio: ["pipe", "pipe", "pipe"],
|
|
});
|
|
|
|
let stdout = "";
|
|
let stderr = "";
|
|
|
|
child.stdout.on("data", (chunk) => {
|
|
stdout += chunk.toString();
|
|
});
|
|
|
|
child.stderr.on("data", (chunk) => {
|
|
stderr += chunk.toString();
|
|
});
|
|
|
|
child.on("error", (error) => {
|
|
reject(error);
|
|
});
|
|
|
|
child.on("close", (code) => {
|
|
if (code !== 0) {
|
|
reject(new Error(stderr || `Python process exited with code ${code}`));
|
|
return;
|
|
}
|
|
resolve(stdout);
|
|
});
|
|
|
|
child.stdin.write(JSON.stringify(requestPayload));
|
|
child.stdin.end();
|
|
});
|
|
}
|
|
|
|
export async function POST(request: Request) {
|
|
try {
|
|
const payload = await request.json();
|
|
const stdout = await runPython(payload);
|
|
const responsePayload = JSON.parse(stdout);
|
|
|
|
await mkdir(RUNTIME_DIR, { recursive: true });
|
|
const targetPath = join(RUNTIME_DIR, `${responsePayload.crossword_id}.json`);
|
|
await writeFile(targetPath, JSON.stringify(responsePayload, null, 2), "utf-8");
|
|
|
|
return NextResponse.json(responsePayload);
|
|
} catch (error) {
|
|
return NextResponse.json(
|
|
{
|
|
status: "error",
|
|
message: error instanceof Error ? error.message : "Unknown error",
|
|
},
|
|
{ status: 500 },
|
|
);
|
|
}
|
|
}
|