trading-platform/docs/90-transversal/inventarios/MIGRACION-SUPABASE-EXPRESS.md
rckrdmrd a7cca885f0 feat: Major platform documentation and architecture updates
Changes include:
- Updated architecture documentation
- Enhanced module definitions (OQI-001 to OQI-008)
- ML integration documentation updates
- Trading strategies documentation
- Orchestration and inventory updates
- Docker configuration updates

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 05:33:35 -06:00

711 lines
23 KiB
Markdown

---
id: "MIGRACION-SUPABASE-EXPRESS"
title: "Supabase a Express Backend"
type: "Documentation"
project: "trading-platform"
version: "1.0.0"
updated_date: "2026-01-04"
---
# Plan de Migracion: Supabase a Express Backend
**Ultima actualizacion:** 2025-12-05
**Proyecto origen:** stc-platform-web (Supabase)
**Proyecto destino:** trading-platform (Express + pg + PostgreSQL)
**Patrones base:** `shared/catalog/auth/` *(inspiración arquitectónica de patrones auth y estructura)*
---
## Resumen Ejecutivo
Este documento detalla el plan de migracion del frontend existente en `stc-platform-web` que usa Supabase como backend hacia una arquitectura con Express.js como backend propio.
**Estado Actual del Backend:**
El backend de trading-platform (`/home/isem/workspace/projects/trading-platform/apps/backend/`) ya tiene implementacion funcional con:
- Express.js + TypeScript
- Autenticacion JWT completa (registro, login, refresh tokens, sesiones)
- OAuth multi-proveedor (Google, Facebook, Twitter, Apple, GitHub)
- 2FA con TOTP (speakeasy)
- Autenticacion por telefono (Twilio SMS/WhatsApp)
- PostgreSQL con pg driver nativo
- Estructura modular escalable
- Guards, interceptors, filters adaptados de patrones NestJS
**Patrones de Gamilit Adaptados:**
Se tomaron patrones del proyecto Gamilit y se adaptaron para Express:
- Constantes compartidas (DB_SCHEMAS, enums)
- Estructura de servicios modulares
- Manejo de errores unificado
- Guards de autenticacion y autorizacion
---
## 1. Arquitectura Objetivo
### Antes (Supabase)
```
Frontend (React) ──> Supabase Client ──> Supabase (Auth + DB + Storage)
├── Auth (Built-in)
├── PostgreSQL
├── Realtime
└── Storage
```
### Despues (Express Backend Propio)
```
Frontend (React) ──> API Client ──> Express Backend ──> PostgreSQL
│ │
│ ├── JWT Auth (jsonwebtoken + Passport)
│ ├── pg driver (Multi-schema)
│ ├── Redis (Sessions/Cache)
│ ├── S3/MinIO (Storage)
│ └── Socket.io (Realtime)
└── WebSocket ──> Realtime Updates
```
---
## 2. Estructura Backend Actual (Express)
```
apps/backend/src/
├── index.ts # Entry point Express server
├── config/
│ └── index.ts # Configuracion centralizada
├── core/
│ ├── decorators/
│ ├── filters/
│ │ ├── http-exception.filter.ts # Excepciones HTTP unificadas
│ │ └── index.ts
│ ├── guards/
│ │ ├── auth.guard.ts # Guards de autenticacion
│ │ └── index.ts
│ ├── interceptors/
│ │ ├── transform-response.interceptor.ts
│ │ └── index.ts
│ └── middleware/
│ ├── auth.middleware.ts # Middleware JWT
│ ├── error-handler.ts # Manejador de errores
│ ├── not-found.ts # 404 handler
│ └── rate-limiter.ts # Rate limiting
├── modules/
│ ├── auth/
│ │ ├── auth.routes.ts # Rutas de auth
│ │ ├── controllers/
│ │ │ └── auth.controller.ts # Controller principal
│ │ ├── services/
│ │ │ ├── email.service.ts # Auth email/password
│ │ │ ├── oauth.service.ts # OAuth multi-provider
│ │ │ ├── phone.service.ts # SMS/WhatsApp (Twilio)
│ │ │ ├── token.service.ts # JWT tokens
│ │ │ └── twofa.service.ts # 2FA TOTP
│ │ ├── strategies/ # Passport strategies
│ │ ├── types/
│ │ │ └── auth.types.ts # Tipos de auth
│ │ └── validators/
│ │ └── auth.validators.ts # Validacion Zod
│ ├── users/
│ │ └── users.routes.ts
│ ├── education/
│ │ └── education.routes.ts
│ ├── trading/
│ │ └── trading.routes.ts
│ ├── investment/
│ │ └── investment.routes.ts
│ ├── payments/
│ │ └── payments.routes.ts
│ └── admin/
│ └── admin.routes.ts
└── shared/
├── constants/
│ ├── database.constants.ts # DB_SCHEMAS, DB_TABLES
│ ├── enums.constants.ts # UserStatusEnum, etc
│ ├── routes.constants.ts # API_ROUTES
│ └── index.ts
├── database/
│ └── index.ts # Pool de conexiones pg
├── dto/
├── types/
│ ├── common.types.ts # ApiResponse, PaginatedResult
│ └── index.ts
└── utils/
└── logger.ts # Winston logger
```
### Servicios de Auth Implementados
| Servicio | Archivo | Funcionalidades |
|----------|---------|-----------------|
| **EmailService** | `email.service.ts` | Registro, login, verificacion email, reset password |
| **OAuthService** | `oauth.service.ts` | Google, Facebook, Twitter, Apple, GitHub |
| **PhoneService** | `phone.service.ts` | SMS OTP, WhatsApp OTP (Twilio) |
| **TokenService** | `token.service.ts` | JWT access/refresh, sesiones |
| **TwoFAService** | `twofa.service.ts` | TOTP setup, verify, backup codes |
---
## 3. Estructura Backend Gamilit (Referencia)
```
gamilit/apps/backend/src/
├── main.ts # Bootstrap con Swagger, CORS, ValidationPipe
├── app.module.ts # Root module con multi-schema TypeORM
├── config/
│ ├── app.config.ts # Configuracion de aplicacion
│ ├── database.config.ts # Configuracion PostgreSQL
│ ├── jwt.config.ts # Configuracion JWT
│ └── env.config.ts # Variables de entorno
├── modules/
│ ├── auth/ # ✅ REUTILIZAR PATRON
│ │ ├── auth.module.ts # Module definition
│ │ ├── controllers/
│ │ │ ├── auth.controller.ts # Login, Register, Refresh
│ │ │ ├── password.controller.ts # Forgot/Reset password
│ │ │ └── users.controller.ts # User CRUD
│ │ ├── services/
│ │ │ ├── auth.service.ts # Core auth logic (25KB)
│ │ │ ├── session-management.service.ts
│ │ │ ├── security.service.ts
│ │ │ ├── password-recovery.service.ts
│ │ │ └── email-verification.service.ts
│ │ ├── entities/
│ │ │ ├── user.entity.ts
│ │ │ ├── profile.entity.ts
│ │ │ ├── user-session.entity.ts
│ │ │ ├── auth-attempt.entity.ts
│ │ │ ├── auth-provider.entity.ts # OAuth providers
│ │ │ └── ...
│ │ ├── strategies/
│ │ │ └── jwt.strategy.ts # Passport JWT strategy
│ │ ├── guards/
│ │ │ └── jwt-auth.guard.ts
│ │ ├── dto/
│ │ │ ├── register-user.dto.ts
│ │ │ ├── login.dto.ts
│ │ │ └── ...
│ │ └── decorators/
│ │ └── current-user.decorator.ts
│ ├── educational/ # → Adaptar para courses
│ ├── progress/ # → Adaptar para course progress
│ ├── gamification/ # → Adaptar para trading stats
│ ├── notifications/ # ✅ REUTILIZAR
│ ├── websocket/ # ✅ REUTILIZAR para signals realtime
│ └── health/ # ✅ REUTILIZAR
└── shared/
├── constants/
│ ├── database.constants.ts # DB_SCHEMAS, DB_TABLES
│ ├── routes.constants.ts # API_PREFIX, API_VERSION
│ └── enums.constants.ts # Shared enums
├── decorators/
├── dto/
├── filters/
│ └── http-exception.filter.ts # Global exception handler
├── guards/
├── interceptors/
│ ├── transform-response.interceptor.ts
│ └── rls.interceptor.ts # Row Level Security
├── middleware/
├── pipes/
└── utils/
```
### Archivos Clave a Copiar/Adaptar
| Archivo Gamilit | Destino Trading Platform | Adaptaciones |
|-----------------|-------------------------|--------------|
| `main.ts` | `apps/backend/src/main.ts` | Cambiar nombre app, tags Swagger |
| `app.module.ts` | `apps/backend/src/app.module.ts` | Cambiar schemas y modules |
| `auth/` (completo) | `modules/auth/` | Agregar OAuth providers adicionales |
| `shared/` (completo) | `shared/` | Adaptar constantes y enums |
| `websocket/` | `modules/websocket/` | Adaptar eventos para trading signals |
| `health/` | `modules/health/` | Sin cambios |
---
## 4. Mapeo de Supabase a Express
### 4.1 Autenticacion
| Supabase Method | Express Equivalent | Endpoint |
|-----------------|-------------------|----------|
| `supabase.auth.signInWithPassword()` | POST `/api/v1/auth/login` | JWT + Refresh Token |
| `supabase.auth.signUp()` | POST `/api/v1/auth/register` | Validacion + Email verification |
| `supabase.auth.signInWithOAuth()` | GET `/api/v1/auth/oauth/:provider/url` | Passport OAuth strategies |
| `supabase.auth.signOut()` | POST `/api/v1/auth/logout` | Invalidar tokens |
| `supabase.auth.resetPasswordForEmail()` | POST `/api/v1/auth/forgot-password` | Nodemailer |
| `supabase.auth.updateUser()` | PATCH `/api/v1/auth/password` | Validacion + bcrypt |
| `supabase.auth.verifyOtp()` | POST `/api/v1/auth/verify-email` | Token validation |
| `supabase.auth.refreshSession()` | POST `/api/v1/auth/refresh` | JWT refresh rotation |
| `supabase.auth.getUser()` | GET `/api/v1/auth/me` | JWT middleware |
| `supabase.auth.getSession()` | (Client-side localStorage) | JWT decode |
| `supabase.auth.onAuthStateChange()` | (Client-side event) | Estado local |
### 4.2 Queries de Base de Datos
| Supabase Query | Express/TypeORM Equivalent |
|----------------|---------------------------|
| `.from('table').select()` | `repository.find()` |
| `.from('table').select('*', { count: 'exact' })` | `repository.findAndCount()` |
| `.from('table').insert()` | `repository.save()` |
| `.from('table').update()` | `repository.update()` |
| `.from('table').delete()` | `repository.delete()` |
| `.eq('column', value)` | `{ where: { column: value } }` |
| `.in('column', [values])` | `{ where: { column: In([values]) } }` |
| `.order('column', { ascending: true })` | `{ order: { column: 'ASC' } }` |
| `.range(0, 10)` | `{ skip: 0, take: 10 }` |
| `.single()` | `repository.findOne()` |
| `.rpc('function_name', params)` | Service method / Raw query |
### 4.3 Realtime
| Supabase Realtime | Express Equivalent |
|-------------------|-------------------|
| `.channel()` | Socket.io room |
| `.on('postgres_changes', ...)` | Socket.io events + DB triggers |
| `.subscribe()` | `socket.join(room)` |
| `supabase.removeChannel()` | `socket.leave(room)` |
### 4.4 Storage
| Supabase Storage | Express Equivalent |
|------------------|-------------------|
| `supabase.storage.from('bucket').upload()` | Multer + S3/MinIO |
| `supabase.storage.from('bucket').download()` | S3 signed URLs |
| `supabase.storage.from('bucket').getPublicUrl()` | CDN URL generation |
---
## 5. Servicios a Migrar (Frontend)
### 5.1 AuthService Migration
**Archivo origen:** `src/features/auth/services/authService.ts`
**Archivo destino:** `apps/frontend/src/services/auth.service.ts`
```typescript
// ANTES (Supabase)
async signInWithEmail(email: string, password: string) {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password
});
// ...
}
// DESPUES (Express API)
async signInWithEmail(email: string, password: string) {
const response = await api.post('/auth/login', { email, password });
if (response.data.accessToken) {
tokenService.setTokens(
response.data.accessToken,
response.data.refreshToken
);
}
return response.data;
}
```
### 5.2 CourseService Migration
**Archivo origen:** `src/features/courses/services/courseService.ts`
**Archivo destino:** `apps/frontend/src/services/course.service.ts`
```typescript
// ANTES (Supabase)
async getCourses(filters, sort, page, perPage) {
let query = supabase
.from('education.courses')
.select(`*, instructor:profiles!instructor_id(...)`)
// ...
}
// DESPUES (Express API)
async getCourses(filters, sort, page, perPage) {
const params = new URLSearchParams({
page: String(page),
per_page: String(perPage),
sort_field: sort.field,
sort_dir: sort.direction,
...filters
});
return api.get(`/courses?${params}`);
}
```
### 5.3 SignalService Migration
**Archivo origen:** `src/features/trading/services/signalService.ts`
**Cambios principales:**
1. Queries → API calls
2. Realtime subscriptions → WebSocket
```typescript
// DESPUES (WebSocket)
subscribeToSignals(botIds: string[], callback: (signal) => void) {
socket.emit('subscribe:signals', { botIds });
socket.on('signal:new', callback);
return () => socket.off('signal:new', callback);
}
```
---
## 6. Estructura de Carpetas Destino
> **Nota:** La estructura del backend ya está implementada. Ver Sección 2 para el estado actual.
> La siguiente es la estructura objetivo del frontend.
### 6.1 Frontend (React)
```
apps/frontend/src/
├── services/
│ ├── api.ts # Axios instance con interceptores
│ ├── auth.service.ts # Migrado de authService.ts
│ ├── course.service.ts # Migrado de courseService.ts
│ ├── trading.service.ts # Consolidado de trading services
│ ├── payment.service.ts # Migrado de stripeService.ts
│ └── socket.service.ts # NUEVO: WebSocket client
├── stores/
│ └── auth.store.ts # Migrar con cambios de API
├── hooks/
│ ├── useAuth.ts # Mantener logica, cambiar imports
│ └── ...
└── lib/
└── socket.ts # Socket.io client config
```
---
## 7. Backend Endpoints a Implementar
### 7.1 Auth Module (Prioridad 1) - ✅ IMPLEMENTADO
```
POST /api/v1/auth/register
POST /api/v1/auth/login
POST /api/v1/auth/logout
POST /api/v1/auth/refresh
GET /api/v1/auth/me
GET /api/v1/auth/oauth/:provider/url
POST /api/v1/auth/oauth/:provider
DELETE /api/v1/auth/oauth/:provider
POST /api/v1/auth/phone/send
POST /api/v1/auth/phone/verify
POST /api/v1/auth/2fa/setup
POST /api/v1/auth/2fa/enable
POST /api/v1/auth/2fa/verify
POST /api/v1/auth/2fa/disable
POST /api/v1/auth/forgot-password
POST /api/v1/auth/reset-password
POST /api/v1/auth/verify-email
POST /api/v1/auth/resend-verification
GET /api/v1/auth/sessions
DELETE /api/v1/auth/sessions/:id
DELETE /api/v1/auth/sessions
```
### 7.2 Users Module
```
GET /api/v1/users
GET /api/v1/users/:id
PATCH /api/v1/users/:id
DELETE /api/v1/users/:id
GET /api/v1/users/:id/profile
PATCH /api/v1/users/:id/profile
POST /api/v1/users/:id/avatar
```
### 7.3 Courses Module (Prioridad 2)
```
GET /api/v1/courses
GET /api/v1/courses/featured
GET /api/v1/courses/search
GET /api/v1/courses/:id
GET /api/v1/courses/:slug
POST /api/v1/courses
PATCH /api/v1/courses/:id
DELETE /api/v1/courses/:id
POST /api/v1/courses/:id/publish
POST /api/v1/courses/:id/archive
GET /api/v1/courses/:id/modules
POST /api/v1/courses/:id/modules
GET /api/v1/courses/:id/modules/:mid/lessons
POST /api/v1/courses/:id/enroll
GET /api/v1/courses/:id/progress
POST /api/v1/courses/:id/lessons/:lid/complete
GET /api/v1/courses/:id/certificate
```
### 7.4 Trading Module (Prioridad 3)
```
GET /api/v1/trading/bots
GET /api/v1/trading/bots/featured
GET /api/v1/trading/bots/:id
POST /api/v1/trading/bots/:id/subscribe
DELETE /api/v1/trading/bots/:id/subscribe
GET /api/v1/trading/bots/:id/reviews
POST /api/v1/trading/bots/:id/reviews
GET /api/v1/trading/signals
GET /api/v1/trading/signals/:id
POST /api/v1/trading/signals/:id/execute
GET /api/v1/trading/portfolio
GET /api/v1/trading/positions
GET /api/v1/trading/history
```
### 7.5 Payments Module (Prioridad 4)
```
GET /api/v1/payments
GET /api/v1/payments/:id
POST /api/v1/payments/checkout
GET /api/v1/payments/credits
POST /api/v1/payments/credits/purchase
GET /api/v1/subscriptions
POST /api/v1/subscriptions
DELETE /api/v1/subscriptions/:id
GET /api/v1/invoices
GET /api/v1/invoices/:id/download
POST /api/v1/webhooks/stripe
```
---
## 8. API Client Configuration
### 8.1 Axios Instance
```typescript
// apps/frontend/src/services/api.ts
import axios from 'axios';
import { tokenService } from './token.service';
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3001/api/v1',
headers: {
'Content-Type': 'application/json'
}
});
// Request interceptor - Add auth header
api.interceptors.request.use((config) => {
const token = tokenService.getAccessToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Response interceptor - Handle 401, refresh token
api.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
try {
const refreshToken = tokenService.getRefreshToken();
const response = await axios.post('/auth/refresh', { refreshToken });
tokenService.setTokens(
response.data.accessToken,
response.data.refreshToken
);
originalRequest.headers.Authorization = `Bearer ${response.data.accessToken}`;
return api(originalRequest);
} catch (refreshError) {
tokenService.clearTokens();
window.location.href = '/login';
return Promise.reject(refreshError);
}
}
return Promise.reject(error);
}
);
export { api };
```
### 8.2 Socket.io Client
```typescript
// apps/frontend/src/lib/socket.ts
import { io, Socket } from 'socket.io-client';
import { tokenService } from '@/services/token.service';
let socket: Socket | null = null;
export function getSocket(): Socket {
if (!socket) {
socket = io(import.meta.env.VITE_WS_URL || 'ws://localhost:3001', {
auth: {
token: tokenService.getAccessToken()
},
autoConnect: false
});
}
return socket;
}
export function connectSocket(): void {
const s = getSocket();
if (!s.connected) {
s.connect();
}
}
export function disconnectSocket(): void {
if (socket?.connected) {
socket.disconnect();
}
}
```
---
## 9. Orden de Migracion
### Fase 1: Autenticacion (Semana 1-2)
1. Backend: Implementar auth endpoints
2. Frontend: Migrar authService.ts
3. Frontend: Migrar tokenService.ts
4. Frontend: Actualizar auth.store.ts
5. Frontend: Actualizar hooks de auth
6. Testing: Flujos completos de auth
### Fase 2: Usuarios y Perfiles (Semana 2)
1. Backend: Implementar users endpoints
2. Frontend: Crear user.service.ts
3. Frontend: Actualizar profileService.ts
4. Testing: CRUD de usuarios
### Fase 3: Cursos/Educacion (Semana 3-4)
1. Backend: Implementar courses endpoints
2. Frontend: Migrar courseService.ts
3. Frontend: Migrar enrollment/progress services
4. Frontend: Migrar quiz/certificate services
5. Testing: Flujos de cursos
### Fase 4: Trading (Semana 4-5)
1. Backend: Implementar trading endpoints
2. Backend: Implementar WebSocket para signals
3. Frontend: Migrar signalService.ts
4. Frontend: Migrar botService.ts
5. Frontend: Crear socket.service.ts
6. Testing: Trading flows + realtime
### Fase 5: Pagos (Semana 5-6)
1. Backend: Implementar payment endpoints
2. Backend: Stripe webhooks
3. Frontend: Migrar stripeService.ts
4. Frontend: Migrar webhookService.ts
5. Testing: Payment flows
---
## 10. Consideraciones Especiales
### 10.1 Manejo de Errores
```typescript
// Error response standard
interface ApiError {
code: string;
message: string;
field?: string;
details?: any;
}
// En frontend, mapear a mensajes amigables
const errorMessages: Record<string, string> = {
'AUTH_INVALID_CREDENTIALS': 'Email o contrasena incorrectos',
'AUTH_EMAIL_NOT_VERIFIED': 'Debes verificar tu email',
'AUTH_TOKEN_EXPIRED': 'Tu sesion ha expirado',
// ...
};
```
### 10.2 Rate Limiting
El backend implementara rate limiting:
- Auth endpoints: 5 requests/15min
- General API: 100 requests/min
- Heavy endpoints: 10 requests/min
### 10.3 Cache
- Redis para sessions y tokens
- Redis para cache de queries frecuentes
- Frontend: TanStack Query cache
### 10.4 Variables de Entorno Frontend
```env
# .env
VITE_API_URL=http://localhost:3001/api/v1
VITE_WS_URL=ws://localhost:3001
VITE_STRIPE_PUBLIC_KEY=pk_test_xxx
VITE_GOOGLE_CLIENT_ID=xxx
VITE_FACEBOOK_APP_ID=xxx
```
---
## 11. Testing Strategy
### 11.1 Backend Tests
- Unit tests para services
- Integration tests para endpoints
- E2E tests para flujos criticos
### 11.2 Frontend Tests
- Unit tests para services migrados
- Integration tests con MSW (mocks)
- E2E tests con Playwright
---
## 12. Rollback Plan
En caso de problemas:
1. Mantener Supabase operativo durante migracion
2. Feature flags para cambiar entre backends
3. Logs detallados para debugging
4. Snapshots de base de datos
---
## 13. Referencias
### Documentacion Interna
- [ADR-001: Stack Tecnologico](../../97-adr/ADR-001-stack-tecnologico.md)
- [ADR-003: Autenticacion Multi-proveedor](../../97-adr/ADR-003-autenticacion-multiproveedor.md)
- [INVENTARIO-STC-PLATFORM-WEB.md](./INVENTARIO-STC-PLATFORM-WEB.md)
- [OQI-001: Fundamentos y Auth](../../01-fase-mvp/OQI-001-fundamentos-auth/_MAP.md)
### Catálogo de Patrones Reutilizables
> **Nota:** Los patrones de autenticación fueron inspirados en arquitecturas previas y están
> documentados en el catálogo central para reutilización.
- **Catálogo Auth:** `shared/catalog/auth/` *(patrones de autenticación JWT, OAuth, 2FA)*
- **Catálogo Session:** `shared/catalog/session-management/` *(gestión de sesiones)*
- **Patrones Backend:** `shared/catalog/backend-patterns/` *(interceptors, filters, guards)*
### Documentacion Externa
- [Express.js Documentation](https://expressjs.com/)
- [node-postgres (pg) Documentation](https://node-postgres.com/)
- [Passport.js Strategies](http://www.passportjs.org/)
- [jsonwebtoken Documentation](https://github.com/auth0/node-jsonwebtoken)
- [Zod Validation](https://zod.dev/)