website poc
This commit is contained in:
89
apps/website/lib/rate-limit.ts
Normal file
89
apps/website/lib/rate-limit.ts
Normal file
@@ -0,0 +1,89 @@
|
||||
import { kv } from '@vercel/kv';
|
||||
|
||||
/**
|
||||
* Rate limit configuration
|
||||
*/
|
||||
const RATE_LIMIT_WINDOW = 60 * 60 * 1000; // 1 hour in milliseconds
|
||||
const MAX_REQUESTS_PER_WINDOW = 5;
|
||||
|
||||
/**
|
||||
* Rate limit key prefix
|
||||
*/
|
||||
const RATE_LIMIT_PREFIX = 'ratelimit:signup:';
|
||||
|
||||
/**
|
||||
* Check if an IP address has exceeded rate limits
|
||||
* @param identifier - IP address or unique identifier
|
||||
* @returns Object with allowed status and retry information
|
||||
*/
|
||||
export async function checkRateLimit(identifier: string): Promise<{
|
||||
allowed: boolean;
|
||||
remaining: number;
|
||||
resetAt: number;
|
||||
}> {
|
||||
const key = `${RATE_LIMIT_PREFIX}${identifier}`;
|
||||
const now = Date.now();
|
||||
|
||||
try {
|
||||
// Get current count
|
||||
const count = await kv.get<number>(key) || 0;
|
||||
|
||||
if (count >= MAX_REQUESTS_PER_WINDOW) {
|
||||
// Get TTL to determine reset time
|
||||
const ttl = await kv.ttl(key);
|
||||
const resetAt = now + (ttl * 1000);
|
||||
|
||||
return {
|
||||
allowed: false,
|
||||
remaining: 0,
|
||||
resetAt,
|
||||
};
|
||||
}
|
||||
|
||||
// Increment counter
|
||||
const newCount = count + 1;
|
||||
|
||||
if (count === 0) {
|
||||
// First request - set with expiry
|
||||
await kv.set(key, newCount, {
|
||||
px: RATE_LIMIT_WINDOW,
|
||||
});
|
||||
} else {
|
||||
// Subsequent request - increment without changing TTL
|
||||
await kv.incr(key);
|
||||
}
|
||||
|
||||
// Calculate reset time
|
||||
const ttl = await kv.ttl(key);
|
||||
const resetAt = now + (ttl * 1000);
|
||||
|
||||
return {
|
||||
allowed: true,
|
||||
remaining: MAX_REQUESTS_PER_WINDOW - newCount,
|
||||
resetAt,
|
||||
};
|
||||
} catch (error) {
|
||||
// If rate limiting fails, allow the request
|
||||
console.error('Rate limit check failed:', error);
|
||||
return {
|
||||
allowed: true,
|
||||
remaining: MAX_REQUESTS_PER_WINDOW,
|
||||
resetAt: now + RATE_LIMIT_WINDOW,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get client IP from request headers
|
||||
*/
|
||||
export function getClientIp(request: Request): string {
|
||||
// Try various headers that might contain the IP
|
||||
const headers = request.headers;
|
||||
|
||||
return (
|
||||
headers.get('x-forwarded-for')?.split(',')[0]?.trim() ||
|
||||
headers.get('x-real-ip') ||
|
||||
headers.get('cf-connecting-ip') || // Cloudflare
|
||||
'unknown'
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user