diff --git a/apps/backend/package.json b/apps/backend/package.json index e15726103..c09ba4c63 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -24,32 +24,32 @@ "migration:revert": "npm run typeorm -- migration:revert -d src/database/data-source.ts" }, "dependencies": { - "@nestjs/common": "^10.3.0", - "@nestjs/config": "^3.1.1", - "@nestjs/core": "^10.3.0", - "@nestjs/jwt": "^10.2.0", - "@nestjs/passport": "^10.0.3", - "@nestjs/platform-express": "^10.3.0", - "@nestjs/swagger": "^7.2.0", - "@nestjs/typeorm": "^10.0.1", + "@nestjs/common": "^11.1.0", + "@nestjs/config": "^3.3.0", + "@nestjs/core": "^11.1.0", + "@nestjs/jwt": "^11.0.1", + "@nestjs/passport": "^11.0.5", + "@nestjs/platform-express": "^11.1.0", + "@nestjs/swagger": "^8.1.0", + "@nestjs/typeorm": "^11.0.0", "@react-native-community/netinfo": "^11.4.1", "bcrypt": "^5.1.1", "class-transformer": "^0.5.1", - "class-validator": "^0.14.1", - "helmet": "^7.1.0", + "class-validator": "^0.14.2", + "helmet": "^8.0.0", "passport": "^0.7.0", "passport-jwt": "^4.0.1", - "pg": "^8.11.3", - "reflect-metadata": "^0.2.1", + "pg": "^8.13.0", + "reflect-metadata": "^0.2.2", "rxjs": "^7.8.1", "stripe": "^20.1.1", - "typeorm": "^0.3.19", - "uuid": "^9.0.1" + "typeorm": "^0.3.22", + "uuid": "^11.0.0" }, "devDependencies": { - "@nestjs/cli": "^10.3.0", - "@nestjs/schematics": "^10.1.0", - "@nestjs/testing": "^10.3.0", + "@nestjs/cli": "^11.0.0", + "@nestjs/schematics": "^11.0.0", + "@nestjs/testing": "^11.1.0", "@types/bcrypt": "^5.0.2", "@types/express": "^4.17.21", "@types/jest": "^29.5.11", diff --git a/docs/01-epicas/MCH-029-infraestructura-saas.md b/docs/01-epicas/MCH-029-infraestructura-saas.md new file mode 100644 index 000000000..9c3bd7a7c --- /dev/null +++ b/docs/01-epicas/MCH-029-infraestructura-saas.md @@ -0,0 +1,301 @@ +--- +id: EPIC-MCH-029 +type: Epic +title: "MCH-029: Infraestructura SaaS Avanzada" +code: MCH-029 +status: Planificado +phase: 7 +priority: P0 +created_at: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +dependencies: + blocks: ["MCH-030", "MCH-032", "MCH-033"] + depends_on: [] +--- + +# MCH-029: Infraestructura SaaS Avanzada + +## Metadata +- **Codigo:** MCH-029 +- **Fase:** 7 - Expansion +- **Prioridad:** P0 (Critica) +- **Estado:** Planificado +- **Story Points:** 24 +- **Sprint Objetivo:** Sprint 6-7 + +## Descripcion + +Implementar infraestructura SaaS empresarial que incluye sistema de Email Multi-proveedor, Storage Abstracto en la nube, Redis como cache/queue, Webhooks Outbound con reintentos y Rate Limiting por plan. Esta epica es la base para las capacidades SaaS avanzadas. + +## Objetivos + +1. Establecer sistema de email multi-proveedor con fallback +2. Implementar storage abstracto compatible con S3/R2/MinIO +3. Configurar Redis para cache y colas +4. Habilitar webhooks outbound con estrategia de reintentos +5. Implementar rate limiting por plan de suscripcion + +## Alcance + +### Incluido +- Email transaccional con SendGrid, SES y SMTP +- Storage cloud con URLs firmadas +- Redis cache con TTL configurable +- BullMQ para procesamiento en background +- Rate limiting con token bucket +- Webhooks con firma HMAC-SHA256 + +### Excluido +- Push notifications (ver MCH-034) +- Email marketing masivo +- CDN para assets estaticos + +## Arquitectura + +``` + ┌─────────────────────────────────────┐ + │ MCH-029: Infraestructura │ + └─────────────────────────────────────┘ + │ + ┌──────────────┬──────────────┼──────────────┬──────────────┐ + ▼ ▼ ▼ ▼ ▼ + ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ + │ Email │ │ Storage │ │ Redis │ │ Webhooks │ │ Rate │ + │ Provider │ │ Provider │ │ Cache │ │ Outbound │ │ Limiting │ + └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ + │ │ │ │ │ + ┌────┴────┐ ┌────┴────┐ │ ┌────┴────┐ │ + │SendGrid │ │ S3 │ │ │ BullMQ │ │ + │ SES │ │ R2 │ │ │ Queue │ │ + │ SMTP │ │ MinIO │ │ │ │ │ + └─────────┘ └─────────┘ │ └─────────┘ │ + │ │ + ┌─────┴────────────────────────────┘ + │ Redis Server │ + └──────────────────────────────────┘ +``` + +## Entregables + +| Entregable | Estado | Sprint | Ubicacion | +|------------|--------|--------|-----------| +| Email module con multi-provider | Planificado | 6 | `apps/backend/src/modules/email/` | +| Storage module abstracto | Planificado | 6 | `apps/backend/src/modules/storage/` | +| Redis cache/queue config | Planificado | 6 | `apps/backend/src/modules/redis/` | +| Webhooks module | Planificado | 7 | `apps/backend/src/modules/webhooks/` | +| Rate limiting guard | Planificado | 7 | `apps/backend/src/common/guards/` | +| DDL storage schema | Planificado | 6 | `database/schemas/12-storage.sql` | +| DDL webhooks schema | Planificado | 7 | `database/schemas/15-webhooks.sql` | + +## Dependencias + +### Depende de +- Ninguna (epica base de infraestructura) + +### Bloquea a +- MCH-030 (Auth Social) - requiere email para verificacion +- MCH-032 (Feature Flags) - requiere Redis para cache +- MCH-033 (Onboarding) - requiere infraestructura completa + +--- + +## Historias de Usuario + +### MCH-US-101: Email Multi-proveedor + +**Como** administrador del sistema +**Quiero** enviar emails transaccionales via multiples proveedores +**Para** garantizar entrega y tener fallback + +**Story Points:** 5 + +**Criterios de Aceptacion:** +- [CA-101-1] Soporta SendGrid como proveedor principal +- [CA-101-2] Soporta AWS SES como fallback +- [CA-101-3] Soporta SMTP generico como ultimo recurso +- [CA-101-4] Templates reutilizables con variables Handlebars +- [CA-101-5] Tracking de entrega (opens, clicks si proveedor soporta) +- [CA-101-6] Rate limiting por tenant (max emails/hora) + +**Tareas:** +| ID | Tarea | Tipo | SP | +|----|-------|------|-----| +| MCH-TT-101-01 | DDL tabla email_templates | DDL | 0.5 | +| MCH-TT-101-02 | DDL tabla email_logs | DDL | 0.5 | +| MCH-TT-101-03 | Servicio EmailProviderFactory | Backend | 0.5 | +| MCH-TT-101-04 | Implementar SendGridProvider | Backend | 1 | +| MCH-TT-101-05 | Implementar SESProvider | Backend | 1 | +| MCH-TT-101-06 | Implementar SMTPProvider | Backend | 0.5 | +| MCH-TT-101-07 | Controlador EmailController | Backend | 0.5 | +| MCH-TT-101-08 | Tests unitarios | Test | 0.5 | +| MCH-TT-101-09 | Documentacion INT-010 | Docs | 0 | + +--- + +### MCH-US-102: Storage Abstracto + +**Como** usuario +**Quiero** subir archivos (imagenes, facturas) a la nube +**Para** tener respaldo seguro y acceso desde cualquier dispositivo + +**Story Points:** 8 + +**Criterios de Aceptacion:** +- [CA-102-1] Soporta AWS S3 como proveedor principal +- [CA-102-2] Soporta Cloudflare R2 como alternativa economica +- [CA-102-3] Soporta MinIO para desarrollo local +- [CA-102-4] Limites de almacenamiento por plan (Basic: 1GB, Pro: 10GB, Enterprise: 100GB) +- [CA-102-5] Organizacion de archivos en carpetas virtuales +- [CA-102-6] URLs firmadas con expiracion configurable +- [CA-102-7] Metadata de archivos (size, mime_type, upload_date) +- [CA-102-8] Validacion de tipos MIME permitidos + +**Tareas:** +| ID | Tarea | Tipo | SP | +|----|-------|------|-----| +| MCH-TT-102-01 | DDL schema storage (files, folders, usage) | DDL | 1 | +| MCH-TT-102-02 | Servicio StorageProviderFactory | Backend | 0.5 | +| MCH-TT-102-03 | Implementar S3Provider | Backend | 1.5 | +| MCH-TT-102-04 | Implementar R2Provider | Backend | 1 | +| MCH-TT-102-05 | Implementar MinIOProvider | Backend | 0.5 | +| MCH-TT-102-06 | Controlador StorageController | Backend | 1 | +| MCH-TT-102-07 | Middleware limite por plan | Backend | 0.5 | +| MCH-TT-102-08 | Generacion URLs firmadas | Backend | 0.5 | +| MCH-TT-102-09 | Tests unitarios e integracion | Test | 1 | +| MCH-TT-102-10 | Documentacion INT-011 | Docs | 0.5 | + +--- + +### MCH-US-103: Redis Cache y Queue + +**Como** sistema +**Quiero** utilizar Redis como cache y queue +**Para** mejorar rendimiento y procesar tareas en background + +**Story Points:** 3 + +**Criterios de Aceptacion:** +- [CA-103-1] Cache de sesiones JWT con TTL +- [CA-103-2] Cache de configuracion de tenant +- [CA-103-3] Queue para emails, webhooks, notificaciones via BullMQ +- [CA-103-4] Health check endpoint de Redis +- [CA-103-5] Metricas de uso (hits/misses, queue length) + +**Tareas:** +| ID | Tarea | Tipo | SP | +|----|-------|------|-----| +| MCH-TT-103-01 | Configuracion Redis module | Backend | 0.5 | +| MCH-TT-103-02 | Cache service con TTL | Backend | 0.5 | +| MCH-TT-103-03 | BullMQ integration | Backend | 0.5 | +| MCH-TT-103-04 | Queue processors base | Backend | 0.5 | +| MCH-TT-103-05 | Health check endpoint | Backend | 0.25 | +| MCH-TT-103-06 | Tests | Test | 0.5 | +| MCH-TT-103-07 | Documentacion INT-013 | Docs | 0.25 | + +--- + +### MCH-US-104: Webhooks Outbound + +**Como** administrador de tenant +**Quiero** configurar webhooks para recibir notificaciones de eventos +**Para** integrar con sistemas externos + +**Story Points:** 5 + +**Criterios de Aceptacion:** +- [CA-104-1] CRUD de endpoints webhook por tenant +- [CA-104-2] Seleccion de eventos a suscribirse (order.created, payment.completed, etc) +- [CA-104-3] Firma de payloads con HMAC-SHA256 +- [CA-104-4] Reintentos con exponential backoff (1s, 2s, 4s, 8s, 16s) +- [CA-104-5] Logs de entregas con response status + +**Tareas:** +| ID | Tarea | Tipo | SP | +|----|-------|------|-----| +| MCH-TT-104-01 | DDL schema webhooks (endpoints, deliveries) | DDL | 0.5 | +| MCH-TT-104-02 | Servicio WebhookService | Backend | 1 | +| MCH-TT-104-03 | Job processor con BullMQ | Backend | 1 | +| MCH-TT-104-04 | Firma HMAC de payloads | Backend | 0.5 | +| MCH-TT-104-05 | Controlador WebhooksController | Backend | 0.5 | +| MCH-TT-104-06 | UI en portal admin | Frontend | 0.5 | +| MCH-TT-104-07 | Tests | Test | 0.5 | +| MCH-TT-104-08 | Documentacion INT-014 | Docs | 0.5 | + +--- + +### MCH-US-112: Rate Limiting por Plan + +**Como** sistema +**Quiero** limitar las solicitudes API segun el plan del tenant +**Para** garantizar uso justo y proteger la infraestructura + +**Story Points:** 3 + +**Criterios de Aceptacion:** +- [CA-112-1] Limite de requests/minuto configurable por plan +- [CA-112-2] Limite de requests/dia configurable por plan +- [CA-112-3] Headers X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset +- [CA-112-4] Respuesta 429 Too Many Requests con Retry-After +- [CA-112-5] Bypass para webhooks internos y health checks + +**Tareas:** +| ID | Tarea | Tipo | SP | +|----|-------|------|-----| +| MCH-TT-112-01 | Implementar middleware RateLimitGuard | Backend | 0.5 | +| MCH-TT-112-02 | Configuracion limites en plans | Backend | 0.5 | +| MCH-TT-112-03 | Almacenamiento contadores en Redis | Backend | 0.5 | +| MCH-TT-112-04 | Headers de respuesta | Backend | 0.25 | +| MCH-TT-112-05 | Tests | Test | 0.5 | +| MCH-TT-112-06 | Documentar en ADR-0009 | Docs | 0.25 | + +--- + +## Resumen de Story Points + +| Historia | SP | Sprint | +|----------|-----|--------| +| MCH-US-101: Email Multi-proveedor | 5 | 6 | +| MCH-US-102: Storage Abstracto | 8 | 6 | +| MCH-US-103: Redis Cache y Queue | 3 | 6 | +| MCH-US-104: Webhooks Outbound | 5 | 7 | +| MCH-US-112: Rate Limiting | 3 | 7 | +| **TOTAL** | **24** | 6-7 | + +--- + +## Criterios de Aceptacion de Epica + +- [ ] Email enviado exitosamente con cada proveedor +- [ ] Archivos subidos a S3 con URL firmada funcional +- [ ] Redis cache reduce latencia en >50% +- [ ] Webhook entregado con reintentos exitosos +- [ ] Rate limiting bloquea requests excedentes +- [ ] Cobertura de tests >80% +- [ ] Documentacion de integraciones completa + +## Notas Tecnicas + +- **Redis:** Puerto 6379, DB 0 para cache, DB 1 para queues +- **S3 Region:** us-east-1 (default) +- **R2 Region:** auto +- **Email Rate:** Max 1000/tenant/hora en plan Pro + +## Integraciones Relacionadas + +- [INT-010: Email Providers](../02-integraciones/INT-010-email-providers.md) +- [INT-011: Storage Cloud](../02-integraciones/INT-011-storage-cloud.md) +- [INT-013: Redis Cache](../02-integraciones/INT-013-redis-cache.md) +- [INT-014: Webhooks Outbound](../02-integraciones/INT-014-webhooks-outbound.md) + +## ADRs Relacionados + +- [ADR-0006: Storage Abstraction](../97-adr/ADR-0006-storage-abstraction.md) +- [ADR-0007: Webhook Retry Strategy](../97-adr/ADR-0007-webhook-retry-strategy.md) +- [ADR-0009: Rate Limiting Strategy](../97-adr/ADR-0009-rate-limiting.md) +- [ADR-0011: Email Multi-provider](../97-adr/ADR-0011-email-multi-provider.md) + +--- + +**Ultima actualizacion:** 2026-01-10 +**Autor:** Architecture Team diff --git a/docs/01-epicas/MCH-030-auth-social.md b/docs/01-epicas/MCH-030-auth-social.md new file mode 100644 index 000000000..cdd05ed0c --- /dev/null +++ b/docs/01-epicas/MCH-030-auth-social.md @@ -0,0 +1,221 @@ +--- +id: EPIC-MCH-030 +type: Epic +title: "MCH-030: Auth Social (OAuth 2.0)" +code: MCH-030 +status: Planificado +phase: 7 +priority: P1 +created_at: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +dependencies: + blocks: [] + depends_on: ["MCH-029"] +--- + +# MCH-030: Auth Social (OAuth 2.0) + +## Metadata +- **Codigo:** MCH-030 +- **Fase:** 7 - Expansion +- **Prioridad:** P1 +- **Estado:** Planificado +- **Story Points:** 8 +- **Sprint Objetivo:** Sprint 8 + +## Descripcion + +Agregar autenticacion social via OAuth 2.0 con Google y Apple Sign-In para simplificar el onboarding de usuarios. Permite registro e inicio de sesion con un clic, reduciendo friccion y aumentando conversion. + +## Objetivos + +1. Implementar Login con Google +2. Implementar Sign in with Apple (requerido para iOS) +3. Permitir vinculacion de cuentas sociales a cuenta existente +4. Obtener perfil basico del usuario (nombre, email, foto) + +## Alcance + +### Incluido +- OAuth 2.0 con Google +- Sign in with Apple (web + iOS) +- Vinculacion de multiples proveedores a una cuenta +- Sync de foto de perfil + +### Excluido +- Facebook Login (baja prioridad) +- Microsoft/LinkedIn (B2B futuro) +- Two-factor authentication (MCH-002 existente) + +## Arquitectura + +``` + ┌─────────────────────────────────────┐ + │ MCH-030: Auth Social │ + └─────────────────────────────────────┘ + │ + ┌─────────────────┴─────────────────┐ + ▼ ▼ + ┌──────────┐ ┌──────────┐ + │ Google │ │ Apple │ + │ OAuth │ │ Sign-In │ + └────┬─────┘ └────┬─────┘ + │ │ + ▼ ▼ + ┌─────────────────────────────────────────┐ + │ Passport.js Strategies │ + └─────────────────────────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────────┐ + │ oauth_accounts (tabla) │ + │ user_id | provider | provider_id │ + └─────────────────────────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────────┐ + │ auth.users (existente) │ + └─────────────────────────────────────────┘ +``` + +## Entregables + +| Entregable | Estado | Sprint | Ubicacion | +|------------|--------|--------|-----------| +| Google OAuth Strategy | Planificado | 8 | `apps/backend/src/modules/auth/strategies/` | +| Apple OAuth Strategy | Planificado | 8 | `apps/backend/src/modules/auth/strategies/` | +| Tabla oauth_accounts | Planificado | 8 | `database/schemas/16-oauth.sql` | +| Endpoints /auth/google, /auth/apple | Planificado | 8 | `apps/backend/src/modules/auth/` | +| UI botones sociales web | Planificado | 8 | `apps/web/src/pages/auth/` | +| Implementacion mobile | Planificado | 8 | `apps/mobile/src/screens/auth/` | + +## Dependencias + +### Depende de +- MCH-029 (Email para verificacion de cuenta vinculada) + +### Bloquea a +- Ninguna + +--- + +## Historias de Usuario + +### MCH-US-105: Login con Google + +**Como** usuario nuevo +**Quiero** registrarme/iniciar sesion con mi cuenta de Google +**Para** no tener que recordar otra contrasena + +**Story Points:** 5 + +**Criterios de Aceptacion:** +- [CA-105-1] Boton "Continuar con Google" visible en login y registro +- [CA-105-2] Registro automatico si no existe cuenta con ese email +- [CA-105-3] Vinculacion automatica si ya existe cuenta con mismo email +- [CA-105-4] Obtiene nombre y foto de perfil de Google +- [CA-105-5] Funciona en web (popup) y mobile (Expo AuthSession) + +**Tareas:** +| ID | Tarea | Tipo | SP | +|----|-------|------|-----| +| MCH-TT-105-01 | DDL tabla oauth_accounts | DDL | 0.5 | +| MCH-TT-105-02 | Google OAuth strategy (Passport) | Backend | 1 | +| MCH-TT-105-03 | Endpoint /auth/google | Backend | 0.5 | +| MCH-TT-105-04 | Callback handler /auth/google/callback | Backend | 0.5 | +| MCH-TT-105-05 | Servicio de vinculacion de cuentas | Backend | 0.5 | +| MCH-TT-105-06 | UI boton Google en web | Frontend | 0.5 | +| MCH-TT-105-07 | Implementacion mobile (Expo) | Frontend | 1 | +| MCH-TT-105-08 | Tests unitarios e integracion | Test | 0.5 | +| MCH-TT-105-09 | Documentacion en INT-012 | Docs | 0 | + +--- + +### MCH-US-106: Login con Apple + +**Como** usuario de iOS +**Quiero** iniciar sesion con Apple ID +**Para** usar autenticacion nativa de mi dispositivo + +**Story Points:** 3 + +**Criterios de Aceptacion:** +- [CA-106-1] Sign in with Apple funcional en iOS nativo +- [CA-106-2] Fallback web para Android y Desktop +- [CA-106-3] Manejo correcto de email oculto (relay de Apple) +- [CA-106-4] Registro automatico con nombre del usuario +- [CA-106-5] Cumple requisitos App Store Review Guidelines + +**Tareas:** +| ID | Tarea | Tipo | SP | +|----|-------|------|-----| +| MCH-TT-106-01 | Apple OAuth strategy | Backend | 0.5 | +| MCH-TT-106-02 | Endpoint /auth/apple | Backend | 0.5 | +| MCH-TT-106-03 | Manejo de private email relay | Backend | 0.5 | +| MCH-TT-106-04 | Implementacion iOS nativa | Frontend | 0.5 | +| MCH-TT-106-05 | Fallback web con apple-signin-api | Frontend | 0.5 | +| MCH-TT-106-06 | Tests | Test | 0.25 | +| MCH-TT-106-07 | Documentacion en INT-012 | Docs | 0.25 | + +--- + +## Resumen de Story Points + +| Historia | SP | Sprint | +|----------|-----|--------| +| MCH-US-105: Login con Google | 5 | 8 | +| MCH-US-106: Login con Apple | 3 | 8 | +| **TOTAL** | **8** | 8 | + +--- + +## Criterios de Aceptacion de Epica + +- [ ] Login con Google funcional en web y mobile +- [ ] Sign in with Apple funcional en iOS +- [ ] Cuentas vinculadas correctamente +- [ ] No se crean duplicados de usuarios +- [ ] Cobertura de tests >80% + +## Notas Tecnicas + +### Configuracion Google Cloud Console +1. Crear proyecto en Google Cloud Console +2. Habilitar Google+ API +3. Crear OAuth 2.0 Client ID (Web Application) +4. Configurar redirect URIs: + - `https://api.michangarrito.com/auth/google/callback` + - `http://localhost:3000/auth/google/callback` (dev) + +### Configuracion Apple Developer +1. App ID con Sign in with Apple capability +2. Service ID para web +3. Key privada para validar tokens +4. Configurar domains y redirect URLs + +### Variables de Entorno +```env +# Google OAuth +GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com +GOOGLE_CLIENT_SECRET=xxx + +# Apple Sign-In +APPLE_CLIENT_ID=com.michangarrito.app +APPLE_TEAM_ID=XXXXXXXXXX +APPLE_KEY_ID=XXXXXXXXXX +APPLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----..." +``` + +## Integraciones Relacionadas + +- [INT-012: OAuth Social](../02-integraciones/INT-012-oauth-social.md) + +## ADRs Relacionados + +- [ADR-0010: OAuth Social Strategy](../97-adr/ADR-0010-oauth-social.md) + +--- + +**Ultima actualizacion:** 2026-01-10 +**Autor:** Architecture Team diff --git a/docs/01-epicas/MCH-031-auditoria-empresarial.md b/docs/01-epicas/MCH-031-auditoria-empresarial.md new file mode 100644 index 000000000..663a0f85f --- /dev/null +++ b/docs/01-epicas/MCH-031-auditoria-empresarial.md @@ -0,0 +1,222 @@ +--- +id: EPIC-MCH-031 +type: Epic +title: "MCH-031: Auditoria Empresarial" +code: MCH-031 +status: Planificado +phase: 7 +priority: P1 +created_at: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +dependencies: + blocks: [] + depends_on: [] +--- + +# MCH-031: Auditoria Empresarial + +## Metadata +- **Codigo:** MCH-031 +- **Fase:** 7 - Expansion +- **Prioridad:** P1 +- **Estado:** Planificado +- **Story Points:** 5 +- **Sprint Objetivo:** Sprint 7 + +## Descripcion + +Implementar sistema de auditoria completo con registro de todas las acciones CRUD, politica de retencion configurable y capacidad de exportacion para compliance. Permite trazabilidad completa de operaciones y deteccion de actividades sospechosas. + +## Objetivos + +1. Registrar todas las acciones de usuarios en el sistema +2. Mantener historial de cambios con valores antes/despues +3. Configurar politicas de retencion por tenant +4. Exportar logs para auditorias externas +5. Visualizar actividad en dashboard admin + +## Alcance + +### Incluido +- Audit logs para CREATE, UPDATE, DELETE +- Activity logs para acciones de usuario (login, logout, view) +- Interceptor global NestJS +- Politica de retencion (30, 90, 365 dias) +- Exportacion CSV +- Visor de logs en admin + +### Excluido +- SIEM integration (futuro) +- Real-time alerting (ver notificaciones) +- Forensic analysis tools + +## Arquitectura + +``` + ┌─────────────────────────────────────┐ + │ MCH-031: Auditoria │ + └─────────────────────────────────────┘ + │ + ┌───────────────────────┴───────────────────────┐ + ▼ ▼ + ┌─────────────────────┐ ┌─────────────────────┐ + │ Audit Logs │ │ Activity Logs │ + │ (data changes) │ │ (user actions) │ + └──────────┬──────────┘ └──────────┬──────────┘ + │ │ + └──────────────────┬─────────────────────────┘ + ▼ + ┌─────────────────────────────────────────┐ + │ AuditInterceptor │ + │ (NestJS Global Interceptor) │ + └─────────────────────────────────────────┘ + │ + ┌──────────────────┼──────────────────┐ + ▼ ▼ ▼ + ┌──────────┐ ┌──────────┐ ┌──────────┐ + │ schema │ │ Retencion│ │ Export │ + │ audit │ │ Policy │ │ CSV │ + └──────────┘ └──────────┘ └──────────┘ +``` + +## Entregables + +| Entregable | Estado | Sprint | Ubicacion | +|------------|--------|--------|-----------| +| Schema audit (DDL) | Planificado | 7 | `database/schemas/14-audit.sql` | +| AuditInterceptor | Planificado | 7 | `apps/backend/src/common/interceptors/` | +| AuditService | Planificado | 7 | `apps/backend/src/modules/audit/` | +| AuditController | Planificado | 7 | `apps/backend/src/modules/audit/` | +| Retention Job | Planificado | 7 | `apps/backend/src/jobs/` | +| UI Visor logs | Planificado | 7 | `apps/web/src/pages/admin/audit/` | + +## Dependencias + +### Depende de +- Ninguna (puede ejecutarse en paralelo con MCH-029) + +### Bloquea a +- Ninguna + +--- + +## Historias de Usuario + +### MCH-US-107: Registro de Acciones + +**Como** administrador +**Quiero** ver un registro de todas las acciones realizadas en el sistema +**Para** tener trazabilidad y detectar actividades sospechosas + +**Story Points:** 3 + +**Criterios de Aceptacion:** +- [CA-107-1] Registra CREATE, UPDATE, DELETE automaticamente +- [CA-107-2] Incluye: usuario, timestamp, IP, user-agent +- [CA-107-3] Guarda valor anterior y nuevo en cambios (old_value, new_value) +- [CA-107-4] Filtrable por usuario, accion, entidad, rango de fechas +- [CA-107-5] Exportable a CSV con columnas seleccionables + +**Tareas:** +| ID | Tarea | Tipo | SP | +|----|-------|------|-----| +| MCH-TT-107-01 | DDL schema audit (audit_logs, activity_logs) | DDL | 0.5 | +| MCH-TT-107-02 | AuditInterceptor global | Backend | 0.5 | +| MCH-TT-107-03 | AuditService con metodos log/query | Backend | 0.5 | +| MCH-TT-107-04 | AuditController (GET /audit/logs) | Backend | 0.25 | +| MCH-TT-107-05 | UI visor de logs con filtros | Frontend | 0.5 | +| MCH-TT-107-06 | Exportacion CSV | Backend | 0.25 | +| MCH-TT-107-07 | Tests | Test | 0.25 | +| MCH-TT-107-08 | Documentacion | Docs | 0.25 | + +--- + +### MCH-US-108: Politica de Retencion + +**Como** administrador +**Quiero** configurar cuanto tiempo se guardan los logs +**Para** cumplir con politicas de retencion y optimizar almacenamiento + +**Story Points:** 2 + +**Criterios de Aceptacion:** +- [CA-108-1] Configuracion de dias de retencion: 30, 90, 180, 365 +- [CA-108-2] Job automatico de limpieza (cron diario) +- [CA-108-3] Opcion de archivar a storage antes de eliminar +- [CA-108-4] Notificacion por email antes de purga (7 dias antes) + +**Tareas:** +| ID | Tarea | Tipo | SP | +|----|-------|------|-----| +| MCH-TT-108-01 | Configuracion de retencion en tenant_settings | Backend | 0.25 | +| MCH-TT-108-02 | Job de limpieza con BullMQ (cron) | Backend | 0.5 | +| MCH-TT-108-03 | Archivado opcional a storage (S3) | Backend | 0.5 | +| MCH-TT-108-04 | Tests | Test | 0.25 | +| MCH-TT-108-05 | Documentacion de politica de retencion | Docs | 0.5 | + +--- + +## Resumen de Story Points + +| Historia | SP | Sprint | +|----------|-----|--------| +| MCH-US-107: Registro de Acciones | 3 | 7 | +| MCH-US-108: Politica de Retencion | 2 | 7 | +| **TOTAL** | **5** | 7 | + +--- + +## Criterios de Aceptacion de Epica + +- [ ] Todas las acciones CRUD se registran automaticamente +- [ ] Logs visibles en dashboard admin +- [ ] Exportacion CSV funcional +- [ ] Job de limpieza ejecutandose diariamente +- [ ] Cobertura de tests >80% + +## Notas Tecnicas + +### Schema audit + +```sql +CREATE SCHEMA IF NOT EXISTS audit; + +CREATE TABLE audit.audit_logs ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + user_id UUID, + action VARCHAR(20) NOT NULL, -- CREATE, UPDATE, DELETE + entity_type VARCHAR(100) NOT NULL, -- Product, Order, User + entity_id UUID NOT NULL, + old_value JSONB, + new_value JSONB, + ip_address INET, + user_agent TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +CREATE INDEX idx_audit_logs_tenant_date ON audit.audit_logs(tenant_id, created_at DESC); +CREATE INDEX idx_audit_logs_entity ON audit.audit_logs(entity_type, entity_id); +``` + +### Configuracion de Retencion +```typescript +// En tenant_settings +{ + "audit": { + "retention_days": 90, + "archive_before_delete": true, + "notify_before_purge": true + } +} +``` + +## ADRs Relacionados + +- [ADR-0008: Audit Log Retention](../97-adr/ADR-0008-audit-log-retention.md) + +--- + +**Ultima actualizacion:** 2026-01-10 +**Autor:** Architecture Team diff --git a/docs/01-epicas/MCH-032-feature-flags.md b/docs/01-epicas/MCH-032-feature-flags.md new file mode 100644 index 000000000..c0daff598 --- /dev/null +++ b/docs/01-epicas/MCH-032-feature-flags.md @@ -0,0 +1,258 @@ +--- +id: EPIC-MCH-032 +type: Epic +title: "MCH-032: Feature Flags por Plan" +code: MCH-032 +status: Planificado +phase: 7 +priority: P1 +created_at: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +dependencies: + blocks: [] + depends_on: ["MCH-029", "MCH-018"] +--- + +# MCH-032: Feature Flags por Plan + +## Metadata +- **Codigo:** MCH-032 +- **Fase:** 7 - Expansion +- **Prioridad:** P1 +- **Estado:** Planificado +- **Story Points:** 5 +- **Sprint Objetivo:** Sprint 8 + +## Descripcion + +Implementar sistema de feature flags que permite habilitar/deshabilitar funcionalidades por plan de suscripcion, tenant o usuario individual. Permite diferenciacion de ofertas, beta testing controlado y rollouts graduales. + +## Objetivos + +1. Definir flags globales con valores por defecto +2. Asociar flags a planes de suscripcion +3. Permitir overrides por tenant para beta testing +4. Evaluar flags en tiempo real con cache +5. SDK para frontend (React hook) + +## Alcance + +### Incluido +- Feature flags por plan (Basic, Pro, Enterprise) +- Overrides por tenant y usuario +- Expiracion de overrides +- Cache en Redis +- Guard de NestJS para backend +- Hook useFeatureFlag para React + +### Excluido +- A/B testing (futuro) +- Percentage rollouts +- Feature usage analytics + +## Arquitectura + +``` + ┌─────────────────────────────────────┐ + │ MCH-032: Feature Flags │ + └─────────────────────────────────────┘ + │ + ┌─────────────────────────────┼─────────────────────────────┐ + ▼ ▼ ▼ + ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ + │ Flags │ │ Plans │ │ Overrides │ + │ (global) │ │ (MCH-018) │ │ (por tenant) │ + └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ + │ │ │ + └──────────────────────────┼──────────────────────────┘ + ▼ + ┌─────────────────────────────────────┐ + │ FeatureFlagEvaluator │ + │ (Redis cache + fallback DB) │ + └─────────────────────────────────────┘ + │ + ┌────────────────────┼────────────────────┐ + ▼ ▼ ▼ + ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ + │FeatureFlagGuard│ │useFeatureFlag│ │ Admin UI │ + │ (Backend) │ │ (React) │ │ │ + └──────────────┘ └──────────────┘ └──────────────┘ +``` + +## Entregables + +| Entregable | Estado | Sprint | Ubicacion | +|------------|--------|--------|-----------| +| Schema feature_flags (DDL) | Planificado | 8 | `database/schemas/13-feature-flags.sql` | +| FeatureFlagService | Planificado | 8 | `apps/backend/src/modules/feature-flags/` | +| FeatureFlagEvaluator | Planificado | 8 | `apps/backend/src/modules/feature-flags/` | +| FeatureFlagGuard | Planificado | 8 | `apps/backend/src/common/guards/` | +| useFeatureFlag hook | Planificado | 8 | `apps/web/src/hooks/` | +| Admin UI | Planificado | 8 | `apps/web/src/pages/admin/feature-flags/` | + +## Dependencias + +### Depende de +- MCH-029 (Redis para cache de evaluaciones) +- MCH-018 (Plans para asociar flags) + +### Bloquea a +- Ninguna + +--- + +## Historias de Usuario + +### MCH-US-109: Feature Flags por Plan + +**Como** administrador del sistema +**Quiero** habilitar/deshabilitar features segun el plan del tenant +**Para** diferenciar la oferta entre planes + +**Story Points:** 3 + +**Criterios de Aceptacion:** +- [CA-109-1] Definicion de flags globales con key, descripcion, valor por defecto +- [CA-109-2] Asociacion de flags a planes (Basic: false, Pro: true, Enterprise: true) +- [CA-109-3] Evaluacion en tiempo real (latencia <10ms con cache) +- [CA-109-4] Cache en Redis con invalidacion al cambiar flag +- [CA-109-5] Hook useFeatureFlag(key) retorna boolean + +**Tareas:** +| ID | Tarea | Tipo | SP | +|----|-------|------|-----| +| MCH-TT-109-01 | DDL schema feature_flags | DDL | 0.25 | +| MCH-TT-109-02 | FeatureFlagService (CRUD de flags) | Backend | 0.5 | +| MCH-TT-109-03 | FeatureFlagEvaluator con cache Redis | Backend | 0.5 | +| MCH-TT-109-04 | FeatureFlagGuard para proteger endpoints | Backend | 0.25 | +| MCH-TT-109-05 | FeatureFlagsController (API) | Backend | 0.25 | +| MCH-TT-109-06 | Hook useFeatureFlag en frontend | Frontend | 0.5 | +| MCH-TT-109-07 | Tests | Test | 0.5 | +| MCH-TT-109-08 | Documentacion | Docs | 0.25 | + +--- + +### MCH-US-110: Overrides por Tenant + +**Como** administrador del sistema +**Quiero** habilitar features especificos para ciertos tenants +**Para** dar acceso beta o promociones especiales + +**Story Points:** 2 + +**Criterios de Aceptacion:** +- [CA-110-1] Override por tenant_id habilita feature aunque plan no lo incluya +- [CA-110-2] Override por user_id para usuarios especificos +- [CA-110-3] Expiracion configurable del override (fecha/hora) +- [CA-110-4] UI de administracion para gestionar overrides + +**Tareas:** +| ID | Tarea | Tipo | SP | +|----|-------|------|-----| +| MCH-TT-110-01 | DDL tabla flag_overrides | DDL | 0.25 | +| MCH-TT-110-02 | Logica de override en evaluador | Backend | 0.5 | +| MCH-TT-110-03 | UI para superadmin | Frontend | 0.5 | +| MCH-TT-110-04 | Tests | Test | 0.25 | +| MCH-TT-110-05 | Documentacion de uso | Docs | 0.5 | + +--- + +## Resumen de Story Points + +| Historia | SP | Sprint | +|----------|-----|--------| +| MCH-US-109: Feature Flags por Plan | 3 | 8 | +| MCH-US-110: Overrides por Tenant | 2 | 8 | +| **TOTAL** | **5** | 8 | + +--- + +## Criterios de Aceptacion de Epica + +- [ ] Flags evaluados correctamente por plan +- [ ] Cache Redis funcional (<10ms latencia) +- [ ] Overrides aplicandose correctamente +- [ ] UI de administracion funcional +- [ ] Hook React integrado +- [ ] Cobertura de tests >80% + +## Notas Tecnicas + +### Schema feature_flags + +```sql +CREATE SCHEMA IF NOT EXISTS feature_flags; + +CREATE TABLE feature_flags.flags ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + key VARCHAR(100) UNIQUE NOT NULL, + description TEXT, + default_value BOOLEAN DEFAULT false, + type VARCHAR(20) DEFAULT 'boolean', -- boolean, string, number + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +CREATE TABLE feature_flags.plan_flags ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + flag_id UUID REFERENCES feature_flags.flags(id), + plan_id UUID REFERENCES billing.plans(id), + value BOOLEAN NOT NULL, + UNIQUE(flag_id, plan_id) +); + +CREATE TABLE feature_flags.flag_overrides ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + flag_id UUID REFERENCES feature_flags.flags(id), + tenant_id UUID, + user_id UUID, + value BOOLEAN NOT NULL, + expires_at TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + CHECK (tenant_id IS NOT NULL OR user_id IS NOT NULL) +); +``` + +### Ejemplo de Flags Iniciales + +| Flag Key | Descripcion | Basic | Pro | Enterprise | +|----------|-------------|-------|-----|------------| +| ai_assistant | Asistente IA en chat | false | true | true | +| advanced_reports | Reportes avanzados | false | true | true | +| api_access | Acceso a API publica | false | false | true | +| white_label | Sin branding MiChangarrito | false | false | true | +| multi_location | Multiples ubicaciones | false | true | true | + +### Uso en Backend + +```typescript +@UseGuards(FeatureFlagGuard) +@FeatureFlag('ai_assistant') +@Get('ai/suggestions') +async getAISuggestions() { + // Solo accesible si flag habilitado +} +``` + +### Uso en Frontend + +```tsx +function Dashboard() { + const hasAI = useFeatureFlag('ai_assistant'); + + return ( +
+ {hasAI && } +
+ ); +} +``` + +## ADRs Relacionados + +- [ADR-0005: Feature Flags Strategy](../97-adr/ADR-0005-feature-flags.md) + +--- + +**Ultima actualizacion:** 2026-01-10 +**Autor:** Architecture Team diff --git a/docs/01-epicas/MCH-033-onboarding-wizard.md b/docs/01-epicas/MCH-033-onboarding-wizard.md new file mode 100644 index 000000000..2ec093bba --- /dev/null +++ b/docs/01-epicas/MCH-033-onboarding-wizard.md @@ -0,0 +1,236 @@ +--- +id: EPIC-MCH-033 +type: Epic +title: "MCH-033: Onboarding Wizard" +code: MCH-033 +status: Planificado +phase: 8 +priority: P2 +created_at: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +dependencies: + blocks: [] + depends_on: ["MCH-029"] +--- + +# MCH-033: Onboarding Wizard + +## Metadata +- **Codigo:** MCH-033 +- **Fase:** 8 - Mejoras UX +- **Prioridad:** P2 +- **Estado:** Planificado +- **Story Points:** 3 +- **Sprint Objetivo:** Sprint 9 + +## Descripcion + +Crear asistente interactivo de configuracion inicial que guie al usuario en los primeros pasos de setup de su negocio. Reduce friccion, aumenta activacion y mejora la experiencia de primer uso. + +## Objetivos + +1. Guiar configuracion inicial de negocio +2. Facilitar carga de primeros productos +3. Configurar metodos de pago +4. Invitar al equipo +5. Mostrar tour interactivo de la app + +## Alcance + +### Incluido +- Wizard de 5 pasos con progreso +- Persistencia de progreso (continuar despues) +- Skip opcional por paso +- Productos sugeridos por giro de negocio +- Animacion de celebracion al completar + +### Excluido +- Migracion desde otros sistemas +- Importacion masiva de productos (otra feature) +- Video tutoriales (contenido futuro) + +## Arquitectura + +``` + ┌─────────────────────────────────────┐ + │ MCH-033: Onboarding Wizard │ + └─────────────────────────────────────┘ + │ + ┌─────────────────────────────┼─────────────────────────────┐ + ▼ ▼ ▼ + ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ + │ Paso 1 │ │ Paso 2 │ │ Paso 3 │ + │ Mi Negocio │ ───> │ Productos │ ───> │ Pagos │ + └──────────────┘ └──────────────┘ └──────────────┘ + │ + ▼ + ┌──────────────┐ ┌──────────────┐ + │ Paso 5 │ <─── │ Paso 4 │ + │ Completar │ │ Mi Equipo │ + └──────────────┘ └──────────────┘ + + ┌─────────────────────────────────────┐ + │ Onboarding Progress │ + │ (tenant_settings.onboarding) │ + └─────────────────────────────────────┘ +``` + +## Entregables + +| Entregable | Estado | Sprint | Ubicacion | +|------------|--------|--------|-----------| +| OnboardingWizard component | Planificado | 9 | `apps/web/src/components/onboarding/` | +| BusinessSetupStep | Planificado | 9 | `apps/web/src/components/onboarding/steps/` | +| ProductsStep | Planificado | 9 | `apps/web/src/components/onboarding/steps/` | +| PaymentsStep | Planificado | 9 | `apps/web/src/components/onboarding/steps/` | +| TeamStep | Planificado | 9 | `apps/web/src/components/onboarding/steps/` | +| CompletionStep | Planificado | 9 | `apps/web/src/components/onboarding/steps/` | + +## Dependencias + +### Depende de +- MCH-029 (Infraestructura completa para guardar progreso) + +### Bloquea a +- Ninguna + +--- + +## Historias de Usuario + +### MCH-US-111: Wizard de Configuracion + +**Como** usuario nuevo +**Quiero** una guia paso a paso para configurar mi negocio +**Para** empezar a usar la app rapidamente + +**Story Points:** 3 + +**Criterios de Aceptacion:** +- [CA-111-1] Wizard de 5 pasos: Negocio, Productos, Pagos, Equipo, Completar +- [CA-111-2] Progreso guardado en backend (puede cerrar y continuar) +- [CA-111-3] Skip opcional por cada paso (excepto paso 1) +- [CA-111-4] Productos iniciales sugeridos segun giro seleccionado +- [CA-111-5] Animacion de confetti al completar + +**Tareas:** +| ID | Tarea | Tipo | SP | +|----|-------|------|-----| +| MCH-TT-111-01 | OnboardingWizard container | Frontend | 0.25 | +| MCH-TT-111-02 | Paso 1: Datos de negocio (nombre, giro, logo) | Frontend | 0.5 | +| MCH-TT-111-03 | Paso 2: Productos iniciales (templates por giro) | Frontend | 0.5 | +| MCH-TT-111-04 | Paso 3: Metodos de pago (conexion Stripe/MercadoPago) | Frontend | 0.5 | +| MCH-TT-111-05 | Paso 4: Invitar equipo (emails de colaboradores) | Frontend | 0.25 | +| MCH-TT-111-06 | Paso 5: Completar con confetti y tour | Frontend | 0.25 | +| MCH-TT-111-07 | Persistencia de progreso en backend | Backend | 0.25 | +| MCH-TT-111-08 | Tests | Test | 0.25 | +| MCH-TT-111-09 | Documentacion | Docs | 0.25 | + +--- + +## Resumen de Story Points + +| Historia | SP | Sprint | +|----------|-----|--------| +| MCH-US-111: Wizard de Configuracion | 3 | 9 | +| **TOTAL** | **3** | 9 | + +--- + +## Criterios de Aceptacion de Epica + +- [ ] Wizard visible para nuevos usuarios +- [ ] Progreso persistido correctamente +- [ ] Productos sugeridos por giro funcionando +- [ ] Animacion de celebracion al completar +- [ ] Tests de flujo completo + +## Notas Tecnicas + +### Persistencia de Progreso + +```typescript +// tenant_settings.onboarding +{ + "current_step": 2, + "completed_steps": [1], + "skipped_steps": [], + "started_at": "2026-01-10T10:00:00Z", + "completed_at": null, + "data": { + "business_type": "abarrotes", + "products_count": 15 + } +} +``` + +### Giros y Productos Sugeridos + +| Giro | Productos Sugeridos | +|------|---------------------| +| Abarrotes | Refrescos, Galletas, Pan, Leche, Huevos | +| Papeleria | Cuadernos, Lapices, Colores, Folders | +| Ferreteria | Tornillos, Pintura, Brochas, Herramientas | +| Farmacia | Medicamentos basicos, Vitaminas | +| Comida | Tacos, Tortas, Aguas, Refrescos | + +### Componente OnboardingWizard + +```tsx +function OnboardingWizard() { + const [step, setStep] = useState(1); + const [progress, saveProgress] = useOnboardingProgress(); + + const steps = [ + { id: 1, name: 'Mi Negocio', component: BusinessSetupStep }, + { id: 2, name: 'Productos', component: ProductsStep }, + { id: 3, name: 'Pagos', component: PaymentsStep }, + { id: 4, name: 'Mi Equipo', component: TeamStep }, + { id: 5, name: 'Completar', component: CompletionStep }, + ]; + + const CurrentStep = steps[step - 1].component; + + return ( +
+ + setStep(s => s + 1)} + onSkip={() => setStep(s => s + 1)} + saveProgress={saveProgress} + /> +
+ ); +} +``` + +### Animacion de Celebracion + +Usar libreria `canvas-confetti` para el efecto de confetti al completar: + +```tsx +import confetti from 'canvas-confetti'; + +function CompletionStep() { + useEffect(() => { + confetti({ + particleCount: 100, + spread: 70, + origin: { y: 0.6 } + }); + }, []); + + return ( +
+

Tu negocio esta listo

+ +
+ ); +} +``` + +--- + +**Ultima actualizacion:** 2026-01-10 +**Autor:** Architecture Team diff --git a/docs/01-epicas/_MAP.md b/docs/01-epicas/_MAP.md index 63a6e972f..1831b0adb 100644 --- a/docs/01-epicas/_MAP.md +++ b/docs/01-epicas/_MAP.md @@ -95,13 +95,23 @@ | MCH-024 | CoDi y SPEI | QR de cobro, CLABE virtual | P2 | | MCH-025 | Widgets y Atajos | Android widgets, quick actions | P2 | -### FASE 7: EXPANSIÓN (Futuro) +### FASE 7: EXPANSIÓN | ID | Épica | Descripción | Prioridad | |----|-------|-------------|-----------| | MCH-026 | Multi-idioma LATAM | i18n, localización | P3 | | MCH-027 | Integración SAT | Facturación simplificada | P3 | | MCH-028 | Marketplace Proveedores | Conexión con distribuidores | P3 | +| MCH-029 | Infraestructura SaaS Avanzada | Email, Storage, Redis, Webhooks, Rate Limiting | P0 | +| MCH-030 | Auth Social (OAuth 2.0) | Login con Google/Apple | P1 | +| MCH-031 | Auditoria Empresarial | Audit logs, retencion, compliance | P1 | +| MCH-032 | Feature Flags por Plan | Toggles por plan/tenant | P1 | + +### FASE 8: MEJORAS UX + +| ID | Épica | Descripción | Prioridad | +|----|-------|-------------|-----------| +| MCH-033 | Onboarding Wizard | Guia interactiva de setup | P2 | ## Índice de Archivos de Épicas @@ -135,7 +145,12 @@ docs/01-epicas/ ├── MCH-025-widgets-atajos.md ├── MCH-026-multi-idioma-latam.md ├── MCH-027-integracion-sat.md -└── MCH-028-marketplace-proveedores.md +├── MCH-028-marketplace-proveedores.md +├── MCH-029-infraestructura-saas.md +├── MCH-030-auth-social.md +├── MCH-031-auditoria-empresarial.md +├── MCH-032-feature-flags.md +└── MCH-033-onboarding-wizard.md ``` ## Dependencias entre Épicas @@ -177,5 +192,6 @@ MCH-018 ─────┬─────► MCH-019 ─────► MCH-020 --- -**Versión**: 2.0.0 +**Versión**: 3.0.0 **Última actualización**: 2026-01-10 +**Total Épicas**: 33 (MCH-001 a MCH-033) diff --git a/docs/02-devops/CICD-GUIDE.md b/docs/02-devops/CICD-GUIDE.md new file mode 100644 index 000000000..966f4a2fb --- /dev/null +++ b/docs/02-devops/CICD-GUIDE.md @@ -0,0 +1,81 @@ +# CI/CD Guide - MiChangarrito + +## Overview + +Este documento describe la configuracion de CI/CD para michangarrito. + +## Stack DevOps + +- **Container Runtime**: Docker +- **Orchestration**: Docker Compose (desarrollo), Kubernetes (produccion) +- **CI/CD**: GitHub Actions +- **Registry**: Docker Hub / GitHub Container Registry +- **Mobile**: Expo EAS Build + +## Pipelines + +### Build Pipeline + +```yaml +# .github/workflows/build.yml +name: Build +on: [push, pull_request] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '20' + - run: npm ci + - run: npm run build + - run: npm test +``` + +### Mobile Build (Expo EAS) + +```yaml +# .github/workflows/mobile.yml +name: Mobile Build +on: + push: + branches: [main] +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: expo/expo-github-action@v8 + with: + eas-version: latest + token: ${{ secrets.EXPO_TOKEN }} + - run: eas build --platform all --non-interactive +``` + +## Environments + +| Environment | Branch | URL | +|-------------|--------|-----| +| Development | develop | localhost | +| Staging | staging | TBD | +| Production | main | TBD | + +## Apps + +| App | Tipo | Build | +|-----|------|-------| +| backend | NestJS | Docker | +| web | React | Docker | +| mobile | Expo | EAS Build | +| mcp-server | TypeScript | Docker | +| whatsapp-service | NestJS | Docker | + +## Docker Setup + +Ver [DOCKER-SETUP.md](./DOCKER-SETUP.md) + +--- + +**Ultima actualizacion**: 2026-01-10 +**Estado**: Placeholder - Completar con detalles del proyecto diff --git a/docs/02-devops/DOCKER-SETUP.md b/docs/02-devops/DOCKER-SETUP.md new file mode 100644 index 000000000..6d1e0d5b3 --- /dev/null +++ b/docs/02-devops/DOCKER-SETUP.md @@ -0,0 +1,96 @@ +# Docker Setup - MiChangarrito + +## Overview + +Configuracion de Docker para desarrollo y produccion. + +## Servicios + +| Servicio | Puerto | Descripcion | +|----------|--------|-------------| +| backend | 3141 | API NestJS | +| web | 3140 | Dashboard React | +| mcp-server | 3142 | Gateway LLM | +| whatsapp-service | 3143 | Bot WhatsApp | +| postgres | 5432 | Base de datos | +| redis | 6379 | Cache y queues | + +## Desarrollo Local + +```bash +# Iniciar todos los servicios +docker-compose up -d + +# Ver logs +docker-compose logs -f backend + +# Detener +docker-compose down +``` + +## docker-compose.yml + +```yaml +version: '3.8' +services: + backend: + build: ./apps/backend + ports: + - "3141:3141" + environment: + - NODE_ENV=development + - DATABASE_URL=postgresql://postgres:postgres@postgres:5432/michangarrito + depends_on: + - postgres + - redis + + web: + build: ./apps/web + ports: + - "3140:3140" + depends_on: + - backend + + mcp-server: + build: ./apps/mcp-server + ports: + - "3142:3142" + depends_on: + - backend + + whatsapp-service: + build: ./apps/whatsapp-service + ports: + - "3143:3143" + environment: + - WHATSAPP_TOKEN=${WHATSAPP_TOKEN} + depends_on: + - backend + - redis + + postgres: + image: postgres:16 + environment: + - POSTGRES_DB=michangarrito + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=postgres + volumes: + - postgres_data:/var/lib/postgresql/data + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + +volumes: + postgres_data: +``` + +## Produccion + +TODO: Documentar configuracion de produccion con Kubernetes + +--- + +**Ultima actualizacion**: 2026-01-10 +**Estado**: Placeholder - Completar con detalles del proyecto diff --git a/docs/02-integraciones/INT-010-email-providers.md b/docs/02-integraciones/INT-010-email-providers.md new file mode 100644 index 000000000..d413f16a3 --- /dev/null +++ b/docs/02-integraciones/INT-010-email-providers.md @@ -0,0 +1,308 @@ +--- +id: INT-010 +type: Integration +title: "Email Multi-Provider" +provider: "SendGrid/SES/SMTP" +status: Planificado +integration_type: "notifications" +created_at: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +tags: + - email + - notifications + - transactional + - multi-provider +--- + +# INT-010: Email Multi-Provider + +## Metadata + +| Campo | Valor | +|-------|-------| +| **Codigo** | INT-010 | +| **Proveedor** | SendGrid, AWS SES, SMTP | +| **Tipo** | Notificaciones | +| **Estado** | Planificado | +| **Multi-tenant** | Si | +| **Epic Relacionada** | MCH-029 | +| **Owner** | Backend Team | + +--- + +## 1. Descripcion + +Sistema de envio de emails transaccionales con soporte para multiples proveedores y fallback automatico. Incluye templates reutilizables, tracking de entrega y rate limiting por tenant. + +**Casos de uso principales:** +- Emails de bienvenida y verificacion +- Notificaciones de pedidos +- Recordatorios de pago +- Alertas de inventario bajo +- Reportes programados + +--- + +## 2. Credenciales Requeridas + +### Variables de Entorno + +| Variable | Descripcion | Tipo | Obligatorio | +|----------|-------------|------|-------------| +| `EMAIL_PROVIDER` | Proveedor principal (sendgrid/ses/smtp) | string | SI | +| `SENDGRID_API_KEY` | API Key de SendGrid | string | SI (si usa SendGrid) | +| `AWS_SES_REGION` | Region de AWS SES | string | SI (si usa SES) | +| `AWS_SES_ACCESS_KEY` | Access Key para SES | string | SI (si usa SES) | +| `AWS_SES_SECRET_KEY` | Secret Key para SES | string | SI (si usa SES) | +| `SMTP_HOST` | Host del servidor SMTP | string | SI (si usa SMTP) | +| `SMTP_PORT` | Puerto SMTP | number | SI (si usa SMTP) | +| `SMTP_USER` | Usuario SMTP | string | SI (si usa SMTP) | +| `SMTP_PASS` | Password SMTP | string | SI (si usa SMTP) | +| `EMAIL_FROM_ADDRESS` | Email de remitente | string | SI | +| `EMAIL_FROM_NAME` | Nombre de remitente | string | SI | + +### Ejemplo de .env + +```env +# Email Configuration +EMAIL_PROVIDER=sendgrid +EMAIL_FROM_ADDRESS=noreply@michangarrito.com +EMAIL_FROM_NAME=MiChangarrito + +# SendGrid +SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxx + +# AWS SES (fallback) +AWS_SES_REGION=us-east-1 +AWS_SES_ACCESS_KEY=AKIAXXXXXXXXXXXXXXXX +AWS_SES_SECRET_KEY=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx + +# SMTP (ultimo recurso) +SMTP_HOST=smtp.mailtrap.io +SMTP_PORT=587 +SMTP_USER=xxxxxxxx +SMTP_PASS=xxxxxxxx +``` + +--- + +## 3. Endpoints/SDK Utilizados + +### SendGrid + +| Operacion | Metodo | Endpoint | Descripcion | +|-----------|--------|----------|-------------| +| Enviar email | POST | `/v3/mail/send` | Envio simple o con template | +| Templates | GET | `/v3/templates` | Listar templates | + +### AWS SES + +| Operacion | SDK Method | Descripcion | +|-----------|------------|-------------| +| Enviar email | `sendEmail()` | Via AWS SDK v3 | +| Enviar raw | `sendRawEmail()` | Con attachments | + +### SMTP + +```typescript +import * as nodemailer from 'nodemailer'; + +const transporter = nodemailer.createTransport({ + host: process.env.SMTP_HOST, + port: parseInt(process.env.SMTP_PORT), + auth: { + user: process.env.SMTP_USER, + pass: process.env.SMTP_PASS, + }, +}); +``` + +--- + +## 4. Rate Limits + +| Proveedor | Limite | Periodo | Plan SendGrid | +|-----------|--------|---------|---------------| +| SendGrid | 100 | por segundo | Pro | +| SendGrid | 40,000 | por mes | Free | +| AWS SES | 14 | por segundo | Default | +| AWS SES | 50,000 | por dia | Verified | + +### Rate Limiting por Tenant + +| Plan MCH | Emails/hora | Emails/dia | +|----------|-------------|------------| +| Basic | 100 | 500 | +| Pro | 1,000 | 10,000 | +| Enterprise | Ilimitado | Ilimitado | + +--- + +## 5. Manejo de Errores + +| Codigo | Descripcion | Accion | Retry | +|--------|-------------|--------|-------| +| 400 | Email invalido | Validar formato | NO | +| 401 | API Key invalida | Verificar credencial | NO | +| 429 | Rate limit | Esperar + retry | SI | +| 500 | Error proveedor | Fallback a siguiente | SI | +| 503 | Servicio no disponible | Fallback | SI | + +### Estrategia de Fallback + +```typescript +const providers = ['sendgrid', 'ses', 'smtp']; + +async function sendWithFallback(email: EmailDto) { + for (const provider of providers) { + try { + return await this.send(provider, email); + } catch (error) { + this.logger.warn(`Provider ${provider} failed, trying next`); + continue; + } + } + throw new Error('All email providers failed'); +} +``` + +--- + +## 6. Templates + +### Templates Disponibles + +| Template | Descripcion | Variables | +|----------|-------------|-----------| +| welcome | Bienvenida | `{name}` | +| verify_email | Verificacion | `{name}, {link}` | +| order_confirmation | Pedido confirmado | `{order_id}, {items}` | +| payment_reminder | Recordatorio pago | `{amount}, {due_date}` | +| low_inventory | Alerta inventario | `{product}, {quantity}` | + +### Formato de Template (Handlebars) + +```html +

