@prosopo/api-express-router 3.1.41 → 3.1.43

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,5 +1,5 @@
1
1
 
2
- > @prosopo/api-express-router@3.1.41 build:cjs
2
+ > @prosopo/api-express-router@3.1.43 build:cjs
3
3
  > NODE_ENV=${NODE_ENV:-development}; vite build --config vite.cjs.config.ts --mode $NODE_ENV
4
4
 
5
5
  ViteCommonJSConfig: .
@@ -51,8 +51,8 @@ Build end
51
51
  rendering chunks...
52
52
  dist/cjs/errorHandler.cjs 0.49 kB
53
53
  dist/cjs/apiExpressRouterFactory.cjs 0.92 kB
54
- dist/cjs/middlewares/requestLoggerMiddleware.cjs 1.06 kB
55
54
  dist/cjs/endpointAdapter/apiExpressDefaultEndpointAdapter.cjs 1.12 kB
56
55
  dist/cjs/index.cjs 1.16 kB
57
56
  dist/cjs/middlewares/authMiddleware.cjs 1.86 kB
58
- ✓ built in 109ms
57
+ dist/cjs/middlewares/requestLoggerMiddleware.cjs 1.98 kB
58
+ ✓ built in 128ms
@@ -1,8 +1,8 @@
1
1
 
2
- > @prosopo/api-express-router@3.1.41 build:tsc
2
+ > @prosopo/api-express-router@3.1.43 build:tsc
3
3
  > tsc --build --verbose
4
4
 
5
- 8:57:30 AM - Projects in this build:
5
+ 1:22:15 PM - Projects in this build:
6
6
  * ../../dev/config/tsconfig.json
7
7
  * ../util/tsconfig.json
8
8
  * ../logger/tsconfig.json
@@ -22,41 +22,41 @@
22
22
  * ../env/tsconfig.json
23
23
  * tsconfig.json
24
24
 
25
- 8:57:30 AM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/webpack/webpack.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
25
+ 1:22:15 PM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/webpack/webpack.config.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
26
26
 
27
- 8:57:30 AM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
27
+ 1:22:15 PM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
28
28
 
29
- 8:57:30 AM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
29
+ 1:22:15 PM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
30
30
 
31
- 8:57:30 AM - Project '../api-route/tsconfig.json' is up to date because newest input '../api-route/src/apiRoutes.ts' is older than output '../api-route/tsconfig.tsbuildinfo'
31
+ 1:22:15 PM - Project '../api-route/tsconfig.json' is up to date because newest input '../api-route/src/apiRoutes.ts' is older than output '../api-route/tsconfig.tsbuildinfo'
32
32
 
33
- 8:57:30 AM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
33
+ 1:22:15 PM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
34
34
 
35
- 8:57:30 AM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
35
+ 1:22:15 PM - Project '../util-crypto/tsconfig.json' is up to date because newest input '../util-crypto/src/types.ts' is older than output '../util-crypto/tsconfig.tsbuildinfo'
36
36
 
37
- 8:57:31 AM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/provider/api.ts' is older than output '../types/tsconfig.tsbuildinfo'
37
+ 1:22:15 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/provider/api.ts' is older than output '../types/tsconfig.tsbuildinfo'
38
38
 
39
- 8:57:31 AM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/index.ts' is older than output '../common/tsconfig.tsbuildinfo'
39
+ 1:22:15 PM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/index.ts' is older than output '../common/tsconfig.tsbuildinfo'
40
40
 
41
- 8:57:31 AM - Project '../ipinfo/tsconfig.json' is up to date because newest input '../ipinfo/src/IpInfoService.ts' is older than output '../ipinfo/tsconfig.tsbuildinfo'
41
+ 1:22:15 PM - Project '../ipinfo/tsconfig.json' is up to date because newest input '../ipinfo/src/index.ts' is older than output '../ipinfo/tsconfig.tsbuildinfo'
42
42
 
43
- 8:57:31 AM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/index.ts' is older than output '../redis-client/tsconfig.tsbuildinfo'
43
+ 1:22:15 PM - Project '../redis-client/tsconfig.json' is up to date because newest input '../redis-client/src/tests/testRedisConnection.ts' is older than output '../redis-client/tsconfig.tsbuildinfo'
44
44
 
45
- 8:57:31 AM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
45
+ 1:22:15 PM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
46
46
 
