Compare commits
3 Commits
burble.dn4
...
enhancemen
Author | SHA1 | Date | |
---|---|---|---|
600bafe08d | |||
66e63c66a1 | |||
166234fa89 |
5
.gitignore
vendored
5
.gitignore
vendored
@ -16,4 +16,7 @@
|
|||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
frontend/frontend
|
frontend/frontend
|
||||||
proxy/proxy
|
proxy/proxy
|
||||||
|
|
||||||
|
# don't include generated bindata file
|
||||||
|
frontend/bindata.go
|
||||||
|
@ -31,8 +31,9 @@ script:
|
|||||||
- |
|
- |
|
||||||
# Build image
|
# Build image
|
||||||
docker build \
|
docker build \
|
||||||
|
--build-arg IMAGE_ARCH=$IMAGE_ARCH \
|
||||||
-t $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_ARCH \
|
-t $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_ARCH \
|
||||||
-f $PROGRAM/Dockerfile.$IMAGE_ARCH \
|
-f $PROGRAM/Dockerfile \
|
||||||
$PROGRAM
|
$PROGRAM
|
||||||
|
|
||||||
# Tag image :{arch} and :{arch}-build{build number}
|
# Tag image :{arch} and :{arch}-build{build number}
|
||||||
|
19
frontend/Dockerfile
Normal file
19
frontend/Dockerfile
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
FROM golang:buster AS step_0
|
||||||
|
#
|
||||||
|
# IMAGE_ARCH is the binary format of the final output
|
||||||
|
# BUILD_ARCH is the binaary format of the build host
|
||||||
|
#
|
||||||
|
ARG IMAGE_ARCH=amd64
|
||||||
|
ARG BUILD_ARCH=amd64
|
||||||
|
#
|
||||||
|
ENV CGO_ENABLED=0 GOOS=linux GOARCH=$IMAGE_ARCH GO111MODULE=on
|
||||||
|
WORKDIR /root
|
||||||
|
COPY . .
|
||||||
|
# go-bindata is run on the build host as part of the go generate step
|
||||||
|
RUN GOARCH=$BUILD_ARCH go get -u github.com/kevinburke/go-bindata/...
|
||||||
|
RUN go generate
|
||||||
|
RUN go build -ldflags "-w -s" -o /frontend
|
||||||
|
|
||||||
|
FROM scratch AS step_1
|
||||||
|
COPY --from=step_0 /frontend /
|
||||||
|
ENTRYPOINT ["/frontend"]
|
@ -1,9 +0,0 @@
|
|||||||
FROM golang:buster AS step_0
|
|
||||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on
|
|
||||||
WORKDIR /root
|
|
||||||
COPY . .
|
|
||||||
RUN go build -o /frontend
|
|
||||||
|
|
||||||
FROM scratch AS step_1
|
|
||||||
COPY --from=step_0 /frontend /
|
|
||||||
ENTRYPOINT ["/frontend"]
|
|
@ -1,9 +0,0 @@
|
|||||||
FROM golang:buster AS step_0
|
|
||||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=arm GO111MODULE=on
|
|
||||||
WORKDIR /root
|
|
||||||
COPY . .
|
|
||||||
RUN go build -o /frontend
|
|
||||||
|
|
||||||
FROM scratch AS step_1
|
|
||||||
COPY --from=step_0 /frontend /
|
|
||||||
ENTRYPOINT ["/frontend"]
|
|
@ -1,9 +0,0 @@
|
|||||||
FROM golang:buster AS step_0
|
|
||||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on
|
|
||||||
WORKDIR /root
|
|
||||||
COPY . .
|
|
||||||
RUN go build -o /frontend
|
|
||||||
|
|
||||||
FROM scratch AS step_1
|
|
||||||
COPY --from=step_0 /frontend /
|
|
||||||
ENTRYPOINT ["/frontend"]
|
|
@ -1,9 +0,0 @@
|
|||||||
FROM golang:buster AS step_0
|
|
||||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=386 GO111MODULE=on
|
|
||||||
WORKDIR /root
|
|
||||||
COPY . .
|
|
||||||
RUN go build -o /frontend
|
|
||||||
|
|
||||||
FROM scratch AS step_1
|
|
||||||
COPY --from=step_0 /frontend /
|
|
||||||
ENTRYPOINT ["/frontend"]
|
|
@ -1,9 +0,0 @@
|
|||||||
FROM golang:buster AS step_0
|
|
||||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le GO111MODULE=on
|
|
||||||
WORKDIR /root
|
|
||||||
COPY . .
|
|
||||||
RUN go build -o /frontend
|
|
||||||
|
|
||||||
FROM scratch AS step_1
|
|
||||||
COPY --from=step_0 /frontend /
|
|
||||||
ENTRYPOINT ["/frontend"]
|
|
@ -1,9 +0,0 @@
|
|||||||
FROM golang:buster AS step_0
|
|
||||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=s390x GO111MODULE=on
|
|
||||||
WORKDIR /root
|
|
||||||
COPY . .
|
|
||||||
RUN go build -o /frontend
|
|
||||||
|
|
||||||
FROM scratch AS step_1
|
|
||||||
COPY --from=step_0 /frontend /
|
|
||||||
ENTRYPOINT ["/frontend"]
|
|
2
frontend/bindata/robots.txt
Normal file
2
frontend/bindata/robots.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
User-agent: *
|
||||||
|
Disallow: /
|
14
frontend/bindata/templates/bgpmap.tpl
Normal file
14
frontend/bindata/templates/bgpmap.tpl
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<h2>BGPmap: {{ html .Target }}</h2>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/viz.js@2.1.2/viz.min.js" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/viz.js@2.1.2/lite.render.js" crossorigin="anonymous"></script>
|
||||||
|
<script>
|
||||||
|
var viz = new Viz();
|
||||||
|
viz.renderSVGElement(`{{ .Result }}`)
|
||||||
|
.then(element => {
|
||||||
|
document.body.appendChild(element);
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
document.body.innerHTML = "<pre>"+error+"</pre>"
|
||||||
|
});
|
||||||
|
</script>
|
2
frontend/bindata/templates/bird.tpl
Normal file
2
frontend/bindata/templates/bird.tpl
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<h2>{{ html .ServerName }}: {{ html .Target }}</h2>
|
||||||
|
{{ .Result }}
|
68
frontend/bindata/templates/page.tpl
Normal file
68
frontend/bindata/templates/page.tpl
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en-US">
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||||
|
<meta name="renderer" content="webkit">
|
||||||
|
<title>{{ .Title }}</title>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.1/dist/css/bootstrap.min.css" integrity="sha256-VoFZSlmyTXsegReQCNmbXrS4hBBUl/cexZvPmPWoJsY=" crossorigin="anonymous">
|
||||||
|
<meta name="robots" content="noindex, nofollow">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
|
<a class="navbar-brand" href="/">{{ .Brand }}</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||||
|
{{ $option := .URLOption }}
|
||||||
|
{{ $server := .URLServer }}
|
||||||
|
{{ $target := .URLCommand }}
|
||||||
|
{{ if .IsWhois }}
|
||||||
|
{{ $option = "summary" }}
|
||||||
|
{{ $server = .AllServersURL }}
|
||||||
|
{{ $target = "" }}
|
||||||
|
{{ end }}
|
||||||
|
<ul class="navbar-nav mr-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link{{ if .AllServersLinkActive }} active{{ end }}"
|
||||||
|
href="/{{ $option }}/{{ .AllServersURL }}/{{ $target }}"> All Servers </a>
|
||||||
|
</li>
|
||||||
|
{{ range $k, $v := .Servers }}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link{{ if eq $server $v }} active{{ end }}"
|
||||||
|
href="/{{ $option }}/{{ $v }}/{{ $target }}">{{ $v }}</a>
|
||||||
|
</li>
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
{{ if .IsWhois }}
|
||||||
|
{{ $target = .WhoisTarget }}
|
||||||
|
{{ end }}
|
||||||
|
<form class="form-inline" action="/redir" method="GET">
|
||||||
|
<div class="input-group">
|
||||||
|
<select name="action" class="form-control">
|
||||||
|
{{ range $k, $v := .Options }}
|
||||||
|
<option value="{{ $k }}"{{ if eq $k $.URLOption }} selected{{end}}>{{ $v }}</option>
|
||||||
|
{{ end }}
|
||||||
|
</select>
|
||||||
|
<input name="server" class="d-none" value="{{ $server }}">
|
||||||
|
<input name="target" class="form-control" placeholder="Target" aria-label="Target" value="{{ $target }}">
|
||||||
|
<div class="input-group-append">
|
||||||
|
<button class="btn btn-outline-success" type="submit">»</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
{{ .Content }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js" integrity="sha256-0IiaoZCI++9oAAvmCb5Y0r93XkuhvJpRalZLffQXLok=" crossorigin="anonymous"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
26
frontend/bindata/templates/summary.tpl
Normal file
26
frontend/bindata/templates/summary.tpl
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{{ $ServerName := urlquery .ServerName }}
|
||||||
|
|
||||||
|
<table class="table table-striped table-bordered table-sm">
|
||||||
|
<thead>
|
||||||
|
{{ range .Header }}
|
||||||
|
<th scope="col">{{ html . }}</th>
|
||||||
|
{{ end }}
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{ range .Rows }}
|
||||||
|
<tr class="table-{{ .MappedState }}">
|
||||||
|
<td><a href="/detail/{{ $ServerName }}/{{ urlquery .Name }}">{{ html .Name }}</a></td>
|
||||||
|
<td>{{ .Proto }}</td>
|
||||||
|
<td>{{ .Table }}</td>
|
||||||
|
<td>{{ .State }}</td>
|
||||||
|
<td>{{ .Since }}</td>
|
||||||
|
<td>{{ .Info }}</td>
|
||||||
|
</tr>
|
||||||
|
{{ end }}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
{{ .Raw }}
|
||||||
|
-->
|
||||||
|
|
2
frontend/bindata/templates/whois.tpl
Normal file
2
frontend/bindata/templates/whois.tpl
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<h2>whois {{ html .Target }}</h2>
|
||||||
|
{{ .Result }}
|
@ -2,4 +2,7 @@ module github.com/xddxdd/bird-lg-go/frontend
|
|||||||
|
|
||||||
go 1.15
|
go 1.15
|
||||||
|
|
||||||
require github.com/gorilla/handlers v1.5.1
|
require (
|
||||||
|
github.com/elazarl/go-bindata-assetfs v1.0.1
|
||||||
|
github.com/gorilla/handlers v1.5.1
|
||||||
|
)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
|
||||||
|
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||||
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
|
||||||
|
@ -7,6 +7,9 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// binary data
|
||||||
|
//go:generate go-bindata -prefix bindata -o bindata.go bindata/...
|
||||||
|
|
||||||
type settingType struct {
|
type settingType struct {
|
||||||
servers []string
|
servers []string
|
||||||
domain string
|
domain string
|
||||||
@ -93,5 +96,6 @@ func main() {
|
|||||||
*navBarBrandPtr,
|
*navBarBrandPtr,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImportTemplates()
|
||||||
webServerStart()
|
webServerStart()
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,41 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"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:]
|
path := r.URL.Path[1:]
|
||||||
split := strings.SplitN(path, "/", 3)
|
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)
|
split = strings.SplitN(path, "/", 3)
|
||||||
|
|
||||||
var args tmplArguments
|
args := TemplatePage{
|
||||||
args.Options = map[string]string{
|
Options: optionsMap,
|
||||||
"summary": "show protocols",
|
Servers: setting.servers,
|
||||||
"detail": "show protocols all",
|
AllServersLinkActive: strings.ToLower(split[1]) == strings.ToLower(strings.Join(setting.servers, "+")),
|
||||||
"route": "show route for ...",
|
AllServersURL: strings.Join(setting.servers, "+"),
|
||||||
"route_all": "show route for ... all",
|
IsWhois: isWhois,
|
||||||
"route_bgpmap": "show route for ... (bgpmap)",
|
WhoisTarget: whoisTarget,
|
||||||
"route_where": "show route where net ~ [ ... ]",
|
|
||||||
"route_where_all": "show route where net ~ [ ... ] all",
|
URLOption: strings.ToLower(split[0]),
|
||||||
"route_where_bgpmap": "show route where net ~ [ ... ] (bgpmap)",
|
URLServer: strings.ToLower(split[1]),
|
||||||
"route_generic": "show route ...",
|
URLCommand: split[2],
|
||||||
"generic": "show ...",
|
Title: setting.titleBrand + title,
|
||||||
"whois": "whois ...",
|
Brand: setting.navBarBrand,
|
||||||
"traceroute": "traceroute ...",
|
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])
|
tmpl := TemplateLibrary["page"]
|
||||||
args.URLServer = strings.ToLower(split[1])
|
err := tmpl.Execute(w, args)
|
||||||
args.URLCommand = split[2]
|
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
|
// Write the given text to http response, and add whois links for
|
||||||
@ -77,87 +97,81 @@ func smartFormatter(s string) string {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
type summaryTableArguments struct {
|
|
||||||
Headers []string
|
|
||||||
Lines [][]string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Output a table for the summary page
|
// Output a table for the summary page
|
||||||
func summaryTable(data string, serverName string) string {
|
func summaryTable(data string, serverName string) string {
|
||||||
var result string
|
|
||||||
|
|
||||||
// Sort the table, excluding title row
|
lines := strings.Split(strings.TrimSpace(data), "\n")
|
||||||
stringsSplitted := strings.Split(strings.TrimSpace(data), "\n")
|
if len(lines) <= 1 {
|
||||||
if len(stringsSplitted) <= 1 {
|
|
||||||
// Likely backend returned an error message
|
// Likely backend returned an error message
|
||||||
result = "<pre>" + strings.TrimSpace(data) + "</pre>"
|
return "<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 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()
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tmplArguments struct {
|
// template argument structures
|
||||||
|
|
||||||
|
// page
|
||||||
|
type TemplatePage struct {
|
||||||
// Global options
|
// Global options
|
||||||
Options map[string]string
|
Options map[string]string
|
||||||
Servers []string
|
Servers []string
|
||||||
@ -27,73 +31,90 @@ type tmplArguments struct {
|
|||||||
Content string
|
Content string
|
||||||
}
|
}
|
||||||
|
|
||||||
var tmpl = template.Must(template.New("tmpl").Parse(`
|
// summary
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en-US">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
|
||||||
<meta name="renderer" content="webkit">
|
|
||||||
<title>{{ .Title }}</title>
|
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.5.1/dist/css/bootstrap.min.css" integrity="sha256-VoFZSlmyTXsegReQCNmbXrS4hBBUl/cexZvPmPWoJsY=" crossorigin="anonymous">
|
|
||||||
<meta name="robots" content="noindex, nofollow">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
type SummaryRowData struct {
|
||||||
<a class="navbar-brand" href="/">{{ .Brand }}</a>
|
Name string
|
||||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
Proto string
|
||||||
<span class="navbar-toggler-icon"></span>
|
Table string
|
||||||
</button>
|
State string
|
||||||
|
MappedState string
|
||||||
|
Since string
|
||||||
|
Info string
|
||||||
|
}
|
||||||
|
|
||||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
// utility functions to allow filtering of results in the template
|
||||||
{{ $option := .URLOption }}
|
|
||||||
{{ $server := .URLServer }}
|
|
||||||
{{ $target := .URLCommand }}
|
|
||||||
{{ if .IsWhois }}
|
|
||||||
{{ $option = "summary" }}
|
|
||||||
{{ $server = .AllServersURL }}
|
|
||||||
{{ $target = "" }}
|
|
||||||
{{ end }}
|
|
||||||
<ul class="navbar-nav mr-auto">
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link{{ if .AllServersLinkActive }} active{{ end }}"
|
|
||||||
href="/{{ $option }}/{{ .AllServersURL }}/{{ $target }}"> All Servers </a>
|
|
||||||
</li>
|
|
||||||
{{ range $k, $v := .Servers }}
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link{{ if eq $server $v }} active{{ end }}"
|
|
||||||
href="/{{ $option }}/{{ $v }}/{{ $target }}">{{ $v }}</a>
|
|
||||||
</li>
|
|
||||||
{{ end }}
|
|
||||||
</ul>
|
|
||||||
{{ if .IsWhois }}
|
|
||||||
{{ $target = .WhoisTarget }}
|
|
||||||
{{ end }}
|
|
||||||
<form class="form-inline" action="/redir" method="GET">
|
|
||||||
<div class="input-group">
|
|
||||||
<select name="action" class="form-control">
|
|
||||||
{{ range $k, $v := .Options }}
|
|
||||||
<option value="{{ $k }}"{{ if eq $k $.URLOption }} selected{{end}}>{{ $v }}</option>
|
|
||||||
{{ end }}
|
|
||||||
</select>
|
|
||||||
<input name="server" class="d-none" value="{{ $server }}">
|
|
||||||
<input name="target" class="form-control" placeholder="Target" aria-label="Target" value="{{ $target }}">
|
|
||||||
<div class="input-group-append">
|
|
||||||
<button class="btn btn-outline-success" type="submit">»</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div class="container">
|
func (r SummaryRowData) NameHasPrefix(prefix string) bool {
|
||||||
{{ .Content }}
|
return strings.HasPrefix(r.Name, prefix)
|
||||||
</div>
|
}
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
func (r SummaryRowData) NameContains(prefix string) bool {
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js" integrity="sha256-0IiaoZCI++9oAAvmCb5Y0r93XkuhvJpRalZLffQXLok=" crossorigin="anonymous"></script>
|
return strings.Contains(r.Name, prefix)
|
||||||
</body>
|
}
|
||||||
</html>
|
|
||||||
`))
|
type TemplateSummary struct {
|
||||||
|
ServerName string
|
||||||
|
Raw string
|
||||||
|
Header []string
|
||||||
|
Rows []SummaryRowData
|
||||||
|
}
|
||||||
|
|
||||||
|
// whois
|
||||||
|
type TemplateWhois struct {
|
||||||
|
Target string
|
||||||
|
Result string
|
||||||
|
}
|
||||||
|
|
||||||
|
// bgpmap
|
||||||
|
type TemplateBGPmap struct {
|
||||||
|
Servers []string
|
||||||
|
Target string
|
||||||
|
Result string
|
||||||
|
}
|
||||||
|
|
||||||
|
// bird
|
||||||
|
type TemplateBird struct {
|
||||||
|
ServerName string
|
||||||
|
Target string
|
||||||
|
Result string
|
||||||
|
}
|
||||||
|
|
||||||
|
// global variable to hold the templates
|
||||||
|
|
||||||
|
var TemplateLibrary map[string]*template.Template
|
||||||
|
|
||||||
|
// list of required templates
|
||||||
|
|
||||||
|
var requiredTemplates = [...]string{
|
||||||
|
"page",
|
||||||
|
"summary",
|
||||||
|
"whois",
|
||||||
|
"bgpmap",
|
||||||
|
"bird",
|
||||||
|
}
|
||||||
|
|
||||||
|
// import templates from bindata
|
||||||
|
|
||||||
|
func ImportTemplates() {
|
||||||
|
|
||||||
|
// create a new (blank) initial template
|
||||||
|
TemplateLibrary = make(map[string]*template.Template)
|
||||||
|
|
||||||
|
// for each template that is needed
|
||||||
|
for _, tmpl := range requiredTemplates {
|
||||||
|
|
||||||
|
// extract the template definition from the bindata
|
||||||
|
def := MustAssetString("templates/" + tmpl + ".tpl")
|
||||||
|
|
||||||
|
// and add it to the template library
|
||||||
|
template, err := template.New(tmpl).Parse(def)
|
||||||
|
if err != nil {
|
||||||
|
panic("Unable to parse template (templates/" + tmpl + ": " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// store in the library
|
||||||
|
TemplateLibrary[tmpl] = template
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,47 +1,82 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"html"
|
"html"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/elazarl/go-bindata-assetfs"
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func webHandlerWhois(w http.ResponseWriter, r *http.Request) {
|
var primitiveMap = map[string]string{
|
||||||
var target string = r.URL.Path[len("/whois/"):]
|
"summary": "show protocols",
|
||||||
|
"detail": "show protocols all %s",
|
||||||
|
"route": "show route for %s",
|
||||||
|
"route_all": "show route for %s all",
|
||||||
|
"route_where": "show route where net ~ [ %s ]",
|
||||||
|
"route_where_all": "show route where net ~ [ %s ] all",
|
||||||
|
"route_generic": "show route %s",
|
||||||
|
"generic": "show %s",
|
||||||
|
"traceroute": "%s",
|
||||||
|
}
|
||||||
|
|
||||||
renderTemplate(
|
// serve up a generic error
|
||||||
|
func serverError(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
|
w.Write([]byte("500 Internal Server Error"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// WHOIS pages
|
||||||
|
func webHandlerWhois(w http.ResponseWriter, r *http.Request) {
|
||||||
|
target, err := url.PathUnescape(r.URL.Path[len("/whois/"):])
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// render the whois template
|
||||||
|
args := TemplateWhois{
|
||||||
|
Target: target,
|
||||||
|
Result: smartFormatter(whois(target)),
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl := TemplateLibrary["whois"]
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
err = tmpl.Execute(&buffer, args)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error rendering whois template:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPageTemplate(
|
||||||
w, r,
|
w, r,
|
||||||
" - whois "+html.EscapeString(target),
|
" - whois "+html.EscapeString(target),
|
||||||
"<h2>whois "+html.EscapeString(target)+"</h2>"+smartFormatter(whois(target)),
|
buffer.String(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serve up results from bird
|
||||||
func webBackendCommunicator(endpoint string, command string) func(w http.ResponseWriter, r *http.Request) {
|
func webBackendCommunicator(endpoint string, command string) func(w http.ResponseWriter, r *http.Request) {
|
||||||
backendCommandPrimitive, commandPresent := (map[string]string{
|
|
||||||
"summary": "show protocols",
|
|
||||||
"detail": "show protocols all %s",
|
|
||||||
"route": "show route for %s",
|
|
||||||
"route_all": "show route for %s all",
|
|
||||||
"route_where": "show route where net ~ [ %s ]",
|
|
||||||
"route_where_all": "show route where net ~ [ %s ] all",
|
|
||||||
"route_generic": "show route %s",
|
|
||||||
"generic": "show %s",
|
|
||||||
"traceroute": "%s",
|
|
||||||
})[command]
|
|
||||||
|
|
||||||
|
backendCommandPrimitive, commandPresent := primitiveMap[command]
|
||||||
if !commandPresent {
|
if !commandPresent {
|
||||||
panic("invalid command: " + command)
|
panic("invalid command: " + command)
|
||||||
}
|
}
|
||||||
|
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
split := strings.SplitN(r.URL.Path[1:], "/", 4)
|
split := strings.SplitN(r.URL.Path[1:], "/", 3)
|
||||||
var urlCommands string
|
var urlCommands string
|
||||||
if len(split) >= 3 {
|
if len(split) >= 3 {
|
||||||
urlCommands = split[2]
|
tmp, err := url.PathUnescape(split[2])
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
urlCommands = tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
var backendCommand string
|
var backendCommand string
|
||||||
@ -52,26 +87,50 @@ func webBackendCommunicator(endpoint string, command string) func(w http.Respons
|
|||||||
}
|
}
|
||||||
backendCommand = strings.TrimSpace(backendCommand)
|
backendCommand = strings.TrimSpace(backendCommand)
|
||||||
|
|
||||||
var servers []string = strings.Split(split[1], "+")
|
escapedServers, err := url.PathUnescape(split[1])
|
||||||
|
if err != nil {
|
||||||
|
serverError(w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
servers := strings.Split(escapedServers, "+")
|
||||||
|
|
||||||
var responses []string = batchRequest(servers, endpoint, backendCommand)
|
var responses []string = batchRequest(servers, endpoint, backendCommand)
|
||||||
var result string
|
var content string
|
||||||
for i, response := range responses {
|
for i, response := range responses {
|
||||||
result += "<h2>" + html.EscapeString(servers[i]) + ": " + html.EscapeString(backendCommand) + "</h2>"
|
|
||||||
|
var result string
|
||||||
if (endpoint == "bird") && backendCommand == "show protocols" && len(response) > 4 && strings.ToLower(response[0:4]) == "name" {
|
if (endpoint == "bird") && backendCommand == "show protocols" && len(response) > 4 && strings.ToLower(response[0:4]) == "name" {
|
||||||
result += summaryTable(response, servers[i])
|
result = summaryTable(response, servers[i])
|
||||||
} else {
|
} else {
|
||||||
result += smartFormatter(response)
|
result = smartFormatter(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// render the bird result template
|
||||||
|
args := TemplateBird{
|
||||||
|
ServerName: servers[i],
|
||||||
|
Target: backendCommand,
|
||||||
|
Result: result,
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl := TemplateLibrary["bird"]
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
err := tmpl.Execute(&buffer, args)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error rendering bird template:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
content += buffer.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTemplate(
|
renderPageTemplate(
|
||||||
w, r,
|
w, r,
|
||||||
" - "+html.EscapeString(endpoint+" "+backendCommand),
|
" - "+html.EscapeString(endpoint+" "+backendCommand),
|
||||||
result,
|
content,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// bgpmap result
|
||||||
func webHandlerBGPMap(endpoint string, command string) func(w http.ResponseWriter, r *http.Request) {
|
func webHandlerBGPMap(endpoint string, command string) func(w http.ResponseWriter, r *http.Request) {
|
||||||
backendCommandPrimitive, commandPresent := (map[string]string{
|
backendCommandPrimitive, commandPresent := (map[string]string{
|
||||||
"route_bgpmap": "show route for %s all",
|
"route_bgpmap": "show route for %s all",
|
||||||
@ -95,51 +154,70 @@ func webHandlerBGPMap(endpoint string, command string) func(w http.ResponseWrite
|
|||||||
|
|
||||||
var servers []string = strings.Split(split[1], "+")
|
var servers []string = strings.Split(split[1], "+")
|
||||||
var responses []string = batchRequest(servers, endpoint, backendCommand)
|
var responses []string = batchRequest(servers, endpoint, backendCommand)
|
||||||
renderTemplate(
|
|
||||||
|
// render the bgpmap result template
|
||||||
|
args := TemplateBGPmap{
|
||||||
|
Servers: servers,
|
||||||
|
Target: backendCommand,
|
||||||
|
Result: birdRouteToGraphviz(servers, responses, urlCommands),
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpl := TemplateLibrary["bgpmap"]
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
err := tmpl.Execute(&buffer, args)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error rendering bgpmap template:", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPageTemplate(
|
||||||
w, r,
|
w, r,
|
||||||
" - "+html.EscapeString(endpoint+" "+backendCommand),
|
" - "+html.EscapeString(endpoint+" "+backendCommand),
|
||||||
`
|
buffer.String(),
|
||||||
<script src="https://cdn.jsdelivr.net/npm/viz.js@2.1.2/viz.min.js" crossorigin="anonymous"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/viz.js@2.1.2/lite.render.js" crossorigin="anonymous"></script>
|
|
||||||
<script>
|
|
||||||
var viz = new Viz();
|
|
||||||
viz.renderSVGElement(`+"`"+birdRouteToGraphviz(servers, responses, urlCommands)+"`"+`)
|
|
||||||
.then(element => {
|
|
||||||
document.body.appendChild(element);
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
document.body.innerHTML = "<pre>"+error+"</pre>"
|
|
||||||
});
|
|
||||||
</script>`,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// redirect from the form input to a path style query
|
||||||
func webHandlerNavbarFormRedirect(w http.ResponseWriter, r *http.Request) {
|
func webHandlerNavbarFormRedirect(w http.ResponseWriter, r *http.Request) {
|
||||||
query := r.URL.Query()
|
query := r.URL.Query()
|
||||||
if query.Get("action") == "whois" {
|
|
||||||
http.Redirect(w, r, "/"+query.Get("action")+"/"+query.Get("target"), 302)
|
action := query.Get("action")
|
||||||
} else if query.Get("action") == "summary" {
|
|
||||||
http.Redirect(w, r, "/"+query.Get("action")+"/"+query.Get("server")+"/", 302)
|
switch action {
|
||||||
} else {
|
case "whois":
|
||||||
http.Redirect(w, r, "/"+query.Get("action")+"/"+query.Get("server")+"/"+query.Get("target"), 302)
|
target := url.PathEscape(query.Get("target"))
|
||||||
|
http.Redirect(w, r, "/"+action+"/"+target, 302)
|
||||||
|
case "summary":
|
||||||
|
server := url.PathEscape(query.Get("server"))
|
||||||
|
http.Redirect(w, r, "/"+action+"/"+server+"/", 302)
|
||||||
|
default:
|
||||||
|
server := url.PathEscape(query.Get("server"))
|
||||||
|
target := url.PathEscape(query.Get("target"))
|
||||||
|
http.Redirect(w, r, "/"+action+"/"+server+"/"+target, 302)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func webHandlerRobotsTxt(w http.ResponseWriter, r *http.Request) {
|
// set up routing paths and start webserver
|
||||||
w.Write([]byte("User-agent: *\nDisallow: /\n"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func webHandler404(w http.ResponseWriter, r *http.Request) {
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
w.Write([]byte("404 not found\n"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func webServerStart() {
|
func webServerStart() {
|
||||||
// Start HTTP server
|
|
||||||
|
// redirect main page to all server summary
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, "/summary/"+strings.Join(setting.servers, "+"), 302)
|
http.Redirect(w, r, "/summary/"+strings.Join(setting.servers, "+"), 302)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// serve static pages using the AssetFS and bindata
|
||||||
|
fs := http.FileServer(&assetfs.AssetFS{
|
||||||
|
Asset: Asset,
|
||||||
|
AssetDir: AssetDir,
|
||||||
|
AssetInfo: AssetInfo,
|
||||||
|
Prefix: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
http.Handle("/static/", fs)
|
||||||
|
http.Handle("/robots.txt", fs)
|
||||||
|
http.Handle("/favicon.ico", fs)
|
||||||
|
|
||||||
|
// backend routes
|
||||||
http.HandleFunc("/summary/", webBackendCommunicator("bird", "summary"))
|
http.HandleFunc("/summary/", webBackendCommunicator("bird", "summary"))
|
||||||
http.HandleFunc("/detail/", webBackendCommunicator("bird", "detail"))
|
http.HandleFunc("/detail/", webBackendCommunicator("bird", "detail"))
|
||||||
http.HandleFunc("/route/", webBackendCommunicator("bird", "route"))
|
http.HandleFunc("/route/", webBackendCommunicator("bird", "route"))
|
||||||
@ -154,7 +232,7 @@ func webServerStart() {
|
|||||||
http.HandleFunc("/whois/", webHandlerWhois)
|
http.HandleFunc("/whois/", webHandlerWhois)
|
||||||
http.HandleFunc("/redir", webHandlerNavbarFormRedirect)
|
http.HandleFunc("/redir", webHandlerNavbarFormRedirect)
|
||||||
http.HandleFunc("/telegram/", webHandlerTelegramBot)
|
http.HandleFunc("/telegram/", webHandlerTelegramBot)
|
||||||
http.HandleFunc("/robots.txt", webHandlerRobotsTxt)
|
|
||||||
http.HandleFunc("/favicon.ico", webHandler404)
|
// Start HTTP server
|
||||||
http.ListenAndServe(setting.listen, handlers.LoggingHandler(os.Stdout, http.DefaultServeMux))
|
http.ListenAndServe(setting.listen, handlers.LoggingHandler(os.Stdout, http.DefaultServeMux))
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
FROM golang:buster AS step_0
|
FROM golang:buster AS step_0
|
||||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on
|
#
|
||||||
|
# IMAGE_ARCH is the binary format of the final output
|
||||||
|
#
|
||||||
|
ARG IMAGE_ARCH=amd64
|
||||||
|
#
|
||||||
|
ENV CGO_ENABLED=0 GOOS=linux GOARCH=$IMAGE_ARCH GO111MODULE=on
|
||||||
WORKDIR /root
|
WORKDIR /root
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN go build -o /proxy
|
RUN go build -ldflags "-w -s" -o /proxy
|
||||||
|
|
||||||
FROM amd64/debian AS step_1
|
FROM amd64/debian AS step_1
|
||||||
ENV TARGET_ARCH=x86_64
|
ENV TARGET_ARCH=x86_64
|
@ -1,23 +0,0 @@
|
|||||||
FROM golang:buster AS step_0
|
|
||||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=arm GO111MODULE=on
|
|
||||||
WORKDIR /root
|
|
||||||
COPY . .
|
|
||||||
RUN go build -o /proxy
|
|
||||||
|
|
||||||
FROM arm32v7/debian AS step_1
|
|
||||||
ENV TARGET_ARCH=arm
|
|
||||||
WORKDIR /root
|
|
||||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y \
|
|
||||||
build-essential musl-dev musl-tools tar wget git
|
|
||||||
RUN git clone https://github.com/sabotage-linux/kernel-headers.git
|
|
||||||
RUN wget https://sourceforge.net/projects/traceroute/files/traceroute/traceroute-2.1.0/traceroute-2.1.0.tar.gz/download \
|
|
||||||
-O traceroute-2.1.0.tar.gz
|
|
||||||
RUN tar xvf traceroute-2.1.0.tar.gz \
|
|
||||||
&& cd traceroute-2.1.0 \
|
|
||||||
&& make -j4 CC=musl-gcc CFLAGS="-I/root/kernel-headers/${TARGET_ARCH}/include" LDFLAGS="-static"
|
|
||||||
|
|
||||||
FROM scratch AS step_2
|
|
||||||
ENV PATH=/
|
|
||||||
COPY --from=step_0 /proxy /
|
|
||||||
COPY --from=step_1 /root/traceroute-2.1.0/traceroute/traceroute /
|
|
||||||
ENTRYPOINT ["/proxy"]
|
|
@ -1,23 +0,0 @@
|
|||||||
FROM golang:buster AS step_0
|
|
||||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=arm64 GO111MODULE=on
|
|
||||||
WORKDIR /root
|
|
||||||
COPY . .
|
|
||||||
RUN go build -o /proxy
|
|
||||||
|
|
||||||
FROM arm64v8/debian AS step_1
|
|
||||||
ENV TARGET_ARCH=arm64
|
|
||||||
WORKDIR /root
|
|
||||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y \
|
|
||||||
build-essential musl-dev musl-tools tar wget git
|
|
||||||
RUN git clone https://github.com/sabotage-linux/kernel-headers.git
|
|
||||||
RUN wget https://sourceforge.net/projects/traceroute/files/traceroute/traceroute-2.1.0/traceroute-2.1.0.tar.gz/download \
|
|
||||||
-O traceroute-2.1.0.tar.gz
|
|
||||||
RUN tar xvf traceroute-2.1.0.tar.gz \
|
|
||||||
&& cd traceroute-2.1.0 \
|
|
||||||
&& make -j4 CC=musl-gcc CFLAGS="-I/root/kernel-headers/${TARGET_ARCH}/include" LDFLAGS="-static"
|
|
||||||
|
|
||||||
FROM scratch AS step_2
|
|
||||||
ENV PATH=/
|
|
||||||
COPY --from=step_0 /proxy /
|
|
||||||
COPY --from=step_1 /root/traceroute-2.1.0/traceroute/traceroute /
|
|
||||||
ENTRYPOINT ["/proxy"]
|
|
@ -1,23 +0,0 @@
|
|||||||
FROM golang:buster AS step_0
|
|
||||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=386 GO111MODULE=on
|
|
||||||
WORKDIR /root
|
|
||||||
COPY . .
|
|
||||||
RUN go build -o /proxy
|
|
||||||
|
|
||||||
FROM i386/debian AS step_1
|
|
||||||
ENV TARGET_ARCH=x86
|
|
||||||
WORKDIR /root
|
|
||||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y \
|
|
||||||
build-essential musl-dev musl-tools tar wget git
|
|
||||||
RUN git clone https://github.com/sabotage-linux/kernel-headers.git
|
|
||||||
RUN wget https://sourceforge.net/projects/traceroute/files/traceroute/traceroute-2.1.0/traceroute-2.1.0.tar.gz/download \
|
|
||||||
-O traceroute-2.1.0.tar.gz
|
|
||||||
RUN tar xvf traceroute-2.1.0.tar.gz \
|
|
||||||
&& cd traceroute-2.1.0 \
|
|
||||||
&& make -j4 CC=musl-gcc CFLAGS="-I/root/kernel-headers/${TARGET_ARCH}/include" LDFLAGS="-static"
|
|
||||||
|
|
||||||
FROM scratch AS step_2
|
|
||||||
ENV PATH=/
|
|
||||||
COPY --from=step_0 /proxy /
|
|
||||||
COPY --from=step_1 /root/traceroute-2.1.0/traceroute/traceroute /
|
|
||||||
ENTRYPOINT ["/proxy"]
|
|
@ -1,23 +0,0 @@
|
|||||||
FROM golang:buster AS step_0
|
|
||||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=ppc64le GO111MODULE=on
|
|
||||||
WORKDIR /root
|
|
||||||
COPY . .
|
|
||||||
RUN go build -o /proxy
|
|
||||||
|
|
||||||
FROM ppc64le/debian AS step_1
|
|
||||||
ENV TARGET_ARCH=ppc64le
|
|
||||||
WORKDIR /root
|
|
||||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y \
|
|
||||||
build-essential musl-dev musl-tools tar wget git
|
|
||||||
RUN git clone https://github.com/sabotage-linux/kernel-headers.git
|
|
||||||
RUN wget https://sourceforge.net/projects/traceroute/files/traceroute/traceroute-2.1.0/traceroute-2.1.0.tar.gz/download \
|
|
||||||
-O traceroute-2.1.0.tar.gz
|
|
||||||
RUN tar xvf traceroute-2.1.0.tar.gz \
|
|
||||||
&& cd traceroute-2.1.0 \
|
|
||||||
&& make -j4 CC=musl-gcc CFLAGS="-I/root/kernel-headers/${TARGET_ARCH}/include" LDFLAGS="-static"
|
|
||||||
|
|
||||||
FROM scratch AS step_2
|
|
||||||
ENV PATH=/
|
|
||||||
COPY --from=step_0 /proxy /
|
|
||||||
COPY --from=step_1 /root/traceroute-2.1.0/traceroute/traceroute /
|
|
||||||
ENTRYPOINT ["/proxy"]
|
|
@ -1,23 +0,0 @@
|
|||||||
FROM golang:buster AS step_0
|
|
||||||
ENV CGO_ENABLED=0 GOOS=linux GOARCH=s390x GO111MODULE=on
|
|
||||||
WORKDIR /root
|
|
||||||
COPY . .
|
|
||||||
RUN go build -o /proxy
|
|
||||||
|
|
||||||
FROM s390x/debian AS step_1
|
|
||||||
ENV TARGET_ARCH=s390
|
|
||||||
WORKDIR /root
|
|
||||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y \
|
|
||||||
build-essential musl-dev musl-tools tar wget git
|
|
||||||
RUN git clone https://github.com/sabotage-linux/kernel-headers.git
|
|
||||||
RUN wget https://sourceforge.net/projects/traceroute/files/traceroute/traceroute-2.1.0/traceroute-2.1.0.tar.gz/download \
|
|
||||||
-O traceroute-2.1.0.tar.gz
|
|
||||||
RUN tar xvf traceroute-2.1.0.tar.gz \
|
|
||||||
&& cd traceroute-2.1.0 \
|
|
||||||
&& make -j4 CC=musl-gcc CFLAGS="-I/root/kernel-headers/${TARGET_ARCH}/include" LDFLAGS="-static"
|
|
||||||
|
|
||||||
FROM scratch AS step_2
|
|
||||||
ENV PATH=/
|
|
||||||
COPY --from=step_0 /proxy /
|
|
||||||
COPY --from=step_1 /root/traceroute-2.1.0/traceroute/traceroute /
|
|
||||||
ENTRYPOINT ["/proxy"]
|
|
Loading…
x
Reference in New Issue
Block a user