- HERENCIA-SIMCO.md actualizado con directivas v3.7 y v3.8 - Actualizaciones de configuracion Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
275 lines
7.5 KiB
Markdown
275 lines
7.5 KiB
Markdown
---
|
|
id: "SAAS-011"
|
|
title: "Storage"
|
|
type: "Module"
|
|
status: "Published"
|
|
priority: "P1"
|
|
module: "storage"
|
|
version: "1.0.0"
|
|
created_date: "2026-01-07"
|
|
updated_date: "2026-01-10"
|
|
---
|
|
|
|
# SAAS-011: Storage
|
|
|
|
## Metadata
|
|
- **Codigo:** SAAS-011
|
|
- **Modulo:** Storage
|
|
- **Prioridad:** P1
|
|
- **Estado:** Implementado
|
|
- **Fase:** 3 - Features Core
|
|
|
|
## Descripcion
|
|
|
|
Sistema de almacenamiento de archivos: upload/download con presigned URLs, organizacion por tenant, limites por plan, y soporte multi-provider (S3, R2, MinIO).
|
|
|
|
## Objetivos
|
|
|
|
1. Upload seguro con presigned URLs
|
|
2. Organizacion por tenant
|
|
3. Limites de almacenamiento por plan
|
|
4. Soporte multi-provider
|
|
5. Procesamiento de imagenes
|
|
|
|
## Implementacion
|
|
|
|
### Estado de Entregables
|
|
|
|
| Entregable | Estado | Archivo |
|
|
|------------|--------|---------|
|
|
| storage.module.ts | Implementado | `apps/backend/src/modules/storage/storage.module.ts` |
|
|
| storage.controller.ts | Implementado | `apps/backend/src/modules/storage/storage.controller.ts` |
|
|
| storage.service.ts | Implementado | `apps/backend/src/modules/storage/services/storage.service.ts` |
|
|
| s3.provider.ts | Implementado | `apps/backend/src/modules/storage/providers/s3.provider.ts` |
|
|
| DDL storage schema | Implementado | `apps/database/ddl/schemas/storage/` |
|
|
| Frontend components | Implementado | `apps/frontend/src/components/storage/` |
|
|
| StoragePage | Implementado | `apps/frontend/src/pages/dashboard/StoragePage.tsx` |
|
|
|
|
### Archivos Creados
|
|
|
|
**Backend:**
|
|
```
|
|
apps/backend/src/modules/storage/
|
|
├── storage.module.ts # NestJS module
|
|
├── storage.controller.ts # REST endpoints (8 endpoints)
|
|
├── index.ts # Module barrel export
|
|
├── entities/
|
|
│ ├── file.entity.ts # FileEntity with enums
|
|
│ ├── pending-upload.entity.ts
|
|
│ ├── storage-usage.entity.ts
|
|
│ └── index.ts
|
|
├── dto/
|
|
│ ├── storage.dto.ts # Request/Response DTOs
|
|
│ └── index.ts
|
|
├── providers/
|
|
│ ├── s3.provider.ts # S3/R2/MinIO provider
|
|
│ └── index.ts
|
|
└── services/
|
|
├── storage.service.ts # Business logic
|
|
└── index.ts
|
|
```
|
|
|
|
**Database:**
|
|
```
|
|
apps/database/ddl/schemas/storage/
|
|
└── tables/
|
|
├── 01-files.sql # files, pending_uploads tables
|
|
└── 02-storage-usage.sql # usage table, triggers, functions
|
|
```
|
|
|
|
**Frontend:**
|
|
```
|
|
apps/frontend/src/
|
|
├── components/storage/
|
|
│ ├── FileUpload.tsx # Drag & drop upload component
|
|
│ ├── FileItem.tsx # File display (grid/list view)
|
|
│ ├── FileList.tsx # Paginated file list
|
|
│ ├── StorageUsageCard.tsx # Usage statistics card
|
|
│ └── index.ts
|
|
├── hooks/
|
|
│ └── useStorage.ts # React Query hooks
|
|
├── pages/dashboard/
|
|
│ └── StoragePage.tsx # Main storage page
|
|
└── services/
|
|
└── api.ts # storageApi client (added)
|
|
```
|
|
|
|
### Tablas de Base de Datos
|
|
|
|
| Tabla | Descripcion |
|
|
|-------|-------------|
|
|
| storage.files | Archivos subidos con metadata |
|
|
| storage.pending_uploads | Uploads pendientes de confirmacion |
|
|
| storage.usage | Uso de almacenamiento por tenant |
|
|
|
|
### Funciones de Base de Datos
|
|
|
|
| Funcion | Descripcion |
|
|
|---------|-------------|
|
|
| storage.update_usage_on_file_change() | Trigger para actualizar usage |
|
|
| storage.can_upload() | Valida si tenant puede subir |
|
|
| storage.get_tenant_stats() | Estadisticas de uso |
|
|
| storage.cleanup_expired_uploads() | Limpieza de uploads expirados |
|
|
|
|
## Endpoints API
|
|
|
|
| Metodo | Endpoint | Descripcion | Implementado |
|
|
|--------|----------|-------------|--------------|
|
|
| POST | /storage/upload-url | Obtener presigned upload URL | ✓ |
|
|
| POST | /storage/confirm | Confirmar upload exitoso | ✓ |
|
|
| GET | /storage/files | Listar archivos | ✓ |
|
|
| GET | /storage/files/:id | Metadata de archivo | ✓ |
|
|
| GET | /storage/files/:id/download | Presigned download URL | ✓ |
|
|
| PATCH | /storage/files/:id | Actualizar metadata | ✓ |
|
|
| DELETE | /storage/files/:id | Eliminar archivo (soft) | ✓ |
|
|
| GET | /storage/usage | Uso actual | ✓ |
|
|
|
|
## Flujo de Upload
|
|
|
|
```
|
|
1. Cliente solicita upload URL
|
|
POST /storage/upload-url
|
|
Body: { filename, mimeType, sizeBytes, folder?, visibility? }
|
|
|
|
2. Backend valida:
|
|
- Tipo MIME permitido
|
|
- Extension no bloqueada
|
|
- Tamaño dentro de limite
|
|
- Espacio disponible
|
|
|
|
3. Backend genera presigned URL (AWS SDK v3)
|
|
- Expira en 15 minutos
|
|
- Limite de tamaño
|
|
|
|
4. Cliente sube directo a S3/R2
|
|
PUT [presigned-url]
|
|
Headers: Content-Type
|
|
Body: [file binary]
|
|
|
|
5. Cliente confirma
|
|
POST /storage/confirm
|
|
Body: { uploadId, metadata? }
|
|
|
|
6. Backend:
|
|
- Verifica archivo existe (HeadObject)
|
|
- Crea registro en storage.files
|
|
- Actualiza storage.usage via trigger
|
|
```
|
|
|
|
## Tipos MIME Permitidos
|
|
|
|
```typescript
|
|
const ALLOWED_MIME_TYPES = {
|
|
images: ['image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/svg+xml'],
|
|
documents: [
|
|
'application/pdf',
|
|
'application/msword',
|
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
],
|
|
spreadsheets: [
|
|
'application/vnd.ms-excel',
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
],
|
|
data: ['text/csv', 'application/json', 'text/plain']
|
|
};
|
|
|
|
const BLOCKED_EXTENSIONS = ['.exe', '.bat', '.sh', '.php', '.js', '.cmd', '.com', '.scr'];
|
|
```
|
|
|
|
## Proveedores Soportados
|
|
|
|
| Proveedor | Uso | Config |
|
|
|-----------|-----|--------|
|
|
| AWS S3 | Produccion | Bucket por region |
|
|
| Cloudflare R2 | Produccion | Sin egress fees |
|
|
| MinIO | Desarrollo | Docker local |
|
|
|
|
## Limites por Plan
|
|
|
|
| Plan | Storage | Max Archivo |
|
|
|------|---------|-------------|
|
|
| Free | 100 MB | 5 MB |
|
|
| Starter | 1 GB | 25 MB |
|
|
| Pro | 10 GB | 100 MB |
|
|
| Enterprise | Ilimitado | 500 MB |
|
|
|
|
## Frontend Hooks
|
|
|
|
```typescript
|
|
// Listar archivos
|
|
const { data, isLoading } = useFiles({ page, limit, folder, search });
|
|
|
|
// Obtener archivo individual
|
|
const { data: file } = useFile(fileId);
|
|
|
|
// Uso de almacenamiento
|
|
const { data: usage } = useStorageUsage();
|
|
|
|
// Subir archivo
|
|
const uploadMutation = useUploadFile();
|
|
await uploadMutation.mutateAsync({
|
|
file,
|
|
folder: 'documents',
|
|
visibility: 'private',
|
|
onProgress: (percent) => setProgress(percent)
|
|
});
|
|
|
|
// Actualizar archivo
|
|
const updateMutation = useUpdateFile();
|
|
await updateMutation.mutateAsync({ id, data: { folder: 'archive' } });
|
|
|
|
// Eliminar archivo
|
|
const deleteMutation = useDeleteFile();
|
|
await deleteMutation.mutateAsync(fileId);
|
|
```
|
|
|
|
## Configuracion
|
|
|
|
### Variables de Entorno
|
|
|
|
```bash
|
|
# Provider: s3 | r2 | minio
|
|
STORAGE_PROVIDER=s3
|
|
STORAGE_BUCKET=my-bucket
|
|
|
|
# AWS/S3
|
|
AWS_REGION=us-east-1
|
|
AWS_ACCESS_KEY_ID=...
|
|
AWS_SECRET_ACCESS_KEY=...
|
|
|
|
# Para R2/MinIO (opcional)
|
|
STORAGE_ENDPOINT=https://...
|
|
```
|
|
|
|
## Criterios de Aceptacion
|
|
|
|
- [x] Upload presigned funciona
|
|
- [x] Download presigned funciona
|
|
- [x] Limites se respetan
|
|
- [ ] Thumbnails se generan (futuro)
|
|
- [x] Uso se trackea
|
|
- [x] Archivos se aislan por tenant
|
|
|
|
## Dependencias
|
|
|
|
### Depende de
|
|
- SAAS-002 (Tenants)
|
|
- SAAS-005 (Plans - limites)
|
|
- AWS S3 / Cloudflare R2 / MinIO
|
|
|
|
### Bloquea a
|
|
- Upload de avatares
|
|
- Adjuntos en modulos
|
|
- Importacion de datos
|
|
|
|
## Notas de Implementacion
|
|
|
|
1. **AWS SDK v3**: Se usa `@aws-sdk/client-s3` y `@aws-sdk/s3-request-presigner`
|
|
2. **Soft Delete**: Los archivos se marcan como deleted, no se eliminan fisicamente de inmediato
|
|
3. **RLS Policies**: Aplicadas para aislamiento por tenant
|
|
4. **Triggers**: El uso se actualiza automaticamente via trigger
|
|
|
|
---
|
|
|
|
**Ultima actualizacion:** 2026-01-10
|