- Use bindata to package static file content in to the frontend binary
- Add golang templates to move HTML rendering out of the go code where possible - Add an endpoint for serving static files - Add URL escaping for servers and targets
This commit is contained in:
@@ -1,13 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func renderTemplate(w http.ResponseWriter, r *http.Request, title string, content string) {
|
||||
// static options map
|
||||
var optionsMap = map[string]string{
|
||||
"summary": "show protocols",
|
||||
"detail": "show protocols all",
|
||||
"route": "show route for ...",
|
||||
"route_all": "show route for ... all",
|
||||
"route_bgpmap": "show route for ... (bgpmap)",
|
||||
"route_where": "show route where net ~ [ ... ]",
|
||||
"route_where_all": "show route where net ~ [ ... ] all",
|
||||
"route_where_bgpmap": "show route where net ~ [ ... ] (bgpmap)",
|
||||
"route_generic": "show route ...",
|
||||
"generic": "show ...",
|
||||
"whois": "whois ...",
|
||||
"traceroute": "traceroute ...",
|
||||
}
|
||||
|
||||
// pre-compiled regexp and constant statemap for summary rendering
|
||||
var splitSummaryLine = regexp.MustCompile(`(\w+)(\s+)(\w+)(\s+)([\w-]+)(\s+)(\w+)(\s+)([0-9\-\. :]+)(.*)`)
|
||||
var summaryStateMap = map[string]string{
|
||||
"up": "success",
|
||||
"down": "secondary",
|
||||
"start": "danger",
|
||||
"passive": "info",
|
||||
}
|
||||
|
||||
// render the page template
|
||||
func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, content string) {
|
||||
path := r.URL.Path[1:]
|
||||
split := strings.SplitN(path, "/", 3)
|
||||
|
||||
@@ -24,36 +52,28 @@ func renderTemplate(w http.ResponseWriter, r *http.Request, title string, conten
|
||||
|
||||
split = strings.SplitN(path, "/", 3)
|
||||
|
||||
var args tmplArguments
|
||||
args.Options = map[string]string{
|
||||
"summary": "show protocols",
|
||||
"detail": "show protocols all",
|
||||
"route": "show route for ...",
|
||||
"route_all": "show route for ... all",
|
||||
"route_bgpmap": "show route for ... (bgpmap)",
|
||||
"route_where": "show route where net ~ [ ... ]",
|
||||
"route_where_all": "show route where net ~ [ ... ] all",
|
||||
"route_where_bgpmap": "show route where net ~ [ ... ] (bgpmap)",
|
||||
"route_generic": "show route ...",
|
||||
"generic": "show ...",
|
||||
"whois": "whois ...",
|
||||
"traceroute": "traceroute ...",
|
||||
args := TemplatePage{
|
||||
Options: optionsMap,
|
||||
Servers: setting.servers,
|
||||
AllServersLinkActive: strings.ToLower(split[1]) == strings.ToLower(strings.Join(setting.servers, "+")),
|
||||
AllServersURL: strings.Join(setting.servers, "+"),
|
||||
IsWhois: isWhois,
|
||||
WhoisTarget: whoisTarget,
|
||||
|
||||
URLOption: strings.ToLower(split[0]),
|
||||
URLServer: strings.ToLower(split[1]),
|
||||
URLCommand: split[2],
|
||||
Title: setting.titleBrand + title,
|
||||
Brand: setting.navBarBrand,
|
||||
Content: content,
|
||||
}
|
||||
args.Servers = setting.servers
|
||||
args.AllServersLinkActive = strings.ToLower(split[1]) == strings.ToLower(strings.Join(setting.servers, "+"))
|
||||
args.AllServersURL = strings.Join(setting.servers, "+")
|
||||
args.IsWhois = isWhois
|
||||
args.WhoisTarget = whoisTarget
|
||||
|
||||
args.URLOption = strings.ToLower(split[0])
|
||||
args.URLServer = strings.ToLower(split[1])
|
||||
args.URLCommand = split[2]
|
||||
tmpl := TemplateLibrary["page"]
|
||||
err := tmpl.Execute(w, args)
|
||||
if err != nil {
|
||||
fmt.Println("Error rendering page:", err.Error())
|
||||
}
|
||||
|
||||
args.Title = setting.titleBrand + title
|
||||
args.Brand = setting.navBarBrand
|
||||
args.Content = content
|
||||
|
||||
tmpl.Execute(w, args)
|
||||
}
|
||||
|
||||
// Write the given text to http response, and add whois links for
|
||||
@@ -77,87 +97,81 @@ func smartFormatter(s string) string {
|
||||
return result
|
||||
}
|
||||
|
||||
type summaryTableArguments struct {
|
||||
Headers []string
|
||||
Lines [][]string
|
||||
}
|
||||
|
||||
// Output a table for the summary page
|
||||
func summaryTable(data string, serverName string) string {
|
||||
var result string
|
||||
|
||||
// Sort the table, excluding title row
|
||||
stringsSplitted := strings.Split(strings.TrimSpace(data), "\n")
|
||||
if len(stringsSplitted) <= 1 {
|
||||
lines := strings.Split(strings.TrimSpace(data), "\n")
|
||||
if len(lines) <= 1 {
|
||||
// Likely backend returned an error message
|
||||
result = "<pre>" + strings.TrimSpace(data) + "</pre>"
|
||||
} else {
|
||||
// Draw the table head
|
||||
result += `<table class="table table-striped table-bordered table-sm">`
|
||||
result += `<thead>`
|
||||
for _, col := range strings.Split(stringsSplitted[0], " ") {
|
||||
colTrimmed := strings.TrimSpace(col)
|
||||
if len(colTrimmed) == 0 {
|
||||
continue
|
||||
}
|
||||
result += `<th scope="col">` + colTrimmed + `</th>`
|
||||
}
|
||||
result += `</thead><tbody>`
|
||||
|
||||
stringsWithoutTitle := stringsSplitted[1:]
|
||||
sort.Strings(stringsWithoutTitle)
|
||||
|
||||
for _, line := range stringsWithoutTitle {
|
||||
// Ignore empty lines
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse a total of 6 columns from bird summary
|
||||
lineSplitted := regexp.MustCompile(`(\w+)(\s+)(\w+)(\s+)([\w-]+)(\s+)(\w+)(\s+)([0-9\-\. :]+)(.*)`).FindStringSubmatch(line)
|
||||
if lineSplitted == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var row [6]string
|
||||
if len(lineSplitted) >= 2 {
|
||||
row[0] = strings.TrimSpace(lineSplitted[1])
|
||||
}
|
||||
if len(lineSplitted) >= 4 {
|
||||
row[1] = strings.TrimSpace(lineSplitted[3])
|
||||
}
|
||||
if len(lineSplitted) >= 6 {
|
||||
row[2] = strings.TrimSpace(lineSplitted[5])
|
||||
}
|
||||
if len(lineSplitted) >= 8 {
|
||||
row[3] = strings.TrimSpace(lineSplitted[7])
|
||||
}
|
||||
if len(lineSplitted) >= 10 {
|
||||
row[4] = strings.TrimSpace(lineSplitted[9])
|
||||
}
|
||||
if len(lineSplitted) >= 11 {
|
||||
row[5] = strings.TrimSpace(lineSplitted[10])
|
||||
}
|
||||
|
||||
// Draw the row in red if the link isn't up
|
||||
result += `<tr class="` + (map[string]string{
|
||||
"up": "table-success",
|
||||
"down": "table-secondary",
|
||||
"start": "table-danger",
|
||||
"passive": "table-info",
|
||||
})[row[3]] + `">`
|
||||
// Add link to detail for first column
|
||||
result += `<td><a href="/detail/` + serverName + `/` + row[0] + `">` + row[0] + `</a></td>`
|
||||
// Draw the other cells
|
||||
for i := 1; i < 6; i++ {
|
||||
result += "<td>" + row[i] + "</td>"
|
||||
}
|
||||
result += "</tr>"
|
||||
}
|
||||
result += "</tbody></table>"
|
||||
result += "<!--" + data + "-->"
|
||||
return "<pre>" + strings.TrimSpace(data) + "</pre>"
|
||||
}
|
||||
|
||||
return result
|
||||
args := TemplateSummary{
|
||||
ServerName: serverName,
|
||||
Raw: data,
|
||||
}
|
||||
|
||||
// extract the table header
|
||||
for _, col := range strings.Split(lines[0], " ") {
|
||||
colTrimmed := strings.TrimSpace(col)
|
||||
if len(colTrimmed) == 0 {
|
||||
continue
|
||||
}
|
||||
args.Header = append(args.Header, col)
|
||||
}
|
||||
|
||||
// sort the remaining rows
|
||||
rows := lines[1:]
|
||||
sort.Strings(rows)
|
||||
|
||||
// parse each line
|
||||
for _, line := range rows {
|
||||
|
||||
// Ignore empty lines
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Parse a total of 6 columns from bird summary
|
||||
lineSplitted := splitSummaryLine.FindStringSubmatch(line)
|
||||
if lineSplitted == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
var row SummaryRowData
|
||||
|
||||
if len(lineSplitted) >= 2 {
|
||||
row.Name = strings.TrimSpace(lineSplitted[1])
|
||||
}
|
||||
if len(lineSplitted) >= 4 {
|
||||
row.Proto = strings.TrimSpace(lineSplitted[3])
|
||||
}
|
||||
if len(lineSplitted) >= 6 {
|
||||
row.Table = strings.TrimSpace(lineSplitted[5])
|
||||
}
|
||||
if len(lineSplitted) >= 8 {
|
||||
row.State = strings.TrimSpace(lineSplitted[7])
|
||||
row.MappedState = summaryStateMap[row.State]
|
||||
}
|
||||
if len(lineSplitted) >= 10 {
|
||||
row.Since = strings.TrimSpace(lineSplitted[9])
|
||||
}
|
||||
if len(lineSplitted) >= 11 {
|
||||
row.Info = strings.TrimSpace(lineSplitted[10])
|
||||
}
|
||||
|
||||
// add to the result
|
||||
args.Rows = append(args.Rows, row)
|
||||
}
|
||||
|
||||
// finally, render the summary template
|
||||
tmpl := TemplateLibrary["summary"]
|
||||
var buffer bytes.Buffer
|
||||
err := tmpl.Execute(&buffer, args)
|
||||
if err != nil {
|
||||
fmt.Println("Error rendering summary:", err.Error())
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user