271 lines
6.3 KiB
TypeScript
271 lines
6.3 KiB
TypeScript
'use server';
|
|
import 'server-only';
|
|
import { createServerClient } from '@/utils/supabase';
|
|
import type { Result } from './index';
|
|
|
|
export type GetStorageProps = {
|
|
bucket: string;
|
|
url: string;
|
|
seconds?: number;
|
|
transform?: {
|
|
width?: number;
|
|
height?: number;
|
|
quality?: number;
|
|
format?: 'origin';
|
|
resize?: 'cover' | 'contain' | 'fill';
|
|
};
|
|
download?: boolean | string;
|
|
};
|
|
|
|
export type UploadStorageProps = {
|
|
bucket: string;
|
|
path: string;
|
|
file: File;
|
|
options?: {
|
|
cacheControl?: string;
|
|
upsert?: boolean;
|
|
contentType?: string;
|
|
};
|
|
};
|
|
|
|
export type ReplaceStorageProps = {
|
|
bucket: string;
|
|
prevPath: string;
|
|
file: File;
|
|
options?: {
|
|
cacheControl?: string;
|
|
upsert?: boolean;
|
|
contentType?: string;
|
|
};
|
|
};
|
|
|
|
export type resizeImageProps = {
|
|
file: File,
|
|
options?: {
|
|
maxWidth?: number,
|
|
maxHeight?: number,
|
|
quality?: number,
|
|
}
|
|
};
|
|
|
|
export const getSignedUrl = async ({
|
|
bucket,
|
|
url,
|
|
seconds = 3600,
|
|
transform = {},
|
|
download = false,
|
|
}: GetStorageProps): Promise<Result<string>> => {
|
|
try {
|
|
const supabase = await createServerClient();
|
|
const { data, error } = await supabase.storage
|
|
.from(bucket)
|
|
.createSignedUrl(url, seconds, {
|
|
download,
|
|
transform,
|
|
});
|
|
|
|
if (error) throw error;
|
|
if (!data?.signedUrl) throw new Error('No signed URL returned');
|
|
|
|
return { success: true, data: data.signedUrl };
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error:
|
|
error instanceof Error
|
|
? error.message
|
|
: 'Unknown error getting signed URL',
|
|
};
|
|
}
|
|
}
|
|
|
|
export const getPublicUrl = async ({
|
|
bucket,
|
|
url,
|
|
transform = {},
|
|
download = false,
|
|
}: GetStorageProps): Promise<Result<string>> => {
|
|
try {
|
|
const supabase = await createServerClient();
|
|
const { data } = supabase.storage
|
|
.from(bucket)
|
|
.getPublicUrl(url, {
|
|
download,
|
|
transform,
|
|
});
|
|
|
|
if (!data?.publicUrl) throw new Error('No public URL returned');
|
|
|
|
return { success: true, data: data.publicUrl };
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error:
|
|
error instanceof Error
|
|
? error.message
|
|
: 'Unknown error getting public URL',
|
|
};
|
|
}
|
|
}
|
|
|
|
export const uploadFile = async ({
|
|
bucket,
|
|
path,
|
|
file,
|
|
options = {},
|
|
}: UploadStorageProps): Promise<Result<string>> => {
|
|
try {
|
|
const supabase = await createServerClient();
|
|
const { data, error } = await supabase.storage
|
|
.from(bucket)
|
|
.upload(path, file, options);
|
|
|
|
if (error) throw error;
|
|
if (!data?.path) throw new Error('No path returned from upload');
|
|
|
|
return { success: true, data: data.path };
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error:
|
|
error instanceof Error ? error.message : 'Unknown error uploading file',
|
|
};
|
|
}
|
|
}
|
|
|
|
export const replaceFile = async ({
|
|
bucket,
|
|
prevPath,
|
|
file,
|
|
options = {},
|
|
}: ReplaceStorageProps): Promise<Result<string>> => {
|
|
try {
|
|
options.upsert = true;
|
|
const supabase = await createServerClient();
|
|
//const deleteFileData = await deleteFile({
|
|
//bucket,
|
|
//path: [...prevPath],
|
|
//});
|
|
const { data, error } = await supabase.storage
|
|
.from(bucket)
|
|
//.update(path, file, options);
|
|
.update(prevPath, file, options);
|
|
if (error) throw error;
|
|
if (!data?.path) throw new Error('No path returned from upload');
|
|
return { success: true, data: data.path };
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error:
|
|
error instanceof Error ? error.message : 'Unknown error replacing file',
|
|
};
|
|
}
|
|
};
|
|
|
|
// Add a helper to delete files
|
|
export const deleteFile = async ({
|
|
bucket,
|
|
path,
|
|
}: {
|
|
bucket: string;
|
|
path: string[];
|
|
}): Promise<Result<null>> => {
|
|
try {
|
|
const supabase = await createServerClient();
|
|
const { error } = await supabase.storage.from(bucket).remove(path);
|
|
|
|
if (error) throw error;
|
|
|
|
return { success: true, data: null };
|
|
} catch (error) {
|
|
return {
|
|
success: false,
|
|
error:
|
|
error instanceof Error ? error.message : 'Unknown error deleting file',
|
|
};
|
|
}
|
|
}
|
|
|
|
// Add a helper to list files in a bucket
|
|
export const listFiles = async ({
|
|
bucket,
|
|
path = '',
|
|
options = {},
|
|
}: {
|
|
bucket: string;
|
|
path?: string;
|
|
options?: {
|
|
limit?: number;
|
|
offset?: number;
|
|
sortBy?: { column: string; order: 'asc' | 'desc' };
|
|
};
|
|
}): Promise<Result<Array<{ name: string; id: string; metadata: unknown }>>> => {
|
|
try {
|
|
const supabase = await createServerClient();
|
|
const { data, error } = await supabase.storage
|
|
.from(bucket)
|
|
.list(path, options);
|
|
|
|
if (error) throw error;
|
|
if (!data) throw new Error('No data returned from list operation');
|
|
|
|
return { success: true, data };
|
|
} catch (error) {
|
|
console.error('Could not list files!', error);
|
|
return {
|
|
success: false,
|
|
error:
|
|
error instanceof Error ? error.message : 'Unknown error listing files',
|
|
};
|
|
}
|
|
}
|
|
|
|
export const resizeImage = async ({
|
|
file,
|
|
options = {},
|
|
}: resizeImageProps): Promise<File> => {
|
|
const {
|
|
maxWidth = 800,
|
|
maxHeight = 800,
|
|
quality = 0.8,
|
|
} = options;
|
|
return new Promise((resolve) => {
|
|
const reader = new FileReader();
|
|
reader.readAsDataURL(file);
|
|
reader.onload = (event) => {
|
|
const img = new Image();
|
|
img.src = event.target?.result as string;
|
|
img.onload = () => {
|
|
let width = img.width;
|
|
let height = img.height;
|
|
if (width > height) {
|
|
if (width > maxWidth) {
|
|
height = Math.round((height * maxWidth / width));
|
|
width = maxWidth;
|
|
}
|
|
} else if (height > maxHeight) {
|
|
width = Math.round((width * maxHeight / height));
|
|
height = maxHeight;
|
|
}
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = width;
|
|
canvas.height = height;
|
|
const ctx = canvas.getContext('2d');
|
|
ctx?.drawImage(img, 0, 0, width, height);
|
|
canvas.toBlob(
|
|
(blob) => {
|
|
if (!blob) return;
|
|
const resizedFile = new File([blob], file.name, {
|
|
type: 'imgage/jpeg',
|
|
lastModified: Date.now(),
|
|
});
|
|
resolve(resizedFile);
|
|
},
|
|
'image/jpeg',
|
|
quality
|
|
);
|
|
};
|
|
};
|
|
});
|
|
};
|