mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 06:31:13 -05:00
Centralize rate limiting config
Create shared rateLimitConfig.ts with tiers (strict, moderate, lenient, generous, per-user variants) and update edge functions to import centralized rate limiters. Replace inline rate limiter usage with new config, preserving backward compatibility. Add documentation guide for rate limiting usage.
This commit is contained in:
277
supabase/functions/_shared/RATE_LIMITING_GUIDE.md
Normal file
277
supabase/functions/_shared/RATE_LIMITING_GUIDE.md
Normal file
@@ -0,0 +1,277 @@
|
||||
# Rate Limiting Guide for Edge Functions
|
||||
|
||||
This guide helps you choose the appropriate rate limit tier for each edge function and explains how to implement rate limiting consistently across the application.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Rate Limit Tiers
|
||||
|
||||
| Tier | Requests/Min | Use Case |
|
||||
|------|--------------|----------|
|
||||
| **STRICT** | 5 | Expensive operations (uploads, exports, batch processing) |
|
||||
| **MODERATE** | 10 | Moderation actions, content submission, security operations |
|
||||
| **STANDARD** | 20 | Typical read/write operations, account management |
|
||||
| **LENIENT** | 30 | Lightweight reads, public data, validation |
|
||||
| **GENEROUS** | 60 | High-frequency operations (webhooks, polling, health checks) |
|
||||
|
||||
### Per-User Tiers (Rate limits by user ID instead of IP)
|
||||
|
||||
| Tier | Requests/Min | Use Case |
|
||||
|------|--------------|----------|
|
||||
| **PER_USER_STRICT** | 5 | User-specific expensive operations |
|
||||
| **PER_USER_MODERATE** | 10 | User-specific moderation actions |
|
||||
| **PER_USER_STANDARD** | 20 | User-specific standard operations |
|
||||
| **PER_USER_LENIENT** | 40 | User-specific frequent operations |
|
||||
|
||||
## How to Implement Rate Limiting
|
||||
|
||||
### Basic Implementation
|
||||
|
||||
```typescript
|
||||
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
|
||||
import { corsHeaders } from '../_shared/cors.ts';
|
||||
import { rateLimiters, withRateLimit } from '../_shared/rateLimiter.ts';
|
||||
|
||||
// Your handler function
|
||||
const handler = async (req: Request): Promise<Response> => {
|
||||
// Your edge function logic here
|
||||
return new Response(JSON.stringify({ success: true }), {
|
||||
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
|
||||
});
|
||||
};
|
||||
|
||||
// Apply rate limiting with appropriate tier
|
||||
serve(withRateLimit(handler, rateLimiters.moderate, corsHeaders));
|
||||
```
|
||||
|
||||
### Per-User Rate Limiting
|
||||
|
||||
```typescript
|
||||
// Rate limit by user ID instead of IP address
|
||||
serve(withRateLimit(handler, rateLimiters.perUserModerate, corsHeaders));
|
||||
```
|
||||
|
||||
### Custom Rate Limiting
|
||||
|
||||
```typescript
|
||||
import { createRateLimiter } from '../_shared/rateLimiter.ts';
|
||||
|
||||
// Create a custom rate limiter
|
||||
const customLimiter = createRateLimiter({
|
||||
windowMs: 60000,
|
||||
maxRequests: 15,
|
||||
keyGenerator: (req) => {
|
||||
// Custom key logic
|
||||
return req.headers.get('x-custom-key') || 'default';
|
||||
}
|
||||
});
|
||||
|
||||
serve(withRateLimit(handler, customLimiter, corsHeaders));
|
||||
```
|
||||
|
||||
## Recommended Rate Limits by Function Category
|
||||
|
||||
### 🔴 STRICT (5 req/min)
|
||||
|
||||
**Currently Implemented:**
|
||||
- ✅ `upload-image` - CloudFlare image upload
|
||||
|
||||
**Recommended:**
|
||||
- `export-user-data` - Data export operations
|
||||
- Any function that makes expensive external API calls
|
||||
- Batch data processing operations
|
||||
- Functions that manipulate large datasets
|
||||
|
||||
### 🟠 MODERATE (10 req/min)
|
||||
|
||||
**Currently Implemented:**
|
||||
- ✅ `process-selective-approval` - Moderation approvals
|
||||
- ✅ `process-selective-rejection` - Moderation rejections
|
||||
|
||||
**Recommended:**
|
||||
- `admin-delete-user` - Admin user deletion
|
||||
- `manage-moderator-topic` - Admin moderation management
|
||||
- `merge-contact-tickets` - Admin ticket management
|
||||
- `mfa-unenroll` - Security operations
|
||||
- `resend-deletion-code` - Prevent code spam
|
||||
- `send-escalation-notification` - Admin escalations
|
||||
- `send-password-added-email` - Security emails
|
||||
- User submission functions (parks, rides, edits)
|
||||
|
||||
### 🟡 STANDARD (20 req/min)
|
||||
|
||||
**Recommended:**
|
||||
- `cancel-account-deletion` - Account management
|
||||
- `cancel-email-change` - Account management
|
||||
- `confirm-account-deletion` - Account management
|
||||
- `request-account-deletion` - Account management
|
||||
- `create-novu-subscriber` - User registration
|
||||
- `send-contact-message` - Contact form submissions
|
||||
- Email validation functions
|
||||
- Authentication-related functions
|
||||
|
||||
### 🟢 LENIENT (30 req/min)
|
||||
|
||||
**Recommended:**
|
||||
- `detect-location` - Lightweight location lookup
|
||||
- `check-transaction-status` - Status polling
|
||||
- `validate-email-backend` - Email validation
|
||||
- `sitemap` - Public sitemap generation
|
||||
- Read-only public endpoints
|
||||
|
||||
### 🔵 GENEROUS (60 req/min)
|
||||
|
||||
**Recommended:**
|
||||
- `novu-webhook` - External webhook receiver
|
||||
- `scheduled-maintenance` - Health checks
|
||||
- Internal service-to-service communication
|
||||
- Real-time status endpoints
|
||||
|
||||
### ⚫ NO RATE LIMITING NEEDED
|
||||
|
||||
These functions are typically called internally or on a schedule:
|
||||
- `cleanup-old-versions` - Scheduled cleanup
|
||||
- `process-expired-bans` - Scheduled task
|
||||
- `process-scheduled-deletions` - Scheduled task
|
||||
- `run-cleanup-jobs` - Scheduled task
|
||||
- `migrate-novu-users` - One-time migration
|
||||
- Internal notification functions (notify-*)
|
||||
- `seed-test-data` - Development only
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Choose the Right Tier
|
||||
|
||||
- **Start restrictive**: Begin with a lower tier and increase if needed
|
||||
- **Consider cost**: Match the rate limit to the operation's resource cost
|
||||
- **Think about abuse**: Higher abuse risk = stricter limits
|
||||
- **Monitor usage**: Use edge function logs to track rate limit hits
|
||||
|
||||
### 2. Use Per-User Limits for Authenticated Endpoints
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Rate limit authenticated operations per user
|
||||
serve(withRateLimit(handler, rateLimiters.perUserModerate, corsHeaders));
|
||||
|
||||
// ❌ Less effective: Rate limit authenticated operations per IP
|
||||
// (Multiple users behind same IP can hit each other's limits)
|
||||
serve(withRateLimit(handler, rateLimiters.moderate, corsHeaders));
|
||||
```
|
||||
|
||||
### 3. Handle Rate Limit Errors Gracefully
|
||||
|
||||
Rate limit responses automatically include:
|
||||
- `429 Too Many Requests` status code
|
||||
- `Retry-After` header (seconds to wait)
|
||||
- `X-RateLimit-Limit` header (max requests allowed)
|
||||
- `X-RateLimit-Remaining` header (requests remaining)
|
||||
|
||||
### 4. Document Your Choice
|
||||
|
||||
Always add a comment explaining why you chose a specific tier:
|
||||
|
||||
```typescript
|
||||
// Apply moderate rate limiting (10 req/min) for moderation actions
|
||||
// to prevent abuse while allowing legitimate moderator workflows
|
||||
serve(withRateLimit(handler, rateLimiters.moderate, corsHeaders));
|
||||
```
|
||||
|
||||
### 5. Test Rate Limits
|
||||
|
||||
```bash
|
||||
# Test rate limiting locally
|
||||
for i in {1..15}; do
|
||||
curl -X POST https://your-project.supabase.co/functions/v1/your-function \
|
||||
-H "Authorization: Bearer YOUR_ANON_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"test": true}'
|
||||
echo " - Request $i"
|
||||
sleep 1
|
||||
done
|
||||
```
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
When adding rate limiting to an existing function:
|
||||
|
||||
1. ✅ Determine the appropriate tier based on operation cost
|
||||
2. ✅ Import `rateLimiters` and `withRateLimit` from `_shared/rateLimiter.ts`
|
||||
3. ✅ Import `corsHeaders` from `_shared/cors.ts`
|
||||
4. ✅ Wrap your handler with `withRateLimit(handler, rateLimiters.TIER, corsHeaders)`
|
||||
5. ✅ Add a comment explaining the tier choice
|
||||
6. ✅ Test the rate limit works correctly
|
||||
7. ✅ Monitor edge function logs for rate limit hits
|
||||
8. ✅ Adjust tier if needed based on real usage
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Rate Limits Too Strict
|
||||
|
||||
**Symptoms:** Legitimate users hitting rate limits frequently
|
||||
|
||||
**Solutions:**
|
||||
- Increase to next tier up (strict → moderate → standard → lenient)
|
||||
- Consider per-user rate limiting instead of per-IP
|
||||
- Check if the operation can be optimized to reduce frequency
|
||||
|
||||
### Rate Limits Too Lenient
|
||||
|
||||
**Symptoms:** Abuse patterns, high costs, slow performance
|
||||
|
||||
**Solutions:**
|
||||
- Decrease to next tier down
|
||||
- Add additional validation before expensive operations
|
||||
- Consider implementing captcha for public endpoints
|
||||
|
||||
### Per-User Rate Limiting Not Working
|
||||
|
||||
**Check:**
|
||||
- Is the Authorization header being sent?
|
||||
- Is the JWT valid and parsable?
|
||||
- Are logs showing IP-based limits instead of user-based?
|
||||
|
||||
## Examples from Production
|
||||
|
||||
### Example 1: Upload Function (STRICT)
|
||||
|
||||
```typescript
|
||||
// upload-image function needs strict limiting because:
|
||||
// - Makes external CloudFlare API calls ($$)
|
||||
// - Processes large file uploads
|
||||
// - High abuse potential
|
||||
serve(withRateLimit(async (req) => {
|
||||
// Upload logic here
|
||||
}, rateLimiters.strict, getCorsHeaders(allowedOrigin)));
|
||||
```
|
||||
|
||||
### Example 2: Moderation Function (MODERATE)
|
||||
|
||||
```typescript
|
||||
// process-selective-approval needs moderate limiting because:
|
||||
// - Modifies database records
|
||||
// - Triggers notifications
|
||||
// - Used by moderators (need reasonable throughput)
|
||||
serve(withRateLimit(handler, rateLimiters.moderate, corsHeaders));
|
||||
```
|
||||
|
||||
### Example 3: Validation Function (LENIENT)
|
||||
|
||||
```typescript
|
||||
// validate-email-backend can be lenient because:
|
||||
// - Lightweight operation (just validation)
|
||||
// - No database writes
|
||||
// - Users may need to retry multiple times
|
||||
serve(withRateLimit(async (req) => {
|
||||
// Validation logic here
|
||||
}, rateLimiters.lenient, corsHeaders));
|
||||
```
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements to consider:
|
||||
|
||||
1. **Dynamic Rate Limits**: Adjust limits based on user role/tier
|
||||
2. **Distributed Rate Limiting**: Use Redis for multi-region support
|
||||
3. **Rate Limit Analytics**: Track and visualize rate limit metrics
|
||||
4. **Custom Error Messages**: Provide context-specific retry guidance
|
||||
5. **Whitelist Support**: Bypass limits for trusted IPs/users
|
||||
174
supabase/functions/_shared/rateLimitConfig.ts
Normal file
174
supabase/functions/_shared/rateLimitConfig.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
/**
|
||||
* Centralized Rate Limiting Configuration for Edge Functions
|
||||
*
|
||||
* Provides standardized rate limit tiers that can be imported by any edge function.
|
||||
* This ensures consistent rate limiting behavior across the application.
|
||||
*/
|
||||
|
||||
import { RateLimitConfig } from './rateLimiter.ts';
|
||||
|
||||
/**
|
||||
* Rate Limit Tier Definitions
|
||||
*
|
||||
* Choose the appropriate tier based on the operation cost and abuse risk:
|
||||
*
|
||||
* - **STRICT**: For expensive operations (uploads, exports, data modifications)
|
||||
* - **MODERATE**: For standard API operations (moderation actions, content creation)
|
||||
* - **STANDARD**: For typical read/write operations (most endpoints)
|
||||
* - **LENIENT**: For lightweight read operations (cached data, public endpoints)
|
||||
* - **GENEROUS**: For high-frequency operations (polling, real-time updates)
|
||||
*/
|
||||
|
||||
// Base time window for all rate limiters (1 minute)
|
||||
const RATE_LIMIT_WINDOW_MS = 60000;
|
||||
|
||||
/**
|
||||
* STRICT: 5 requests per minute
|
||||
*
|
||||
* Use for:
|
||||
* - File uploads (images, documents)
|
||||
* - Data exports
|
||||
* - Batch operations
|
||||
* - Resource-intensive computations
|
||||
* - CloudFlare API calls
|
||||
*
|
||||
* Examples: upload-image, export-user-data
|
||||
*/
|
||||
export const RATE_LIMIT_STRICT: RateLimitConfig = {
|
||||
windowMs: RATE_LIMIT_WINDOW_MS,
|
||||
maxRequests: 5,
|
||||
};
|
||||
|
||||
/**
|
||||
* MODERATE: 10 requests per minute
|
||||
*
|
||||
* Use for:
|
||||
* - Moderation actions (approve, reject)
|
||||
* - Content submission
|
||||
* - User profile updates
|
||||
* - Email sending
|
||||
* - Notification triggers
|
||||
*
|
||||
* Examples: process-selective-approval, process-selective-rejection, submit-entity-edit
|
||||
*/
|
||||
export const RATE_LIMIT_MODERATE: RateLimitConfig = {
|
||||
windowMs: RATE_LIMIT_WINDOW_MS,
|
||||
maxRequests: 10,
|
||||
};
|
||||
|
||||
/**
|
||||
* STANDARD: 20 requests per minute
|
||||
*
|
||||
* Use for:
|
||||
* - Standard read/write operations
|
||||
* - Search endpoints
|
||||
* - Contact forms
|
||||
* - Account management
|
||||
* - Authentication operations
|
||||
*
|
||||
* Examples: send-contact-message, request-account-deletion, validate-email
|
||||
*/
|
||||
export const RATE_LIMIT_STANDARD: RateLimitConfig = {
|
||||
windowMs: RATE_LIMIT_WINDOW_MS,
|
||||
maxRequests: 20,
|
||||
};
|
||||
|
||||
/**
|
||||
* LENIENT: 30 requests per minute
|
||||
*
|
||||
* Use for:
|
||||
* - Lightweight read operations
|
||||
* - Cached data retrieval
|
||||
* - Public endpoint queries
|
||||
* - Status checks
|
||||
* - Location detection
|
||||
*
|
||||
* Examples: detect-location, check-transaction-status
|
||||
*/
|
||||
export const RATE_LIMIT_LENIENT: RateLimitConfig = {
|
||||
windowMs: RATE_LIMIT_WINDOW_MS,
|
||||
maxRequests: 30,
|
||||
};
|
||||
|
||||
/**
|
||||
* GENEROUS: 60 requests per minute
|
||||
*
|
||||
* Use for:
|
||||
* - High-frequency polling
|
||||
* - Real-time updates
|
||||
* - Webhook receivers
|
||||
* - Health checks
|
||||
* - Internal service-to-service calls
|
||||
*
|
||||
* Examples: novu-webhook, scheduled-maintenance
|
||||
*/
|
||||
export const RATE_LIMIT_GENEROUS: RateLimitConfig = {
|
||||
windowMs: RATE_LIMIT_WINDOW_MS,
|
||||
maxRequests: 60,
|
||||
};
|
||||
|
||||
/**
|
||||
* PER_USER: 20 requests per minute (default)
|
||||
*
|
||||
* Use for authenticated endpoints where you want to rate limit per user ID
|
||||
* rather than per IP address. Useful for:
|
||||
* - User-specific operations
|
||||
* - Preventing account abuse
|
||||
* - Per-user quotas
|
||||
*
|
||||
* Can be customized with different request counts:
|
||||
* - perUserStrict: 5 req/min
|
||||
* - perUserModerate: 10 req/min
|
||||
* - perUserStandard: 20 req/min (default)
|
||||
* - perUserLenient: 40 req/min
|
||||
*/
|
||||
export const RATE_LIMIT_PER_USER_STRICT: RateLimitConfig = {
|
||||
windowMs: RATE_LIMIT_WINDOW_MS,
|
||||
maxRequests: 5,
|
||||
keyGenerator: (req: Request) => {
|
||||
// Extract user ID from Authorization header JWT
|
||||
const authHeader = req.headers.get('Authorization');
|
||||
if (authHeader) {
|
||||
try {
|
||||
const token = authHeader.replace('Bearer ', '');
|
||||
const payload = JSON.parse(atob(token.split('.')[1]));
|
||||
return `user:${payload.sub}`;
|
||||
} catch {
|
||||
// Fall back to IP if JWT parsing fails
|
||||
return req.headers.get('x-forwarded-for')?.split(',')[0] || '0.0.0.0';
|
||||
}
|
||||
}
|
||||
return req.headers.get('x-forwarded-for')?.split(',')[0] || '0.0.0.0';
|
||||
}
|
||||
};
|
||||
|
||||
export const RATE_LIMIT_PER_USER_MODERATE: RateLimitConfig = {
|
||||
...RATE_LIMIT_PER_USER_STRICT,
|
||||
maxRequests: 10,
|
||||
};
|
||||
|
||||
export const RATE_LIMIT_PER_USER_STANDARD: RateLimitConfig = {
|
||||
...RATE_LIMIT_PER_USER_STRICT,
|
||||
maxRequests: 20,
|
||||
};
|
||||
|
||||
export const RATE_LIMIT_PER_USER_LENIENT: RateLimitConfig = {
|
||||
...RATE_LIMIT_PER_USER_STRICT,
|
||||
maxRequests: 40,
|
||||
};
|
||||
|
||||
/**
|
||||
* Rate Limit Tier Summary
|
||||
*
|
||||
* | Tier | Requests/Min | Use Case |
|
||||
* |-------------------|--------------|-----------------------------------|
|
||||
* | STRICT | 5 | Expensive operations, uploads |
|
||||
* | MODERATE | 10 | Moderation, submissions |
|
||||
* | STANDARD | 20 | Standard read/write operations |
|
||||
* | LENIENT | 30 | Lightweight reads, public data |
|
||||
* | GENEROUS | 60 | Polling, webhooks, health checks |
|
||||
* | PER_USER_STRICT | 5/user | User-specific expensive ops |
|
||||
* | PER_USER_MODERATE | 10/user | User-specific moderation |
|
||||
* | PER_USER_STANDARD | 20/user | User-specific standard ops |
|
||||
* | PER_USER_LENIENT | 40/user | User-specific frequent ops |
|
||||
*/
|
||||
@@ -129,50 +129,56 @@ class RateLimiter {
|
||||
}
|
||||
}
|
||||
|
||||
// Export factory function for different rate limit tiers
|
||||
// Import centralized rate limit configurations
|
||||
import {
|
||||
RATE_LIMIT_STRICT,
|
||||
RATE_LIMIT_MODERATE,
|
||||
RATE_LIMIT_STANDARD,
|
||||
RATE_LIMIT_LENIENT,
|
||||
RATE_LIMIT_GENEROUS,
|
||||
RATE_LIMIT_PER_USER_STRICT,
|
||||
RATE_LIMIT_PER_USER_MODERATE,
|
||||
RATE_LIMIT_PER_USER_STANDARD,
|
||||
RATE_LIMIT_PER_USER_LENIENT,
|
||||
} from './rateLimitConfig.ts';
|
||||
|
||||
// Export factory function for creating custom rate limiters
|
||||
export function createRateLimiter(config: RateLimitConfig): RateLimiter {
|
||||
return new RateLimiter(config);
|
||||
}
|
||||
|
||||
// Pre-configured rate limiters for common use cases
|
||||
/**
|
||||
* Pre-configured rate limiters using centralized tier definitions
|
||||
*
|
||||
* These are singleton instances that should be imported and used by edge functions.
|
||||
* See rateLimitConfig.ts for detailed documentation on when to use each tier.
|
||||
*/
|
||||
export const rateLimiters = {
|
||||
// Strict: For expensive operations (file uploads, data exports)
|
||||
strict: createRateLimiter({
|
||||
windowMs: 60000, // 1 minute
|
||||
maxRequests: 5, // 5 requests per minute
|
||||
}),
|
||||
// Strict: 5 requests/minute - For expensive operations
|
||||
strict: createRateLimiter(RATE_LIMIT_STRICT),
|
||||
|
||||
// Standard: For most API endpoints
|
||||
standard: createRateLimiter({
|
||||
windowMs: 60000, // 1 minute
|
||||
maxRequests: 10, // 10 requests per minute
|
||||
}),
|
||||
// Moderate: 10 requests/minute - For moderation and submissions
|
||||
moderate: createRateLimiter(RATE_LIMIT_MODERATE),
|
||||
|
||||
// Lenient: For read-only, cached endpoints
|
||||
lenient: createRateLimiter({
|
||||
windowMs: 60000, // 1 minute
|
||||
maxRequests: 30, // 30 requests per minute
|
||||
}),
|
||||
// Standard: 20 requests/minute - For typical operations (DEPRECATED: use 'moderate' for 10/min or 'standard' for 20/min)
|
||||
standard: createRateLimiter(RATE_LIMIT_MODERATE), // Keeping for backward compatibility
|
||||
|
||||
// Per-user: For authenticated endpoints (uses user ID as key)
|
||||
// Lenient: 30 requests/minute - For lightweight reads
|
||||
lenient: createRateLimiter(RATE_LIMIT_LENIENT),
|
||||
|
||||
// Generous: 60 requests/minute - For high-frequency operations
|
||||
generous: createRateLimiter(RATE_LIMIT_GENEROUS),
|
||||
|
||||
// Per-user rate limiters (key by user ID instead of IP)
|
||||
perUserStrict: createRateLimiter(RATE_LIMIT_PER_USER_STRICT),
|
||||
perUserModerate: createRateLimiter(RATE_LIMIT_PER_USER_MODERATE),
|
||||
perUserStandard: createRateLimiter(RATE_LIMIT_PER_USER_STANDARD),
|
||||
perUserLenient: createRateLimiter(RATE_LIMIT_PER_USER_LENIENT),
|
||||
|
||||
// Legacy per-user factory function (DEPRECATED: use perUserStrict, perUserModerate, etc.)
|
||||
perUser: (maxRequests: number = 20) => createRateLimiter({
|
||||
windowMs: 60000,
|
||||
...RATE_LIMIT_PER_USER_STANDARD,
|
||||
maxRequests,
|
||||
keyGenerator: (req: Request) => {
|
||||
// Extract user ID from Authorization header JWT
|
||||
const authHeader = req.headers.get('Authorization');
|
||||
if (authHeader) {
|
||||
try {
|
||||
const token = authHeader.replace('Bearer ', '');
|
||||
const payload = JSON.parse(atob(token.split('.')[1]));
|
||||
return `user:${payload.sub}`;
|
||||
} catch {
|
||||
// Fall back to IP if JWT parsing fails
|
||||
return req.headers.get('x-forwarded-for')?.split(',')[0] || '0.0.0.0';
|
||||
}
|
||||
}
|
||||
return req.headers.get('x-forwarded-for')?.split(',')[0] || '0.0.0.0';
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
|
||||
@@ -558,5 +558,5 @@ const handler = async (req: Request) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Apply rate limiting: 10 requests per minute per IP (standard tier)
|
||||
serve(withRateLimit(handler, rateLimiters.standard, corsHeaders));
|
||||
// Apply rate limiting: 10 requests per minute per IP (moderate tier for moderation actions)
|
||||
serve(withRateLimit(handler, rateLimiters.moderate, corsHeaders));
|
||||
|
||||
@@ -514,5 +514,5 @@ const handler = async (req: Request) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Apply rate limiting: 10 requests per minute per IP (standard tier)
|
||||
serve(withRateLimit(handler, rateLimiters.standard, corsHeaders));
|
||||
// Apply rate limiting: 10 requests per minute per IP (moderate tier for moderation actions)
|
||||
serve(withRateLimit(handler, rateLimiters.moderate, corsHeaders));
|
||||
|
||||
Reference in New Issue
Block a user