Files
thrilltrack-explorer/supabase/functions/rate-limit-metrics/index.ts
gpt-engineer-app[bot] afe7a93f69 Migrate complex functions in batches
Batch 1 of Phase 2: migrate 3-4 edge functions to use createEdgeFunction wrapper (process-selective-approval, process-selective-rejection, rate-limit-metrics) to enable automatic error logging, CORS, auth, and reduced boilerplate; preserve existing logic where applicable and prepare for subsequent batches.
2025-11-11 19:17:21 +00:00

130 lines
3.4 KiB
TypeScript

/**
* Rate Limit Metrics API
*
* Exposes rate limiting metrics for monitoring and analysis.
* Requires admin/moderator authentication.
*/
import { createEdgeFunction } from '../_shared/edgeFunctionWrapper.ts';
import {
getRecentMetrics,
getMetricsStats,
getFunctionMetrics,
getUserMetrics,
getIPMetrics,
clearMetrics,
} from '../_shared/rateLimitMetrics.ts';
interface QueryParams {
action?: string;
limit?: string;
timeWindow?: string;
functionName?: string;
userId?: string;
clientIP?: string;
}
const handler = async (req: Request, context: { supabase: any; user: any; span: any; requestId: string }) => {
const { supabase, user } = context;
// Check if user has admin or moderator role
const { data: roles } = await supabase
.from('user_roles')
.select('role')
.eq('user_id', user.id);
const userRoles = roles?.map((r: any) => r.role) || [];
const isAuthorized = userRoles.some((role: string) =>
['admin', 'moderator', 'superuser'].includes(role)
);
if (!isAuthorized) {
throw new Error('Insufficient permissions');
}
// Parse query parameters
const url = new URL(req.url);
const action = url.searchParams.get('action') || 'stats';
const limit = parseInt(url.searchParams.get('limit') || '100', 10);
const timeWindow = parseInt(url.searchParams.get('timeWindow') || '60000', 10);
const functionName = url.searchParams.get('functionName');
const userId = url.searchParams.get('userId');
const clientIP = url.searchParams.get('clientIP');
let responseData: any;
// Route to appropriate metrics handler
switch (action) {
case 'recent':
responseData = {
metrics: getRecentMetrics(limit),
count: getRecentMetrics(limit).length,
};
break;
case 'stats':
responseData = getMetricsStats(timeWindow);
break;
case 'function':
if (!functionName) {
throw new Error('functionName parameter required for function action');
}
responseData = {
functionName,
metrics: getFunctionMetrics(functionName, limit),
count: getFunctionMetrics(functionName, limit).length,
};
break;
case 'user':
if (!userId) {
throw new Error('userId parameter required for user action');
}
responseData = {
userId,
metrics: getUserMetrics(userId, limit),
count: getUserMetrics(userId, limit).length,
};
break;
case 'ip':
if (!clientIP) {
throw new Error('clientIP parameter required for ip action');
}
responseData = {
clientIP,
metrics: getIPMetrics(clientIP, limit),
count: getIPMetrics(clientIP, limit).length,
};
break;
case 'clear':
// Only superusers can clear metrics
const isSuperuser = userRoles.includes('superuser');
if (!isSuperuser) {
throw new Error('Only superusers can clear metrics');
}
clearMetrics();
responseData = { success: true, message: 'Metrics cleared' };
break;
default:
throw new Error('Invalid action. Valid actions: recent, stats, function, user, ip, clear');
}
return responseData;
};
// Create edge function with automatic error handling, CORS, auth, and logging
createEdgeFunction(
{
name: 'rate-limit-metrics',
requireAuth: true,
corsEnabled: true,
enableTracing: false,
rateLimitTier: 'lenient'
},
handler
);