Definir almacenamiento personalizado en FireCMS
FireCMS ofrece flexibilidad a la hora de integrar soluciones de almacenamiento personalizadas. Aunque viene con soporte integrado para Firebase Storage, es posible que desees definir tus propias soluciones de almacenamiento para manejar subidas de archivos, descargas y gestión de metadatos. Esta guía te ayudará a configurar una solución de almacenamiento personalizada.
Crear una fuente de almacenamiento personalizada
Sección titulada «Crear una fuente de almacenamiento personalizada»Una fuente de almacenamiento personalizada en FireCMS se define implementando la interfaz StorageSource.
Aquí hay una plantilla para crear una fuente de almacenamiento personalizada.
Atención: Este es un ejemplo de cómo puedes implementar y usar S3 directamente desde una fuente de almacenamiento personalizada. No es la forma preferida de usar S3, ya que se desaconseja consumirlo directamente desde el frontend. Como tendrás que insertar la API key y el secret en el frontend, lo cual no es seguro. En su lugar, podrías usar Amplify/Storage o una autenticación IAM personalizada.
A continuación una plantilla para crear una fuente de almacenamiento personalizada:
import {StorageSource, UploadFileProps, UploadFileResult, DownloadConfig} from "@firecms/core";import {S3Client, PutObjectCommand, GetObjectCommand} from "@aws-sdk/client-s3";import {getSignedUrl} from "@aws-sdk/s3-request-presigner";
export interface S3StorageSourceProps { apiKey: string; apiSecret: string; region: string; defaultBucket?: string;}
function initializeCustomClient({apiKey, apiSecret, region, defaultBucket}: S3StorageSourceProps) { const s3Client = new S3Client({region, credentials: {accessKeyId: apiKey, secretAccessKey: apiSecret}});
return { uploadFile: async (destinationPath: string, file: File, bucket?: string, metadata?: any) => { await s3Client.send(new PutObjectCommand({ Bucket: bucket || defaultBucket, Key: destinationPath, Body: file, ContentType: metadata?.contentType, Metadata: metadata })); return { path: destinationPath, bucket: bucket || defaultBucket || "" } }, getFile: async (path: string, bucket?: string) => { return await s3Client.send(new GetObjectCommand({ Bucket: bucket || defaultBucket || "", Key: path })); }, getDownloadURL: async (path: string, bucket?: string) => { const command = new GetObjectCommand({Bucket: bucket || defaultBucket, Key: path}); return await getSignedUrl(s3Client, command, {expiresIn: 3600}); }, getMetadata: async (path: string, bucket?: string) => { const s3Object = await s3Client.send(new GetObjectCommand({ Bucket: bucket || defaultBucket, Key: path })); return { bucket: (bucket || defaultBucket) ?? "", fullPath: path, name: path, size: s3Object.ContentLength || 0, contentType: s3Object.ContentType || "", customMetadata: s3Object.Metadata || {} } } };}
export function useCustomStorageSource(props: S3StorageSourceProps): StorageSource { const customClient = initializeCustomClient(props);
return { async uploadFile({file, fileName, path, metadata, bucket}: UploadFileProps): Promise<UploadFileResult> { const usedFilename = fileName ?? file.name; const destinationPath = `${path}/${usedFilename}`; return await customClient.uploadFile(destinationPath, file, bucket, metadata); },
async getFile(path: string, bucket?: string): Promise<File | null> { const targetBucket = bucket ?? props.defaultBucket; try { const fileData = await customClient.getFile(path, targetBucket); if (fileData && fileData.Body) { const byteArray = await fileData.Body.transformToByteArray(); const blob = new Blob([byteArray], {type: fileData.ContentType}); return new File([blob], path); } else { return null; } } catch (e) { return null; } },
async getDownloadURL(path: string, bucket?: string): Promise<DownloadConfig> { const targetBucket = bucket || props.defaultBucket; try { const url = await customClient.getDownloadURL(path, targetBucket); const metadata = await customClient.getMetadata(path, targetBucket); return {url, metadata}; } catch (e) { return {url: null, fileNotFound: true}; } } };}Usar la fuente de almacenamiento personalizada
Sección titulada «Usar la fuente de almacenamiento personalizada»Después de crear la fuente de almacenamiento personalizada, puedes usarla en tu aplicación FireCMS inicializándola en tu componente y pasándola al componente FireCMS.
Ejemplo de uso
Sección titulada «Ejemplo de uso»Aquí hay un ejemplo de cómo usar la fuente de almacenamiento personalizada en tu aplicación FireCMS
import React from "react";import "typeface-rubik";import "@fontsource/jetbrains-mono";import { FireCMS, ModeControllerProvider, Scaffold, AppBar, Drawer, NavigationRoutes, SideDialogs, SnackbarProvider, useBuildLocalConfigurationPersistence, useBuildModeController, useBuildNavigationController, useValidateAuthenticator, CenteredView, CircularProgressCenter} from "@firecms/core";import { productsCollection } from "./collections/products_collection";import { useCustomStorageSource, CustomStorageSourceProps } from "./hooks/useCustomStorageSource";
const customStorageConfig: CustomStorageSourceProps = { apiKey: "your-api-key", apiSecret: "your-api-secret", region: "your-region", defaultBucket: "your-bucket-name" // ... otras propiedades necesarias};
const CustomStorageApp: React.FC = () => { const name = "My Custom Storage FireCMS App";
const modeController = useBuildModeController(); const userConfigPersistence = useBuildLocalConfigurationPersistence(); const storageSource = useCustomStorageSource(customStorageConfig);
const navigationController = useBuildNavigationController({ collections: [productsCollection], });
return ( <SnackbarProvider> <ModeControllerProvider value={modeController}> <FireCMS navigationController={navigationController} userConfigPersistence={userConfigPersistence} storageSource={storageSource} > {({ context, loading }) => { if (loading || authLoading) { return <CircularProgressCenter size="large" />; }
if (!canAccessMainView) { return <CenteredView>{notAllowedError}</CenteredView>; }
return ( <Scaffold> <AppBar title={"My app"} /> <Drawer /> <NavigationRoutes /> <SideDialogs /> </Scaffold> ); }} </FireCMS> </ModeControllerProvider> </SnackbarProvider> );};
export default CustomStorageApp;Como este ejemplo usa AWS S3, también necesitarás habilitar CORS en el bucket, como se describe en la documentación de AWS - Configurar el uso compartido de recursos entre orígenes (CORS).
[ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "GET", "PUT", "POST", "DELETE" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [ "x-amz-server-side-encryption", "x-amz-request-id", "x-amz-id-2" ], "MaxAgeSeconds": 3000 }]Esta documentación proporciona una guía clara para definir soluciones de almacenamiento personalizadas en FireCMS. Sigue la plantilla para integrar otros servicios de almacenamiento según tus requisitos.