The AS Name Search card on the home page said the search was coming soon.
It now asks the new GET /api/v1/search/as?q=<text> and lists the matches
as links to their AS pages. asinfo.Search ignores case, returns the first
matches up to a limit in AS number order, and counts all matches. An
empty q answers 400.
When the database has no record of an AS, its page and /api/v1/as/{asn}
take its handle and description from asinfo and still list its prefixes.
They answer 404 only for an AS with no record, no asinfo entry and no
prefix. AS 0 means no known origin: it always answers 404, and the search
never lists it.
Model: opus-5-5
This commit is contained in:
+101
-11
@@ -174,6 +174,38 @@
|
||||
#asname-results .search-message {
|
||||
font-size: 13px;
|
||||
}
|
||||
#asname-results .error {
|
||||
color: #c00;
|
||||
}
|
||||
.asname-list {
|
||||
list-style: none;
|
||||
margin: 12px 0 0 0;
|
||||
padding: 0;
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.asname-list li {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
.asname-list a {
|
||||
display: block;
|
||||
padding: 8px 4px;
|
||||
color: #2c3e50;
|
||||
text-decoration: none;
|
||||
font-size: 14px;
|
||||
}
|
||||
.asname-list a:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.asname-list .asn {
|
||||
font-weight: 600;
|
||||
color: #3498db;
|
||||
}
|
||||
.asname-list .description {
|
||||
display: block;
|
||||
color: #7f8c8d;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* IP Lookup result */
|
||||
.ip-result {
|
||||
@@ -306,7 +338,7 @@
|
||||
<input type="text" id="asname-input" placeholder="e.g., Google, Cloudflare" autocomplete="off">
|
||||
<button type="submit">Search</button>
|
||||
</form>
|
||||
<p class="search-hint">Search for autonomous systems by organization name</p>
|
||||
<p class="search-hint">Search for autonomous systems by organization name or handle</p>
|
||||
<div id="asname-results"></div>
|
||||
</div>
|
||||
|
||||
@@ -387,24 +419,82 @@
|
||||
}
|
||||
});
|
||||
|
||||
// AS name search
|
||||
// AS name search. The handles and descriptions come from outside data,
|
||||
// so the results are built with textContent, never innerHTML.
|
||||
const asNameResults = document.getElementById('asname-results');
|
||||
|
||||
// Replace the search results with one line of text.
|
||||
function showASNameMessage(text, className) {
|
||||
const line = document.createElement('p');
|
||||
line.className = className;
|
||||
line.textContent = text;
|
||||
asNameResults.replaceChildren(line);
|
||||
}
|
||||
|
||||
// List the matches of a search answer, each as a link to its AS page,
|
||||
// and say how many more matched than the answer holds.
|
||||
function showASNameMatches(data) {
|
||||
if (data.total === 0) {
|
||||
showASNameMessage('No AS matches "' + data.query + '".', 'search-message');
|
||||
return;
|
||||
}
|
||||
|
||||
const list = document.createElement('ul');
|
||||
list.className = 'asname-list';
|
||||
for (const match of data.results) {
|
||||
const asn = document.createElement('span');
|
||||
asn.className = 'asn';
|
||||
asn.textContent = 'AS' + match.asn;
|
||||
|
||||
const description = document.createElement('span');
|
||||
description.className = 'description';
|
||||
description.textContent = match.description;
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = '/as/' + match.asn;
|
||||
link.append(asn, ' ' + match.handle, description);
|
||||
|
||||
const item = document.createElement('li');
|
||||
item.append(link);
|
||||
list.append(item);
|
||||
}
|
||||
asNameResults.replaceChildren(list);
|
||||
|
||||
const more = data.total - data.results.length;
|
||||
if (more > 0) {
|
||||
const line = document.createElement('p');
|
||||
line.className = 'search-message';
|
||||
line.textContent = more.toLocaleString() + ' more matched. Narrow the search to see them.';
|
||||
asNameResults.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('asname-form').addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const query = document.getElementById('asname-input').value.trim();
|
||||
if (!query) return;
|
||||
|
||||
const resultsDiv = document.getElementById('asname-results');
|
||||
resultsDiv.innerHTML = '<p class="loading">Searching...</p>';
|
||||
|
||||
// Use a simple client-side search against the asinfo data
|
||||
// For now, redirect to AS page if it looks like an ASN
|
||||
if (/^\d+$/.test(query)) {
|
||||
window.location.href = '/as/' + query;
|
||||
// A number, or AS and a number, opens that AS's page
|
||||
const asn = query.replace(/^AS/i, '');
|
||||
if (/^\d+$/.test(asn)) {
|
||||
window.location.href = '/as/' + asn;
|
||||
return;
|
||||
}
|
||||
|
||||
// Show a message that server-side search is coming
|
||||
resultsDiv.innerHTML = '<p class="search-message">AS name search coming soon. For now, try an AS number.</p>';
|
||||
showASNameMessage('Searching...', 'loading');
|
||||
|
||||
fetch('/api/v1/search/as?q=' + encodeURIComponent(query))
|
||||
.then(response => response.json())
|
||||
.then(response => {
|
||||
if (response.status === 'error') {
|
||||
showASNameMessage('Error: ' + response.error.msg, 'error');
|
||||
} else {
|
||||
showASNameMatches(response.data);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
showASNameMessage('Error: ' + error.message, 'error');
|
||||
});
|
||||
});
|
||||
|
||||
// IP lookup
|
||||
|
||||
Reference in New Issue
Block a user