Add files via upload

This commit is contained in:
Egor
2026-01-18 06:53:17 +03:00
committed by GitHub
parent 84e351ceb4
commit 039e75586d

117
src/utils/rateLimit.ts Normal file
View File

@@ -0,0 +1,117 @@
/**
* Client-side rate limiting utilities to prevent spam requests
*/
interface RateLimitEntry {
count: number
resetTime: number
}
const rateLimitStore = new Map<string, RateLimitEntry>()
/**
* Check if action is rate limited
* @param key Unique key for the action (e.g., 'payment', 'ticket-create')
* @param maxRequests Maximum requests allowed in the time window
* @param windowMs Time window in milliseconds
* @returns true if action is allowed, false if rate limited
*/
export function checkRateLimit(
key: string,
maxRequests: number = 3,
windowMs: number = 10000
): boolean {
const now = Date.now()
const entry = rateLimitStore.get(key)
// Clean up expired entry
if (entry && now >= entry.resetTime) {
rateLimitStore.delete(key)
}
const currentEntry = rateLimitStore.get(key)
if (!currentEntry) {
// First request - allow and start tracking
rateLimitStore.set(key, {
count: 1,
resetTime: now + windowMs,
})
return true
}
if (currentEntry.count >= maxRequests) {
// Rate limit exceeded
return false
}
// Increment counter and allow
currentEntry.count++
return true
}
/**
* Get remaining time until rate limit resets (in seconds)
*/
export function getRateLimitResetTime(key: string): number {
const entry = rateLimitStore.get(key)
if (!entry) return 0
const remaining = entry.resetTime - Date.now()
return remaining > 0 ? Math.ceil(remaining / 1000) : 0
}
/**
* Reset rate limit for a key (e.g., after successful action)
*/
export function resetRateLimit(key: string): void {
rateLimitStore.delete(key)
}
/**
* Debounce function - delays execution until after wait ms have elapsed
*/
export function debounce<T extends (...args: unknown[]) => unknown>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
let timeoutId: ReturnType<typeof setTimeout> | null = null
return (...args: Parameters<T>) => {
if (timeoutId) {
clearTimeout(timeoutId)
}
timeoutId = setTimeout(() => {
func(...args)
timeoutId = null
}, wait)
}
}
/**
* Throttle function - ensures function is called at most once per limit ms
*/
export function throttle<T extends (...args: unknown[]) => unknown>(
func: T,
limit: number
): (...args: Parameters<T>) => void {
let inThrottle = false
return (...args: Parameters<T>) => {
if (!inThrottle) {
func(...args)
inThrottle = true
setTimeout(() => {
inThrottle = false
}, limit)
}
}
}
// Rate limit keys for different actions
export const RATE_LIMIT_KEYS = {
PAYMENT: 'payment',
TICKET_CREATE: 'ticket-create',
TICKET_REPLY: 'ticket-reply',
PROMO_ACTIVATE: 'promo-activate',
WHEEL_SPIN: 'wheel-spin',
} as const