@glec/mcp-server
Version:
GLEC API MCP Server for Claude Desktop - Carbon emission calculation for logistics and transportation
131 lines • 4.41 kB
JavaScript
import { createHash } from 'crypto';
// In-memory rate limiter (for single instance)
class MemoryRateLimiter {
limits = new Map();
config;
constructor(config) {
this.config = config;
// Clean up expired entries every minute
setInterval(() => {
this.cleanup();
}, 60000);
}
cleanup() {
const now = Date.now();
for (const [key, entry] of this.limits.entries()) {
if (now > entry.resetTime) {
this.limits.delete(key);
}
}
}
generateKey(identifier) {
if (this.config.keyGenerator) {
return this.config.keyGenerator(identifier);
}
return createHash('md5').update(identifier).digest('hex');
}
async checkLimit(identifier) {
const key = this.generateKey(identifier);
const now = Date.now();
const entry = this.limits.get(key);
if (!entry || now > entry.resetTime) {
// Create new entry or reset expired entry
this.limits.set(key, {
count: 1,
resetTime: now + this.config.windowMs
});
return {
allowed: true,
remaining: this.config.maxRequests - 1,
resetTime: now + this.config.windowMs,
retryAfter: 0
};
}
if (entry.count >= this.config.maxRequests) {
return {
allowed: false,
remaining: 0,
resetTime: entry.resetTime,
retryAfter: Math.ceil((entry.resetTime - now) / 1000)
};
}
// Increment counter
entry.count++;
this.limits.set(key, entry);
return {
allowed: true,
remaining: this.config.maxRequests - entry.count,
resetTime: entry.resetTime,
retryAfter: 0
};
}
}
// Rate limiter factory
export class RateLimiterFactory {
static instance;
static getInstance() {
if (!this.instance) {
this.instance = new MemoryRateLimiter({
windowMs: 60000, // 1 minute
maxRequests: 100, // 100 requests per minute
keyGenerator: (identifier) => {
// Include timestamp for better distribution
const timestamp = Math.floor(Date.now() / 60000); // 1-minute buckets
return createHash('md5').update(`${identifier}:${timestamp}`).digest('hex');
}
});
}
return this.instance;
}
static createCustomLimiter(config) {
return new MemoryRateLimiter(config);
}
}
// Rate limit decorator
export function rateLimit(identifier, customConfig) {
return function (target, propertyName, descriptor) {
const method = descriptor.value;
const limiter = customConfig
? RateLimiterFactory.createCustomLimiter({ windowMs: 60000, maxRequests: 100, ...customConfig })
: RateLimiterFactory.getInstance();
descriptor.value = async function (...args) {
const result = await limiter.checkLimit(identifier);
if (!result.allowed) {
throw new Error(`Rate limit exceeded. Try again in ${result.retryAfter} seconds.`);
}
return method.apply(this, args);
};
};
}
// Rate limit middleware
export const createRateLimitMiddleware = (config) => {
const limiter = new MemoryRateLimiter(config);
return async (identifier) => {
return await limiter.checkLimit(identifier);
};
};
// Default rate limiters
export const defaultRateLimiters = {
// General API calls
api: RateLimiterFactory.createCustomLimiter({
windowMs: 60000, // 1 minute
maxRequests: 100
}),
// Emission calculations (more restrictive)
calculation: RateLimiterFactory.createCustomLimiter({
windowMs: 60000, // 1 minute
maxRequests: 50
}),
// Report generation (most restrictive)
report: RateLimiterFactory.createCustomLimiter({
windowMs: 300000, // 5 minutes
maxRequests: 10
}),
// Shipper management
shipper: RateLimiterFactory.createCustomLimiter({
windowMs: 60000, // 1 minute
maxRequests: 20
})
};
export default RateLimiterFactory;
//# sourceMappingURL=rateLimiter.js.map