| id |
type |
title |
provider |
status |
integration_type |
created_at |
updated_at |
simco_version |
tags |
| INT-011 |
Integration |
Storage Cloud |
AWS S3/Cloudflare R2/MinIO |
Planificado |
storage |
2026-01-10 |
2026-01-10 |
4.0.1 |
| storage |
| s3 |
| r2 |
| files |
| multi-cloud |
|
INT-011: Storage Cloud
Metadata
| Campo |
Valor |
| Codigo |
INT-011 |
| Proveedor |
AWS S3, Cloudflare R2, MinIO |
| Tipo |
Almacenamiento |
| Estado |
Planificado |
| Multi-tenant |
Si |
| Epic Relacionada |
MCH-029 |
| Owner |
Backend Team |
1. Descripcion
Sistema de almacenamiento abstracto que soporta multiples proveedores cloud (S3, R2, MinIO). Permite subir archivos como imagenes de productos, facturas y documentos con URLs firmadas y control de acceso por tenant.
Casos de uso principales:
- Imagenes de productos
- Fotos de perfil de usuario
- Facturas y recibos PDF
- Respaldos de datos
- Assets de la tienda (logo, banner)
2. Credenciales Requeridas
Variables de Entorno
| Variable |
Descripcion |
Tipo |
Obligatorio |
STORAGE_PROVIDER |
Proveedor (s3/r2/minio) |
string |
SI |
S3_BUCKET |
Nombre del bucket |
string |
SI |
S3_REGION |
Region AWS |
string |
SI (S3) |
S3_ACCESS_KEY |
Access Key |
string |
SI |
S3_SECRET_KEY |
Secret Key |
string |
SI |
S3_ENDPOINT |
Endpoint custom (R2/MinIO) |
string |
NO |
STORAGE_CDN_URL |
URL de CDN (opcional) |
string |
NO |
Ejemplo de .env
# Storage Configuration
STORAGE_PROVIDER=s3
# AWS S3
S3_BUCKET=michangarrito-uploads
S3_REGION=us-east-1
S3_ACCESS_KEY=AKIAXXXXXXXXXXXXXXXX
S3_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Cloudflare R2 (alternativa)
# STORAGE_PROVIDER=r2
# S3_ENDPOINT=https://xxx.r2.cloudflarestorage.com
# S3_ACCESS_KEY=xxx
# S3_SECRET_KEY=xxx
# MinIO (desarrollo)
# STORAGE_PROVIDER=minio
# S3_ENDPOINT=http://localhost:9000
# S3_ACCESS_KEY=minioadmin
# S3_SECRET_KEY=minioadmin
3. SDK Utilizado
AWS SDK v3
import { S3Client, PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
const s3Client = new S3Client({
region: process.env.S3_REGION,
endpoint: process.env.S3_ENDPOINT,
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_SECRET_KEY,
},
});
Operaciones
| Operacion |
Comando SDK |
Descripcion |
| Upload |
PutObjectCommand |
Subir archivo |
| Download |
GetObjectCommand |
Descargar archivo |
| Delete |
DeleteObjectCommand |
Eliminar archivo |
| List |
ListObjectsV2Command |
Listar archivos |
| Signed URL |
getSignedUrl |
URL temporal |
4. Limites por Plan
| Plan |
Almacenamiento |
Archivos Max |
Tamano Max/Archivo |
| Basic |
1 GB |
500 |
5 MB |
| Pro |
10 GB |
5,000 |
25 MB |
| Enterprise |
100 GB |
50,000 |
100 MB |
MIME Types Permitidos
const ALLOWED_MIME_TYPES = [
'image/jpeg',
'image/png',
'image/webp',
'image/gif',
'application/pdf',
'application/vnd.ms-excel',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
];
5. Manejo de Errores
| Codigo |
Descripcion |
Accion |
Retry |
| 400 |
Archivo invalido |
Validar MIME/size |
NO |
| 403 |
Sin permisos |
Verificar policy |
NO |
| 404 |
Archivo no existe |
- |
NO |
| 413 |
Archivo muy grande |
Reducir tamano |
NO |
| 500 |
Error de storage |
Retry |
SI |
| 503 |
Servicio no disponible |
Retry con backoff |
SI |
6. Estructura de Archivos
Path Convention
{bucket}/
├── tenants/
│ └── {tenant_id}/
│ ├── products/
│ │ └── {product_id}/
│ │ ├── main.jpg
│ │ └── thumb.jpg
│ ├── invoices/
│ │ └── {year}/{month}/
│ │ └── INV-{id}.pdf
│ ├── users/
│ │ └── {user_id}/
│ │ └── avatar.jpg
│ └── assets/
│ ├── logo.png
│ └── banner.jpg
└── public/
└── templates/
Ejemplo de Path
tenants/550e8400-e29b-41d4-a716-446655440000/products/abc123/main.jpg
7. URLs Firmadas
Generacion
async function getSignedUploadUrl(
tenantId: string,
filename: string,
contentType: string,
expiresIn: number = 3600
): Promise<string> {
const key = `tenants/${tenantId}/uploads/${Date.now()}-${filename}`;
const command = new PutObjectCommand({
Bucket: process.env.S3_BUCKET,
Key: key,
ContentType: contentType,
});
return getSignedUrl(s3Client, command, { expiresIn });
}
Expiracion por Tipo
| Tipo |
Expiracion |
Uso |
| Upload |
1 hora |
Subida de archivos |
| Download publico |
24 horas |
Imagenes de productos |
| Download privado |
15 minutos |
Facturas, documentos |
8. Multi-tenant
Aislamiento
- Cada tenant tiene su propio directorio
- RLS en tabla
files por tenant_id
- Bucket policies restringen acceso
Almacenamiento de Metadata
CREATE SCHEMA IF NOT EXISTS storage;
CREATE TABLE storage.files (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
folder_id UUID REFERENCES storage.folders(id),
name VARCHAR(255) NOT NULL,
original_name VARCHAR(255) NOT NULL,
path VARCHAR(1000) NOT NULL,
size_bytes BIGINT NOT NULL,
mime_type VARCHAR(100) NOT NULL,
provider VARCHAR(20) NOT NULL,
url TEXT,
is_public BOOLEAN DEFAULT false,
metadata JSONB,
created_by UUID,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
deleted_at TIMESTAMP WITH TIME ZONE
);
CREATE TABLE storage.folders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL,
parent_id UUID REFERENCES storage.folders(id),
name VARCHAR(255) NOT NULL,
path VARCHAR(1000) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE TABLE storage.storage_usage (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID UNIQUE NOT NULL,
bytes_used BIGINT DEFAULT 0,
files_count INTEGER DEFAULT 0,
last_calculated_at TIMESTAMP WITH TIME ZONE
);
9. Testing
MinIO para Desarrollo
# docker-compose.yml
services:
minio:
image: minio/minio:latest
ports:
- "9000:9000"
- "9001:9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
command: server /data --console-address ":9001"
volumes:
- minio_data:/data
Test de Conexion
# Listar buckets
aws s3 ls --endpoint-url http://localhost:9000
# Subir archivo
aws s3 cp test.jpg s3://michangarrito-uploads/test/ --endpoint-url http://localhost:9000
10. Monitoreo
Metricas
| Metrica |
Descripcion |
Alerta |
| storage_bytes_total |
Bytes usados |
> 80% plan |
| storage_uploads_total |
Uploads exitosos |
- |
| storage_failures_total |
Uploads fallidos |
> 5% |
| storage_signed_urls_total |
URLs generadas |
- |
11. Referencias
Ultima actualizacion: 2026-01-10
Autor: Backend Team