Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions app_production.py
Original file line number Diff line number Diff line change
Expand Up @@ -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/<resume_id>", methods=["GET"])
@app.route("/api/v1/user/resumes/download/<resume_id>", 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/<resume_id>", methods=["GET"])
@app.route("/api/v1/user/resumes/preview/<resume_id>", 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


Expand Down
23 changes: 12 additions & 11 deletions frontend/src/components/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -91,7 +87,7 @@ export default function Dashboard() {
};

const closePreview = () => {
if (previewUrl) {
if (previewUrl && previewUrl.startsWith('blob:')) {
window.URL.revokeObjectURL(previewUrl);
}
setPreviewUrl(null);
Expand Down Expand Up @@ -482,10 +478,15 @@ export default function Dashboard() {
<div className="p-6 bg-card border-t border-muted flex flex-col sm:flex-row justify-center items-center gap-4">
<button
onClick={() => {
const link = document.createElement('a');
link.href = previewUrl;
link.setAttribute('download', 'tailored_resume.pdf');
link.click();
if (previewResumeId) {
const downloadUrl = `${API_BASE_URL}/api/v1/user/resumes/download/${previewResumeId}`;
const link = document.createElement('a');
link.href = downloadUrl;
link.setAttribute('download', 'tailored_resume.pdf');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
}}
className="w-full sm:w-auto flex items-center justify-center gap-3 bg-primary hover:bg-primary-dark text-primary-foreground px-10 py-3.5 rounded-2xl font-black shadow-xl shadow-primary/25 transition-smooth"
>
Expand Down