From fec4db99d1c028ada75454cda4a0e52ddeec5a54 Mon Sep 17 00:00:00 2001 From: Hrishikesh Uchake Date: Sun, 25 Jan 2026 14:56:22 -0600 Subject: [PATCH] Improve resume preview: Fix CORS origins, support direct URLs in iframes, and add robust backend logging --- app_production.py | 22 ++++++++++++++++------ frontend/src/components/Dashboard.tsx | 23 ++++++++++++----------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/app_production.py b/app_production.py index ef72f78..a87381d 100644 --- a/app_production.py +++ b/app_production.py @@ -752,48 +752,58 @@ def get_user_resumes(user_id): return jsonify({"error": "INTERNAL_ERROR", "message": str(e)}), 500 +@app.route("/api/user/resumes/download/", methods=["GET"]) @app.route("/api/v1/user/resumes/download/", methods=["GET"]) def download_resume(resume_id): """Download a previously generated resume PDF""" try: from database.repositories import ResumeRepository + logger.info(f"Download request for resume: {resume_id}") resume = ResumeRepository.get_resume(resume_id) if not resume or not resume.pdf_path: + logger.warning(f"Resume not found or no PDF path: {resume_id}") return jsonify({"error": "Resume not found"}), 404 - if not os.path.exists(resume.pdf_path): + abs_path = os.path.abspath(resume.pdf_path) + if not os.path.exists(abs_path): + logger.error(f"PDF file not found on disk: {abs_path}") return jsonify({"error": "PDF file not found on server"}), 404 return send_file( - resume.pdf_path, + abs_path, mimetype="application/pdf", as_attachment=True, download_name=f"resume_{resume.job_title.replace(' ', '_')}.pdf" ) except Exception as e: - logger.error(f"Failed to download resume: {e}", exc_info=True) + logger.error(f"Failed to download resume {resume_id}: {e}", exc_info=True) return jsonify({"error": "INTERNAL_ERROR", "message": str(e)}), 500 +@app.route("/api/user/resumes/preview/", methods=["GET"]) @app.route("/api/v1/user/resumes/preview/", methods=["GET"]) def preview_resume(resume_id): """Preview a previously generated resume PDF inline""" try: from database.repositories import ResumeRepository + logger.info(f"Preview request for resume: {resume_id}") resume = ResumeRepository.get_resume(resume_id) if not resume or not resume.pdf_path: + logger.warning(f"Resume not found or no PDF path: {resume_id}") return jsonify({"error": "Resume not found"}), 404 - if not os.path.exists(resume.pdf_path): + abs_path = os.path.abspath(resume.pdf_path) + if not os.path.exists(abs_path): + logger.error(f"PDF file not found on disk: {abs_path}") return jsonify({"error": "PDF file not found on server"}), 404 return send_file( - resume.pdf_path, + abs_path, mimetype="application/pdf", as_attachment=False ) except Exception as e: - logger.error(f"Failed to preview resume: {e}", exc_info=True) + logger.error(f"Failed to preview resume {resume_id}: {e}", exc_info=True) return jsonify({"error": "INTERNAL_ERROR", "message": str(e)}), 500 diff --git a/frontend/src/components/Dashboard.tsx b/frontend/src/components/Dashboard.tsx index 000fb43..0e8fe56 100644 --- a/frontend/src/components/Dashboard.tsx +++ b/frontend/src/components/Dashboard.tsx @@ -77,12 +77,8 @@ export default function Dashboard() { const handlePreview = async (resumeId: string) => { try { setPreviewResumeId(resumeId); - const response = await axios.get(`${API_BASE_URL}/api/v1/user/resumes/preview/${resumeId}`, { - responseType: 'blob' - }); - - const blob = new Blob([response.data], { type: 'application/pdf' }); - const url = window.URL.createObjectURL(blob); + // Direct URL is more robust for PDF iframes than blob URLs + const url = `${API_BASE_URL}/api/v1/user/resumes/preview/${resumeId}`; setPreviewUrl(url); } catch (err: any) { console.error('Preview error:', err); @@ -91,7 +87,7 @@ export default function Dashboard() { }; const closePreview = () => { - if (previewUrl) { + if (previewUrl && previewUrl.startsWith('blob:')) { window.URL.revokeObjectURL(previewUrl); } setPreviewUrl(null); @@ -482,10 +478,15 @@ export default function Dashboard() {