// ECO-PANNEAU.FR - _react/admin/_admin_cyberdefense.jsx window.pano_AdminCyberdefenseTab = ({ data, refreshData, openLocalDialog, closeCurrentLayer, activeDialog }) => { const { useState, useEffect } = React; // 1. - SÉCURITÉ ANTI-FUITE DE MÉMOIRE (Zéro-Dette) const { isMounted, safeFetch } = window.pano_useSafeFetch(); // 2. - Routage et modales const { activeDialog: hookActiveDialog, openDialog: hookOpenDialog, closeCurrentLayer: hookCloseLayer } = window.pano_useUrlModal(); const routerActiveDialog = activeDialog || hookActiveDialog; const routerCloseLayer = closeCurrentLayer || hookCloseLayer; const routerOpenDialog = openLocalDialog || hookOpenDialog; // 3. - États const [isSaving, setIsSaving] = useState(false); const [settings, setSettings] = useState(data.settings || {}); const [pwdRequestData, setPwdRequestData] = useState(null); const [generatedKey, setGeneratedKey] = useState(''); // États pour le statut de la Cyberdéfense (Blocages actifs) const [cyberStatus, setCyberStatus] = useState({ current_ip: '', blocked_list: [] }); const [isLoadingStatus, setIsLoadingStatus] = useState(true); // 4. - Composants et Icônes const { ShieldAlertIcon, ShieldCheckIcon, SaveIcon, LoaderIcon, ActivityIcon, LockIcon, CopyIcon, RefreshCwIcon, Trash2Icon } = window.pano_getIcons(); const { FormInput, FormTextarea, Toggle, Button, PasswordPromptModal, CardGrid, DataCard, IconBadge } = window.pano_getComponents(); // 5. - Méthodes métier // 5.1 - Fetch du statut des blocages const fetchCyberStatus = async () => { setIsLoadingStatus(true); const d = await safeFetch('system/cyberdefense/status', { method: 'GET', silent: true }); if (!isMounted.current) return; // SÉCURITÉ : Coupe-circuit if (d && d.status === 'success') { setCyberStatus(d.data); } setIsLoadingStatus(false); }; useEffect(() => { fetchCyberStatus(); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); // 5.2 - Action : Débloquer une IP const handleUnblock = async (hash) => { const d = await safeFetch('system/cyberdefense/unblock', { body: { hash }, setLoading: setIsSaving, successMessage: 'Accès débloqué avec succès.' }); if (!isMounted.current) return; // SÉCURITÉ : Coupe-circuit if (d) fetchCyberStatus(); }; const updateSetting = (key, val) => setSettings({ ...settings, [key]: val }); const handleSave = async () => { const payload = {}; let localHasChanges = false; const protectedFields = [ 'maintenance', 'allow_new_purchases', 'blacklist', 'greylist', 'sec_ip_limit', 'mail_limit_count', 'mail_limit_window', 'sec_global_limit', 'sec_lock_min', 'sec_lock_max', 'honeypot_min_seconds' // AJOUT DU HONEYPOT ]; for (const key of protectedFields) { const oldVal = String(data.settings?.[key] ?? '').replace(/\r\n/g, '\n'); const newVal = String(settings[key] ?? '').replace(/\r\n/g, '\n'); if (oldVal !== newVal) { payload[key] = settings[key]; localHasChanges = true; } } if (!localHasChanges) { if (window.pano_showToast) window.pano_showToast("Aucune modification à sauvegarder.", "info"); return; } setPwdRequestData({ title: "Sécurité Zéro-Trust", desc: "La modification des paramètres de cyberdéfense nécessite votre clé de sécurité globale (AES_KEY_CONFIRM) :", onConfirm: async (pwd) => { const d = await executeSave({ ...payload, pwd }); if (!isMounted.current) return; // SÉCURITÉ : Coupe-circuit if (d) { routerCloseLayer(); } } }); routerOpenDialog('pwd_request'); }; const executeSave = async (finalPayload) => { const d = await safeFetch('settings/update', { body: finalPayload, setLoading: setIsSaving, successMessage: "Règles de cyberdéfense mises à jour !" }); if (!isMounted.current) return; // SÉCURITÉ : Coupe-circuit if (d) { refreshData(); } return d; }; const generateNewKey = () => { const array = new Uint8Array(32); crypto.getRandomValues(array); const hex = Array.from(array).map(b => b.toString(16).padStart(2, '0')).join(''); setGeneratedKey(hex); }; // 5.3 - Vérification de conformité comptable const isBillingComplete = !!( data.settings?.billing_company?.trim() && data.settings?.billing_address?.trim() && data.settings?.billing_siret?.trim() && (data.settings?.billing_has_tva === '0' || data.settings?.billing_tva?.trim()) ); // 5.4 - Calcul de "hasChanges" pour afficher/griser le bouton "Sauvegarder" let hasChanges = false; const cyberFields = [ 'maintenance', 'allow_new_purchases', 'blacklist', 'greylist', 'sec_ip_limit', 'mail_limit_count', 'mail_limit_window', 'sec_global_limit', 'sec_lock_min', 'sec_lock_max', 'honeypot_min_seconds' // AJOUT DU HONEYPOT ]; for (const key of cyberFields) { const oldVal = String(data.settings?.[key] ?? '').replace(/\r\n/g, '\n'); const newVal = String(settings[key] ?? '').replace(/\r\n/g, '\n'); if (oldVal !== newVal) { hasChanges = true; break; } } // 6. - Rendu UI return ( <>

