refactor: update Proxmox firewall view to improve UI elements, enhance log parsing with debug logging, and streamline network adapter management

This commit is contained in:
2025-12-04 18:46:14 -05:00
parent f8fc966cfc
commit 081534a412
2 changed files with 105 additions and 62 deletions

View File

@@ -1250,7 +1250,7 @@ class Firewall extends Component
}
// Map fields to standardized names
return [
$parsed = [
'raw' => $line,
'timestamp' => $fields['timestamp'] ?? '-',
'timestamp_iso' => $this->convertTimestamp($fields['timestamp'] ?? ''),
@@ -1267,6 +1267,19 @@ class Firewall extends Component
'protocol' => $fields['proto'] ?? '-',
'mac' => $fields['mac'] ?? '-',
];
// Debug log first entry to verify parsing
static $loggedOnce = false;
if (!$loggedOnce && count($fields) > 5) {
Log::info('Firewall log parse sample', [
'parsed_fields' => $fields,
'protocol_value' => $parsed['protocol'],
'source_value' => $parsed['source'],
]);
$loggedOnce = true;
}
return $parsed;
}
protected function convertTimestamp(string $timestamp): string

View File

@@ -38,27 +38,97 @@
<x-ri-shield-line class="size-6 text-primary" />
</div>
<div>
<h2 class="text-xl font-semibold">Firewall Status</h2>
<h2 class="text-xl font-semibold">Master Firewall Control</h2>
<p class="text-sm text-base/60 mt-1">
@if($firewallEnabled)
<span class="text-success font-medium">Active and protecting your virtual machine</span>
<span class="text-success font-medium">✓ Active - Your VM is protected by firewall rules</span>
@else
<span class="text-warning font-medium">Currently disabled - your VM is not protected</span>
<span class="text-warning font-medium">⚠ Disabled - Your VM has no firewall protection</span>
@endif
</p>
</div>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox"
@if($firewallEnabled) checked @endif
wire:click="toggleFirewall"
wire:loading.attr="disabled"
class="sr-only peer">
<div class="w-14 h-7 bg-neutral/30 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-primary/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[4px] after:bg-white after:border-neutral after:border after:rounded-full after:h-6 after:w-6 after:transition-all peer-checked:bg-primary"></div>
</label>
<div class="flex items-center gap-3">
<span class="text-sm font-medium text-base/60">{{ $firewallEnabled ? 'Enabled' : 'Disabled' }}</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox"
@if($firewallEnabled) checked @endif
wire:click="toggleFirewall"
wire:loading.attr="disabled"
class="sr-only peer">
<div class="w-14 h-7 bg-neutral/30 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-primary/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[4px] after:bg-white after:border-neutral after:border after:rounded-full after:h-6 after:w-6 after:transition-all peer-checked:bg-primary"></div>
</label>
</div>
</div>
</div>
{{-- Network Adapters - Always Visible --}}
<div class="bg-background border border-neutral rounded-xl p-6 mb-6">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 rounded-lg bg-success/10 flex items-center justify-center">
<x-ri-router-line class="size-5 text-success" />
</div>
<div class="flex-1">
<h3 class="text-lg font-semibold">Network Adapters (Per-Device Firewall)</h3>
<p class="text-sm text-base/60">Enable or disable firewall for each network adapter individually</p>
</div>
</div>
@if(!$firewallEnabled)
<div class="mb-4 p-3 bg-warning/10 border border-warning/30 rounded-lg">
<div class="flex items-start gap-2">
<x-ri-information-line class="size-4 text-warning flex-shrink-0 mt-0.5" />
<p class="text-xs text-warning/90">
<strong>Note:</strong> Per-adapter firewall settings can be configured here, but the <strong>Master Firewall</strong> above must be enabled for these settings to take effect.
</p>
</div>
</div>
@endif
@if(count($networkAdapters) > 0)
<div class="space-y-3">
@foreach($networkAdapters as $adapter)
<div class="p-4 bg-background-secondary/50 border border-neutral/20 rounded-lg hover:border-neutral/40 transition-all">
<div class="flex items-center justify-between">
<div class="flex-1">
<div class="flex items-center gap-2 mb-1">
<span class="font-semibold text-sm">{{ $adapter['name'] }}</span>
@if($adapterFirewallStates[$adapter['name']] ?? false)
<span class="px-2 py-0.5 bg-success/20 text-success text-xs font-medium rounded-full">Firewall Active</span>
@else
<span class="px-2 py-0.5 bg-base/20 text-base/60 text-xs font-medium rounded-full">Firewall Off</span>
@endif
</div>
<div class="text-xs text-base/60">
Model: {{ ucfirst($adapter['model'] ?? 'Unknown') }}
@if($adapter['mac'])
<span class="ml-2">MAC: <code class="text-xs bg-background px-1 rounded">{{ $adapter['mac'] }}</code></span>
@endif
</div>
</div>
<div class="flex items-center gap-3">
<span class="text-sm font-medium text-base/60">{{ ($adapterFirewallStates[$adapter['name']] ?? false) ? 'On' : 'Off' }}</span>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox"
@if($adapterFirewallStates[$adapter['name']] ?? false) checked @endif
wire:click="toggleAdapterFirewall('{{ $adapter['name'] }}')"
wire:loading.attr="disabled"
class="sr-only peer">
<div class="w-11 h-6 bg-neutral/30 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-success/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-neutral after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-success"></div>
</label>
</div>
</div>
</div>
@endforeach
</div>
@else
<div class="text-center py-4 text-base/60 text-sm">
<x-ri-router-line class="size-8 mx-auto mb-2 opacity-50" />
<p>No network adapters found</p>
</div>
@endif
</div>
{{-- Firewall Policies & Filters --}}
@if($firewallEnabled)
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
@@ -118,51 +188,6 @@
</div>
</div>
{{-- Network Adapters Card --}}
<div class="bg-background border border-neutral rounded-xl p-6">
<div class="flex items-center gap-3 mb-4">
<div class="w-10 h-10 rounded-lg bg-success/10 flex items-center justify-center">
<x-ri-router-line class="size-5 text-success" />
</div>
<div>
<h3 class="text-lg font-semibold">Network Adapters</h3>
<p class="text-sm text-base/60">Per-adapter firewall control</p>
</div>
</div>
@if(count($networkAdapters) > 0)
<div class="space-y-3">
@foreach($networkAdapters as $adapter)
<div class="p-4 bg-background-secondary/50 border border-neutral/20 rounded-lg">
<div class="flex items-center justify-between">
<div class="flex-1">
<div class="font-semibold text-sm">{{ $adapter['name'] }}</div>
<div class="text-xs text-base/60 mt-1">
Model: {{ ucfirst($adapter['model'] ?? 'Unknown') }}
@if($adapter['mac'])
<span class="ml-2">MAC: <code class="text-xs bg-background px-1 rounded">{{ $adapter['mac'] }}</code></span>
@endif
</div>
</div>
<label class="relative inline-flex items-center cursor-pointer">
<input type="checkbox"
@if($adapterFirewallStates[$adapter['name']] ?? false) checked @endif
wire:click="toggleAdapterFirewall('{{ $adapter['name'] }}')"
wire:loading.attr="disabled"
class="sr-only peer">
<div class="w-11 h-6 bg-neutral/30 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-primary/20 rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-neutral after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-success"></div>
</label>
</div>
</div>
@endforeach
</div>
@else
<div class="text-center py-4 text-base/60 text-sm">
<x-ri-router-line class="size-8 mx-auto mb-2 opacity-50" />
<p>No network adapters found</p>
</div>
@endif
</div>
</div>
{{-- Policy Info Modal --}}
@@ -799,7 +824,7 @@
</tr>
</thead>
<tbody>
@foreach(array_slice($firewallLogs, 0, 50) as $log)
@foreach(array_slice(array_reverse($firewallLogs), 0, 50) as $log)
<tr class="border-b border-neutral/10 hover:bg-background-secondary/30 transition-colors">
<td class="py-3 px-4 text-xs text-base/70">{{ $log['timestamp'] ?? '-' }}</td>
<td class="py-3 px-4 text-sm">
@@ -1056,7 +1081,7 @@
</div>
@if(count($firewallLogs) > 50)
<div class="mt-4 text-center text-sm text-base/60">
Showing first 50 of {{ count($firewallLogs) }} log entries
Showing latest 50 of {{ count($firewallLogs) }} log entries (most recent first)
</div>
@endif
@else
@@ -1239,11 +1264,16 @@
</div>
</div>
</div>
<div class="flex gap-3 justify-end">
<button wire:click="cancelDeleteRule" class="px-4 py-2 bg-background border border-neutral rounded-lg hover:bg-background-secondary transition-colors text-sm font-medium">
<div style="display: flex; gap: 12px; justify-content: flex-end;">
<button wire:click="cancelDeleteRule"
type="button"
style="padding: 8px 16px; background-color: rgba(var(--background), 1); border: 1px solid rgba(var(--neutral), 0.3); border-radius: 8px; font-size: 14px; font-weight: 500; cursor: pointer; transition: all 0.2s;">
Cancel
</button>
<button wire:click="deleteRule" wire:loading.attr="disabled" class="px-4 py-2 bg-error text-white rounded-lg hover:bg-error/90 transition-colors text-sm font-medium">
<button wire:click="deleteRule"
wire:loading.attr="disabled"
type="button"
style="padding: 8px 16px; background-color: #ef4444; color: white; border: none; border-radius: 8px; font-size: 14px; font-weight: 500; cursor: pointer; transition: all 0.2s;">
<span wire:loading.remove wire:target="deleteRule">Delete Rule</span>
<span wire:loading wire:target="deleteRule">Deleting...</span>
</button>