202 lines
5.8 KiB
JavaScript
202 lines
5.8 KiB
JavaScript
const express = require('express');
|
|
const cors = require('cors');
|
|
const helmet = require('helmet');
|
|
const compression = require('compression');
|
|
const rateLimit = require('express-rate-limit');
|
|
require('dotenv').config();
|
|
|
|
// Configuración de PostgreSQL
|
|
const { Pool } = require('pg');
|
|
const pool = new Pool({
|
|
connectionString: process.env.DATABASE_URL,
|
|
max: 20,
|
|
idleTimeoutMillis: 30000,
|
|
connectionTimeoutMillis: 2000,
|
|
});
|
|
|
|
// Establecer search_path para incluir el esquema dtic
|
|
pool.on('connect', (client) => {
|
|
client.query('SET search_path TO dtic, public');
|
|
});
|
|
|
|
// Importar rutas
|
|
const authRoutes = require('./routes/auth');
|
|
const tecnicosRoutes = require('./routes/tecnicos');
|
|
const tareasRoutes = require('./routes/tareas');
|
|
const recursosRoutes = require('./routes/recursos');
|
|
const usuariosRelacionadosRoutes = require('./routes/usuarios_relacionados');
|
|
const tareaRecursosRoutes = require('./routes/tarea-recursos');
|
|
const systemRoutes = require('./routes/system');
|
|
|
|
// Configuración de la aplicación
|
|
const app = express();
|
|
const PORT = process.env.PORT || 3001;
|
|
|
|
// Middleware de seguridad
|
|
app.use(helmet({
|
|
crossOriginResourcePolicy: { policy: "cross-origin" }
|
|
}));
|
|
|
|
// Rate limiting - más permisivo para desarrollo
|
|
const limiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15 minutos
|
|
max: 1000, // límite de 1000 requests por windowMs (aumentado para desarrollo)
|
|
message: 'Demasiadas solicitudes desde esta IP, por favor intenta más tarde.'
|
|
});
|
|
app.use('/api/', limiter);
|
|
|
|
// Rate limiting más estricto para autenticación
|
|
const authLimiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15 minutos
|
|
max: 5, // límite de 5 requests por windowMs
|
|
message: 'Demasiados intentos de autenticación, por favor intenta más tarde.'
|
|
});
|
|
app.use('/api/auth/login', authLimiter);
|
|
|
|
// CORS - Allow all origins for development and remote access
|
|
app.use(cors({
|
|
origin: true, // Allow all origins
|
|
credentials: true,
|
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With']
|
|
}));
|
|
|
|
// Compresión
|
|
app.use(compression());
|
|
|
|
// Body parsing with logging
|
|
app.use(express.json({
|
|
limit: '10mb',
|
|
verify: (req, res, buf) => {
|
|
console.log(`[PAYLOAD SIZE] ${req.method} ${req.path}: ${buf.length} bytes`);
|
|
}
|
|
}));
|
|
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
|
|
|
// Logging básico
|
|
app.use((req, res, next) => {
|
|
console.log(`${new Date().toISOString()} - ${req.method} ${req.path}`);
|
|
next();
|
|
});
|
|
|
|
// Health check
|
|
app.get('/health', (req, res) => {
|
|
res.json({ status: 'OK', timestamp: new Date().toISOString() });
|
|
});
|
|
|
|
// Rutas de la API
|
|
app.use('/api/auth', authRoutes);
|
|
app.use('/api/tecnicos', tecnicosRoutes);
|
|
app.use('/api/tareas', tareasRoutes);
|
|
app.use('/api/recursos', recursosRoutes);
|
|
app.use('/api/tarea-recursos', tareaRecursosRoutes); // Rutas para asignaciones tarea-recurso
|
|
app.use('/api/system', systemRoutes);
|
|
// Ruta para servir el archivo de configuración YAML
|
|
app.get('/api/config/entities.yml', (req, res) => {
|
|
const path = require('path');
|
|
const fs = require('fs');
|
|
|
|
// El archivo está montado en /host/_app-npm/frontend/src/config/entities.yml
|
|
const configPath = path.join('/host', '_app-npm', 'frontend', 'src', 'config', 'entities.yml');
|
|
|
|
console.log(`[CONFIG] Intentando leer archivo desde: ${configPath}`);
|
|
|
|
fs.readFile(configPath, 'utf8', (err, data) => {
|
|
if (err) {
|
|
console.error('Error leyendo archivo de configuración:', err);
|
|
console.error('Path attempted:', configPath);
|
|
return res.status(500).json({
|
|
success: false,
|
|
message: 'Error al leer el archivo de configuración',
|
|
details: process.env.NODE_ENV === 'development' ? err.message : undefined
|
|
});
|
|
}
|
|
|
|
console.log(`[CONFIG] Archivo de configuración leído exitosamente (${data.length} bytes)`);
|
|
res.setHeader('Content-Type', 'text/yaml');
|
|
res.send(data);
|
|
});
|
|
});
|
|
app.use('/api/usuarios_relacionados', usuariosRelacionadosRoutes);
|
|
|
|
// Manejo de errores 404
|
|
app.use('/api/*', (req, res) => {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: 'Endpoint no encontrado'
|
|
});
|
|
});
|
|
|
|
// Middleware de manejo de errores
|
|
app.use((err, req, res, next) => {
|
|
console.error('Error:', err);
|
|
|
|
// Error de validación
|
|
if (err.name === 'ValidationError') {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: 'Datos de entrada inválidos',
|
|
errors: err.errors
|
|
});
|
|
}
|
|
|
|
// Error de base de datos
|
|
if (err.code && err.code.startsWith('23')) {
|
|
return res.status(400).json({
|
|
success: false,
|
|
message: 'Error de integridad de datos'
|
|
});
|
|
}
|
|
|
|
// Error interno del servidor
|
|
res.status(500).json({
|
|
success: false,
|
|
message: process.env.NODE_ENV === 'production'
|
|
? 'Error interno del servidor'
|
|
: err.message
|
|
});
|
|
});
|
|
|
|
// Función para verificar conexión a la base de datos
|
|
const testDatabaseConnection = async () => {
|
|
try {
|
|
const client = await pool.connect();
|
|
console.log('✅ Conexión a PostgreSQL exitosa');
|
|
client.release();
|
|
} catch (err) {
|
|
console.error('❌ Error conectando a PostgreSQL:', err);
|
|
process.exit(1);
|
|
}
|
|
};
|
|
|
|
// Iniciar servidor
|
|
const startServer = async () => {
|
|
await testDatabaseConnection();
|
|
|
|
app.listen(PORT, () => {
|
|
console.log(`🚀 Servidor ejecutándose en puerto ${PORT}`);
|
|
console.log(`📊 Ambiente: ${process.env.NODE_ENV || 'development'}`);
|
|
});
|
|
};
|
|
|
|
// Manejo de señales para cerrar gracefully
|
|
process.on('SIGTERM', () => {
|
|
console.log('SIGTERM recibido, cerrando servidor...');
|
|
pool.end(() => {
|
|
console.log('Pool de conexiones cerrado');
|
|
process.exit(0);
|
|
});
|
|
});
|
|
|
|
process.on('SIGINT', () => {
|
|
console.log('SIGINT recibido, cerrando servidor...');
|
|
pool.end(() => {
|
|
console.log('Pool de conexiones cerrado');
|
|
process.exit(0);
|
|
});
|
|
});
|
|
|
|
// Exportar app para uso en otros módulos si es necesario
|
|
module.exports = { app, pool };
|
|
|
|
startServer(); |