import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { useNavigate } from 'react-router-dom'; import toast from 'react-hot-toast'; import { rateLimitApi, type ActiveRateLimit } from '../../services/api'; import Card from '../../components/ui/Card'; import Button from '../../components/ui/Button'; import { ArrowLeft, RefreshCw, ShieldOff, Unlock } from 'lucide-react'; function formatTime(iso: string): string { const d = new Date(iso); const diffMin = Math.floor((Date.now() - d.getTime()) / 60000); if (diffMin < 1) return 'gerade eben'; if (diffMin === 1) return 'vor 1 Minute'; return `vor ${diffMin} Minuten`; } function limiterLabel(name: string): string { if (name === 'login') return 'Login'; if (name === 'password-reset') return 'Passwort-Reset'; return name; } export default function RateLimits() { const navigate = useNavigate(); const qc = useQueryClient(); const { data, isLoading, refetch } = useQuery({ queryKey: ['rate-limits-active'], queryFn: () => rateLimitApi.getActive(), refetchInterval: 15000, }); const resetMutation = useMutation({ mutationFn: (e: ActiveRateLimit) => rateLimitApi.reset({ ipAddress: e.ipAddress, email: e.email || undefined }), onSuccess: (_, e) => { const label = e.email ? `${e.ipAddress} + ${e.email}` : e.ipAddress; toast.success(`Rate-Limit für ${label} freigegeben`); qc.invalidateQueries({ queryKey: ['rate-limits-active'] }); }, onError: (err) => { const msg = err instanceof Error ? err.message : 'Reset fehlgeschlagen'; toast.error(msg); }, }); const entries: ActiveRateLimit[] = data?.data || []; return (

Rate-Limit-Sperren

Gesperrte (IP + Account)-Paare aus den letzten 15 Minuten. Andere Accounts von derselben IP sind nicht betroffen, und der gesperrte Account kann sich weiter von einer anderen IP einloggen.

{isLoading ? (
Lade...
) : entries.length === 0 ? (
Aktuell keine Sperren aktiv.
) : (
{entries.map((e) => ( ))}
IP-Adresse Account (E-Mail) Limiter Hits Zuletzt Aktion
{e.ipAddress} {e.email ? ( {e.email} ) : ( — (kein Account) )} {e.limiters.map((l) => ( {limiterLabel(l)} ))} {e.hitCount} {formatTime(e.lastHit)}
)}
Hinweis: Der Limiter sperrt pro (IP, Account)-Paar – andere Accounts von derselben IP bzw. derselbe Account von einer anderen IP sind nicht betroffen. Jede Freigabe wird im Audit-Log protokolliert.
); }