A comprehensive backend system that streamlines job applications by leveraging AI to analyze CVs, generate tailored cover letters, and manage job application workflows with automated email submission.
- OAuth2 Authentication - Secure Google OAuth2 integration with token management
- Profile Management - Create and manage multiple professional profiles with CVs
- CV Storage - Secure S3-based CV storage with upload, download, and deletion
- AI Job Analysis - Claude AI integration for intelligent CV-job description matching
- Smart Cover Letter Generation - AI-powered personalized cover letters
- Automated Email Sending - Direct job application submission via Gmail API
- Application Tracking - Track application status (SENT, INTERVIEW, REJECTED, ACCEPTED, FOLLOW_UP)
- Spring Boot 3.x with modern Java practices
- JPA/Hibernate for robust data management
- AWS S3 integration for file storage
- Anthropic Claude AI for intelligent analysis
- Gmail API for automated email sending
- Scheduled token refresh to maintain OAuth2 sessions
- RESTful API design with comprehensive error handling
Built with production-grade resilience patterns to handle external service failures gracefully:
- Non-blocking email sending with custom thread pool (5-10 threads)
- Instant API responses while operations complete in background
- Configurable queue capacity (50 tasks) for load management
- Gmail API: 3 retries with exponential backoff (2s → 4s → 8s)
- Claude AI: 3 retries with exponential backoff (1s → 2s → 4s)
- Intelligent exception classification (retry transient failures only)
- Gmail Service Circuit:
- Opens after 50% failure rate (10 call sliding window)
- 30s recovery wait period
- Automatic half-open testing (3 calls)
- Claude AI Service Circuit:
- Opens after 60% failure rate (10 call sliding window)
- 60s recovery wait period (AI services need more time)
- Automatic half-open testing (2 calls)
- Fast-fail when services are down (no wasted retries)
- Automatic recovery detection
- Gmail Failures: Updates job application status to
FAILED_TO_SEND- Users see failed applications in dashboard
- Manual retry available when service recovers
- No silent failures
- AI Failures: Returns professional template cover letter
- Users can still submit applications
- Clear warning message about AI unavailability
- Customizable template response
- Circuit breaker health indicators via Spring Actuator
- Real-time circuit state monitoring
- Circuit breaker events tracking
- Detailed metrics for failure rates and response times
- Comprehensive logging
Application statuses include:
SENT- Successfully deliveredFAILED_TO_SEND- Delivery failed (circuit open or retries exhausted)INTERVIEW- Interview scheduledREJECTED- Application rejectedACCEPTED- Offer receivedFOLLOW_UP- Awaiting response
- Java 17 or higher
- Maven 3.6+
- PostgreSQL (or your preferred database)
- AWS Account (for S3)
- Google Cloud Console project (for OAuth2 and Gmail API)
- Anthropic API key (for Claude AI)
git clone https://github.com/oluwatimilehinawoniyi/paladin-be.git
cd paladin-beCreate application.yml in src/main/resources/:
spring:
datasource:
url: jdbc:postgresql://localhost:5432/paladin_db
username: your_db_username
password: your_db_password
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_CLIENT_SECRET}
redirect-uri: "{baseUrl}/oauth2/callback/google"
scope:
- email
- profile
- https://www.googleapis.com/auth/gmail.send
ai:
anthropic:
api-key: ${ANTHROPIC_API_KEY}
chat:
options:
model: claude-sonnet-4-5-20250929
aws:
access-key-id: ${AWS_ACCESS_KEY}
secret-access-key: ${AWS_SECRET_KEY}
s3:
bucket-name: ${AWS_S3_BUCKET}
region: ${AWS_REGION}
app:
frontend:
url: http://localhost:5173- Go to Google Cloud Console
- Create a new project or select existing
- Enable Google+ API and Gmail API
- Create OAuth 2.0 credentials
- Add authorized redirect URIs:
http://localhost:8080/oauth2/callback/google- Your production URL
- Create an S3 bucket
- Configure CORS policy:
[
{
"AllowedHeaders": ["*"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
"AllowedOrigins": ["*"],
"ExposeHeaders": []
}
]# Build the project
mvn clean install
# Run the application
mvn spring-boot:runThe API will be available at http://localhost:8080
GET /oauth2/authorization/googleGET /api/auth/mePOST /api/auth/logoutPOST /api/v1/profiles
Content-Type: multipart/form-data
{
"title": "Senior Software Engineer",
"summary": "Experienced developer...",
"skills": ["Java", "Spring Boot", "AWS"],
"file": <CV_FILE>
}GET /api/v1/profiles/meGET /api/v1/profiles/{profileId}PATCH /api/v1/profiles/{profileId}
Content-Type: application/json
{
"title": "Updated Title",
"summary": "Updated summary",
"skills": ["New", "Skills"]
}DELETE /api/v1/profiles/{profileId}POST /api/v1/cv/upload
Content-Type: multipart/form-data
{
"file": <CV_FILE>,
"profileId": "uuid"
}GET /api/v1/cv/{cvId}/downloadPUT /api/v1/cv/{cvId}
Content-Type: multipart/form-data
{
"file": <NEW_CV_FILE>
}DELETE /api/v1/cv/{cvId}POST /api/v1/job-applications/analyze-application
Content-Type: application/json
{
"profileId": "uuid",
"jobDescription": "Full job posting text..."
}Response:
{
"jobDetails": {
"company": "Tech Corp",
"position": "Senior Developer",
"jobEmail": "hr@techcorp.com"
},
"coverLetter": "Dear Hiring Manager...",
"matchAnalysis": {
"overallMatchPercentage": 85,
"strengths": ["Strong backend experience", "AWS expertise"],
"gaps": ["Limited frontend experience"],
"recommendation": "Highly recommended to apply",
"confidenceLevel": "High"
}
}POST /api/v1/job-applications/send
Content-Type: application/json
{
"profileId": "uuid",
"company": "Tech Corp",
"jobTitle": "Senior Developer",
"jobEmail": "hr@techcorp.com",
"subject": "Application for Senior Developer",
"bodyText": "Cover letter content..."
}GET /api/v1/job-applications/mePATCH /api/v1/job-applications/{applicationId}/status
Content-Type: application/json
"INTERVIEW"GET /api/v1/cover-letters/generate/{category}?candidateName=John&companyName=TechCorp&position=Developer
Categories: professional, enthusiastic, results, conversationalGET /api/v1/users/meDELETE /api/v1/users/meThis permanently deletes all user data including profiles, CVs, and applications
GET /actuator/healthResponse:
{
"status": "UP",
"components": {
"circuitBreakers": {
"status": "UP",
"details": {
"gmailService": {
"status": "UP",
"state": "CLOSED",
"failureRate": "0.0%",
"slowCallRate": "0.0%",
"bufferedCalls": 10,
"failedCalls": 0
},
"claudeAIService": {
"status": "UP",
"state": "CLOSED",
"failureRate": "0.0%",
"slowCallRate": "0.0%",
"bufferedCalls": 10,
"failedCalls": 0
}
}
},
"db": {
"status": "UP"
}
}
}Circuit Breaker States:
CLOSED- Normal operation (service healthy) ✓OPEN- Service failing, fast-failing requests ⊘HALF_OPEN- Testing recovery after wait periodFORCED_OPEN- Manually disabled for maintenance
GET /actuator/circuitbreakereventsShows recent circuit breaker events including:
- State transitions (CLOSED → OPEN → HALF_OPEN → CLOSED)
- Success and failure events
- Error details and timestamps
Example Response:
{
"circuitBreakerEvents": [
{
"circuitBreakerName": "gmailService",
"type": "SUCCESS",
"creationTime": "2024-01-08T10:30:15.123Z"
},
{
"circuitBreakerName": "claudeAIService",
"type": "ERROR",
"creationTime": "2024-01-08T10:31:20.456Z",
"errorMessage": "Claude AI API call failed"
}
]
}GET /actuator/metrics/resilience4j.circuitbreaker.callsProvides detailed metrics for circuit breaker calls:
- Total calls
- Successful calls
- Failed calls
- Call duration percentiles
┌─────────────────┐
│ Frontend │
│ (SvelteKit) │
└────────┬────────┘
│
│ REST API
│
┌────────▼───────────────────────────────────┐
│ Spring Boot Backend │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Controllers │ │ Services │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ ┌──────▼─────────────────▼───────┐ │
│ │ Business Logic │ │
│ │ - OAuth2 Management │ │
│ │ - File Upload/Download │ │
│ │ - AI Analysis │ │
│ │ - Email Automation │ │
│ └──────┬─────────────────────────┘ │
│ │ │
│ ┌──────▼──────────┐ ┌─────────────┐ │
│ │ Repositories │ │ Mappers │ │
│ └──────┬──────────┘ └─────────────┘ │
└─────────┼──────────────────────────────────┘
│
┌─────┴─────────────────────────┐
│ │
┌───▼────┐ ┌──────┐ ┌──────────┐ ┌▼─────┐
│ DB │ │ S3 │ │ Gmail API│ │Claude│
└────────┘ └──────┘ └──────────┘ └──────┘
- OAuth2 Authentication with Google
- Session Management with automatic token refresh
- User Authorization - Users can only access their own data
- Secure File Storage in AWS S3
- CORS Configuration for frontend integration
- CSRF Protection disabled for API-only backend
- HTTPS Strict Transport Security headers
- OAuth2 user information
- Token management (access token, refresh token, expiry)
- Relationship to profiles
- Multiple profiles per user
- Title, summary, skills
- One-to-one relationship with CV
- Stored in S3
- Metadata in database (filename, URL, size, content type)
- Track all applications
- Status management
- Linked to profiles
FROM eclipse-temurin:17-jdk-alpine
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]# Build
docker build -t paladin-be .
# Run
docker run -p 8080:8080 \
-e GOOGLE_CLIENT_ID=your_client_id \
-e GOOGLE_CLIENT_SECRET=your_secret \
-e AWS_ACCESS_KEY=your_key \
-e AWS_SECRET_KEY=your_secret \
-e ANTHROPIC_API_KEY=your_key \
paladin-backend- Fork the repository
- Create a feature branch (
git checkout -b feature/AmazingFeature) - Commit changes (
git commit -m 'Add AmazingFeature') - Push to branch (
git push origin feature/AmazingFeature) - Open a Pull Request
Your Name - Oluwatimilehin Awoniyi