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 (
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.
| 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)} |