create-apppaaaul
Version:
Create projects as paaauldev would
125 lines (100 loc) • 3.74 kB
text/typescript
import { S3Client, PutObjectCommand, DeleteObjectCommand } from "@aws-sdk/client-s3";
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
// Configuración del cliente S3 para Cloudflare R2
const createS3Client = () => {
const endpoint = process.env.CLOUDFLARE_ENDPOINT;
const accessKeyId = process.env.CLOUDFLARE_ACCESS_KEY;
const secretAccessKey = process.env.CLOUDFLARE_SECRET_KEY;
if (!endpoint || !accessKeyId || !secretAccessKey) {
throw new Error("Cloudflare R2 environment variables are not properly configured");
}
return new S3Client({
region: "auto",
endpoint,
credentials: {
accessKeyId,
secretAccessKey,
},
});
};
export interface UploadUrlResponse {
presignedUrl: string;
publicUrl: string;
key: string;
}
export async function generateUploadUrl(
fileName: string,
fileType: string,
userId: string,
): Promise<UploadUrlResponse> {
const s3Client = createS3Client();
const bucketName = process.env.CLOUDFLARE_BUCKET;
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
if (!bucketName) {
throw new Error("CLOUDFLARE_BUCKET environment variable is not set");
}
if (!accountId) {
throw new Error("CLOUDFLARE_ACCOUNT_ID environment variable is not set");
}
// Generar nombre único para el archivo
const timestamp = Date.now();
const sanitizedFileName = fileName.replace(/[^a-zA-Z0-9.-]/g, "_");
const key = `tickets/${userId}/${timestamp.toString()}-${sanitizedFileName}`;
// Crear comando para subir archivo
const putCommand = new PutObjectCommand({
Bucket: bucketName,
Key: key,
ContentType: fileType,
});
// Generar URL presignada (válida por 5 minutos)
const presignedUrl = await getSignedUrl(s3Client, putCommand, {
expiresIn: 300, // 5 minutos
});
// URL pública del archivo (después de ser subido)
const publicDomain = process.env.CLOUDFLARE_PUBLIC_URL;
const publicUrl = publicDomain
? `${publicDomain}/${key}`
: `https://${bucketName}.${accountId}.r2.cloudflarestorage.com/${key}`;
return {
presignedUrl,
publicUrl,
key,
};
}
// Función para eliminar un archivo de R2
export async function deleteFile(key: string): Promise<void> {
const s3Client = createS3Client();
const bucketName = process.env.CLOUDFLARE_BUCKET;
if (!bucketName) {
throw new Error("CLOUDFLARE_BUCKET environment variable is not set");
}
const deleteCommand = new DeleteObjectCommand({
Bucket: bucketName,
Key: key,
});
await s3Client.send(deleteCommand);
}
// Función para extraer la key de una URL pública
export function extractKeyFromUrl(publicUrl: string): string | null {
try {
// Obtener el dominio público configurado
const publicDomain = process.env.CLOUDFLARE_PUBLIC_URL;
if (publicDomain && publicUrl.startsWith(publicDomain)) {
// URL con dominio personalizado: https://images.skinsbrain.com/tickets/user123/1234567890-image.jpg
return publicUrl.replace(`${publicDomain}/`, "");
}
// URL estándar de R2: https://bucket.accountid.r2.cloudflarestorage.com/tickets/user123/1234567890-image.jpg
const bucketName = process.env.CLOUDFLARE_BUCKET;
const accountId = process.env.CLOUDFLARE_ACCOUNT_ID;
if (bucketName && accountId) {
const standardDomain = `https://${bucketName}.${accountId}.r2.cloudflarestorage.com/`;
if (publicUrl.startsWith(standardDomain)) {
return publicUrl.replace(standardDomain, "");
}
}
return null;
} catch (error) {
console.error("Error extracting key from URL:", error);
return null;
}
}