Skip to content

Commit 8ec3e31

Browse files
committed
fix: upgrade express-rate-limit and retryAfter math
Use the latest express-rate-limit release so the patched ip-address version is resolved without a local override. Also correct the custom 429 retryAfter payload to report seconds remaining instead of an epoch timestamp, and add focused coverage for the helper.
1 parent c43d344 commit 8ec3e31

5 files changed

Lines changed: 46 additions & 11 deletions

File tree

package-lock.json

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@
7575
"dotenv": "^17.2.3",
7676
"express": "^5.2.1",
7777
"express-joi-validation": "^6.1.0",
78-
"express-rate-limit": "^8.2.1",
78+
"express-rate-limit": "^8.5.1",
7979
"joi": "^17.13.3",
8080
"js-yaml": "^4.1.1",
8181
"lodash": "^4.18.1",
@@ -123,7 +123,6 @@
123123
"overrides": {
124124
"diff": "8.0.3",
125125
"esbuild": "0.27.3",
126-
"ip-address": "10.1.1",
127126
"tar": "7.5.13",
128127
"mocha": {
129128
"serialize-javascript": "7.0.5"

src/middleware.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { Organization } from './models';
2020
import { OrganizationsV2 } from './models/v2/index.js';
2121
import { logger } from './config/logger.js';
2222
import { sendReadOnlyError } from './utils/read-only-response.js';
23+
import { getRateLimitRetryAfterSeconds } from './utils/rate-limit.js';
2324

2425
const { USE_SIMULATOR } = getConfig().APP;
2526

@@ -68,7 +69,7 @@ const generalLimiter = rateLimit({
6869
message: 'Too many requests. Please slow down and try again later.',
6970
error: 'RATE_LIMIT_EXCEEDED',
7071
success: false,
71-
retryAfter: Math.ceil(req.rateLimit.resetTime / 1000), // seconds until reset
72+
retryAfter: getRateLimitRetryAfterSeconds(req.rateLimit?.resetTime),
7273
});
7374
},
7475
});

src/utils/rate-limit.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export const getRateLimitRetryAfterSeconds = (resetTime, now = Date.now()) => {
2+
if (!(resetTime instanceof Date)) {
3+
return 0;
4+
}
5+
6+
const resetTimestamp = resetTime.getTime();
7+
if (Number.isNaN(resetTimestamp)) {
8+
return 0;
9+
}
10+
11+
return Math.max(0, Math.ceil((resetTimestamp - now) / 1000));
12+
};

tests/resources/rate-limit.spec.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { expect } from 'chai';
2+
import { getRateLimitRetryAfterSeconds } from '../../src/utils/rate-limit.js';
3+
4+
describe('Rate limit utilities', function () {
5+
it('returns seconds remaining until reset', function () {
6+
const now = 1_700_000_000_000;
7+
const resetTime = new Date(now + 4_500);
8+
9+
expect(getRateLimitRetryAfterSeconds(resetTime, now)).to.equal(5);
10+
});
11+
12+
it('returns zero when the reset time has already passed', function () {
13+
const now = 1_700_000_000_000;
14+
const resetTime = new Date(now - 1_000);
15+
16+
expect(getRateLimitRetryAfterSeconds(resetTime, now)).to.equal(0);
17+
});
18+
19+
it('returns zero when reset time is missing or invalid', function () {
20+
expect(getRateLimitRetryAfterSeconds()).to.equal(0);
21+
expect(getRateLimitRetryAfterSeconds(new Date('invalid date'))).to.equal(0);
22+
});
23+
});

0 commit comments

Comments
 (0)