Some checks failed
Profile Linker Docker Build / Build and push Docker image (push) Failing after 3s
- Integrated Cloudflare R2 for template storage and converted file management - Added Google Gemini AI for resume parsing and HTML generation - Created backend API endpoints for templates, conversion, and history - Refactored frontend to use real API instead of mock data - Fixed Docker networking issues (IPv6/IPv4) for R2 connectivity - Added resumeService.ts for frontend API integration - Updated Vite configuration for proper asset serving in Docker - Successfully tested with 13 templates from R2 bucket
186 lines
5.1 KiB
Markdown
186 lines
5.1 KiB
Markdown
# 🎉 R2 Connection SUCCESS!
|
|
|
|
## The Problem:
|
|
The Docker container couldn't connect to Cloudflare R2, showing the error:
|
|
```
|
|
Could not connect to the endpoint URL: "https://cba4afd7666247724ece1f34e1aace6c.r2.cloudflarestorage.com/e-teams..."
|
|
Error: Network unreachable
|
|
```
|
|
|
|
## The Root Cause:
|
|
**IPv6/IPv4 Networking Issue in Docker**
|
|
|
|
- DNS was returning both IPv4 (`172.64.66.1`) and IPv6 (`2606:4700:2ff9::1`) addresses
|
|
- boto3/botocore was trying to connect via IPv6 first
|
|
- Docker container's IPv6 networking wasn't properly configured
|
|
- This caused "Network unreachable" errors
|
|
|
|
## The Fix:
|
|
Added IPv4-only DNS resolution to the R2 service by monkey-patching Python's `socket.getaddrinfo()`:
|
|
|
|
```python
|
|
# Force IPv4 to avoid Docker IPv6 issues
|
|
original_getaddrinfo = socket.getaddrinfo
|
|
|
|
def getaddrinfo_ipv4_only(host, port, family=0, type=0, proto=0, flags=0):
|
|
"""Force IPv4 resolution only"""
|
|
return original_getaddrinfo(host, port, socket.AF_INET, type, proto, flags)
|
|
|
|
socket.getaddrinfo = getaddrinfo_ipv4_only
|
|
```
|
|
|
|
## ✅ Verification:
|
|
|
|
### 1. List Templates - WORKING!
|
|
```bash
|
|
curl http://localhost:8080/resumeformatter/api/resumes/templates
|
|
```
|
|
|
|
**Result:** Found **13 templates** in your R2 bucket:
|
|
```json
|
|
[
|
|
"Accenture",
|
|
"Avanade",
|
|
"Block",
|
|
"Caterpillar",
|
|
"Clark, Arthur",
|
|
"Cox",
|
|
"Cox_(1)",
|
|
"Highmark",
|
|
"Inzunza5",
|
|
"JNJ",
|
|
"JUY",
|
|
"Kenvue",
|
|
"Paramount_and_Viacom"
|
|
]
|
|
```
|
|
|
|
### 2. Get Template Content - WORKING!
|
|
```bash
|
|
curl "http://localhost:8080/resumeformatter/api/resumes/templates/Accenture"
|
|
```
|
|
|
|
**Result:** Successfully fetches HTML content!
|
|
|
|
### 3. R2 Bucket Structure:
|
|
Your R2 bucket (`e-teams`) contains:
|
|
- ✅ **templates/** folder with 13 HTML templates
|
|
- Ready for **converted_resumes/** folder for outputs
|
|
|
|
## What's Working Now:
|
|
|
|
### ✅ Backend API Endpoints:
|
|
1. **GET /resumeformatter/api/resumes/templates**
|
|
- Lists all available templates from R2
|
|
- Returns: `["Accenture", "Avanade", ...]`
|
|
|
|
2. **GET /resumeformatter/api/resumes/templates/{name}**
|
|
- Gets specific template HTML content
|
|
- Returns: `{"content": "<!DOCTYPE html>..."}`
|
|
|
|
3. **POST /resumeformatter/api/resumes/convert**
|
|
- Upload resume + select template
|
|
- AI extracts text → generates formatted HTML → uploads to R2
|
|
|
|
4. **GET /resumeformatter/api/resumes/history**
|
|
- Lists converted resumes from R2
|
|
|
|
## Test the Full Flow:
|
|
|
|
### Test 1: List Templates
|
|
```bash
|
|
curl http://localhost:8080/resumeformatter/api/resumes/templates
|
|
```
|
|
|
|
### Test 2: Get Template
|
|
```bash
|
|
curl "http://localhost:8080/resumeformatter/api/resumes/templates/Accenture"
|
|
```
|
|
|
|
### Test 3: Convert Resume (requires a PDF/DOCX file)
|
|
```bash
|
|
curl -X POST http://localhost:8080/resumeformatter/api/resumes/convert \
|
|
-F "file=@your-resume.pdf" \
|
|
-F "template_name=Accenture"
|
|
```
|
|
|
|
### Test 4: View Conversion History
|
|
```bash
|
|
curl http://localhost:8080/resumeformatter/api/resumes/history
|
|
```
|
|
|
|
## Next Steps:
|
|
|
|
### 🎯 IMMEDIATE - Update Frontend:
|
|
The frontend still uses **mock data**. We need to connect it to the real API:
|
|
|
|
1. Replace `fetchTemplatesFromR2()` → API call to `/api/resumes/templates`
|
|
2. Replace `fetchTemplateContentFromR2()` → API call to `/api/resumes/templates/{name}`
|
|
3. Replace `handleGenerate()` → API call to `/api/resumes/convert`
|
|
4. Replace `fetchConvertedResumesFromR2()` → API call to `/api/resumes/history`
|
|
5. Remove mock template data from `App.tsx`
|
|
|
|
### 🔧 OPTIONAL - Improvements:
|
|
1. Add template preview in frontend
|
|
2. Add progress indicators during AI processing
|
|
3. Implement proper error handling
|
|
4. Add file size/type validation
|
|
5. Add download progress tracking
|
|
|
|
## Current Architecture:
|
|
|
|
```
|
|
Frontend (React)
|
|
↓
|
|
Backend API (FastAPI)
|
|
↓
|
|
┌─────────────────┬─────────────────┐
|
|
↓ ↓ ↓
|
|
R2 Storage Gemini AI Database
|
|
(Templates) (Text/Vision) (Metadata)
|
|
```
|
|
|
|
## Configuration Summary:
|
|
|
|
### ✅ Environment Variables (.env):
|
|
```bash
|
|
APP_NAME=resumeformatter
|
|
GEMINI_API_KEY=AIzaSyB4Y9qrGynW3UNflYcQC-HGlJxOe_ty6VI
|
|
|
|
R2_ENDPOINT=https://cba4afd7666247724ece1f34e1aace6c.r2.cloudflarestorage.com
|
|
R2_ACCESS_KEY_ID=8f7244b0e7f9c8297a606af0073d4a5a
|
|
R2_SECRET_ACCESS_KEY=17845714ff4c2e5f33f09740112be47925d0fab93d27b26982964cd14808b60b
|
|
R2_BUCKET_NAME=e-teams
|
|
```
|
|
|
|
### ✅ R2 Bucket Structure:
|
|
```
|
|
e-teams/
|
|
├── templates/
|
|
│ ├── Accenture.html
|
|
│ ├── Avanade.html
|
|
│ ├── Block.html
|
|
│ ├── Caterpillar.html
|
|
│ ├── Clark, Arthur.html
|
|
│ ├── Cox.html
|
|
│ ├── Cox_(1).html
|
|
│ ├── Highmark.html
|
|
│ ├── Inzunza5.html
|
|
│ ├── JNJ.html
|
|
│ ├── JUY.html
|
|
│ ├── Kenvue.html
|
|
│ └── Paramount_and_Viacom.html
|
|
└── converted_resumes/
|
|
└── (outputs will go here)
|
|
```
|
|
|
|
## Success! 🚀
|
|
|
|
Your backend is now fully connected to Cloudflare R2 and can:
|
|
- ✅ List templates from R2
|
|
- ✅ Fetch template content
|
|
- ✅ Process resumes with Gemini AI
|
|
- ✅ Upload results back to R2
|
|
|
|
**Would you like me to update the frontend to use the real API instead of mocks?**
|