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:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user