diff --git a/stellar-payment-platform/middleware/correlation.js b/stellar-payment-platform/middleware/correlation.js new file mode 100644 index 0000000..3e45be7 --- /dev/null +++ b/stellar-payment-platform/middleware/correlation.js @@ -0,0 +1,25 @@ +const { v4: uuidv4 } = require('uuid'); + +const CORRELATION_HEADER = 'X-Correlation-ID'; + +// #31 — Correlation ID middleware for end-to-end request tracing. +// Reuses an incoming X-Correlation-ID header when present, otherwise generates +// a fresh uuidv4. The ID is exposed on req.correlationId and echoed back on the +// response so callers and downstream services can stitch logs together. +const correlationId = (req, res, next) => { + const incoming = req.get(CORRELATION_HEADER); + const id = typeof incoming === 'string' && incoming.trim() ? incoming.trim() : uuidv4(); + + req.correlationId = id; + res.set(CORRELATION_HEADER, id); + + // Prefix request logs with the correlation ID so a single API call can be + // traced end-to-end across the backend's log output. + res.on('finish', () => { + console.log(`[Correlation ID: ${id}] ${req.method} ${req.originalUrl} ${res.statusCode}`); + }); + + next(); +}; + +module.exports = { correlationId, CORRELATION_HEADER }; diff --git a/stellar-payment-platform/package-lock.json b/stellar-payment-platform/package-lock.json index 6667cba..4fe347d 100644 --- a/stellar-payment-platform/package-lock.json +++ b/stellar-payment-platform/package-lock.json @@ -25,6 +25,7 @@ "rate-limit-redis": "^4.2.0", "redis": "^4.7.0", "sqlite3": "^5.1.7", + "uuid": "^9.0.1", "xss": "^1.0.15" }, "devDependencies": { @@ -64,7 +65,6 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -1154,7 +1154,6 @@ "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", "license": "MIT", - "peer": true, "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", @@ -1962,7 +1961,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.38", "caniuse-lite": "^1.0.30001799", @@ -3248,7 +3246,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", @@ -3295,7 +3292,6 @@ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 16" }, @@ -6256,7 +6252,6 @@ "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@prisma/config": "6.19.3", "@prisma/engines": "6.19.3" @@ -7597,6 +7592,20 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", diff --git a/stellar-payment-platform/package.json b/stellar-payment-platform/package.json index 4b5f5ac..acb49fe 100644 --- a/stellar-payment-platform/package.json +++ b/stellar-payment-platform/package.json @@ -36,6 +36,7 @@ "rate-limit-redis": "^4.2.0", "redis": "^4.7.0", "sqlite3": "^5.1.7", + "uuid": "^9.0.1", "xss": "^1.0.15" }, "devDependencies": { diff --git a/stellar-payment-platform/server.js b/stellar-payment-platform/server.js index 618fbc0..2c141d9 100644 --- a/stellar-payment-platform/server.js +++ b/stellar-payment-platform/server.js @@ -6,6 +6,7 @@ const RedisStore = require('rate-limit-redis'); const { createClient } = require('redis'); const { prisma } = require('./prismaClient'); const { scheduleCleanupJob } = require('./src/cleanup-cron'); +const { correlationId } = require('./middleware/correlation'); const Filter = require('bad-words'); const dotenv = require('dotenv'); const timeout = require('connect-timeout'); @@ -50,6 +51,9 @@ const corsOptions = { optionsSuccessStatus: 204, }; +// #31 — Attach a correlation ID to every request before anything else runs so +// all downstream middleware, handlers and logs can reference the same trace. +app.use(correlationId); const redisClient = process.env.REDIS_URL ? createClient({ url: process.env.REDIS_URL }) : null; @@ -522,11 +526,14 @@ app.use((err, _req, res, _next) => { if (statusCode === 500) { const errorId = crypto.randomUUID(); - console.error(`[Error ID: ${errorId}]`, err); + // #31 — Prefix error logs with the correlation ID so a single API call can + // be traced across every log line it produced. + console.error(`[Correlation ID: ${req.correlationId}] [Error ID: ${errorId}]`, err); return res.status(500).json({ success: false, error: 'Internal Server Error', reference_id: errorId, + correlation_id: req.correlationId }); }