Spaces:
Paused
Paused
| # Technical Architecture | |
| Complete technical documentation for the modern Job Apply AI React SaaS application. | |
| ## Overview | |
| The application uses a **monolithic architecture** with: | |
| - **Frontend**: React 18 + TypeScript + Vite | |
| - **Backend**: Flask REST API | |
| - **Communication**: JSON over HTTP | |
| - **State**: Zustand (frontend), Session (backend) | |
| - **Styling**: Tailwind CSS + Framer Motion | |
| ## Application Flow | |
| ``` | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| β USER BROWSER β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ | |
| β React App (Port 3000) β | |
| β βββ HomePage (landing page) β | |
| β βββ WorkflowPage (3-step wizard) β | |
| β βββ JobListPage (job browsing + selection) β | |
| β βββ SettingsModal (configuration) β | |
| β β | |
| β State: Zustand Store β | |
| β βββ jobs: Job[] β | |
| β βββ cvTemplate: CVTemplate β | |
| β βββ tailoringMode: 'local' | 'api' β | |
| β βββ notifications: Toast[] β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| β (REST API Calls) | |
| β (JSON requests/responses) | |
| β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| β FLASK SERVER (Port 5050) β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ | |
| β Route Handlers β | |
| β βββ GET /api/health β | |
| β βββ GET /api/config β | |
| β βββ POST /api/upload-cv β | |
| β βββ POST /api/search β | |
| β βββ GET /api/jobs β | |
| β βββ POST /api/generate-cv/<id> β | |
| β βββ POST /api/generate-all-cvs β | |
| β βββ GET /api/download/<filename> β | |
| β β | |
| β Business Logic β | |
| β βββ LinkedInScraper (job collection) β | |
| β βββ CVAnalyzer (skill extraction) β | |
| β βββ CVModifier (CV customization) β | |
| β β | |
| β Data Storage β | |
| β βββ .runtime/ (local filesystem) β | |
| β βββ uploads/cv_templates/ β | |
| β βββ uploads/cvs/generated_cvs/ β | |
| β βββ uploads/jobs/excel_exports/ β | |
| β βββ uploads/session_state/json_files/ β | |
| βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| ``` | |
| ## Component Hierarchy | |
| ``` | |
| App | |
| βββ HomePage | |
| β βββ Header | |
| β βββ HeroSection | |
| β βββ FeaturesGrid | |
| β βββ Footer | |
| β | |
| βββ WorkflowPage | |
| β βββ Header | |
| β βββ ProgressSteps | |
| β βββ CVUpload (step 1) | |
| β βββ JobSearch (step 2) | |
| β βββ ReviewSection (step 3) | |
| β | |
| βββ JobListPage | |
| β βββ JobListHeader | |
| β βββ SelectionControls | |
| β βββ JobCard (repeated) | |
| β β βββ JobHeader | |
| β β βββ SkillBadges | |
| β β βββ ExpandedDetails | |
| β β βββ GenerateButton | |
| β βββ BatchProgress (floating) | |
| β | |
| βββ SettingsModal | |
| β βββ TailoringModeSelector | |
| β βββ LLMProviderSelector | |
| β βββ AdvancedOptions | |
| β | |
| βββ Toast (notification) | |
| ``` | |
| ## Data Models | |
| ### Frontend (TypeScript) | |
| ```typescript | |
| interface Job { | |
| id: string; | |
| title: string; | |
| company: string; | |
| link: string; | |
| source: 'LinkedIn' | 'Indeed' | 'Other'; | |
| posted_days_ago: number; | |
| description: string; | |
| matched_skills: string[]; | |
| matched_categories: Record<string, string[]>; | |
| salary?: string; | |
| location?: string; | |
| } | |
| interface CVTemplate { | |
| filename: string; | |
| uploadedAt: string; | |
| size: number; | |
| content?: ArrayBuffer; | |
| } | |
| interface GeneratedCV { | |
| jobId: string; | |
| jobTitle: string; | |
| company: string; | |
| filename: string; | |
| url: string; | |
| generatedAt: string; | |
| status: 'success' | 'failed'; | |
| error?: string; | |
| } | |
| interface AppState { | |
| jobs: Job[]; | |
| cvTemplate: CVTemplate | null; | |
| tailoringMode: 'local' | 'api'; | |
| isSearching: boolean; | |
| isGenerating: boolean; | |
| selectedJobIds: Set<string>; | |
| batchProgress: BatchProgress; | |
| notification: Toast | null; | |
| // ... setters and methods | |
| } | |
| ``` | |
| ### Backend (Python/JSON) | |
| ```python | |
| # Job response from /api/search | |
| { | |
| "jobs": [ | |
| { | |
| "id": "job_0", | |
| "title": "Senior React Developer", | |
| "company": "Tech Corp", | |
| "link": "https://...", | |
| "source": "LinkedIn", | |
| "posted_days_ago": 3, | |
| "description": "Full job description...", | |
| "location": "San Francisco, CA", | |
| "matched_skills": ["React", "TypeScript", "REST API"], | |
| "matched_categories": { | |
| "Frameworks & Libraries": ["React"], | |
| "Programming Languages": ["TypeScript"], | |
| "Tools & Platforms": ["REST API"] | |
| } | |
| } | |
| ], | |
| "excel_file": "linkedin_jobs_2024-01-15_1705353600.xlsx" | |
| } | |
| # CV generation response from /api/generate-cv/<id> | |
| { | |
| "success": true, | |
| "filename": "CV_20240115_120530_TechCorp_ReactDeveloper.docx", | |
| "job_title": "Senior React Developer", | |
| "company": "Tech Corp", | |
| "message": "CV generated successfully" | |
| } | |
| ``` | |
| ## Request/Response Examples | |
| ### Upload CV | |
| **Request:** | |
| ``` | |
| POST /api/upload-cv | |
| Content-Type: multipart/form-data | |
| file: (binary .docx file) | |
| ``` | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "filename": "resume.docx", | |
| "message": "CV template uploaded successfully" | |
| } | |
| ``` | |
| ### Search Jobs | |
| **Request:** | |
| ```json | |
| POST /api/search | |
| { | |
| "keyword": "React Developer", | |
| "location": "San Francisco", | |
| "max_jobs": 10, | |
| "max_days_old": 14, | |
| "tailoring_mode": "local" | |
| } | |
| ``` | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "jobs": [/* job objects */], | |
| "excel_file": "linkedin_jobs_2024-01-15_1234567890.xlsx", | |
| "message": "Found 10 jobs" | |
| } | |
| ``` | |
| ### Generate All CVs | |
| **Request:** | |
| ```json | |
| POST /api/generate-all-cvs | |
| { | |
| "job_indices": [0, 2, 5] // null for all | |
| } | |
| ``` | |
| **Response:** | |
| ```json | |
| { | |
| "success": true, | |
| "successful": [ | |
| { | |
| "job_index": 0, | |
| "filename": "CV_..._TechCorp_ReactDev.docx", | |
| "job_title": "Senior React Developer", | |
| "company": "Tech Corp" | |
| } | |
| ], | |
| "failed": [ | |
| { | |
| "job_index": 2, | |
| "error": "File processing error", | |
| "job_title": "Mid-level Engineer" | |
| } | |
| ], | |
| "zip_filename": "CVs_20240115_120530.zip", | |
| "total_generated": 2, | |
| "total_failed": 1 | |
| } | |
| ``` | |
| ## State Management Flow | |
| ### Zustand Store Pattern | |
| ```typescript | |
| // 1. Define store with getter/setter/action methods | |
| export const useJobStore = create<AppState>()( | |
| persist((set, get) => ({ | |
| jobs: [], | |
| setJobs: (jobs) => set({ jobs }), | |
| addJob: (job) => set((state) => ({ | |
| jobs: [...state.jobs, job] | |
| })), | |
| toggleJobSelection: (jobId) => set((state) => ({ | |
| selectedJobIds: new Set(...) | |
| })), | |
| // ... more methods | |
| }), { | |
| name: 'job-apply-store', // localStorage key | |
| partialize: (state) => ({ // what to persist | |
| tailoringMode: state.tailoringMode, | |
| llmProvider: state.llmProvider, | |
| // Don't persist large data | |
| }) | |
| }) | |
| ); | |
| // 2. Use in components | |
| const MyComponent = () => { | |
| const { jobs, setJobs, isSearching } = useJobStore(); | |
| // Component re-renders when state changes | |
| }; | |
| // 3. Update state | |
| await handleSearch(); // calls setJobs() | |
| // Component automatically re-renders | |
| ``` | |
| ### Session Flow | |
| ``` | |
| 1. Browser β Upload CV β Flask saves to .runtime/uploads/ | |
| 2. Browser β Search β Flask queries LinkedIn | |
| 3. Flask β Processes results β Saves to .runtime/session_state/{uuid}.json | |
| 4. Browser β Get jobs from session | |
| 5. Browser β Generate CV β Flask reads job from session state | |
| 6. Flask β Modifies document β Saves to .runtime/uploads/cvs/ | |
| 7. Browser β Download CV | |
| ``` | |
| ## File System Structure | |
| ``` | |
| .runtime/ | |
| βββ uploads/ | |
| β βββ cv_template_1705353600.docx | |
| β β | |
| β βββ cvs/ | |
| β β βββ CV_20240115_120530_TechCorp_ReactDeveloper.docx | |
| β β βββ CV_20240115_120531_DataCorp_Engineer.docx | |
| β β βββ CVs_20240115_120530.zip | |
| β β | |
| β βββ jobs/ | |
| β β βββ linkedin_jobs_2024-01-15_1705353600.xlsx | |
| β β | |
| β βββ session_state/ | |
| β βββ c8f9d2e1-4b3c-5a7f-8e9c-2d4f6a8b9c1d.json | |
| ``` | |
| ## API Error Handling | |
| ### Client-Side | |
| ```typescript | |
| try { | |
| const result = await jobsAPI.searchJobs(filters); | |
| setJobs(result.jobs); | |
| } catch (error) { | |
| setNotification({ | |
| type: 'error', | |
| message: error.message | |
| }); | |
| } | |
| ``` | |
| ### Server-Side | |
| ```python | |
| @app.route('/api/search', methods=['POST']) | |
| def api_search_jobs(): | |
| try: | |
| # Validate input | |
| if not keyword: return jsonify({...}, 400) | |
| # Process | |
| jobs = scraper.scrape_job_listings(...) | |
| # Return | |
| return jsonify({'success': True, 'jobs': jobs}) | |
| except Exception as e: | |
| logger.error(str(e)) | |
| return jsonify({'success': False, 'error': str(e)}, 500) | |
| ``` | |
| ### Error Types | |
| | Type | HTTPCode | User Message | | |
| |------|----------|--------------| | |
| | Missing required field | 400 | "Keyword and location are required" | | |
| | Invalid file type | 400 | "Only .docx files are supported" | | |
| | File not found | 404 | "CV template not found" | | |
| | Server error | 500 | "An error occurred. Please try again" | | |
| ## Performance Considerations | |
| ### Frontend | |
| - **Code Splitting**: Route-based chunk splitting via Vite | |
| - **Lazy Loading**: Components load on demand | |
| - **Memoization**: React.memo for expensive components | |
| - **Debouncing**: Search input debounced | |
| - **CSS**: Tailwind purges unused styles | |
| ### Backend | |
| - **Batch Operations**: Process multiple CVs in single request | |
| - **Session Caching**: Job data cached in .runtime/ | |
| - **Connection Pooling**: Selenium reuses browser window | |
| - **Async**: Non-blocking operations where possible | |
| ### Network | |
| - **JSON": Compact data format vs XML/Form | |
| - **Compression**: Gzip enabled by default | |
| - **Caching": Etag headers for static assets | |
| - **Streaming**: Files streamed for download | |
| ## Security Measures | |
| ### Input Validation | |
| ```python | |
| # File type check | |
| if not file.filename.endswith('.docx'): | |
| return error | |
| # Size limit | |
| if file.size > 10_000_000: | |
| return error | |
| # Path traversal prevention | |
| if '..' in filename or '/' in filename: | |
| return error | |
| ``` | |
| ### Session Security | |
| ```python | |
| # Unique session key per run | |
| app.config['SESSION_COOKIE_NAME'] = f"job_apply_ai_{int(time.time())}" | |
| # Secure path storage | |
| _session_state_path(state_id) # Safe path construction | |
| # CORS configuration | |
| CORS(app, resources={r"/api/*":{"origins":"*"}}) | |
| ``` | |
| ### Data Protection | |
| - No passwords stored | |
| - No PII logged | |
| - Uploaded files deleted after processing (optional) | |
| - Session files cleaned up | |
| ## Scaling Architecture | |
| ### Horizontal Scaling | |
| ``` | |
| User β Load Balancer | |
| βββ Flask Server 1 β Shared Session Store (Redis) | |
| βββ Flask Server 2 β Shared File Storage (S3/NFS) | |
| βββ Flask Server 3 | |
| ``` | |
| ### Vertical Scaling | |
| ``` | |
| Single Machine | |
| βββ Increase Worker Processes | |
| βββ Add RAM for larger job batches | |
| βββ SSD for faster CV processing | |
| βββ Dedicated GPU for future AI tasks | |
| ``` | |
| ## Deployment Targets | |
| ### Development | |
| - Vite dev server on :3000 | |
| - Flask dev server on :5050 | |
| - Hot reload enabled | |
| ### Staging | |
| - Docker containers | |
| - Cloud platform (AWS/GCP/Azure) | |
| - Full testing suite | |
| ### Production | |
| - Built React app on :3000 (or CDN) | |
| - Gunicorn server on :5050 | |
| - Database for sessions | |
| - Cloud storage for files | |
| ## Future Architectural Improvements | |
| ### Phase 2 | |
| - [ ] Authentication/Authorization system | |
| - [ ] Database (PostgreSQL) for persistent storage | |
| - [ ] Redis for session caching and queue | |
| - [ ] Celery for async job processing | |
| - [ ] WebSocket for real-time progress | |
| ### Phase 3 | |
| - [ ] Microservices architecture | |
| - [ ] Kubernetes orchestration | |
| - [ ] Message queue (RabbitMQ) | |
| - [ ] API rate limiting | |
| - [ ] Advanced caching | |
| ### Phase 4 | |
| - [ ] GraphQL API option | |
| - [ ] Event streaming (Kafka) | |
| - [ ] Service mesh (Istio) | |
| - [ ] Distributed tracing | |
| - [ ] Advanced analytics | |
| ## Technology Decision Rationale | |
| | Choice | Why | | |
| |--------|-----| | |
| | React | Large ecosystem, component reusability, strong community | | |
| | Zustand | Simple, no boilerplate compared to Redux | | |
| | Tailwind | Fast development, consistent design system | | |
| | Framer Motion | Smooth animations, good perf, learning curve | | |
| | Vite | Fast builds, excellent DX, modern tooling | | |
| | Flask | Lightweight, Pythonic, good for API + rendering | | |
| | pandas | Data processing, Excel export | | |
| | Selenium | Web automation, JS-heavy site support | | |
| | spaCy | NLP, good performance, pre-trained models | | |
| ## Monitoring & Observability | |
| ### Logging | |
| ```python | |
| logger.info("Job search initiated") | |
| logger.warning("CV generation slow") | |
| logger.error("Scraping failed") | |
| ``` | |
| ### Metrics to Track | |
| - Page load times | |
| - API response times | |
| - CV generation duration | |
| - Success/failure rates | |
| - File upload sizes | |
| ### Debugging | |
| ``` | |
| Browser DevTools β Network β Check API responses | |
| Browser Console β Check React errors | |
| Flask Terminal β Check server logs | |
| Browser Storage β Check localStorage/state | |
| ``` | |
| --- | |
| This architecture provides a solid foundation for growth and future enhancements while maintaining simplicity and ease of development. | |