import { useState, useEffect } from 'react' import { useQuery } from '@tanstack/react-query' import { Copy, Check, AlertTriangle, X, Eye, EyeOff } from 'lucide-react' import { useTranslation } from 'react-i18next' import { Alert } from './ui/Alert' import { Button } from './ui/Button' import { toast } from '../utils/toast' import { getCrowdsecKeyStatus, type CrowdSecKeyStatus } from '../api/crowdsec' const DISMISSAL_STORAGE_KEY = 'crowdsec-key-warning-dismissed' interface DismissedState { dismissed: boolean key?: string } function getDismissedState(): DismissedState { try { const stored = localStorage.getItem(DISMISSAL_STORAGE_KEY) if (stored) { return JSON.parse(stored) } } catch { // Ignore parse errors } return { dismissed: false } } function setDismissedState(fullKey: string) { try { localStorage.setItem(DISMISSAL_STORAGE_KEY, JSON.stringify({ dismissed: true, key: fullKey })) } catch { // Ignore storage errors } } export function CrowdSecKeyWarning() { const { t, ready } = useTranslation() const [copied, setCopied] = useState(false) const [dismissed, setDismissed] = useState(false) const [showKey, setShowKey] = useState(false) const { data: keyStatus, isLoading } = useQuery({ queryKey: ['crowdsec-key-status'], queryFn: getCrowdsecKeyStatus, refetchInterval: 60000, retry: 1, }) useEffect(() => { if (keyStatus?.env_key_rejected && keyStatus.full_key) { const storedState = getDismissedState() // If dismissed but for a different key, show the warning again if (storedState.dismissed && storedState.key !== keyStatus.full_key) { setDismissed(false) } else if (storedState.dismissed && storedState.key === keyStatus.full_key) { setDismissed(true) } } }, [keyStatus]) const handleCopy = async () => { if (!keyStatus?.full_key) return try { await navigator.clipboard.writeText(keyStatus.full_key) setCopied(true) toast.success(t('security.crowdsec.keyWarning.copied')) setTimeout(() => setCopied(false), 2000) } catch { toast.error(t('security.crowdsec.copyFailed')) } } const handleDismiss = () => { if (keyStatus?.full_key) { setDismissedState(keyStatus.full_key) } setDismissed(true) } if (!ready || isLoading || !keyStatus?.env_key_rejected || !keyStatus?.full_key || dismissed) { return null } const envVarLine = `CHARON_SECURITY_CROWDSEC_API_KEY=${keyStatus.full_key}` const maskedKey = `CHARON_SECURITY_CROWDSEC_API_KEY=${'•'.repeat(Math.min(keyStatus.full_key.length, 40))}` return (

{t('security.crowdsec.keyWarning.title')}

{t('security.crowdsec.keyWarning.description')}

{t('security.crowdsec.keyWarning.instructions')}

{showKey ? envVarLine : maskedKey}

{t('security.crowdsec.keyWarning.restartNote')}

) }