2026-01-19 08:10:16 +00:00
|
|
|
/**
|
|
|
|
|
* EMERGES TES - Backend API Server
|
2026-01-25 20:09:47 +00:00
|
|
|
*
|
2026-01-19 08:10:16 +00:00
|
|
|
* FASE 1: Infraestructura Base
|
2026-01-25 20:09:47 +00:00
|
|
|
*
|
2026-01-19 08:10:16 +00:00
|
|
|
* IMPORTANTE: Este es un esqueleto básico.
|
|
|
|
|
* La implementación completa se hará progresivamente.
|
|
|
|
|
*/
|
|
|
|
|
import express from 'express';
|
|
|
|
|
import cors from 'cors';
|
|
|
|
|
import dotenv from 'dotenv';
|
|
|
|
|
import { join } from 'path';
|
|
|
|
|
import { testConnection } from '../config/database.js';
|
|
|
|
|
import { validateSecurityConfig } from './config/security.js';
|
|
|
|
|
import { validateEnv } from './config/env.js';
|
|
|
|
|
import { getCorsConfig } from './config/cors.js';
|
|
|
|
|
import { securityHeaders } from './middleware/security-headers.js';
|
|
|
|
|
import { generalLimiter } from './middleware/rate-limit.js';
|
|
|
|
|
import logger, { logError } from './utils/logger.js';
|
|
|
|
|
import requestLogger from './middleware/request-logger.js';
|
|
|
|
|
import authRoutes from './routes/auth.js'; // TypeScript
|
|
|
|
|
import contentRoutes from './routes/content.js'; // TypeScript
|
|
|
|
|
import statsRoutes from './routes/stats.js'; // TypeScript
|
|
|
|
|
import contentPackRoutes from './routes/content-pack.js'; // TypeScript
|
|
|
|
|
import contentPackAdminRoutes from './routes/content-pack-admin.js'; // TypeScript
|
|
|
|
|
import mediaRoutes from './routes/media.js'; // TypeScript
|
|
|
|
|
import contentResourcesRoutes from './routes/content-resources.js'; // TypeScript
|
|
|
|
|
import scormRoutes from './routes/scorm.js'; // TypeScript
|
|
|
|
|
import validationRoutes from './routes/validation.js'; // TypeScript
|
|
|
|
|
import drugsRoutes from './routes/drugs.js'; // TypeScript
|
|
|
|
|
import webhookRoutes from './routes/webhook.js'; // TypeScript
|
2026-01-25 20:09:47 +00:00
|
|
|
import healthRoutes from './routes/health.js'; // TypeScript
|
2026-01-19 08:10:16 +00:00
|
|
|
dotenv.config();
|
|
|
|
|
// ✅ VALIDACIÓN CRÍTICA DE SEGURIDAD AL STARTUP
|
|
|
|
|
// Si alguna validación falla, la app no arranca
|
|
|
|
|
logger.info('🔒 Validando configuración de seguridad...');
|
|
|
|
|
validateSecurityConfig();
|
|
|
|
|
validateEnv();
|
|
|
|
|
logger.info('✅ Configuración validada correctamente');
|
|
|
|
|
const app = express();
|
|
|
|
|
const PORT = process.env.PORT || process.env.API_PORT || 3000;
|
|
|
|
|
// ✅ SECURITY HEADERS (Helmet.js)
|
|
|
|
|
app.use(securityHeaders);
|
|
|
|
|
// ✅ CORS MEJORADO (con validación de orígenes)
|
|
|
|
|
app.use(cors(getCorsConfig()));
|
|
|
|
|
// ✅ RATE LIMITING GENERAL
|
|
|
|
|
app.use(generalLimiter);
|
|
|
|
|
// ✅ REQUEST LOGGING
|
|
|
|
|
app.use(requestLogger);
|
|
|
|
|
// ✅ JSON PARSING
|
|
|
|
|
app.use(express.json({ limit: '10mb' })); // Limitar tamaño de payload
|
|
|
|
|
// Servir archivos estáticos de media
|
|
|
|
|
app.use('/storage/media', express.static(join(process.cwd(), 'storage', 'media')));
|
2026-01-25 20:09:47 +00:00
|
|
|
// Health check básico (mantener para compatibilidad)
|
|
|
|
|
app.get('/health', async (_req, res) => {
|
|
|
|
|
const dbStart = Date.now();
|
|
|
|
|
const dbConnected = await testConnection();
|
|
|
|
|
const dbResponseTime = Date.now() - dbStart;
|
|
|
|
|
const health = {
|
|
|
|
|
status: dbConnected ? 'ok' : 'degraded',
|
|
|
|
|
timestamp: new Date().toISOString(),
|
|
|
|
|
database: dbConnected ? 'connected' : 'disconnected',
|
|
|
|
|
databaseResponseTime: dbResponseTime,
|
|
|
|
|
uptime: process.uptime(),
|
|
|
|
|
memory: {
|
|
|
|
|
used: Math.round(process.memoryUsage().heapUsed / 1024 / 1024),
|
|
|
|
|
total: Math.round(process.memoryUsage().heapTotal / 1024 / 1024),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
if (!dbConnected) {
|
|
|
|
|
logger.warn('Health check: Database disconnected');
|
|
|
|
|
}
|
|
|
|
|
res.json(health);
|
2026-01-19 08:10:16 +00:00
|
|
|
});
|
|
|
|
|
// Root endpoint
|
2026-01-25 20:09:47 +00:00
|
|
|
app.get('/', (_req, res) => {
|
|
|
|
|
res.json({
|
|
|
|
|
message: 'EMERGES TES Backend API',
|
|
|
|
|
version: '1.0.0',
|
|
|
|
|
endpoints: {
|
|
|
|
|
auth: '/api/auth',
|
|
|
|
|
content: '/api/content',
|
|
|
|
|
stats: '/api/stats',
|
|
|
|
|
contentPack: '/api/content-pack',
|
|
|
|
|
health: '/health',
|
|
|
|
|
},
|
|
|
|
|
});
|
2026-01-19 08:10:16 +00:00
|
|
|
});
|
|
|
|
|
// API Routes
|
|
|
|
|
app.use('/api/auth', authRoutes);
|
|
|
|
|
app.use('/api/content', contentRoutes);
|
|
|
|
|
app.use('/api/stats', statsRoutes);
|
|
|
|
|
app.use('/api/content-pack', contentPackRoutes);
|
|
|
|
|
app.use('/api/admin/content-pack', contentPackAdminRoutes);
|
|
|
|
|
app.use('/api/media', mediaRoutes);
|
|
|
|
|
app.use('/api/content', contentResourcesRoutes);
|
|
|
|
|
app.use('/api/scorm', scormRoutes);
|
|
|
|
|
app.use('/api/validation', validationRoutes);
|
|
|
|
|
app.use('/api/drugs', drugsRoutes);
|
|
|
|
|
app.use('/api/webhook', webhookRoutes);
|
2026-01-25 20:09:47 +00:00
|
|
|
app.use('/api/health', healthRoutes);
|
2026-01-19 08:10:16 +00:00
|
|
|
// Iniciar servidor
|
|
|
|
|
app.listen(PORT, async () => {
|
2026-01-25 20:09:47 +00:00
|
|
|
logger.info('🚀 EMERGES TES Backend API iniciado', {
|
|
|
|
|
port: PORT,
|
|
|
|
|
environment: process.env.NODE_ENV || 'development',
|
|
|
|
|
nodeVersion: process.version,
|
|
|
|
|
});
|
|
|
|
|
logger.info('📍 Endpoints disponibles', {
|
|
|
|
|
health: `http://localhost:${PORT}/health`,
|
|
|
|
|
auth: `http://localhost:${PORT}/api/auth`,
|
|
|
|
|
content: `http://localhost:${PORT}/api/content`,
|
|
|
|
|
});
|
|
|
|
|
// Test de conexión a BD
|
|
|
|
|
const dbConnected = await testConnection();
|
|
|
|
|
if (dbConnected) {
|
|
|
|
|
logger.info('✅ Conexión a base de datos establecida');
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
logger.error('❌ Error conectando a base de datos');
|
|
|
|
|
}
|
2026-01-19 08:10:16 +00:00
|
|
|
});
|
|
|
|
|
// Manejo de errores
|
|
|
|
|
process.on('SIGTERM', async () => {
|
2026-01-25 20:09:47 +00:00
|
|
|
logger.info('SIGTERM recibido, cerrando servidor...');
|
|
|
|
|
process.exit(0);
|
2026-01-19 08:10:16 +00:00
|
|
|
});
|
|
|
|
|
process.on('unhandledRejection', (error) => {
|
2026-01-25 20:09:47 +00:00
|
|
|
logError(error instanceof Error ? error : new Error(String(error)), { type: 'unhandledRejection' });
|
2026-01-19 08:10:16 +00:00
|
|
|
});
|
|
|
|
|
process.on('uncaughtException', (error) => {
|
2026-01-25 20:09:47 +00:00
|
|
|
logError(error, { type: 'uncaughtException' });
|
|
|
|
|
process.exit(1);
|
2026-01-19 08:10:16 +00:00
|
|
|
});
|
2026-01-25 20:09:47 +00:00
|
|
|
//# sourceMappingURL=index.js.map
|