import { ApiError } from "../errors"; import { readIdempotency, writeIdempotency } from "../idempotency/idempotencyStore"; import { env } from "../../config/env"; export function idempotency(options) { return function idempotencyMiddleware(req, _res, next) { const idempotencyKey = req.header("idempotency-key"); if (!idempotencyKey) { if (options.required === false) { return next(); } return next(new ApiError("DUPLICATE_REQUEST", "Missing Idempotency-Key", 400)); } const cached = readIdempotency(options.scope, idempotencyKey); if (cached) { const cachedPayload = cached.response ?? cached; const cachedStatus = cached.statusCode || 200; return _res.status(cachedStatus).json(cachedPayload); } req.body = { ...(req.body || {}), __idempotencyKey: idempotencyKey }; const originalJson = _res.json.bind(_res); const originalStatus = _res.status.bind(_res); let statusCode = 200; _res.status = function statusWithStore(code) { statusCode = code; return originalStatus(code); }; _res.json = function jsonWithStore(payload) { writeIdempotency(options.scope, idempotencyKey, { response: payload, statusCode, at: Date.now() }, options.ttlMs || env.IDEMPOTENCY_TTL_MS); return originalJson(payload); }; next(); }; }