- 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:
Simon Marsh
2021-01-11 13:13:21 +00:00
parent 8d0e210572
commit 6179c688be
19 changed files with 463 additions and 229 deletions

View File

@@ -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()
}