feat: enhance Proxmox Firewall log view with detailed IP and port information, including modals for geolocation and protocol descriptions, improving user interaction and data accessibility

This commit is contained in:
2025-12-03 04:29:56 -05:00
parent ec5675e108
commit 83c7315ece
2 changed files with 574 additions and 5 deletions

View File

@@ -1152,6 +1152,49 @@ class Firewall extends Component
return $result;
}
/**
* Fetch IP location data from ip-api.com
*/
public function fetchIPLocation(string $ip): array
{
try {
// Validate IP address
if (!filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return [
'status' => 'fail',
'message' => 'Invalid or private IP address'
];
}
// Fetch location data from ip-api.com (HTTP endpoint)
$response = Http::timeout(5)->get("http://ip-api.com/json/{$ip}");
if ($response->successful()) {
$data = $response->json();
// Check if the API returned success
if (isset($data['status']) && $data['status'] === 'success') {
return $data;
} else {
return [
'status' => 'fail',
'message' => $data['message'] ?? 'Unable to fetch location data'
];
}
}
return [
'status' => 'fail',
'message' => 'Failed to fetch location data (HTTP ' . $response->status() . ')'
];
} catch (\Exception $e) {
return [
'status' => 'fail',
'message' => 'Error: ' . $e->getMessage()
];
}
}
public function render()
{
return view('proxmox::firewall')->layoutData([

View File

@@ -537,18 +537,350 @@
<tbody>
<template x-for="(log, index) in paginatedLogs" :key="log.index || index">
<tr class="border-b border-neutral/20 hover:bg-background-secondary/50">
<!-- Timestamp -->
<td class="py-3 px-4 text-sm" x-text="log.timestamp_display || '-' "></td>
<!-- Level -->
<td class="py-3 px-4 text-sm">
<span class="px-2 py-1 rounded text-xs font-semibold uppercase" :class="log.level_badge_class" x-text="log.level_label || '-' "></span>
</td>
<!-- Action -->
<td class="py-3 px-4 text-sm">
<span class="px-2 py-1 rounded text-xs font-semibold" :class="log.action_badge_class" x-text="log.action || '-' "></span>
</td>
<td class="py-3 px-4 text-sm" x-text="log.protocol || '-' "></td>
<td class="py-3 px-4 text-sm font-mono" x-text="log.source || '-' "></td>
<td class="py-3 px-4 text-sm" x-text="log.source_port || '-' "></td>
<td class="py-3 px-4 text-sm font-mono" x-text="log.destination || '-' "></td>
<td class="py-3 px-4 text-sm" x-text="log.destination_port || '-' "></td>
<!-- Protocol with info modal -->
<td class="py-3 px-4 text-sm">
<div class="flex items-center gap-1">
<span x-text="log.protocol || '-' "></span>
<template x-if="log.protocol && log.protocol !== '-'">
<div class="relative" x-data="{ open: false }" x-cloak>
<button type="button" @click="open = true" class="text-base/50 hover:text-primary transition-colors">
<x-ri-information-line class="size-3" />
</button>
<!-- Modal Backdrop -->
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
style="display: none;">
</div>
<!-- Modal -->
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.away="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 flex items-center justify-center p-4"
style="display: none;">
<div class="relative w-full max-w-2xl bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl p-6 max-h-[80vh] overflow-y-auto">
<button @click="open = false" class="absolute top-4 right-4 text-base/50 hover:text-base transition-colors">
<x-ri-close-line class="size-5" />
</button>
<div class="pr-8">
<div class="flex items-center gap-2 mb-4">
<x-ri-information-line class="size-6 text-primary" />
<h3 class="text-lg font-semibold" x-text="`${log.protocol} Protocol`"></h3>
</div>
<div class="text-sm text-base/90" x-html="window.getProtocolDescription(log.protocol)"></div>
</div>
</div>
</div>
</div>
</template>
</div>
</td>
<!-- Source IP with location lookup -->
<td class="py-3 px-4 text-sm">
<div class="flex items-center gap-1">
<span class="font-mono text-xs" x-text="log.source || '-' "></span>
<template x-if="log.source && log.source !== '-' && window.isPublicIP(log.source)">
<div class="relative" x-data="{ open: false, loading: false, ipData: null }" x-cloak>
<button type="button" @click="
if (!ipData && !loading) {
loading = true;
open = true;
window.fetchIPLocation(log.source).then(data => {
ipData = data;
loading = false;
}).catch(err => {
ipData = { status: 'error', message: 'Failed to load location data' };
loading = false;
});
} else {
open = true;
}
" class="text-base/50 hover:text-primary transition-colors" title="View IP Location">
<x-ri-map-pin-line class="size-3" />
</button>
<!-- Modal Backdrop -->
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
style="display: none;">
</div>
<!-- Modal -->
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.away="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 flex items-center justify-center p-4"
style="display: none;">
<div class="relative w-full max-w-md bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl p-6">
<button @click="open = false" class="absolute top-4 right-4 text-base/50 hover:text-base transition-colors">
<x-ri-close-line class="size-5" />
</button>
<div class="pr-8">
<div class="flex items-center gap-2 mb-4">
<x-ri-map-pin-line class="size-6 text-primary" />
<h3 class="text-lg font-semibold">IP Location</h3>
</div>
<template x-if="loading">
<div class="flex items-center justify-center py-8">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
</div>
</template>
<template x-if="!loading && ipData">
<div>
<template x-if="ipData.status === 'success'">
<div class="space-y-3 text-sm">
<div><span class="font-semibold">IP:</span> <span class="font-mono" x-text="log.source"></span></div>
<div><span class="font-semibold">Country:</span> <span x-text="ipData.country"></span></div>
<div><span class="font-semibold">Region:</span> <span x-text="ipData.regionName"></span></div>
<div><span class="font-semibold">City:</span> <span x-text="ipData.city"></span></div>
<div><span class="font-semibold">ISP:</span> <span x-text="ipData.isp"></span></div>
<div><span class="font-semibold">Organization:</span> <span x-text="ipData.org"></span></div>
<div><span class="font-semibold">Timezone:</span> <span x-text="ipData.timezone"></span></div>
</div>
</template>
<template x-if="ipData.status !== 'success'">
<p class="text-sm text-warning" x-text="ipData.message || 'Failed to load location data'"></p>
</template>
</div>
</template>
</div>
</div>
</div>
</div>
</template>
</div>
</td>
<!-- Source Port with description -->
<td class="py-3 px-4 text-sm">
<div class="flex items-center gap-1">
<span x-text="log.source_port || '-' "></span>
<template x-if="log.source_port && log.source_port !== '-'">
<div class="relative" x-data="{ open: false }" x-cloak>
<button type="button" @click="open = true" class="text-base/50 hover:text-primary transition-colors">
<x-ri-information-line class="size-3" />
</button>
<!-- Modal Backdrop -->
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
style="display: none;">
</div>
<!-- Modal -->
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.away="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 flex items-center justify-center p-4"
style="display: none;">
<div class="relative w-full max-w-md bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl p-6">
<button @click="open = false" class="absolute top-4 right-4 text-base/50 hover:text-base transition-colors">
<x-ri-close-line class="size-5" />
</button>
<div class="pr-8">
<div class="flex items-center gap-2 mb-4">
<x-ri-information-line class="size-6 text-primary" />
<h3 class="text-lg font-semibold">Port <span x-text="log.source_port"></span></h3>
</div>
<p class="text-sm text-base/90" x-text="window.getPortDescription(parseInt(log.source_port) || 0)"></p>
</div>
</div>
</div>
</div>
</template>
</div>
</td>
<!-- Destination IP with location lookup -->
<td class="py-3 px-4 text-sm">
<div class="flex items-center gap-1">
<span class="font-mono text-xs" x-text="log.destination || '-' "></span>
<template x-if="log.destination && log.destination !== '-' && window.isPublicIP(log.destination)">
<div class="relative" x-data="{ open: false, loading: false, ipData: null }" x-cloak>
<button type="button" @click="
if (!ipData && !loading) {
loading = true;
open = true;
window.fetchIPLocation(log.destination).then(data => {
ipData = data;
loading = false;
}).catch(err => {
ipData = { status: 'error', message: 'Failed to load location data' };
loading = false;
});
} else {
open = true;
}
" class="text-base/50 hover:text-primary transition-colors" title="View IP Location">
<x-ri-map-pin-line class="size-3" />
</button>
<!-- Modal Backdrop -->
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
style="display: none;">
</div>
<!-- Modal -->
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.away="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 flex items-center justify-center p-4"
style="display: none;">
<div class="relative w-full max-w-md bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl p-6">
<button @click="open = false" class="absolute top-4 right-4 text-base/50 hover:text-base transition-colors">
<x-ri-close-line class="size-5" />
</button>
<div class="pr-8">
<div class="flex items-center gap-2 mb-4">
<x-ri-map-pin-line class="size-6 text-primary" />
<h3 class="text-lg font-semibold">IP Location</h3>
</div>
<template x-if="loading">
<div class="flex items-center justify-center py-8">
<div class="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
</div>
</template>
<template x-if="!loading && ipData">
<div>
<template x-if="ipData.status === 'success'">
<div class="space-y-3 text-sm">
<div><span class="font-semibold">IP:</span> <span class="font-mono" x-text="log.destination"></span></div>
<div><span class="font-semibold">Country:</span> <span x-text="ipData.country"></span></div>
<div><span class="font-semibold">Region:</span> <span x-text="ipData.regionName"></span></div>
<div><span class="font-semibold">City:</span> <span x-text="ipData.city"></span></div>
<div><span class="font-semibold">ISP:</span> <span x-text="ipData.isp"></span></div>
<div><span class="font-semibold">Organization:</span> <span x-text="ipData.org"></span></div>
<div><span class="font-semibold">Timezone:</span> <span x-text="ipData.timezone"></span></div>
</div>
</template>
<template x-if="ipData.status !== 'success'">
<p class="text-sm text-warning" x-text="ipData.message || 'Failed to load location data'"></p>
</template>
</div>
</template>
</div>
</div>
</div>
</div>
</template>
</div>
</td>
<!-- Destination Port with description -->
<td class="py-3 px-4 text-sm">
<div class="flex items-center gap-1">
<span x-text="log.destination_port || '-' "></span>
<template x-if="log.destination_port && log.destination_port !== '-'">
<div class="relative" x-data="{ open: false }" x-cloak>
<button type="button" @click="open = true" class="text-base/50 hover:text-primary transition-colors">
<x-ri-information-line class="size-3" />
</button>
<!-- Modal Backdrop -->
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
@click="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 bg-black/50 backdrop-blur-sm"
style="display: none;">
</div>
<!-- Modal -->
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-150"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
@click.away="open = false"
@keydown.escape.window="open = false"
class="fixed inset-0 z-50 flex items-center justify-center p-4"
style="display: none;">
<div class="relative w-full max-w-md bg-background-secondary rounded-xl border border-neutral/20 shadow-2xl p-6">
<button @click="open = false" class="absolute top-4 right-4 text-base/50 hover:text-base transition-colors">
<x-ri-close-line class="size-5" />
</button>
<div class="pr-8">
<div class="flex items-center gap-2 mb-4">
<x-ri-information-line class="size-6 text-primary" />
<h3 class="text-lg font-semibold">Port <span x-text="log.destination_port"></span></h3>
</div>
<p class="text-sm text-base/90" x-text="window.getPortDescription(parseInt(log.destination_port) || 0)"></p>
</div>
</div>
</div>
</div>
</template>
</div>
</td>
</tr>
</template>
</tbody>
@@ -854,4 +1186,198 @@
@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
</x-proxmox::layout>