Files
smom-dbis-138/services/token-aggregation/src/api/middleware/cache.ts
defiQUG 11c97777d4
Some checks failed
CI/CD Pipeline / Solidity Contracts (push) Failing after 1m11s
CI/CD Pipeline / Security Scanning (push) Has been cancelled
CI/CD Pipeline / Lint and Format (push) Has been cancelled
CI/CD Pipeline / Terraform Validation (push) Has been cancelled
CI/CD Pipeline / Kubernetes Validation (push) Has been cancelled
Validation / validate-genesis (push) Has been cancelled
Validation / validate-terraform (push) Has been cancelled
Validation / validate-kubernetes (push) Has been cancelled
Validation / validate-smart-contracts (push) Has been cancelled
Validation / validate-security (push) Has been cancelled
Validation / validate-documentation (push) Has been cancelled
Deploy ChainID 138 / Deploy ChainID 138 (push) Failing after 1m4s
HYBX OMNL TypeScript & anchor / token-aggregation build + reconcile artifact (push) Failing after 31s
OMNL reconcile anchor / Run omnl:reconcile and upload artifacts (push) Failing after 29s
Verify Deployment / Verify Deployment (push) Failing after 57s
feat(chain138): Monad CCIP, token aggregation OMNL gates, HYBX client, and PMM deploy updates.
Relay router, reserve system, oracle publisher, token-aggregation compliance middleware, and Monad deployment scripts.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-18 00:11:33 -07:00

89 lines
2.7 KiB
TypeScript

import { Request, Response, NextFunction } from 'express';
interface CacheEntry {
data: unknown;
expiresAt: number;
}
const cache = new Map<string, CacheEntry>();
const DEFAULT_TTL = 60 * 1000; // 1 minute
/** Never cache generic API error envelopes (avoids poisoning cache if status/body ever disagree). */
function looksLikeGenericErrorPayload(body: unknown): boolean {
if (body == null || typeof body !== 'object' || Array.isArray(body)) return false;
const o = body as Record<string, unknown>;
if (typeof o.error !== 'string') return false;
// Success shapes we must not treat as errors
if ('pools' in o || 'tokens' in o || 'data' in o || 'chains' in o || 'tree' in o || 'quote' in o) return false;
if (o.status === 'healthy') return false;
return true;
}
function buildCacheKey(req: Request): string {
const base = `${req.method}:${req.originalUrl}`;
if (req.method !== 'POST' && req.method !== 'PUT' && req.method !== 'PATCH') {
return base;
}
try {
const body = req.body;
if (body == null) {
return base;
}
const serialized = typeof body === 'string' ? body : JSON.stringify(body);
if (!serialized || serialized === '{}' || serialized === '[]') {
return base;
}
return `${base}:${serialized}`;
} catch {
return base;
}
}
export function cacheMiddleware(ttl: number = DEFAULT_TTL) {
return (req: Request, res: Response, next: NextFunction) => {
const bypassCache =
req.query.refresh === '1' ||
req.query.noCache === '1' ||
/\bno-cache\b|\bno-store\b/i.test(req.header('cache-control') || '');
const key = buildCacheKey(req);
const cached = bypassCache ? undefined : cache.get(key);
if (cached && cached.expiresAt > Date.now()) {
res.setHeader('X-Token-Aggregation-Cache', 'hit');
return res.json(cached.data);
}
res.setHeader('X-Token-Aggregation-Cache', bypassCache ? 'bypass' : 'miss');
// Store original json method
const originalJson = res.json.bind(res);
// Override json method to cache response
res.json = function (body: unknown) {
// Only cache successful payloads. Otherwise a 500 body gets replayed on cache hit with HTTP 200.
const okStatus = res.statusCode >= 200 && res.statusCode < 300;
if (!bypassCache && okStatus && !looksLikeGenericErrorPayload(body)) {
cache.set(key, {
data: body,
expiresAt: Date.now() + ttl,
});
}
return originalJson(body);
};
next();
};
}
export function clearCache(): void {
cache.clear();
}
export function clearCacheForPattern(pattern: string): void {
for (const key of cache.keys()) {
if (key.includes(pattern)) {
cache.delete(key);
}
}
}