Configuración de Producción
Esta guía proporciona las mejores prácticas y configuraciones esenciales para desplegar Fox Framework en entornos de producción de manera segura, escalable y confiable.
Lista de Verificación Pre-Producción
✅ Seguridad
- Variables de entorno configuradas correctamente
- Secretos almacenados de forma segura
- HTTPS/TLS habilitado
- CORS configurado apropiadamente
- Rate limiting implementado
- Validación de entrada robusta
- Headers de seguridad configurados
- Logs sanitizados (sin secretos)
✅ Rendimiento
- Caché configurado y optimizado
- Pool de conexiones de BD ajustado
- Assets estáticos optimizados
- Compresión habilitada
- CDN configurado
- Queries de BD optimizadas
- Memory leaks auditados
✅ Observabilidad
- Logging estructurado configurado
- Métricas de aplicación implementadas
- Health checks funcionando
- Alertas configuradas
- Dashboards desplegados
- Error tracking activo
✅ Reliability
- Backups automatizados
- Disaster recovery plan
- Circuit breakers implementados
- Graceful shutdown configurado
- Auto-scaling configurado
- Load balancing activo
Configuración del Entorno
Variables de Entorno de Producción
# Aplicación
NODE_ENV=production
APP_NAME=fox-framework-app
APP_VERSION=1.0.0
PORT=3000
HOST=0.0.0.0
# Base de datos
DATABASE_URL=postgresql://user:password@db-cluster:5432/production_db
DATABASE_SSL=true
DATABASE_POOL_MIN=2
DATABASE_POOL_MAX=20
DATABASE_POOL_IDLE_TIMEOUT=10000
DATABASE_POOL_ACQUIRE_TIMEOUT=10000
# Cache/Redis
REDIS_URL=redis://redis-cluster:6379
REDIS_PASSWORD=secure_redis_password
REDIS_MAX_RETRIES=3
REDIS_RETRY_DELAY=100
# Seguridad
JWT_SECRET=ultra-secure-jwt-secret-min-32-chars
JWT_EXPIRES_IN=7d
SESSION_SECRET=ultra-secure-session-secret-min-32-chars
BCRYPT_ROUNDS=12
# CORS
CORS_ORIGIN=https://yourdomain.com,https://www.yourdomain.com
CORS_CREDENTIALS=true
# Rate Limiting
RATE_LIMIT_WINDOW=15
RATE_LIMIT_MAX=100
# SSL/TLS
SSL_CERT_PATH=/etc/ssl/certs/app.crt
SSL_KEY_PATH=/etc/ssl/private/app.key
# Logging
LOG_LEVEL=info
LOG_FORMAT=json
LOG_FILE=/var/log/app/app.log
LOG_MAX_SIZE=100m
LOG_MAX_FILES=10
# Monitoring
PROMETHEUS_ENABLED=true
PROMETHEUS_PORT=9090
HEALTH_CHECK_PORT=8080
# Email
EMAIL_PROVIDER=sendgrid
EMAIL_API_KEY=your_sendgrid_api_key
EMAIL_FROM=noreply@yourdomain.com
# Storage
STORAGE_PROVIDER=aws
AWS_ACCESS_KEY_ID=your_access_key
AWS_SECRET_ACCESS_KEY=your_secret_key
AWS_REGION=us-east-1
AWS_S3_BUCKET=your-production-bucket
# External APIs
PAYMENT_GATEWAY_URL=https://api.payment-provider.com
PAYMENT_GATEWAY_API_KEY=your_payment_api_keyConfiguración Principal de Producción
src/config/production.ts
import { FoxConfig } from '@foxframework/core';
import { SecurityConfig } from '@foxframework/security';
import { DatabaseConfig } from '@foxframework/database';
export const productionConfig: FoxConfig = {
// Servidor
server: {
port: parseInt(process.env.PORT || '3000'),
host: process.env.HOST || '0.0.0.0',
trustProxy: true,
keepAliveTimeout: 65000,
headersTimeout: 66000,
maxHeaderSize: 8192,
// SSL/TLS
ssl: {
enabled: true,
cert: process.env.SSL_CERT_PATH,
key: process.env.SSL_KEY_PATH,
redirectHttp: true
}
},
// Base de datos
database: {
url: process.env.DATABASE_URL!,
ssl: {
rejectUnauthorized: true,
require: true
},
pool: {
min: parseInt(process.env.DATABASE_POOL_MIN || '2'),
max: parseInt(process.env.DATABASE_POOL_MAX || '20'),
idleTimeoutMillis: parseInt(process.env.DATABASE_POOL_IDLE_TIMEOUT || '10000'),
acquireTimeoutMillis: parseInt(process.env.DATABASE_POOL_ACQUIRE_TIMEOUT || '10000')
},
logging: false, // Deshabilitado en producción por rendimiento
retryAttempts: 3,
retryDelay: 3000
},
// Cache
cache: {
type: 'redis',
url: process.env.REDIS_URL!,
password: process.env.REDIS_PASSWORD,
ttl: 3600,
maxMemory: '512mb',
evictionPolicy: 'allkeys-lru',
retryAttempts: parseInt(process.env.REDIS_MAX_RETRIES || '3'),
retryDelay: parseInt(process.env.REDIS_RETRY_DELAY || '100')
},
// Seguridad
security: {
cors: {
origin: process.env.CORS_ORIGIN?.split(',') || false,
credentials: process.env.CORS_CREDENTIALS === 'true',
maxAge: 86400 // 24 horas
},
helmet: {
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'", "https://fonts.googleapis.com"],
fontSrc: ["'self'", "https://fonts.gstatic.com"],
scriptSrc: ["'self'", "'unsafe-inline'"],
imgSrc: ["'self'", "data:", "https:"],
connectSrc: ["'self'", "https://api.yourdomain.com"]
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
},
rateLimit: {
windowMs: parseInt(process.env.RATE_LIMIT_WINDOW || '15') * 60 * 1000,
max: parseInt(process.env.RATE_LIMIT_MAX || '100'),
standardHeaders: true,
legacyHeaders: false
},
jwt: {
secret: process.env.JWT_SECRET!,
expiresIn: process.env.JWT_EXPIRES_IN || '7d',
algorithm: 'HS256'
}
},
// Logging
logging: {
level: process.env.LOG_LEVEL || 'info',
format: process.env.LOG_FORMAT as 'json' | 'simple' || 'json',
transports: [
{
type: 'console',
silent: false
},
{
type: 'file',
filename: process.env.LOG_FILE || '/var/log/app/app.log',
maxSize: process.env.LOG_MAX_SIZE || '100m',
maxFiles: parseInt(process.env.LOG_MAX_FILES || '10')
}
]
},
// Monitoring
monitoring: {
enabled: true,
prometheus: {
enabled: process.env.PROMETHEUS_ENABLED === 'true',
port: parseInt(process.env.PROMETHEUS_PORT || '9090'),
path: '/metrics'
},
health: {
enabled: true,
port: parseInt(process.env.HEALTH_CHECK_PORT || '8080'),
path: '/health'
}
}
};Optimización de Base de Datos
Configuración de Pool de Conexiones
export const databaseConfig: DatabaseConfig = {
// Pool de conexiones optimizado para producción
pool: {
min: 2, // Mínimo de conexiones siempre activas
max: 20, // Máximo basado en la capacidad del servidor DB
idleTimeoutMillis: 10000, // 10 segundos
acquireTimeoutMillis: 10000, // 10 segundos
createTimeoutMillis: 3000, // 3 segundos
destroyTimeoutMillis: 5000, // 5 segundos
reapIntervalMillis: 1000, // 1 segundo
createRetryIntervalMillis: 100
},
// Configuración de performance
performance: {
// Preparar statements para mejor performance
prepareStatements: true,
// Query timeout
queryTimeout: 30000,
// Statement timeout
statementTimeout: 10000,
// Auto-vacuum configurado
autoVacuum: true
},
// Monitoring de queries
monitoring: {
// Log queries lentas
logSlowQueries: true,
slowQueryThreshold: 1000, // 1 segundo
// Métricas de conexiones
trackConnectionMetrics: true
}
};Índices y Optimizaciones
-- Índices recomendados para performance
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);
CREATE INDEX CONCURRENTLY idx_users_active ON users(active) WHERE active = true;
CREATE INDEX CONCURRENTLY idx_posts_published_at ON posts(published_at) WHERE published = true;
CREATE INDEX CONCURRENTLY idx_sessions_expires_at ON sessions(expires_at);
-- Índices compuestos para queries complejas
CREATE INDEX CONCURRENTLY idx_orders_user_status ON orders(user_id, status, created_at);
CREATE INDEX CONCURRENTLY idx_products_category_price ON products(category_id, price) WHERE active = true;
-- Configuración de autovacuum optimizada
ALTER TABLE users SET (autovacuum_vacuum_scale_factor = 0.1);
ALTER TABLE posts SET (autovacuum_vacuum_scale_factor = 0.1);
ALTER TABLE orders SET (autovacuum_analyze_scale_factor = 0.05);Configuración de Cache
Redis Optimizado para Producción
import { CacheConfig } from '@foxframework/cache';
export const cacheConfig: CacheConfig = {
redis: {
// Configuración de conexión
url: process.env.REDIS_URL,
password: process.env.REDIS_PASSWORD,
// Pool de conexiones
pool: {
min: 2,
max: 10
},
// Configuración de memoria
maxMemory: '512mb',
evictionPolicy: 'allkeys-lru',
// Persistencia (para datos críticos)
persistence: {
rdb: {
enabled: true,
saveInterval: 900 // 15 minutos
},
aof: {
enabled: true,
policy: 'everysec'
}
},
// Configuración de red
network: {
keepAlive: true,
connectTimeout: 10000,
lazyConnect: true,
maxRetriesPerRequest: 3,
retryDelayOnFailover: 100
}
},
// Estrategias de cache
strategies: {
// Cache de sesiones (crítico)
sessions: {
ttl: 86400, // 24 horas
refreshOnAccess: true
},
// Cache de datos de usuario
userData: {
ttl: 3600, // 1 hora
refreshOnAccess: false
},
// Cache de contenido estático
staticContent: {
ttl: 604800, // 1 semana
refreshOnAccess: false
}
}
};Implementación de Cache Multi-Layer
export class ProductionCacheManager {
private l1Cache: Map<string, any>; // Memory cache
private l2Cache: RedisCache; // Redis cache
private l3Cache: DatabaseCache; // Database cache
constructor() {
this.l1Cache = new Map();
this.l2Cache = new RedisCache(cacheConfig.redis);
this.l3Cache = new DatabaseCache();
}
async get(key: string): Promise<any> {
// L1 (Memory) - Más rápido
if (this.l1Cache.has(key)) {
return this.l1Cache.get(key);
}
// L2 (Redis) - Rápido y distribuido
const l2Value = await this.l2Cache.get(key);
if (l2Value !== null) {
this.l1Cache.set(key, l2Value);
return l2Value;
}
// L3 (Database) - Fallback
const l3Value = await this.l3Cache.get(key);
if (l3Value !== null) {
await this.l2Cache.set(key, l3Value, 3600);
this.l1Cache.set(key, l3Value);
return l3Value;
}
return null;
}
}Gestión de Secretos
Usando HashiCorp Vault
import { VaultClient } from '@foxframework/core';
export class SecretManager {
private vault: VaultClient;
constructor() {
this.vault = new VaultClient({
endpoint: process.env.VAULT_ENDPOINT,
token: process.env.VAULT_TOKEN,
namespace: process.env.VAULT_NAMESPACE
});
}
async getSecret(path: string): Promise<string> {
try {
const secret = await this.vault.read(path);
return secret.data.value;
} catch (error) {
throw new Error(`Failed to retrieve secret from path: ${path}`);
}
}
async getDatabaseCredentials(): Promise<DatabaseCredentials> {
const credentials = await this.vault.read('database/creds/production');
return {
username: credentials.data.username,
password: credentials.data.password,
host: credentials.data.host,
port: credentials.data.port
};
}
}Configuración con AWS Secrets Manager
import { SecretsManager } from 'aws-sdk';
export class AWSSecretManager {
private secretsManager: SecretsManager;
constructor() {
this.secretsManager = new SecretsManager({
region: process.env.AWS_REGION
});
}
async getSecret(secretId: string): Promise<any> {
try {
const result = await this.secretsManager.getSecretValue({
SecretId: secretId
}).promise();
return JSON.parse(result.SecretString!);
} catch (error) {
throw new Error(`Failed to retrieve secret: ${secretId}`);
}
}
}Configuración de Logging Avanzado
Logging Estructurado
import { Logger, LogLevel } from '@foxframework/logging';
export const productionLogger = new Logger({
level: LogLevel.INFO,
format: 'json',
// Campos estándar en todos los logs
defaultFields: {
service: 'fox-framework-app',
version: process.env.APP_VERSION,
environment: 'production',
hostname: process.env.HOSTNAME
},
// Transports para diferentes destinos
transports: [
// Console (para contenedores)
{
type: 'console',
format: 'json',
level: 'info'
},
// Archivo local (rotación automática)
{
type: 'file',
filename: '/var/log/app/app.log',
maxSize: '100m',
maxFiles: 10,
format: 'json'
},
// Elasticsearch (centralizado)
{
type: 'elasticsearch',
node: process.env.ELASTICSEARCH_URL,
index: 'fox-framework-logs',
level: 'info'
},
// CloudWatch (AWS)
{
type: 'cloudwatch',
logGroupName: '/aws/ec2/fox-framework-app',
logStreamName: process.env.HOSTNAME,
region: process.env.AWS_REGION
}
],
// Sanitización para production
sanitize: {
fields: ['password', 'token', 'secret', 'key'],
replacement: '[REDACTED]'
}
});Correlación de Logs
export class LogCorrelationMiddleware {
static middleware() {
return (req: Request, res: Response, next: NextFunction) => {
// Generar o extraer correlation ID
const correlationId = req.headers['x-correlation-id'] ||
req.headers['x-request-id'] ||
this.generateCorrelationId();
// Agregar al contexto de la request
req.correlationId = correlationId;
// Agregar header a la respuesta
res.setHeader('x-correlation-id', correlationId);
// Configurar logger con contexto
req.logger = productionLogger.child({
correlationId,
requestId: correlationId,
userId: req.user?.id,
userAgent: req.headers['user-agent'],
ip: req.ip
});
next();
};
}
private static generateCorrelationId(): string {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
}Performance y Escalabilidad
Auto-scaling Configuración
# Kubernetes HPA (Horizontal Pod Autoscaler)
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: fox-framework-app-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: fox-framework-app
minReplicas: 2
maxReplicas: 50
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 60
policies:
- type: Percent
value: 100
periodSeconds: 15
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 10
periodSeconds: 60Circuit Breaker Pattern
import { CircuitBreaker } from '@foxframework/core';
export class ExternalServiceClient {
private circuitBreaker: CircuitBreaker;
constructor() {
this.circuitBreaker = new CircuitBreaker({
// Threshold para abrir el circuit
failureThreshold: 5,
// Tiempo antes de intentar half-open
timeout: 60000,
// Monitor de circuit breaker
monitor: true,
// Callback cuando se abre
onOpen: () => {
logger.warn('Circuit breaker opened for external service');
},
// Callback cuando se cierra
onClose: () => {
logger.info('Circuit breaker closed for external service');
}
});
}
async callExternalAPI(data: any): Promise<any> {
return this.circuitBreaker.fire(async () => {
const response = await fetch('https://external-api.com/endpoint', {
method: 'POST',
body: JSON.stringify(data),
timeout: 5000
});
if (!response.ok) {
throw new Error(`API call failed: ${response.status}`);
}
return response.json();
});
}
}Graceful Shutdown
export class GracefulShutdown {
private server: Server;
private connections: Set<Socket> = new Set();
private isShuttingDown = false;
constructor(server: Server) {
this.server = server;
this.setupSignalHandlers();
this.trackConnections();
}
private setupSignalHandlers() {
const signals: NodeJS.Signals[] = ['SIGTERM', 'SIGINT', 'SIGQUIT'];
signals.forEach(signal => {
process.on(signal, async () => {
logger.info(`Received ${signal}, starting graceful shutdown`);
await this.shutdown();
});
});
}
private trackConnections() {
this.server.on('connection', (socket) => {
this.connections.add(socket);
socket.on('close', () => {
this.connections.delete(socket);
});
});
}
private async shutdown(): Promise<void> {
if (this.isShuttingDown) return;
this.isShuttingDown = true;
try {
// 1. Parar de aceptar nuevas conexiones
this.server.close();
// 2. Esperar que las requests activas terminen (max 30s)
await this.waitForActiveConnections(30000);
// 3. Cerrar conexiones de base de datos
await this.closeDatabaseConnections();
// 4. Cerrar conexiones de cache
await this.closeCacheConnections();
// 5. Finalizar procesos en background
await this.stopBackgroundJobs();
logger.info('Graceful shutdown completed');
process.exit(0);
} catch (error) {
logger.error('Error during graceful shutdown', { error });
process.exit(1);
}
}
private async waitForActiveConnections(timeout: number): Promise<void> {
return new Promise((resolve) => {
const timer = setTimeout(() => {
logger.warn('Force closing remaining connections');
this.connections.forEach(socket => socket.destroy());
resolve();
}, timeout);
const checkConnections = () => {
if (this.connections.size === 0) {
clearTimeout(timer);
resolve();
} else {
setTimeout(checkConnections, 100);
}
};
checkConnections();
});
}
}Disaster Recovery
Backup Automatizado
export class BackupManager {
async performBackup(): Promise<void> {
const timestamp = new Date().toISOString();
const backupId = `backup-${timestamp}`;
try {
// 1. Backup de base de datos
await this.backupDatabase(backupId);
// 2. Backup de archivos
await this.backupFiles(backupId);
// 3. Backup de configuración
await this.backupConfiguration(backupId);
// 4. Verificar integridad
await this.verifyBackup(backupId);
// 5. Limpiar backups antiguos
await this.cleanupOldBackups();
logger.info('Backup completed successfully', { backupId });
} catch (error) {
logger.error('Backup failed', { error, backupId });
throw error;
}
}
private async backupDatabase(backupId: string): Promise<void> {
const backupFile = `/backups/db/${backupId}.sql`;
await execAsync(`pg_dump ${process.env.DATABASE_URL} > ${backupFile}`);
await this.uploadToS3(backupFile, `database/${backupId}.sql`);
}
}Mejores Prácticas de Seguridad
Middleware de Seguridad
export class ProductionSecurityMiddleware {
static configure(app: Express): void {
// Security headers
app.use(helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
styleSrc: ["'self'", "'unsafe-inline'"],
scriptSrc: ["'self'"],
imgSrc: ["'self'", "data:", "https:"]
}
},
hsts: {
maxAge: 31536000,
includeSubDomains: true,
preload: true
}
}));
// Rate limiting
app.use('/api/', rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutos
max: 100,
message: 'Too many requests from this IP'
}));
// Request sanitization
app.use(this.sanitizeMiddleware());
// CORS
app.use(cors({
origin: process.env.CORS_ORIGIN?.split(','),
credentials: true,
maxAge: 86400
}));
}
private static sanitizeMiddleware() {
return (req: Request, res: Response, next: NextFunction) => {
// Sanitizar parámetros de query
for (const key in req.query) {
if (typeof req.query[key] === 'string') {
req.query[key] = this.sanitizeString(req.query[key] as string);
}
}
// Sanitizar body
if (req.body && typeof req.body === 'object') {
this.sanitizeObject(req.body);
}
next();
};
}
}Esta configuración de producción te proporciona una base sólida para ejecutar Fox Framework en entornos empresariales con alta disponibilidad, seguridad y rendimiento óptimos.