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) { 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 summary = ''; pythonProcess.stdout.on('data', (data: Buffer) => { summary += data.toString(); }); pythonProcess.stderr.on('data', (data: Buffer) => { console.error(`stderr: ${data}`); }); let pythonProcessError = false; let input_tokens = 0; let output_tokens = 0; let total_tokens = 0; let cost = 0; let rawOutput = ""; let openaiOutputFilePath = ""; pythonProcess.stdout.on('data', (data: Buffer) => { const output = data.toString(); rawOutput += output; }); pythonProcess.on('close', (code: number) => { console.log(`child process exited with code ${code}`); if (code !== 0) { summary = "Error generating summary"; pythonProcessError = true; } else { summary = rawOutput.split("Summary: ")[1]?.split("\n--- Usage Information ---")[0] || "Error generating summary"; try { input_tokens = parseInt(rawOutput.split("Input tokens: ")[1]?.split("\n")[0] || "0"); output_tokens = parseInt(rawOutput.split("Output tokens: ")[1]?.split("\n")[0] || "0"); total_tokens = parseInt(rawOutput.split("Total tokens: ")[1]?.split("\n")[0] || "0"); cost = parseFloat(rawOutput.split("Cost: $")[1]?.split("\n")[0] || "0"); // Create OpenAI output file path openaiOutputFilePath = newFilePath.replace(/\.pdf$/i, "_openai.txt"); fs.writeFileSync(openaiOutputFilePath, rawOutput); console.log(`OpenAI output saved to: ${openaiOutputFilePath}`); } catch (e) { console.error("Error parsing token information", e); } } console.log(`--- Usage Information ---`); console.log(`Input tokens: ${input_tokens}`); console.log(`Output tokens: ${output_tokens}`); console.log(`Total tokens: ${total_tokens}`); console.log(`Cost: $${cost}`); }); 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 generating summary"; pythonProcessError = true; } console.log(`--- Usage Information ---`); console.log(`Input tokens: ${input_tokens}`); console.log(`Output tokens: ${output_tokens}`); console.log(`Total tokens: ${total_tokens}`); console.log(`Cost: $${cost}`); }); // Add a timeout to the python process const timeout = setTimeout(() => { console.error("Python process timed out"); pythonProcess.kill(); summary = "Error generating summary: Timeout"; pythonProcessError = true; }, 10000); // 10 seconds return new Promise((resolve) => { pythonProcess.on('close', (code: number) => { clearTimeout(timeout); resolve(NextResponse.json({ summary: summary }, { status: pythonProcessError ? 500 : 200 })); }); }); } catch (error: any) { console.error("Error during file processing:", error); return NextResponse.json({ message: "Error processing file: " + error.message }, { status: 500 }); } }