Fixed .pdf resume upload, Logout button background

This commit is contained in:
Josh 2025-05-09 13:13:31 +00:00
parent f3125b2145
commit b0b5ff3aa8
9 changed files with 63 additions and 23 deletions

View File

@ -8,11 +8,12 @@ import sqlite3 from 'sqlite3';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import path from 'path'; import path from 'path';
import fs from 'fs'; import fs from 'fs/promises';
import multer from 'multer'; import multer from 'multer';
import mammoth from 'mammoth'; import mammoth from 'mammoth';
import { fileURLToPath } from 'url'; import { fileURLToPath } from 'url';
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.js'; import pkg from 'pdfjs-dist';
import OpenAI from 'openai'; import OpenAI from 'openai';
// --- Basic file init --- // --- Basic file init ---
@ -28,6 +29,7 @@ dotenv.config({ path: envPath }); // Load .env file
const app = express(); const app = express();
const PORT = process.env.PREMIUM_PORT || 5002; const PORT = process.env.PREMIUM_PORT || 5002;
const { getDocument } = pkg;
let db; let db;
const initDB = async () => { const initDB = async () => {
@ -1655,6 +1657,22 @@ ${resumeText}
Precisely Tailored, ATS-Optimized Resume: Precisely Tailored, ATS-Optimized Resume:
`; `;
async function extractTextFromPDF(filePath) {
const fileBuffer = await fs.readFile(filePath);
const uint8Array = new Uint8Array(fileBuffer); // Convert Buffer explicitly
const pdfDoc = await getDocument({ data: uint8Array }).promise;
let text = '';
for (let pageNum = 1; pageNum <= pdfDoc.numPages; pageNum++) {
const page = await pdfDoc.getPage(pageNum);
const pageText = await page.getTextContent();
text += pageText.items.map(item => item.str).join(' ');
}
return text;
}
// Your corrected endpoint with limits correctly returned:
app.post( app.post(
'/api/premium/resume/optimize', '/api/premium/resume/optimize',
upload.single('resumeFile'), upload.single('resumeFile'),
@ -1668,7 +1686,6 @@ app.post(
const userId = req.userId; const userId = req.userId;
const now = new Date(); const now = new Date();
const currentWeek = getWeekNumber(now); // Function defined below
const userProfile = await db.get( const userProfile = await db.get(
`SELECT is_premium, is_pro_premium, resume_optimizations_used, resume_limit_reset, resume_booster_count `SELECT is_premium, is_pro_premium, resume_optimizations_used, resume_limit_reset, resume_booster_count
@ -1702,16 +1719,20 @@ app.post(
} }
const filePath = req.file.path; const filePath = req.file.path;
const fileExt = req.file.originalname.split('.').pop().toLowerCase(); const mimeType = req.file.mimetype;
let resumeText = ''; let resumeText = '';
if (fileExt === 'pdf') { if (mimeType === 'application/pdf') {
resumeText = await extractTextFromPDF(filePath); resumeText = await extractTextFromPDF(filePath);
} else if (fileExt === 'docx') { } else if (
mimeType === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||
mimeType === 'application/msword'
) {
const result = await mammoth.extractRawText({ path: filePath }); const result = await mammoth.extractRawText({ path: filePath });
resumeText = result.value; resumeText = result.value;
} else { } else {
return res.status(400).json({ error: 'Unsupported file type.' }); await fs.unlink(filePath);
return res.status(400).json({ error: 'Unsupported or corrupted file upload.' });
} }
const prompt = buildResumePrompt(resumeText, jobTitle, jobDescription); const prompt = buildResumePrompt(resumeText, jobTitle, jobDescription);
@ -1729,11 +1750,15 @@ app.post(
[userId] [userId]
); );
// Calculate remaining optimizations
const remainingOptimizations = totalLimit - (userProfile.resume_optimizations_used + 1); const remainingOptimizations = totalLimit - (userProfile.resume_optimizations_used + 1);
fs.unlinkSync(filePath); await fs.unlink(filePath);
res.json({ optimizedResume, remainingOptimizations }); res.json({
optimizedResume,
remainingOptimizations,
resetDate: resetDate.toISOString() // <-- explicitly returned here!
});
} catch (err) { } catch (err) {
console.error('Error optimizing resume:', err); console.error('Error optimizing resume:', err);
res.status(500).json({ error: 'Failed to optimize resume.' }); res.status(500).json({ error: 'Failed to optimize resume.' });

View File

@ -236,12 +236,12 @@ function App() {
{/* Logout */} {/* Logout */}
<li> <li>
<button <button
className="text-red-600 hover:text-red-800" className="text-red-600 hover:text-red-800 bg-transparent border-none"
onClick={handleLogout} onClick={handleLogout}
> >
Logout Logout
</button> </button>
</li> </li>
</ul> </ul>
</nav> </nav>

View File

@ -9,6 +9,7 @@ function ResumeRewrite() {
const [error, setError] = useState(''); const [error, setError] = useState('');
const [remainingOptimizations, setRemainingOptimizations] = useState(null); const [remainingOptimizations, setRemainingOptimizations] = useState(null);
const [resetDate, setResetDate] = useState(null); const [resetDate, setResetDate] = useState(null);
const [loading, setLoading] = useState(false); // ADDED loading state
const handleFileChange = (e) => { const handleFileChange = (e) => {
setResumeFile(e.target.files[0]); setResumeFile(e.target.files[0]);
@ -39,6 +40,8 @@ function ResumeRewrite() {
return; return;
} }
setLoading(true); // ACTIVATE loading
try { try {
const token = localStorage.getItem('token'); const token = localStorage.getItem('token');
const formData = new FormData(); const formData = new FormData();
@ -55,12 +58,12 @@ function ResumeRewrite() {
setOptimizedResume(res.data.optimizedResume || ''); setOptimizedResume(res.data.optimizedResume || '');
setError(''); setError('');
// Refresh remaining optimizations after optimizing
fetchRemainingOptimizations(); fetchRemainingOptimizations();
} catch (err) { } catch (err) {
console.error('Resume optimization error:', err); console.error('Resume optimization error:', err);
setError(err.response?.data?.error || 'Failed to optimize resume.'); setError(err.response?.data?.error || 'Failed to optimize resume.');
} finally {
setLoading(false); // DEACTIVATE loading
} }
}; };
@ -101,10 +104,22 @@ function ResumeRewrite() {
{error && <p className="text-red-600 font-semibold">{error}</p>} {error && <p className="text-red-600 font-semibold">{error}</p>}
<button type="submit" <button
className="inline-block bg-blue-600 text-white font-semibold px-5 py-2 rounded hover:bg-blue-700 transition-colors"> type="submit"
Optimize Resume disabled={loading}
className={`inline-block font-semibold px-5 py-2 rounded transition-colors ${
loading ? 'bg-gray-400 cursor-not-allowed' : 'bg-blue-600 hover:bg-blue-700'
} text-white`}
>
{loading ? 'Optimizing Resume...' : 'Optimize Resume'}
</button> </button>
{loading && (
<div className="flex items-center justify-center mt-4">
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-blue-500"></div>
<span className="ml-3 text-blue-700 font-semibold">Optimizing your resume...</span>
</div>
)}
</form> </form>
{optimizedResume && ( {optimizedResume && (

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.