mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-25 04:31:13 -05:00
Add audit logging for admin settings changes, rate limit config updates, anomaly detection config changes (skipped due to no UI), and version cleanup settings updates. Implement logging via central logAdminAction helper and integrate into AdminSettings, VersionCleanupSettings, and RateLimitAlerts mutations (create, update, delete).
249 lines
7.0 KiB
TypeScript
249 lines
7.0 KiB
TypeScript
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import { supabase } from '@/integrations/supabase/client';
|
|
import { toast } from 'sonner';
|
|
|
|
export interface AlertConfig {
|
|
id: string;
|
|
metric_type: 'block_rate' | 'total_requests' | 'unique_ips' | 'function_specific';
|
|
threshold_value: number;
|
|
time_window_ms: number;
|
|
function_name?: string;
|
|
enabled: boolean;
|
|
created_at: string;
|
|
updated_at: string;
|
|
}
|
|
|
|
export interface Alert {
|
|
id: string;
|
|
config_id: string;
|
|
metric_type: string;
|
|
metric_value: number;
|
|
threshold_value: number;
|
|
time_window_ms: number;
|
|
function_name?: string;
|
|
alert_message: string;
|
|
resolved_at?: string;
|
|
created_at: string;
|
|
}
|
|
|
|
export function useAlertConfigs() {
|
|
return useQuery({
|
|
queryKey: ['rateLimitAlertConfigs'],
|
|
queryFn: async () => {
|
|
const { data, error } = await supabase
|
|
.from('rate_limit_alert_config')
|
|
.select('*')
|
|
.order('metric_type');
|
|
|
|
if (error) throw error;
|
|
return data as AlertConfig[];
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useAlertHistory(limit: number = 50) {
|
|
return useQuery({
|
|
queryKey: ['rateLimitAlerts', limit],
|
|
queryFn: async () => {
|
|
const { data, error } = await supabase
|
|
.from('rate_limit_alerts')
|
|
.select('*')
|
|
.order('created_at', { ascending: false })
|
|
.limit(limit);
|
|
|
|
if (error) throw error;
|
|
return data as Alert[];
|
|
},
|
|
refetchInterval: 30000, // Refetch every 30 seconds
|
|
});
|
|
}
|
|
|
|
export function useUnresolvedAlerts() {
|
|
return useQuery({
|
|
queryKey: ['rateLimitAlertsUnresolved'],
|
|
queryFn: async () => {
|
|
const { data, error } = await supabase
|
|
.from('rate_limit_alerts')
|
|
.select('*')
|
|
.is('resolved_at', null)
|
|
.order('created_at', { ascending: false });
|
|
|
|
if (error) throw error;
|
|
return data as Alert[];
|
|
},
|
|
refetchInterval: 15000, // Refetch every 15 seconds
|
|
});
|
|
}
|
|
|
|
export function useUpdateAlertConfig() {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async ({ id, updates }: { id: string; updates: Partial<AlertConfig> }) => {
|
|
// Fetch old config for audit log
|
|
const { data: oldConfig } = await supabase
|
|
.from('rate_limit_alert_config')
|
|
.select('*')
|
|
.eq('id', id)
|
|
.single();
|
|
|
|
const { data, error } = await supabase
|
|
.from('rate_limit_alert_config')
|
|
.update(updates)
|
|
.eq('id', id)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) throw error;
|
|
return { data, oldConfig };
|
|
},
|
|
onSuccess: async ({ data, oldConfig }) => {
|
|
queryClient.invalidateQueries({ queryKey: ['rateLimitAlertConfigs'] });
|
|
|
|
// Log to audit trail
|
|
const { logAdminAction } = await import('@/lib/adminActionAuditHelpers');
|
|
await logAdminAction('rate_limit_config_updated', {
|
|
config_id: data.id,
|
|
metric_type: data.metric_type,
|
|
old_threshold: oldConfig?.threshold_value,
|
|
new_threshold: data.threshold_value,
|
|
old_enabled: oldConfig?.enabled,
|
|
new_enabled: data.enabled,
|
|
function_name: data.function_name,
|
|
});
|
|
|
|
toast.success('Alert configuration updated');
|
|
},
|
|
onError: (error) => {
|
|
toast.error(`Failed to update alert config: ${error.message}`);
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useCreateAlertConfig() {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (config: Omit<AlertConfig, 'id' | 'created_at' | 'updated_at'>) => {
|
|
const { data, error } = await supabase
|
|
.from('rate_limit_alert_config')
|
|
.insert(config)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) throw error;
|
|
return data;
|
|
},
|
|
onSuccess: async (data) => {
|
|
queryClient.invalidateQueries({ queryKey: ['rateLimitAlertConfigs'] });
|
|
|
|
// Log to audit trail
|
|
const { logAdminAction } = await import('@/lib/adminActionAuditHelpers');
|
|
await logAdminAction('rate_limit_config_created', {
|
|
config_id: data.id,
|
|
metric_type: data.metric_type,
|
|
threshold_value: data.threshold_value,
|
|
time_window_ms: data.time_window_ms,
|
|
function_name: data.function_name,
|
|
enabled: data.enabled,
|
|
});
|
|
|
|
toast.success('Alert configuration created');
|
|
},
|
|
onError: (error) => {
|
|
toast.error(`Failed to create alert config: ${error.message}`);
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useDeleteAlertConfig() {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (id: string) => {
|
|
// Fetch config details before deletion for audit log
|
|
const { data: config } = await supabase
|
|
.from('rate_limit_alert_config')
|
|
.select('*')
|
|
.eq('id', id)
|
|
.single();
|
|
|
|
const { error } = await supabase
|
|
.from('rate_limit_alert_config')
|
|
.delete()
|
|
.eq('id', id);
|
|
|
|
if (error) throw error;
|
|
return config;
|
|
},
|
|
onSuccess: async (config) => {
|
|
queryClient.invalidateQueries({ queryKey: ['rateLimitAlertConfigs'] });
|
|
|
|
// Log to audit trail
|
|
if (config) {
|
|
const { logAdminAction } = await import('@/lib/adminActionAuditHelpers');
|
|
await logAdminAction('rate_limit_config_deleted', {
|
|
config_id: config.id,
|
|
metric_type: config.metric_type,
|
|
threshold_value: config.threshold_value,
|
|
time_window_ms: config.time_window_ms,
|
|
function_name: config.function_name,
|
|
});
|
|
}
|
|
|
|
toast.success('Alert configuration deleted');
|
|
},
|
|
onError: (error) => {
|
|
toast.error(`Failed to delete alert config: ${error.message}`);
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useResolveAlert() {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: async (id: string) => {
|
|
// Fetch full alert details before resolving
|
|
const { data: alert, error: fetchError } = await supabase
|
|
.from('rate_limit_alerts')
|
|
.select('*')
|
|
.eq('id', id)
|
|
.single();
|
|
|
|
if (fetchError) throw fetchError;
|
|
|
|
// Resolve the alert
|
|
const { data, error } = await supabase
|
|
.from('rate_limit_alerts')
|
|
.update({ resolved_at: new Date().toISOString() })
|
|
.eq('id', id)
|
|
.select()
|
|
.single();
|
|
|
|
if (error) throw error;
|
|
|
|
// Log to audit trail
|
|
const { logAdminAction } = await import('@/lib/adminActionAuditHelpers');
|
|
await logAdminAction('rate_limit_alert_resolved', {
|
|
alert_id: id,
|
|
metric_type: alert.metric_type,
|
|
metric_value: alert.metric_value,
|
|
threshold_value: alert.threshold_value,
|
|
function_name: alert.function_name,
|
|
time_window_ms: alert.time_window_ms,
|
|
});
|
|
|
|
return data;
|
|
},
|
|
onSuccess: () => {
|
|
queryClient.invalidateQueries({ queryKey: ['rateLimitAlerts'] });
|
|
queryClient.invalidateQueries({ queryKey: ['rateLimitAlertsUnresolved'] });
|
|
toast.success('Alert resolved');
|
|
},
|
|
onError: (error) => {
|
|
toast.error(`Failed to resolve alert: ${error.message}`);
|
|
},
|
|
});
|
|
}
|