diff --git a/static/js/looking_glass.js b/static/js/looking_glass.js
index 9a27600..acdef14 100644
--- a/static/js/looking_glass.js
+++ b/static/js/looking_glass.js
@@ -7,10 +7,8 @@ document.addEventListener('DOMContentLoaded', function() {
const outputConsole = document.getElementById('output-console');
const visualizerContainer = document.getElementById('visualizer-container');
const networkContainer = document.getElementById('mynetwork');
- const alertModal = new bootstrap.Modal(document.getElementById('alertModal'));
- const alertModalBody = document.getElementById('alertModalBody');
+ const formError = document.getElementById('form-error');
const mainLocationSelector = document.getElementById('main-location-selector');
- const locationsDropdownMenu = document.getElementById('locations-dropdown-menu');
const locationNameDisplay = document.getElementById('location-name-display');
const facilityNameDisplay = document.getElementById('facility-name-display');
const mapLinkBtn = document.getElementById('map-link-btn');
@@ -55,46 +53,82 @@ document.addEventListener('DOMContentLoaded', function() {
const target = document.getElementById('target').value.trim();
const method = methodSelect.value;
- if (!target) {
- showModalAlert('Please enter a target.');
- return;
- }
-
- let isValid = false;
- 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;
- }
+ formError.textContent = '';
+ outputConsole.innerHTML = 'Output will appear here...';
+ visualizerContainer.style.display = 'none';
+ if (network) {
+ network.destroy();
+ networkContainer.innerHTML = '';
}
setLoadingState(true);
if (method === 'visualize') {
handleVisualize(target);
+ } else if (method === 'bgp_raw') {
+ handleRawBgpLookup(target);
} else {
handleCommand(method, target);
}
});
mainLocationSelector.addEventListener('change', updateLocationInfo);
- locationsDropdownMenu.addEventListener('click', function(e) {
- if (e.target.matches('a.dropdown-item')) {
- e.preventDefault();
- mainLocationSelector.value = e.target.dataset.location;
- mainLocationSelector.dispatchEvent(new Event('change'));
+
+ function highlightBGPOutput(text) {
+ if (!text) return '';
+
+ text = text.replace(/&/g, "&").replace(//g, ">");
+
+ text = text.replace(/\b215085\b/g, '215085');
+
+ text = text.replace(/\b\d{4,6}\b/g, match => {
+ if (match === '215085') return `215085`;
+ return `${match}`;
+ });
+
+ text = text.split('\n').map(line => {
+ const lowerLine = line.toLowerCase();
+ if (lowerLine.includes('best') || lowerLine.includes('table entry') || lowerLine.includes('multipath')) {
+ return `${line}`;
+ }
+ return line;
+ }).join('\n');
+
+ return text.replace(/\n/g, '
');
+ }
+
+ 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) {
visualizerContainer.style.display = 'none';
@@ -107,26 +141,32 @@ document.addEventListener('DOMContentLoaded', function() {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ method, target })
});
+
+ const contentType = response.headers.get("content-type");
- if (!response.ok) {
- const errorText = await response.text();
- throw new Error(errorText);
- }
-
- const reader = response.body.getReader();
- const decoder = new TextDecoder();
-
- outputConsole.textContent = '';
-
- while (true) {
- const { value, done } = await reader.read();
- if (done) break;
- outputConsole.textContent += decoder.decode(value, { stream: true });
- outputConsole.scrollTop = outputConsole.scrollHeight;
+ 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 decoder = new TextDecoder();
+ outputConsole.textContent = '';
+
+ while (true) {
+ const { value, done } = await reader.read();
+ if (done) break;
+ outputConsole.textContent += decoder.decode(value, { stream: true });
+ outputConsole.scrollTop = outputConsole.scrollHeight;
+ }
+ } else {
+ throw new Error('Received an unexpected response from the server.');
}
} catch (error) {
- outputConsole.textContent = `--- ERROR ---\n${error.message}`;
+ formError.textContent = error.message;
} finally {
setLoadingState(false);
}
@@ -148,20 +188,22 @@ document.addEventListener('DOMContentLoaded', function() {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ip_address: target })
})
- .then(response => response.json().then(data => ({ ok: response.ok, data })))
- .then(({ ok, data }) => {
- if (!ok || data.error) {
- throw new Error(data.error || 'Failed to retrieve visualization data.');
+ .then(response => response.json())
+ .then(data => {
+ if (data.error) {
+ formError.textContent = data.error;
+ visualizerContainer.style.display = 'none';
+ return;
}
if (data.not_found) {
+ formError.textContent = `Route information not found for: ${data.target}`;
visualizerContainer.style.display = 'none';
- showModalAlert(`Route information not found for: ${data.target}`);
return;
}
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`;
network = new vis.Network(networkContainer, data, {
@@ -180,37 +222,21 @@ document.addEventListener('DOMContentLoaded', function() {
})
.catch(error => {
visualizerContainer.style.display = 'none';
- outputConsole.style.display = 'block';
- outputConsole.textContent = `Visualization Error: ${error.message}`;
+ formError.textContent = `An unexpected error occurred: ${error.message}`;
})
.finally(() => {
loader.style.display = 'none';
setLoadingState(false);
});
}
-
- function showModalAlert(message) {
- alertModalBody.textContent = message;
- alertModal.show();
- }
function populateLocationSelectors() {
mainLocationSelector.innerHTML = '';
- locationsDropdownMenu.innerHTML = '';
for (const locationName in allLocationsData) {
const option = document.createElement('option');
option.value = locationName;
option.textContent = locationName;
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);
}
}