Fix prefix URL routing by using URL encoding

- Replace slash-to-dash conversion with proper URL encoding
- Update handlePrefixDetail and handlePrefixDetailJSON to URL decode prefix parameter
- Update handleIPRedirect to URL encode the prefix in the redirect
- Add urlEncode template function for use in templates
- Update AS detail template to URL encode prefix links
- This properly handles the slash in CIDR notation (e.g., /prefix/192.168.1.0%2F24)
This commit is contained in:
Jeffrey Paul 2025-07-28 04:34:34 +02:00
parent af9ff258b1
commit df31cf880a
3 changed files with 27 additions and 8 deletions

View File

@ -6,6 +6,7 @@ import (
"errors" "errors"
"net" "net"
"net/http" "net/http"
"net/url"
"runtime" "runtime"
"strconv" "strconv"
"time" "time"
@ -419,13 +420,21 @@ func (s *Server) handleASDetailJSON() http.HandlerFunc {
// handlePrefixDetailJSON returns prefix details as JSON // handlePrefixDetailJSON returns prefix details as JSON
func (s *Server) handlePrefixDetailJSON() http.HandlerFunc { func (s *Server) handlePrefixDetailJSON() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
prefix := chi.URLParam(r, "prefix") prefixParam := chi.URLParam(r, "prefix")
if prefix == "" { if prefixParam == "" {
writeJSONError(w, http.StatusBadRequest, "Prefix parameter is required") writeJSONError(w, http.StatusBadRequest, "Prefix parameter is required")
return return
} }
// URL decode the prefix parameter
prefix, err := url.QueryUnescape(prefixParam)
if err != nil {
writeJSONError(w, http.StatusBadRequest, "Invalid prefix parameter")
return
}
routes, err := s.db.GetPrefixDetails(prefix) routes, err := s.db.GetPrefixDetails(prefix)
if err != nil { if err != nil {
if errors.Is(err, database.ErrNoRoute) { if errors.Is(err, database.ErrNoRoute) {
@ -520,13 +529,21 @@ func (s *Server) handleASDetail() http.HandlerFunc {
// handlePrefixDetail returns a handler that serves the prefix detail HTML page // handlePrefixDetail returns a handler that serves the prefix detail HTML page
func (s *Server) handlePrefixDetail() http.HandlerFunc { func (s *Server) handlePrefixDetail() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
prefix := chi.URLParam(r, "prefix") prefixParam := chi.URLParam(r, "prefix")
if prefix == "" { if prefixParam == "" {
http.Error(w, "Prefix parameter is required", http.StatusBadRequest) http.Error(w, "Prefix parameter is required", http.StatusBadRequest)
return return
} }
// URL decode the prefix parameter
prefix, err := url.QueryUnescape(prefixParam)
if err != nil {
http.Error(w, "Invalid prefix parameter", http.StatusBadRequest)
return
}
routes, err := s.db.GetPrefixDetails(prefix) routes, err := s.db.GetPrefixDetails(prefix)
if err != nil { if err != nil {
if errors.Is(err, database.ErrNoRoute) { if errors.Is(err, database.ErrNoRoute) {
@ -642,7 +659,7 @@ func (s *Server) handleIPRedirect() http.HandlerFunc {
return return
} }
// Redirect to the prefix detail page // Redirect to the prefix detail page (URL encode the prefix)
http.Redirect(w, r, "/prefix/"+asInfo.Prefix, http.StatusSeeOther) http.Redirect(w, r, "/prefix/"+url.QueryEscape(asInfo.Prefix), http.StatusSeeOther)
} }
} }

View File

@ -178,7 +178,7 @@
<tbody> <tbody>
{{range .IPv4Prefixes}} {{range .IPv4Prefixes}}
<tr> <tr>
<td><a href="/prefix/{{.Prefix}}" class="prefix-link">{{.Prefix}}</a></td> <td><a href="/prefix/{{.Prefix | urlEncode}}" class="prefix-link">{{.Prefix}}</a></td>
<td>/{{.MaskLength}}</td> <td>/{{.MaskLength}}</td>
<td>{{.LastUpdated.Format "2006-01-02 15:04:05"}}</td> <td>{{.LastUpdated.Format "2006-01-02 15:04:05"}}</td>
<td class="age">{{.LastUpdated | timeSince}}</td> <td class="age">{{.LastUpdated | timeSince}}</td>
@ -207,7 +207,7 @@
<tbody> <tbody>
{{range .IPv6Prefixes}} {{range .IPv6Prefixes}}
<tr> <tr>
<td><a href="/prefix/{{.Prefix}}" class="prefix-link">{{.Prefix}}</a></td> <td><a href="/prefix/{{.Prefix | urlEncode}}" class="prefix-link">{{.Prefix}}</a></td>
<td>/{{.MaskLength}}</td> <td>/{{.MaskLength}}</td>
<td>{{.LastUpdated.Format "2006-01-02 15:04:05"}}</td> <td>{{.LastUpdated.Format "2006-01-02 15:04:05"}}</td>
<td class="age">{{.LastUpdated | timeSince}}</td> <td class="age">{{.LastUpdated | timeSince}}</td>

View File

@ -4,6 +4,7 @@ package templates
import ( import (
_ "embed" _ "embed"
"html/template" "html/template"
"net/url"
"sync" "sync"
"time" "time"
) )
@ -78,6 +79,7 @@ func initTemplates() {
// Create common template functions // Create common template functions
funcs := template.FuncMap{ funcs := template.FuncMap{
"timeSince": timeSince, "timeSince": timeSince,
"urlEncode": url.QueryEscape,
} }
// Parse status template // Parse status template