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
151 lines
4.1 KiB
TypeScript
151 lines
4.1 KiB
TypeScript
/**
|
|
* Resume API Service
|
|
* Handles all API calls to the backend for resume processing
|
|
*/
|
|
|
|
const APP_NAME = import.meta.env.VITE_APP_NAME || 'resumeformatter';
|
|
const API_BASE_URL = `/${APP_NAME}/api/resumes`;
|
|
|
|
export interface ConvertedFile {
|
|
id: string;
|
|
name: string;
|
|
url: string;
|
|
size: number;
|
|
lastModified: string;
|
|
timestamp: Date;
|
|
}
|
|
|
|
export interface ConvertResponse {
|
|
success: boolean;
|
|
html_url: string;
|
|
html_content: string;
|
|
message: string;
|
|
}
|
|
|
|
/**
|
|
* Fetch list of available templates from R2
|
|
*/
|
|
export const fetchTemplates = async (): Promise<string[]> => {
|
|
console.log('Fetching templates from API...');
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/templates`);
|
|
if (!response.ok) {
|
|
throw new Error(`API error: ${response.status}`);
|
|
}
|
|
const data = await response.json();
|
|
console.log('Templates loaded:', data);
|
|
return data;
|
|
} catch (error) {
|
|
console.error('Error fetching templates:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Fetch HTML content of a specific template
|
|
*/
|
|
export const fetchTemplateContent = async (templateName: string): Promise<string> => {
|
|
console.log(`Fetching template content for: ${templateName}`);
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/templates/${encodeURIComponent(templateName)}`);
|
|
if (!response.ok) {
|
|
throw new Error(`API error: ${response.status}`);
|
|
}
|
|
const data = await response.json();
|
|
console.log('Template content loaded');
|
|
return data.content;
|
|
} catch (error) {
|
|
console.error('Error fetching template content:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Convert a resume file using the specified template
|
|
*/
|
|
export const convertResume = async (
|
|
file: File,
|
|
templateName: string,
|
|
onProgress?: (message: string) => void
|
|
): Promise<ConvertResponse> => {
|
|
console.log(`Converting resume: ${file.name} with template: ${templateName}`);
|
|
|
|
try {
|
|
// Create form data
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
formData.append('template_name', templateName);
|
|
|
|
onProgress?.('Uploading file to server...');
|
|
|
|
// Send request
|
|
const response = await fetch(`${API_BASE_URL}/convert`, {
|
|
method: 'POST',
|
|
body: formData,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
const errorData = await response.json().catch(() => ({ detail: 'Unknown error' }));
|
|
throw new Error(errorData.detail || `API error: ${response.status}`);
|
|
}
|
|
|
|
onProgress?.('Processing complete!');
|
|
|
|
const data = await response.json();
|
|
console.log('Conversion complete:', data);
|
|
return data;
|
|
} catch (error) {
|
|
console.error('Error converting resume:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Fetch list of converted resumes from R2
|
|
*/
|
|
export const fetchConversionHistory = async (limit: number = 50): Promise<ConvertedFile[]> => {
|
|
console.log('Fetching conversion history from API...');
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/history?limit=${limit}`);
|
|
if (!response.ok) {
|
|
throw new Error(`API error: ${response.status}`);
|
|
}
|
|
const data = await response.json();
|
|
|
|
// Transform data to match ConvertedFile interface
|
|
const files: ConvertedFile[] = data.map((file: any) => ({
|
|
id: file.id,
|
|
name: file.name,
|
|
url: file.url,
|
|
size: file.size,
|
|
lastModified: file.lastModified,
|
|
timestamp: new Date(file.lastModified),
|
|
}));
|
|
|
|
console.log('Conversion history loaded:', files.length, 'files');
|
|
return files;
|
|
} catch (error) {
|
|
console.error('Error fetching conversion history:', error);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Get download URL for a specific file
|
|
*/
|
|
export const getDownloadUrl = async (fileKey: string): Promise<string> => {
|
|
console.log(`Getting download URL for: ${fileKey}`);
|
|
try {
|
|
const response = await fetch(`${API_BASE_URL}/download/${encodeURIComponent(fileKey)}`);
|
|
if (!response.ok) {
|
|
throw new Error(`API error: ${response.status}`);
|
|
}
|
|
const data = await response.json();
|
|
console.log('Download URL retrieved');
|
|
return data.url;
|
|
} catch (error) {
|
|
console.error('Error getting download URL:', error);
|
|
throw error;
|
|
}
|
|
};
|