Структура API
Ограничение частоты запросов
Стратегия: Token Bucket
Ограничение частоты запросов использует @nestjs/throttler с многоуровневыми лимитами. Для продакшн-развёртываний следует использовать Redis-адаптер для распределённого ограничения частоты между несколькими инстансами.
typescript
// nestjs-throttler с хранилищем в PostgreSQL
@Module({
imports: [
ThrottlerModule.forRoot({
throttlers: [
{ name: 'short', ttl: 1000, limit: 10 }, // 10 запросов/сек
{ name: 'medium', ttl: 60000, limit: 100 }, // 100 запросов/мин
{ name: 'long', ttl: 3600000, limit: 1000 } // 1000 запросов/час
],
storage: new ThrottlerStorageService() // С поддержкой БД
})
]
})Лимиты по эндпоинтам
| Категория эндпоинтов | Лимит |
|---|---|
| Аутентификация (вход, регистрация) | 5/мин |
| Сброс пароля | 3/час |
| Платёжные webhooks | 100/сек |
| API общий | 100/мин |
| Административные эндпоинты | 200/мин |
Лимиты финансовых эндпоинтов
| Эндпоинт | Лимит |
|---|---|
POST /mlm/payouts/request | 1/мин на пользователя |
POST /investment/participations | 3/мин на пользователя |
POST /cart/checkout | 2/мин на пользователя |
PATCH /users/me (реквизиты выплат) | 3/час на пользователя |
POST /admin/commissions/approve | 10/мин на администратора |
POST /admin/payouts/process | 5/мин на администратора |
Реализация
typescript
// Кастомный декоратор для финансовых эндпоинтов
export const FinancialRateLimit = () =>
applyDecorators(
Throttle({ default: { limit: 1, ttl: 60000 } }), // 1 в минуту
UseGuards(ThrottlerGuard),
);
// Использование в контроллере
@Controller('mlm/payouts')
export class PayoutsController {
@Post('request')
@FinancialRateLimit()
async requestPayout(@CurrentUser() user: User, @Body() dto: PayoutRequestDto) {
return this.payoutService.createRequest(user.partnerId, dto);
}
}Ограничение частоты по пользователю
Для финансовых эндпоинтов ограничение должно быть по пользователю, а не по IP:
typescript
@Injectable()
export class UserThrottlerGuard extends ThrottlerGuard {
protected async getTracker(req: Request): Promise<string> {
// Использование ID пользователя для аутентифицированных запросов (предотвращает проблемы с общим IP)
const user = req.user as AuthenticatedUser;
if (user?.id) {
return `user:${user.id}`;
}
// Fallback на IP для неаутентифицированных запросов
return req.ip;
}
}Заголовки ограничения частоты
Финансовые эндпоинты включают дополнительные заголовки для прозрачности:
X-RateLimit-Limit: 1
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1705329600
X-RateLimit-Policy: financialОтвет при превышении лимита
json
{
"statusCode": 429,
"message": "Too Many Requests",
"retryAfter": 45
}Обзор эндпоинтов
/api/v1
├── /auth
│ ├── POST /register
│ ├── POST /login
│ ├── POST /refresh
│ ├── POST /logout
│ └── POST /2fa/*
│
├── /users
│ ├── GET /me
│ ├── PATCH /me
│ └── /kyc/*
│
├── /investment
│ ├── GET /strategies
│ ├── GET /strategies/:id
│ ├── POST /participations
│ ├── GET /participations
│ └── GET /portfolio
│
├── /products
│ ├── GET /
│ ├── GET /:id
│ ├── GET /categories
│ └── POST /reviews
│
├── /cart
│ ├── GET /
│ ├── POST /items
│ ├── PATCH /items/:id
│ ├── DELETE /items/:id
│ └── POST /checkout
│
├── /orders
│ ├── GET /
│ ├── GET /:id
│ └── GET /:id/tracking
│
├── /mlm
│ ├── /partner
│ │ ├── GET /me
│ │ ├── GET /network
│ │ └── GET /network/stats
│ │
│ ├── /referrals
│ │ ├── GET /links
│ │ ├── POST /links
│ │ └── GET /links/:id/stats
│ │
│ ├── /commissions
│ │ ├── GET /
│ │ ├── GET /summary
│ │ └── GET /history
│ │
│ ├── /ranks
│ │ ├── GET /current
│ │ ├── GET /progress
│ │ └── GET /history
│ │
│ └── /payouts
│ ├── GET /balance
│ ├── POST /request
│ └── GET /history
│
└── /admin (Суперадминистратор)
├── /users/*
├── /partners/*
├── /commissions/*
├── /products/*
├── /strategies/*
└── /analytics/*