michangarrito/docs/02-integraciones/INT-011-storage-cloud.md
rckrdmrd 2c916e75e5 [SIMCO-V4] feat: Agregar documentación SaaS, ADRs e integraciones
Nuevas Épicas (MCH-029 a MCH-033):
- Infraestructura SaaS multi-tenant
- Auth Social (OAuth2)
- Auditoría Empresarial
- Feature Flags
- Onboarding Wizard

Nuevas Integraciones (INT-010 a INT-014):
- Email Providers (SendGrid, Mailgun, SES)
- Storage Cloud (S3, GCS, Azure)
- OAuth Social
- Redis Cache
- Webhooks Outbound

Nuevos ADRs (0004 a 0011):
- Notifications Realtime
- Feature Flags Strategy
- Storage Abstraction
- Webhook Retry Strategy
- Audit Log Retention
- Rate Limiting
- OAuth Social Implementation
- Email Multi-provider

Actualizados:
- MASTER_INVENTORY.yml
- CONTEXT-MAP.yml
- HERENCIA-SIMCO.md
- Mapas de documentación

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 01:43:15 -06:00

7.5 KiB

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