feat: implement IP extraction and validation functions in Proxmox firewall view for enhanced IP handling and geolocation fetching
This commit is contained in:
@@ -2534,10 +2534,40 @@
|
||||
|
||||
@script
|
||||
<script>
|
||||
// Extract IP from string, handling ports for both IPv4 and IPv6
|
||||
window.extractIP = function(ip) {
|
||||
if (!ip || typeof ip !== 'string') return ip;
|
||||
|
||||
ip = ip.trim();
|
||||
|
||||
// Handle IPv4 with port (e.g., "192.168.1.1:8080")
|
||||
const ipv4WithPort = /^(\d+\.\d+\.\d+\.\d+):(\d+)$/.exec(ip);
|
||||
if (ipv4WithPort) {
|
||||
return ipv4WithPort[1];
|
||||
}
|
||||
|
||||
// Handle IPv6 with port in brackets (e.g., "[2001:db8::1]:8080")
|
||||
const ipv6WithPort = /^\[([^\]]+)\]:(\d+)$/.exec(ip);
|
||||
if (ipv6WithPort) {
|
||||
return ipv6WithPort[1];
|
||||
}
|
||||
|
||||
// Check if it's a valid IPv6 address (contains multiple colons)
|
||||
if (ip.includes(':') && (ip.match(/:/g) || []).length >= 2) {
|
||||
// Might be IPv6, return as-is
|
||||
return ip;
|
||||
}
|
||||
|
||||
return ip;
|
||||
};
|
||||
|
||||
// Check if an IP address is public (not private/localhost)
|
||||
window.isPublicIP = function(ip) {
|
||||
if (!ip || ip === '—' || ip === '-' || typeof ip !== 'string') return false;
|
||||
|
||||
// Extract IP address (handles ports properly for both IPv4 and IPv6)
|
||||
ip = window.extractIP(ip);
|
||||
|
||||
ip = ip.trim();
|
||||
|
||||
// Check for localhost
|
||||
@@ -2561,34 +2591,61 @@
|
||||
const ipv4MappedMatch = ip.match(/^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/i);
|
||||
if (ipv4MappedMatch) {
|
||||
// Recursively check if the embedded IPv4 address is public
|
||||
return isPublicIP(ipv4MappedMatch[1]);
|
||||
}
|
||||
|
||||
// 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 window.isPublicIP(ipv4MappedMatch[1]);
|
||||
}
|
||||
|
||||
// Check if it's IPv4 (basic validation)
|
||||
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
||||
if (ipv4Regex.test(ip)) {
|
||||
// Already checked private ranges above, so it's public
|
||||
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
|
||||
// Basic IPv6 validation
|
||||
const ipv6Regex = /^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$/;
|
||||
return ipv6Regex.test(ip);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// Fetch IP location data
|
||||
// Fetch IP geolocation data via server-side proxy (uses HTTP protocol server-side)
|
||||
window.fetchIPLocation = async function(ip) {
|
||||
if (!window.isPublicIP(ip)) {
|
||||
return { status: 'fail', message: 'Not a public IP address' };
|
||||
}
|
||||
|
||||
// Extract IP address (handles ports properly for both IPv4 and IPv6)
|
||||
ip = window.extractIP(ip);
|
||||
|
||||
try {
|
||||
const response = await fetch(`https://ipapi.co/${ip}/json/`);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
// Use Livewire to call server-side method which fetches from http://ip-api.com/json/{ip}
|
||||
// This avoids mixed content issues when the site is served over HTTPS
|
||||
const data = await @this.call('fetchIPLocation', ip);
|
||||
return data || { status: 'fail', message: 'No data returned' };
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch IP location:', error);
|
||||
return null;
|
||||
console.error('Error fetching IP location:', error);
|
||||
return { status: 'fail', message: 'Failed to fetch location data: ' + (error.message || 'Unknown error') };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user