// 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 ( <>
Contrôle des boucliers réseau, filtres sémantiques et accès globaux.
Mode maintenance
Verrouille l'accès public et client au portail.
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).
)}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.
Aucune adresse IP n'est actuellement bloquée.
{b.ip}
Bloqué jusqu'au : {new Date(b.locked_until.replace(' ', 'T')).toLocaleString('fr-FR')} • {b.attempts} tentatives
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.