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;
}