@adaptic/backend-legacy 0.0.46 → 0.0.47
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
2
|
/**
|
|
3
|
-
* Rate limiter for GraphQL endpoint
|
|
4
|
-
*
|
|
3
|
+
* Rate limiter for GraphQL endpoint.
|
|
4
|
+
*
|
|
5
|
+
* Authenticated requests: 1000 requests per 15 minutes (configurable via RATE_LIMIT_MAX)
|
|
6
|
+
* Unauthenticated requests: 200 requests per 15 minutes (configurable via RATE_LIMIT_MAX_UNAUTH)
|
|
5
7
|
*/
|
|
6
8
|
export declare const graphqlRateLimiter: (req: Request, res: Response, next: NextFunction) => void;
|
|
7
9
|
/**
|
|
8
|
-
* Rate limiter for authentication endpoints
|
|
9
|
-
*
|
|
10
|
+
* Rate limiter for authentication endpoints.
|
|
11
|
+
*
|
|
12
|
+
* Authenticated requests: 50 requests per 15 minutes
|
|
13
|
+
* Unauthenticated requests: 20 requests per 15 minutes
|
|
10
14
|
*/
|
|
11
15
|
export declare const authRateLimiter: (req: Request, res: Response, next: NextFunction) => void;
|
|
12
16
|
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../../src/middleware/rate-limiter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../../src/middleware/rate-limiter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAuG1D;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,QA5ChB,OAAO,OAAO,QAAQ,QAAQ,YAAY,KAAG,IAmD1D,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,QA3Db,OAAO,OAAO,QAAQ,QAAQ,YAAY,KAAG,IAkE1D,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../src/middleware/rate-limiter.ts"],"names":[],"mappings":"AAAA,gHAAgH;
|
|
1
|
+
{"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../../src/middleware/rate-limiter.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
// Integration: add to server.ts - app.use('/graphql', graphqlRateLimiter) and app.use('/auth', authRateLimiter)
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Checks whether a request carries a valid-looking authentication token.
|
|
4
|
+
* Does not verify the token -- only checks for its presence in the
|
|
5
|
+
* Authorization header as a Bearer token with three dot-separated parts
|
|
6
|
+
* (standard JWT structure).
|
|
7
|
+
*/
|
|
8
|
+
function isAuthenticated(req) {
|
|
9
|
+
const authHeader = req.headers.authorization || '';
|
|
10
|
+
if (!authHeader.startsWith('Bearer ')) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const token = authHeader.slice(7);
|
|
14
|
+
// Google OAuth tokens (ya29.) and JWTs (three dot-separated segments) count
|
|
15
|
+
if (token.startsWith('ya29.')) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
return token.split('.').length === 3;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Creates a simple in-memory rate limiter middleware with separate limits
|
|
22
|
+
* for authenticated and unauthenticated requests.
|
|
23
|
+
*
|
|
24
|
+
* Response headers (when standardHeaders is enabled):
|
|
25
|
+
* X-RateLimit-Limit - maximum requests allowed in the current window
|
|
26
|
+
* X-RateLimit-Remaining - requests remaining in the current window
|
|
27
|
+
* X-RateLimit-Reset - seconds until the current window resets
|
|
28
|
+
* Retry-After - seconds to wait before retrying (only on 429)
|
|
29
|
+
*
|
|
4
30
|
* @param config - Rate limit configuration
|
|
5
31
|
* @returns Express middleware function
|
|
6
32
|
*/
|
|
@@ -17,26 +43,31 @@ function createRateLimiter(config) {
|
|
|
17
43
|
}, 60000);
|
|
18
44
|
return (req, res, next) => {
|
|
19
45
|
const identifier = req.ip || req.connection.remoteAddress || 'unknown';
|
|
46
|
+
const authenticated = isAuthenticated(req);
|
|
47
|
+
const effectiveMax = authenticated ? config.maxAuthenticated : config.maxUnauthenticated;
|
|
48
|
+
const storeKey = `${identifier}:${authenticated ? 'auth' : 'anon'}`;
|
|
20
49
|
const now = Date.now();
|
|
21
|
-
if (!store[
|
|
22
|
-
store[
|
|
50
|
+
if (!store[storeKey] || store[storeKey].resetTime < now) {
|
|
51
|
+
store[storeKey] = {
|
|
23
52
|
count: 1,
|
|
24
53
|
resetTime: now + config.windowMs,
|
|
25
54
|
};
|
|
26
55
|
}
|
|
27
56
|
else {
|
|
28
|
-
store[
|
|
57
|
+
store[storeKey].count += 1;
|
|
29
58
|
}
|
|
30
|
-
const current = store[
|
|
31
|
-
const remaining = Math.max(0,
|
|
32
|
-
const
|
|
59
|
+
const current = store[storeKey];
|
|
60
|
+
const remaining = Math.max(0, effectiveMax - current.count);
|
|
61
|
+
const resetSeconds = Math.ceil((current.resetTime - now) / 1000);
|
|
33
62
|
// Add rate limit headers
|
|
34
63
|
if (config.standardHeaders !== false) {
|
|
35
|
-
res.setHeader('X-RateLimit-Limit',
|
|
64
|
+
res.setHeader('X-RateLimit-Limit', effectiveMax.toString());
|
|
36
65
|
res.setHeader('X-RateLimit-Remaining', remaining.toString());
|
|
37
|
-
res.setHeader('X-RateLimit-Reset',
|
|
66
|
+
res.setHeader('X-RateLimit-Reset', resetSeconds.toString());
|
|
38
67
|
}
|
|
39
|
-
if (current.count >
|
|
68
|
+
if (current.count > effectiveMax) {
|
|
69
|
+
// Include Retry-After header on 429 responses (RFC 6585 / RFC 7231)
|
|
70
|
+
res.setHeader('Retry-After', resetSeconds.toString());
|
|
40
71
|
res.status(429).json(config.message);
|
|
41
72
|
return;
|
|
42
73
|
}
|
|
@@ -44,23 +75,29 @@ function createRateLimiter(config) {
|
|
|
44
75
|
};
|
|
45
76
|
}
|
|
46
77
|
/**
|
|
47
|
-
* Rate limiter for GraphQL endpoint
|
|
48
|
-
*
|
|
78
|
+
* Rate limiter for GraphQL endpoint.
|
|
79
|
+
*
|
|
80
|
+
* Authenticated requests: 1000 requests per 15 minutes (configurable via RATE_LIMIT_MAX)
|
|
81
|
+
* Unauthenticated requests: 200 requests per 15 minutes (configurable via RATE_LIMIT_MAX_UNAUTH)
|
|
49
82
|
*/
|
|
50
83
|
export const graphqlRateLimiter = createRateLimiter({
|
|
51
84
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
52
|
-
|
|
85
|
+
maxAuthenticated: parseInt(process.env.RATE_LIMIT_MAX || '1000', 10),
|
|
86
|
+
maxUnauthenticated: parseInt(process.env.RATE_LIMIT_MAX_UNAUTH || '200', 10),
|
|
53
87
|
standardHeaders: true,
|
|
54
88
|
legacyHeaders: false,
|
|
55
89
|
message: { errors: [{ message: 'Too many requests, please try again later.' }] },
|
|
56
90
|
});
|
|
57
91
|
/**
|
|
58
|
-
* Rate limiter for authentication endpoints
|
|
59
|
-
*
|
|
92
|
+
* Rate limiter for authentication endpoints.
|
|
93
|
+
*
|
|
94
|
+
* Authenticated requests: 50 requests per 15 minutes
|
|
95
|
+
* Unauthenticated requests: 20 requests per 15 minutes
|
|
60
96
|
*/
|
|
61
97
|
export const authRateLimiter = createRateLimiter({
|
|
62
98
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
63
|
-
|
|
99
|
+
maxAuthenticated: 50,
|
|
100
|
+
maxUnauthenticated: 20,
|
|
64
101
|
standardHeaders: true,
|
|
65
102
|
legacyHeaders: false,
|
|
66
103
|
message: { errors: [{ message: 'Too many authentication attempts.' }] },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adaptic/backend-legacy",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.47",
|
|
4
4
|
"description": "Backend executable CRUD functions with dynamic variables construction, and type definitions for the Adaptic AI platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "index.d.ts",
|