FabioFratini 6 月之前
父節點
當前提交
b3e3da6d3c

+ 75 - 35
app/Http/Livewire/VpnManagement.php

@@ -5,6 +5,7 @@ namespace App\Http\Livewire;
 use Livewire\Component;
 use App\Services\VpnManager;
 use Illuminate\Support\Facades\Log;
+use Illuminate\Validation\ValidationException;
 
 class VpnManagement extends Component
 {
@@ -31,7 +32,8 @@ class VpnManagement extends Component
 
     public function mount()
     {
-        $this->refreshStatus();
+        // Al caricamento, forza una sincronizzazione con il sistema
+        $this->refreshStatus(true);
     }
 
     public function render()
@@ -39,85 +41,123 @@ class VpnManagement extends Component
         return view('livewire.vpn-management');
     }
 
-    public function refreshStatus()
+    public function refreshStatus($forceSystemCheck = false)
     {
-        $this->vpnStatus = $this->vpnManager->getVpnStatus();
+        $this->vpnStatus = $this->vpnManager->getVpnStatus($forceSystemCheck);
         $this->lastUpdate = $this->vpnManager->getLastUpdate();
         $this->emit('vpnStatusUpdated', $this->vpnStatus);
     }
 
-    public function updateCredentials()
+    public function syncWithSystem()
     {
-        $this->validate();
-
         try {
-            $success = $this->vpnManager->updateCredentials(
-                $this->username,
-                $this->password,
-                $this->server
-            );
+            $realStatus = $this->vpnManager->syncStatusWithSystem();
+            $this->vpnStatus = $realStatus;
+            $this->lastUpdate = $this->vpnManager->getLastUpdate();
 
-            if ($success) {
-                $this->successMessage = 'Credenziali VPN aggiornate con successo!';
-                $this->errorMessage = '';
-                $this->password = ''; // Clear password field for security
+            $this->emit('vpnStatusUpdated', $this->vpnStatus);
+            $this->emit('showToast', 'success', 'Stato sincronizzato: ' . $this->getStatusText());
 
-                // Emit event for JavaScript notification
-                $this->emit('showToast', 'success', 'Credenziali VPN aggiornate con successo!');
+            Log::info('VPN status manually synced from frontend', [
+                'new_status' => $realStatus
+            ]);
 
-                Log::info('VPN credentials updated successfully');
-            } else {
-                $this->errorMessage = 'Errore nell\'aggiornamento delle credenziali VPN.';
-                $this->successMessage = '';
-                $this->emit('showToast', 'error', 'Errore nell\'aggiornamento delle credenziali VPN.');
-            }
         } catch (\Exception $e) {
-            $this->errorMessage = 'Errore: ' . $e->getMessage();
-            $this->successMessage = '';
-            $this->emit('showToast', 'error', 'Errore: ' . $e->getMessage());
-            Log::error('Error updating VPN credentials: ' . $e->getMessage());
+            $this->emit('showToast', 'error', 'Errore nella sincronizzazione: ' . $e->getMessage());
+            Log::error('Error syncing VPN status from frontend: ' . $e->getMessage());
         }
     }
 
     public function connectVpn()
     {
         try {
+            // Se già connesso, non fare nulla
+            if ($this->vpnStatus === 'connected') {
+                $this->emit('showToast', 'info', 'VPN già connessa');
+                return;
+            }
+
             $result = $this->vpnManager->connectVpn();
 
             if ($result) {
                 $this->emit('showToast', 'success', 'Comando di connessione VPN inviato!');
-                Log::info('VPN connection command sent');
+                Log::info('VPN connection command sent from frontend');
 
                 // Refresh status after a delay
                 $this->emit('refreshStatusDelayed');
             } else {
                 $this->emit('showToast', 'error', 'Errore nel lancio della connessione VPN');
-                Log::error('Failed to send VPN connection command');
+                Log::error('Failed to send VPN connection command from frontend');
             }
         } catch (\Exception $e) {
             $this->emit('showToast', 'error', 'Errore: ' . $e->getMessage());
-            Log::error('Error connecting VPN: ' . $e->getMessage());
+            Log::error('Error in connectVpn from frontend: ' . $e->getMessage());
         }
     }
 
     public function disconnectVpn()
     {
         try {
+            // Se già disconnesso, non fare nulla
+            if ($this->vpnStatus === 'disconnected') {
+                $this->emit('showToast', 'info', 'VPN già disconnessa');
+                return;
+            }
+
             $result = $this->vpnManager->disconnectVpn();
 
             if ($result) {
                 $this->emit('showToast', 'success', 'Comando di disconnessione VPN inviato!');
-                Log::info('VPN disconnection command sent');
+                Log::info('VPN disconnection command sent from frontend');
+
+                // Update status immediately for disconnect
+                $this->vpnStatus = 'disconnected';
+                $this->lastUpdate = now();
+                $this->emit('vpnStatusUpdated', $this->vpnStatus);
 
-                // Refresh status after a delay
-                $this->emit('refreshStatusDelayed');
             } else {
                 $this->emit('showToast', 'error', 'Errore nella disconnessione VPN');
-                Log::error('Failed to send VPN disconnection command');
+                Log::error('Failed to send VPN disconnection command from frontend');
+            }
+        } catch (\Exception $e) {
+            $this->emit('showToast', 'error', 'Errore: ' . $e->getMessage());
+            Log::error('Error in disconnectVpn from frontend: ' . $e->getMessage());
+        }
+    }
+
+    public function updateCredentials()
+    {
+        try {
+            $this->validate();
+
+            $success = $this->vpnManager->updateCredentials(
+                $this->username,
+                $this->password,
+                $this->server
+            );
+
+            if ($success) {
+                $this->successMessage = 'Credenziali VPN aggiornate con successo!';
+                $this->errorMessage = '';
+                $this->password = ''; // Clear password field for security
+
+                // Emit event for JavaScript notification
+                $this->emit('showToast', 'success', 'Credenziali VPN aggiornate con successo!');
+
+                Log::info('VPN credentials updated successfully');
+            } else {
+                $this->errorMessage = 'Errore nell\'aggiornamento delle credenziali VPN.';
+                $this->successMessage = '';
+                $this->emit('showToast', 'error', 'Errore nell\'aggiornamento delle credenziali VPN.');
             }
+        } catch (ValidationException $e) {
+            // Validation errors are handled automatically by Livewire
+            $this->emit('showToast', 'error', 'Errori di validazione nei campi');
         } catch (\Exception $e) {
+            $this->errorMessage = 'Errore: ' . $e->getMessage();
+            $this->successMessage = '';
             $this->emit('showToast', 'error', 'Errore: ' . $e->getMessage());
-            Log::error('Error disconnecting VPN: ' . $e->getMessage());
+            Log::error('Error updating VPN credentials: ' . $e->getMessage());
         }
     }
 

