Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | import { Injectable, Logger } from '@nestjs/common'; import { AppException } from '../../../common/errors/app.exception'; import { ErrorCode } from '../../../common/errors/error-codes.enum'; import { HttpStatus } from '@nestjs/common'; interface IdempotencyRecord { response: any; statusCode: number; createdAt: Date; } @Injectable() export class ApiIdempotencyService { private readonly logger = new Logger(ApiIdempotencyService.name); // In production this is backed by Redis with a TTL. In-memory here for local dev. private readonly store = new Map<string, IdempotencyRecord>(); private readonly TTL_MS = 24 * 60 * 60 * 1000; // 24 hours buildKey(tenantId: string, idempotencyKey: string): string { return `idempotency:${tenantId}:${idempotencyKey}`; } async get(key: string): Promise<IdempotencyRecord | undefined> { const record = this.store.get(key); if (!record) return undefined; if (Date.now() - record.createdAt.getTime() > this.TTL_MS) { this.store.delete(key); return undefined; } return record; } async set(key: string, response: any, statusCode: number): Promise<void> { this.store.set(key, { response, statusCode, createdAt: new Date() }); } async checkAndEnforce( tenantId: string, idempotencyKey: string, ): Promise<IdempotencyRecord | null> { const key = this.buildKey(tenantId, idempotencyKey); const existing = await this.get(key); return existing ?? null; } } |