store/resources/js/lib/s3-upload.ts
Yoga Pangestu d02af40836
Some checks failed
linter / quality (push) Waiting to run
tests / ci (8.3) (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
feat: implement presigned upload functionality for S3 in PresignedUploadController; update media upload handling across various components to support S3 keys and improve user experience
2026-07-02 14:28:51 +07:00

60 lines
1.7 KiB
TypeScript

import { apiFetch } from '@/lib/api';
export type PresignedUploadResponse = {
key: string;
url: string;
expires_at: string;
};
export async function getPresignedUploadUrl(
filename: string,
mimeType: string,
): Promise<PresignedUploadResponse> {
return apiFetch<PresignedUploadResponse>('/admin/media/presign', {
method: 'POST',
body: JSON.stringify({ filename, mime_type: mimeType }),
});
}
export async function uploadFileToS3(
presignedUrl: string,
file: File,
onProgress?: (percent: number) => void,
): Promise<void> {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (event) => {
if (event.lengthComputable && onProgress) {
onProgress(Math.round((event.loaded / event.total) * 100));
}
});
xhr.addEventListener('load', () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve();
} else {
reject(new Error(`Upload gagal (HTTP ${xhr.status})`));
}
});
xhr.addEventListener('error', () => reject(new Error('Upload gagal. Periksa koneksi internet Anda.')));
xhr.addEventListener('abort', () => reject(new Error('Upload dibatalkan.')));
xhr.open('PUT', presignedUrl);
xhr.setRequestHeader('Content-Type', file.type);
xhr.send(file);
});
}
export async function uploadFileAndGetKey(
file: File,
onProgress?: (percent: number) => void,
): Promise<string> {
const { key, url } = await getPresignedUploadUrl(file.name, file.type);
await uploadFileToS3(url, file, onProgress);
return key;
}