+ 100 - 9
app/Services/VpnManager.php

@@ -5,7 +5,8 @@ namespace App\Services;
 use Illuminate\Support\Facades\Storage;
 use Illuminate\Support\Facades\Crypt;
 use Illuminate\Support\Facades\Log;
-use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Facades\Schema;
+
 class VpnManager
 {
     private $configPath = 'scripts/vpn-config.conf';
@@ -37,31 +38,121 @@ class VpnManager
         }
     }
 
-    public function getVpnStatus()
+    public function getVpnStatus($forceRefresh = false)
     {
         try {
-            $status = DB::table('vpn_status')->first();
-            return $status ? $status->status : 'unknown';
+            // Se richiesto refresh, sincronizza con il sistema
+            if ($forceRefresh) {
+                return $this->syncStatusWithSystem();
+            }
+
+            // Verifica se la tabella esiste
+            if (!Schema::hasTable('vpn_status')) {
+                Log::warning('Tabella vpn_status non esiste');
+                return 'disconnected';
+            }
+
+            $status = \DB::table('vpn_status')->first();
+
+            if (!$status) {
+                // Se non ci sono record, sincronizza con il sistema
+                return $this->syncStatusWithSystem();
+            }
+
+            return $status->status;
+
         } catch (\Exception $e) {
             Log::error('Errore nel recupero dello stato VPN: ' . $e->getMessage());
-            return 'error';
+            return 'disconnected';
         }
     }
 
     public function getLastUpdate()
     {
         try {
-            $status = DB::table('vpn_status')->first();
-            return $status ? $status->last_update : null;
+            if (!Schema::hasTable('vpn_status')) {
+                return now();
+            }
+
+            $status = \DB::table('vpn_status')->first();
+            return $status ? $status->last_update : now();
+
         } catch (\Exception $e) {
             Log::error('Errore nel recupero dell\'ultimo aggiornamento VPN: ' . $e->getMessage());
-            return null;
+            return now();
+        }
+    }
+
+    public function checkRealVpnStatus()
+    {
+        try {
+            // Esegui il comando per verificare lo stato reale
+            $output = [];
+            $returnVar = 0;
+            exec('/opt/cisco/anyconnect/bin/vpn state 2>&1', $output, $returnVar);
+
+            $statusText = implode(' ', $output);
+
+            Log::info('VPN real status check', [
+                'output' => $output,
+                'return_var' => $returnVar,
+                'status_text' => $statusText
+            ]);
+
+            // Determina lo stato basandosi sull'output
+            if (strpos($statusText, 'state: Connected') !== false ||
+                strpos($statusText, 'Connected') !== false) {
+                return 'connected';
+            } elseif (strpos($statusText, 'Disconnected') !== false ||
+                      strpos($statusText, 'state: Disconnected') !== false) {
+                return 'disconnected';
+            } else {
+                return 'unknown';
+            }
+
+        } catch (\Exception $e) {
+            Log::error('Error checking real VPN status: ' . $e->getMessage());
+            return 'error';
+        }
+    }
+
+    public function syncStatusWithSystem()
+    {
+        try {
+            $realStatus = $this->checkRealVpnStatus();
+
+            // Aggiorna il database con lo stato reale
+            \DB::table('vpn_status')->updateOrInsert(
+                ['id' => 1],
+                [
+                    'status' => $realStatus,
+                    'last_update' => now(),
+                    'updated_at' => now(),
+                ]
+            );
+
+            Log::info('VPN status synced with system', [
+                'real_status' => $realStatus
+            ]);
+
+            return $realStatus;
+
+        } catch (\Exception $e) {
+            Log::error('Error syncing VPN status: ' . $e->getMessage());
+            return 'error';
         }
     }
 
     public function connectVpn()
     {
         try {
+            // Prima controlla se è già connesso
+            $currentStatus = $this->checkRealVpnStatus();
+            if ($currentStatus === 'connected') {
+                Log::info('VPN already connected, no action needed');
+                return true;
+            }
+
             $scriptPath = storage_path('app/' . $this->scriptPath);
 
             if (!file_exists($scriptPath)) {
@@ -94,7 +185,7 @@ class VpnManager
             exec($command, $output, $returnVar);
 
             // Aggiorna lo stato nel database
-            DB::table('vpn_status')->updateOrInsert(
+            \DB::table('vpn_status')->updateOrInsert(
                 ['id' => 1],
                 [
                     'status' => 'disconnected',

+ 225 - 50
resources/views/livewire/vpn-management.blade.php

@@ -27,9 +27,18 @@
                         <i class="fas fa-shield-alt"></i> Stato Connessione VPN
                     </h3>
                     <div class="card-tools">
-                        <button type="button" class="btn btn-tool" wire:click="refreshStatus">
+                        <button type="button"
+                                class="btn btn-tool"
+                                wire:click="refreshStatus(false)"
+                                title="Aggiorna stato database">
                             <i class="fas fa-sync"></i>
                         </button>
+                        <button type="button"
+                                class="btn btn-tool"
+                                wire:click="syncWithSystem"
+                                title="Sincronizza con sistema">
+                            <i class="fas fa-sync-alt"></i>
+                        </button>
                     </div>
                 </div>
                 <div class="card-body">
@@ -54,29 +63,80 @@
                         </div>
                         <div class="col-md-6">
                             <div class="text-center">
-                                <button type="button"
-                                        class="btn btn-success btn-lg"
-                                        wire:click="connectVpn"
-                                        wire:loading.attr="disabled"
-                                        wire:target="connectVpn">
-                                    <span wire:loading.remove wire:target="connectVpn">
-                                        <i class="fas fa-play"></i> Connetti VPN
-                                    </span>
-                                    <span wire:loading wire:target="connectVpn">
-                                        <i class="fas fa-spinner fa-spin"></i> Connettendo...
-                                    </span>
-                                </button>
+                                @if($vpnStatus === 'connected')
+                                    <button type="button" class="btn btn-success btn-lg" disabled>
+                                        <i class="fas fa-check"></i> VPN Connessa
+                                    </button>
+                                    <br><br>
+                                    <button type="button"
+                                            class="btn btn-warning btn-lg"
+                                            wire:click="disconnectVpn"
+                                            wire:loading.attr="disabled"
+                                            wire:target="disconnectVpn">
+                                        <span wire:loading.remove wire:target="disconnectVpn">
+                                            <i class="fas fa-stop"></i> Disconnetti VPN
+                                        </span>
+                                        <span wire:loading wire:target="disconnectVpn">
+                                            <i class="fas fa-spinner fa-spin"></i> Disconnettendo...
+                                        </span>
+                                    </button>
+                                @elseif($vpnStatus === 'disconnected')
+                                    <button type="button"
+                                            class="btn btn-success btn-lg"
+                                            wire:click="connectVpn"
+                                            wire:loading.attr="disabled"
+                                            wire:target="connectVpn">
+                                        <span wire:loading.remove wire:target="connectVpn">
+                                            <i class="fas fa-play"></i> Connetti VPN
+                                        </span>
+                                        <span wire:loading wire:target="connectVpn">
+                                            <i class="fas fa-spinner fa-spin"></i> Connettendo...
+                                        </span>
+                                    </button>
+                                    <br><br>
+                                    <button type="button" class="btn btn-secondary btn-lg" disabled>
+                                        <i class="fas fa-times"></i> VPN Disconnessa
+                                    </button>
+                                @else
+                                    <button type="button"
+                                            class="btn btn-success btn-lg"
+                                            wire:click="connectVpn"
+                                            wire:loading.attr="disabled"
+                                            wire:target="connectVpn">
+                                        <span wire:loading.remove wire:target="connectVpn">
+                                            <i class="fas fa-play"></i> Connetti VPN
+                                        </span>
+                                        <span wire:loading wire:target="connectVpn">
+                                            <i class="fas fa-spinner fa-spin"></i> Connettendo...
+                                        </span>
+                                    </button>
+                                    <br><br>
+                                    <button type="button"
+                                            class="btn btn-warning btn-lg"
+                                            wire:click="disconnectVpn"
+                                            wire:loading.attr="disabled"
+                                            wire:target="disconnectVpn">
+                                        <span wire:loading.remove wire:target="disconnectVpn">
+                                            <i class="fas fa-stop"></i> Disconnetti VPN
+                                        </span>
+                                        <span wire:loading wire:target="disconnectVpn">
+                                            <i class="fas fa-spinner fa-spin"></i> Disconnettendo...
+                                        </span>
+                                    </button>
+                                @endif
+
                                 <br><br>
                                 <button type="button"
-                                        class="btn btn-warning btn-lg"
-                                        wire:click="disconnectVpn"
+                                        class="btn btn-info btn-sm"
+                                        wire:click="syncWithSystem"
                                         wire:loading.attr="disabled"
-                                        wire:target="disconnectVpn">
-                                    <span wire:loading.remove wire:target="disconnectVpn">
-                                        <i class="fas fa-stop"></i> Disconnetti VPN
+                                        wire:target="syncWithSystem"
+                                        title="Sincronizza stato con sistema">
+                                    <span wire:loading.remove wire:target="syncWithSystem">
+                                        <i class="fas fa-sync-alt"></i> Sincronizza
                                     </span>
-                                    <span wire:loading wire:target="disconnectVpn">
-                                        <i class="fas fa-spinner fa-spin"></i> Disconnettendo...
+                                    <span wire:loading wire:target="syncWithSystem">
+                                        <i class="fas fa-spinner fa-spin"></i> Sincronizzando...
                                     </span>
                                 </button>
                             </div>
@@ -223,51 +283,166 @@
 @push('scripts')
 <script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.js"></script>
 <script>
-    // Listen for Livewire events
-    document.addEventListener('livewire:load', function () {
-        // Auto-refresh status every 30 seconds
-        setInterval(function() {
-            @this.call('refreshStatus');
-        }, 30000);
+    let loadingTimeouts = {};
+
+    // Funzione per forzare il reset degli stati di loading
+    function forceResetLoadingStates() {
+        // Reset loading indicators
+        document.querySelectorAll('[wire\\:loading]').forEach(el => {
+            el.style.display = 'none';
+        });
 
-        // Update sidebar badge when status changes
-        Livewire.on('vpnStatusUpdated', status => {
-            updateSidebarBadge(status);
+        // Show normal buttons
+        document.querySelectorAll('[wire\\:loading\\.remove]').forEach(el => {
+            el.style.display = '';
         });
 
-        // Show toast notifications
-        Livewire.on('showToast', (type, message) => {
+        // Re-enable buttons (except permanently disabled ones)
+        document.querySelectorAll('button[disabled]').forEach(btn => {
+            if (!btn.classList.contains('permanently-disabled')) {
+                btn.removeAttribute('disabled');
+            }
+        });
+
+        // Clear all timeouts
+        Object.values(loadingTimeouts).forEach(timeout => clearTimeout(timeout));
+        loadingTimeouts = {};
+
+        console.log('Loading states forcefully reset');
+    }
+
+    // Set timeout for each loading state
+    function setLoadingTimeout(target, duration = 8000) {
+        if (loadingTimeouts[target]) {
+            clearTimeout(loadingTimeouts[target]);
+        }
+
+        loadingTimeouts[target] = setTimeout(() => {
+            console.warn(`Loading timeout for ${target}, forcing reset`);
+            forceResetLoadingStates();
+
+            // Show error toast
             if (typeof toastr !== 'undefined') {
-                toastr[type](message);
+                toastr.error('Operazione completata (timeout raggiunto)');
             }
+        }, duration);
+    }
+
+    // Clear timeout when loading finishes
+    function clearLoadingTimeout(target) {
+        if (loadingTimeouts[target]) {
+            clearTimeout(loadingTimeouts[target]);
+            delete loadingTimeouts[target];
+        }
+    }
+
+    document.addEventListener('DOMContentLoaded', function() {
+        // Livewire event listeners
+        window.addEventListener('livewire:load', function () {
+            console.log('Livewire loaded');
+
+            // Auto-refresh status every 30 seconds (only if not loading)
+            setInterval(function() {
+                if (Object.keys(loadingTimeouts).length === 0) {
+                    @this.call('refreshStatus', false);
+                }
+            }, 30000);
+
+            // Update sidebar badge when status changes
+            Livewire.on('vpnStatusUpdated', status => {
+                updateSidebarBadge(status);
+            });
+
+            // Show toast notifications
+            Livewire.on('showToast', (type, message) => {
+                if (typeof toastr !== 'undefined') {
+                    toastr[type](message);
+                } else {
+                    alert(type.toUpperCase() + ': ' + message);
+                }
+            });
+
+            // Refresh status with delay
+            Livewire.on('refreshStatusDelayed', () => {
+                setTimeout(function() {
+                    if (Object.keys(loadingTimeouts).length === 0) {
+                        @this.call('refreshStatus', false);
+                    }
+                }, 3000);
+            });
         });
 
-        // Refresh status with delay (after connect/disconnect commands)
-        Livewire.on('refreshStatusDelayed', () => {
-            setTimeout(function() {
-                @this.call('refreshStatus');
-            }, 3000);
+        // Track when loading starts
+        window.addEventListener('livewire:loading:start', function(event) {
+            const target = event.detail.target || 'unknown';
+            console.log('Loading started for:', target);
+            setLoadingTimeout(target);
         });
+
+        // Track when loading finishes
+        window.addEventListener('livewire:loading:finish', function(event) {
+            const target = event.detail.target || 'unknown';
+            console.log('Loading finished for:', target);
+            clearLoadingTimeout(target);
+        });
+
+        // Force reset after 10 seconds on page load
+        setTimeout(forceResetLoadingStates, 10000);
+
+        // Initialize sidebar badge
+        const currentStatus = @json($vpnStatus ?? 'unknown');
+        updateSidebarBadge(currentStatus);
     });
 
     function updateSidebarBadge(status) {
         const badge = document.getElementById('vpn-status-badge');
         if (badge) {
-            badge.className = ''; // Clear existing classes
-            if (status === 'connected') {
-                badge.className = 'badge badge-success right';
-                badge.textContent = 'ON';
-            } else if (status === 'disconnected') {
-                badge.className = 'badge badge-danger right';
-                badge.textContent = 'OFF';
-            } else if (status === 'error') {
-                badge.className = 'badge badge-danger right';
-                badge.textContent = 'ERR';
-            } else {
-                badge.className = 'badge badge-warning right';
-                badge.textContent = '?';
+            badge.className = '';
+            switch(status) {
+                case 'connected':
+                    badge.className = 'badge badge-success right';
+                    badge.textContent = 'ON';
+                    break;
+                case 'disconnected':
+                    badge.className = 'badge badge-danger right';
+                    badge.textContent = 'OFF';
+                    break;
+                case 'error':
+                    badge.className = 'badge badge-danger right';
+                    badge.textContent = 'ERR';
+                    break;
+                default:
+                    badge.className = 'badge badge-warning right';
+                    badge.textContent = '?';
             }
         }
     }
+
+    // Emergency reset button (remove this in production)
+    function addEmergencyResetButton() {
+        if (document.getElementById('emergency-reset')) return;
+
+        const resetBtn = document.createElement('button');
+        resetBtn.id = 'emergency-reset';
+        resetBtn.innerHTML = '🔄 Reset Loading';
+        resetBtn.className = 'btn btn-danger btn-sm';
+        resetBtn.style.position = 'fixed';
+        resetBtn.style.top = '10px';
+        resetBtn.style.right = '10px';
+        resetBtn.style.zIndex = '9999';
+        resetBtn.onclick = forceResetLoadingStates;
+
+        document.body.appendChild(resetBtn);
+
+        // Auto-remove after 30 seconds
+        setTimeout(() => {
+            if (resetBtn.parentNode) {
+                resetBtn.parentNode.removeChild(resetBtn);
+            }
+        }, 30000);
+    }
+
+    // Add emergency button if loading states persist
+    setTimeout(addEmergencyResetButton, 5000);
 </script>
 @endpush

+ 121 - 0
storage/scripts/check-vpn-status.sh

@@ -0,0 +1,121 @@
+#!/bin/bash
+
+# Script per testare la connessione VPN
+# Salva come: storage/scripts/test-vpn-connection.sh
+
+echo "=== VPN CONNECTION TEST ==="
+echo "Data: $(date)"
+echo ""
+
+SCRIPT_DIR="$(dirname "$0")"
+CONFIG_FILE="$SCRIPT_DIR/vpn-config.conf"
+
+# 1. Verifica prerequisiti
+echo "1. Checking prerequisites..."
+
+if [[ ! -f "/opt/cisco/anyconnect/bin/vpn" ]]; then
+    echo "   ❌ Cisco AnyConnect not found"
+    exit 1
+fi
+echo "   ✅ Cisco AnyConnect installed"
+
+if [[ ! -f "$CONFIG_FILE" ]]; then
+    echo "   ❌ VPN config file not found: $CONFIG_FILE"
+    exit 1
+fi
+echo "   ✅ VPN config file found"
+
+# 2. Carica configurazione
+echo ""
+echo "2. Loading configuration..."
+source "$CONFIG_FILE"
+
+if [[ -z "$VPN_USERNAME" || -z "$VPN_PASSWORD" || -z "$VPN_SERVER" ]]; then
+    echo "   ❌ Configuration incomplete"
+    echo "      Username: ${VPN_USERNAME:-[missing]}"
+    echo "      Password: ${VPN_PASSWORD:+[set]}${VPN_PASSWORD:-[missing]}"
+    echo "      Server: ${VPN_SERVER:-[missing]}"
+    exit 1
+fi
+
+echo "   ✅ Configuration loaded"
+echo "      Username: $VPN_USERNAME"
+echo "      Server: $VPN_SERVER"
+
+# 3. Test connettività server
+echo ""
+echo "3. Testing server connectivity..."
+SERVER_HOST=$(echo "$VPN_SERVER" | cut -d'/' -f1)
+
+if ping -c 3 "$SERVER_HOST" >/dev/null 2>&1; then
+    echo "   ✅ Server $SERVER_HOST is reachable"
+else
+    echo "   ⚠️  Server $SERVER_HOST ping failed (might be normal for VPN servers)"
+fi
+
+# 4. Controlla stato attuale
+echo ""
+echo "4. Checking current VPN status..."
+CURRENT_STATUS=$(/opt/cisco/anyconnect/bin/vpn state 2>/dev/null)
+echo "   Current status:"
+echo "$CURRENT_STATUS" | sed 's/^/      /'
+
+# 5. Test di connessione (solo se disconnesso)
+if echo "$CURRENT_STATUS" | grep -q "Disconnected"; then
+    echo ""
+    echo "5. Testing VPN connection..."
+    echo "   Attempting to connect (this may take 30-60 seconds)..."
+
+    # Crea un file temporaneo con le credenziali
+    TEMP_CREDS=$(mktemp)
+    echo -e "${VPN_USERNAME}\n${VPN_PASSWORD}\ny" > "$TEMP_CREDS"
+
+    # Tenta la connessione con timeout
+    timeout 60 /opt/cisco/anyconnect/bin/vpn -s connect "$VPN_SERVER" < "$TEMP_CREDS" >/dev/null 2>&1
+    CONNECT_RESULT=$?
+
+    # Pulisci il file temporaneo
+    rm -f "$TEMP_CREDS"
+
+    # Controlla il risultato
+    sleep 5  # Aspetta che la connessione si stabilizzi
+    NEW_STATUS=$(/opt/cisco/anyconnect/bin/vpn state 2>/dev/null)
+
+    if echo "$NEW_STATUS" | grep -q "Connected"; then
+        echo "   ✅ VPN connection successful!"
+        echo "   New status:"
+        echo "$NEW_STATUS" | sed 's/^/      /'
+
+        # Test di connettività attraverso VPN
+        echo ""
+        echo "6. Testing connectivity through VPN..."
+        if curl -s --max-time 10 https://httpbin.org/ip >/dev/null 2>&1; then
+            echo "   ✅ Internet connectivity through VPN: OK"
+
+            # Mostra IP pubblico
+            PUBLIC_IP=$(curl -s --max-time 5 https://httpbin.org/ip | grep -o '"origin":"[^"]*"' | cut -d'"' -f4)
+            echo "   Public IP: ${PUBLIC_IP:-[unable to detect]}"
+        else
+            echo "   ⚠️  Internet connectivity test failed"
+        fi
+
+        # Disconnetti dopo il test
+        echo ""
+        echo "7. Disconnecting test connection..."
+        /opt/cisco/anyconnect/bin/vpn disconnect >/dev/null 2>&1
+        sleep 3
+        echo "   ✅ Disconnected"
+
+    else
+        echo "   ❌ VPN connection failed"
+        echo "   Status after attempt:"
+        echo "$NEW_STATUS" | sed 's/^/      /'
+    fi
+
+else
+    echo ""
+    echo "5. Skipping connection test (VPN already connected or in unknown state)"
+fi
+
+echo ""
+echo "=== TEST COMPLETED ==="

+ 66 - 0
storage/scripts/monitor-vpn-logs.sh

@@ -0,0 +1,66 @@
+#!/bin/bash
+
+# Script per monitorare i log VPN in tempo reale
+# Salva come: storage/scripts/monitor-vpn-logs.sh
+# Uso: bash storage/scripts/monitor-vpn-logs.sh
+
+SCRIPT_DIR="$(dirname "$0")"
+LOG_FILE="$SCRIPT_DIR/../logs/vpn-connection.log"
+LARAVEL_LOG="/var/www/polizia/storage/logs/laravel.log"
+
+echo "=== VPN LOG MONITOR ==="
+echo "Data: $(date)"
+echo "Log file: $LOG_FILE"
+echo "Laravel log: $LARAVEL_LOG"
+echo ""
+echo "Premi Ctrl+C per fermare il monitoraggio"
+echo "==========================================="
+echo ""
+
+# Funzione per mostrare log colorati
+show_colored_log() {
+    while IFS= read -r line; do
+        if [[ $line == *"ERROR"* ]]; then
+            echo -e "\033[31m$line\033[0m"  # Rosso
+        elif [[ $line == *"SUCCESS"* ]]; then
+            echo -e "\033[32m$line\033[0m"  # Verde
+        elif [[ $line == *"INFO"* ]]; then
+            echo -e "\033[34m$line\033[0m"  # Blu
+        elif [[ $line == *"WARNING"* ]]; then
+            echo -e "\033[33m$line\033[0m"  # Giallo
+        else
+            echo "$line"
+        fi
+    done
+}
+
+# Crea il file di log se non esiste
+if [[ ! -f "$LOG_FILE" ]]; then
+    echo "$(date '+%Y-%m-%d %H:%M:%S') [INFO] Log monitoring started" >> "$LOG_FILE"
+fi
+
+# Mostra le ultime 20 righe esistenti
+if [[ -f "$LOG_FILE" ]]; then
+    echo "=== ULTIMI LOG ESISTENTI ==="
+    tail -20 "$LOG_FILE" | show_colored_log
+    echo ""
+    echo "=== MONITORING IN TEMPO REALE ==="
+fi
+
+# Monitora in tempo reale
+tail -f "$LOG_FILE" 2>/dev/null | show_colored_log &
+VPN_LOG_PID=$!
+
+# Se esiste anche il log Laravel, monitoralo per errori VPN
+if [[ -f "$LARAVEL_LOG" ]]; then
+    tail -f "$LARAVEL_LOG" 2>/dev/null | grep -E "(VPN|vpn)" | while read line; do
+        echo -e "\033[35m[LARAVEL] $line\033[0m"  # Magenta
+    done &
+    LARAVEL_LOG_PID=$!
+fi
+
+# Gestisci l'interruzione
+trap 'echo ""; echo "Stopping log monitoring..."; kill $VPN_LOG_PID 2>/dev/null; [[ -n $LARAVEL_LOG_PID ]] && kill $LARAVEL_LOG_PID 2>/dev/null; exit 0' INT
+
+# Mantieni lo script in esecuzione
+wait

+ 59 - 0
storage/scripts/sync-vpn-status.sh

@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# Script per sincronizzare lo stato VPN con il database
+# Salva come: storage/scripts/sync-vpn-status.sh
+# Aggiungi al cron: */5 * * * * /var/www/polizia/storage/scripts/sync-vpn-status.sh
+
+SCRIPT_DIR="$(dirname "$0")"
+LARAVEL_DIR="$(dirname "$(dirname "$SCRIPT_DIR")")"
+LOG_FILE="$SCRIPT_DIR/../logs/vpn-status-sync.log"
+
+# Funzione di logging
+log_message() {
+    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
+}
+
+# Controlla se AnyConnect è installato
+if [[ ! -f "/opt/cisco/anyconnect/bin/vpn" ]]; then
+    log_message "ERROR: Cisco AnyConnect not found"
+    exit 1
+fi
+
+# Ottieni lo stato reale del sistema
+VPN_OUTPUT=$(/opt/cisco/anyconnect/bin/vpn state 2>/dev/null)
+SYSTEM_STATUS="unknown"
+
+if echo "$VPN_OUTPUT" | grep -q "state: Connected"; then
+    SYSTEM_STATUS="connected"
+elif echo "$VPN_OUTPUT" | grep -q "Disconnected"; then
+    SYSTEM_STATUS="disconnected"
+fi
+
+log_message "INFO: System VPN status detected: $SYSTEM_STATUS"
+
+# Ottieni lo stato dal database
+cd "$LARAVEL_DIR"
+DB_STATUS=$(php artisan tinker --execute="echo \DB::table('vpn_status')->first()->status ?? 'no_record';" 2>/dev/null | tail -1)
+
+log_message "INFO: Database VPN status: $DB_STATUS"
+
+# Aggiorna il database se necessario
+if [[ "$SYSTEM_STATUS" != "$DB_STATUS" && "$SYSTEM_STATUS" != "unknown" ]]; then
+    log_message "INFO: Status mismatch detected. Updating database from '$DB_STATUS' to '$SYSTEM_STATUS'"
+
+    php artisan tinker --execute="
+        \DB::table('vpn_status')->updateOrInsert(
+            ['id' => 1],
+            [
+                'status' => '$SYSTEM_STATUS',
+                'last_update' => now(),
+                'updated_at' => now(),
+            ]
+        );
+        echo 'Database updated successfully';
+    " >> "$LOG_FILE" 2>&1
+
+    log_message "SUCCESS: Database status updated to: $SYSTEM_STATUS"
+else
+    log_message "INFO: Status already in sync: $SYSTEM_STATUS"
+fi

+ 121 - 0
storage/scripts/test-vpn-connection.sh

@@ -0,0 +1,121 @@
+#!/bin/bash
+
+# Script per testare la connessione VPN
+# Salva come: storage/scripts/test-vpn-connection.sh
+
+echo "=== VPN CONNECTION TEST ==="
+echo "Data: $(date)"
+echo ""
+
+SCRIPT_DIR="$(dirname "$0")"
+CONFIG_FILE="$SCRIPT_DIR/vpn-config.conf"
+
+# 1. Verifica prerequisiti
+echo "1. Checking prerequisites..."
+
+if [[ ! -f "/opt/cisco/anyconnect/bin/vpn" ]]; then
+    echo "   ❌ Cisco AnyConnect not found"
+    exit 1
+fi
+echo "   ✅ Cisco AnyConnect installed"
+
+if [[ ! -f "$CONFIG_FILE" ]]; then
+    echo "   ❌ VPN config file not found: $CONFIG_FILE"
+    exit 1
+fi
+echo "   ✅ VPN config file found"
+
+# 2. Carica configurazione
+echo ""
+echo "2. Loading configuration..."
+source "$CONFIG_FILE"
+
+if [[ -z "$VPN_USERNAME" || -z "$VPN_PASSWORD" || -z "$VPN_SERVER" ]]; then
+    echo "   ❌ Configuration incomplete"
+    echo "      Username: ${VPN_USERNAME:-[missing]}"
+    echo "      Password: ${VPN_PASSWORD:+[set]}${VPN_PASSWORD:-[missing]}"
+    echo "      Server: ${VPN_SERVER:-[missing]}"
+    exit 1
+fi
+
+echo "   ✅ Configuration loaded"
+echo "      Username: $VPN_USERNAME"
+echo "      Server: $VPN_SERVER"
+
+# 3. Test connettività server
+echo ""
+echo "3. Testing server connectivity..."
+SERVER_HOST=$(echo "$VPN_SERVER" | cut -d'/' -f1)
+
+if ping -c 3 "$SERVER_HOST" >/dev/null 2>&1; then
+    echo "   ✅ Server $SERVER_HOST is reachable"
+else
+    echo "   ⚠️  Server $SERVER_HOST ping failed (might be normal for VPN servers)"
+fi
+
+# 4. Controlla stato attuale
+echo ""
+echo "4. Checking current VPN status..."
+CURRENT_STATUS=$(/opt/cisco/anyconnect/bin/vpn state 2>/dev/null)
+echo "   Current status:"
+echo "$CURRENT_STATUS" | sed 's/^/      /'
+
+# 5. Test di connessione (solo se disconnesso)
+if echo "$CURRENT_STATUS" | grep -q "Disconnected"; then
+    echo ""
+    echo "5. Testing VPN connection..."
+    echo "   Attempting to connect (this may take 30-60 seconds)..."
+
+    # Crea un file temporaneo con le credenziali
+    TEMP_CREDS=$(mktemp)
+    echo -e "${VPN_USERNAME}\n${VPN_PASSWORD}\ny" > "$TEMP_CREDS"
+
+    # Tenta la connessione con timeout
+    timeout 60 /opt/cisco/anyconnect/bin/vpn -s connect "$VPN_SERVER" < "$TEMP_CREDS" >/dev/null 2>&1
+    CONNECT_RESULT=$?
+
+    # Pulisci il file temporaneo
+    rm -f "$TEMP_CREDS"
+
+    # Controlla il risultato
+    sleep 5  # Aspetta che la connessione si stabilizzi
+    NEW_STATUS=$(/opt/cisco/anyconnect/bin/vpn state 2>/dev/null)
+
+    if echo "$NEW_STATUS" | grep -q "Connected"; then
+        echo "   ✅ VPN connection successful!"
+        echo "   New status:"
+        echo "$NEW_STATUS" | sed 's/^/      /'
+
+        # Test di connettività attraverso VPN
+        echo ""
+        echo "6. Testing connectivity through VPN..."
+        if curl -s --max-time 10 https://httpbin.org/ip >/dev/null 2>&1; then
+            echo "   ✅ Internet connectivity through VPN: OK"
+
+            # Mostra IP pubblico
+            PUBLIC_IP=$(curl -s --max-time 5 https://httpbin.org/ip | grep -o '"origin":"[^"]*"' | cut -d'"' -f4)
+            echo "   Public IP: ${PUBLIC_IP:-[unable to detect]}"
+        else
+            echo "   ⚠️  Internet connectivity test failed"
+        fi
+
+        # Disconnetti dopo il test
+        echo ""
+        echo "7. Disconnecting test connection..."
+        /opt/cisco/anyconnect/bin/vpn disconnect >/dev/null 2>&1
+        sleep 3
+        echo "   ✅ Disconnected"
+
+    else
+        echo "   ❌ VPN connection failed"
+        echo "   Status after attempt:"
+        echo "$NEW_STATUS" | sed 's/^/      /'
+    fi
+
+else
+    echo ""
+    echo "5. Skipping connection test (VPN already connected or in unknown state)"
+fi
+
+echo ""
+echo "=== TEST COMPLETED ==="