Cyberdéfense

Contrôle des boucliers réseau, filtres sémantiques et accès globaux.

Contrôle d'accès global

updateSetting('maintenance', settings.maintenance === '1' ? '0' : '1')} className="flex items-center justify-between p-4 bg-slate-50 border border-slate-100 rounded-xl cursor-pointer hover:bg-slate-100 transition shadow-sm min-w-0">

Mode maintenance

Verrouille l'accès public et client au portail.

{Toggle &&
updateSetting('maintenance', v ? '1' : '0')} variant="danger" />
}
{ if(isBillingComplete) updateSetting('allow_new_purchases', settings.allow_new_purchases !== '0' ? '0' : '1'); }} className={`flex items-center justify-between p-4 bg-slate-50 border border-slate-100 rounded-xl transition shadow-sm min-w-0 ${!isBillingComplete ? 'opacity-70 cursor-not-allowed' : 'cursor-pointer hover:bg-slate-100'}`} >

Autoriser les commandes

Autoriser la validation et le paiement de nouveaux panneaux.

{!isBillingComplete && (

Requiert la configuration complète des informations de facturation dans l'onglet Paramètres système (raison sociale, adresse, SIRET, TVA).

)}
{Toggle &&
{ if(isBillingComplete) updateSetting('allow_new_purchases', v ? '1' : '0'); }} variant="success" />
}

Moteur sémantique

{FormTextarea && ( <> updateSetting('blacklist', e.target.value)} hint="Mots séparés par des virgules. Rejet silencieux immédiat." rows={3} className="min-w-0 w-full" /> updateSetting('greylist', e.target.value)} hint="Tolérance maximale : 2 occurrences. Au-delà, validation requise." rows={3} className="min-w-0 w-full" /> )}
{/* BLOC : ÉTAT DES BLOCAGES ACTIFS */}

Blocages actifs

Votre IP actuelle

{cyberStatus.current_ip || 'Chargement...'}

Pour vous prémunir des blocages de sécurité lors de vos tests, vous pouvez inscrire cette IP dans le fichier _whiteListe-[*****].txt situé à la racine de votre hébergement.

{isLoadingStatus ? (
Chargement des statuts...
) : cyberStatus.blocked_list.length === 0 ? (

Aucune adresse IP n'est actuellement bloquée.

) : (
{cyberStatus.blocked_list.map((b, i) => (

{b.ip}

Bloqué jusqu'au : {new Date(b.locked_until.replace(' ', 'T')).toLocaleString('fr-FR')} • {b.attempts} tentatives

))}
)}

Filtres IP et anti-spam (L4)

{FormInput && ( <> updateSetting('sec_ip_limit', e.target.value)} hint="Nb. max d'échecs (15 min)" className="min-w-0 w-full" /> updateSetting('honeypot_min_seconds', e.target.value)} hint="Délai min. saisie (sec)" className="min-w-0 w-full" /> updateSetting('mail_limit_count', e.target.value)} hint="Messages max / IP" className="min-w-0 w-full" /> updateSetting('mail_limit_window', e.target.value)} hint="Durée en minutes" className="min-w-0 w-full" /> )}

Bouclier anti-DDoS (L7)

{FormInput && ( <> updateSetting('sec_global_limit', e.target.value)} hint="IPs distinctes / 60s" className="col-span-full min-w-0 w-full" /> updateSetting('sec_lock_min', e.target.value)} hint="Durée minimale (min)" className="min-w-0 w-full" /> updateSetting('sec_lock_max', e.target.value)} hint="Durée maximale (min)" className="min-w-0 w-full" /> )}

Générateur de clés de sécurité

Générez une clé SHA-256 (64 caractères hexadécimaux) cryptographiquement sécurisée pour configurer les variables d'environnement de votre installation. L'opération est effectuée localement sur votre navigateur.

{routerActiveDialog === 'pwd_request' && pwdRequestData && PasswordPromptModal && ( )} ); }; /* EOF ========== [_react/admin/_admin_cyberdefense.jsx] */