75 lines
3.3 KiB
TypeScript
75 lines
3.3 KiB
TypeScript
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;
|