import 'core-js/features/promise/with-resolvers'; // Polyfill for Promise.withResolvers import { NextResponse } from "next/server"; import fs from "fs"; import path from "path"; import { v4 as uuidv4 } from 'uuid'; import { fileTypeFromBuffer } from 'file-type'; import escapeStringRegexp from 'escape-string-regexp'; import { exec as childProcessExec } from 'child_process'; import { promisify } from 'util'; const exec = promisify(childProcessExec); const uploadDir = path.join(process.cwd(), "uploads", "cv"); // Ensure the upload directory exists if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir, { recursive: true }); } export async function POST(req: Request): Promise { console.log("Received request for CV file upload"); try { const formData = await req.formData(); const file: File | null = formData.get('cv') as unknown as File | null; if (!file) { console.warn("No file uploaded."); return NextResponse.json({ message: "No file uploaded." }, { status: 400 }); } const originalFilename = file.name; const uniqueFilename = `${uuidv4()}-${originalFilename}`; const newFilePath = path.join(uploadDir, uniqueFilename); console.log(`Saving file to: ${newFilePath}`); const fileBuffer = await file.arrayBuffer(); const type = await fileTypeFromBuffer(Buffer.from(fileBuffer)); console.log("Detected file type:", type); if (!type || type.mime !== 'application/pdf') { return NextResponse.json({ message: "Unsupported file type detected. Only PDF files are allowed." }, { status: 400 }); } await fs.promises.writeFile(newFilePath, Buffer.from(fileBuffer)); console.log("File uploaded and saved successfully!"); // Get the PDF file size const pdfFileSize = fs.statSync(newFilePath).size; console.log(`PDF file size: ${pdfFileSize} bytes`); // Extract text from PDF using pdfminer.six let textContent = ''; let extractedTextFilePath = ''; try { const extractTextCommand = `pdf2txt.py "${newFilePath}"`; const { stdout, stderr } = await exec(extractTextCommand); textContent = stdout; // Create extracted text file path extractedTextFilePath = newFilePath.replace(/\.pdf$/i, ".txt") // Write extracted text to file fs.writeFileSync(extractedTextFilePath, textContent); console.log(`Extracted text saved to: ${extractedTextFilePath}`); } catch (error: any) { console.error("Error extracting text from PDF:", error); return NextResponse.json({ summary: "Error extracting text from PDF" }, { status: 500 }); } // Execute the resume analysis script const { spawn } = require('child_process'); const pythonProcess = spawn('python3', [path.join(process.cwd(), 'utils', 'resume_analysis.py'), "-f", extractedTextFilePath]); let rawOutput = ''; let pythonProcessError = false; let summary: any = null; // Change summary to 'any' type let openaiOutputFilePath = path.join(uploadDir, "openai_raw_output.txt"); // Define path here pythonProcess.stdout.on('data', (data: Buffer) => { const output = data.toString(); rawOutput += output; const lines = output.trim().split('\n'); // Split output into lines const jsonOutputLine = lines[lines.length - 1]; // Take the last line as JSON output fs.writeFileSync(openaiOutputFilePath, jsonOutputLine); // Save last line to file }); pythonProcess.stderr.on('data', (data: Buffer) => { console.error(`stderr: ${data}`); }); pythonProcess.on('close', (code: number) => { console.log(`child process exited with code ${code}`); if (code !== 0) { summary = { error: "Error generating summary" }; pythonProcessError = true; } else { try { // Parse JSON from the last line of the output const lines = rawOutput.trim().split('\n'); const jsonOutputLine = lines[lines.length - 1]; console.log("Attempting to parse JSON:", jsonOutputLine); // Log raw JSON string try { summary = JSON.parse(jsonOutputLine); } catch (error) { console.error("Failed to parse JSON from python script:", error); console.error("Raw JSON string that failed to parse:", jsonOutputLine); // Log the raw JSON string that failed summary = { error: "Failed to parse JSON from python script" }; pythonProcessError = true; // Log raw output to file for debugging const errorLogPath = path.join(uploadDir, "openai_raw_output.txt"); const timestamp = new Date().toISOString(); try { if (error instanceof Error) { fs.appendFileSync(errorLogPath, `\n--- JSON Parse Error ---\nTimestamp: ${timestamp}\nRaw Output:\n${rawOutput}\nError: ${error.message}\nFailed JSON String:\n${jsonOutputLine}\n`); // Include failed JSON string in log } else { fs.appendFileSync(errorLogPath, `\n--- JSON Parse Error ---\nTimestamp: ${timestamp}\nRaw Output:\n${rawOutput}\nError: Unknown error\nFailed JSON String:\n${jsonOutputLine}\n`); // Include failed JSON string in log } console.log(`Raw Python output logged to ${errorLogPath}`); } catch (logError: any) { // Explicitly type logError as any console.error("Error logging raw output:", logError); } } } catch (outerError) { // Correctly placed catch block for the outer try console.error("Outer try block error:", outerError); } } }); // Add a timeout to the python process const timeout = setTimeout(() => { console.error("Python process timed out"); pythonProcess.kill(); summary = { error: "Error generating summary: Timeout" }; pythonProcessError = true; }, 30000); // 30 seconds return new Promise((resolve) => { pythonProcess.on('close', () => { clearTimeout(timeout); const status = pythonProcessError ? 500 : 200; resolve(NextResponse.json(summary, { status })); }); }) as Promise; } catch (error: unknown) { console.error("Error during file processing:", error); const message = error instanceof Error ? error.message : 'An unknown error occurred'; return NextResponse.json({ message: "Error processing file: " + message }, { status: 500 }); } }