miinventario-v2/docs/02-integraciones/INT-005-s3-storage.md
rckrdmrd 1a53b5c4d3 [MIINVENTARIO] feat: Initial commit - Sistema de inventario con análisis de video IA
- Backend NestJS con módulos de autenticación, inventario, créditos
- Frontend React con dashboard y componentes UI
- Base de datos PostgreSQL con migraciones
- Tests E2E configurados
- Configuración de Docker y deployment

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

308 lines
7.1 KiB
Markdown

# INT-005: Integracion S3/MinIO
---
id: INT-005
type: Integration
status: Pendiente
version: "1.0.0"
created_date: 2026-01-10
updated_date: 2026-01-10
simco_version: "4.0.0"
---
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | INT-005 |
| **Servicio** | AWS S3 / MinIO |
| **Proposito** | Almacenamiento de videos y artefactos |
| **Criticidad** | P0 |
| **Estado** | Pendiente |
---
## 1. Descripcion
Integracion con almacenamiento compatible con S3 para guardar videos de inventario, frames extraidos, y otros artefactos del procesamiento.
---
## 2. Informacion del Servicio
| Campo | Valor |
|-------|-------|
| Desarrollo | MinIO (local) |
| Produccion | AWS S3 o DigitalOcean Spaces |
| SDK | @aws-sdk/client-s3, @aws-sdk/s3-request-presigner |
---
## 3. Configuracion
### Variables de Entorno
```env
# Desarrollo (MinIO)
S3_ENDPOINT=http://localhost:9002
S3_ACCESS_KEY=minioadmin
S3_SECRET_KEY=minioadmin
S3_BUCKET=miinventario
S3_REGION=us-east-1
# Produccion (AWS S3)
S3_ENDPOINT=https://s3.amazonaws.com
S3_ACCESS_KEY=AKIA...
S3_SECRET_KEY=...
S3_BUCKET=miinventario-prod
S3_REGION=us-east-1
```
### Instalacion
```bash
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner
```
---
## 4. Estructura de Buckets
```
miinventario/
├── videos/
│ └── {userId}/{sessionId}/
│ └── video.mp4
├── frames/
│ └── {sessionId}/
│ ├── frame_001.jpg
│ ├── frame_002.jpg
│ └── ...
├── thumbnails/
│ └── {sessionId}/
│ └── thumb.jpg
├── evidence/
│ └── {sessionId}/
│ └── closeup_{itemId}.jpg
└── products/
└── {productId}/
└── image.jpg
```
---
## 5. Implementacion Backend
### Servicio S3
```typescript
import { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand } from '@aws-sdk/client-s3';
import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
@Injectable()
export class S3Service {
private client: S3Client;
private bucket: string;
constructor() {
this.client = new S3Client({
endpoint: process.env.S3_ENDPOINT,
region: process.env.S3_REGION,
credentials: {
accessKeyId: process.env.S3_ACCESS_KEY,
secretAccessKey: process.env.S3_SECRET_KEY,
},
forcePathStyle: true, // Necesario para MinIO
});
this.bucket = process.env.S3_BUCKET;
}
async upload(key: string, body: Buffer, contentType: string) {
await this.client.send(new PutObjectCommand({
Bucket: this.bucket,
Key: key,
Body: body,
ContentType: contentType,
}));
return `${process.env.S3_ENDPOINT}/${this.bucket}/${key}`;
}
async getSignedUploadUrl(key: string, contentType: string, expiresIn = 3600) {
const command = new PutObjectCommand({
Bucket: this.bucket,
Key: key,
ContentType: contentType,
});
return getSignedUrl(this.client, command, { expiresIn });
}
async getSignedDownloadUrl(key: string, expiresIn = 3600) {
const command = new GetObjectCommand({
Bucket: this.bucket,
Key: key,
});
return getSignedUrl(this.client, command, { expiresIn });
}
async delete(key: string) {
await this.client.send(new DeleteObjectCommand({
Bucket: this.bucket,
Key: key,
}));
}
}
```
### Upload Multipart
```typescript
import { CreateMultipartUploadCommand, UploadPartCommand, CompleteMultipartUploadCommand } from '@aws-sdk/client-s3';
async initMultipartUpload(key: string) {
const { UploadId } = await this.client.send(
new CreateMultipartUploadCommand({
Bucket: this.bucket,
Key: key,
})
);
return UploadId;
}
async uploadPart(key: string, uploadId: string, partNumber: number, body: Buffer) {
const { ETag } = await this.client.send(
new UploadPartCommand({
Bucket: this.bucket,
Key: key,
UploadId: uploadId,
PartNumber: partNumber,
Body: body,
})
);
return { ETag, PartNumber: partNumber };
}
async completeMultipartUpload(key: string, uploadId: string, parts: any[]) {
await this.client.send(
new CompleteMultipartUploadCommand({
Bucket: this.bucket,
Key: key,
UploadId: uploadId,
MultipartUpload: { Parts: parts },
})
);
}
```
---
## 6. Flujo de Upload de Video
```
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Mobile │───▶│ Backend │───▶│ S3 │
└──────────┘ └──────────┘ └──────────┘
│ │ │
│ 1. Init │ │
│ upload │ │
│──────────────▶│ │
│ │ 2. Create │
│ │ multipart │
│ │──────────────▶│
│◀──────────────│ uploadId │
│ │ │
│ 3. Upload │ │
│ parts │ │
│───────────────────────────────▶
│ (presigned) │ │
│ │ │
│ 4. Complete │ │
│──────────────▶│ │
│ │ 5. Complete │
│ │ multipart │
│ │──────────────▶│
│◀──────────────│ │
```
---
## 7. Lifecycle Rules
### Configuracion de Expiracion
```json
{
"Rules": [
{
"ID": "DeleteOldVideos",
"Status": "Enabled",
"Filter": {
"Prefix": "videos/"
},
"Expiration": {
"Days": 30
}
},
{
"ID": "DeleteOldFrames",
"Status": "Enabled",
"Filter": {
"Prefix": "frames/"
},
"Expiration": {
"Days": 7
}
}
]
}
```
---
## 8. Docker Compose (MinIO)
```yaml
minio:
image: minio/minio
container_name: mii_minio
ports:
- "9002:9000"
- "9003:9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
command: server /data --console-address ":9001"
volumes:
- minio_data:/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
```
---
## 9. Consideraciones de Seguridad
| Aspecto | Implementacion |
|---------|----------------|
| URLs firmadas | Expiracion corta (1h) |
| Bucket privado | No acceso publico |
| CORS | Solo dominios permitidos |
| Encriptacion | SSE-S3 en reposo |
---
## 10. Referencias
- [AWS S3 SDK](https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/s3-example-creating-buckets.html)
- [MinIO Docs](https://min.io/docs/minio/linux/index.html)
- [MII-004](../01-epicas/MII-004-captura-video.md) - Captura de video
---
**Ultima Actualizacion:** 2026-01-10