Spaces:
Build error
Build error
File size: 3,317 Bytes
93c19dc | 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 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | /**
* Section 1: Backend Core - Rate Limiting Middleware
*
* Implements token bucket algorithm to prevent API abuse
* Tracks rate limits per user with configurable windows
*/
/**
* Token bucket for rate limiting
*/
interface TokenBucket {
tokens: number;
lastRefillTime: number;
}
/**
* Rate limit configuration
*/
export const RATE_LIMIT_CONFIG = {
requestsPerMinute: 30,
requestsPerHour: 500,
burstSize: 5,
};
/**
* In-memory store for rate limit buckets
* In production, use Redis for distributed rate limiting
*/
const rateLimitBuckets = new Map<string, TokenBucket>();
/**
* Clean up old buckets periodically (every 5 minutes)
*/
setInterval(() => {
const now = Date.now();
const fiveMinutesAgo = now - 5 * 60 * 1000;
rateLimitBuckets.forEach((bucket, key) => {
if (bucket.lastRefillTime < fiveMinutesAgo) {
rateLimitBuckets.delete(key);
}
});
}, 5 * 60 * 1000);
/**
* Check if a request is allowed based on rate limits
*
* @param userId - User identifier (or IP for anonymous users)
* @param requestType - Type of request (e.g., "chat", "imagine")
* @returns Object with allowed status and remaining tokens
*/
export function checkRateLimit(
userId: string,
requestType: string = "default"
): { allowed: boolean; remainingTokens: number; resetIn: number } {
const key = `${userId}:${requestType}`;
const now = Date.now();
const refillRate = RATE_LIMIT_CONFIG.requestsPerMinute / 60; // tokens per second
let bucket = rateLimitBuckets.get(key);
if (!bucket) {
bucket = {
tokens: RATE_LIMIT_CONFIG.burstSize,
lastRefillTime: now,
};
rateLimitBuckets.set(key, bucket);
}
// Calculate elapsed time since last refill
const elapsedSeconds = (now - bucket.lastRefillTime) / 1000;
// Refill tokens based on elapsed time
const tokensToAdd = elapsedSeconds * refillRate;
bucket.tokens = Math.min(
RATE_LIMIT_CONFIG.burstSize,
bucket.tokens + tokensToAdd
);
bucket.lastRefillTime = now;
// Check if request is allowed
const allowed = bucket.tokens >= 1;
if (allowed) {
bucket.tokens -= 1;
}
// Calculate reset time (when next token will be available)
const resetIn = allowed ? 0 : (1 - bucket.tokens) / refillRate * 1000;
return {
allowed,
remainingTokens: Math.floor(bucket.tokens),
resetIn: Math.ceil(resetIn),
};
}
/**
* Reset rate limit for a user (admin only)
*
* @param userId - User identifier
*/
export function resetRateLimit(userId: string): void {
const keysToDelete = Array.from(rateLimitBuckets.keys()).filter((key) =>
key.startsWith(`${userId}:`)
);
keysToDelete.forEach((key) => rateLimitBuckets.delete(key));
console.log(`Rate limit reset for user: ${userId}`);
}
/**
* Get current rate limit status for a user
*
* @param userId - User identifier
* @returns Current bucket status
*/
export function getRateLimitStatus(userId: string): {
[key: string]: { tokens: number; lastRefillTime: number };
} {
const status: { [key: string]: { tokens: number; lastRefillTime: number } } =
{};
rateLimitBuckets.forEach((bucket, key) => {
if (key.startsWith(`${userId}:`)) {
status[key] = {
tokens: bucket.tokens,
lastRefillTime: bucket.lastRefillTime,
};
}
});
return status;
}
|