Spaces:
Sleeping
Sleeping
File size: 3,842 Bytes
94ad3aa b6ee67e bce154c b6ee67e bce154c 714ef9d 737a6e6 b6ee67e c918afd b6ee67e 4a8f3ab b6ee67e d28338a b6ee67e 714ef9d bce154c 714ef9d bce154c b6ee67e bce154c 714ef9d 737a6e6 b6ee67e c918afd dbdc105 c918afd ff0376a c918afd dbdc105 b6ee67e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | /**
* Aplicacion Express principal — configuracion de middlewares y montaje de rutas.
*
* Middlewares aplicados (en orden):
* 1. helmet() — headers de seguridad (X-Frame-Options, HSTS, etc.).
* 2. cors() — CORS con origen configurable (CORS_ORIGIN).
* 3. rateLimit() — 200 peticiones / 15 min por IP.
* 4. express.json() — parseo de JSON con limite de 1 MB.
*
* Rutas REST montadas bajo /api/v1:
* - /auth → login, perfil (auth.routes.js)
* - /markets → listado y detalle de mercados (markets.routes.js)
* - /markets → senales IA por mercado (signals.routes.js, subruta)
* - /positions → simulador de posiciones virtuales (positions.routes.js)
* - /watchlist → lista de seguimiento (watchlist.routes.js)
* - /alerts → historial de alertas (alerts.routes.js)
* - /health → healthcheck basico
*
* Manejo de errores:
* - notFound → 404 para rutas no definidas.
* - errorHandler → 500 generico en produccion, detalles en desarrollo.
*/
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
import { config } from './config.js';
import { ok } from './utils/apiResponse.js';
import authRoutes from './auth/auth.routes.js';
import marketsRoutes from './markets/markets.routes.js';
import signalsRoutes from './signals/signals.routes.js';
import positionsRoutes from './positions/positions.routes.js';
import watchlistRoutes from './watchlist/watchlist.routes.js';
import alertsRoutes from './alerts/alerts.routes.js';
import statsRoutes from './stats/stats.routes.js';
import preferencesRoutes from './preferences/preferences.routes.js';
import { notFound } from './middlewares/notFound.js';
import { errorHandler } from './middlewares/errorHandler.js';
import { existsSync } from 'node:fs';
const app = express();
app.set('trust proxy', 1);
app.use(
helmet({
contentSecurityPolicy: {
directives: {
defaultSrc: ["'self'"],
baseUri: ["'self'"],
fontSrc: ["'self'", 'https:', 'data:'],
formAction: ["'self'"],
frameAncestors: ["'self'"],
imgSrc: ["'self'", 'data:', 'https:'],
objectSrc: ["'none'"],
scriptSrc: ["'self'"],
scriptSrcAttr: ["'none'"],
styleSrc: ["'self'", 'https:', "'unsafe-inline'"],
upgradeInsecureRequests: [],
},
},
}),
);
app.use(cors({ origin: config.CORS_ORIGIN, credentials: true }));
// Rate limit: muy permisivo en desarrollo, restrictivo en producción
const rateLimitMax = config.NODE_ENV === 'production' ? 200 : 5000;
app.use(
rateLimit({
windowMs: 15 * 60 * 1000,
max: rateLimitMax,
standardHeaders: true,
legacyHeaders: false,
message: { ok: false, error: { code: 'TOO_MANY_REQUESTS', message: 'Rate limit exceeded' } },
}),
);
app.use(express.json({ limit: '1mb' }));
app.get('/api/v1/health', (_req, res) => ok(res, { status: 'up' }));
app.use('/api/v1/auth', authRoutes);
app.use('/api/v1/markets', marketsRoutes);
app.use('/api/v1/markets', signalsRoutes);
app.use('/api/v1/positions', positionsRoutes);
app.use('/api/v1/watchlist', watchlistRoutes);
app.use('/api/v1/alerts', alertsRoutes);
app.use('/api/v1/stats', statsRoutes);
app.use('/api/v1/preferences', preferencesRoutes);
// Servir frontend estático en producción (HuggingFace Spaces / Docker)
// Detecta si estamos en la raíz del proyecto o dentro de backend/
const frontendDist = existsSync('../frontend/dist') ? '../frontend/dist' : 'frontend/dist';
if (config.NODE_ENV === 'production') {
app.use(express.static(frontendDist));
app.get(/.*/, (_req, res) => {
res.sendFile('index.html', { root: frontendDist });
});
}
app.use(notFound);
app.use(errorHandler);
export default app;
|