feat: enhance Proxmox firewall view with improved rule management features, including drag-and-drop reordering and dynamic log updates
This commit is contained in:
@@ -503,34 +503,18 @@
|
|||||||
|
|
||||||
@if($firewallEnabled)
|
@if($firewallEnabled)
|
||||||
<!-- Firewall Rules Card -->
|
<!-- Firewall Rules Card -->
|
||||||
<div class="bg-background border border-neutral rounded-lg p-6">
|
<div class="bg-background border border-neutral rounded-lg p-6"
|
||||||
<div class="flex items-center justify-between mb-4">
|
x-data="{
|
||||||
<div class="flex items-center gap-3">
|
editingPositions: false,
|
||||||
<div class="flex items-center justify-center w-10 h-10 rounded-lg bg-primary/10">
|
rules: @js($rules),
|
||||||
<x-ri-list-check class="size-5 text-primary" />
|
draggedIndex: null,
|
||||||
</div>
|
dragOverIndex: null,
|
||||||
<div>
|
|
||||||
<h2 class="text-xl font-semibold">Firewall Rules</h2>
|
|
||||||
<p class="text-sm text-base/60 mt-1">Manage allow and block rules for network traffic</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
wire:click="startCreateRule"
|
|
||||||
class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors text-sm font-medium">
|
|
||||||
<x-ri-add-line class="size-4 inline mr-1" />
|
|
||||||
Add Rule
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Rules Table -->
|
|
||||||
@if(count($rules) > 0)
|
|
||||||
<div class="overflow-x-auto"
|
|
||||||
x-data="{
|
|
||||||
rules: @js($rules),
|
|
||||||
draggedIndex: null,
|
|
||||||
dragOverIndex: null,
|
|
||||||
|
|
||||||
startDrag(event, index) {
|
startDrag(event, index) {
|
||||||
|
if (!this.editingPositions) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.draggedIndex = index;
|
this.draggedIndex = index;
|
||||||
event.target.closest('tr').classList.add('dragging');
|
event.target.closest('tr').classList.add('dragging');
|
||||||
event.dataTransfer.effectAllowed = 'move';
|
event.dataTransfer.effectAllowed = 'move';
|
||||||
@@ -593,6 +577,37 @@
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}">
|
}">
|
||||||
|
<div class="flex items-center justify-between mb-4">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="flex items-center justify-center w-10 h-10 rounded-lg bg-primary/10">
|
||||||
|
<x-ri-list-check class="size-5 text-primary" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2 class="text-xl font-semibold">Firewall Rules</h2>
|
||||||
|
<p class="text-sm text-base/60 mt-1">Manage allow and block rules for network traffic</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<button
|
||||||
|
@click="editingPositions = !editingPositions"
|
||||||
|
class="px-4 py-2 rounded-lg transition-colors text-sm font-medium border"
|
||||||
|
:class="editingPositions ? 'bg-primary text-white border-primary hover:bg-primary/90' : 'bg-background-secondary text-base border-neutral/20 hover:bg-background-secondary/80'"
|
||||||
|
:title="editingPositions ? 'Done editing positions' : 'Edit rule positions'">
|
||||||
|
<x-ri-draggable class="size-4 inline mr-1" />
|
||||||
|
<span x-text="editingPositions ? 'Done' : 'Edit Positions'"></span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
wire:click="startCreateRule"
|
||||||
|
class="px-4 py-2 bg-primary text-white rounded-lg hover:bg-primary/90 transition-colors text-sm font-medium">
|
||||||
|
<x-ri-add-line class="size-4 inline mr-1" />
|
||||||
|
Add Rule
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Rules Table -->
|
||||||
|
@if(count($rules) > 0)
|
||||||
|
<div class="overflow-x-auto">
|
||||||
<table class="w-full border-collapse">
|
<table class="w-full border-collapse">
|
||||||
<thead>
|
<thead>
|
||||||
<tr class="border-b border-neutral">
|
<tr class="border-b border-neutral">
|
||||||
@@ -610,7 +625,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<template x-for="(rule, index) in rules" :key="rule.pos">
|
<template x-for="(rule, index) in rules" :key="rule.pos">
|
||||||
<tr class="rule-row border-b border-neutral/20 hover:bg-background-secondary/50"
|
<tr class="rule-row border-b border-neutral/20 hover:bg-background-secondary/50"
|
||||||
draggable="true"
|
:draggable="editingPositions"
|
||||||
@dragstart="startDrag($event, index)"
|
@dragstart="startDrag($event, index)"
|
||||||
@dragend="endDrag($event)"
|
@dragend="endDrag($event)"
|
||||||
@dragover="dragOver($event, index)"
|
@dragover="dragOver($event, index)"
|
||||||
@@ -618,7 +633,7 @@
|
|||||||
@drop="drop($event, index)"
|
@drop="drop($event, index)"
|
||||||
:class="{ 'drag-over': dragOverIndex === index }">
|
:class="{ 'drag-over': dragOverIndex === index }">
|
||||||
<td class="py-3 px-2 text-sm">
|
<td class="py-3 px-2 text-sm">
|
||||||
<div class="drag-handle" title="Drag to reorder">
|
<div x-show="editingPositions" class="drag-handle" title="Drag to reorder">
|
||||||
<x-ri-menu-line class="size-4" />
|
<x-ri-menu-line class="size-4" />
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@@ -683,7 +698,17 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<h2 class="text-xl font-semibold">Firewall Logs</h2>
|
<h2 class="text-xl font-semibold">Firewall Logs</h2>
|
||||||
<p class="text-sm text-base/60 mt-1">Recent firewall activity and blocked/allowed connections ({{ count($firewallLogs) }} entries loaded)</p>
|
<p class="text-sm text-base/60 mt-1">
|
||||||
|
Recent firewall activity and blocked/allowed connections
|
||||||
|
<span
|
||||||
|
class="inline-block transition-all duration-300"
|
||||||
|
wire:key="log-count-{{ count($firewallLogs) }}"
|
||||||
|
x-data="{ updated: false }"
|
||||||
|
x-init="$watch('$wire.firewallLogs', () => { updated = true; setTimeout(() => updated = false, 1000) })"
|
||||||
|
:class="{ 'text-success font-semibold scale-110': updated }">
|
||||||
|
({{ count($firewallLogs) }} entries loaded)
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
@@ -941,9 +966,26 @@
|
|||||||
if (this.currentPage > 1) {
|
if (this.currentPage > 1) {
|
||||||
this.currentPage--;
|
this.currentPage--;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
init() {
|
||||||
|
// Listen for Livewire updates to refresh logs with animation
|
||||||
|
this.$watch('$wire.firewallLogs', (newLogs) => {
|
||||||
|
if (newLogs && Array.isArray(newLogs)) {
|
||||||
|
this.isRefreshing = true;
|
||||||
|
// Small delay for animation effect
|
||||||
|
setTimeout(() => {
|
||||||
|
this.initialize(newLogs);
|
||||||
|
this.currentPage = 1;
|
||||||
|
this.isRefreshing = false;
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}"
|
}"
|
||||||
x-effect="if (currentPage > totalPages && totalPages > 0) currentPage = totalPages"
|
x-effect="if (currentPage > totalPages && totalPages > 0) currentPage = totalPages"
|
||||||
|
x-transition:enter="transition ease-out duration-300"
|
||||||
|
x-transition:enter-start="opacity-0 scale-95"
|
||||||
|
x-transition:enter-end="opacity-100 scale-100"
|
||||||
class="space-y-4"
|
class="space-y-4"
|
||||||
>
|
>
|
||||||
<!-- Filter Tabs -->
|
<!-- Filter Tabs -->
|
||||||
@@ -1194,7 +1236,11 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<template x-for="(log, index) in paginatedLogs" :key="log.index || index">
|
<template x-for="(log, index) in paginatedLogs" :key="log.index || index">
|
||||||
<tr class="border-b border-neutral/20 hover:bg-background-secondary/50">
|
<tr
|
||||||
|
class="border-b border-neutral/20 hover:bg-background-secondary/50 transition-all duration-200"
|
||||||
|
x-transition:enter="transition ease-out duration-300"
|
||||||
|
x-transition:enter-start="opacity-0 translate-y-2"
|
||||||
|
x-transition:enter-end="opacity-100 translate-y-0">
|
||||||
<!-- Timestamp -->
|
<!-- Timestamp -->
|
||||||
<td class="py-3 px-4 text-sm" x-text="log.timestamp_display || '-' "></td>
|
<td class="py-3 px-4 text-sm" x-text="log.timestamp_display || '-' "></td>
|
||||||
|
|
||||||
@@ -2099,13 +2145,146 @@
|
|||||||
<p class="text-base/60">Enable the firewall above to manage firewall rules.</p>
|
<p class="text-base/60">Enable the firewall above to manage firewall rules.</p>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Add/Edit Rule Modal -->
|
@script
|
||||||
@if($showRuleForm)
|
<script>
|
||||||
<div class="fixed inset-0 flex items-center justify-center z-50 bg-black/50 backdrop-blur-sm"
|
// Check if an IP address is public (not private/localhost)
|
||||||
wire:click="$set('showRuleForm', false); $set('editingRulePos', null); resetRuleForm()">
|
window.isPublicIP = function(ip) {
|
||||||
<div class="bg-background border border-neutral rounded-lg p-6 shadow-lg max-w-4xl w-full mx-4 max-h-[90vh] overflow-y-auto" wire:click.stop>
|
if (!ip || ip === '—' || ip === '-' || typeof ip !== 'string') return false;
|
||||||
<div class="flex items-center justify-between mb-4">
|
|
||||||
|
ip = ip.trim();
|
||||||
|
|
||||||
|
// Check for localhost
|
||||||
|
if (ip === 'localhost' || ip === '127.0.0.1' || ip === '::1') return false;
|
||||||
|
|
||||||
|
// Check for IPv4 private ranges
|
||||||
|
const ipv4PrivateRanges = [
|
||||||
|
/^10\./, // 10.0.0.0/8
|
||||||
|
/^172\.(1[6-9]|2[0-9]|3[0-1])\./, // 172.16.0.0/12
|
||||||
|
/^192\.168\./, // 192.168.0.0/16
|
||||||
|
/^169\.254\./, // 169.254.0.0/16 (link-local)
|
||||||
|
/^127\./, // 127.0.0.0/8
|
||||||
|
/^0\./ // 0.0.0.0/8
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const range of ipv4PrivateRanges) {
|
||||||
|
if (range.test(ip)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for IPv6 private ranges (very simplified)
|
||||||
|
const ipv6PrivatePatterns = [
|
||||||
|
/^fe80:/i, // Link-local
|
||||||
|
/^fc00:/i, // Unique local
|
||||||
|
/^fd00:/i, // Unique local
|
||||||
|
/^::1$/, // Loopback
|
||||||
|
/^::/ // Unspecified
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const pattern of ipv6PrivatePatterns) {
|
||||||
|
if (pattern.test(ip)) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fetch IP location data
|
||||||
|
window.fetchIPLocation = async function(ip) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`https://ipapi.co/${ip}/json/`);
|
||||||
|
const data = await response.json();
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch IP location:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get protocol description
|
||||||
|
window.getProtocolDescription = function(protocol) {
|
||||||
|
if (!protocol) return 'Unknown Protocol';
|
||||||
|
|
||||||
|
const descriptions = {
|
||||||
|
'TCP': '<p class="mb-2"><strong>Transmission Control Protocol (TCP)</strong> is a connection-oriented protocol that provides reliable, ordered delivery of data between applications.</p><ul class="list-disc ml-5 space-y-1 text-sm"><li>Connection-oriented with three-way handshake</li><li>Guaranteed delivery with error checking</li><li>Used by HTTP, HTTPS, FTP, SSH, and many other services</li></ul>',
|
||||||
|
'UDP': '<p class="mb-2"><strong>User Datagram Protocol (UDP)</strong> is a connectionless protocol that sends messages without establishing a connection.</p><ul class="list-disc ml-5 space-y-1 text-sm"><li>Connectionless and lightweight</li><li>No guaranteed delivery or ordering</li><li>Lower latency than TCP</li><li>Used by DNS, DHCP, streaming, and gaming</li></ul>',
|
||||||
|
'ICMP': '<p class="mb-2"><strong>Internet Control Message Protocol (ICMP)</strong> is used for diagnostic and control purposes in IP networks.</p><ul class="list-disc ml-5 space-y-1 text-sm"><li>Used by ping and traceroute tools</li><li>Reports errors and network conditions</li><li>Not used for data transfer</li></ul>',
|
||||||
|
'GRE': '<p class="mb-2"><strong>Generic Routing Encapsulation (GRE)</strong> is a tunneling protocol that encapsulates network layer protocols.</p><ul class="list-disc ml-5 space-y-1 text-sm"><li>Used for VPNs and tunneling</li><li>Can encapsulate various protocols</li></ul>',
|
||||||
|
'ESP': '<p class="mb-2"><strong>Encapsulating Security Payload (ESP)</strong> provides confidentiality, authentication, and integrity in IPsec.</p><ul class="list-disc ml-5 space-y-1 text-sm"><li>Part of IPsec protocol suite</li><li>Encrypts packet payload</li><li>Used in VPNs</li></ul>',
|
||||||
|
'AH': '<p class="mb-2"><strong>Authentication Header (AH)</strong> provides connectionless integrity and data origin authentication for IP datagrams.</p><ul class="list-disc ml-5 space-y-1 text-sm"><li>Part of IPsec protocol suite</li><li>Provides authentication but not encryption</li></ul>',
|
||||||
|
'SCTP': '<p class="mb-2"><strong>Stream Control Transmission Protocol (SCTP)</strong> is a transport layer protocol similar to TCP and UDP.</p><ul class="list-disc ml-5 space-y-1 text-sm"><li>Message-oriented like UDP</li><li>Reliable like TCP</li><li>Supports multi-homing and multi-streaming</li></ul>',
|
||||||
|
'IGMP': '<p class="mb-2"><strong>Internet Group Management Protocol (IGMP)</strong> manages IP multicast group membership.</p><ul class="list-disc ml-5 space-y-1 text-sm"><li>Used for multicast groups</li><li>Allows hosts to join/leave multicast groups</li></ul>'
|
||||||
|
};
|
||||||
|
|
||||||
|
return descriptions[protocol.toUpperCase()] || `<p>${protocol} protocol</p>`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get port description
|
||||||
|
window.getPortDescription = function(port) {
|
||||||
|
if (!port || port === '-') return 'No specific port';
|
||||||
|
|
||||||
|
const portDescriptions = {
|
||||||
|
'20': 'FTP Data Transfer',
|
||||||
|
'21': 'FTP Control',
|
||||||
|
'22': 'SSH (Secure Shell)',
|
||||||
|
'23': 'Telnet',
|
||||||
|
'25': 'SMTP (Email)',
|
||||||
|
'53': 'DNS',
|
||||||
|
'80': 'HTTP (Web)',
|
||||||
|
'110': 'POP3 (Email)',
|
||||||
|
'143': 'IMAP (Email)',
|
||||||
|
'443': 'HTTPS (Secure Web)',
|
||||||
|
'465': 'SMTPS (Secure Email)',
|
||||||
|
'587': 'SMTP Submission',
|
||||||
|
'993': 'IMAPS (Secure IMAP)',
|
||||||
|
'995': 'POP3S (Secure POP3)',
|
||||||
|
'3306': 'MySQL Database',
|
||||||
|
'3389': 'RDP (Remote Desktop)',
|
||||||
|
'5432': 'PostgreSQL Database',
|
||||||
|
'5900': 'VNC',
|
||||||
|
'8080': 'HTTP Alternate',
|
||||||
|
'8443': 'HTTPS Alternate'
|
||||||
|
};
|
||||||
|
|
||||||
|
return portDescriptions[port] || 'Unknown Port - No standard service associated with this port number';
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
@endscript
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Modals (outside main container to escape overflow-hidden) -->
|
||||||
|
|
||||||
|
<!-- Error Message Modal -->
|
||||||
|
@if($error && $showErrorModal)
|
||||||
|
<div class="fixed inset-0 flex items-center justify-center z-[9999] bg-black/50 backdrop-blur-sm"
|
||||||
|
wire:click="$set('error', ''); $set('showErrorModal', false)">
|
||||||
|
<div class="bg-error/20 border-2 border-error rounded-lg p-6 shadow-lg max-w-3xl w-full mx-4 max-h-[80vh] overflow-y-auto" wire:click.stop>
|
||||||
|
<div class="flex items-start gap-3">
|
||||||
|
<x-ri-error-warning-fill class="size-6 text-error flex-shrink-0 mt-0.5" />
|
||||||
|
<div class="flex-1">
|
||||||
|
<h3 class="text-lg font-semibold text-error mb-2 text-center">Firewall Error</h3>
|
||||||
|
<div class="bg-background/50 rounded p-3 border border-error/30">
|
||||||
|
<p class="text-sm text-error/90 leading-relaxed whitespace-pre-wrap break-words">{{ $error }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
wire:click="$set('error', ''); $set('showErrorModal', false)"
|
||||||
|
class="text-error/70 hover:text-error transition-colors flex-shrink-0">
|
||||||
|
<x-ri-close-line class="size-5" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!-- Add/Edit Rule Modal -->
|
||||||
|
@if($showRuleForm)
|
||||||
|
<div class="fixed inset-0 flex items-center justify-center z-[9999] bg-black/50 backdrop-blur-sm"
|
||||||
|
wire:click="$set('showRuleForm', false); $set('editingRulePos', null); resetRuleForm()">
|
||||||
|
<div class="bg-background border border-neutral rounded-lg p-6 shadow-lg max-w-4xl w-full mx-4 max-h-[90vh] overflow-y-auto" wire:click.stop>
|
||||||
|
<div class="flex items-center justify-between mb-4">
|
||||||
<h3 class="text-lg font-semibold">{{ $editingRulePos !== null ? 'Edit Rule' : 'Create New Rule' }}</h3>
|
<h3 class="text-lg font-semibold">{{ $editingRulePos !== null ? 'Edit Rule' : 'Create New Rule' }}</h3>
|
||||||
<button
|
<button
|
||||||
wire:click="$set('showRuleForm', false); $set('editingRulePos', null); resetRuleForm()"
|
wire:click="$set('showRuleForm', false); $set('editingRulePos', null); resetRuleForm()"
|
||||||
@@ -2247,12 +2426,12 @@
|
|||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<!-- Delete Rule Confirmation Modal -->
|
<!-- Delete Rule Confirmation Modal -->
|
||||||
@if($showDeleteModal && $deletingRulePos !== null)
|
@if($showDeleteModal && $deletingRulePos !== null)
|
||||||
@php
|
@php
|
||||||
$ruleToDelete = collect($rules)->firstWhere('pos', $deletingRulePos);
|
$ruleToDelete = collect($rules)->firstWhere('pos', $deletingRulePos);
|
||||||
@endphp
|
@endphp
|
||||||
<div class="fixed inset-0 flex items-center justify-center z-50 bg-black/60 backdrop-blur-sm transition-opacity"
|
<div class="fixed inset-0 flex items-center justify-center z-[9999] bg-black/60 backdrop-blur-sm transition-opacity"
|
||||||
wire:click="cancelDeleteRule">
|
wire:click="cancelDeleteRule">
|
||||||
<div class="bg-background border border-neutral/30 rounded-xl p-6 shadow-2xl max-w-lg w-full mx-4 transform transition-all" wire:click.stop>
|
<div class="bg-background border border-neutral/30 rounded-xl p-6 shadow-2xl max-w-lg w-full mx-4 transform transition-all" wire:click.stop>
|
||||||
<div class="flex items-start gap-4 mb-6">
|
<div class="flex items-start gap-4 mb-6">
|
||||||
@@ -2340,204 +2519,5 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
</div>
|
|
||||||
|
|
||||||
@script
|
|
||||||
<script>
|
|
||||||
// Check if an IP address is public (not private/localhost)
|
|
||||||
window.isPublicIP = function(ip) {
|
|
||||||
if (!ip || ip === '—' || ip === '-' || typeof ip !== 'string') return false;
|
|
||||||
|
|
||||||
ip = ip.trim();
|
|
||||||
|
|
||||||
// Check for localhost
|
|
||||||
if (ip === 'localhost' || ip === '127.0.0.1' || ip === '::1') return false;
|
|
||||||
|
|
||||||
// Check for IPv4 private ranges
|
|
||||||
const ipv4PrivateRanges = [
|
|
||||||
/^10\./, // 10.0.0.0/8
|
|
||||||
/^172\.(1[6-9]|2[0-9]|3[0-1])\./, // 172.16.0.0/12
|
|
||||||
/^192\.168\./, // 192.168.0.0/16
|
|
||||||
/^169\.254\./, // 169.254.0.0/16 (link-local)
|
|
||||||
/^127\./, // 127.0.0.0/8
|
|
||||||
/^0\./ // 0.0.0.0/8
|
|
||||||
];
|
|
||||||
|
|
||||||
// Check if it's IPv4
|
|
||||||
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
||||||
if (ipv4Regex.test(ip)) {
|
|
||||||
// Check against private ranges
|
|
||||||
if (ipv4PrivateRanges.some(range => range.test(ip))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if it's IPv6
|
|
||||||
if (ip.includes(':')) {
|
|
||||||
// Check for IPv6 private ranges
|
|
||||||
// Link-local (fe80::/10)
|
|
||||||
if (/^fe[89ab][0-9a-fA-F]:/i.test(ip)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unique Local Address - ULA (fc00::/7)
|
|
||||||
if (/^f[cd][0-9a-fA-F]/i.test(ip)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPv4-mapped IPv6 (::ffff:x.x.x.x)
|
|
||||||
if (ip.startsWith('::ffff:')) {
|
|
||||||
const ipv4Part = ip.substring(7);
|
|
||||||
return window.isPublicIP(ipv4Part);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's a valid-looking IPv6 and not in private ranges, consider it public
|
|
||||||
const ipv6Regex = /^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/;
|
|
||||||
return ipv6Regex.test(ip);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Fetch IP geolocation data
|
|
||||||
window.fetchIPLocation = async function(ip) {
|
|
||||||
if (!window.isPublicIP(ip)) {
|
|
||||||
return { status: 'fail', message: 'Not a public IP address' };
|
|
||||||
}
|
|
||||||
|
|
||||||
ip = ip.trim();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Use Livewire to call server-side method
|
|
||||||
const data = await @this.call('fetchIPLocation', ip);
|
|
||||||
return data || { status: 'fail', message: 'No data returned' };
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching IP location:', error);
|
|
||||||
return { status: 'fail', message: 'Failed to fetch location data: ' + (error.message || 'Unknown error') };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Helper function to format descriptions with sections
|
|
||||||
const formatDescription = (overview, meaning, scenarios, notes = null) => {
|
|
||||||
let html = '<div class="space-y-4">';
|
|
||||||
if (overview) {
|
|
||||||
html += `<div><h4 class="font-semibold text-base mb-2">Overview</h4><p>${overview}</p></div>`;
|
|
||||||
}
|
|
||||||
if (meaning) {
|
|
||||||
html += `<div><h4 class="font-semibold text-base mb-2">What This Means</h4><p>${meaning}</p></div>`;
|
|
||||||
}
|
|
||||||
if (scenarios) {
|
|
||||||
html += `<div><h4 class="font-semibold text-base mb-2">Common Scenarios</h4>${scenarios}</div>`;
|
|
||||||
}
|
|
||||||
if (notes) {
|
|
||||||
html += `<div><h4 class="font-semibold text-base mb-2">Additional Notes</h4><p>${notes}</p></div>`;
|
|
||||||
}
|
|
||||||
html += '</div>';
|
|
||||||
return html;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Protocol descriptions mapping
|
|
||||||
window.getProtocolDescription = function(protocol) {
|
|
||||||
if (!protocol || typeof protocol !== 'string') {
|
|
||||||
return formatDescription('Unknown protocol', '', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
const protocolUpper = protocol.toUpperCase();
|
|
||||||
const protocolDescriptions = {
|
|
||||||
'TCP': formatDescription(
|
|
||||||
'TCP (Transmission Control Protocol) is a connection-oriented protocol that provides reliable, ordered, and error-checked delivery of data between applications.',
|
|
||||||
'TCP establishes a connection before data transfer and ensures data integrity through acknowledgments and retransmissions. It guarantees that data is delivered in order and without errors.',
|
|
||||||
'<p>Common uses include:</p><ul class="list-disc list-inside space-y-1 ml-2 mt-2"><li>Web browsing (HTTP/HTTPS)</li><li>Email (SMTP, IMAP, POP3)</li><li>File transfers (FTP)</li><li>Remote access (SSH, RDP)</li><li>Database connections</li><li>API communications</li></ul>',
|
|
||||||
'TCP is the most commonly used transport protocol on the Internet and provides reliable communication at the cost of slightly higher overhead compared to UDP.'
|
|
||||||
),
|
|
||||||
'UDP': formatDescription(
|
|
||||||
'UDP (User Datagram Protocol) is a connectionless protocol that provides fast, low-overhead data transmission without guarantees of delivery, ordering, or duplicate protection.',
|
|
||||||
'UDP does not establish connections before sending data and does not provide reliability features like acknowledgments or retransmissions. It prioritizes speed over reliability.',
|
|
||||||
'<p>Common uses include:</p><ul class="list-disc list-inside space-y-1 ml-2 mt-2"><li>DNS queries</li><li>Video streaming</li><li>Online gaming</li><li>VoIP (Voice over IP)</li><li>Real-time applications</li><li>DHCP (Dynamic Host Configuration Protocol)</li></ul>',
|
|
||||||
'UDP is ideal for applications where speed is more important than reliability, or where occasional packet loss is acceptable.'
|
|
||||||
),
|
|
||||||
'ICMP': formatDescription(
|
|
||||||
'ICMP (Internet Control Message Protocol) is a network layer protocol used for diagnostic and control purposes.',
|
|
||||||
'ICMP is used by network devices to send error messages and operational information. It is essential for network troubleshooting and does not carry application data.',
|
|
||||||
'<p>Common uses include:</p><ul class="list-disc list-inside space-y-1 ml-2 mt-2"><li>Ping (echo request/reply) for connectivity testing</li><li>Traceroute for path discovery</li><li>Error reporting (destination unreachable, time exceeded)</li><li>Network diagnostics</li></ul>',
|
|
||||||
'ICMP is a stateless protocol and does not use connection states like TCP or UDP. It operates at the network layer alongside IP.'
|
|
||||||
),
|
|
||||||
'ICMPV6': formatDescription(
|
|
||||||
'ICMPv6 (Internet Control Message Protocol version 6) is the IPv6 equivalent of ICMP, used for error reporting and diagnostic functions in IPv6 networks.',
|
|
||||||
'ICMPv6 includes additional features beyond what ICMP provides for IPv4, including integrated support for address configuration and network discovery.',
|
|
||||||
'<p>Key features include:</p><ul class="list-disc list-inside space-y-1 ml-2 mt-2"><li>Neighbor Discovery Protocol (NDP) for IPv6</li><li>Path MTU Discovery for IPv6</li><li>Router discovery and configuration</li><li>Error reporting and diagnostics</li></ul>',
|
|
||||||
'ICMPv6 is essential for IPv6 network operation and provides functions that in IPv4 are handled by multiple protocols (ARP, ICMP, etc.).'
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
return protocolDescriptions[protocolUpper] || formatDescription(
|
|
||||||
`The protocol "${protocol}" is a network protocol identifier that defines the rules and format for data communication between network devices.`,
|
|
||||||
'Network protocols specify how data is formatted, transmitted, and received over a network.',
|
|
||||||
''
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Port descriptions mapping
|
|
||||||
window.getPortDescription = function(port) {
|
|
||||||
const portDescriptions = {
|
|
||||||
20: 'FTP Data Transfer - File Transfer Protocol data connection',
|
|
||||||
21: 'FTP Control - File Transfer Protocol control connection',
|
|
||||||
22: 'SSH - Secure Shell, used for secure remote login and command execution',
|
|
||||||
23: 'Telnet - Unencrypted remote terminal access (insecure)',
|
|
||||||
25: 'SMTP - Simple Mail Transfer Protocol for sending email',
|
|
||||||
53: 'DNS - Domain Name System for resolving domain names to IP addresses',
|
|
||||||
67: 'DHCP Server - Dynamic Host Configuration Protocol server',
|
|
||||||
68: 'DHCP Client - Dynamic Host Configuration Protocol client',
|
|
||||||
69: 'TFTP - Trivial File Transfer Protocol',
|
|
||||||
80: 'HTTP - Hypertext Transfer Protocol for web traffic',
|
|
||||||
88: 'Kerberos - Network authentication protocol',
|
|
||||||
110: 'POP3 - Post Office Protocol version 3 for receiving email',
|
|
||||||
119: 'NNTP - Network News Transfer Protocol',
|
|
||||||
123: 'NTP - Network Time Protocol for time synchronization',
|
|
||||||
135: 'MSRPC - Microsoft Remote Procedure Call',
|
|
||||||
139: 'NetBIOS - Network Basic Input/Output System',
|
|
||||||
143: 'IMAP - Internet Message Access Protocol for email',
|
|
||||||
161: 'SNMP - Simple Network Management Protocol',
|
|
||||||
162: 'SNMP Trap - Simple Network Management Protocol trap',
|
|
||||||
389: 'LDAP - Lightweight Directory Access Protocol',
|
|
||||||
443: 'HTTPS - Hypertext Transfer Protocol Secure (encrypted web traffic)',
|
|
||||||
445: 'SMB - Server Message Block for file sharing (Windows)',
|
|
||||||
465: 'SMTPS - SMTP over SSL/TLS',
|
|
||||||
514: 'Syslog - System logging protocol',
|
|
||||||
515: 'LPD/LPR - Line Printer Daemon/Line Printer Remote',
|
|
||||||
587: 'SMTP Submission - Mail submission port',
|
|
||||||
636: 'LDAPS - LDAP over SSL/TLS',
|
|
||||||
993: 'IMAPS - IMAP over SSL/TLS',
|
|
||||||
995: 'POP3S - POP3 over SSL/TLS',
|
|
||||||
1433: 'MSSQL - Microsoft SQL Server database',
|
|
||||||
1521: 'Oracle Database - Oracle database listener',
|
|
||||||
3306: 'MySQL - MySQL database server',
|
|
||||||
3389: 'RDP - Remote Desktop Protocol (Windows)',
|
|
||||||
5432: 'PostgreSQL - PostgreSQL database server',
|
|
||||||
5900: 'VNC - Virtual Network Computing remote desktop',
|
|
||||||
6379: 'Redis - Redis in-memory data store',
|
|
||||||
8080: 'HTTP Alternate - Alternative HTTP port, often used for web proxies',
|
|
||||||
8443: 'HTTPS Alternate - Alternative HTTPS port',
|
|
||||||
27017: 'MongoDB - MongoDB database server',
|
|
||||||
27018: 'MongoDB Shard - MongoDB sharding port',
|
|
||||||
27019: 'MongoDB Config - MongoDB configuration server',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Check for common port ranges
|
|
||||||
if (port >= 1024 && port <= 49151) {
|
|
||||||
return 'Registered/User Port - Port in the registered range, commonly used by applications';
|
|
||||||
} else if (port >= 49152 && port <= 65535) {
|
|
||||||
return 'Dynamic/Private Port - Ephemeral port range used for temporary connections';
|
|
||||||
}
|
|
||||||
|
|
||||||
return portDescriptions[port] || 'Unknown Port - No standard service associated with this port number';
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
@endscript
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</x-proxmox::layout>
|
</x-proxmox::layout>
|
||||||
|
|||||||
Reference in New Issue
Block a user