@constructive-io/graphql-server 5.33.3 → 5.33.4
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.
- package/esm/middleware/admission-control.js +3 -21
- package/esm/middleware/client-ip.js +27 -0
- package/esm/refusals/recorder.js +3 -15
- package/esm/server.js +2 -2
- package/middleware/admission-control.js +2 -20
- package/middleware/client-ip.d.ts +17 -0
- package/middleware/client-ip.js +32 -0
- package/package.json +5 -7
- package/refusals/recorder.js +2 -14
- package/server.js +2 -2
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import './types'; // for Request type
|
|
2
2
|
import { errors } from '@constructive-io/errors';
|
|
3
|
-
import { clientIpFrom, ConcurrencyLimiter, DEFAULT_REQUEST_PROTECTION, RateWindow
|
|
3
|
+
import { clientIpFrom, ConcurrencyLimiter, DEFAULT_REQUEST_PROTECTION, RateWindow } from '@constructive-io/express-context';
|
|
4
4
|
import { Logger } from '@pgpmjs/logger';
|
|
5
5
|
import { respondWithGraphQLError } from '../errors/graphql-response';
|
|
6
6
|
import { recordRefusal } from '../refusals/recorder';
|
|
7
|
+
import { resolveProxyHops } from './client-ip';
|
|
7
8
|
const log = new Logger('admission');
|
|
8
9
|
/**
|
|
9
10
|
* admission-control — the gate that decides whether a GraphQL request starts.
|
|
@@ -44,25 +45,6 @@ const protectionOf = (req) => req.requestProtection ?? DEFAULT_REQUEST_PROTECTIO
|
|
|
44
45
|
const routeOf = (req) => `${req.method} ${req.baseUrl}${req.path}`;
|
|
45
46
|
/** Seconds for a `Retry-After` header — the smallest honest whole number. */
|
|
46
47
|
const retryAfterSeconds = (ms) => String(Math.max(Math.ceil(ms / 1000), 1));
|
|
47
|
-
/**
|
|
48
|
-
* How far back through `X-Forwarded-For` to believe.
|
|
49
|
-
*
|
|
50
|
-
* `req.clientIp` and `req.ip` are both unusable as a limiter key here: this
|
|
51
|
-
* server sets `trust proxy` to a predicate that returns true unconditionally,
|
|
52
|
-
* and `request-ip` reads the *leftmost* forwarded entry regardless — so either
|
|
53
|
-
* one hands a caller a fresh key per request for the cost of a header. The
|
|
54
|
-
* fallback to 1 hop exists because the opposite failure is just as bad: behind
|
|
55
|
-
* an ingress with no hop count configured, every caller resolves to the
|
|
56
|
-
* ingress's address and one abuser throttles the whole tenant.
|
|
57
|
-
*/
|
|
58
|
-
const resolveHops = (req, configured) => {
|
|
59
|
-
if (typeof configured === 'number')
|
|
60
|
-
return configured;
|
|
61
|
-
const fromEnv = trustedProxyHops();
|
|
62
|
-
if (fromEnv > 0)
|
|
63
|
-
return fromEnv;
|
|
64
|
-
return req.app?.get('trust proxy') ? 1 : 0;
|
|
65
|
-
};
|
|
66
48
|
/**
|
|
67
49
|
* Rate-limit callers per route, then admit at most `maxConcurrentRequests` of
|
|
68
50
|
* them per database into the handler chain.
|
|
@@ -79,7 +61,7 @@ export const createAdmissionControlMiddleware = (options = {}) => {
|
|
|
79
61
|
const protection = protectionOf(req);
|
|
80
62
|
const databaseId = req.databaseId ?? UNKNOWN_DATABASE;
|
|
81
63
|
// ─── Per-caller rate ────────────────────────────────────────────────────
|
|
82
|
-
const hops =
|
|
64
|
+
const hops = resolveProxyHops(req, options.trustedProxyHops);
|
|
83
65
|
const ip = clientIpFrom(req, hops);
|
|
84
66
|
const rateKey = `${databaseId}\u0000${ip}\u0000${routeOf(req)}`;
|
|
85
67
|
if (!rate.admit(rateKey, protection.rateLimitRpm, protection.rateLimitBurst)) {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import './types'; // for Request type
|
|
2
|
+
import { clientIpFrom, trustedProxyHops } from '@constructive-io/express-context';
|
|
3
|
+
/**
|
|
4
|
+
* How far back through `X-Forwarded-For` to believe: an explicit count, then
|
|
5
|
+
* `TRUSTED_PROXY_HOPS` (an explicit 0 included), then 1 when `SERVER_TRUST_PROXY`
|
|
6
|
+
* puts a proxy in front.
|
|
7
|
+
*
|
|
8
|
+
* Express's `trust proxy` predicate and `req.ip` are not used as the answer:
|
|
9
|
+
* the predicate trusts every hop, which would hand back the leftmost entry —
|
|
10
|
+
* the one a caller writes. One ingress means one hop is believed.
|
|
11
|
+
*/
|
|
12
|
+
export const resolveProxyHops = (req, configured) => {
|
|
13
|
+
if (typeof configured === 'number')
|
|
14
|
+
return configured;
|
|
15
|
+
const fromEnv = trustedProxyHops();
|
|
16
|
+
if (fromEnv !== null)
|
|
17
|
+
return fromEnv;
|
|
18
|
+
return req.app?.get('trust proxy') ? 1 : 0;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Sets `req.clientIp` — the address `jwt.claims.ip_address`, logs and per-IP
|
|
22
|
+
* limits are keyed on — to the entry our own proxy appended, or the socket peer.
|
|
23
|
+
*/
|
|
24
|
+
export const clientIpMiddleware = (req, _res, next) => {
|
|
25
|
+
req.clientIp = clientIpFrom(req, resolveProxyHops(req));
|
|
26
|
+
next();
|
|
27
|
+
};
|
package/esm/refusals/recorder.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import '../middleware/types'; // for Request type
|
|
2
|
-
import { clientIpFrom, createRecordRefusalsSink, RefusalRecorder
|
|
2
|
+
import { clientIpFrom, createRecordRefusalsSink, RefusalRecorder } from '@constructive-io/express-context';
|
|
3
3
|
import { Logger } from '@pgpmjs/logger';
|
|
4
4
|
import { getPgPool } from 'pg-cache';
|
|
5
|
+
import { resolveProxyHops } from '../middleware/client-ip';
|
|
5
6
|
const log = new Logger('refusals');
|
|
6
7
|
/**
|
|
7
8
|
* refusals/recorder — the GraphQL lane's `RefusalRecorder`.
|
|
@@ -42,19 +43,6 @@ export const getRefusalRecorder = () => installed;
|
|
|
42
43
|
export const getRefusalRecorderStats = () => installed?.stats() ?? null;
|
|
43
44
|
/** The route half of a refusal key — the same shape admission control keys on. */
|
|
44
45
|
export const routeKeyOf = (req) => `${req.method} ${req.baseUrl ?? ''}${req.path ?? req.url ?? ''}`;
|
|
45
|
-
/**
|
|
46
|
-
* How far back through `X-Forwarded-For` to believe; mirrors admission
|
|
47
|
-
* control's resolution so the refusal source is the same address the limiter
|
|
48
|
-
* keyed on.
|
|
49
|
-
*/
|
|
50
|
-
const resolveHops = (req, configured) => {
|
|
51
|
-
if (typeof configured === 'number')
|
|
52
|
-
return configured;
|
|
53
|
-
const fromEnv = trustedProxyHops();
|
|
54
|
-
if (fromEnv > 0)
|
|
55
|
-
return fromEnv;
|
|
56
|
-
return req.app?.get('trust proxy') ? 1 : 0;
|
|
57
|
-
};
|
|
58
46
|
/**
|
|
59
47
|
* Count one GraphQL-lane refusal. Synchronous; never throws; does nothing
|
|
60
48
|
* when no recorder is installed.
|
|
@@ -70,7 +58,7 @@ export const recordRefusal = (req, reason, opts = {}) => {
|
|
|
70
58
|
routeKey: routeKeyOf(req),
|
|
71
59
|
sourceIp: opts.sourceIp !== undefined
|
|
72
60
|
? opts.sourceIp
|
|
73
|
-
: clientIpFrom(req,
|
|
61
|
+
: clientIpFrom(req, resolveProxyHops(req, opts.trustedProxyHops))
|
|
74
62
|
};
|
|
75
63
|
try {
|
|
76
64
|
recorder.record(refusal);
|
package/esm/server.js
CHANGED
|
@@ -9,7 +9,6 @@ import express from 'express';
|
|
|
9
9
|
import { closeAllCaches, graphileCache } from 'graphile-cache';
|
|
10
10
|
import graphqlUpload from 'graphql-upload';
|
|
11
11
|
import { getPgPool } from 'pg-cache';
|
|
12
|
-
import requestIp from 'request-ip';
|
|
13
12
|
import { createAgenticRouter } from './agentic';
|
|
14
13
|
import { closeDebugDatabasePools } from './diagnostics/debug-db-snapshot';
|
|
15
14
|
import { startDebugSampler } from './diagnostics/debug-sampler';
|
|
@@ -19,6 +18,7 @@ import { createApiMiddleware } from './middleware/api';
|
|
|
19
18
|
import { createAuthenticateMiddleware, getBearerToken } from './middleware/auth';
|
|
20
19
|
// Auth cookie handling is done via AuthCookiePlugin in grafserv
|
|
21
20
|
import { createCaptchaMiddleware } from './middleware/captcha';
|
|
21
|
+
import { clientIpMiddleware } from './middleware/client-ip';
|
|
22
22
|
import { parseCookieValue, rejectInvalidSessionCookieHeader, SESSION_COOKIE_NAME } from './middleware/cookie';
|
|
23
23
|
import { cors } from './middleware/cors';
|
|
24
24
|
import { errorHandler, notFoundHandler } from './middleware/error-handler';
|
|
@@ -135,7 +135,7 @@ class Server {
|
|
|
135
135
|
// Rewrite Content-Type after graphql-upload so grafserv accepts the request
|
|
136
136
|
app.use('/graphql', multipartBridge);
|
|
137
137
|
app.use(parseDomains());
|
|
138
|
-
app.use(
|
|
138
|
+
app.use(clientIpMiddleware);
|
|
139
139
|
app.use(requestIdMiddleware());
|
|
140
140
|
app.use(requestLogger);
|
|
141
141
|
app.use(api);
|
|
@@ -7,6 +7,7 @@ const express_context_1 = require("@constructive-io/express-context");
|
|
|
7
7
|
const logger_1 = require("@pgpmjs/logger");
|
|
8
8
|
const graphql_response_1 = require("../errors/graphql-response");
|
|
9
9
|
const recorder_1 = require("../refusals/recorder");
|
|
10
|
+
const client_ip_1 = require("./client-ip");
|
|
10
11
|
const log = new logger_1.Logger('admission');
|
|
11
12
|
/**
|
|
12
13
|
* admission-control — the gate that decides whether a GraphQL request starts.
|
|
@@ -47,25 +48,6 @@ const protectionOf = (req) => req.requestProtection ?? express_context_1.DEFAULT
|
|
|
47
48
|
const routeOf = (req) => `${req.method} ${req.baseUrl}${req.path}`;
|
|
48
49
|
/** Seconds for a `Retry-After` header — the smallest honest whole number. */
|
|
49
50
|
const retryAfterSeconds = (ms) => String(Math.max(Math.ceil(ms / 1000), 1));
|
|
50
|
-
/**
|
|
51
|
-
* How far back through `X-Forwarded-For` to believe.
|
|
52
|
-
*
|
|
53
|
-
* `req.clientIp` and `req.ip` are both unusable as a limiter key here: this
|
|
54
|
-
* server sets `trust proxy` to a predicate that returns true unconditionally,
|
|
55
|
-
* and `request-ip` reads the *leftmost* forwarded entry regardless — so either
|
|
56
|
-
* one hands a caller a fresh key per request for the cost of a header. The
|
|
57
|
-
* fallback to 1 hop exists because the opposite failure is just as bad: behind
|
|
58
|
-
* an ingress with no hop count configured, every caller resolves to the
|
|
59
|
-
* ingress's address and one abuser throttles the whole tenant.
|
|
60
|
-
*/
|
|
61
|
-
const resolveHops = (req, configured) => {
|
|
62
|
-
if (typeof configured === 'number')
|
|
63
|
-
return configured;
|
|
64
|
-
const fromEnv = (0, express_context_1.trustedProxyHops)();
|
|
65
|
-
if (fromEnv > 0)
|
|
66
|
-
return fromEnv;
|
|
67
|
-
return req.app?.get('trust proxy') ? 1 : 0;
|
|
68
|
-
};
|
|
69
51
|
/**
|
|
70
52
|
* Rate-limit callers per route, then admit at most `maxConcurrentRequests` of
|
|
71
53
|
* them per database into the handler chain.
|
|
@@ -82,7 +64,7 @@ const createAdmissionControlMiddleware = (options = {}) => {
|
|
|
82
64
|
const protection = protectionOf(req);
|
|
83
65
|
const databaseId = req.databaseId ?? UNKNOWN_DATABASE;
|
|
84
66
|
// ─── Per-caller rate ────────────────────────────────────────────────────
|
|
85
|
-
const hops =
|
|
67
|
+
const hops = (0, client_ip_1.resolveProxyHops)(req, options.trustedProxyHops);
|
|
86
68
|
const ip = (0, express_context_1.clientIpFrom)(req, hops);
|
|
87
69
|
const rateKey = `${databaseId}\u0000${ip}\u0000${routeOf(req)}`;
|
|
88
70
|
if (!rate.admit(rateKey, protection.rateLimitRpm, protection.rateLimitBurst)) {
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import './types';
|
|
2
|
+
import type { NextFunction, Request, Response } from 'express';
|
|
3
|
+
/**
|
|
4
|
+
* How far back through `X-Forwarded-For` to believe: an explicit count, then
|
|
5
|
+
* `TRUSTED_PROXY_HOPS` (an explicit 0 included), then 1 when `SERVER_TRUST_PROXY`
|
|
6
|
+
* puts a proxy in front.
|
|
7
|
+
*
|
|
8
|
+
* Express's `trust proxy` predicate and `req.ip` are not used as the answer:
|
|
9
|
+
* the predicate trusts every hop, which would hand back the leftmost entry —
|
|
10
|
+
* the one a caller writes. One ingress means one hop is believed.
|
|
11
|
+
*/
|
|
12
|
+
export declare const resolveProxyHops: (req: Request, configured?: number) => number;
|
|
13
|
+
/**
|
|
14
|
+
* Sets `req.clientIp` — the address `jwt.claims.ip_address`, logs and per-IP
|
|
15
|
+
* limits are keyed on — to the entry our own proxy appended, or the socket peer.
|
|
16
|
+
*/
|
|
17
|
+
export declare const clientIpMiddleware: (req: Request, _res: Response, next: NextFunction) => void;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.clientIpMiddleware = exports.resolveProxyHops = void 0;
|
|
4
|
+
require("./types"); // for Request type
|
|
5
|
+
const express_context_1 = require("@constructive-io/express-context");
|
|
6
|
+
/**
|
|
7
|
+
* How far back through `X-Forwarded-For` to believe: an explicit count, then
|
|
8
|
+
* `TRUSTED_PROXY_HOPS` (an explicit 0 included), then 1 when `SERVER_TRUST_PROXY`
|
|
9
|
+
* puts a proxy in front.
|
|
10
|
+
*
|
|
11
|
+
* Express's `trust proxy` predicate and `req.ip` are not used as the answer:
|
|
12
|
+
* the predicate trusts every hop, which would hand back the leftmost entry —
|
|
13
|
+
* the one a caller writes. One ingress means one hop is believed.
|
|
14
|
+
*/
|
|
15
|
+
const resolveProxyHops = (req, configured) => {
|
|
16
|
+
if (typeof configured === 'number')
|
|
17
|
+
return configured;
|
|
18
|
+
const fromEnv = (0, express_context_1.trustedProxyHops)();
|
|
19
|
+
if (fromEnv !== null)
|
|
20
|
+
return fromEnv;
|
|
21
|
+
return req.app?.get('trust proxy') ? 1 : 0;
|
|
22
|
+
};
|
|
23
|
+
exports.resolveProxyHops = resolveProxyHops;
|
|
24
|
+
/**
|
|
25
|
+
* Sets `req.clientIp` — the address `jwt.claims.ip_address`, logs and per-IP
|
|
26
|
+
* limits are keyed on — to the entry our own proxy appended, or the socket peer.
|
|
27
|
+
*/
|
|
28
|
+
const clientIpMiddleware = (req, _res, next) => {
|
|
29
|
+
req.clientIp = (0, express_context_1.clientIpFrom)(req, (0, exports.resolveProxyHops)(req));
|
|
30
|
+
next();
|
|
31
|
+
};
|
|
32
|
+
exports.clientIpMiddleware = clientIpMiddleware;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constructive-io/graphql-server",
|
|
3
|
-
"version": "5.33.
|
|
3
|
+
"version": "5.33.4",
|
|
4
4
|
"author": "Constructive <developers@constructive.io>",
|
|
5
5
|
"description": "Constructive GraphQL Server",
|
|
6
6
|
"main": "index.js",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"@agentic-kit/ollama": "2.16.0",
|
|
44
44
|
"@constructive-io/csrf": "^0.31.0",
|
|
45
45
|
"@constructive-io/errors": "^0.25.0",
|
|
46
|
-
"@constructive-io/express-context": "^0.35.
|
|
46
|
+
"@constructive-io/express-context": "^0.35.3",
|
|
47
47
|
"@constructive-io/graphql-env": "^3.37.2",
|
|
48
48
|
"@constructive-io/graphql-types": "^3.36.1",
|
|
49
49
|
"@constructive-io/llm-env": "^0.16.0",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
"graphile-plugin-utils": "^1.14.1",
|
|
73
73
|
"graphile-presigned-url-plugin": "^1.23.4",
|
|
74
74
|
"graphile-scoped-introspection": "^0.6.0",
|
|
75
|
-
"graphile-settings": "^6.28.
|
|
75
|
+
"graphile-settings": "^6.28.3",
|
|
76
76
|
"graphile-utils": "5.0.3",
|
|
77
77
|
"graphql": "16.13.0",
|
|
78
78
|
"graphql-upload": "^13.0.0",
|
|
@@ -82,8 +82,7 @@
|
|
|
82
82
|
"pg-env": "^1.33.0",
|
|
83
83
|
"pg-query-context": "^2.33.0",
|
|
84
84
|
"pg-sql2": "5.0.1",
|
|
85
|
-
"postgraphile": "5.1.4"
|
|
86
|
-
"request-ip": "^3.3.0"
|
|
85
|
+
"postgraphile": "5.1.4"
|
|
87
86
|
},
|
|
88
87
|
"devDependencies": {
|
|
89
88
|
"@aws-sdk/client-s3": "^3.1052.0",
|
|
@@ -92,7 +91,6 @@
|
|
|
92
91
|
"@types/express": "^5.0.6",
|
|
93
92
|
"@types/graphql-upload": "^8.0.12",
|
|
94
93
|
"@types/pg": "^8.20.4",
|
|
95
|
-
"@types/request-ip": "^0.0.41",
|
|
96
94
|
"@types/supertest": "^7.2.1",
|
|
97
95
|
"graphile-test": "5.19.0",
|
|
98
96
|
"makage": "^0.8.0",
|
|
@@ -100,5 +98,5 @@
|
|
|
100
98
|
"supertest": "^7.2.2",
|
|
101
99
|
"ts-node": "^10.9.2"
|
|
102
100
|
},
|
|
103
|
-
"gitHead": "
|
|
101
|
+
"gitHead": "7ce27b6ff591192fe5eb87e26bb3fb1e538d6505"
|
|
104
102
|
}
|
package/refusals/recorder.js
CHANGED
|
@@ -5,6 +5,7 @@ require("../middleware/types"); // for Request type
|
|
|
5
5
|
const express_context_1 = require("@constructive-io/express-context");
|
|
6
6
|
const logger_1 = require("@pgpmjs/logger");
|
|
7
7
|
const pg_cache_1 = require("pg-cache");
|
|
8
|
+
const client_ip_1 = require("../middleware/client-ip");
|
|
8
9
|
const log = new logger_1.Logger('refusals');
|
|
9
10
|
/**
|
|
10
11
|
* refusals/recorder — the GraphQL lane's `RefusalRecorder`.
|
|
@@ -49,19 +50,6 @@ exports.getRefusalRecorderStats = getRefusalRecorderStats;
|
|
|
49
50
|
/** The route half of a refusal key — the same shape admission control keys on. */
|
|
50
51
|
const routeKeyOf = (req) => `${req.method} ${req.baseUrl ?? ''}${req.path ?? req.url ?? ''}`;
|
|
51
52
|
exports.routeKeyOf = routeKeyOf;
|
|
52
|
-
/**
|
|
53
|
-
* How far back through `X-Forwarded-For` to believe; mirrors admission
|
|
54
|
-
* control's resolution so the refusal source is the same address the limiter
|
|
55
|
-
* keyed on.
|
|
56
|
-
*/
|
|
57
|
-
const resolveHops = (req, configured) => {
|
|
58
|
-
if (typeof configured === 'number')
|
|
59
|
-
return configured;
|
|
60
|
-
const fromEnv = (0, express_context_1.trustedProxyHops)();
|
|
61
|
-
if (fromEnv > 0)
|
|
62
|
-
return fromEnv;
|
|
63
|
-
return req.app?.get('trust proxy') ? 1 : 0;
|
|
64
|
-
};
|
|
65
53
|
/**
|
|
66
54
|
* Count one GraphQL-lane refusal. Synchronous; never throws; does nothing
|
|
67
55
|
* when no recorder is installed.
|
|
@@ -77,7 +65,7 @@ const recordRefusal = (req, reason, opts = {}) => {
|
|
|
77
65
|
routeKey: (0, exports.routeKeyOf)(req),
|
|
78
66
|
sourceIp: opts.sourceIp !== undefined
|
|
79
67
|
? opts.sourceIp
|
|
80
|
-
: (0, express_context_1.clientIpFrom)(req,
|
|
68
|
+
: (0, express_context_1.clientIpFrom)(req, (0, client_ip_1.resolveProxyHops)(req, opts.trustedProxyHops))
|
|
81
69
|
};
|
|
82
70
|
try {
|
|
83
71
|
recorder.record(refusal);
|
package/server.js
CHANGED
|
@@ -15,7 +15,6 @@ const express_1 = __importDefault(require("express"));
|
|
|
15
15
|
const graphile_cache_1 = require("graphile-cache");
|
|
16
16
|
const graphql_upload_1 = __importDefault(require("graphql-upload"));
|
|
17
17
|
const pg_cache_1 = require("pg-cache");
|
|
18
|
-
const request_ip_1 = __importDefault(require("request-ip"));
|
|
19
18
|
const agentic_1 = require("./agentic");
|
|
20
19
|
const debug_db_snapshot_1 = require("./diagnostics/debug-db-snapshot");
|
|
21
20
|
const debug_sampler_1 = require("./diagnostics/debug-sampler");
|
|
@@ -25,6 +24,7 @@ const api_1 = require("./middleware/api");
|
|
|
25
24
|
const auth_1 = require("./middleware/auth");
|
|
26
25
|
// Auth cookie handling is done via AuthCookiePlugin in grafserv
|
|
27
26
|
const captcha_1 = require("./middleware/captcha");
|
|
27
|
+
const client_ip_1 = require("./middleware/client-ip");
|
|
28
28
|
const cookie_1 = require("./middleware/cookie");
|
|
29
29
|
const cors_1 = require("./middleware/cors");
|
|
30
30
|
const error_handler_1 = require("./middleware/error-handler");
|
|
@@ -142,7 +142,7 @@ class Server {
|
|
|
142
142
|
// Rewrite Content-Type after graphql-upload so grafserv accepts the request
|
|
143
143
|
app.use('/graphql', multipart_bridge_1.multipartBridge);
|
|
144
144
|
app.use((0, url_domains_1.middleware)());
|
|
145
|
-
app.use(
|
|
145
|
+
app.use(client_ip_1.clientIpMiddleware);
|
|
146
146
|
app.use((0, express_context_1.requestIdMiddleware)());
|
|
147
147
|
app.use(requestLogger);
|
|
148
148
|
app.use(api);
|