121 lines
3.4 KiB
TypeScript
121 lines
3.4 KiB
TypeScript
import { S3Client, PutObjectCommand, DeleteObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
|
|
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
|
|
|
|
// Initialize S3 client
|
|
const s3Client = new S3Client({
|
|
region: process.env.AWS_REGION || 'ir-thr-at1',
|
|
endpoint: process.env.AWS_S3_ENDPOINT,
|
|
credentials: {
|
|
accessKeyId: process.env.AWS_ACCESS_KEY_ID || '',
|
|
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || '',
|
|
},
|
|
});
|
|
|
|
const BUCKET_NAME = process.env.AWS_S3_BUCKET || '';
|
|
|
|
export interface UploadFileParams {
|
|
file: File;
|
|
key: string;
|
|
contentType?: string;
|
|
makePublic?: boolean; // Set to false for private files
|
|
}
|
|
|
|
/**
|
|
* Upload a file to S3
|
|
*/
|
|
export async function uploadFileToS3(params: UploadFileParams): Promise<string> {
|
|
const { file, key, contentType, makePublic = true } = params;
|
|
|
|
// Convert File to Buffer
|
|
const arrayBuffer = await file.arrayBuffer();
|
|
const buffer = Buffer.from(arrayBuffer);
|
|
|
|
const command = new PutObjectCommand({
|
|
Bucket: BUCKET_NAME,
|
|
Key: key,
|
|
Body: buffer,
|
|
...(makePublic && { ACL: "public-read" }), // Only set ACL if makePublic is true
|
|
ContentType: contentType || file.type,
|
|
});
|
|
|
|
await s3Client.send(command);
|
|
|
|
// Return the public URL if public, otherwise return the key
|
|
if (makePublic) {
|
|
return `https://${BUCKET_NAME}.s3.ir-thr-at1.arvanstorage.ir/${key}`;
|
|
} else {
|
|
return key; // Return just the key for private files
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete a file from S3
|
|
*/
|
|
export async function deleteFileFromS3(key: string): Promise<void> {
|
|
const command = new DeleteObjectCommand({
|
|
Bucket: BUCKET_NAME,
|
|
Key: key,
|
|
});
|
|
|
|
await s3Client.send(command);
|
|
}
|
|
|
|
/**
|
|
* Generate a presigned URL for uploading
|
|
*/
|
|
export async function getPresignedUploadUrl(key: string, contentType: string): Promise<string> {
|
|
const command = new PutObjectCommand({
|
|
Bucket: BUCKET_NAME,
|
|
Key: key,
|
|
ContentType: contentType,
|
|
});
|
|
|
|
const url = await getSignedUrl(s3Client, command, { expiresIn: 3600 }); // 1 hour
|
|
return url;
|
|
}
|
|
|
|
/**
|
|
* Generate a presigned URL for downloading/viewing a file
|
|
* @param key - The S3 object key
|
|
* @param expiresIn - Expiration time in seconds (default: 1 hour)
|
|
* @returns Pre-signed URL
|
|
*/
|
|
export async function getPresignedUrl(key: string, expiresIn: number = 3600): Promise<string> {
|
|
const command = new GetObjectCommand({
|
|
Bucket: BUCKET_NAME,
|
|
Key: key,
|
|
});
|
|
|
|
const url = await getSignedUrl(s3Client, command, { expiresIn });
|
|
return url;
|
|
}
|
|
|
|
/**
|
|
* Generate multiple presigned URLs for a list of keys
|
|
* @param keys - Array of S3 object keys
|
|
* @param expiresIn - Expiration time in seconds (default: 1 hour)
|
|
* @returns Map of key to pre-signed URL
|
|
*/
|
|
export async function getMultiplePresignedUrls(
|
|
keys: string[],
|
|
expiresIn: number = 3600
|
|
): Promise<Record<string, string>> {
|
|
const urlPromises = keys.map(async (key) => {
|
|
const url = await getPresignedUrl(key, expiresIn);
|
|
return [key, url];
|
|
});
|
|
|
|
const entries = await Promise.all(urlPromises);
|
|
return Object.fromEntries(entries);
|
|
}
|
|
|
|
/**
|
|
* Generate a unique key for file uploads
|
|
*/
|
|
export function generateFileKey(folder: string, filename: string): string {
|
|
const timestamp = Date.now();
|
|
const randomString = Math.random().toString(36).substring(2, 15);
|
|
const sanitizedFilename = filename.replace(/[^a-zA-Z0-9.-]/g, '_');
|
|
return `${folder}/${timestamp}-${randomString}-${sanitizedFilename}`;
|
|
}
|