Update static/js/looking_glass.js

This commit is contained in:
Blackwhitebear8 2025-08-14 12:54:25 +02:00
parent 5ecbe2e5b7
commit cdd414b8a0

View file

@ -7,10 +7,8 @@ document.addEventListener('DOMContentLoaded', function() {
const outputConsole = document.getElementById('output-console'); const outputConsole = document.getElementById('output-console');
const visualizerContainer = document.getElementById('visualizer-container'); const visualizerContainer = document.getElementById('visualizer-container');
const networkContainer = document.getElementById('mynetwork'); const networkContainer = document.getElementById('mynetwork');
const alertModal = new bootstrap.Modal(document.getElementById('alertModal')); const formError = document.getElementById('form-error');
const alertModalBody = document.getElementById('alertModalBody');
const mainLocationSelector = document.getElementById('main-location-selector'); const mainLocationSelector = document.getElementById('main-location-selector');
const locationsDropdownMenu = document.getElementById('locations-dropdown-menu');
const locationNameDisplay = document.getElementById('location-name-display'); const locationNameDisplay = document.getElementById('location-name-display');
const facilityNameDisplay = document.getElementById('facility-name-display'); const facilityNameDisplay = document.getElementById('facility-name-display');
const mapLinkBtn = document.getElementById('map-link-btn'); const mapLinkBtn = document.getElementById('map-link-btn');
@ -55,47 +53,83 @@ document.addEventListener('DOMContentLoaded', function() {
const target = document.getElementById('target').value.trim(); const target = document.getElementById('target').value.trim();
const method = methodSelect.value; const method = methodSelect.value;
if (!target) { formError.textContent = '';
showModalAlert('Please enter a target.'); outputConsole.innerHTML = 'Output will appear here...';
return; visualizerContainer.style.display = 'none';
} if (network) {
network.destroy();
let isValid = false; networkContainer.innerHTML = '';
if (method === 'visualize') {
isValid = isValidIpOrCidr(target);
if (!isValid) {
showModalAlert('Invalid input for Visualizer. Please provide a valid IPv4/IPv6 address or CIDR prefix.');
return;
}
} else {
const isSingleIp = isValidIPv4(target) || isValidIPv6(target);
const isHostname = isValidFqdn(target);
isValid = isSingleIp || isHostname;
if (!isValid) {
showModalAlert('Invalid input. For this method, please provide a valid IP address (without a prefix) or a fully qualified domain name.');
return;
}
} }
setLoadingState(true); setLoadingState(true);
if (method === 'visualize') { if (method === 'visualize') {
handleVisualize(target); handleVisualize(target);
} else if (method === 'bgp_raw') {
handleRawBgpLookup(target);
} else { } else {
handleCommand(method, target); handleCommand(method, target);
} }
}); });
mainLocationSelector.addEventListener('change', updateLocationInfo); mainLocationSelector.addEventListener('change', updateLocationInfo);
locationsDropdownMenu.addEventListener('click', function(e) {
if (e.target.matches('a.dropdown-item')) { function highlightBGPOutput(text) {
e.preventDefault(); if (!text) return '';
mainLocationSelector.value = e.target.dataset.location;
mainLocationSelector.dispatchEvent(new Event('change')); text = text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
}
text = text.replace(/\b215085\b/g, '<span class="asn215085-highlight">215085</span>');
text = text.replace(/\b\d{4,6}\b/g, match => {
if (match === '215085') return `<span class="asn215085-highlight">215085</span>`;
return `<span class="asn-highlight">${match}</span>`;
}); });
text = text.split('\n').map(line => {
const lowerLine = line.toLowerCase();
if (lowerLine.includes('best') || lowerLine.includes('table entry') || lowerLine.includes('multipath')) {
return `<strong class="best-line">${line}</strong>`;
}
return line;
}).join('\n');
return text.replace(/\n/g, '<br>');
}
async function handleRawBgpLookup(target) {
visualizerContainer.style.display = 'none';
outputConsole.style.display = 'block';
outputConsole.innerHTML = `Looking up BGP route for ${target}...\n\nThis may take a few moments. Please wait.`;
try {
const response = await fetch('/api/bgp_raw_lookup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ target })
});
const contentType = response.headers.get("content-type");
if (contentType && contentType.indexOf("application/json") !== -1) {
const data = await response.json();
if (data.error) {
formError.textContent = data.error;
outputConsole.innerHTML = '';
}
} else if (response.ok && contentType && contentType.indexOf("text/plain") !== -1) {
const rawText = await response.text();
outputConsole.innerHTML = highlightBGPOutput(rawText);
} else {
throw new Error('Received an unexpected response from the server.');
}
} catch (error) {
formError.textContent = error.message;
} finally {
setLoadingState(false);
}
}
async function handleCommand(method, target) { async function handleCommand(method, target) {
visualizerContainer.style.display = 'none'; visualizerContainer.style.display = 'none';
outputConsole.style.display = 'block'; outputConsole.style.display = 'block';
@ -108,14 +142,17 @@ document.addEventListener('DOMContentLoaded', function() {
body: JSON.stringify({ method, target }) body: JSON.stringify({ method, target })
}); });
if (!response.ok) { const contentType = response.headers.get("content-type");
const errorText = await response.text();
throw new Error(errorText);
}
if (contentType && contentType.indexOf("application/json") !== -1) {
const data = await response.json();
if (data.error) {
formError.textContent = data.error;
outputConsole.textContent = '';
}
} else if (response.ok && contentType && contentType.indexOf("text/plain") !== -1) {
const reader = response.body.getReader(); const reader = response.body.getReader();
const decoder = new TextDecoder(); const decoder = new TextDecoder();
outputConsole.textContent = ''; outputConsole.textContent = '';
while (true) { while (true) {
@ -124,9 +161,12 @@ document.addEventListener('DOMContentLoaded', function() {
outputConsole.textContent += decoder.decode(value, { stream: true }); outputConsole.textContent += decoder.decode(value, { stream: true });
outputConsole.scrollTop = outputConsole.scrollHeight; outputConsole.scrollTop = outputConsole.scrollHeight;
} }
} else {
throw new Error('Received an unexpected response from the server.');
}
} catch (error) { } catch (error) {
outputConsole.textContent = `--- ERROR ---\n${error.message}`; formError.textContent = error.message;
} finally { } finally {
setLoadingState(false); setLoadingState(false);
} }
@ -148,20 +188,22 @@ document.addEventListener('DOMContentLoaded', function() {
headers: { 'Content-Type': 'application/json' }, headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ip_address: target }) body: JSON.stringify({ ip_address: target })
}) })
.then(response => response.json().then(data => ({ ok: response.ok, data }))) .then(response => response.json())
.then(({ ok, data }) => { .then(data => {
if (!ok || data.error) { if (data.error) {
throw new Error(data.error || 'Failed to retrieve visualization data.'); formError.textContent = data.error;
visualizerContainer.style.display = 'none';
return;
} }
if (data.not_found) { if (data.not_found) {
formError.textContent = `Route information not found for: ${data.target}`;
visualizerContainer.style.display = 'none'; visualizerContainer.style.display = 'none';
showModalAlert(`Route information not found for: ${data.target}`);
return; return;
} }
const pathCount = data.path_count || 0; const pathCount = data.path_count || 0;
const dynamicHeight = Math.max(400, 200 + (pathCount * 150)); const dynamicHeight = Math.max(600, 200 + (pathCount * 150));
networkContainer.style.height = `${dynamicHeight}px`; networkContainer.style.height = `${dynamicHeight}px`;
network = new vis.Network(networkContainer, data, { network = new vis.Network(networkContainer, data, {
@ -180,8 +222,7 @@ document.addEventListener('DOMContentLoaded', function() {
}) })
.catch(error => { .catch(error => {
visualizerContainer.style.display = 'none'; visualizerContainer.style.display = 'none';
outputConsole.style.display = 'block'; formError.textContent = `An unexpected error occurred: ${error.message}`;
outputConsole.textContent = `Visualization Error: ${error.message}`;
}) })
.finally(() => { .finally(() => {
loader.style.display = 'none'; loader.style.display = 'none';
@ -189,28 +230,13 @@ document.addEventListener('DOMContentLoaded', function() {
}); });
} }
function showModalAlert(message) {
alertModalBody.textContent = message;
alertModal.show();
}
function populateLocationSelectors() { function populateLocationSelectors() {
mainLocationSelector.innerHTML = ''; mainLocationSelector.innerHTML = '';
locationsDropdownMenu.innerHTML = '';
for (const locationName in allLocationsData) { for (const locationName in allLocationsData) {
const option = document.createElement('option'); const option = document.createElement('option');
option.value = locationName; option.value = locationName;
option.textContent = locationName; option.textContent = locationName;
mainLocationSelector.appendChild(option); mainLocationSelector.appendChild(option);
const li = document.createElement('li');
const a = document.createElement('a');
a.className = 'dropdown-item';
a.href = '#';
a.textContent = locationName;
a.dataset.location = locationName;
li.appendChild(a);
locationsDropdownMenu.appendChild(li);
} }
} }