47
- 8:57:31 AM - Project '../user-access-policy/tsconfig.json' is up to date because newest input '../user-access-policy/src/transformRule.ts' is older than output '../user-access-policy/tsconfig.tsbuildinfo'
47
+ 1:22:15 PM - Project '../user-access-policy/tsconfig.json' is up to date because newest input '../user-access-policy/src/transformRule.ts' is older than output '../user-access-policy/tsconfig.tsbuildinfo'
48
48
 
49
- 8:57:31 AM - Project '../types-database/tsconfig.json' is up to date because newest input '../types-database/src/types/captcha.ts' is older than output '../types-database/tsconfig.tsbuildinfo'
49
+ 1:22:15 PM - Project '../types-database/tsconfig.json' is up to date because newest input '../types-database/src/types/bannedDomain.ts' is older than output '../types-database/tsconfig.tsbuildinfo'
50
50
 
51
- 8:57:31 AM - Project '../database/tsconfig.json' is up to date because newest input '../database/src/tests/mongoGlobalSetup.ts' is older than output '../database/tsconfig.tsbuildinfo'
51
+ 1:22:15 PM - Project '../database/tsconfig.json' is up to date because newest input '../database/src/tests/mongoGlobalSetup.ts' is older than output '../database/tsconfig.tsbuildinfo'
52
52
 
53
- 8:57:31 AM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/keyring/keyring.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
53
+ 1:22:15 PM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/pair/decode.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
54
54
 
55
- 8:57:31 AM - Project '../types-env/tsconfig.json' is up to date because newest input '../types-env/src/env.ts' is older than output '../types-env/tsconfig.tsbuildinfo'
55
+ 1:22:15 PM - Project '../types-env/tsconfig.json' is up to date because newest input '../types-env/src/env.ts' is older than output '../types-env/tsconfig.tsbuildinfo'
56
56
 
57
- 8:57:31 AM - Project '../env/tsconfig.json' is up to date because newest input '../env/src/env.ts' is older than output '../env/tsconfig.tsbuildinfo'
57
+ 1:22:15 PM - Project '../env/tsconfig.json' is up to date because newest input '../env/src/env.ts' is older than output '../env/tsconfig.tsbuildinfo'
58
58
 
59
- 8:57:31 AM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
59
+ 1:22:15 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
60
60
 
61
- 8:57:31 AM - Building project '/home/runner/work/captcha/captcha/packages/api-express-router/tsconfig.json'...
61
+ 1:22:15 PM - Building project '/home/runner/work/captcha/captcha/packages/api-express-router/tsconfig.json'...
62
62
 
@@ -1,9 +1,9 @@
1
1
 
2
- > @prosopo/api-express-router@3.1.41 build
2
+ > @prosopo/api-express-router@3.1.43 build
3
3
  > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
4
 
5
5
 
6
- > @prosopo/api-express-router@3.1.41 build:cross-env
6
+ > @prosopo/api-express-router@3.1.43 build:cross-env
7
7
  > vite build --config vite.esm.config.ts --mode production
8
8
 
9
9
  ViteEsmConfig: .
@@ -56,7 +56,7 @@ rendering chunks...
56
56
  dist/errorHandler.js 0.39 kB
57
57
  dist/index.js 0.78 kB
58
58
  dist/apiExpressRouterFactory.js 0.78 kB
59
- dist/middlewares/requestLoggerMiddleware.js 0.95 kB
60
59
  dist/endpointAdapter/apiExpressDefaultEndpointAdapter.js 1.01 kB
61
60
  dist/middlewares/authMiddleware.js 1.73 kB
62
- ✓ built in 219ms
61
+ dist/middlewares/requestLoggerMiddleware.js 1.86 kB
62
+ ✓ built in 177ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,62 @@
1
1
  # @prosopo/api-express-router
2
2
 
