Make the home page's AS name search work (closes #151)
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
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>, which searches the AS list built into asinfo, and lists the matches as links to their AS pages. The answer holds the query, the number of matches and the first 50 of them; an empty q answers 400. asinfo.Search now ignores case, uses strings.Contains instead of its own substring loop, and returns its matches in AS number order. A number, or AS and a number, typed into the card still opens that AS's page. The card builds the list with textContent, since the names come from outside data. Model: opus-5-5
This commit is contained in:
@@ -56,10 +56,18 @@ const (
|
||||
// Error messages an answer sends, in place of the error behind it where there
|
||||
// is one.
|
||||
const (
|
||||
invalidPrefixMessage = "Invalid prefix"
|
||||
hostNotResolvedMessage = "could not resolve host name"
|
||||
hostNotPublicMessage = "host name has no public address"
|
||||
tooManyTargetsMessage = "Too many addresses or host names"
|
||||
invalidPrefixMessage = "Invalid prefix"
|
||||
hostNotResolvedMessage = "could not resolve host name"
|
||||
hostNotPublicMessage = "host name has no public address"
|
||||
tooManyTargetsMessage = "Too many addresses or host names"
|
||||
searchQueryMissingMessage = "Missing search query"
|
||||
)
|
||||
|
||||
// The AS name search reads the text it looks for from the URL query parameter
|
||||
// asSearchQueryParam, and answers with at most maxASSearchResults matches.
|
||||
const (
|
||||
asSearchQueryParam = "q"
|
||||
maxASSearchResults = 50
|
||||
)
|
||||
|
||||
// IP versions as database.LiveRoute.IPVersion records them.
|
||||
@@ -1105,6 +1113,46 @@ func (s *Server) handlePrefixDetailJSON() http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
|
||||
// ASSearchResponse is the answer to an AS name search: the text searched for,
|
||||
// the number of ASes that match it, and the first maxASSearchResults of them in
|
||||
// AS number order.
|
||||
type ASSearchResponse struct {
|
||||
Query string `json:"query"`
|
||||
Total int `json:"total"`
|
||||
Results []asinfo.Info `json:"results"`
|
||||
}
|
||||
|
||||
// handleASSearch returns a handler that searches the AS list built into
|
||||
// asinfo for the text in the q parameter, without its leading and trailing
|
||||
// spaces. It answers 400 when no text is left.
|
||||
func (s *Server) handleASSearch() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
query := strings.TrimSpace(r.URL.Query().Get(asSearchQueryParam))
|
||||
if query == "" {
|
||||
s.writeJSONError(w, http.StatusBadRequest, searchQueryMissingMessage)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
matches := asinfo.Search(query)
|
||||
|
||||
// results is an empty list rather than nil when nothing matches, so
|
||||
// that the answer holds [] and not null.
|
||||
shown := min(len(matches), maxASSearchResults)
|
||||
results := make([]asinfo.Info, 0, shown)
|
||||
results = append(results, matches[:shown]...)
|
||||
|
||||
err := writeJSONSuccess(w, ASSearchResponse{
|
||||
Query: query,
|
||||
Total: len(matches),
|
||||
Results: results,
|
||||
})
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to encode AS search results", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handleASDetail returns a handler that serves the AS detail HTML page
|
||||
func (s *Server) handleASDetail() http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user