import express from 'express'; import helmet from 'helmet'; import cors from 'cors'; import cookieParser from 'cookie-parser'; import swaggerUi from 'swagger-ui-express'; import { env } from './config/env'; import { swaggerSpec } from './config/swagger'; import { traceMiddleware } from './middleware/trace'; import { authMiddleware } from './middleware/auth'; import { csrfMiddleware } from './middleware/csrf'; import { globalLimiter, mutateLimiter, sensitiveLimiter, mnemonicRevealLimiter } from './middleware/rate-limit'; import { errorHandler } from './middleware/error-handler'; import walletRoutes from './routes/wallet.routes'; import relayProxyRoutes from './routes/relay-proxy.routes'; import tronProxyRoutes from './routes/tron-proxy.routes'; import solSwapProxyRoutes from './routes/sol-swap-proxy.routes'; import tronSwapProxyRoutes from './routes/tron-swap-proxy.routes'; import btcProxyRoutes from './routes/btc-proxy.routes'; import bscSwapProxyRoutes from './routes/bsc-swap-proxy.routes'; const app = express(); // Trust proxy для корректного req.ip за reverse proxy / load balancer app.set('trust proxy', 1); app.use(helmet()); app.use( cors({ origin: env.cors.origins.length > 0 ? env.cors.origins : false, credentials: env.cors.allowCredentials, }), ); app.use(express.json({ limit: '64kb' })); // защита от больших payload-DoS app.use(cookieParser()); app.use(traceMiddleware); // ── PUBLIC endpoints ───────────────────────────────────────────────────────── app.get('/api/health', (_req, res) => { res.json({ success: true, data: { status: 'ok' } }); }); app.use('/api/docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); app.get('/api/docs/swagger.json', (_req, res) => { res.json(swaggerSpec); }); // ── Глобальный rate limit на весь API после public endpoints ──────────────── app.use('/api', globalLimiter); // ── PROTECTED endpoints (JWT + CSRF) ───────────────────────────────────────── const protect = [authMiddleware, csrfMiddleware]; // Sensitive — самый строгий лимит. Каждый POST защищён JWT + CSRF. app.use('/api/wallets/create', ...protect, sensitiveLimiter); app.use('/api/wallets/mnemonic/reveal', ...protect, mnemonicRevealLimiter); app.use('/api/wallets/:chain/send', ...protect, sensitiveLimiter); // Mutating (proxy + read endpoints) — повышенный лимит app.use('/api/wallets', ...protect, mutateLimiter, walletRoutes); app.use('/api/relay', ...protect, mutateLimiter, relayProxyRoutes); app.use('/api/tron', ...protect, mutateLimiter, tronProxyRoutes); app.use('/api/sol/swap', ...protect, mutateLimiter, solSwapProxyRoutes); app.use('/api/tron/swap', ...protect, mutateLimiter, tronSwapProxyRoutes); app.use('/api/btc', ...protect, mutateLimiter, btcProxyRoutes); app.use('/api/bsc/swap', ...protect, mutateLimiter, bscSwapProxyRoutes); // 404 для всего что не сматчилось выше — единый JSON-ответ, не express default text app.use((_req, res) => { res.status(404).json({ success: false, error: 'Not found' }); }); app.use(errorHandler); export default app;