147 lines
5.8 KiB
TypeScript

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<NextResponse> {
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];
summary = JSON.parse(jsonOutputLine);
} catch (error) {
console.error("Failed to parse JSON from python script:", error);
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}\n`);
} else {
fs.appendFileSync(errorLogPath, `\n--- JSON Parse Error ---\nTimestamp: ${timestamp}\nRaw Output:\n${rawOutput}\nError: Unknown error\n`);
}
console.log(`Raw Python output logged to ${errorLogPath}`);
} catch (logError: any) { // Explicitly type logError as any
console.error("Error logging raw output:", logError);
}
}
}
});
// 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;
}, 10000); // 10 seconds
return new Promise<NextResponse>((resolve) => {
pythonProcess.on('close', () => {
clearTimeout(timeout);
const status = pythonProcessError ? 500 : 200;
resolve(NextResponse.json(summary, { status }));
});
}) as Promise<NextResponse>;
} 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 });
}
}