This commit is contained in:
pradeepkumar
2026-01-24 21:06:13 +05:30
parent 0f84e3d4d7
commit eba83e2961
9 changed files with 585 additions and 26 deletions

View File

@@ -0,0 +1,151 @@
import api from './api';
export interface UploadedFile {
id: string;
name: string;
size: number;
type: string;
url: string;
uploadedAt: string;
}
export interface UploadProgress {
loaded: number;
total: number;
percentage: number;
}
interface PresignedUrlResponse {
uploadUrl: string;
publicUrl: string;
key: string;
}
type UploadFolder = 'avatars' | 'documents' | 'properties' | 'verification';
class UploadService {
private basePath = '/upload';
/**
* Get presigned URL from backend
*/
private async getPresignedUrl(
filename: string,
contentType: string,
folder: UploadFolder = 'documents'
): Promise<PresignedUrlResponse> {
const response = await api.post<{ data: PresignedUrlResponse }>(
`${this.basePath}/presigned-url`,
{
filename,
contentType,
folder,
}
);
return response.data.data;
}
/**
* Upload file directly to S3 using presigned URL
*/
private async uploadToS3(
uploadUrl: string,
file: File,
onProgress?: (progress: UploadProgress) => void
): Promise<void> {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable && onProgress) {
const percentage = Math.round((event.loaded * 100) / event.total);
onProgress({
loaded: event.loaded,
total: event.total,
percentage,
});
}
});
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve();
} else {
reject(new Error(`Upload failed with status ${xhr.status}`));
}
});
xhr.addEventListener('error', () => {
reject(new Error('Upload failed'));
});
xhr.open('PUT', uploadUrl);
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);
});
}
/**
* Upload a single file to S3
*/
async uploadFile(
file: File,
folder: UploadFolder = 'documents',
onProgress?: (progress: UploadProgress) => void
): Promise<UploadedFile> {
// Step 1: Get presigned URL from backend
const { uploadUrl, publicUrl, key } = await this.getPresignedUrl(
file.name,
file.type,
folder
);
// Step 2: Upload directly to S3
await this.uploadToS3(uploadUrl, file, onProgress);
// Step 3: Return uploaded file info
return {
id: key, // Use the S3 key as the ID
name: file.name,
size: file.size,
type: file.type,
url: publicUrl,
uploadedAt: new Date().toISOString(),
};
}
/**
* Upload multiple files to S3
*/
async uploadFiles(
files: File[],
folder: UploadFolder = 'documents',
onProgress?: (fileIndex: number, progress: UploadProgress) => void
): Promise<UploadedFile[]> {
const uploadedFiles: UploadedFile[] = [];
for (let i = 0; i < files.length; i++) {
const file = files[i];
const uploadedFile = await this.uploadFile(
file,
folder,
onProgress ? (progress) => onProgress(i, progress) : undefined
);
uploadedFiles.push(uploadedFile);
}
return uploadedFiles;
}
/**
* Delete a file from S3
*/
async deleteFile(fileKey: string): Promise<void> {
// The key needs to be URL encoded for path parameter
const encodedKey = encodeURIComponent(fileKey);
await api.delete(`${this.basePath}/${encodedKey}`);
}
}
export const uploadService = new UploadService();