164 lines
5.9 KiB
TypeScript
164 lines
5.9 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) {
|
|
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 });
|
|
}
|
|
}
|