57 lines
1.7 KiB
TypeScript
57 lines
1.7 KiB
TypeScript
'use client';
|
|
|
|
import * as storage from '@/lib/storage/storage.external';
|
|
import { StorageProvider } from '@/lib/storage/storage.interface';
|
|
import { wrap } from '@/utils/types/results';
|
|
import { z } from 'zod';
|
|
|
|
export const FileUploadResp = z.object({
|
|
signedUrl: z.string(),
|
|
key: z.string(),
|
|
});
|
|
export type FileUploadResp = z.infer<StorageProvider>;
|
|
|
|
async function hashFile(file: File): Promise<string> {
|
|
const buffer = await file.arrayBuffer();
|
|
const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
|
|
const hex = Array.from(new Uint8Array(hashBuffer))
|
|
.map((b) => b.toString(16).padStart(2, '0'))
|
|
.join('');
|
|
const ext = file.name.split('.').pop();
|
|
return ext ? `${hex}.${ext}` : hex;
|
|
}
|
|
|
|
export const uploadFile = wrap(async (file: File) => {
|
|
const fileKey = await hashFile(file);
|
|
|
|
const existsResult = await storage.checkExists(fileKey);
|
|
if (existsResult) {
|
|
const presignedUrl = await storage.getSignedUrl(fileKey);
|
|
if (!presignedUrl.ok) {
|
|
throw new Error('Failed to retrieve file URL');
|
|
}
|
|
return { signedUrl: presignedUrl.value, key: fileKey };
|
|
}
|
|
|
|
const result = await storage.getPutUrl(fileKey, file.type);
|
|
if (!result.ok) {
|
|
throw new Error('File upload failed');
|
|
}
|
|
|
|
const response = await fetch(result.value, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': file.type },
|
|
body: file,
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Failed to upload file');
|
|
}
|
|
|
|
const presignedUrl = await storage.getSignedUrl(fileKey);
|
|
if (!presignedUrl.ok) {
|
|
throw new Error('Failed to retrieve file URL');
|
|
}
|
|
return { signedUrl: presignedUrl.value, key: fileKey };
|
|
});
|