3
+ ## 3.1.43
4
+ ### Patch Changes
5
+
6
+ - 6abff15: Fix request-scoped logger fields leaking across concurrent captcha requests, and give every endpoint a proper request/response envelope in OpenObserve.
7
+
8
+ Three interlocking changes:
9
+
10
+ - **`Tasks.setLogger` no longer mutates `db.logger`.** `env.getDb()` returns a
11
+ process-wide singleton; overwriting `db.logger` on every request meant two
12
+ concurrent captcha submits raced, and whichever request landed second
13
+ stamped its `user`/`siteKey`/`sessionId` bindings onto the *other* request's
14
+ DB-level log lines. In practice you'd see a `PuzzleCaptcha record updated
15
+ successfully` for user A's challenge tagged with user B's account and site
16
+ key, breaking log-based forensics. `setLogger` still updates the per-request
17
+ Tasks instance and its per-request manager instances (those are safe —
18
+ they're constructed inside the Tasks constructor) but stops mutating the
19
+ shared DB. Callers in `getPoWCaptchaChallenge` and `getPuzzleCaptchaChallenge`
20
+ now pass `req.logger` directly into `new Tasks(env, req.logger)` and drop
21
+ the redundant `.setLogger(req.logger)` call that followed.
22
+
23
+ - **`requestLoggerMiddleware` now emits `Request received` and `Response sent`
24
+ envelope lines on every route** (with `method`, `path`, `status`,
25
+ `durationMs`, and the request id). Previously only `/frictionless` had a
26
+ `res.on('finish', ...)` block, so `getPow/PuzzleCaptchaChallenge`,
27
+ `submitPow/PuzzleCaptchaSolution`, `verify.ts` etc. produced no envelope in
28
+ OO — a challenge issued by one endpoint and verified by another shared
29
+ nothing you could group on. Health-probe paths (`/healthz`, `/health`,
30
+ `/readyz`) are excluded so they don't drown the stream. The middleware
31
+ also now mirrors `x-request-id` back on the outbound response so callers
32
+ downstream of the Node process can correlate without depending on Caddy.
33
+
34
+ - **`requestId` (set on the request logger via `.with({requestId})`) is
35
+ promoted to a top-level `req_id` field on the emitted JSON log record.**
36
+ OpenObserve indexes top-level fields as their own columns, so
37
+ `WHERE req_id = '…'` is now cheap; previously the id only lived inside
38
+ `data.requestId`, which flattened to `data_requestid` in OO's ingestion
39
+ and had no top-level column. `data.requestId` is preserved for backwards
40
+ compatibility with existing dashboards. Two new unit tests in
41
+ `@prosopo/logger` cover the promotion and the "absent when unset" case.
42
+ - Updated dependencies [6abff15]
43
+ - @prosopo/logger@2.0.3
44
+ - @prosopo/api-route@2.6.52
45
+ - @prosopo/common@3.1.45
46
+ - @prosopo/env@3.6.12
47
+
48
+ ## 3.1.42
49
+ ### Patch Changes
50
+
51
+ - Updated dependencies [550d20a]
52
+ - Updated dependencies [85e8857]
53
+ - @prosopo/env@3.6.11
54
+ - @prosopo/types@4.9.8
55
+ - @prosopo/util@3.3.4
56
+ - @prosopo/common@3.1.44
57
+ - @prosopo/logger@2.0.2
58
+ - @prosopo/api-route@2.6.51
59
+
3
60
  ## 3.1.41
4
61
  ### Patch Changes
5
62
 
@@ -8,12 +8,13 @@ const getHeaderValue = (req, headerName) => {
8
8
  }
9
9
  return void 0;
10
10
  };
11
+ const HEALTH_CHECK_PATHS = /* @__PURE__ */ new Set(["/healthz", "/health", "/readyz"]);
11
12
  function requestLoggerMiddleware(env) {
12
13
  return (req, res, next) => {
13
14
  const requestId = req.headers["x-request-id"] || `e-${uuid.v4()}`;
14
15
  const user = getHeaderValue(req, "prosopo-user");
15
16
  const siteKey = getHeaderValue(req, "prosopo-site-key");
16
- const sessionId = req.body?.sessionId ? req.body.sessionId : null;
17
+ const sessionId = req.body?.sessionId ? req.body.sessionId : void 0;
17
18
  const logger$1 = logger.getLogger(
18
19
  logger.parseLogLevel(env.config.logLevel),
19
20
  "provider:request"
@@ -25,6 +26,37 @@ function requestLoggerMiddleware(env) {
25
26
  });
26
27
  req.logger = logger$1;
27
28
  req.requestId = requestId;
29
+ res.setHeader("x-request-id", requestId);
30
+ if (HEALTH_CHECK_PATHS.has(req.path)) {
31
+ next();
32
+ return;
33
+ }
34
+ const startNs = process.hrtime.bigint();
35
+ logger$1.info(() => ({
36
+ msg: "Request received",
37
+ data: {
38
+ method: req.method,
39
+ path: req.path
40
+ }
41
+ }));
42
+ let finished = false;
43
+ const emitFinish = (outcome) => {
44
+ if (finished) return;
45
+ finished = true;
46
+ const durationMs = Number(process.hrtime.bigint() - startNs) / 1e6;
47
+ logger$1.info(() => ({
48
+ msg: "Response sent",
49
+ data: {
50
+ method: req.method,
51
+ path: req.path,
52
+ status: res.statusCode,
53
+ durationMs,
54
+ outcome
55
+ }
56
+ }));
57
+ };
58
+ res.on("finish", () => emitFinish("finish"));
59
+ res.on("close", () => emitFinish("close"));
28
60
  next();
29
61
  };
30
62
  }
@@ -1 +1 @@
1
- {"version":3,"file":"requestLoggerMiddleware.d.ts","sourceRoot":"","sources":["../../src/middlewares/requestLoggerMiddleware.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAa/D,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,mBAAmB,SAClD,OAAO,OAAO,QAAQ,QAAQ,YAAY,UAwBvD"}
1
+ {"version":3,"file":"requestLoggerMiddleware.d.ts","sourceRoot":"","sources":["../../src/middlewares/requestLoggerMiddleware.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAe/D,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,mBAAmB,SAClD,OAAO,OAAO,QAAQ,QAAQ,YAAY,UAuEvD"}
@@ -6,12 +6,13 @@ const getHeaderValue = (req, headerName) => {
6
6
  }
7
7
  return void 0;
8
8
  };
9
+ const HEALTH_CHECK_PATHS = /* @__PURE__ */ new Set(["/healthz", "/health", "/readyz"]);
9
10
  function requestLoggerMiddleware(env) {
10
11
  return (req, res, next) => {
11
12
  const requestId = req.headers["x-request-id"] || `e-${v4()}`;
12
13
  const user = getHeaderValue(req, "prosopo-user");
13
14
  const siteKey = getHeaderValue(req, "prosopo-site-key");
14
- const sessionId = req.body?.sessionId ? req.body.sessionId : null;
15
+ const sessionId = req.body?.sessionId ? req.body.sessionId : void 0;
15
16
  const logger = getLogger(
16
17
  parseLogLevel(env.config.logLevel),
17
18
  "provider:request"
@@ -23,6 +24,37 @@ function requestLoggerMiddleware(env) {
23
24
  });
24
25
  req.logger = logger;
25
26
  req.requestId = requestId;
27
+ res.setHeader("x-request-id", requestId);
28
+ if (HEALTH_CHECK_PATHS.has(req.path)) {
29
+ next();
30
+ return;
31
+ }
32
+ const startNs = process.hrtime.bigint();
33
+ logger.info(() => ({
34
+ msg: "Request received",
35
+ data: {
36
+ method: req.method,
37
+ path: req.path
38
+ }
39
+ }));
40
+ let finished = false;
41
+ const emitFinish = (outcome) => {
42
+ if (finished) return;
43
+ finished = true;
44
+ const durationMs = Number(process.hrtime.bigint() - startNs) / 1e6;
45
+ logger.info(() => ({
46
+ msg: "Response sent",
47
+ data: {
48
+ method: req.method,
49
+ path: req.path,
50
+ status: res.statusCode,
51
+ durationMs,
52
+ outcome
53
+ }
54
+ }));
55
+ };
56
+ res.on("finish", () => emitFinish("finish"));
57
+ res.on("close", () => emitFinish("close"));
26
58
  next();
27
59
  };
28
60
  }
@@ -1 +1 @@
1
- {"version":3,"file":"requestLoggerMiddleware.js","sourceRoot":"","sources":["../../src/middlewares/requestLoggerMiddleware.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAE3D,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AAEpC,MAAM,cAAc,GAAG,CACtB,GAAY,EACZ,UAAkB,EACG,EAAE;IACvB,IAAI,UAAU,IAAI,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;QAC1D,OAAO,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC3C,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC,CAAC;AAEF,MAAM,UAAU,uBAAuB,CAAC,GAAwB;IAC/D,OAAO,CAAC,GAAY,EAAE,GAAa,EAAE,IAAkB,EAAE,EAAE;QAC1D,MAAM,SAAS,GACb,GAAG,CAAC,OAAO,CAAC,cAAc,CAAY,IAAI,KAAK,MAAM,EAAE,EAAE,CAAC;QAC5D,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;QACxD,MAAM,SAAS,GAAG,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC;QAElE,MAAM,MAAM,GAAG,SAAS,CACvB,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,EAClC,kBAAkB,CAClB,CAAC,IAAI,CAAC;YACN,SAAS;YACT,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzB,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/B,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACnC,CAAC,CAAC;QAGH,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;QACpB,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC;QAG1B,IAAI,EAAE,CAAC;IACR,CAAC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"requestLoggerMiddleware.js","sourceRoot":"","sources":["../../src/middlewares/requestLoggerMiddleware.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prosopo/api-express-router",
3
- "version": "3.1.41",
3
+ "version": "3.1.43",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -26,13 +26,13 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@polkadot/util": "13.5.7",
29
- "@prosopo/api-route": "2.6.50",
30
- "@prosopo/common": "3.1.43",
31
- "@prosopo/logger": "2.0.1",
32
- "@prosopo/env": "3.6.10",
29
+ "@prosopo/api-route": "2.6.52",
30
+ "@prosopo/common": "3.1.45",
31
+ "@prosopo/logger": "2.0.3",
32
+ "@prosopo/env": "3.6.12",
33
33
  "@prosopo/locale": "3.2.6",
34
- "@prosopo/types": "4.9.7",
35
- "@prosopo/util": "3.3.3",
34
+ "@prosopo/types": "4.9.8",
35
+ "@prosopo/util": "3.3.4",
36
36
  "@prosopo/util-crypto": "13.5.30",
37
37
  "dotenv": "16.4.5",
38
38
  "express": "4.21.2",
@@ -27,13 +27,18 @@ const getHeaderValue = (
27
27
  return undefined;
28
28
  };
29
29
 
30
+ const HEALTH_CHECK_PATHS = new Set(["/healthz", "/health", "/readyz"]);
31
+
30
32
  export function requestLoggerMiddleware(env: ProviderEnvironment) {
31
33
  return (req: Request, res: Response, next: NextFunction) => {
34
+ // Honour an inbound `x-request-id` (Caddy/upstream proxy assigns one and
35
+ // we want the same value to link Caddy access logs to Node app logs).
36
+ // Fall back to `e-<uuid>` when the upstream didn't set one.
32
37
  const requestId =
33
- (req.headers["x-request-id"] as string) || `e-${uuidv4()}`; // use prefix to differentiate from other IDs
38
+ (req.headers["x-request-id"] as string) || `e-${uuidv4()}`;
34
39
  const user = getHeaderValue(req, "prosopo-user");
35
40
  const siteKey = getHeaderValue(req, "prosopo-site-key");
36
- const sessionId = req.body?.sessionId ? req.body.sessionId : null;
41
+ const sessionId = req.body?.sessionId ? req.body.sessionId : undefined;
37
42
 
38
43
  const logger = getLogger(
39
44
  parseLogLevel(env.config.logLevel),
@@ -45,11 +50,55 @@ export function requestLoggerMiddleware(env: ProviderEnvironment) {
45
50
  ...(sessionId ? { sessionId } : {}),
46
51
  });
47
52
 
48
- // Attach logger to the request
49
53
  req.logger = logger;
50
54
  req.requestId = requestId;
51
55
 
52
- // Continue to next middleware
56
+ // Mirror the requestId back to the client so downstream systems can
57
+ // correlate their own logs to ours.
58
+ res.setHeader("x-request-id", requestId);
59
+
60
+ // Skip request/response envelope logging for high-volume health probes
61
+ // so they don't drown out useful captcha-endpoint entries.
62
+ if (HEALTH_CHECK_PATHS.has(req.path)) {
63
+ next();
64
+ return;
65
+ }
66
+
67
+ // Emit a single request-received line so every endpoint has an entry
68
+ // log in OpenObserve even when the handler itself doesn't log. Kept at
69
+ // info level; drop to debug if the volume becomes a problem.
70
+ const startNs = process.hrtime.bigint();
71
+ logger.info(() => ({
72
+ msg: "Request received",
73
+ data: {
74
+ method: req.method,
75
+ path: req.path,
76
+ },
77
+ }));
78
+
79
+ // Log a response-finished line. `finish` fires when the response was
80
+ // sent successfully; `close` fires when the client disconnects before
81
+ // the response was fully sent. Guard against both firing (once() would
82
+ // only cover a single event source).
83
+ let finished = false;
84
+ const emitFinish = (outcome: "finish" | "close") => {
85
+ if (finished) return;
86
+ finished = true;
87
+ const durationMs = Number(process.hrtime.bigint() - startNs) / 1e6;
88
+ logger.info(() => ({
89
+ msg: "Response sent",
90
+ data: {
91
+ method: req.method,
92
+ path: req.path,
93
+ status: res.statusCode,
94
+ durationMs,
95
+ outcome,
96
+ },
97
+ }));
98
+ };
99
+ res.on("finish", () => emitFinish("finish"));
100
+ res.on("close", () => emitFinish("close"));
101
+
53
102
  next();
54
103
  };
55
104
  }