File size: 4,861 Bytes
cc6cee7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
from fastapi import FastAPI, UploadFile, File, HTTPException
# Trigger reload
from fastapi.middleware.cors import CORSMiddleware
import os
import shutil
import time
from dotenv import load_dotenv

# Load env vars *before* importing services that might need them
load_dotenv()

from services.resume_parser import parse_resume
from services.excel_reader import read_company_excel
from services.ai_engine import generate_cold_email
from services.email_sender import send_email
from pydantic import BaseModel
from typing import List, Dict, Any, Optional


app = FastAPI(title="Hunter AI Backend")

origins = [
    "http://localhost:5173",
    "http://localhost:3000",
    os.getenv("FRONTEND_URL", "http://localhost:5173"),  # Allow Render frontend
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

UPLOAD_DIR = "uploads"
os.makedirs(UPLOAD_DIR, exist_ok=True)

@app.get("/")
def read_root():
    return "AI Outreach Backend is running ๐Ÿš€"

@app.post("/upload")
async def upload_files(resume: UploadFile = File(...), company_excel: UploadFile = File(...)):
    resume_path = os.path.join(UPLOAD_DIR, resume.filename)
    excel_path = os.path.join(UPLOAD_DIR, company_excel.filename)
    
    with open(resume_path, "wb") as buffer:
        shutil.copyfileobj(resume.file, buffer)
        
    with open(excel_path, "wb") as buffer:
        shutil.copyfileobj(company_excel.file, buffer)
        
    resume_text = parse_resume(resume_path)
    companies = read_company_excel(excel_path)
    
    return {
        "status": "success",
        "resume_filename": resume.filename,
        "excel_filename": company_excel.filename,
        "resume_preview": resume_text[:500] if resume_text else "No text extracted",
        "companies_count": len(companies),
        "first_company_example": companies[0] if companies else None
    }

class GenerateRequest(BaseModel):
    resume_filename: str
    excel_filename: str

@app.post("/generate-emails")
async def generate_emails_endpoint(request: GenerateRequest):
    resume_path = os.path.join(UPLOAD_DIR, request.resume_filename)
    excel_path = os.path.join(UPLOAD_DIR, request.excel_filename)
    
    if not os.path.exists(resume_path) or not os.path.exists(excel_path):
        raise HTTPException(status_code=404, detail="Files not found")

    resume_text = parse_resume(resume_path)
    companies = read_company_excel(excel_path)
    
    results = []
    # Limit to first 5 for safety in MVP/Demo to avoid burning API quota or time
    for company in companies[:5]: 
        email = generate_cold_email(resume_text, company)
        results.append({
            "company": company.get("Company Name", "Unknown"),
            "email": email,
            "hr_email": company.get("Email", "") # Ensure we have the target email
        })
    return {"emails": results}

class SendEmailRequest(BaseModel):
    emails: List[Dict[str, str]]  # List of { "to": "...", "subject": "...", "body": "..." }
    smtp_email: str
    smtp_password: str
    resume_filename: Optional[str] = None

@app.post("/send-bulk-emails")
async def send_bulk_emails_endpoint(request: SendEmailRequest):
    results = []
    smtp_config = {"email": request.smtp_email, "password": request.smtp_password}
    
    # Construct attachment path if provided
    attachment_path = None
    if request.resume_filename:
        attachment_path = os.path.join(UPLOAD_DIR, request.resume_filename)
    
    print(f"๐Ÿš€ Starting bulk email send for {len(request.emails)} recipients...")
    if attachment_path:
        print(f"๐Ÿ“Ž Including attachment: {request.resume_filename}")
    
    for index, item in enumerate(request.emails):
        to_email = item.get("to")
        subject = item.get("subject")
        body = item.get("body")
        
        if to_email and subject and body:
            print(f"[{index+1}/{len(request.emails)}] Sending to {to_email}...")
            
            # Pass attachment_path to send_email
            success, message = send_email(to_email, subject, body, smtp_config, attachment_path)
            
            results.append({"to": to_email, "status": "sent" if success else "failed", "error": message})
            
            # Rate limiting: Sleep 2 seconds between emails to avoid spam filters
            if index < len(request.emails) - 1:
                time.sleep(2)
        else:
            results.append({"to": to_email, "status": "failed", "error": "Invalid data"})
            
    print("โœ… Bulk sending complete.")
    return {"results": results}

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=10000)