@prosopo/api-express-router 3.1.42 → 3.1.44

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.42 build:cjs
2
+ > @prosopo/api-express-router@3.1.44 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 81ms
57
+ dist/cjs/middlewares/requestLoggerMiddleware.cjs 1.98 kB
58
+ ✓ built in 103ms
@@ -1,8 +1,8 @@
1
1
 
2
- > @prosopo/api-express-router@3.1.42 build:tsc
2
+ > @prosopo/api-express-router@3.1.44 build:tsc
3
3
  > tsc --build --verbose
4
4
 
5
- 10:35:23 AM - Projects in this build:
5
+ 4:36:03 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
- 10:35:23 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
+ 4:36:03 PM - Project '../../dev/config/tsconfig.json' is up to date because newest input '../../dev/config/src/index.ts' is older than output '../../dev/config/tsconfig.tsbuildinfo'
26
26
 
27
- 10:35:23 AM - Project '../util/tsconfig.json' is up to date because newest input '../util/src/url.ts' is older than output '../util/tsconfig.tsbuildinfo'
27
+ 4:36:03 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
- 10:35:24 AM - Project '../logger/tsconfig.json' is up to date because newest input '../logger/src/index.ts' is older than output '../logger/tsconfig.tsbuildinfo'
29
+ 4:36:03 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
- 10:35:24 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
+ 4:36:03 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
- 10:35:24 AM - Project '../locale/tsconfig.json' is up to date because newest input '../locale/src/translationKey.ts' is older than output '../locale/tsconfig.tsbuildinfo'
33
+ 4:36:03 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
- 10:35:24 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
+ 4:36:03 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
- 10:35:24 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
+ 4:36:03 PM - Project '../types/tsconfig.json' is up to date because newest input '../types/src/keyring/keyring/types.ts' is older than output '../types/tsconfig.tsbuildinfo'
38
38
 
39
- 10:35:24 AM - Project '../common/tsconfig.json' is up to date because newest input '../common/src/error.ts' is older than output '../common/tsconfig.tsbuildinfo'
39
+ 4:36:03 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
- 10:35:24 AM - Project '../ipinfo/tsconfig.json' is up to date because newest input '../ipinfo/src/IpInfoService.ts' is older than output '../ipinfo/tsconfig.tsbuildinfo'
41
+ 4:36:03 PM - Project '../ipinfo/tsconfig.json' is up to date because newest input '../ipinfo/src/IpInfoService.ts' is older than output '../ipinfo/tsconfig.tsbuildinfo'
42
42
 
43
- 10:35:24 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
+ 4:36:03 PM - 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'
44
44
 
45
- 10:35:24 AM - Project '../api/tsconfig.json' is up to date because newest input '../api/src/index.ts' is older than output '../api/tsconfig.tsbuildinfo'
45
+ 4:36:03 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
- 10:35:24 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
+ 4:36:03 PM - Project '../user-access-policy/tsconfig.json' is up to date because newest input '../user-access-policy/src/rule.ts' is older than output '../user-access-policy/tsconfig.tsbuildinfo'
48
48
 
49
- 10:35:24 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
+ 4:36:04 PM - Project '../types-database/tsconfig.json' is up to date because newest input '../types-database/src/index.ts' is older than output '../types-database/tsconfig.tsbuildinfo'
50
50
 
51
- 10:35:24 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
+ 4:36:04 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
- 10:35:24 AM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/index.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
53
+ 4:36:04 PM - Project '../keyring/tsconfig.json' is up to date because newest input '../keyring/src/index.ts' is older than output '../keyring/tsconfig.tsbuildinfo'
54
54
 
55
- 10:35:24 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
+ 4:36:04 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
- 10:35:24 AM - Project '../env/tsconfig.json' is up to date because newest input '../env/src/env.ts' is older than output '../env/tsconfig.tsbuildinfo'
57
+ 4:36:04 PM - Project '../env/tsconfig.json' is up to date because newest input '../env/src/index.ts' is older than output '../env/tsconfig.tsbuildinfo'
58
58
 
59
- 10:35:24 AM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
59
+ 4:36:04 PM - Project 'tsconfig.json' is out of date because output file 'tsconfig.tsbuildinfo' does not exist
60
60
 
61
- 10:35:24 AM - Building project '/home/runner/work/captcha/captcha/packages/api-express-router/tsconfig.json'...
61
+ 4:36:04 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.42 build
2
+ > @prosopo/api-express-router@3.1.44 build
3
3
  > npm run build:cross-env -- --mode ${NODE_ENV:-development}
4
4
 
5
5
 
