Add "Apply to All Servers" functionality in Firewall component and view

This commit is contained in:
2025-12-08 14:13:14 -05:00
parent 78d3d17c36
commit b01593eac2
2 changed files with 464 additions and 4 deletions

View File

@@ -1402,10 +1402,26 @@
<h3 class="text-lg font-semibold">Saved Configurations</h3>
<p class="text-sm text-base/60 mt-1">Save and reuse firewall configurations across your VPS instances</p>
</div>
<button type="button" wire:click="openSaveConfigModal" class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors text-sm font-medium flex items-center gap-2">
<x-ri-save-3-line class="size-4" />
Save Current Configuration
</button>
<div class="flex items-center gap-2">
{{-- Apply to All Button --}}
<button type="button"
wire:click="openApplyToAllModal"
@if(!$this->canApplyToAll()) disabled @endif
class="px-4 py-2 bg-success text-white rounded-lg transition-colors text-sm font-medium flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed {{ $this->canApplyToAll() ? 'hover:bg-success/90' : '' }}"
title="{{ !$this->currentConfigMatchesSaved() ? 'Save the current configuration first' : (count($this->getUserOtherServers()) === 0 ? 'No other servers to apply to' : 'Apply to all your other servers') }}">
<x-ri-global-line class="size-4" />
Apply to All
</button>
{{-- Save Current Configuration Button --}}
<button type="button"
wire:click="openSaveConfigModal"
@if($this->currentConfigMatchesSaved()) disabled @endif
class="px-4 py-2 bg-primary text-white rounded-lg transition-colors text-sm font-medium flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed {{ !$this->currentConfigMatchesSaved() ? 'hover:bg-primary/90' : '' }}"
title="{{ $this->currentConfigMatchesSaved() ? 'This configuration is already saved' : 'Save the current configuration' }}">
<x-ri-save-3-line class="size-4" />
Save Current Configuration
</button>
</div>
</div>
@if(count($savedConfigurations) > 0)
@@ -2236,6 +2252,123 @@
</div>
@endif
{{-- Apply to All Servers Modal --}}
@if($showApplyToAllModal)
<div class="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm flex items-center justify-center p-4">
<div class="relative w-full max-w-2xl bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl p-6">
<button wire:click="closeApplyToAllModal" class="absolute top-4 right-4 text-base/50 hover:text-base transition-colors">
<x-ri-close-line class="size-5" />
</button>
<div class="flex items-start gap-4 mb-6">
<div class="w-12 h-12 rounded-full bg-success/10 flex items-center justify-center flex-shrink-0">
<x-ri-global-line class="size-6 text-success" />
</div>
<div>
<h3 class="text-xl font-bold mb-2">Apply to All Servers</h3>
<p class="text-sm text-base/70">Apply the current firewall configuration to all your other VPS instances.</p>
</div>
</div>
@php $otherServers = $this->getUserOtherServers(); @endphp
@if(count($applyToAllResults) > 0)
{{-- Show Results --}}
<div class="mb-6">
<h4 class="text-sm font-semibold mb-3">Results</h4>
<div class="space-y-2 max-h-64 overflow-y-auto">
@foreach($applyToAllResults as $result)
<div class="flex items-center justify-between p-3 rounded-lg {{ $result['success'] ? 'bg-success/10 border border-success/20' : 'bg-error/10 border border-error/20' }}">
<div class="flex items-center gap-3">
@if($result['success'])
<x-ri-checkbox-circle-fill class="size-5 text-success" />
@else
<x-ri-close-circle-fill class="size-5 text-error" />
@endif
<span class="font-medium">{{ $result['hostname'] }}</span>
</div>
<span class="text-xs {{ $result['success'] ? 'text-success' : 'text-error' }}">{{ $result['message'] }}</span>
</div>
@endforeach
</div>
</div>
<div class="flex gap-3 justify-end">
<button wire:click="closeApplyToAllModal" class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors text-sm font-medium">
Done
</button>
</div>
@else
{{-- Show Confirmation --}}
<div class="bg-warning/10 border border-warning/30 rounded-lg p-4 mb-6">
<div class="flex items-start gap-3">
<x-ri-alert-line class="size-5 text-warning flex-shrink-0 mt-0.5" />
<div>
<h4 class="font-semibold text-warning mb-1">Warning</h4>
<p class="text-sm text-base/70">This will replace the firewall configuration on all your other servers. Existing rules will be deleted and replaced with the current configuration.</p>
</div>
</div>
</div>
@if(count($otherServers) > 0)
<div class="mb-6">
<h4 class="text-sm font-semibold mb-3">Servers to Update ({{ count($otherServers) }})</h4>
<div class="space-y-2 max-h-48 overflow-y-auto">
@foreach($otherServers as $otherServer)
<div class="flex items-center justify-between p-3 bg-background/50 border border-neutral/20 rounded-lg">
<div class="flex items-center gap-3">
<div class="w-8 h-8 rounded-lg bg-primary/10 flex items-center justify-center">
<x-ri-server-line class="size-4 text-primary" />
</div>
<div>
<span class="font-medium">{{ $otherServer['hostname'] ?? 'VPS ' . $otherServer['vm_id'] }}</span>
<span class="text-xs text-base/50 ml-2">VM {{ $otherServer['vm_id'] }}</span>
</div>
</div>
</div>
@endforeach
</div>
</div>
<div class="bg-background/50 border border-neutral/30 rounded-lg p-4 mb-6">
<h4 class="text-sm font-semibold mb-2">Configuration to Apply</h4>
<div class="grid grid-cols-3 gap-3 text-sm">
<div>
<span class="text-base/60">Rules:</span>
<span class="font-medium ml-1">{{ count($rules) }}</span>
</div>
<div>
<span class="text-base/60">Policy In:</span>
<span class="font-medium ml-1 {{ $policyIn === 'DROP' ? 'text-error' : 'text-success' }}">{{ $policyIn }}</span>
</div>
<div>
<span class="text-base/60">Policy Out:</span>
<span class="font-medium ml-1 {{ $policyOut === 'DROP' ? 'text-error' : 'text-success' }}">{{ $policyOut }}</span>
</div>
</div>
</div>
@else
<div class="text-center py-8 text-base/60">
<x-ri-server-line class="size-12 mx-auto mb-3 opacity-30" />
<p>No other servers found to apply the configuration to.</p>
</div>
@endif
<div class="flex gap-3 justify-end">
<button wire:click="closeApplyToAllModal" class="px-4 py-2 bg-background border border-neutral rounded-lg hover:bg-background-secondary transition-colors text-sm font-medium">
Cancel
</button>
@if(count($otherServers) > 0)
<button wire:click="applyToAllServers" wire:loading.attr="disabled" class="px-4 py-2 bg-success text-white rounded-lg hover:bg-success/90 transition-colors text-sm font-medium disabled:opacity-50">
<span wire:loading.remove wire:target="applyToAllServers">Apply to {{ count($otherServers) }} Server{{ count($otherServers) > 1 ? 's' : '' }}</span>
<span wire:loading wire:target="applyToAllServers">Applying...</span>
</button>
@endif
</div>
@endif
</div>
</div>
@endif
@script
<script>
// IP location helper functions