Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/backend/src/common/rate-limit/rate-limit.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,15 @@

const parts: string[] = [];

// Prefer an authenticated principal when available
const reqAny = request as any;

Check failure on line 222 in apps/backend/src/common/rate-limit/rate-limit.config.ts

View workflow job for this annotation

GitHub Actions / backend-checks

Unsafe assignment of an `any` value
const user = reqAny.user as { id?: string; sub?: string; stellarPublicKey?: string } | undefined;

Check failure on line 223 in apps/backend/src/common/rate-limit/rate-limit.config.ts

View workflow job for this annotation

GitHub Actions / backend-checks

Unsafe member access .user on an `any` value
const principalId = user?.id || user?.sub || user?.stellarPublicKey || '';
if (principalId) {
parts.push(`principal:${principalId}`);
return parts.join('|');
}

if (settings.tracker.useApiKey && apiKey) {
parts.push(`api-key:${apiKey}`);
}
Expand Down
21 changes: 20 additions & 1 deletion apps/backend/src/common/rate-limit/rate-limit.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
} from '@nestjs/common';
import { ThrottlerGuard, ThrottlerLimitDetail } from '@nestjs/throttler';
import { ErrorCode } from '../enums/error-code.enum';
import { Request } from 'express';
import { Request, Response } from 'express';
import { config } from '../../lib/config';
import * as net from 'net';

Expand Down Expand Up @@ -76,6 +76,25 @@ export class RateLimitGuard extends ThrottlerGuard {
void context;
await Promise.resolve();

// Set standard rate limit headers on the response when available
try {
const response = context.switchToHttp().getResponse<Response>();
const ttlSeconds = Math.ceil(throttlerLimitDetail.ttl / 1000);
const retryAfter = Math.max(
Number(throttlerLimitDetail.timeToBlockExpire) || 0,
0,
);

if (response && typeof response.setHeader === 'function') {
response.setHeader('Retry-After', String(retryAfter));
response.setHeader('X-RateLimit-Limit', String(throttlerLimitDetail.limit));
response.setHeader('X-RateLimit-Remaining', '0');
response.setHeader('X-RateLimit-Reset', String(ttlSeconds));
}
} catch {
// Ignore header errors and continue to throw the exception
}

throw new HttpException(
{
code: ErrorCode.SYS_RATE_LIMIT_EXCEEDED,
Expand Down
Loading