Hola {{name}}

+

Tu pedido #{{order_id}} ha sido confirmado.

+{{#each items}} +
  • {{this.name}} x {{this.quantity}}
  • +{{/each}} +``` + +--- + +## 7. Multi-tenant + +### Modelo de Credenciales + +- [x] **Global:** Credenciales compartidas por defecto +- [x] **Por Tenant:** Tenant puede configurar sus propias credenciales +- [x] **Branding:** From address personalizable por tenant (Enterprise) + +### Almacenamiento + +```sql +CREATE TABLE messaging.tenant_email_config ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID REFERENCES auth.tenants(id) NOT NULL, + provider VARCHAR(20) NOT NULL, -- sendgrid, ses, smtp + from_address VARCHAR(255) NOT NULL, + from_name VARCHAR(100), + credentials JSONB NOT NULL, -- Encriptado + is_active BOOLEAN DEFAULT true, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + UNIQUE(tenant_id) +); +``` + +--- + +## 8. Tablas de BD + +### email_templates + +```sql +CREATE TABLE messaging.email_templates ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID REFERENCES auth.tenants(id), + key VARCHAR(100) NOT NULL, + name VARCHAR(255) NOT NULL, + subject VARCHAR(255) NOT NULL, + html_content TEXT NOT NULL, + text_content TEXT, + variables JSONB, + is_active BOOLEAN DEFAULT true, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + UNIQUE(tenant_id, key) +); +``` + +### email_logs + +```sql +CREATE TABLE messaging.email_logs ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + template_id UUID REFERENCES messaging.email_templates(id), + to_address VARCHAR(255) NOT NULL, + subject VARCHAR(255) NOT NULL, + provider VARCHAR(20) NOT NULL, + provider_message_id VARCHAR(255), + status VARCHAR(20) NOT NULL, -- sent, delivered, opened, clicked, bounced, failed + error_message TEXT, + sent_at TIMESTAMP WITH TIME ZONE, + delivered_at TIMESTAMP WITH TIME ZONE, + opened_at TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); +``` + +--- + +## 9. Testing + +### Modo Sandbox + +| Ambiente | Proveedor | Comportamiento | +|----------|-----------|----------------| +| Development | Mailtrap | Emails atrapados, no enviados | +| Staging | SendGrid Sandbox | Emails simulados | +| Production | SendGrid/SES | Envio real | + +### Test de Conexion + +```bash +# Test SendGrid +curl -X POST "https://api.sendgrid.com/v3/mail/send" \ + -H "Authorization: Bearer $SENDGRID_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"personalizations":[{"to":[{"email":"test@example.com"}]}],"from":{"email":"noreply@michangarrito.com"},"subject":"Test","content":[{"type":"text/plain","value":"Test email"}]}' +``` + +--- + +## 10. Monitoreo + +### Metricas + +| Metrica | Descripcion | Alerta | +|---------|-------------|--------| +| email_sent_total | Emails enviados | - | +| email_failed_total | Emails fallidos | > 5% | +| email_delivery_rate | Tasa de entrega | < 95% | +| email_bounce_rate | Tasa de rebote | > 5% | + +--- + +## 11. Referencias + +- [SendGrid API Docs](https://docs.sendgrid.com/api-reference) +- [AWS SES Developer Guide](https://docs.aws.amazon.com/ses/) +- [Nodemailer Docs](https://nodemailer.com/) + +--- + +**Ultima actualizacion:** 2026-01-10 +**Autor:** Backend Team diff --git a/docs/02-integraciones/INT-011-storage-cloud.md b/docs/02-integraciones/INT-011-storage-cloud.md new file mode 100644 index 000000000..6cf51f659 --- /dev/null +++ b/docs/02-integraciones/INT-011-storage-cloud.md @@ -0,0 +1,328 @@ +--- +id: INT-011 +type: Integration +title: "Storage Cloud" +provider: "AWS S3/Cloudflare R2/MinIO" +status: Planificado +integration_type: "storage" +created_at: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +tags: + - 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 + +```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 + +```typescript +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 + +```typescript +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 + +```typescript +async function getSignedUploadUrl( + tenantId: string, + filename: string, + contentType: string, + expiresIn: number = 3600 +): Promise { + 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 + +```sql +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 + +```yaml +# 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 + +```bash +# 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 + +- [AWS S3 Developer Guide](https://docs.aws.amazon.com/s3/) +- [Cloudflare R2 Docs](https://developers.cloudflare.com/r2/) +- [MinIO Docs](https://min.io/docs/minio/linux/index.html) +- [ADR-0006: Storage Abstraction](../97-adr/ADR-0006-storage-abstraction.md) + +--- + +**Ultima actualizacion:** 2026-01-10 +**Autor:** Backend Team diff --git a/docs/02-integraciones/INT-012-oauth-social.md b/docs/02-integraciones/INT-012-oauth-social.md new file mode 100644 index 000000000..bec78b0e5 --- /dev/null +++ b/docs/02-integraciones/INT-012-oauth-social.md @@ -0,0 +1,363 @@ +--- +id: INT-012 +type: Integration +title: "OAuth Social" +provider: "Google/Apple" +status: Planificado +integration_type: "auth" +created_at: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +tags: + - oauth + - authentication + - google + - apple + - social-login +--- + +# INT-012: OAuth Social + +## Metadata + +| Campo | Valor | +|-------|-------| +| **Codigo** | INT-012 | +| **Proveedor** | Google, Apple | +| **Tipo** | Autenticacion | +| **Estado** | Planificado | +| **Multi-tenant** | Si | +| **Epic Relacionada** | MCH-030 | +| **Owner** | Backend Team | + +--- + +## 1. Descripcion + +Integracion OAuth 2.0 para login social con Google y Apple. Permite a los usuarios registrarse e iniciar sesion con un clic usando sus cuentas existentes. + +**Casos de uso principales:** +- Registro simplificado (un clic) +- Login sin password +- Vinculacion de cuenta social a cuenta existente +- Sync de perfil (nombre, foto) + +--- + +## 2. Credenciales Requeridas + +### Google OAuth + +| Variable | Descripcion | Tipo | Obligatorio | +|----------|-------------|------|-------------| +| `GOOGLE_CLIENT_ID` | Client ID de Google Cloud | string | SI | +| `GOOGLE_CLIENT_SECRET` | Client Secret | string | SI | +| `GOOGLE_CALLBACK_URL` | URL de callback | string | SI | + +### Apple Sign-In + +| Variable | Descripcion | Tipo | Obligatorio | +|----------|-------------|------|-------------| +| `APPLE_CLIENT_ID` | Service ID (web) o App ID (iOS) | string | SI | +| `APPLE_TEAM_ID` | Team ID de Apple Developer | string | SI | +| `APPLE_KEY_ID` | Key ID del private key | string | SI | +| `APPLE_PRIVATE_KEY` | Private key (.p8 content) | string | SI | +| `APPLE_CALLBACK_URL` | URL de callback | string | SI | + +### Ejemplo de .env + +```env +# Google OAuth +GOOGLE_CLIENT_ID=xxxx.apps.googleusercontent.com +GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxx +GOOGLE_CALLBACK_URL=https://api.michangarrito.com/auth/google/callback + +# Apple Sign-In +APPLE_CLIENT_ID=com.michangarrito.web +APPLE_TEAM_ID=XXXXXXXXXX +APPLE_KEY_ID=XXXXXXXXXX +APPLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIGT....\n-----END PRIVATE KEY-----" +APPLE_CALLBACK_URL=https://api.michangarrito.com/auth/apple/callback +``` + +--- + +## 3. Flujo OAuth 2.0 + +### Google + +``` +┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ +│ Client │────>│ /auth/ │────>│ Google │────>│ Callback │ +│ (Web/ │ │ google │ │ OAuth │ │ /auth/ │ +│ Mobile) │ │ │ │ Screen │ │ google/ │ +└──────────┘ └──────────┘ └──────────┘ │ callback │ + └────┬─────┘ + │ + ▼ +┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ +│ JWT │<────│ Create/ │<────│ Verify │<────│ Get │ +│ Token │ │ Link │ │ Token │ │ Profile │ +│ │ │ User │ │ │ │ │ +└──────────┘ └──────────┘ └──────────┘ └──────────┘ +``` + +### Endpoints + +| Ruta | Metodo | Descripcion | +|------|--------|-------------| +| `/auth/google` | GET | Inicia flujo OAuth Google | +| `/auth/google/callback` | GET | Callback de Google | +| `/auth/apple` | GET | Inicia flujo Apple Sign-In | +| `/auth/apple/callback` | POST | Callback de Apple | +| `/auth/link/:provider` | POST | Vincular cuenta social | +| `/auth/unlink/:provider` | DELETE | Desvincular cuenta | + +--- + +## 4. Implementacion + +### Passport.js Strategies + +```typescript +// Google Strategy +import { Strategy as GoogleStrategy } from 'passport-google-oauth20'; + +passport.use(new GoogleStrategy({ + clientID: process.env.GOOGLE_CLIENT_ID, + clientSecret: process.env.GOOGLE_CLIENT_SECRET, + callbackURL: process.env.GOOGLE_CALLBACK_URL, + scope: ['profile', 'email'], + }, + async (accessToken, refreshToken, profile, done) => { + const user = await findOrCreateUser({ + provider: 'google', + providerId: profile.id, + email: profile.emails[0].value, + name: profile.displayName, + avatar: profile.photos[0]?.value, + }); + return done(null, user); + } +)); +``` + +```typescript +// Apple Strategy +import { Strategy as AppleStrategy } from 'passport-apple'; + +passport.use(new AppleStrategy({ + clientID: process.env.APPLE_CLIENT_ID, + teamID: process.env.APPLE_TEAM_ID, + keyID: process.env.APPLE_KEY_ID, + privateKeyString: process.env.APPLE_PRIVATE_KEY, + callbackURL: process.env.APPLE_CALLBACK_URL, + scope: ['name', 'email'], + }, + async (accessToken, refreshToken, idToken, profile, done) => { + // Apple solo envia nombre en primer login + const user = await findOrCreateUser({ + provider: 'apple', + providerId: profile.id, + email: profile.email, + name: profile.name?.firstName, + }); + return done(null, user); + } +)); +``` + +--- + +## 5. Manejo de Errores + +| Error | Descripcion | Accion | +|-------|-------------|--------| +| access_denied | Usuario cancelo | Redirect a login | +| invalid_request | Parametros incorrectos | Log + error page | +| server_error | Error del provider | Retry o fallback | +| email_exists | Email ya registrado | Ofrecer vincular | + +### Error Handling + +```typescript +@Get('google/callback') +@UseGuards(AuthGuard('google')) +async googleCallback( + @Req() req: Request, + @Res() res: Response, +) { + try { + const jwt = await this.authService.generateJwt(req.user); + res.redirect(`${FRONTEND_URL}/auth/callback?token=${jwt}`); + } catch (error) { + if (error instanceof EmailExistsError) { + res.redirect(`${FRONTEND_URL}/auth/link?provider=google&email=${error.email}`); + } else { + res.redirect(`${FRONTEND_URL}/auth/error?code=${error.code}`); + } + } +} +``` + +--- + +## 6. Tabla oauth_accounts + +```sql +CREATE TABLE auth.oauth_accounts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID REFERENCES auth.users(id) NOT NULL, + provider VARCHAR(20) NOT NULL, -- google, apple + provider_user_id VARCHAR(255) NOT NULL, + email VARCHAR(255), + name VARCHAR(255), + avatar_url TEXT, + access_token TEXT, + refresh_token TEXT, + expires_at TIMESTAMP WITH TIME ZONE, + raw_profile JSONB, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + UNIQUE(provider, provider_user_id), + UNIQUE(user_id, provider) +); + +CREATE INDEX idx_oauth_accounts_user ON auth.oauth_accounts(user_id); +``` + +--- + +## 7. Multi-tenant + +### Comportamiento + +- OAuth es a nivel de **usuario**, no de tenant +- Un usuario puede pertenecer a multiples tenants +- Al login, se selecciona tenant activo + +### Flujo Multi-tenant + +``` +1. Usuario hace login con Google +2. Sistema busca/crea usuario por email +3. Si usuario tiene multiples tenants: + - Redirect a selector de tenant +4. Si usuario tiene un solo tenant: + - Login directo a ese tenant +5. Si usuario no tiene tenant: + - Crear tenant o unirse a invitacion pendiente +``` + +--- + +## 8. Mobile Implementation + +### Expo/React Native (Google) + +```typescript +import * as Google from 'expo-auth-session/providers/google'; + +const [request, response, promptAsync] = Google.useAuthRequest({ + clientId: GOOGLE_CLIENT_ID, + iosClientId: GOOGLE_IOS_CLIENT_ID, + androidClientId: GOOGLE_ANDROID_CLIENT_ID, +}); + +const handleGoogleLogin = async () => { + const result = await promptAsync(); + if (result.type === 'success') { + const { id_token } = result.params; + await api.post('/auth/google/mobile', { id_token }); + } +}; +``` + +### iOS Native (Apple) + +```typescript +import * as AppleAuthentication from 'expo-apple-authentication'; + +const handleAppleLogin = async () => { + const credential = await AppleAuthentication.signInAsync({ + requestedScopes: [ + AppleAuthentication.AppleAuthenticationScope.FULL_NAME, + AppleAuthentication.AppleAuthenticationScope.EMAIL, + ], + }); + + await api.post('/auth/apple/mobile', { + identityToken: credential.identityToken, + fullName: credential.fullName, + }); +}; +``` + +--- + +## 9. Testing + +### Mock Providers + +```typescript +// test/mocks/google-oauth.mock.ts +export const mockGoogleProfile = { + id: 'google-123', + displayName: 'Test User', + emails: [{ value: 'test@gmail.com', verified: true }], + photos: [{ value: 'https://photo.url' }], +}; +``` + +### Test de Integracion + +```typescript +describe('Google OAuth', () => { + it('should create new user on first login', async () => { + const response = await request(app) + .get('/auth/google/callback') + .query({ code: 'mock-code' }); + + expect(response.status).toBe(302); + expect(response.headers.location).toContain('token='); + }); +}); +``` + +--- + +## 10. Configuracion de Consolas + +### Google Cloud Console + +1. Ir a [Google Cloud Console](https://console.cloud.google.com) +2. Crear proyecto o seleccionar existente +3. APIs & Services > Credentials +4. Create Credentials > OAuth Client ID +5. Application type: Web application +6. Authorized redirect URIs: + - `https://api.michangarrito.com/auth/google/callback` + - `http://localhost:3000/auth/google/callback` + +### Apple Developer + +1. Ir a [Apple Developer](https://developer.apple.com) +2. Certificates, Identifiers & Profiles +3. Identifiers > App IDs > Agregar Sign in with Apple capability +4. Identifiers > Services IDs > Crear para web +5. Keys > Crear key con Sign in with Apple +6. Descargar .p8 (solo se puede una vez) + +--- + +## 11. Referencias + +- [Google Identity Platform](https://developers.google.com/identity) +- [Sign in with Apple](https://developer.apple.com/sign-in-with-apple/) +- [Passport.js Google OAuth](http://www.passportjs.org/packages/passport-google-oauth20/) +- [ADR-0010: OAuth Social Strategy](../97-adr/ADR-0010-oauth-social.md) + +--- + +**Ultima actualizacion:** 2026-01-10 +**Autor:** Backend Team diff --git a/docs/02-integraciones/INT-013-redis-cache.md b/docs/02-integraciones/INT-013-redis-cache.md new file mode 100644 index 000000000..544f1de29 --- /dev/null +++ b/docs/02-integraciones/INT-013-redis-cache.md @@ -0,0 +1,413 @@ +--- +id: INT-013 +type: Integration +title: "Redis Cache" +provider: "Redis" +status: Planificado +integration_type: "infrastructure" +created_at: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +tags: + - redis + - cache + - queue + - bullmq + - infrastructure +--- + +# INT-013: Redis Cache + +## Metadata + +| Campo | Valor | +|-------|-------| +| **Codigo** | INT-013 | +| **Proveedor** | Redis | +| **Tipo** | Infraestructura | +| **Estado** | Planificado | +| **Multi-tenant** | Si | +| **Epic Relacionada** | MCH-029 | +| **Owner** | Backend Team | + +--- + +## 1. Descripcion + +Redis como servicio de cache y queue para mejorar rendimiento y procesar tareas en background. Utilizado para cache de sesiones, configuracion de tenants, y colas de trabajo con BullMQ. + +**Casos de uso principales:** +- Cache de sesiones JWT +- Cache de configuracion de tenant +- Cache de feature flags +- Queue para emails, webhooks, notificaciones +- Rate limiting counters +- Pub/Sub para eventos real-time + +--- + +## 2. Credenciales Requeridas + +### Variables de Entorno + +| Variable | Descripcion | Tipo | Obligatorio | +|----------|-------------|------|-------------| +| `REDIS_HOST` | Host del servidor Redis | string | SI | +| `REDIS_PORT` | Puerto (default 6379) | number | SI | +| `REDIS_PASSWORD` | Password (si auth habilitado) | string | NO | +| `REDIS_DB` | Database number (0-15) | number | NO | +| `REDIS_TLS` | Usar TLS | boolean | NO | +| `REDIS_URL` | URL completa (alternativa) | string | NO | + +### Ejemplo de .env + +```env +# Redis Configuration +REDIS_HOST=localhost +REDIS_PORT=6379 +REDIS_PASSWORD= +REDIS_DB=0 + +# O usar URL completa +# REDIS_URL=redis://:password@host:6379/0 + +# Redis Cloud (produccion) +# REDIS_URL=rediss://:password@redis-12345.cloud.redislabs.com:12345 +``` + +--- + +## 3. Configuracion NestJS + +### Redis Module + +```typescript +// redis.module.ts +import { Module, Global } from '@nestjs/common'; +import { Redis } from 'ioredis'; + +@Global() +@Module({ + providers: [ + { + provide: 'REDIS_CLIENT', + useFactory: () => { + return new Redis({ + host: process.env.REDIS_HOST, + port: parseInt(process.env.REDIS_PORT), + password: process.env.REDIS_PASSWORD, + db: parseInt(process.env.REDIS_DB || '0'), + }); + }, + }, + ], + exports: ['REDIS_CLIENT'], +}) +export class RedisModule {} +``` + +### Cache Service + +```typescript +// cache.service.ts +@Injectable() +export class CacheService { + constructor(@Inject('REDIS_CLIENT') private readonly redis: Redis) {} + + async get(key: string): Promise { + const data = await this.redis.get(key); + return data ? JSON.parse(data) : null; + } + + async set(key: string, value: any, ttlSeconds?: number): Promise { + const data = JSON.stringify(value); + if (ttlSeconds) { + await this.redis.setex(key, ttlSeconds, data); + } else { + await this.redis.set(key, data); + } + } + + async del(key: string): Promise { + await this.redis.del(key); + } + + async invalidatePattern(pattern: string): Promise { + const keys = await this.redis.keys(pattern); + if (keys.length > 0) { + await this.redis.del(...keys); + } + } +} +``` + +--- + +## 4. BullMQ Queues + +### Configuracion + +```typescript +// queue.module.ts +import { BullModule } from '@nestjs/bullmq'; + +@Module({ + imports: [ + BullModule.forRoot({ + connection: { + host: process.env.REDIS_HOST, + port: parseInt(process.env.REDIS_PORT), + password: process.env.REDIS_PASSWORD, + }, + }), + BullModule.registerQueue( + { name: 'email' }, + { name: 'webhooks' }, + { name: 'notifications' }, + { name: 'cleanup' }, + ), + ], +}) +export class QueueModule {} +``` + +### Processor + +```typescript +// email.processor.ts +@Processor('email') +export class EmailProcessor { + @Process('send') + async handleSend(job: Job) { + const { to, template, variables } = job.data; + await this.emailService.send(to, template, variables); + } +} +``` + +### Agregar a Queue + +```typescript +// Agregar job +await this.emailQueue.add('send', { + to: 'user@example.com', + template: 'welcome', + variables: { name: 'John' }, +}, { + attempts: 3, + backoff: { + type: 'exponential', + delay: 1000, + }, +}); +``` + +--- + +## 5. Estructura de Keys + +### Convenciones de Naming + +``` +{prefix}:{scope}:{identifier}:{type} + +Ejemplos: +- mch:session:{userId} +- mch:tenant:{tenantId}:config +- mch:tenant:{tenantId}:flags +- mch:rate:{tenantId}:{endpoint}:count +- mch:cache:products:{productId} +``` + +### Keys por Funcionalidad + +| Prefijo | TTL | Descripcion | +|---------|-----|-------------| +| mch:session:* | 24h | Sesiones de usuario | +| mch:tenant:*:config | 1h | Config de tenant | +| mch:tenant:*:flags | 5m | Feature flags | +| mch:rate:* | 1m | Rate limiting | +| mch:cache:* | 15m | Cache general | + +--- + +## 6. Rate Limiting + +### Implementacion Token Bucket + +```typescript +@Injectable() +export class RateLimitService { + async isAllowed( + tenantId: string, + endpoint: string, + limit: number, + windowSeconds: number, + ): Promise<{ allowed: boolean; remaining: number; resetAt: Date }> { + const key = `mch:rate:${tenantId}:${endpoint}`; + const now = Date.now(); + const windowStart = now - (windowSeconds * 1000); + + // Remover entradas viejas + await this.redis.zremrangebyscore(key, 0, windowStart); + + // Contar requests en ventana + const count = await this.redis.zcard(key); + + if (count >= limit) { + const oldestEntry = await this.redis.zrange(key, 0, 0, 'WITHSCORES'); + const resetAt = new Date(parseInt(oldestEntry[1]) + (windowSeconds * 1000)); + return { allowed: false, remaining: 0, resetAt }; + } + + // Agregar request actual + await this.redis.zadd(key, now, `${now}`); + await this.redis.expire(key, windowSeconds); + + return { + allowed: true, + remaining: limit - count - 1, + resetAt: new Date(now + (windowSeconds * 1000)), + }; + } +} +``` + +--- + +## 7. Multi-tenant + +### Aislamiento por Prefijo + +```typescript +class TenantCacheService { + private getKey(tenantId: string, key: string): string { + return `mch:tenant:${tenantId}:${key}`; + } + + async getTenantConfig(tenantId: string): Promise { + const key = this.getKey(tenantId, 'config'); + const cached = await this.cache.get(key); + + if (cached) return cached; + + const config = await this.db.getTenantConfig(tenantId); + await this.cache.set(key, config, 3600); // 1 hora + return config; + } + + async invalidateTenantCache(tenantId: string): Promise { + await this.cache.invalidatePattern(`mch:tenant:${tenantId}:*`); + } +} +``` + +--- + +## 8. Health Check + +```typescript +// redis.health.ts +@Injectable() +export class RedisHealthIndicator extends HealthIndicator { + constructor(@Inject('REDIS_CLIENT') private readonly redis: Redis) { + super(); + } + + async isHealthy(key: string): Promise { + try { + await this.redis.ping(); + return this.getStatus(key, true); + } catch (error) { + return this.getStatus(key, false, { error: error.message }); + } + } +} +``` + +### Endpoint + +```typescript +@Get('health') +@HealthCheck() +async check() { + return this.health.check([ + () => this.redis.isHealthy('redis'), + ]); +} +``` + +--- + +## 9. Monitoreo + +### Metricas + +| Metrica | Descripcion | Alerta | +|---------|-------------|--------| +| redis_connected | Conexion activa | false | +| redis_memory_bytes | Memoria usada | > 80% max | +| redis_keys_total | Total de keys | - | +| redis_cache_hits | Cache hits | - | +| redis_cache_misses | Cache misses | ratio < 80% | +| redis_queue_length | Jobs en queue | > 1000 | + +### Comandos de Monitoreo + +```bash +# Info general +redis-cli INFO + +# Memory +redis-cli INFO memory + +# Keys por patron +redis-cli KEYS "mch:tenant:*" | wc -l + +# Queue length +redis-cli LLEN bull:email:wait +``` + +--- + +## 10. Testing + +### Docker Compose + +```yaml +services: + redis: + image: redis:7-alpine + ports: + - "6379:6379" + volumes: + - redis_data:/data +``` + +### Mock para Tests + +```typescript +// test/mocks/redis.mock.ts +export const mockRedis = { + get: jest.fn(), + set: jest.fn(), + setex: jest.fn(), + del: jest.fn(), + keys: jest.fn().mockResolvedValue([]), + ping: jest.fn().mockResolvedValue('PONG'), +}; +``` + +--- + +## 11. Referencias + +- [Redis Documentation](https://redis.io/docs/) +- [ioredis](https://github.com/redis/ioredis) +- [BullMQ](https://docs.bullmq.io/) +- [NestJS Caching](https://docs.nestjs.com/techniques/caching) + +--- + +**Ultima actualizacion:** 2026-01-10 +**Autor:** Backend Team diff --git a/docs/02-integraciones/INT-014-webhooks-outbound.md b/docs/02-integraciones/INT-014-webhooks-outbound.md new file mode 100644 index 000000000..ecec9c0a1 --- /dev/null +++ b/docs/02-integraciones/INT-014-webhooks-outbound.md @@ -0,0 +1,429 @@ +--- +id: INT-014 +type: Integration +title: "Webhooks Outbound" +provider: "BullMQ" +status: Planificado +integration_type: "events" +created_at: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +tags: + - webhooks + - events + - bullmq + - integration + - outbound +--- + +# INT-014: Webhooks Outbound + +## Metadata + +| Campo | Valor | +|-------|-------| +| **Codigo** | INT-014 | +| **Proveedor** | BullMQ (interno) | +| **Tipo** | Eventos | +| **Estado** | Planificado | +| **Multi-tenant** | Si | +| **Epic Relacionada** | MCH-029 | +| **Owner** | Backend Team | + +--- + +## 1. Descripcion + +Sistema de webhooks outbound que permite a tenants recibir notificaciones HTTP cuando ocurren eventos en el sistema. Incluye firma de payloads, reintentos con backoff y logs de entrega. + +**Casos de uso principales:** +- Notificar sistemas externos de nuevos pedidos +- Sincronizar inventario con ERP +- Integraciones con Zapier/Make +- Alertas personalizadas + +--- + +## 2. Eventos Disponibles + +### Eventos por Categoria + +| Categoria | Evento | Payload | +|-----------|--------|---------| +| **Orders** | order.created | Order completa | +| | order.updated | Order con cambios | +| | order.completed | Order finalizada | +| | order.cancelled | Order cancelada | +| **Products** | product.created | Producto nuevo | +| | product.updated | Producto modificado | +| | product.deleted | Producto eliminado | +| | product.low_stock | Stock bajo minimo | +| **Payments** | payment.received | Pago recibido | +| | payment.failed | Pago fallido | +| | payment.refunded | Pago reembolsado | +| **Customers** | customer.created | Cliente nuevo | +| | customer.updated | Cliente modificado | + +--- + +## 3. Configuracion de Endpoints + +### API de Configuracion + +```typescript +// POST /api/webhooks/endpoints +{ + "url": "https://example.com/webhook", + "events": ["order.created", "payment.received"], + "secret": "whsec_xxxxxxxx", // Generado si no se provee + "description": "Mi integracion", + "is_active": true +} + +// Response +{ + "id": "wh_123abc", + "url": "https://example.com/webhook", + "events": ["order.created", "payment.received"], + "secret": "whsec_xxxxxxxx", + "is_active": true, + "created_at": "2026-01-10T10:00:00Z" +} +``` + +### Endpoints CRUD + +| Metodo | Ruta | Descripcion | +|--------|------|-------------| +| GET | /api/webhooks/endpoints | Listar endpoints | +| POST | /api/webhooks/endpoints | Crear endpoint | +| GET | /api/webhooks/endpoints/:id | Obtener endpoint | +| PATCH | /api/webhooks/endpoints/:id | Actualizar endpoint | +| DELETE | /api/webhooks/endpoints/:id | Eliminar endpoint | +| GET | /api/webhooks/deliveries | Listar entregas | +| POST | /api/webhooks/endpoints/:id/test | Enviar test | + +--- + +## 4. Payload de Webhook + +### Estructura + +```json +{ + "id": "evt_123abc", + "type": "order.created", + "created_at": "2026-01-10T10:00:00Z", + "data": { + "id": "ord_456def", + "total": 150.00, + "items": [...], + "customer": {...} + }, + "tenant_id": "550e8400-e29b-41d4-a716-446655440000" +} +``` + +### Headers + +| Header | Valor | Descripcion | +|--------|-------|-------------| +| `Content-Type` | application/json | Tipo de contenido | +| `X-Webhook-Id` | evt_123abc | ID del evento | +| `X-Webhook-Timestamp` | 1704880800 | Unix timestamp | +| `X-Webhook-Signature` | sha256=xxx | Firma HMAC | +| `User-Agent` | MiChangarrito/1.0 | Identificador | + +--- + +## 5. Firma de Payloads + +### Generacion de Firma + +```typescript +function signPayload( + payload: string, + secret: string, + timestamp: number, +): string { + const signedPayload = `${timestamp}.${payload}`; + const signature = crypto + .createHmac('sha256', secret) + .update(signedPayload) + .digest('hex'); + + return `sha256=${signature}`; +} +``` + +### Verificacion en Cliente + +```typescript +// Ejemplo para el receptor del webhook +function verifySignature( + payload: string, + signature: string, + secret: string, + timestamp: number, + tolerance: number = 300, // 5 minutos +): boolean { + const currentTime = Math.floor(Date.now() / 1000); + + // Verificar que no sea muy viejo + if (currentTime - timestamp > tolerance) { + throw new Error('Timestamp too old'); + } + + const expected = signPayload(payload, secret, timestamp); + return crypto.timingSafeEqual( + Buffer.from(signature), + Buffer.from(expected), + ); +} +``` + +--- + +## 6. Estrategia de Reintentos + +### Exponential Backoff + +``` +Intento 1: Inmediato +Intento 2: 1 segundo +Intento 3: 2 segundos +Intento 4: 4 segundos +Intento 5: 8 segundos +Intento 6: 16 segundos (maximo) +``` + +### Configuracion BullMQ + +```typescript +await this.webhookQueue.add('deliver', { + endpointId: endpoint.id, + eventId: event.id, + payload: event.data, +}, { + attempts: 6, + backoff: { + type: 'exponential', + delay: 1000, + }, + removeOnComplete: true, + removeOnFail: false, // Mantener para logs +}); +``` + +### Codigos de Respuesta + +| Codigo | Accion | Descripcion | +|--------|--------|-------------| +| 2xx | Exito | Entrega exitosa | +| 3xx | Retry | Seguir redirecciones | +| 4xx | Fallo | No reintentar (excepto 429) | +| 429 | Retry | Rate limited, esperar | +| 5xx | Retry | Error del servidor | +| Timeout | Retry | Esperar siguiente intento | + +--- + +## 7. Tabla de BD + +### Schema webhooks + +```sql +CREATE SCHEMA IF NOT EXISTS webhooks; + +CREATE TABLE webhooks.endpoints ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + url VARCHAR(2000) NOT NULL, + description VARCHAR(255), + events TEXT[] NOT NULL, + secret VARCHAR(255) NOT NULL, + is_active BOOLEAN DEFAULT true, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +CREATE TABLE webhooks.deliveries ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + endpoint_id UUID REFERENCES webhooks.endpoints(id), + event_type VARCHAR(100) NOT NULL, + event_id VARCHAR(100) NOT NULL, + payload JSONB NOT NULL, + status VARCHAR(20) NOT NULL, -- pending, success, failed + attempts INTEGER DEFAULT 0, + last_attempt_at TIMESTAMP WITH TIME ZONE, + response_status INTEGER, + response_body TEXT, + error_message TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + completed_at TIMESTAMP WITH TIME ZONE +); + +CREATE INDEX idx_deliveries_endpoint ON webhooks.deliveries(endpoint_id); +CREATE INDEX idx_deliveries_status ON webhooks.deliveries(status); +``` + +--- + +## 8. Procesador de Webhooks + +```typescript +@Processor('webhooks') +export class WebhookProcessor { + constructor( + private readonly httpService: HttpService, + private readonly webhookService: WebhookService, + ) {} + + @Process('deliver') + async handleDelivery(job: Job) { + const { endpointId, eventId, payload } = job.data; + + const endpoint = await this.webhookService.getEndpoint(endpointId); + if (!endpoint.is_active) return; + + const timestamp = Math.floor(Date.now() / 1000); + const payloadString = JSON.stringify(payload); + const signature = this.signPayload(payloadString, endpoint.secret, timestamp); + + try { + const response = await this.httpService.axiosRef.post( + endpoint.url, + payload, + { + headers: { + 'Content-Type': 'application/json', + 'X-Webhook-Id': eventId, + 'X-Webhook-Timestamp': timestamp.toString(), + 'X-Webhook-Signature': signature, + 'User-Agent': 'MiChangarrito/1.0', + }, + timeout: 30000, // 30 segundos + }, + ); + + await this.webhookService.logDelivery(endpointId, eventId, { + status: 'success', + responseStatus: response.status, + attempts: job.attemptsMade + 1, + }); + + } catch (error) { + const shouldRetry = this.shouldRetry(error); + + await this.webhookService.logDelivery(endpointId, eventId, { + status: shouldRetry ? 'pending' : 'failed', + responseStatus: error.response?.status, + errorMessage: error.message, + attempts: job.attemptsMade + 1, + }); + + if (shouldRetry) { + throw error; // BullMQ reintentara + } + } + } + + private shouldRetry(error: any): boolean { + if (!error.response) return true; // Timeout o network error + const status = error.response.status; + return status === 429 || status >= 500; + } +} +``` + +--- + +## 9. UI de Administracion + +### Funcionalidades + +- Lista de endpoints configurados +- Crear/editar/eliminar endpoints +- Ver historial de entregas +- Reintentar entregas fallidas +- Enviar evento de prueba +- Rotar secret + +### Ejemplo de Vista + +``` ++--------------------------------------------------+ +| Webhooks [+ Nuevo] | ++--------------------------------------------------+ +| URL | Eventos | Estado | +|------------------------|--------------|----------| +| https://example.com/wh | order.* | Activo | +| https://zapier.com/... | payment.* | Activo | +| https://erp.local/api | product.* | Inactivo | ++--------------------------------------------------+ + +Entregas Recientes: ++--------------------------------------------------+ +| Evento | Destino | Estado | Fecha | +|-----------------|--------------|--------|--------| +| order.created | example.com | OK | 10:00 | +| payment.failed | zapier.com | OK | 09:55 | +| product.updated | erp.local | Failed | 09:50 | ++--------------------------------------------------+ +``` + +--- + +## 10. Testing + +### Enviar Evento de Prueba + +```typescript +// POST /api/webhooks/endpoints/:id/test +{ + "event_type": "test.webhook" +} + +// Payload enviado +{ + "id": "evt_test_123", + "type": "test.webhook", + "created_at": "2026-01-10T10:00:00Z", + "data": { + "message": "This is a test webhook" + } +} +``` + +### Herramientas de Debug + +- [webhook.site](https://webhook.site) - Receptor de prueba +- [ngrok](https://ngrok.com) - Tunel para localhost + +--- + +## 11. Monitoreo + +### Metricas + +| Metrica | Descripcion | Alerta | +|---------|-------------|--------| +| webhook_deliveries_total | Total entregas | - | +| webhook_deliveries_success | Entregas exitosas | - | +| webhook_deliveries_failed | Entregas fallidas | > 10% | +| webhook_delivery_latency | Latencia de entrega | > 5s | +| webhook_queue_length | Jobs pendientes | > 100 | + +--- + +## 12. Referencias + +- [Webhooks Best Practices](https://webhooks.dev/best-practices) +- [BullMQ Documentation](https://docs.bullmq.io/) +- [Stripe Webhooks](https://stripe.com/docs/webhooks) (referencia) +- [ADR-0007: Webhook Retry Strategy](../97-adr/ADR-0007-webhook-retry-strategy.md) + +--- + +**Ultima actualizacion:** 2026-01-10 +**Autor:** Backend Team diff --git a/docs/02-integraciones/_MAP.md b/docs/02-integraciones/_MAP.md index d2fda4452..f035f5bd2 100644 --- a/docs/02-integraciones/_MAP.md +++ b/docs/02-integraciones/_MAP.md @@ -11,11 +11,11 @@ | Metrica | Valor | |---------|-------| -| Total integraciones | 9 | -| Documentadas | 9 | +| Total integraciones | 14 | +| Documentadas | 14 | | Implementadas | 4 | | En desarrollo | 2 | -| Pendientes | 3 | +| Pendientes | 8 | | Progreso | 100% documentado | --- @@ -34,7 +34,12 @@ | [INT-006-codi-banxico.md](./INT-006-codi-banxico.md) | Banxico/STP | Pagos QR | Mock | Pagos CoDi/SPEI | | [INT-007-firebase-fcm.md](./INT-007-firebase-fcm.md) | Firebase | Notificaciones | Pendiente | Push notifications | | [INT-008-google-vision.md](./INT-008-google-vision.md) | Google Cloud | OCR | Pendiente | Vision OCR productos | -| [INT-009-whisper.md](./INT-009-whisper.md) | OpenAI | Speech-to-Text | Pendiente | Transcripcion audio +| [INT-009-whisper.md](./INT-009-whisper.md) | OpenAI | Speech-to-Text | Pendiente | Transcripcion audio | +| [INT-010-email-providers.md](./INT-010-email-providers.md) | SendGrid/SES/SMTP | Email | Planificado | Email multi-proveedor | +| [INT-011-storage-cloud.md](./INT-011-storage-cloud.md) | S3/R2/MinIO | Storage | Planificado | Almacenamiento cloud | +| [INT-012-oauth-social.md](./INT-012-oauth-social.md) | Google/Apple | Auth | Planificado | OAuth 2.0 social login | +| [INT-013-redis-cache.md](./INT-013-redis-cache.md) | Redis | Infraestructura | Planificado | Cache y queues | +| [INT-014-webhooks-outbound.md](./INT-014-webhooks-outbound.md) | BullMQ | Eventos | Planificado | Webhooks salientes | --- @@ -55,6 +60,19 @@ 2. [INT-008 - Google Vision](./INT-008-google-vision.md) - OCR 3. [INT-009 - Whisper](./INT-009-whisper.md) - Speech-to-Text +### Notificaciones +1. [INT-010 - Email Providers](./INT-010-email-providers.md) - Email transaccional + +### Almacenamiento +1. [INT-011 - Storage Cloud](./INT-011-storage-cloud.md) - S3/R2/MinIO + +### Autenticacion +1. [INT-012 - OAuth Social](./INT-012-oauth-social.md) - Google/Apple + +### Infraestructura +1. [INT-013 - Redis Cache](./INT-013-redis-cache.md) - Cache y queues +2. [INT-014 - Webhooks Outbound](./INT-014-webhooks-outbound.md) - Eventos salientes + --- ## Arquitectura de Integraciones @@ -118,4 +136,5 @@ MERCADOPAGO_ACCESS_TOKEN=xxxxx --- **Mantenido por:** Documentation Team -**Version:** 1.0.0 +**Version:** 2.0.0 +**Total Integraciones:** 14 (INT-001 a INT-014) diff --git a/docs/04-modelado/trazabilidad/TRACEABILITY-MASTER.yml b/docs/04-modelado/trazabilidad/TRACEABILITY-MASTER.yml index 720e5532a..02a18ad4b 100644 --- a/docs/04-modelado/trazabilidad/TRACEABILITY-MASTER.yml +++ b/docs/04-modelado/trazabilidad/TRACEABILITY-MASTER.yml @@ -1,34 +1,646 @@ -# TRACEABILITY-MASTER.yml v2.0 +# TRACEABILITY-MASTER.yml v3.0 # Proyecto: Mi Changarrito -# Prefijo v2: MCH +# Prefijo: MCH +# Actualizado: 2026-01-10 +# Sistema: SIMCO v4.0.1 -traceability_version: "2.0" +traceability_version: "3.0" project: code: "MCH" name: "Mi Changarrito - Sistema POS para Pequenos Comercios" updated: "2026-01-10" + simco_version: "4.0.1" + +# ============================================================================ +# RESUMEN DE EPICAS +# ============================================================================ epics_summary: - total: 0 - completed: 0 + total: 33 + completed: 22 in_progress: 0 - planned: 0 + pending: 6 + planned: 5 nomenclature_mapping: - legacy: "MCH-EP" + current: "MCH-NNN" + legacy: "MCH-EP-NNN" -epics: [] +# ============================================================================ +# EPICAS POR FASE +# ============================================================================ -dependency_graph: {} +epics: + # FASE 1: MVP CORE + - id: "MCH-001" + name: "Infraestructura Base" + status: "completed" + phase: 1 + priority: "P0" + story_points: 8 + dependencies: [] + blocks: ["MCH-002", "MCH-003", "MCH-010"] + integrations: [] + adrs: ["ADR-0001"] + + - id: "MCH-002" + name: "Autenticacion" + status: "completed" + phase: 1 + priority: "P0" + story_points: 13 + dependencies: ["MCH-001"] + blocks: ["MCH-006", "MCH-014"] + integrations: [] + adrs: [] + + - id: "MCH-003" + name: "Catalogo de Productos" + status: "completed" + phase: 1 + priority: "P0" + story_points: 13 + dependencies: ["MCH-001"] + blocks: ["MCH-004", "MCH-007"] + integrations: [] + adrs: [] + + - id: "MCH-004" + name: "Punto de Venta" + status: "completed" + phase: 1 + priority: "P0" + story_points: 21 + dependencies: ["MCH-003"] + blocks: ["MCH-005", "MCH-008"] + integrations: [] + adrs: [] + + - id: "MCH-005" + name: "Integraciones de Pago" + status: "completed" + phase: 1 + priority: "P0" + story_points: 13 + dependencies: ["MCH-004"] + blocks: [] + integrations: ["INT-002", "INT-004", "INT-005"] + adrs: [] + + # FASE 2: INTELIGENCIA + - id: "MCH-006" + name: "Onboarding Inteligente" + status: "completed" + phase: 2 + priority: "P1" + story_points: 8 + dependencies: ["MCH-002"] + blocks: [] + integrations: [] + adrs: [] + + - id: "MCH-007" + name: "Templates y Catalogos" + status: "completed" + phase: 2 + priority: "P1" + story_points: 5 + dependencies: ["MCH-003"] + blocks: [] + integrations: [] + adrs: [] + + - id: "MCH-008" + name: "Sistema de Fiados" + status: "completed" + phase: 2 + priority: "P1" + story_points: 8 + dependencies: ["MCH-004"] + blocks: [] + integrations: [] + adrs: [] + + - id: "MCH-009" + name: "Prediccion Inventario" + status: "completed" + phase: 2 + priority: "P1" + story_points: 5 + dependencies: ["MCH-003"] + blocks: [] + integrations: ["INT-008"] + adrs: [] + + # FASE 3: ASISTENTE IA + - id: "MCH-010" + name: "MCP Server" + status: "completed" + phase: 3 + priority: "P0" + story_points: 13 + dependencies: ["MCH-001"] + blocks: ["MCH-011", "MCH-012", "MCH-013"] + integrations: ["INT-003"] + adrs: ["ADR-0003"] + + - id: "MCH-011" + name: "WhatsApp Service" + status: "completed" + phase: 3 + priority: "P0" + story_points: 13 + dependencies: ["MCH-010"] + blocks: ["MCH-015"] + integrations: ["INT-001", "INT-009"] + adrs: ["ADR-0002"] + + - id: "MCH-012" + name: "Chat LLM Dueno" + status: "completed" + phase: 3 + priority: "P1" + story_points: 8 + dependencies: ["MCH-010"] + blocks: [] + integrations: ["INT-003"] + adrs: ["ADR-0003"] + + - id: "MCH-013" + name: "Chat LLM Cliente" + status: "completed" + phase: 3 + priority: "P1" + story_points: 5 + dependencies: ["MCH-010"] + blocks: [] + integrations: ["INT-003"] + adrs: ["ADR-0003"] + + # FASE 4: PEDIDOS Y CLIENTES + - id: "MCH-014" + name: "Gestion de Clientes" + status: "completed" + phase: 4 + priority: "P1" + story_points: 8 + dependencies: ["MCH-002"] + blocks: ["MCH-015"] + integrations: [] + adrs: [] + + - id: "MCH-015" + name: "Pedidos via WhatsApp" + status: "completed" + phase: 4 + priority: "P1" + story_points: 13 + dependencies: ["MCH-011", "MCH-014"] + blocks: ["MCH-016"] + integrations: ["INT-001"] + adrs: [] + + - id: "MCH-016" + name: "Entregas a Domicilio" + status: "completed" + phase: 4 + priority: "P2" + story_points: 5 + dependencies: ["MCH-015"] + blocks: [] + integrations: [] + adrs: [] + + - id: "MCH-017" + name: "Notificaciones" + status: "completed" + phase: 4 + priority: "P1" + story_points: 5 + dependencies: ["MCH-011"] + blocks: [] + integrations: ["INT-007"] + adrs: [] + + # FASE 5: MONETIZACION + - id: "MCH-018" + name: "Planes y Suscripciones" + status: "completed" + phase: 5 + priority: "P0" + story_points: 8 + dependencies: ["MCH-002"] + blocks: ["MCH-019", "MCH-020"] + integrations: ["INT-002"] + adrs: [] + + - id: "MCH-019" + name: "Tienda de Tokens" + status: "completed" + phase: 5 + priority: "P1" + story_points: 5 + dependencies: ["MCH-018"] + blocks: [] + integrations: [] + adrs: [] + + - id: "MCH-020" + name: "Pagos Suscripcion" + status: "completed" + phase: 5 + priority: "P0" + story_points: 8 + dependencies: ["MCH-018"] + blocks: [] + integrations: ["INT-002"] + adrs: [] + + - id: "MCH-021" + name: "Dashboard Web" + status: "completed" + phase: 5 + priority: "P1" + story_points: 13 + dependencies: ["MCH-018"] + blocks: [] + integrations: [] + adrs: [] + + # FASE 6: CRECIMIENTO + - id: "MCH-022" + name: "Modo Offline" + status: "completed" + phase: 6 + priority: "P1" + story_points: 13 + dependencies: ["MCH-004"] + blocks: [] + integrations: [] + adrs: [] + + - id: "MCH-023" + name: "Programa de Referidos" + status: "pending" + phase: 6 + priority: "P2" + story_points: 5 + dependencies: ["MCH-018"] + blocks: [] + integrations: [] + adrs: [] + + - id: "MCH-024" + name: "CoDi y SPEI" + status: "pending" + phase: 6 + priority: "P2" + story_points: 8 + dependencies: ["MCH-005"] + blocks: [] + integrations: ["INT-006"] + adrs: [] + + - id: "MCH-025" + name: "Widgets y Atajos" + status: "pending" + phase: 6 + priority: "P2" + story_points: 5 + dependencies: ["MCH-004"] + blocks: [] + integrations: [] + adrs: [] + + # FASE 7: EXPANSION + - id: "MCH-026" + name: "Multi-idioma LATAM" + status: "pending" + phase: 7 + priority: "P3" + story_points: 5 + dependencies: [] + blocks: [] + integrations: [] + adrs: [] + + - id: "MCH-027" + name: "Integracion SAT" + status: "pending" + phase: 7 + priority: "P3" + story_points: 13 + dependencies: [] + blocks: [] + integrations: [] + adrs: [] + + - id: "MCH-028" + name: "Marketplace Proveedores" + status: "pending" + phase: 7 + priority: "P3" + story_points: 8 + dependencies: [] + blocks: [] + integrations: [] + adrs: [] + + # FASE 7: EXPANSION SAAS (NUEVAS) + - id: "MCH-029" + name: "Infraestructura SaaS Avanzada" + status: "planned" + phase: 7 + priority: "P0" + story_points: 24 + sprint_target: "6-7" + dependencies: [] + blocks: ["MCH-030", "MCH-032", "MCH-033"] + integrations: ["INT-010", "INT-011", "INT-013", "INT-014"] + adrs: ["ADR-0006", "ADR-0007", "ADR-0009", "ADR-0011"] + user_stories: + - id: "MCH-US-101" + name: "Email Multi-proveedor" + story_points: 5 + - id: "MCH-US-102" + name: "Storage Abstracto" + story_points: 8 + - id: "MCH-US-103" + name: "Redis Cache y Queue" + story_points: 3 + - id: "MCH-US-104" + name: "Webhooks Outbound" + story_points: 5 + - id: "MCH-US-112" + name: "Rate Limiting por Plan" + story_points: 3 + + - id: "MCH-030" + name: "Auth Social OAuth 2.0" + status: "planned" + phase: 7 + priority: "P1" + story_points: 8 + sprint_target: "8" + dependencies: ["MCH-029"] + blocks: [] + integrations: ["INT-012"] + adrs: ["ADR-0010"] + user_stories: + - id: "MCH-US-105" + name: "Login con Google" + story_points: 5 + - id: "MCH-US-106" + name: "Login con Apple" + story_points: 3 + + - id: "MCH-031" + name: "Auditoria Empresarial" + status: "planned" + phase: 7 + priority: "P1" + story_points: 5 + sprint_target: "7" + dependencies: [] + blocks: [] + integrations: [] + adrs: ["ADR-0008"] + user_stories: + - id: "MCH-US-107" + name: "Registro de Acciones" + story_points: 3 + - id: "MCH-US-108" + name: "Politica de Retencion" + story_points: 2 + + - id: "MCH-032" + name: "Feature Flags por Plan" + status: "planned" + phase: 7 + priority: "P1" + story_points: 5 + sprint_target: "8" + dependencies: ["MCH-029", "MCH-018"] + blocks: [] + integrations: ["INT-013"] + adrs: ["ADR-0005"] + user_stories: + - id: "MCH-US-109" + name: "Feature Flags por Plan" + story_points: 3 + - id: "MCH-US-110" + name: "Overrides por Tenant" + story_points: 2 + + # FASE 8: MEJORAS UX + - id: "MCH-033" + name: "Onboarding Wizard" + status: "planned" + phase: 8 + priority: "P2" + story_points: 3 + sprint_target: "9" + dependencies: ["MCH-029"] + blocks: [] + integrations: [] + adrs: [] + user_stories: + - id: "MCH-US-111" + name: "Wizard de Configuracion" + story_points: 3 + +# ============================================================================ +# INTEGRACIONES +# ============================================================================ + +integrations: + - id: "INT-001" + name: "WhatsApp Meta Business" + category: "Mensajeria" + status: "active" + epics: ["MCH-011", "MCH-015"] + + - id: "INT-002" + name: "Stripe" + category: "Pagos" + status: "active" + epics: ["MCH-005", "MCH-018", "MCH-020"] + + - id: "INT-003" + name: "OpenRouter LLM" + category: "AI/LLM" + status: "active" + epics: ["MCH-010", "MCH-012", "MCH-013"] + + - id: "INT-004" + name: "MercadoPago" + category: "Pagos" + status: "pending" + epics: ["MCH-005"] + + - id: "INT-005" + name: "Clip Mexico" + category: "Pagos" + status: "mock" + epics: ["MCH-005"] + + - id: "INT-006" + name: "CoDi/SPEI Banxico" + category: "Pagos" + status: "mock" + epics: ["MCH-024"] + + - id: "INT-007" + name: "Firebase FCM" + category: "Notificaciones" + status: "pending" + epics: ["MCH-017"] + + - id: "INT-008" + name: "Google Cloud Vision" + category: "AI/ML" + status: "pending" + epics: ["MCH-009"] + + - id: "INT-009" + name: "OpenAI Whisper" + category: "AI/ML" + status: "pending" + epics: ["MCH-011"] + + # NUEVAS INTEGRACIONES SAAS + - id: "INT-010" + name: "Email Multi-Provider" + category: "Notificaciones" + status: "planned" + epics: ["MCH-029"] + providers: ["SendGrid", "AWS SES", "SMTP"] + + - id: "INT-011" + name: "Storage Cloud" + category: "Almacenamiento" + status: "planned" + epics: ["MCH-029"] + providers: ["AWS S3", "Cloudflare R2", "MinIO"] + + - id: "INT-012" + name: "OAuth Social" + category: "Autenticacion" + status: "planned" + epics: ["MCH-030"] + providers: ["Google", "Apple"] + + - id: "INT-013" + name: "Redis Cache" + category: "Infraestructura" + status: "planned" + epics: ["MCH-029", "MCH-032"] + + - id: "INT-014" + name: "Webhooks Outbound" + category: "Eventos" + status: "planned" + epics: ["MCH-029"] + +# ============================================================================ +# ADRS +# ============================================================================ + +adrs: + - id: "ADR-0001" + title: "Arquitectura Multi-Tenant" + status: "accepted" + epics: ["MCH-001"] + + - id: "ADR-0002" + title: "WhatsApp First Approach" + status: "accepted" + epics: ["MCH-011"] + + - id: "ADR-0003" + title: "LLM Agnostic Strategy" + status: "accepted" + epics: ["MCH-010", "MCH-012", "MCH-013"] + + - id: "ADR-0004" + title: "Notificaciones en Tiempo Real" + status: "accepted" + epics: ["MCH-017"] + + - id: "ADR-0005" + title: "Feature Flags por Plan" + status: "accepted" + epics: ["MCH-032"] + + - id: "ADR-0006" + title: "Storage Abstraction" + status: "accepted" + epics: ["MCH-029"] + + - id: "ADR-0007" + title: "Webhook Retry Strategy" + status: "accepted" + epics: ["MCH-029"] + + - id: "ADR-0008" + title: "Audit Log Retention" + status: "accepted" + epics: ["MCH-031"] + + - id: "ADR-0009" + title: "Rate Limiting Strategy" + status: "accepted" + epics: ["MCH-029"] + + - id: "ADR-0010" + title: "OAuth Social Strategy" + status: "accepted" + epics: ["MCH-030"] + + - id: "ADR-0011" + title: "Email Multi-Provider" + status: "accepted" + epics: ["MCH-029"] + +# ============================================================================ +# GRAFO DE DEPENDENCIAS +# ============================================================================ + +dependency_graph: + MCH-001: ["MCH-002", "MCH-003", "MCH-010"] + MCH-002: ["MCH-006", "MCH-014", "MCH-018"] + MCH-003: ["MCH-004", "MCH-007", "MCH-009"] + MCH-004: ["MCH-005", "MCH-008", "MCH-022"] + MCH-010: ["MCH-011", "MCH-012", "MCH-013"] + MCH-011: ["MCH-015", "MCH-017"] + MCH-014: ["MCH-015"] + MCH-015: ["MCH-016"] + MCH-018: ["MCH-019", "MCH-020", "MCH-021", "MCH-023"] + MCH-029: ["MCH-030", "MCH-032", "MCH-033"] + +# ============================================================================ +# INVENTARIOS +# ============================================================================ inventories: - master: "docs/90-transversal/inventarios/" + master: "orchestration/inventarios/MASTER_INVENTORY.yml" + backend: "orchestration/inventarios/BACKEND_INVENTORY.yml" + frontend: "orchestration/inventarios/FRONTEND_INVENTORY.yml" + database: "orchestration/inventarios/DATABASE_INVENTORY.yml" + +# ============================================================================ +# HEALTH +# ============================================================================ health: last_validated: "2026-01-10" - score: 0.50 - issues: - - severity: "low" - type: "initialization" - description: "Proyecto en migracion inicial v2" + score: 1.0 + issues: [] + saas_integration: + status: "completed" + date: "2026-01-10" + new_epics: 5 + new_integrations: 5 + new_adrs: 8 + story_points: 45 diff --git a/docs/97-adr/ADR-0004-notifications-realtime.md b/docs/97-adr/ADR-0004-notifications-realtime.md new file mode 100644 index 000000000..baefb6ff8 --- /dev/null +++ b/docs/97-adr/ADR-0004-notifications-realtime.md @@ -0,0 +1,181 @@ +--- +id: ADR-0004 +type: ADR +title: "Notificaciones en Tiempo Real" +status: Accepted +decision_date: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +stakeholders: + - "Equipo MiChangarrito" +tags: + - notifications + - websocket + - sse + - realtime +--- + +# ADR-0004: Notificaciones en Tiempo Real + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | ADR-0004 | +| **Estado** | Accepted | +| **Fecha** | 2026-01-10 | +| **Autor** | Architecture Team | +| **Supersede** | - | + +--- + +## Contexto + +MiChangarrito necesita notificar a los usuarios en tiempo real sobre eventos como nuevos pedidos, pagos recibidos, alertas de inventario y actualizaciones de estado. Se requiere una solucion que funcione tanto en web como en mobile. + +--- + +## Decision + +**Adoptamos Server-Sent Events (SSE) para notificaciones web y push notifications nativas para mobile.** + +SSE es unidireccional (servidor -> cliente), suficiente para notificaciones, mas simple de implementar que WebSockets, y funciona mejor con proxies y load balancers. + +```typescript +// Endpoint SSE +@Get('notifications/stream') +@Sse() +notificationStream(@Req() req: Request): Observable { + const userId = req.user.id; + return this.notificationService.getUserStream(userId); +} +``` + +--- + +## Alternativas Consideradas + +### Opcion 1: WebSockets (Socket.io) +- **Pros:** + - Bidireccional + - Ampliamente soportado + - Buena libreria (Socket.io) +- **Cons:** + - Mas complejo de escalar + - Requiere sticky sessions + - Overhead para notificaciones unidireccionales + +### Opcion 2: Server-Sent Events (Elegida) +- **Pros:** + - Simple de implementar + - Unidireccional (perfecto para notificaciones) + - Reconexion automatica + - Funciona sobre HTTP/2 + - No requiere sticky sessions +- **Cons:** + - Solo unidireccional + - Limite de conexiones por dominio + - No soportado en IE + +### Opcion 3: Polling +- **Pros:** + - Muy simple + - Funciona en cualquier navegador +- **Cons:** + - Ineficiente + - Latencia alta + - Carga innecesaria al servidor + +--- + +## Consecuencias + +### Positivas + +1. **Simplicidad:** SSE es nativo del navegador +2. **Escalabilidad:** Funciona con load balancers +3. **Eficiencia:** Conexion persistente sin overhead +4. **Resiliencia:** Reconexion automatica + +### Negativas + +1. **Limite conexiones:** 6 por dominio en HTTP/1.1 +2. **Solo unidireccional:** Si necesitamos bidireccional, agregar WebSocket +3. **IE no soportado:** Usar polyfill si es necesario + +--- + +## Implementacion + +### Backend (NestJS) + +```typescript +@Controller('notifications') +export class NotificationController { + constructor( + private readonly notificationService: NotificationService, + ) {} + + @Get('stream') + @UseGuards(JwtAuthGuard) + @Sse() + stream(@Req() req: Request): Observable { + const userId = req.user.id; + const tenantId = req.user.tenantId; + + return this.notificationService + .createStream(userId, tenantId) + .pipe( + map(notification => ({ + data: notification, + type: notification.type, + id: notification.id, + })), + ); + } +} +``` + +### Frontend (React) + +```typescript +function useNotifications() { + const [notifications, setNotifications] = useState([]); + + useEffect(() => { + const eventSource = new EventSource('/api/notifications/stream', { + withCredentials: true, + }); + + eventSource.onmessage = (event) => { + const notification = JSON.parse(event.data); + setNotifications(prev => [notification, ...prev]); + toast.info(notification.message); + }; + + eventSource.onerror = () => { + // Reconexion automatica + }; + + return () => eventSource.close(); + }, []); + + return notifications; +} +``` + +### Mobile (Push Notifications) + +Para mobile, usamos push notifications nativas en lugar de SSE para mejor experiencia cuando la app esta en background. + +--- + +## Referencias + +- [MDN Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) +- [NestJS SSE](https://docs.nestjs.com/techniques/server-sent-events) + +--- + +**Fecha decision:** 2026-01-10 +**Autores:** Architecture Team diff --git a/docs/97-adr/ADR-0005-feature-flags.md b/docs/97-adr/ADR-0005-feature-flags.md new file mode 100644 index 000000000..2837d8521 --- /dev/null +++ b/docs/97-adr/ADR-0005-feature-flags.md @@ -0,0 +1,227 @@ +--- +id: ADR-0005 +type: ADR +title: "Feature Flags por Plan" +status: Accepted +decision_date: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +stakeholders: + - "Equipo MiChangarrito" +tags: + - feature-flags + - plans + - multi-tenant +--- + +# ADR-0005: Feature Flags por Plan + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | ADR-0005 | +| **Estado** | Accepted | +| **Fecha** | 2026-01-10 | +| **Autor** | Architecture Team | +| **Supersede** | - | + +--- + +## Contexto + +MiChangarrito ofrece multiples planes de suscripcion (Basic, Pro, Enterprise) con diferentes funcionalidades. Necesitamos un sistema que: + +1. Habilite/deshabilite features segun el plan +2. Permita overrides para beta testing +3. Sea rapido en evaluacion (no impacte rendimiento) +4. Funcione tanto en backend como frontend + +--- + +## Decision + +**Implementamos un sistema de feature flags propio con cache en Redis y evaluacion en tiempo real.** + +Los flags se definen globalmente, se asocian a planes, y pueden tener overrides por tenant o usuario. La evaluacion usa cache Redis para latencia <10ms. + +```typescript +// Evaluacion de flag +const hasFeature = await featureFlagService.evaluate('ai_assistant', { + tenantId, + userId, + planId, +}); +``` + +--- + +## Alternativas Consideradas + +### Opcion 1: LaunchDarkly/Flagsmith (SaaS) +- **Pros:** + - Funcionalidad completa + - Dashboard listo + - SDKs para todo +- **Cons:** + - Costo adicional + - Dependencia externa + - Datos salen del sistema + +### Opcion 2: Unleash (Open Source) +- **Pros:** + - Open source + - Self-hosted + - Feature completo +- **Cons:** + - Infraestructura adicional + - Complejidad de mantener + - Overhead para nuestro caso + +### Opcion 3: Implementacion propia (Elegida) +- **Pros:** + - Sin dependencias externas + - Integrado con nuestro sistema de planes + - Cache en Redis existente + - Control total +- **Cons:** + - Desarrollo inicial + - Sin UI avanzada (la construimos) + - Mantenimiento propio + +--- + +## Consecuencias + +### Positivas + +1. **Integracion:** Se integra directamente con plans y tenants +2. **Performance:** Cache Redis garantiza <10ms +3. **Control:** Logica de negocio en nuestro codigo +4. **Costo:** Sin gastos adicionales + +### Negativas + +1. **Desarrollo:** Tiempo de implementacion +2. **Features:** Menos features que soluciones SaaS +3. **Mantenimiento:** Responsabilidad propia + +--- + +## Implementacion + +### Modelo de Datos + +```sql +-- Flags globales +CREATE TABLE feature_flags.flags ( + id UUID PRIMARY KEY, + key VARCHAR(100) UNIQUE NOT NULL, + description TEXT, + default_value BOOLEAN DEFAULT false +); + +-- Flags por plan +CREATE TABLE feature_flags.plan_flags ( + flag_id UUID REFERENCES flags(id), + plan_id UUID REFERENCES billing.plans(id), + value BOOLEAN NOT NULL, + PRIMARY KEY (flag_id, plan_id) +); + +-- Overrides +CREATE TABLE feature_flags.flag_overrides ( + flag_id UUID REFERENCES flags(id), + tenant_id UUID, + user_id UUID, + value BOOLEAN NOT NULL, + expires_at TIMESTAMP +); +``` + +### Evaluador + +```typescript +async evaluate(key: string, context: FlagContext): Promise { + // 1. Buscar en cache + const cacheKey = `flags:${key}:${context.tenantId}:${context.userId}`; + const cached = await this.redis.get(cacheKey); + if (cached !== null) return cached === 'true'; + + // 2. Buscar override de usuario + const userOverride = await this.findOverride(key, { userId: context.userId }); + if (userOverride) { + await this.cache(cacheKey, userOverride.value); + return userOverride.value; + } + + // 3. Buscar override de tenant + const tenantOverride = await this.findOverride(key, { tenantId: context.tenantId }); + if (tenantOverride) { + await this.cache(cacheKey, tenantOverride.value); + return tenantOverride.value; + } + + // 4. Evaluar por plan + const planValue = await this.getPlanValue(key, context.planId); + if (planValue !== null) { + await this.cache(cacheKey, planValue); + return planValue; + } + + // 5. Valor por defecto + const flag = await this.getFlag(key); + await this.cache(cacheKey, flag.default_value); + return flag.default_value; +} +``` + +### Uso en Backend + +```typescript +@UseGuards(FeatureFlagGuard) +@FeatureFlag('advanced_reports') +@Get('reports/advanced') +getAdvancedReports() { + // Solo accesible si el flag esta habilitado +} +``` + +### Uso en Frontend + +```tsx +function Dashboard() { + const canUseAI = useFeatureFlag('ai_assistant'); + + return ( +
    + {canUseAI && } +
    + ); +} +``` + +--- + +## Flags Iniciales + +| Key | Descripcion | Basic | Pro | Enterprise | +|-----|-------------|-------|-----|------------| +| ai_assistant | Asistente IA | false | true | true | +| advanced_reports | Reportes avanzados | false | true | true | +| api_access | API publica | false | false | true | +| white_label | Sin branding | false | false | true | +| multi_location | Multiples ubicaciones | false | true | true | +| custom_integrations | Integraciones custom | false | false | true | + +--- + +## Referencias + +- [Feature Flags Best Practices](https://martinfowler.com/articles/feature-toggles.html) +- [MCH-032: Feature Flags](../01-epicas/MCH-032-feature-flags.md) + +--- + +**Fecha decision:** 2026-01-10 +**Autores:** Architecture Team diff --git a/docs/97-adr/ADR-0006-storage-abstraction.md b/docs/97-adr/ADR-0006-storage-abstraction.md new file mode 100644 index 000000000..b0e867161 --- /dev/null +++ b/docs/97-adr/ADR-0006-storage-abstraction.md @@ -0,0 +1,209 @@ +--- +id: ADR-0006 +type: ADR +title: "Storage Abstraction" +status: Accepted +decision_date: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +stakeholders: + - "Equipo MiChangarrito" +tags: + - storage + - s3 + - r2 + - abstraction + - multi-cloud +--- + +# ADR-0006: Storage Abstraction + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | ADR-0006 | +| **Estado** | Accepted | +| **Fecha** | 2026-01-10 | +| **Autor** | Architecture Team | +| **Supersede** | - | + +--- + +## Contexto + +MiChangarrito necesita almacenar archivos (imagenes de productos, facturas, documentos) en la nube. Queremos: + +1. Flexibilidad para cambiar de proveedor +2. Desarrollo local sin depender de cloud +3. Optimizacion de costos (R2 es mas barato que S3) + +--- + +## Decision + +**Implementamos una capa de abstraccion con Factory Pattern que soporta S3, Cloudflare R2 y MinIO.** + +Todos los proveedores implementan la misma interfaz, permitiendo cambiar de proveedor sin modificar codigo de negocio. + +```typescript +interface StorageProvider { + upload(key: string, file: Buffer, options?: UploadOptions): Promise; + download(key: string): Promise; + delete(key: string): Promise; + getSignedUrl(key: string, expiresIn: number): Promise; + list(prefix: string): Promise; +} +``` + +--- + +## Alternativas Consideradas + +### Opcion 1: Usar S3 directamente +- **Pros:** + - Simple + - Bien documentado +- **Cons:** + - Vendor lock-in + - Sin desarrollo local facil + - Costos pueden ser altos + +### Opcion 2: Abstraccion con Factory (Elegida) +- **Pros:** + - Flexibilidad de proveedor + - MinIO para desarrollo local + - Optimizacion de costos con R2 + - Codigo limpio +- **Cons:** + - Complejidad adicional + - Mantener multiples providers + +### Opcion 3: Usar libreria existente (flydrive) +- **Pros:** + - Ya implementado + - Multiples drivers +- **Cons:** + - Dependencia externa + - Menos control + - Puede no cubrir todos nuestros casos + +--- + +## Consecuencias + +### Positivas + +1. **Flexibilidad:** Cambiar proveedor sin impacto en negocio +2. **Desarrollo:** MinIO local sin credenciales cloud +3. **Costos:** Migrar a R2 reduce costos ~75% +4. **Testing:** Facil mock de storage + +### Negativas + +1. **Complejidad:** Mantener 3 implementaciones +2. **Features especificos:** Podemos perder algunas features unicas + +--- + +## Implementacion + +### Factory + +```typescript +@Injectable() +export class StorageFactory { + create(provider: StorageProviderType): StorageProvider { + switch (provider) { + case 's3': + return new S3StorageProvider(this.configService); + case 'r2': + return new R2StorageProvider(this.configService); + case 'minio': + return new MinIOStorageProvider(this.configService); + default: + throw new Error(`Unknown storage provider: ${provider}`); + } + } +} +``` + +### Provider S3 + +```typescript +class S3StorageProvider implements StorageProvider { + private client: S3Client; + + constructor(config: ConfigService) { + this.client = new S3Client({ + region: config.get('S3_REGION'), + credentials: { + accessKeyId: config.get('S3_ACCESS_KEY'), + secretAccessKey: config.get('S3_SECRET_KEY'), + }, + }); + } + + async upload(key: string, file: Buffer, options?: UploadOptions): Promise { + const command = new PutObjectCommand({ + Bucket: this.bucket, + Key: key, + Body: file, + ContentType: options?.contentType, + }); + + await this.client.send(command); + + return { + key, + url: `https://${this.bucket}.s3.amazonaws.com/${key}`, + }; + } + + // ... otros metodos +} +``` + +### Uso + +```typescript +@Injectable() +export class FileService { + constructor(private readonly storageFactory: StorageFactory) {} + + async uploadProductImage(productId: string, file: Buffer): Promise { + const storage = this.storageFactory.create(process.env.STORAGE_PROVIDER); + const key = `products/${productId}/image.jpg`; + + const result = await storage.upload(key, file, { + contentType: 'image/jpeg', + }); + + return result.url; + } +} +``` + +--- + +## Configuracion por Ambiente + +| Ambiente | Proveedor | Razon | +|----------|-----------|-------| +| Development | MinIO | Local, sin credenciales | +| Staging | R2 | Costos bajos | +| Production | S3 o R2 | Segun necesidad | + +--- + +## Referencias + +- [AWS S3 SDK](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/client/s3/) +- [Cloudflare R2](https://developers.cloudflare.com/r2/) +- [MinIO](https://min.io/) +- [INT-011: Storage Cloud](../02-integraciones/INT-011-storage-cloud.md) + +--- + +**Fecha decision:** 2026-01-10 +**Autores:** Architecture Team diff --git a/docs/97-adr/ADR-0007-webhook-retry-strategy.md b/docs/97-adr/ADR-0007-webhook-retry-strategy.md new file mode 100644 index 000000000..ea9cd739e --- /dev/null +++ b/docs/97-adr/ADR-0007-webhook-retry-strategy.md @@ -0,0 +1,236 @@ +--- +id: ADR-0007 +type: ADR +title: "Webhook Retry Strategy" +status: Accepted +decision_date: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +stakeholders: + - "Equipo MiChangarrito" +tags: + - webhooks + - retry + - bullmq + - resilience +--- + +# ADR-0007: Webhook Retry Strategy + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | ADR-0007 | +| **Estado** | Accepted | +| **Fecha** | 2026-01-10 | +| **Autor** | Architecture Team | +| **Supersede** | - | + +--- + +## Contexto + +MiChangarrito ofrece webhooks outbound para notificar a sistemas externos sobre eventos. Los endpoints destino pueden fallar temporalmente, y necesitamos una estrategia de reintentos que: + +1. Sea resiliente a fallos temporales +2. No sobrecargue el destino +3. Eventualmente falle despues de intentos razonables +4. Proporcione visibilidad del estado + +--- + +## Decision + +**Adoptamos exponential backoff con jitter usando BullMQ, con maximo 6 intentos y timeout de 30 segundos por request.** + +``` +Intento 1: Inmediato +Intento 2: 1s + jitter +Intento 3: 2s + jitter +Intento 4: 4s + jitter +Intento 5: 8s + jitter +Intento 6: 16s + jitter +``` + +Despues del intento 6, el webhook se marca como fallido y se registra en logs. + +--- + +## Alternativas Consideradas + +### Opcion 1: Retry inmediato +- **Pros:** + - Simple +- **Cons:** + - Puede sobrecargar el destino + - Fallos en cascada + +### Opcion 2: Fixed interval +- **Pros:** + - Predecible +- **Cons:** + - No se adapta a la situacion + - Thundering herd problem + +### Opcion 3: Exponential backoff con jitter (Elegida) +- **Pros:** + - Reduce carga en destino + - Evita thundering herd + - Estandar de industria +- **Cons:** + - Mas tiempo total antes de fallo definitivo + +--- + +## Consecuencias + +### Positivas + +1. **Resilencia:** Tolera fallos temporales +2. **Cortesia:** No sobrecarga destinos +3. **Predecible:** Comportamiento conocido + +### Negativas + +1. **Latencia:** Puede tomar ~31 segundos en fallar definitivamente +2. **Complejidad:** Manejo de estados de entrega + +--- + +## Implementacion + +### Configuracion BullMQ + +```typescript +await this.webhookQueue.add('deliver', payload, { + attempts: 6, + backoff: { + type: 'exponential', + delay: 1000, // Base: 1 segundo + }, + removeOnComplete: { + age: 86400, // 24 horas + count: 1000, + }, + removeOnFail: false, +}); +``` + +### Logica de Retry + +```typescript +@Process('deliver') +async handleDelivery(job: Job) { + try { + const response = await this.httpService.axiosRef.post( + job.data.url, + job.data.payload, + { + timeout: 30000, + headers: this.buildHeaders(job.data), + } + ); + + if (response.status >= 200 && response.status < 300) { + return { success: true, status: response.status }; + } + + throw new Error(`Unexpected status: ${response.status}`); + } catch (error) { + const shouldRetry = this.shouldRetry(error); + + this.logger.warn('Webhook delivery failed', { + attempt: job.attemptsMade + 1, + maxAttempts: job.opts.attempts, + willRetry: shouldRetry, + error: error.message, + }); + + if (!shouldRetry) { + // No reintentar, marcar como fallido definitivo + await this.markAsFailed(job.data.deliveryId, error.message); + return { success: false, permanent: true }; + } + + throw error; // BullMQ reintentara + } +} + +private shouldRetry(error: any): boolean { + // No reintentar errores del cliente (4xx) excepto 429 + if (error.response) { + const status = error.response.status; + if (status === 429) return true; // Rate limited + if (status >= 400 && status < 500) return false; // Client error + if (status >= 500) return true; // Server error + } + + // Reintentar errores de red y timeouts + return true; +} +``` + +### Jitter + +BullMQ aplica jitter automaticamente. Si queremos control manual: + +```typescript +function getBackoffDelay(attempt: number): number { + const baseDelay = 1000; + const maxDelay = 16000; + const exponentialDelay = Math.min(baseDelay * Math.pow(2, attempt), maxDelay); + const jitter = Math.random() * 1000; // 0-1 segundo de jitter + return exponentialDelay + jitter; +} +``` + +--- + +## Codigos de Respuesta y Acciones + +| Codigo | Categoria | Accion | Retry | +|--------|-----------|--------|-------| +| 200-299 | Exito | Marcar entregado | No | +| 301-308 | Redirect | Seguir redirect | - | +| 400 | Bad Request | Marcar fallido | No | +| 401 | Unauthorized | Marcar fallido | No | +| 403 | Forbidden | Marcar fallido | No | +| 404 | Not Found | Marcar fallido | No | +| 429 | Rate Limited | Retry con delay | Si | +| 500-599 | Server Error | Retry | Si | +| Timeout | Network | Retry | Si | +| ECONNREFUSED | Network | Retry | Si | + +--- + +## Monitoreo + +### Metricas + +```typescript +// Prometheus metrics +webhook_delivery_attempts_total{status="success|retry|failed"} +webhook_delivery_duration_seconds +webhook_delivery_retries_total +``` + +### Alertas + +- `webhook_delivery_failure_rate > 0.1` - Mas del 10% fallando +- `webhook_queue_length > 100` - Cola creciendo +- `webhook_delivery_duration_seconds_p99 > 25` - Latencia alta + +--- + +## Referencias + +- [Exponential Backoff](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) +- [BullMQ Retries](https://docs.bullmq.io/guide/retrying-failing-jobs) +- [Stripe Webhooks](https://stripe.com/docs/webhooks/best-practices) +- [INT-014: Webhooks Outbound](../02-integraciones/INT-014-webhooks-outbound.md) + +--- + +**Fecha decision:** 2026-01-10 +**Autores:** Architecture Team diff --git a/docs/97-adr/ADR-0008-audit-log-retention.md b/docs/97-adr/ADR-0008-audit-log-retention.md new file mode 100644 index 000000000..75cef5953 --- /dev/null +++ b/docs/97-adr/ADR-0008-audit-log-retention.md @@ -0,0 +1,263 @@ +--- +id: ADR-0008 +type: ADR +title: "Audit Log Retention" +status: Accepted +decision_date: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +stakeholders: + - "Equipo MiChangarrito" +tags: + - audit + - retention + - compliance + - storage +--- + +# ADR-0008: Audit Log Retention + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | ADR-0008 | +| **Estado** | Accepted | +| **Fecha** | 2026-01-10 | +| **Autor** | Architecture Team | +| **Supersede** | - | + +--- + +## Contexto + +MiChangarrito registra audit logs de todas las acciones del sistema para compliance, debugging y seguridad. Necesitamos definir: + +1. Cuanto tiempo mantener los logs +2. Como archivar logs antiguos +3. Como purgar datos sin perder trazabilidad + +--- + +## Decision + +**Implementamos politica de retencion configurable por tenant con archivado opcional a storage antes de purga.** + +- **Default:** 90 dias en base de datos +- **Archivado:** Opcional a S3/R2 antes de purga +- **Notificacion:** 7 dias antes de purga +- **Configuracion:** Por tenant segun plan + +--- + +## Alternativas Consideradas + +### Opcion 1: Retencion infinita +- **Pros:** + - Nunca se pierde informacion + - Simple +- **Cons:** + - Crecimiento ilimitado de BD + - Costos de almacenamiento + - Performance degradada + +### Opcion 2: Retencion fija (30 dias) +- **Pros:** + - Simple de implementar + - Predecible +- **Cons:** + - No flexible para diferentes necesidades + - Puede no cumplir regulaciones + +### Opcion 3: Retencion configurable + archivado (Elegida) +- **Pros:** + - Flexible por tenant + - Archivado economico + - Cumple regulaciones +- **Cons:** + - Mas complejo + - Necesita job de limpieza + +--- + +## Consecuencias + +### Positivas + +1. **Compliance:** Tenants pueden configurar segun sus necesidades +2. **Performance:** BD no crece indefinidamente +3. **Economia:** Archivado en storage es barato +4. **Flexibilidad:** Diferentes politicas por plan + +### Negativas + +1. **Complejidad:** Job de limpieza y archivado +2. **Acceso:** Logs archivados requieren restauracion + +--- + +## Implementacion + +### Configuracion por Plan + +| Plan | Retencion BD | Archivado | Restauracion | +|------|--------------|-----------|--------------| +| Basic | 30 dias | No | N/A | +| Pro | 90 dias | Si (1 ano) | Bajo demanda | +| Enterprise | 365 dias | Si (7 anos) | Autoservicio | + +### Job de Limpieza + +```typescript +@Cron('0 3 * * *') // 3 AM diario +async cleanupAuditLogs() { + const tenants = await this.tenantService.findAll(); + + for (const tenant of tenants) { + const config = await this.getRetentionConfig(tenant.id); + const cutoffDate = subDays(new Date(), config.retentionDays); + + // 1. Archivar si esta habilitado + if (config.archiveBeforeDelete) { + await this.archiveLogs(tenant.id, cutoffDate); + } + + // 2. Purgar logs antiguos + await this.purgeLogs(tenant.id, cutoffDate); + + this.logger.log(`Cleaned audit logs for tenant ${tenant.id}`, { + cutoffDate, + archived: config.archiveBeforeDelete, + }); + } +} +``` + +### Archivado + +```typescript +async archiveLogs(tenantId: string, olderThan: Date) { + const logs = await this.auditRepository.find({ + where: { + tenantId, + createdAt: LessThan(olderThan), + }, + }); + + if (logs.length === 0) return; + + // Comprimir y subir a storage + const archive = await this.compressLogs(logs); + const key = `audit-archives/${tenantId}/${format(new Date(), 'yyyy-MM')}.json.gz`; + + await this.storageService.upload(key, archive, { + contentType: 'application/gzip', + metadata: { + tenantId, + recordCount: logs.length.toString(), + dateRange: `${logs[0].createdAt}-${logs[logs.length - 1].createdAt}`, + }, + }); +} +``` + +### Notificacion Pre-Purga + +```typescript +@Cron('0 9 * * *') // 9 AM diario +async notifyUpcomingPurge() { + const tenants = await this.tenantService.findAll(); + + for (const tenant of tenants) { + const config = await this.getRetentionConfig(tenant.id); + if (!config.notifyBeforePurge) continue; + + const warningDate = subDays(new Date(), config.retentionDays - 7); + const count = await this.countLogsOlderThan(tenant.id, warningDate); + + if (count > 0) { + await this.notificationService.send(tenant.adminEmail, { + template: 'audit-purge-warning', + variables: { + count, + purgeDate: addDays(warningDate, 7), + archiveEnabled: config.archiveBeforeDelete, + }, + }); + } + } +} +``` + +--- + +## Formato de Archivo + +### Estructura + +``` +audit-archives/ +└── {tenant_id}/ + ├── 2026-01.json.gz + ├── 2026-02.json.gz + └── ... +``` + +### Contenido (descomprimido) + +```json +{ + "tenant_id": "550e8400-...", + "exported_at": "2026-01-10T03:00:00Z", + "record_count": 15000, + "date_range": { + "from": "2025-10-01T00:00:00Z", + "to": "2025-10-31T23:59:59Z" + }, + "records": [ + { + "id": "...", + "user_id": "...", + "action": "UPDATE", + "entity_type": "Product", + "entity_id": "...", + "old_value": {...}, + "new_value": {...}, + "created_at": "2025-10-15T10:30:00Z" + } + ] +} +``` + +--- + +## Restauracion + +### API de Restauracion (Enterprise) + +```typescript +// POST /api/audit/restore +{ + "month": "2025-10", + "reason": "Auditoria externa" +} + +// Response +{ + "status": "processing", + "estimatedTime": "5 minutes", + "jobId": "restore-123" +} +``` + +--- + +## Referencias + +- [GDPR Data Retention](https://gdpr.eu/data-retention/) +- [MCH-031: Auditoria Empresarial](../01-epicas/MCH-031-auditoria-empresarial.md) + +--- + +**Fecha decision:** 2026-01-10 +**Autores:** Architecture Team diff --git a/docs/97-adr/ADR-0009-rate-limiting.md b/docs/97-adr/ADR-0009-rate-limiting.md new file mode 100644 index 000000000..6f2b96248 --- /dev/null +++ b/docs/97-adr/ADR-0009-rate-limiting.md @@ -0,0 +1,251 @@ +--- +id: ADR-0009 +type: ADR +title: "Rate Limiting Strategy" +status: Accepted +decision_date: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +stakeholders: + - "Equipo MiChangarrito" +tags: + - rate-limiting + - api + - redis + - security +--- + +# ADR-0009: Rate Limiting Strategy + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | ADR-0009 | +| **Estado** | Accepted | +| **Fecha** | 2026-01-10 | +| **Autor** | Architecture Team | +| **Supersede** | - | + +--- + +## Contexto + +MiChangarrito expone una API REST que puede ser abusada por clientes mal configurados o ataques maliciosos. Necesitamos rate limiting para: + +1. Proteger la infraestructura +2. Garantizar uso justo entre tenants +3. Diferenciar limites por plan +4. Informar a clientes sobre limites + +--- + +## Decision + +**Implementamos Token Bucket algorithm con Redis, limites por plan/tenant y headers estandar de rate limit.** + +``` +Header: X-RateLimit-Limit: 1000 +Header: X-RateLimit-Remaining: 999 +Header: X-RateLimit-Reset: 1704880800 +``` + +--- + +## Alternativas Consideradas + +### Opcion 1: Fixed Window +- **Pros:** + - Simple de implementar + - Facil de entender +- **Cons:** + - Vulnerable a bursts al cambiar ventana + - No es smooth + +### Opcion 2: Sliding Window Log +- **Pros:** + - Preciso + - Sin bursts +- **Cons:** + - Alto uso de memoria + - Queries complejas + +### Opcion 3: Token Bucket (Elegida) +- **Pros:** + - Permite bursts controlados + - Eficiente en memoria + - Smooth rate limiting +- **Cons:** + - Ligeramente mas complejo + +--- + +## Consecuencias + +### Positivas + +1. **Proteccion:** API protegida contra abusos +2. **Justicia:** Cada tenant tiene su cuota +3. **Transparencia:** Headers informan estado +4. **Flexibilidad:** Limites por plan + +### Negativas + +1. **Dependencia Redis:** Requiere Redis disponible +2. **Complejidad:** Logica adicional en cada request + +--- + +## Implementacion + +### Limites por Plan + +| Plan | Requests/minuto | Requests/dia | Burst | +|------|-----------------|--------------|-------| +| Basic | 60 | 10,000 | 10 | +| Pro | 300 | 100,000 | 50 | +| Enterprise | 1,000 | 1,000,000 | 100 | + +### Guard + +```typescript +@Injectable() +export class RateLimitGuard implements CanActivate { + constructor( + @Inject('REDIS_CLIENT') private readonly redis: Redis, + private readonly planService: PlanService, + ) {} + + async canActivate(context: ExecutionContext): Promise { + const request = context.switchToHttp().getRequest(); + const response = context.switchToHttp().getResponse(); + const tenantId = request.user?.tenantId; + + if (!tenantId) return true; // Sin tenant, skip + + const plan = await this.planService.getTenantPlan(tenantId); + const limit = this.getLimitForPlan(plan); + + const result = await this.checkLimit(tenantId, limit); + + // Agregar headers + response.setHeader('X-RateLimit-Limit', limit.requestsPerMinute); + response.setHeader('X-RateLimit-Remaining', result.remaining); + response.setHeader('X-RateLimit-Reset', result.resetAt); + + if (!result.allowed) { + response.setHeader('Retry-After', result.retryAfter); + throw new HttpException({ + statusCode: 429, + message: 'Too Many Requests', + retryAfter: result.retryAfter, + }, 429); + } + + return true; + } + + private async checkLimit( + tenantId: string, + limit: RateLimit, + ): Promise { + const key = `rate:${tenantId}`; + const now = Date.now(); + const windowMs = 60 * 1000; // 1 minuto + + const pipe = this.redis.pipeline(); + + // Remover tokens viejos + pipe.zremrangebyscore(key, 0, now - windowMs); + + // Contar tokens actuales + pipe.zcard(key); + + // Agregar token actual + pipe.zadd(key, now, `${now}-${Math.random()}`); + + // Expirar key + pipe.expire(key, 60); + + const results = await pipe.exec(); + const count = results[1][1] as number; + + const allowed = count < limit.requestsPerMinute; + const remaining = Math.max(0, limit.requestsPerMinute - count - 1); + const resetAt = Math.floor((now + windowMs) / 1000); + + return { + allowed, + remaining, + resetAt, + retryAfter: allowed ? 0 : Math.ceil(windowMs / 1000), + }; + } +} +``` + +### Bypass + +Algunas rutas no tienen rate limit: + +```typescript +@RateLimitBypass() +@Get('health') +healthCheck() { + return { status: 'ok' }; +} + +// Decorator +export const RateLimitBypass = () => SetMetadata('rateLimitBypass', true); +``` + +### Response 429 + +```json +{ + "statusCode": 429, + "message": "Too Many Requests", + "error": "Rate limit exceeded. Please retry after 45 seconds.", + "retryAfter": 45 +} +``` + +--- + +## Headers Estandar + +| Header | Descripcion | +|--------|-------------| +| `X-RateLimit-Limit` | Limite maximo de requests | +| `X-RateLimit-Remaining` | Requests restantes | +| `X-RateLimit-Reset` | Unix timestamp cuando se resetea | +| `Retry-After` | Segundos a esperar (solo en 429) | + +--- + +## Monitoreo + +### Metricas + +```typescript +rate_limit_requests_total{tenant, plan, allowed} +rate_limit_exceeded_total{tenant, plan} +``` + +### Alertas + +- Rate limit hits > 100/minuto por tenant +- Mismo IP excediendo multiples tenants + +--- + +## Referencias + +- [Token Bucket Algorithm](https://en.wikipedia.org/wiki/Token_bucket) +- [RFC 6585 - 429 Too Many Requests](https://tools.ietf.org/html/rfc6585) +- [GitHub API Rate Limiting](https://docs.github.com/en/rest/rate-limit) + +--- + +**Fecha decision:** 2026-01-10 +**Autores:** Architecture Team diff --git a/docs/97-adr/ADR-0010-oauth-social.md b/docs/97-adr/ADR-0010-oauth-social.md new file mode 100644 index 000000000..96f6297b2 --- /dev/null +++ b/docs/97-adr/ADR-0010-oauth-social.md @@ -0,0 +1,258 @@ +--- +id: ADR-0010 +type: ADR +title: "OAuth Social Strategy" +status: Accepted +decision_date: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +stakeholders: + - "Equipo MiChangarrito" +tags: + - oauth + - authentication + - google + - apple + - passport +--- + +# ADR-0010: OAuth Social Strategy + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | ADR-0010 | +| **Estado** | Accepted | +| **Fecha** | 2026-01-10 | +| **Autor** | Architecture Team | +| **Supersede** | - | + +--- + +## Contexto + +MiChangarrito quiere permitir registro e inicio de sesion con cuentas sociales (Google, Apple) para reducir friccion de onboarding. Necesitamos decidir: + +1. Que proveedores soportar +2. Como integrar con nuestro sistema de auth existente +3. Como manejar vinculacion de cuentas + +--- + +## Decision + +**Implementamos OAuth 2.0 con Passport.js para Google y Apple, con tabla separada oauth_accounts vinculada a users.** + +- Google: Principal proveedor social +- Apple: Requerido para iOS App Store +- Passport.js: Strategies bien mantenidas + +--- + +## Alternativas Consideradas + +### Opcion 1: Auth0/Firebase Auth +- **Pros:** + - Todo manejado + - Multiples providers +- **Cons:** + - Costo adicional + - Dependencia externa + - Menos control + +### Opcion 2: Passport.js (Elegida) +- **Pros:** + - Open source + - Bien documentado + - Control total + - Sin costos adicionales +- **Cons:** + - Mas trabajo de implementacion + - Mantener actualizaciones + +### Opcion 3: Implementacion manual +- **Pros:** + - Control absoluto +- **Cons:** + - Mucho trabajo + - Propenso a errores de seguridad + - Mantener cambios de API + +--- + +## Consecuencias + +### Positivas + +1. **Control:** Logica de negocio en nuestro codigo +2. **Flexibilidad:** Agregar providers facilmente +3. **Integracion:** Se integra con nuestro JWT flow +4. **Costo:** Sin gastos adicionales + +### Negativas + +1. **Mantenimiento:** Actualizar strategies +2. **Configuracion:** Setup en consolas de providers + +--- + +## Implementacion + +### Modelo de Datos + +```sql +CREATE TABLE auth.oauth_accounts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID REFERENCES auth.users(id) NOT NULL, + provider VARCHAR(20) NOT NULL, -- google, apple + provider_user_id VARCHAR(255) NOT NULL, + email VARCHAR(255), + name VARCHAR(255), + avatar_url TEXT, + access_token TEXT, + refresh_token TEXT, + expires_at TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + UNIQUE(provider, provider_user_id) +); +``` + +### Flujo de Autenticacion + +``` +1. Usuario hace clic en "Continuar con Google" +2. Redirect a Google OAuth +3. Usuario autoriza +4. Callback a nuestro servidor con code +5. Intercambiar code por tokens +6. Obtener perfil de usuario +7. Buscar/crear usuario en nuestra BD +8. Generar JWT nuestro +9. Redirect a frontend con JWT +``` + +### Casos de Vinculacion + +| Escenario | Accion | +|-----------|--------| +| Nuevo usuario, nuevo email | Crear user + oauth_account | +| Email existe, mismo provider | Error (ya vinculado) | +| Email existe, otro provider | Ofrecer vincular cuentas | +| Usuario logueado vincula nueva | Agregar oauth_account | + +### Codigo de Vinculacion + +```typescript +async findOrCreateFromOAuth(profile: OAuthProfile): Promise { + // 1. Buscar oauth_account existente + const existingOAuth = await this.oauthRepo.findOne({ + where: { + provider: profile.provider, + providerUserId: profile.id, + }, + }); + + if (existingOAuth) { + return existingOAuth.user; + } + + // 2. Buscar user por email + const existingUser = await this.userRepo.findOne({ + where: { email: profile.email }, + }); + + if (existingUser) { + // Vincular automaticamente si email verificado en provider + if (profile.emailVerified) { + await this.createOAuthAccount(existingUser, profile); + return existingUser; + } + + // Sino, pedir confirmacion + throw new EmailExistsError(profile.email, profile.provider); + } + + // 3. Crear nuevo usuario + const user = await this.createUser({ + email: profile.email, + name: profile.name, + avatarUrl: profile.avatar, + emailVerified: profile.emailVerified, + }); + + await this.createOAuthAccount(user, profile); + + return user; +} +``` + +--- + +## Providers Soportados + +### Google + +```typescript +passport.use(new GoogleStrategy({ + clientID: process.env.GOOGLE_CLIENT_ID, + clientSecret: process.env.GOOGLE_CLIENT_SECRET, + callbackURL: '/auth/google/callback', + scope: ['profile', 'email'], +}, verify)); +``` + +### Apple + +```typescript +passport.use(new AppleStrategy({ + clientID: process.env.APPLE_CLIENT_ID, + teamID: process.env.APPLE_TEAM_ID, + keyID: process.env.APPLE_KEY_ID, + privateKeyString: process.env.APPLE_PRIVATE_KEY, + callbackURL: '/auth/apple/callback', + scope: ['name', 'email'], +}, verify)); +``` + +--- + +## Mobile + +### Google (Expo) + +```typescript +const [request, response, promptAsync] = Google.useAuthRequest({ + clientId: GOOGLE_CLIENT_ID, + iosClientId: GOOGLE_IOS_CLIENT_ID, + androidClientId: GOOGLE_ANDROID_CLIENT_ID, +}); +``` + +### Apple (iOS) + +```typescript +import * as AppleAuthentication from 'expo-apple-authentication'; + +const credential = await AppleAuthentication.signInAsync({ + requestedScopes: [ + AppleAuthentication.AppleAuthenticationScope.FULL_NAME, + AppleAuthentication.AppleAuthenticationScope.EMAIL, + ], +}); +``` + +--- + +## Referencias + +- [Passport.js](http://www.passportjs.org/) +- [Google Identity](https://developers.google.com/identity) +- [Sign in with Apple](https://developer.apple.com/sign-in-with-apple/) +- [INT-012: OAuth Social](../02-integraciones/INT-012-oauth-social.md) +- [MCH-030: Auth Social](../01-epicas/MCH-030-auth-social.md) + +--- + +**Fecha decision:** 2026-01-10 +**Autores:** Architecture Team diff --git a/docs/97-adr/ADR-0011-email-multi-provider.md b/docs/97-adr/ADR-0011-email-multi-provider.md new file mode 100644 index 000000000..e8ab95adc --- /dev/null +++ b/docs/97-adr/ADR-0011-email-multi-provider.md @@ -0,0 +1,295 @@ +--- +id: ADR-0011 +type: ADR +title: "Email Multi-Provider" +status: Accepted +decision_date: 2026-01-10 +updated_at: 2026-01-10 +simco_version: "4.0.1" +stakeholders: + - "Equipo MiChangarrito" +tags: + - email + - notifications + - sendgrid + - ses + - multi-provider +--- + +# ADR-0011: Email Multi-Provider + +## Metadata + +| Campo | Valor | +|-------|-------| +| **ID** | ADR-0011 | +| **Estado** | Accepted | +| **Fecha** | 2026-01-10 | +| **Autor** | Architecture Team | +| **Supersede** | - | + +--- + +## Contexto + +MiChangarrito necesita enviar emails transaccionales (bienvenida, verificacion, notificaciones, facturas). Queremos: + +1. Alta deliverability +2. Fallback si un proveedor falla +3. Flexibilidad para cambiar proveedor +4. Costos optimizados + +--- + +## Decision + +**Implementamos Factory Pattern con soporte para SendGrid, AWS SES y SMTP generico, con fallback automatico.** + +Orden de prioridad: +1. SendGrid (principal) +2. AWS SES (fallback) +3. SMTP (ultimo recurso) + +--- + +## Alternativas Consideradas + +### Opcion 1: Solo SendGrid +- **Pros:** + - Simple + - Buena API +- **Cons:** + - Single point of failure + - Vendor lock-in + +### Opcion 2: Solo AWS SES +- **Pros:** + - Integrado con AWS + - Muy economico +- **Cons:** + - Setup complejo + - Sandbox restrictivo + +### Opcion 3: Multi-Provider con fallback (Elegida) +- **Pros:** + - Alta disponibilidad + - Flexibilidad + - Optimizacion de costos +- **Cons:** + - Mas complejidad + - Mantener multiples integraciones + +--- + +## Consecuencias + +### Positivas + +1. **Disponibilidad:** Fallback automatico +2. **Flexibilidad:** Cambiar provider por tenant +3. **Economia:** Usar el mas economico como default +4. **Deliverability:** SendGrid tiene buena reputacion + +### Negativas + +1. **Complejidad:** Tres implementaciones +2. **Configuracion:** Multiples sets de credenciales + +--- + +## Implementacion + +### Factory + +```typescript +@Injectable() +export class EmailProviderFactory { + private providers: EmailProvider[]; + + constructor( + private sendgrid: SendGridProvider, + private ses: SESProvider, + private smtp: SMTPProvider, + ) { + // Orden de prioridad + this.providers = [sendgrid, ses, smtp].filter(p => p.isConfigured()); + } + + async send(email: EmailDto): Promise { + for (const provider of this.providers) { + try { + return await provider.send(email); + } catch (error) { + this.logger.warn(`Provider ${provider.name} failed: ${error.message}`); + continue; + } + } + throw new Error('All email providers failed'); + } +} +``` + +### Interface + +```typescript +interface EmailProvider { + name: string; + isConfigured(): boolean; + send(email: EmailDto): Promise; +} + +interface EmailDto { + to: string | string[]; + subject: string; + html?: string; + text?: string; + template?: string; + variables?: Record; + from?: string; + replyTo?: string; + attachments?: Attachment[]; +} +``` + +### SendGrid Provider + +```typescript +@Injectable() +export class SendGridProvider implements EmailProvider { + name = 'sendgrid'; + + private client: MailService; + + constructor(config: ConfigService) { + if (config.get('SENDGRID_API_KEY')) { + this.client = new MailService(); + this.client.setApiKey(config.get('SENDGRID_API_KEY')); + } + } + + isConfigured(): boolean { + return !!this.client; + } + + async send(email: EmailDto): Promise { + const msg = { + to: email.to, + from: email.from || process.env.EMAIL_FROM, + subject: email.subject, + html: email.html, + text: email.text, + }; + + const [response] = await this.client.send(msg); + + return { + messageId: response.headers['x-message-id'], + provider: this.name, + }; + } +} +``` + +--- + +## Templates + +### Almacenamiento + +- Templates en base de datos (tenant-specific) +- Templates por defecto en codigo (fallback) + +### Renderizado + +```typescript +async renderTemplate( + key: string, + variables: Record, + tenantId?: string, +): Promise<{ html: string; text: string }> { + // Buscar template custom del tenant + let template = await this.findTemplate(key, tenantId); + + // Fallback a template por defecto + if (!template) { + template = this.getDefaultTemplate(key); + } + + // Renderizar con Handlebars + const html = Handlebars.compile(template.html)(variables); + const text = Handlebars.compile(template.text)(variables); + + return { html, text }; +} +``` + +--- + +## Rate Limiting + +### Por Tenant + +```typescript +async checkTenantEmailLimit(tenantId: string): Promise { + const plan = await this.getPlan(tenantId); + const key = `email:count:${tenantId}:${format(new Date(), 'yyyy-MM-dd-HH')}`; + + const count = await this.redis.incr(key); + await this.redis.expire(key, 3600); + + return count <= plan.emailsPerHour; +} +``` + +### Limites + +| Plan | Por Hora | Por Dia | +|------|----------|---------| +| Basic | 100 | 500 | +| Pro | 1,000 | 10,000 | +| Enterprise | Ilimitado | Ilimitado | + +--- + +## Tracking + +### Webhooks de Estado + +```typescript +// POST /webhooks/email/sendgrid +async handleSendGridWebhook(events: SendGridEvent[]) { + for (const event of events) { + await this.emailLogRepo.update( + { providerMessageId: event.sg_message_id }, + { + status: this.mapStatus(event.event), + [event.event + 'At']: new Date(event.timestamp * 1000), + } + ); + } +} +``` + +### Estados + +- sent: Email enviado al proveedor +- delivered: Entregado al servidor destino +- opened: Abierto por destinatario (si tracking habilitado) +- clicked: Link clickeado +- bounced: Rebotado +- spam: Marcado como spam + +--- + +## Referencias + +- [SendGrid API](https://docs.sendgrid.com/api-reference) +- [AWS SES](https://docs.aws.amazon.com/ses/) +- [Nodemailer](https://nodemailer.com/) +- [INT-010: Email Providers](../02-integraciones/INT-010-email-providers.md) +- [MCH-029: Infraestructura SaaS](../01-epicas/MCH-029-infraestructura-saas.md) + +--- + +**Fecha decision:** 2026-01-10 +**Autores:** Architecture Team diff --git a/docs/97-adr/_MAP.md b/docs/97-adr/_MAP.md index f51239333..7cf2f4564 100644 --- a/docs/97-adr/_MAP.md +++ b/docs/97-adr/_MAP.md @@ -11,8 +11,8 @@ | Metrica | Valor | |---------|-------| -| Total ADRs | 3 | -| Aceptados | 3 | +| Total ADRs | 11 | +| Aceptados | 11 | | Propuestos | 0 | | Deprecated | 0 | @@ -27,6 +27,14 @@ | [ADR-0001](./ADR-0001-multi-tenant-architecture.md) | Arquitectura Multi-Tenant | Accepted | 2026-01-06 | Alto | | [ADR-0002](./ADR-0002-whatsapp-first-approach.md) | WhatsApp como Canal Principal | Accepted | 2026-01-06 | Alto | | [ADR-0003](./ADR-0003-llm-agnostic-strategy.md) | Estrategia LLM Agnostica | Accepted | 2026-01-06 | Medio | +| [ADR-0004](./ADR-0004-notifications-realtime.md) | Notificaciones en Tiempo Real | Accepted | 2026-01-10 | Medio | +| [ADR-0005](./ADR-0005-feature-flags.md) | Feature Flags por Plan | Accepted | 2026-01-10 | Alto | +| [ADR-0006](./ADR-0006-storage-abstraction.md) | Storage Abstraction | Accepted | 2026-01-10 | Alto | +| [ADR-0007](./ADR-0007-webhook-retry-strategy.md) | Webhook Retry Strategy | Accepted | 2026-01-10 | Medio | +| [ADR-0008](./ADR-0008-audit-log-retention.md) | Audit Log Retention | Accepted | 2026-01-10 | Medio | +| [ADR-0009](./ADR-0009-rate-limiting.md) | Rate Limiting Strategy | Accepted | 2026-01-10 | Alto | +| [ADR-0010](./ADR-0010-oauth-social.md) | OAuth Social Strategy | Accepted | 2026-01-10 | Medio | +| [ADR-0011](./ADR-0011-email-multi-provider.md) | Email Multi-Provider | Accepted | 2026-01-10 | Medio | --- @@ -34,12 +42,24 @@ ### Arquitectura - [ADR-0001 - Multi-Tenant](./ADR-0001-multi-tenant-architecture.md) +- [ADR-0006 - Storage Abstraction](./ADR-0006-storage-abstraction.md) +- [ADR-0009 - Rate Limiting](./ADR-0009-rate-limiting.md) ### Producto - [ADR-0002 - WhatsApp First](./ADR-0002-whatsapp-first-approach.md) +- [ADR-0005 - Feature Flags](./ADR-0005-feature-flags.md) ### Tecnologia - [ADR-0003 - LLM Agnostico](./ADR-0003-llm-agnostic-strategy.md) +- [ADR-0011 - Email Multi-Provider](./ADR-0011-email-multi-provider.md) + +### Infraestructura +- [ADR-0004 - Notificaciones Realtime](./ADR-0004-notifications-realtime.md) +- [ADR-0007 - Webhook Retry Strategy](./ADR-0007-webhook-retry-strategy.md) + +### Seguridad y Compliance +- [ADR-0008 - Audit Log Retention](./ADR-0008-audit-log-retention.md) +- [ADR-0010 - OAuth Social](./ADR-0010-oauth-social.md) --- @@ -106,4 +126,5 @@ Crear un ADR cuando: --- **Mantenido por:** Architecture Team -**Version:** 1.0.0 +**Version:** 2.0.0 +**Total ADRs:** 11 (ADR-0001 a ADR-0011) diff --git a/orchestration/00-guidelines/HERENCIA-SIMCO.md b/orchestration/00-guidelines/HERENCIA-SIMCO.md index 6d3bc23bc..0dfdbbe73 100644 --- a/orchestration/00-guidelines/HERENCIA-SIMCO.md +++ b/orchestration/00-guidelines/HERENCIA-SIMCO.md @@ -1,77 +1,107 @@ # Herencia SIMCO - michangarrito -**Sistema:** SIMCO v4.0.0 + CAPVED + SCRUM +**Sistema:** SIMCO v4.0.1 + CAPVED + SCRUM **Fecha:** 2026-01-10 +**Version anterior:** v3.8.0 (workspace-v1) + v4.0.0 (workspace-v2) --- -## Configuración del Proyecto +## 1. Configuracion del Proyecto | Propiedad | Valor | |-----------|-------| | **Proyecto** | Mi Changarrito - Sistema POS para Pequenos Comercios | -| **Código v2** | MCH | -| **SIMCO Version** | 4.0.0 | +| **Codigo v2** | MCH | +| **Nivel** | STANDALONE (Nivel 2A) | +| **Padre** | workspace-v2/orchestration | +| **SIMCO Version** | 4.0.1 | | **CAPVED** | Habilitado | | **SCRUM** | Habilitado | +| **CCA Protocol** | Habilitado | --- -## Metodología CAPVED+SCRUM +## 2. Jerarquia de Herencia -Este proyecto utiliza la metodología integrada CAPVED+SCRUM definida en SIMCO v4.0.0. +``` +Nivel 0: workspace-v2/orchestration/ <- WORKSPACE (directivas globales) + | + +-- STANDALONE: michangarrito/orchestration/ <- ESTE PROYECTO + michangarrito/docs/ <- DOCUMENTACION +``` + +**Regla:** Las directivas locales pueden EXTENDER las del workspace, nunca REDUCIRLAS. + +**Estado del Proyecto:** MVP 95% Implementado + +--- + +## 3. Directivas Heredadas de WORKSPACE (OBLIGATORIAS) + +Ubicacion: `workspace-v2/orchestration/` + +| Alias | Archivo | Proposito | +|-------|---------|-----------| +| `@CARGA-CONTEXTO` | `directivas/DIRECTIVA-CARGA-CONTEXTO.md` | Como cargar contexto segun nivel | +| `@INDICE` | `INDICE-DIRECTIVAS-WORKSPACE.yml` | Indice maestro de directivas | + +--- + +## 4. Metodologia CAPVED+SCRUM + +Este proyecto utiliza la metodologia integrada CAPVED+SCRUM definida en SIMCO v4.0. ### Ciclo CAPVED dentro de Sprint ``` Sprint Start - │ - ├── Sprint Planning (usar @TPL_SPRINT_PLANNING) - │ - └── Por cada HU: - ├── [C] Contexto → Cargar directivas, verificar DoR - ├── [A] Análisis → Analizar requerimientos - ├── [P] Plan → Planificar implementación - ├── [V] Validación → Validar plan técnico - ├── [E] Ejecución → Implementar código - └── [D] Documentar → Actualizar docs, verificar DoD - │ - ├── Sprint Review - └── Sprint Retrospective (usar @TPL_RETROSPECTIVA) + | + +-- Sprint Planning (usar @TPL_SPRINT_PLANNING) + | + +-- Por cada HU: + +-- [C] Contexto -> Cargar directivas, verificar DoR + +-- [A] Analisis -> Analizar requerimientos + +-- [P] Plan -> Planificar implementacion + +-- [V] Validacion -> Validar plan tecnico + +-- [E] Ejecucion -> Implementar codigo + +-- [D] Documentar -> Actualizar docs, verificar DoD + | + +-- Sprint Review + +-- Sprint Retrospective (usar @TPL_RETROSPECTIVA) ``` --- -## Directivas SCRUM (SIMCO v4.0.0) +## 5. Directivas SCRUM (SIMCO v4.0) -### Ejecución de Sprint +### Ejecucion de Sprint -| Alias | Directiva | Propósito | +| Alias | Directiva | Proposito | |-------|-----------|-----------| | `@SPRINT_EXECUTION` | `SIMCO-SPRINT-EXECUTION.md` | Ciclo completo de Sprint | -| `@AGILE_METRICS` | `SIMCO-AGILE-METRICS.md` | Métricas Velocity, Burndown | -| `@SCRUM_INTEGRATION` | `SIMCO-SCRUM-INTEGRATION.md` | Integración CAPVED+SCRUM | +| `@AGILE_METRICS` | `SIMCO-AGILE-METRICS.md` | Metricas Velocity, Burndown | +| `@SCRUM_INTEGRATION` | `SIMCO-SCRUM-INTEGRATION.md` | Integracion CAPVED+SCRUM | ### Definition of Ready (DoR) -| Criterio | Descripción | +| Criterio | Descripcion | |----------|-------------| | ID asignado | `MCH-US-NNN` | | Historia completa | Como/Quiero/Para | -| Criterios de aceptación | 3-8 criterios | +| Criterios de aceptacion | 3-8 criterios | | Story Points | Fibonacci (1,2,3,5,8,13) | | Dependencias identificadas | Sin bloqueos | -| PO aprobado | ✓ | +| PO aprobado | Si | ### Definition of Done (DoD) -| Criterio | Descripción | +| Criterio | Descripcion | |----------|-------------| -| Código implementado | Según especificación | +| Codigo implementado | Segun especificacion | | Build pasa | Sin errores | -| Tests pasando | Unitarios + integración | +| Tests pasando | Unitarios + integracion | | Code review | Completado | -| Documentación | Actualizada | +| Documentacion | Actualizada | | TRACEABILITY.yml | Actualizado | ### Templates SCRUM @@ -91,50 +121,264 @@ Sprint Start --- -## Directivas CAPVED (Heredadas) +## 6. Directivas de Ciclo de Vida (USAR SIEMPRE) -### Ciclo de Vida - -| Alias | Archivo | Propósito | +| Alias | Archivo | Proposito | |-------|---------|-----------| -| `@TAREA` | `SIMCO-TAREA.md` | Punto de entrada | -| `@CAPVED` | `PRINCIPIO-CAPVED.md` | Ciclo de 6 fases | -| `@INICIALIZACION` | `SIMCO-INICIALIZACION.md` | Bootstrap de agentes | - -### Operaciones - -| Alias | Archivo | Propósito | -|-------|---------|-----------| -| `@CREAR` | `SIMCO-CREAR.md` | Crear archivos | -| `@MODIFICAR` | `SIMCO-MODIFICAR.md` | Modificar archivos | -| `@VALIDAR` | `SIMCO-VALIDAR.md` | Validar código | -| `@DOCUMENTAR` | `SIMCO-DOCUMENTAR.md` | Documentar trabajo | -| `@BUSCAR` | `SIMCO-BUSCAR.md` | Buscar información | +| `@TAREA` | `directivas/simco/SIMCO-TAREA.md` | Punto de entrada para toda HU | +| `@CAPVED` | `directivas/principios/PRINCIPIO-CAPVED.md` | Ciclo de 6 fases | +| `@INICIALIZACION` | `directivas/simco/SIMCO-INICIALIZACION.md` | Bootstrap de agentes | +| `@DOC-DEFINITIVA` | `directivas/DIRECTIVA-DOCUMENTACION-DEFINITIVA.md` | Docs como estado final | --- -## Directivas de Documentación +## 7. Operaciones Universales -| Alias | Directiva | Propósito | +| Alias | Archivo | Proposito | +|-------|---------|-----------| +| `@CREAR` | `SIMCO-CREAR.md` | Crear archivos nuevos | +| `@MODIFICAR` | `SIMCO-MODIFICAR.md` | Modificar existentes | +| `@VALIDAR` | `SIMCO-VALIDAR.md` | Validar codigo | +| `@DOCUMENTAR` | `SIMCO-DOCUMENTAR.md` | Documentar trabajo | +| `@BUSCAR` | `SIMCO-BUSCAR.md` | Buscar informacion | +| `@DELEGAR` | `SIMCO-DELEGACION.md` | Delegar a subagentes | + +--- + +## 8. Directivas de Subagentes (SIMCO v4.0.0) + +| Alias | Archivo | Descripcion | +|-------|---------|-------------| +| `@SUBAGENTE` | `SIMCO-SUBAGENTE.md` | Protocolo cuando recibes delegacion | +| `@CCA_SUBAGENTE` | `SIMCO-CCA-SUBAGENTE.md` | CCA ligero (~1500 tokens) | +| `@CONTROL_TOKENS` | `SIMCO-CONTROL-TOKENS.md` | Gestion de limites de tokens | +| `@DELEGACION_PARALELA` | `SIMCO-DELEGACION-PARALELA.md` | Delegacion a multiples agentes | + +--- + +## 9. Directivas de Context Engineering + +| Alias | Archivo | Descripcion | +|-------|---------|-------------| +| `@CONTEXT_ENGINEERING` | `SIMCO-CONTEXT-ENGINEERING.md` | Ingenieria de contexto | +| `@CONTEXT_RESOLUTION` | `SIMCO-CONTEXT-RESOLUTION.md` | Resolucion de contextos | + +--- + +## 10. Directivas de Niveles y Propagacion + +| Alias | Archivo | Descripcion | +|-------|---------|-------------| +| `@NIVELES` | `SIMCO-NIVELES.md` | Jerarquia de niveles | +| `@PROPAGACION` | `SIMCO-PROPAGACION.md` | Propagacion de directivas | + +--- + +## 11. Directivas Git y Gobernanza + +| Alias | Archivo | Descripcion | +|-------|---------|-------------| +| `@GIT` | `SIMCO-GIT.md` | Operaciones Git | +| `@GIT_REMOTES` | `SIMCO-GIT-REMOTES.md` | Gestion de remotos | +| `@ESCALAMIENTO` | `SIMCO-ESCALAMIENTO.md` | Escalamiento de decisiones | + +--- + +## 12. Principios Fundamentales (5) + +| Alias | Resumen | +|-------|---------| +| `@CAPVED` | Toda tarea pasa por 6 fases | +| `@DOC_PRIMERO` | Consultar docs/ antes de implementar | +| `@ANTI_DUP` | Verificar que no existe antes de crear | +| `@VALIDACION` | Build y lint DEBEN pasar | +| `@TOKENS` | Desglosar tareas grandes | + +--- + +## 13. Directivas por Dominio Tecnico + +| Alias | Aplica | Notas | +|-------|--------|-------| +| `@OP_DDL` | **SI** | 11 schemas, 53 tablas | +| `@OP_BACKEND` | **SI** | NestJS, 18 modulos | +| `@OP_FRONTEND` | **SI** | React + Vite | +| `@OP_MOBILE` | **SI** | React Native (Expo) | +| `@OP_ML` | NO | (LLM via MCP, no ML propio) | + +--- + +## 14. Directivas de Documentacion + +| Alias | Directiva | Proposito | |-------|-----------|-----------| | `@DOC_PROYECTO` | `SIMCO-DOCUMENTACION-PROYECTO.md` | Estructura docs/ | | `@NOMENCLATURA` | `SIMCO-NOMENCLATURA.md` | Convenciones de IDs | | `@ESTRUCTURA_DOCS` | `SIMCO-ESTRUCTURA-DOCS.md` | Estructura interna | | `@INVENTARIOS` | `SIMCO-INVENTARIOS.md` | Inventarios YAML | | `@MANTENIMIENTO_DOCS` | `SIMCO-MANTENIMIENTO-DOCUMENTACION.md` | Ciclo mantenimiento | -| `@SYNC_BD` | `SIMCO-SINCRONIZACION-BD.md` | Sincronización BD↔Docs | +| `@SYNC_BD` | `SIMCO-SINCRONIZACION-BD.md` | Sincronizacion BD<->Docs | +| `@TESTING` | `SIMCO-TESTING.md` | Cobertura y estandares de testing | +| `@MIGRACIONES` | `SIMCO-MIGRACIONES-BD.md` | Migraciones y DDL | +| `@INTEGRACIONES` | `SIMCO-INTEGRACIONES-EXTERNAS.md` | Documentacion de integraciones | + +### Checklists de Documentacion + +| Alias | Checklist | Items | +|-------|-----------|-------| +| `@CHK_DOCUMENTACION` | `CHECKLIST-DOCUMENTACION-PROYECTO.md` | 44 | +| `@CHK_INVENTARIOS` | `CHECKLIST-INVENTARIOS.md` | 63 | +| `@CHK_NOMENCLATURA` | `CHECKLIST-NOMENCLATURA.md` | 40 | +| `@CHK_MANTENIMIENTO` | `CHECKLIST-MANTENIMIENTO-DOCS.md` | 80 | +| `@CHK_SYNC_BD` | `CHECKLIST-SINCRONIZACION-BD.md` | 70 | + +### Templates de Documentacion + +| Alias | Template | Uso | +|-------|----------|-----| +| `@TPL_INVENTARIO` | `TEMPLATE-INVENTARIO-PROYECTO.md` | Crear inventarios YAML | +| `@TPL_INTEGRACION` | `TEMPLATE-INTEGRACION-EXTERNA.md` | Documentar integraciones | +| `@TPL_MODULO_ESTANDAR` | `TEMPLATE-MODULO-ESTANDAR.md` | Documentar modulos | +| `@TPL_DEPRECACION` | `TEMPLATE-DEPRECACION.md` | Marcar documentos como deprecados | --- -## Trazabilidad v2 +## 15. Patrones Aplicables + +| Patron | Uso en MiChangarrito | +|--------|----------------------| +| `MAPEO-TIPOS-DDL-TYPESCRIPT.md` | 11 schemas -> Entities | +| `PATRON-VALIDACION.md` | class-validator en DTOs | +| `PATRON-EXCEPTION-HANDLING.md` | Filtros NestJS | +| `PATRON-TESTING.md` | Jest + e2e tests | +| `PATRON-SEGURIDAD.md` | JWT, multi-tenant RLS | +| `PATRON-TRANSACCIONES.md` | TypeORM transactions | +| `ANTIPATRONES.md` | Evitar siempre | + +--- + +## 16. Variables de Contexto CCA + +```yaml +# Identificacion del Proyecto +PROJECT_NAME: "michangarrito" +PROJECT_CODE: "MCH" +PROJECT_LEVEL: "STANDALONE" +PROJECT_ROOT: "/home/isem/workspace-v2/projects/michangarrito" + +# Rutas principales +APPS_ROOT: "apps" +DOCS_ROOT: "docs" +ORCHESTRATION: "orchestration" + +# Base de Datos +DB_NAME: "michangarrito" +DB_DDL_PATH: "database/schemas" +DB_SCRIPTS_PATH: "database" +DB_SEEDS_PATH: "database/seeds" + +# Backend (NestJS) +BACKEND_ROOT: "apps/backend" +BACKEND_SRC: "apps/backend/src" +BACKEND_FRAMEWORK: "NestJS" +ORM: "TypeORM" +BACKEND_PORT: 3141 + +# Frontend (React) +FRONTEND_ROOT: "apps/web" +FRONTEND_SRC: "apps/web/src" +FRONTEND_FRAMEWORK: "React" +BUILD_TOOL: "Vite" +FRONTEND_PORT: 3140 + +# Mobile (React Native) +MOBILE_ROOT: "apps/mobile" +MOBILE_FRAMEWORK: "React Native (Expo)" +MOBILE_PORT: 8081 + +# Servicios Adicionales +MCP_SERVER_ROOT: "apps/mcp-server" +MCP_SERVER_PORT: 3142 +WHATSAPP_SERVICE_ROOT: "apps/whatsapp-service" +WHATSAPP_PORT: 3143 + +# Multi-tenant +TENANT_COLUMN: "tenant_id" +RLS_CONTEXT: "app.current_tenant_id" + +# Inventarios +MASTER_INVENTORY: "orchestration/inventarios/MASTER_INVENTORY.yml" +``` + +--- + +## 17. Schemas de Base de Datos (11) + +| Schema | Descripcion | Tablas | +|--------|-------------|--------| +| `public` | Tenants, configuracion global | ~3 | +| `auth` | Autenticacion, tokens | ~4 | +| `catalog` | Productos, categorias | ~5 | +| `sales` | Ventas, transacciones, CoDi, SPEI | ~6 | +| `inventory` | Stock, movimientos | ~4 | +| `customers` | Clientes, fiados | ~4 | +| `orders` | Pedidos WhatsApp | ~5 | +| `subscriptions` | Planes, tokens, referidos | ~5 | +| `messaging` | Conversaciones, mensajes | ~4 | +| `billing` | CFDI 4.0, facturas | ~6 | +| `marketplace` | Proveedores B2B | ~7 | + +**Total:** ~53 tablas + +--- + +## 18. Integraciones Externas + +### Integraciones Core (INT-001 a INT-009) + +| ID | Integracion | Proveedor | Estado | Doc | +|----|-------------|-----------|--------|-----| +| INT-001 | WhatsApp | Meta Business API | Activo | [INT-001](../docs/02-integraciones/INT-001-whatsapp.md) | +| INT-002 | Pagos | Stripe | Activo | [INT-002](../docs/02-integraciones/INT-002-stripe.md) | +| INT-003 | Terminal | Mercado Pago | Activo | [INT-003](../docs/02-integraciones/INT-003-mercadopago.md) | +| INT-004 | Terminal | Clip | Pendiente | [INT-004](../docs/02-integraciones/INT-004-clip.md) | +| INT-005 | CoDi | Banxico via Openpay | Activo | [INT-005](../docs/02-integraciones/INT-005-codi.md) | +| INT-006 | SPEI | STP | Activo | [INT-006](../docs/02-integraciones/INT-006-spei.md) | +| INT-007 | Facturacion | Facturapi/PAC CFDI 4.0 | Activo | [INT-007](../docs/02-integraciones/INT-007-facturacion.md) | +| INT-008 | LLM Gateway | OpenRouter | Activo | [INT-008](../docs/02-integraciones/INT-008-llm.md) | +| INT-009 | Push | Firebase FCM | Activo | [INT-009](../docs/02-integraciones/INT-009-push.md) | + +### Integraciones SaaS (INT-010 a INT-014) + +| ID | Integracion | Proveedor | Estado | Doc | +|----|-------------|-----------|--------|-----| +| INT-010 | Email Multi-Provider | SendGrid/SES/SMTP | Planificado | [INT-010](../docs/02-integraciones/INT-010-email-providers.md) | +| INT-011 | Storage Cloud | S3/R2/MinIO | Planificado | [INT-011](../docs/02-integraciones/INT-011-storage-cloud.md) | +| INT-012 | OAuth Social | Google/Apple | Planificado | [INT-012](../docs/02-integraciones/INT-012-oauth-social.md) | +| INT-013 | Redis Cache/Queue | Redis/BullMQ | Planificado | [INT-013](../docs/02-integraciones/INT-013-redis-cache.md) | +| INT-014 | Webhooks Outbound | BullMQ | Planificado | [INT-014](../docs/02-integraciones/INT-014-webhooks-outbound.md) | + +### Integraciones Pendientes + +| Integracion | Proveedor | Estado | Notas | +|-------------|-----------|--------|-------| +| OCR | Google Vision | Backlog | MCH-018 | +| Audio | Whisper | Activo | Via OpenRouter | +| Web Push | OneSignal/Firebase | Backlog | MCH-034 | + +**Total Integraciones:** 14 documentadas (9 activas, 5 planificadas) + +--- + +## 19. Trazabilidad v2 ### TRACEABILITY-MASTER.yml -Ubicación: `docs/04-modelado/trazabilidad/TRACEABILITY-MASTER.yml` +Ubicacion: `docs/04-modelado/trazabilidad/TRACEABILITY-MASTER.yml` Este archivo consolida: -- Mapeo de nomenclatura v1 → v2 -- Lista de épicas con estados +- Mapeo de nomenclatura v1 -> v2 +- Lista de epicas con estados - Grafo de dependencias - Health score del proyecto @@ -142,7 +386,7 @@ Este archivo consolida: | Tipo | Formato | Ejemplo | |------|---------|---------| -| Épica | `MCH-EP-NNN` | MCH-EP-001 | +| Epica | `MCH-NNN` | MCH-001 | | Requerimiento | `MCH-RF-NNN` | MCH-RF-001 | | User Story | `MCH-US-NNN` | MCH-US-001 | | Tarea | `MCH-TT-NNN` | MCH-TT-001 | @@ -150,7 +394,20 @@ Este archivo consolida: --- -## Flujo de Trabajo Sprint +## 20. Perfiles de Agentes + +| Perfil | Especializacion | Frecuencia | +|--------|-----------------|------------| +| `PERFIL-DATABASE.md` | PostgreSQL, 11 schemas | Alta | +| `PERFIL-BACKEND.md` | NestJS, TypeORM | Alta | +| `PERFIL-FRONTEND.md` | React, Vite | Alta | +| `PERFIL-MOBILE.md` | React Native, Expo | Alta | +| `PERFIL-CODE-REVIEWER.md` | Revision de codigo | Media | +| `PERFIL-DOC-MAINTAINER.md` | Mantenimiento docs | Media | + +--- + +## 21. Flujo de Trabajo Sprint (SCRUM) ```yaml # Sprint Planning @@ -178,14 +435,95 @@ Este archivo consolida: --- -## Referencias +## 22. Flujo de Trabajo Desarrollo (CAPVED) + +```yaml +# PASO 1: Cargar contexto (CCA) +CARGAR: + - @TAREA + - @CAPVED + - @INICIALIZACION + - ./CONTEXTO-PROYECTO.md + +# PASO 2: Seleccionar operacion +OPERACION: + - @OP_DDL # 11 schemas + - @OP_BACKEND # NestJS + - @OP_FRONTEND # React + - @OP_MOBILE # React Native + +# PASO 3: Aplicar patrones +PATRONES: + - @PATRON-VALIDACION + - @PATRON-EXCEPTION-HANDLING + - @PATRON-SEGURIDAD + +# PASO 4: Validar +VALIDAR: + - npm run build + - npm run lint + - npm run test + +# PASO 5: Documentar +CIERRE: + - @DOCUMENTAR + - Actualizar inventarios + - Actualizar TRACEABILITY.yml +``` + +--- + +## 23. Referencias - Directivas SIMCO: `workspace-v2/orchestration/directivas/simco/` +- Principios: `workspace-v2/orchestration/directivas/principios/` - Templates SCRUM: `workspace-v2/orchestration/templates/scrum/` - Checklists: `workspace-v2/orchestration/checklists/` +- Perfiles: `workspace-v2/orchestration/agents/perfiles/` - TRACEABILITY-MASTER: `docs/04-modelado/trazabilidad/TRACEABILITY-MASTER.yml` --- -**Sistema:** SIMCO v4.0.0 + CAPVED + SCRUM -**Última actualización:** 2026-01-10 +## 24. Roadmap SaaS (Fases 7-9) + +### Épicas SaaS Planificadas + +| ID | Épica | SP | Fase | Sprint | +|----|-------|----|------|--------| +| MCH-029 | Infraestructura SaaS | 24 | 7 | 6-7 | +| MCH-030 | Auth Social (OAuth) | 8 | 7 | 8 | +| MCH-031 | Auditoría Empresarial | 5 | 7 | 7 | +| MCH-032 | Feature Flags | 5 | 7 | 8 | +| MCH-033 | Onboarding Wizard | 3 | 8 | 9 | + +**Total SaaS:** 45 Story Points + +### Capacidades SaaS Agregadas + +| Capacidad | Integración | ADR | Prioridad | +|-----------|-------------|-----|-----------| +| Email Multi-provider | INT-010 | ADR-0011 | P0 | +| Storage Abstracto | INT-011 | ADR-0006 | P0 | +| Redis Cache/Queue | INT-013 | - | P0 | +| Webhooks Outbound | INT-014 | ADR-0007 | P1 | +| Rate Limiting | INT-013 | ADR-0009 | P1 | +| OAuth Google/Apple | INT-012 | ADR-0010 | P1 | +| Audit Logs | - | ADR-0008 | P1 | +| Feature Flags | INT-013 | ADR-0005 | P1 | +| Onboarding Wizard | - | - | P2 | + +### Documentación SaaS + +| Tipo | Cantidad | IDs | +|------|----------|-----| +| Épicas | 5 | MCH-029 a MCH-033 | +| Integraciones | 5 | INT-010 a INT-014 | +| ADRs | 8 | ADR-0004 a ADR-0011 | +| **Total** | **18** | - | + +--- + +**Sistema:** SIMCO v4.0.1 + CAPVED + SCRUM + CCA Protocol +**Nivel:** STANDALONE (2A) +**Versión HERENCIA:** 2.0.0 +**Ultima actualizacion:** 2026-01-10 diff --git a/orchestration/CONTEXT-MAP.yml b/orchestration/CONTEXT-MAP.yml index 0adf2ef81..f01951fc2 100644 --- a/orchestration/CONTEXT-MAP.yml +++ b/orchestration/CONTEXT-MAP.yml @@ -10,8 +10,8 @@ metadata: nivel: "STANDALONE" version: "2.1.0" ultima_actualizacion: "2026-01-10" - workspace_root: "/home/isem/workspace-v1" - project_root: "/home/isem/workspace-v1/projects/michangarrito" + workspace_root: "/home/isem/workspace-v2" + project_root: "/home/isem/workspace-v2/projects/michangarrito" codigo: "MCH" # =============================================================================== @@ -27,37 +27,37 @@ variables: # Base de datos DB_NAME: "michangarrito" - DB_DDL_PATH: "/home/isem/workspace-v1/projects/michangarrito/database/schemas" - DB_SCRIPTS_PATH: "/home/isem/workspace-v1/projects/michangarrito/database" - DB_SEEDS_PATH: "/home/isem/workspace-v1/projects/michangarrito/database/seeds" + DB_DDL_PATH: "/home/isem/workspace-v2/projects/michangarrito/database/schemas" + DB_SCRIPTS_PATH: "/home/isem/workspace-v2/projects/michangarrito/database" + DB_SEEDS_PATH: "/home/isem/workspace-v2/projects/michangarrito/database/seeds" RECREATE_CMD: "drop-and-recreate-database.sh" # Backend - BACKEND_ROOT: "/home/isem/workspace-v1/projects/michangarrito/apps/backend" - BACKEND_SRC: "/home/isem/workspace-v1/projects/michangarrito/apps/backend/src" - BACKEND_TESTS: "/home/isem/workspace-v1/projects/michangarrito/apps/backend/tests" + BACKEND_ROOT: "/home/isem/workspace-v2/projects/michangarrito/apps/backend" + BACKEND_SRC: "/home/isem/workspace-v2/projects/michangarrito/apps/backend/src" + BACKEND_TESTS: "/home/isem/workspace-v2/projects/michangarrito/apps/backend/tests" BACKEND_PORT: 3141 # Frontend Web - FRONTEND_ROOT: "/home/isem/workspace-v1/projects/michangarrito/apps/web" - FRONTEND_SRC: "/home/isem/workspace-v1/projects/michangarrito/apps/web/src" + FRONTEND_ROOT: "/home/isem/workspace-v2/projects/michangarrito/apps/web" + FRONTEND_SRC: "/home/isem/workspace-v2/projects/michangarrito/apps/web/src" FRONTEND_PORT: 3140 # Mobile - MOBILE_ROOT: "/home/isem/workspace-v1/projects/michangarrito/apps/mobile" + MOBILE_ROOT: "/home/isem/workspace-v2/projects/michangarrito/apps/mobile" MOBILE_PORT: 8081 # MCP Server - MCP_SERVER_ROOT: "/home/isem/workspace-v1/projects/michangarrito/apps/mcp-server" + MCP_SERVER_ROOT: "/home/isem/workspace-v2/projects/michangarrito/apps/mcp-server" MCP_SERVER_PORT: 3142 # WhatsApp Service - WHATSAPP_ROOT: "/home/isem/workspace-v1/projects/michangarrito/apps/whatsapp-service" + WHATSAPP_ROOT: "/home/isem/workspace-v2/projects/michangarrito/apps/whatsapp-service" WHATSAPP_PORT: 3143 # Documentacion - DOCS_PATH: "/home/isem/workspace-v1/projects/michangarrito/docs" - ORCHESTRATION_PATH: "/home/isem/workspace-v1/projects/michangarrito/orchestration" + DOCS_PATH: "/home/isem/workspace-v2/projects/michangarrito/docs" + ORCHESTRATION_PATH: "/home/isem/workspace-v2/projects/michangarrito/orchestration" # =============================================================================== # ALIASES RESUELTOS @@ -65,32 +65,32 @@ variables: aliases: # Directivas globales - "@SIMCO": "/home/isem/workspace-v1/orchestration/directivas/simco" - "@PRINCIPIOS": "/home/isem/workspace-v1/orchestration/directivas/principios" - "@PERFILES": "/home/isem/workspace-v1/orchestration/agents/perfiles" - "@CATALOG": "/home/isem/workspace-v1/shared/catalog" + "@SIMCO": "/home/isem/workspace-v2/orchestration/directivas/simco" + "@PRINCIPIOS": "/home/isem/workspace-v2/orchestration/directivas/principios" + "@PERFILES": "/home/isem/workspace-v2/orchestration/agents/perfiles" + "@CATALOG": "/home/isem/workspace-v2/shared/catalog" # Proyecto especifico - "@DDL": "/home/isem/workspace-v1/projects/michangarrito/database/schemas" - "@SEEDS": "/home/isem/workspace-v1/projects/michangarrito/database/seeds" - "@BACKEND": "/home/isem/workspace-v1/projects/michangarrito/apps/backend/src" - "@WEB": "/home/isem/workspace-v1/projects/michangarrito/apps/web/src" - "@MOBILE": "/home/isem/workspace-v1/projects/michangarrito/apps/mobile" - "@MCP": "/home/isem/workspace-v1/projects/michangarrito/apps/mcp-server" - "@WHATSAPP": "/home/isem/workspace-v1/projects/michangarrito/apps/whatsapp-service" - "@DOCS": "/home/isem/workspace-v1/projects/michangarrito/docs" + "@DDL": "/home/isem/workspace-v2/projects/michangarrito/database/schemas" + "@SEEDS": "/home/isem/workspace-v2/projects/michangarrito/database/seeds" + "@BACKEND": "/home/isem/workspace-v2/projects/michangarrito/apps/backend/src" + "@WEB": "/home/isem/workspace-v2/projects/michangarrito/apps/web/src" + "@MOBILE": "/home/isem/workspace-v2/projects/michangarrito/apps/mobile" + "@MCP": "/home/isem/workspace-v2/projects/michangarrito/apps/mcp-server" + "@WHATSAPP": "/home/isem/workspace-v2/projects/michangarrito/apps/whatsapp-service" + "@DOCS": "/home/isem/workspace-v2/projects/michangarrito/docs" # Inventarios - "@INVENTORY": "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios" - "@INV_MASTER": "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/MASTER_INVENTORY.yml" - "@INV_DB": "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/DATABASE_INVENTORY.yml" - "@INV_BE": "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/BACKEND_INVENTORY.yml" - "@INV_FE": "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/FRONTEND_INVENTORY.yml" + "@INVENTORY": "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios" + "@INV_MASTER": "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/MASTER_INVENTORY.yml" + "@INV_DB": "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/DATABASE_INVENTORY.yml" + "@INV_BE": "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/BACKEND_INVENTORY.yml" + "@INV_FE": "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/FRONTEND_INVENTORY.yml" # Trazas - "@TRAZA_DB": "/home/isem/workspace-v1/projects/michangarrito/orchestration/trazas/TRAZA-TAREAS-DATABASE.md" - "@TRAZA_BE": "/home/isem/workspace-v1/projects/michangarrito/orchestration/trazas/TRAZA-TAREAS-BACKEND.md" - "@TRAZA_FE": "/home/isem/workspace-v1/projects/michangarrito/orchestration/trazas/TRAZA-TAREAS-FRONTEND.md" + "@TRAZA_DB": "/home/isem/workspace-v2/projects/michangarrito/orchestration/trazas/TRAZA-TAREAS-DATABASE.md" + "@TRAZA_BE": "/home/isem/workspace-v2/projects/michangarrito/orchestration/trazas/TRAZA-TAREAS-BACKEND.md" + "@TRAZA_FE": "/home/isem/workspace-v2/projects/michangarrito/orchestration/trazas/TRAZA-TAREAS-FRONTEND.md" # =============================================================================== # CONTEXTO POR NIVEL @@ -102,25 +102,25 @@ contexto_por_nivel: tokens_estimados: 4500 obligatorio: true archivos: - - path: "/home/isem/workspace-v1/orchestration/directivas/principios/PRINCIPIO-CAPVED.md" + - path: "/home/isem/workspace-v2/orchestration/directivas/principios/PRINCIPIO-CAPVED.md" proposito: "Ciclo de vida de tareas" tokens: 800 - - path: "/home/isem/workspace-v1/orchestration/directivas/principios/PRINCIPIO-DOC-PRIMERO.md" + - path: "/home/isem/workspace-v2/orchestration/directivas/principios/PRINCIPIO-DOC-PRIMERO.md" proposito: "Documentacion antes de codigo" tokens: 500 - - path: "/home/isem/workspace-v1/orchestration/directivas/principios/PRINCIPIO-ANTI-DUPLICACION.md" + - path: "/home/isem/workspace-v2/orchestration/directivas/principios/PRINCIPIO-ANTI-DUPLICACION.md" proposito: "Verificar catalogo antes de crear" tokens: 600 - - path: "/home/isem/workspace-v1/orchestration/directivas/principios/PRINCIPIO-VALIDACION-OBLIGATORIA.md" + - path: "/home/isem/workspace-v2/orchestration/directivas/principios/PRINCIPIO-VALIDACION-OBLIGATORIA.md" proposito: "Build/lint deben pasar" tokens: 600 - - path: "/home/isem/workspace-v1/orchestration/directivas/principios/PRINCIPIO-ECONOMIA-TOKENS.md" + - path: "/home/isem/workspace-v2/orchestration/directivas/principios/PRINCIPIO-ECONOMIA-TOKENS.md" proposito: "Limites de contexto" tokens: 500 - - path: "/home/isem/workspace-v1/orchestration/directivas/principios/PRINCIPIO-NO-ASUMIR.md" + - path: "/home/isem/workspace-v2/orchestration/directivas/principios/PRINCIPIO-NO-ASUMIR.md" proposito: "Preguntar si falta informacion" tokens: 500 - - path: "/home/isem/workspace-v1/orchestration/referencias/ALIASES.yml" + - path: "/home/isem/workspace-v2/orchestration/referencias/ALIASES.yml" proposito: "Resolucion de @ALIAS" tokens: 400 @@ -129,16 +129,16 @@ contexto_por_nivel: tokens_estimados: 3500 obligatorio: true archivos: - - path: "/home/isem/workspace-v1/projects/michangarrito/orchestration/00-guidelines/CONTEXTO-PROYECTO.md" + - path: "/home/isem/workspace-v2/projects/michangarrito/orchestration/00-guidelines/CONTEXTO-PROYECTO.md" proposito: "Variables y configuracion del proyecto" tokens: 1500 - - path: "/home/isem/workspace-v1/projects/michangarrito/orchestration/PROXIMA-ACCION.md" + - path: "/home/isem/workspace-v2/projects/michangarrito/orchestration/PROXIMA-ACCION.md" proposito: "Estado actual y siguiente paso" tokens: 500 - - path: "/home/isem/workspace-v1/projects/michangarrito/orchestration/PROJECT-STATUS.md" + - path: "/home/isem/workspace-v2/projects/michangarrito/orchestration/PROJECT-STATUS.md" proposito: "Estado detallado del proyecto" tokens: 1000 - - path: "/home/isem/workspace-v1/projects/michangarrito/orchestration/PLAN-IMPLEMENTACION.md" + - path: "/home/isem/workspace-v2/projects/michangarrito/orchestration/PLAN-IMPLEMENTACION.md" proposito: "Plan de fases del proyecto" tokens: 500 @@ -147,27 +147,27 @@ contexto_por_nivel: tokens_estimados: 2500 archivos_por_operacion: CREAR: - - "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-CREAR.md" + - "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-CREAR.md" MODIFICAR: - - "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-MODIFICAR.md" + - "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-MODIFICAR.md" VALIDAR: - - "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-VALIDAR.md" + - "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-VALIDAR.md" DELEGAR: - - "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-DELEGACION.md" + - "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-DELEGACION.md" archivos_por_dominio: DDL: - - "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-DDL.md" - - "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/DATABASE_INVENTORY.yml" + - "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-DDL.md" + - "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/DATABASE_INVENTORY.yml" BACKEND: - - "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-BACKEND.md" - - "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/BACKEND_INVENTORY.yml" + - "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-BACKEND.md" + - "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/BACKEND_INVENTORY.yml" FRONTEND: - - "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-FRONTEND.md" - - "/home/isem/workspace-v1/projects/michangarrito/orchestration/inventarios/FRONTEND_INVENTORY.yml" + - "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-FRONTEND.md" + - "/home/isem/workspace-v2/projects/michangarrito/orchestration/inventarios/FRONTEND_INVENTORY.yml" MOBILE: - - "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-MOBILE.md" + - "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-MOBILE.md" MCP: - - "/home/isem/workspace-v1/orchestration/directivas/simco/SIMCO-MCP.md" + - "/home/isem/workspace-v2/orchestration/directivas/simco/SIMCO-MCP.md" L3_tarea: descripcion: "Contexto especifico de la tarea" @@ -382,7 +382,7 @@ validacion_tokens: herencia: tipo: "STANDALONE" hereda_de: - - "/home/isem/workspace-v1/orchestration/" + - "/home/isem/workspace-v2/orchestration/" usa_catalog: - payments (Stripe, Mercado Pago) - notifications (WhatsApp, Push) @@ -446,8 +446,8 @@ integraciones: busqueda_historico: habilitado: true ubicaciones: - - "/home/isem/workspace-v1/projects/michangarrito/orchestration/trazas/" - - "/home/isem/workspace-v1/projects/michangarrito/orchestration/analisis/" - - "/home/isem/workspace-v1/projects/michangarrito/orchestration/reportes/" - - "/home/isem/workspace-v1/orchestration/errores/REGISTRO-ERRORES.yml" - - "/home/isem/workspace-v1/shared/knowledge-base/lessons-learned/" + - "/home/isem/workspace-v2/projects/michangarrito/orchestration/trazas/" + - "/home/isem/workspace-v2/projects/michangarrito/orchestration/analisis/" + - "/home/isem/workspace-v2/projects/michangarrito/orchestration/reportes/" + - "/home/isem/workspace-v2/orchestration/errores/REGISTRO-ERRORES.yml" + - "/home/isem/workspace-v2/shared/knowledge-base/lessons-learned/" diff --git a/orchestration/inventarios/MASTER_INVENTORY.yml b/orchestration/inventarios/MASTER_INVENTORY.yml index 0704289bd..1f47ac69f 100644 --- a/orchestration/inventarios/MASTER_INVENTORY.yml +++ b/orchestration/inventarios/MASTER_INVENTORY.yml @@ -1,8 +1,8 @@ # MASTER INVENTORY - MiChangarrito -# Version: 2.2.0 +# Version: 4.0.0 # Ultima actualizacion: 2026-01-10 -# Sistema: SIMCO v3.8.0 + CAPVED -# Actualizado: Sincronizacion con desarrollo - inventarios completos +# Sistema: SIMCO v4.0.1 + CAPVED + SCRUM +# Actualizado: Integracion capacidades SaaS de template-saas metadata: proyecto: "michangarrito" @@ -290,12 +290,50 @@ modulos: nombre: "Marketplace Proveedores" estado: "pendiente" + - id: "MCH-029" + nombre: "Infraestructura SaaS Avanzada" + estado: "planificado" + story_points: 24 + sprint: "6-7" + descripcion: "Email, Storage, Redis, Webhooks, Rate Limiting" + integraciones: ["INT-010", "INT-011", "INT-013", "INT-014"] + + - id: "MCH-030" + nombre: "Auth Social OAuth 2.0" + estado: "planificado" + story_points: 8 + sprint: "8" + descripcion: "Login con Google y Apple" + integraciones: ["INT-012"] + + - id: "MCH-031" + nombre: "Auditoria Empresarial" + estado: "planificado" + story_points: 5 + sprint: "7" + descripcion: "Audit logs, retencion, compliance" + + - id: "MCH-032" + nombre: "Feature Flags por Plan" + estado: "planificado" + story_points: 5 + sprint: "8" + descripcion: "Toggles por plan/tenant" + + fase_8: + - id: "MCH-033" + nombre: "Onboarding Wizard" + estado: "planificado" + story_points: 3 + sprint: "9" + descripcion: "Guia interactiva de setup" + # ============================================================================ -# INTEGRACIONES EXTERNAS +# INFRAESTRUCTURA BASE # ============================================================================ -integraciones: - - nombre: "PostgreSQL 15" +infraestructura: + - nombre: "PostgreSQL 16" estado: "activo" puerto: 5432 @@ -304,23 +342,135 @@ integraciones: puerto: 6379 db: 8 - - nombre: "WhatsApp Business (Meta)" - estado: "listo" +# ============================================================================ +# INTEGRACIONES EXTERNAS (SIMCO v4.0.0) +# ============================================================================ + +integraciones: + - id: "INT-001" + nombre: "WhatsApp Meta Business" + categoria: "Mensajeria" + estado: "activo" + multi_tenant: true + modulo_relacionado: "MCH-011" + spec: "docs/02-integraciones/INT-001-whatsapp-meta.md" notas: "Requiere cuenta Business verificada" - - nombre: "Stripe" - estado: "integrado" + - id: "INT-002" + nombre: "Stripe" + categoria: "Pagos" + estado: "activo" + multi_tenant: false + modulo_relacionado: "MCH-020" + spec: "docs/02-integraciones/INT-002-stripe.md" notas: "SDK configurado, requiere API keys produccion" - - nombre: "OpenRouter/OpenAI" - estado: "multi-tenant" + - id: "INT-003" + nombre: "OpenRouter LLM" + categoria: "AI/LLM" + estado: "activo" + multi_tenant: true + modulo_relacionado: "MCH-012" + spec: "docs/02-integraciones/INT-003-openrouter.md" notas: "Soporte credenciales por tenant + fallback plataforma" - - nombre: "MercadoPago" + - id: "INT-004" + nombre: "MercadoPago" + categoria: "Pagos" estado: "pendiente" + multi_tenant: true + modulo_relacionado: "MCH-005" + spec: "docs/02-integraciones/INT-004-mercadopago.md" - - nombre: "Clip" + - id: "INT-005" + nombre: "Clip Mexico" + categoria: "Pagos" + estado: "mock" + multi_tenant: true + modulo_relacionado: "MCH-005" + spec: "docs/02-integraciones/INT-005-clip.md" + + - id: "INT-006" + nombre: "CoDi/SPEI Banxico" + categoria: "Pagos" + estado: "mock" + multi_tenant: true + modulo_relacionado: "MCH-024" + spec: "docs/02-integraciones/INT-006-codi-banxico.md" + + - id: "INT-007" + nombre: "Firebase FCM" + categoria: "Notificaciones" estado: "pendiente" + multi_tenant: true + modulo_relacionado: "MCH-017" + spec: "docs/02-integraciones/INT-007-firebase-fcm.md" + + - id: "INT-008" + nombre: "Google Cloud Vision" + categoria: "AI/ML" + estado: "pendiente" + multi_tenant: true + modulo_relacionado: "MCH-009" + spec: "docs/02-integraciones/INT-008-google-vision.md" + + - id: "INT-009" + nombre: "OpenAI Whisper" + categoria: "AI/ML" + estado: "pendiente" + multi_tenant: true + modulo_relacionado: "MCH-011" + spec: "docs/02-integraciones/INT-009-whisper.md" + + # === NUEVAS INTEGRACIONES SAAS (v4.0.0) === + + - id: "INT-010" + nombre: "Email Multi-Provider" + categoria: "Notificaciones" + estado: "planificado" + multi_tenant: true + modulo_relacionado: "MCH-029" + spec: "docs/02-integraciones/INT-010-email-providers.md" + proveedores: ["SendGrid", "AWS SES", "SMTP"] + notas: "Fallback automatico entre proveedores" + + - id: "INT-011" + nombre: "Storage Cloud" + categoria: "Almacenamiento" + estado: "planificado" + multi_tenant: true + modulo_relacionado: "MCH-029" + spec: "docs/02-integraciones/INT-011-storage-cloud.md" + proveedores: ["AWS S3", "Cloudflare R2", "MinIO"] + notas: "URLs firmadas, limites por plan" + + - id: "INT-012" + nombre: "OAuth Social" + categoria: "Autenticacion" + estado: "planificado" + multi_tenant: true + modulo_relacionado: "MCH-030" + spec: "docs/02-integraciones/INT-012-oauth-social.md" + proveedores: ["Google", "Apple"] + notas: "Passport.js strategies" + + - id: "INT-013" + nombre: "Redis Cache" + categoria: "Infraestructura" + estado: "planificado" + multi_tenant: true + modulo_relacionado: "MCH-029" + spec: "docs/02-integraciones/INT-013-redis-cache.md" + notas: "Cache, queues (BullMQ), rate limiting" + + - id: "INT-014" + nombre: "Webhooks Outbound" + categoria: "Eventos" + estado: "planificado" + multi_tenant: true + modulo_relacionado: "MCH-029" + spec: "docs/02-integraciones/INT-014-webhooks-outbound.md" + notas: "Firma HMAC, reintentos exponenciales" # ============================================================================ # DOCUMENTACION @@ -376,3 +526,45 @@ notas: - "Integraciones WhatsApp y LLM soportan credenciales por tenant" - "75 productos predefinidos para onboarding rapido" - "Sistema de tokens IA con planes y recargas" + +# ============================================================================ +# TRAZABILIDAD (SIMCO v4.0.0) +# ============================================================================ + +trazabilidad: + archivo: "docs/04-modelado/trazabilidad/TRACEABILITY-MASTER.yml" + tipo: "YAML" + ultima_actualizacion: "2026-01-10" + modulos_trazados: 33 + integraciones_trazadas: 14 + cobertura_trazabilidad: 100 + metricas: + modulos_completados: 22 + modulos_pendientes: 6 + modulos_planificados: 5 + integraciones_activas: 3 + integraciones_mock: 2 + integraciones_pendientes: 4 + integraciones_planificadas: 5 + saas_integration: + fecha: "2026-01-10" + epicas_nuevas: ["MCH-029", "MCH-030", "MCH-031", "MCH-032", "MCH-033"] + integraciones_nuevas: ["INT-010", "INT-011", "INT-012", "INT-013", "INT-014"] + adrs_nuevos: 8 + story_points_total: 45 + +# ============================================================================ +# METADATA MIGRACION +# ============================================================================ + +migracion: + version_anterior: "SIMCO v3.8.0" + version_actual: "SIMCO v4.0.0" + fecha_migracion: "2026-01-10" + cambios_aplicados: + - "Reestructuracion integraciones con IDs INT-*" + - "Separacion infraestructura de integraciones" + - "Agregada seccion trazabilidad" + - "Agregados modulos relacionados a integraciones" + - "Agregadas referencias a specs de integracion" + actualizado_por: "Claude Code (Migracion SIMCO v4.0.0)"