59 lines
2.4 KiB
JavaScript
59 lines
2.4 KiB
JavaScript
import { ApiError } from "../errors";
|
|
import { readIdempotency, writeIdempotency } from "../idempotency/idempotencyStore";
|
|
import { env } from "../../config/env";
|
|
import { successResponse } from "./errorMiddleware";
|
|
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;
|
|
const payload = (() => {
|
|
if (cachedPayload &&
|
|
typeof cachedPayload === "object" &&
|
|
"data" in cachedPayload &&
|
|
"request_id" in cachedPayload &&
|
|
"timestamp" in cachedPayload) {
|
|
const typed = cachedPayload;
|
|
typed.request_id = req.requestId;
|
|
typed.timestamp = new Date().toISOString();
|
|
return typed;
|
|
}
|
|
return cachedPayload;
|
|
})();
|
|
return _res.status(cachedStatus).json(payload);
|
|
}
|
|
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) {
|
|
const responsePayload = payload &&
|
|
typeof payload === "object" &&
|
|
"data" in payload &&
|
|
"request_id" in payload &&
|
|
"timestamp" in payload
|
|
? successResponse(req, payload.data)
|
|
: payload;
|
|
writeIdempotency(options.scope, idempotencyKey, {
|
|
response: responsePayload,
|
|
statusCode,
|
|
at: Date.now()
|
|
}, options.ttlMs || env.IDEMPOTENCY_TTL_MS);
|
|
return originalJson(responsePayload);
|
|
};
|
|
next();
|
|
};
|
|
}
|