6
- > @prosopo/api-express-router@3.1.42 build:cross-env
6
+ > @prosopo/api-express-router@3.1.44 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 137ms
61
+ dist/middlewares/requestLoggerMiddleware.js 1.86 kB
62
+ ✓ built in 127ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,59 @@
1
1
  # @prosopo/api-express-router
2
2
 
3
+ ## 3.1.44
4
+ ### Patch Changes
5
+
6
+ - Updated dependencies [b500d56]
7
+ - @prosopo/locale@3.2.7
8
+ - @prosopo/env@3.6.13
9
+ - @prosopo/common@3.1.46
10
+ - @prosopo/types@4.9.9
11
+
12
+ ## 3.1.43
13
+ ### Patch Changes
14
+
15
+ - 6abff15: Fix request-scoped logger fields leaking across concurrent captcha requests, and give every endpoint a proper request/response envelope in OpenObserve.
16
+
17
+ Three interlocking changes:
18
+
19
+ - **`Tasks.setLogger` no longer mutates `db.logger`.** `env.getDb()` returns a
20
+ process-wide singleton; overwriting `db.logger` on every request meant two
21
+ concurrent captcha submits raced, and whichever request landed second
22
+ stamped its `user`/`siteKey`/`sessionId` bindings onto the *other* request's
23
+ DB-level log lines. In practice you'd see a `PuzzleCaptcha record updated
24
+ successfully` for user A's challenge tagged with user B's account and site
25
+ key, breaking log-based forensics. `setLogger` still updates the per-request
26
+ Tasks instance and its per-request manager instances (those are safe —
27
+ they're constructed inside the Tasks constructor) but stops mutating the
28
+ shared DB. Callers in `getPoWCaptchaChallenge` and `getPuzzleCaptchaChallenge`
29
+ now pass `req.logger` directly into `new Tasks(env, req.logger)` and drop
30
+ the redundant `.setLogger(req.logger)` call that followed.
31
+
32
+ - **`requestLoggerMiddleware` now emits `Request received` and `Response sent`
33
+ envelope lines on every route** (with `method`, `path`, `status`,
34
+ `durationMs`, and the request id). Previously only `/frictionless` had a
35
+ `res.on('finish', ...)` block, so `getPow/PuzzleCaptchaChallenge`,
36
+ `submitPow/PuzzleCaptchaSolution`, `verify.ts` etc. produced no envelope in
37
+ OO — a challenge issued by one endpoint and verified by another shared
38
+ nothing you could group on. Health-probe paths (`/healthz`, `/health`,
39
+ `/readyz`) are excluded so they don't drown the stream. The middleware
40
+ also now mirrors `x-request-id` back on the outbound response so callers
41
+ downstream of the Node process can correlate without depending on Caddy.
42
+
43
+ - **`requestId` (set on the request logger via `.with({requestId})`) is
44
+ promoted to a top-level `req_id` field on the emitted JSON log record.**
45
+ OpenObserve indexes top-level fields as their own columns, so
46
+ `WHERE req_id = '…'` is now cheap; previously the id only lived inside
47
+ `data.requestId`, which flattened to `data_requestid` in OO's ingestion
48
+ and had no top-level column. `data.requestId` is preserved for backwards
49
+ compatibility with existing dashboards. Two new unit tests in
50
+ `@prosopo/logger` cover the promotion and the "absent when unset" case.
51
+ - Updated dependencies [6abff15]
52
+ - @prosopo/logger@2.0.3
53
+ - @prosopo/api-route@2.6.52
54
+ - @prosopo/common@3.1.45
55
+ - @prosopo/env@3.6.12
56
+
3
57
  ## 3.1.42
4
58
  ### Patch Changes
5
59
 
@@ -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.42",
3
+ "version": "3.1.44",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",
@@ -26,12 +26,12 @@
26
26
  },
27
27
  "dependencies": {
28
28
  "@polkadot/util": "13.5.7",
29
- "@prosopo/api-route": "2.6.51",
30
- "@prosopo/common": "3.1.44",
31
- "@prosopo/logger": "2.0.2",
32
- "@prosopo/env": "3.6.11",
33
- "@prosopo/locale": "3.2.6",
34
- "@prosopo/types": "4.9.8",
29
+ "@prosopo/api-route": "2.6.52",
30
+ "@prosopo/common": "3.1.46",
31
+ "@prosopo/logger": "2.0.3",
32
+ "@prosopo/env": "3.6.13",
33
+ "@prosopo/locale": "3.2.7",
34
+ "@prosopo/types": "4.9.9",
35
35
  "@prosopo/util": "3.3.4",
36
36
  "@prosopo/util-crypto": "13.5.30",
37
37
  "dotenv": "16.4.5",
@@ -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
  }