Files
gpt-engineer-app[bot] de921a5fcf Migrate remaining edge functions to wrapper
Refactor process-expired-bans, detect-location, detect-anomalies, rate-limit-metrics, and collect-metrics to use createEdgeFunction wrapper with standardized error handling, tracing, and reduced boilerplate. Update signatures to receive { supabase, span, requestId } (and user where applicable), replace manual logging with span events, remove per-function boilerplate, and ensure consistent wrapper configuration (cors, auth, rate limits, and tracing).
2025-11-11 20:30:24 +00:00

110 lines
3.1 KiB
TypeScript

import { serve } from 'https://deno.land/std@0.190.0/http/server.ts';
import { createEdgeFunction, type EdgeFunctionContext } from '../_shared/edgeFunctionWrapper.ts';
import { corsHeaders } from '../_shared/cors.ts';
import {
getRecentMetrics,
getMetricsStats,
getFunctionMetrics,
getUserMetrics,
getIPMetrics,
clearMetrics,
} from '../_shared/rateLimitMetrics.ts';
const handler = async (req: Request, { supabase, user, span, requestId }: EdgeFunctionContext) => {
// 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;
};
serve(createEdgeFunction({
name: 'rate-limit-metrics',
requireAuth: true,
corsHeaders,
enableTracing: true,
rateLimitTier: 'lenient',
}, handler));