You've already forked bird-lg-go
mirror of
https://github.com/xddxdd/bird-lg-go
synced 2025-10-17 22:42:12 +02:00
Compare commits
56 Commits
dependabot
...
v1.1.1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
47c66b125c | ||
![]() |
9e17b116f1 | ||
![]() |
335ad40634 | ||
![]() |
6ec0f2e7a6 | ||
![]() |
4b73cf0fcb | ||
![]() |
3b1d001543 | ||
![]() |
675cb26ed1 | ||
![]() |
556d3e50d3 | ||
![]() |
06796f546e | ||
![]() |
d029d6684c | ||
![]() |
5ce0f55f35 | ||
![]() |
890ab51b07 | ||
![]() |
8e4a35cc8c | ||
![]() |
97f3c6088f | ||
![]() |
982326a678 | ||
![]() |
4b3980f6bd | ||
![]() |
6f6b2bd283 | ||
![]() |
892a7bee22 | ||
![]() |
348295b9aa | ||
![]() |
950c018b18 | ||
![]() |
26efeb4996 | ||
![]() |
5a5dfbc93f | ||
![]() |
f60a292129 | ||
![]() |
e7f6026854 | ||
![]() |
a4e0f4c193 | ||
![]() |
af5b653326 | ||
![]() |
58847759b3 | ||
![]() |
6481e7cc8d | ||
![]() |
2166d73b3d | ||
![]() |
a64d839e2c | ||
![]() |
1a3c618522 | ||
![]() |
fbd190628c | ||
![]() |
823b639245 | ||
![]() |
b0c0e5442d | ||
![]() |
4e4ce89418 | ||
![]() |
234aadadd9 | ||
![]() |
bee26f421c | ||
![]() |
2e0cb131ca | ||
![]() |
4c248c638a | ||
![]() |
3550362a4d | ||
![]() |
256a80646f | ||
![]() |
03c42eb1e8 | ||
![]() |
aea85e774c | ||
![]() |
80d9351a58 | ||
![]() |
5e0bc081e6 | ||
![]() |
4d53d1f095 | ||
![]() |
5883015294 | ||
![]() |
80e66a7a81 | ||
![]() |
41329da7cb | ||
![]() |
8e56705205 | ||
![]() |
6a8b3a0e55 | ||
![]() |
83ab403706 | ||
![]() |
7c7814cc7b | ||
![]() |
8598060cc0 | ||
![]() |
bda06ddd5e | ||
![]() |
f404072ab8 |
@@ -4,16 +4,19 @@ workflows:
|
||||
docker:
|
||||
jobs:
|
||||
- build
|
||||
- deploy:
|
||||
- docker-frontend-deploy:
|
||||
context:
|
||||
- docker
|
||||
requires:
|
||||
- build
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
- docker-proxy-deploy:
|
||||
context:
|
||||
- docker
|
||||
requires:
|
||||
- build
|
||||
matrix:
|
||||
parameters:
|
||||
program: [frontend, proxy]
|
||||
# latest is amd64 arch + push to default latest tag
|
||||
image_arch: [latest, i386, arm32v7, arm64v8, ppc64le, s390x]
|
||||
filters:
|
||||
branches:
|
||||
only: master
|
||||
@@ -21,41 +24,104 @@ workflows:
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/golang:1.15
|
||||
working_directory: /go/src/github.com/xddxdd/bird-lg-go
|
||||
- image: cimg/go:1.17
|
||||
working_directory: /home/circleci/go/src/github.com/xddxdd/bird-lg-go
|
||||
steps:
|
||||
- checkout
|
||||
- run: go get -v -t -d ./...
|
||||
- run: go get -u github.com/kevinburke/go-bindata/...
|
||||
- run: cd frontend && go generate
|
||||
- run: go test -v ./...
|
||||
deploy:
|
||||
docker:
|
||||
- image: circleci/golang:1.15
|
||||
working_directory: /go/src/github.com/xddxdd/bird-lg-go
|
||||
parameters:
|
||||
image_arch:
|
||||
type: string
|
||||
program:
|
||||
type: string
|
||||
steps:
|
||||
- checkout
|
||||
- setup_remote_docker:
|
||||
version: 19.03.13
|
||||
- run:
|
||||
name: Install GPP
|
||||
- run:
|
||||
name: Test frontend
|
||||
command: |
|
||||
sudo apt-get update && sudo apt-get install -y gpp
|
||||
export GO111MODULE=on
|
||||
cd frontend
|
||||
go get -v -t -d ./...
|
||||
go test -v ./...
|
||||
- run:
|
||||
name: Test proxy
|
||||
command: |
|
||||
export GO111MODULE=on
|
||||
cd proxy
|
||||
go get -v -t -d ./...
|
||||
go test -v ./...
|
||||
|
||||
docker-frontend-deploy:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
environment:
|
||||
BUILDX_PLATFORMS: linux/amd64,linux/arm64,linux/386,linux/arm/v7
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install buildx
|
||||
command: |
|
||||
BUILDX_BINARY_URL="https://github.com/docker/buildx/releases/download/v0.7.1/buildx-v0.7.1.linux-amd64"
|
||||
|
||||
curl --output docker-buildx \
|
||||
--silent --show-error --location --fail --retry 3 \
|
||||
"$BUILDX_BINARY_URL"
|
||||
|
||||
mkdir -p ~/.docker/cli-plugins
|
||||
|
||||
mv docker-buildx ~/.docker/cli-plugins/
|
||||
chmod a+x ~/.docker/cli-plugins/docker-buildx
|
||||
|
||||
docker buildx install
|
||||
# Run binfmt
|
||||
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
- run:
|
||||
name: Build Docker image
|
||||
environment:
|
||||
IMAGE_ARCH: << parameters.image_arch >>
|
||||
PROGRAM: << parameters.program >>
|
||||
BUILD_ID: << pipeline.number >>
|
||||
command: |
|
||||
make -f Makefile.docker _crossbuild
|
||||
echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin
|
||||
make -f Makefile.docker \
|
||||
DOCKER_USERNAME=$DOCKER_USERNAME \
|
||||
BUILD_ID=circleci-build$BUILD_ID \
|
||||
$PROGRAM/$IMAGE_ARCH
|
||||
docker buildx create --name mybuilder --use
|
||||
docker buildx build \
|
||||
--platform $BUILDX_PLATFORMS \
|
||||
-t $DOCKER_USERNAME/bird-lg-go:circleci-build$BUILD_ID \
|
||||
--progress plain \
|
||||
--push frontend
|
||||
docker buildx build \
|
||||
--platform $BUILDX_PLATFORMS \
|
||||
-t $DOCKER_USERNAME/bird-lg-go:latest \
|
||||
--progress plain \
|
||||
--push frontend
|
||||
|
||||
docker-proxy-deploy:
|
||||
machine:
|
||||
image: ubuntu-2004:202111-02
|
||||
environment:
|
||||
BUILDX_PLATFORMS: linux/amd64,linux/arm64,linux/386,linux/arm/v7
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install buildx
|
||||
command: |
|
||||
BUILDX_BINARY_URL="https://github.com/docker/buildx/releases/download/v0.7.1/buildx-v0.7.1.linux-amd64"
|
||||
|
||||
curl --output docker-buildx \
|
||||
--silent --show-error --location --fail --retry 3 \
|
||||
"$BUILDX_BINARY_URL"
|
||||
|
||||
mkdir -p ~/.docker/cli-plugins
|
||||
|
||||
mv docker-buildx ~/.docker/cli-plugins/
|
||||
chmod a+x ~/.docker/cli-plugins/docker-buildx
|
||||
|
||||
docker buildx install
|
||||
# Run binfmt
|
||||
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
- run:
|
||||
name: Build Docker image
|
||||
environment:
|
||||
BUILD_ID: << pipeline.number >>
|
||||
command: |
|
||||
echo $DOCKER_PASSWORD | docker login -u $DOCKER_USERNAME --password-stdin
|
||||
docker buildx create --name mybuilder --use
|
||||
docker buildx build \
|
||||
--platform $BUILDX_PLATFORMS \
|
||||
-t $DOCKER_USERNAME/bird-lgproxy-go:circleci-build$BUILD_ID \
|
||||
--push proxy
|
||||
docker buildx build \
|
||||
--platform $BUILDX_PLATFORMS \
|
||||
-t $DOCKER_USERNAME/bird-lgproxy-go:latest \
|
||||
--progress plain \
|
||||
--push proxy
|
||||
|
35
.github/workflows/release.yaml
vendored
Normal file
35
.github/workflows/release.yaml
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
releases-matrix:
|
||||
name: Release Go Binary
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
goos: [linux, windows, darwin]
|
||||
goarch: ["386", amd64, "arm", arm64]
|
||||
exclude:
|
||||
- goarch: "386"
|
||||
goos: darwin
|
||||
- goarch: "arm"
|
||||
goos: darwin
|
||||
- goarch: "arm"
|
||||
goos: windows
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: wangyoucao577/go-release-action@v1.30
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
goos: ${{ matrix.goos }}
|
||||
goarch: ${{ matrix.goarch }}
|
||||
project_path: "./frontend"
|
||||
binary_name: "bird-lg-go"
|
||||
- uses: wangyoucao577/go-release-action@v1.30
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
goos: ${{ matrix.goos }}
|
||||
goarch: ${{ matrix.goarch }}
|
||||
project_path: "./proxy"
|
||||
binary_name: "bird-lgproxy-go"
|
4
Makefile
4
Makefile
@@ -9,5 +9,5 @@ proxy:
|
||||
all: frontend proxy
|
||||
|
||||
install:
|
||||
install -m 755 frontend/frontend /usr/local/bin/frontend
|
||||
install -m 755 proxy/proxy /usr/local/bin/proxy
|
||||
install -m 755 frontend/frontend /usr/local/bin/bird-lg-go
|
||||
install -m 755 proxy/proxy /usr/local/bin/bird-lgproxy-go
|
||||
|
@@ -1,77 +0,0 @@
|
||||
# Basic definitions
|
||||
DOCKER_USERNAME := xddxdd
|
||||
ARCHITECTURES := amd64 i386 arm32v7 arm64v8 ppc64le s390x
|
||||
IMAGES := frontend proxy
|
||||
|
||||
# General Purpose Preprocessor config
|
||||
GPP_INCLUDE_DIR := include
|
||||
GPP_FLAGS_U := "" "" "(" "," ")" "(" ")" "\#" ""
|
||||
GPP_FLAGS_M := "\#" "\n" " " " " "\n" "(" ")"
|
||||
GPP_FLAGS_EXTRA := +c "\\\n" ""
|
||||
GPP_FLAGS := -I ${GPP_INCLUDE_DIR} --nostdinc -U ${GPP_FLAGS_U} -M ${GPP_FLAGS_M} ${GPP_FLAGS_EXTRA}
|
||||
|
||||
BUILD_ID ?= $(shell date +%Y%m%d%H%M)
|
||||
|
||||
define create-image-arch-target
|
||||
frontend/Dockerfile.$1: frontend/template.Dockerfile
|
||||
@gpp ${GPP_FLAGS} -D ARCH_$(shell echo $1 | tr a-z A-Z) -o frontend/Dockerfile.$1 frontend/template.Dockerfile || rm -rf frontend/Dockerfile.$1
|
||||
|
||||
frontend/$1: frontend/Dockerfile.$1
|
||||
@if [ -f frontend/Dockerfile.$1 ]; then \
|
||||
docker build --pull --no-cache -t ${DOCKER_USERNAME}/bird-lg-go:$1-${BUILD_ID} -f frontend/Dockerfile.$1 frontend || exit 1; \
|
||||
docker push ${DOCKER_USERNAME}/bird-lg-go:$1-${BUILD_ID} || exit 1; \
|
||||
docker tag ${DOCKER_USERNAME}/bird-lg-go:$1-${BUILD_ID} ${DOCKER_USERNAME}/bird-lg-go:$1 || exit 1; \
|
||||
docker push ${DOCKER_USERNAME}/bird-lg-go:$1 || exit 1; \
|
||||
else \
|
||||
echo "Dockerfile generation failed, see error above"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
proxy/Dockerfile.$1: proxy/template.Dockerfile
|
||||
@gpp ${GPP_FLAGS} -D ARCH_$(shell echo $1 | tr a-z A-Z) -o proxy/Dockerfile.$1 proxy/template.Dockerfile || rm -rf proxy/Dockerfile.$1
|
||||
|
||||
proxy/$1: proxy/Dockerfile.$1
|
||||
@if [ -f proxy/Dockerfile.$1 ]; then \
|
||||
docker build --pull --no-cache -t ${DOCKER_USERNAME}/bird-lgproxy-go:$1-${BUILD_ID} -f proxy/Dockerfile.$1 proxy || exit 1; \
|
||||
docker push ${DOCKER_USERNAME}/bird-lgproxy-go:$1-${BUILD_ID} || exit 1; \
|
||||
docker tag ${DOCKER_USERNAME}/bird-lgproxy-go:$1-${BUILD_ID} ${DOCKER_USERNAME}/bird-lgproxy-go:$1 || exit 1; \
|
||||
docker push ${DOCKER_USERNAME}/bird-lgproxy-go:$1 || exit 1; \
|
||||
else \
|
||||
echo "Dockerfile generation failed, see error above"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
endef
|
||||
|
||||
$(foreach arch,${ARCHITECTURES},$(eval $(call create-image-arch-target,$(arch))))
|
||||
|
||||
frontend:$(foreach arch,latest ${ARCHITECTURES},frontend/${arch})
|
||||
|
||||
frontend/latest: frontend/amd64
|
||||
@docker tag ${DOCKER_USERNAME}/bird-lg-go:amd64-${BUILD_ID} ${DOCKER_USERNAME}/bird-lg-go:${BUILD_ID} || exit 1
|
||||
@docker push ${DOCKER_USERNAME}/bird-lg-go:${BUILD_ID} || exit 1
|
||||
@docker tag ${DOCKER_USERNAME}/bird-lg-go:amd64-${BUILD_ID} ${DOCKER_USERNAME}/bird-lg-go:latest || exit 1
|
||||
@docker push ${DOCKER_USERNAME}/bird-lg-go:latest || exit 1
|
||||
|
||||
proxy:$(foreach arch,latest ${ARCHITECTURES},proxy/${arch})
|
||||
|
||||
proxy/latest: proxy/amd64
|
||||
@docker tag ${DOCKER_USERNAME}/bird-lgproxy-go:amd64-${BUILD_ID} ${DOCKER_USERNAME}/bird-lgproxy-go:${BUILD_ID} || exit 1
|
||||
@docker push ${DOCKER_USERNAME}/bird-lgproxy-go:${BUILD_ID} || exit 1
|
||||
@docker tag ${DOCKER_USERNAME}/bird-lgproxy-go:amd64-${BUILD_ID} ${DOCKER_USERNAME}/bird-lgproxy-go:latest || exit 1
|
||||
@docker push ${DOCKER_USERNAME}/bird-lgproxy-go:latest || exit 1
|
||||
|
||||
.DEFAULT_GOAL := images
|
||||
.DELETE_ON_ERROR:
|
||||
.SECONDARY:
|
||||
|
||||
# Target to enable multiarch support
|
||||
_crossbuild:
|
||||
@docker run --rm --privileged multiarch/qemu-user-static --reset -p yes >/dev/null
|
||||
|
||||
dockerfiles: $(foreach image,${IMAGES},$(foreach arch,${ARCHITECTURES},$(image)/Dockerfile.$(arch)))
|
||||
|
||||
images: $(foreach image,${IMAGES},$(image))
|
||||
|
||||
clean:
|
||||
@rm -rf */Dockerfile.{$(shell echo ${ARCHITECTURES} | sed "s/ /,/g")}
|
@@ -153,9 +153,11 @@ Request:
|
||||
|
||||
```json
|
||||
{
|
||||
"servers": [],
|
||||
"type": "server_list",
|
||||
"args": ""
|
||||
"servers": [
|
||||
"alpha"
|
||||
],
|
||||
"type": "bird",
|
||||
"args": "show status"
|
||||
}
|
||||
```
|
||||
|
||||
@@ -179,11 +181,9 @@ Request:
|
||||
|
||||
```json
|
||||
{
|
||||
"servers": [
|
||||
"alpha"
|
||||
],
|
||||
"type": "bird",
|
||||
"args": "show status"
|
||||
"servers": [],
|
||||
"type": "server_list",
|
||||
"args": ""
|
||||
}
|
||||
```
|
||||
|
22
docs/Telegram.md
Normal file
22
docs/Telegram.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# Telegram Bot Webhook
|
||||
|
||||
The frontend can act as a Telegram Bot webhook endpoint, to add BGP route/traceroute/whois lookup functionality to your tech group.
|
||||
|
||||
There is no configuration necessary on the frontend, just start it up normally.
|
||||
|
||||
Set your Telegram Bot webhook URL to `https://your.frontend.com/telegram/alpha+beta+gamma`, where `alpha+beta+gamma` is the list of servers to be queried on Telegram commands, separated by `+`.
|
||||
|
||||
You may omit `alpha+beta+gamma` to use all your servers, but it is not recommended when you have lots of servers, or the message would be too long and hard to read.
|
||||
|
||||
## Example of setting the webhook
|
||||
|
||||
```bash
|
||||
curl "https://api.telegram.org/bot${BOT_TOKEN}/setWebhook?url=https://your.frontend.com:5000/telegram/alpha+beta+gamma"
|
||||
```
|
||||
|
||||
## Supported commands
|
||||
|
||||
- `path`: Show bird's ASN path to target IP
|
||||
- `route`: Show bird's preferred route to target IP
|
||||
- `trace`: Traceroute to target IP/domain
|
||||
- `whois`: Whois query
|
11
frontend/Dockerfile
Normal file
11
frontend/Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
||||
FROM golang:buster AS step_0
|
||||
ENV CGO_ENABLED=0 GO111MODULE=on
|
||||
WORKDIR /root
|
||||
COPY . .
|
||||
RUN go build -ldflags "-w -s" -o /frontend
|
||||
|
||||
################################################################################
|
||||
|
||||
FROM scratch AS step_1
|
||||
COPY --from=step_0 /frontend /
|
||||
ENTRYPOINT ["/frontend"]
|
@@ -1,5 +1,3 @@
|
||||
.PHONY: all
|
||||
all:
|
||||
go get -u github.com/kevinburke/go-bindata/...
|
||||
go generate
|
||||
go build -ldflags "-w -s" -o frontend
|
||||
|
BIN
frontend/assets/favicon.ico
Normal file
BIN
frontend/assets/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
7
frontend/assets/static/jsdelivr/npm/bootstrap@4.5.1/dist/css/bootstrap.min.css
vendored
Normal file
7
frontend/assets/static/jsdelivr/npm/bootstrap@4.5.1/dist/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
7
frontend/assets/static/jsdelivr/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js
vendored
Normal file
7
frontend/assets/static/jsdelivr/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
frontend/assets/static/jsdelivr/npm/jquery@3.5.1/dist/jquery.min.js
vendored
Normal file
2
frontend/assets/static/jsdelivr/npm/jquery@3.5.1/dist/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
8
frontend/assets/static/jsdelivr/npm/viz.js@2.1.2/viz.min.js
vendored
Normal file
8
frontend/assets/static/jsdelivr/npm/viz.js@2.1.2/viz.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
73
frontend/assets/static/sortTable.js
Normal file
73
frontend/assets/static/sortTable.js
Normal file
@@ -0,0 +1,73 @@
|
||||
// adapted from https://stackoverflow.com/a/57080195
|
||||
|
||||
document.querySelectorAll('table.sortable')
|
||||
.forEach((table)=> {
|
||||
table.querySelectorAll('th')
|
||||
.forEach((element, columnNo) => {
|
||||
element.addEventListener('click', event => {
|
||||
if(element.classList.contains('ascSorted')) {
|
||||
dir = -1;
|
||||
element.classList.remove('ascSorted');
|
||||
element.classList.add('descSorted');
|
||||
element.innerText = element.innerText.slice(0,-2) + " ↓";
|
||||
} else if(element.classList.contains('descSorted')) {
|
||||
dir = 1;
|
||||
element.classList.remove('descSorted');
|
||||
element.classList.add('ascSorted');
|
||||
element.innerText = element.innerText.slice(0,-2) + " ↑";
|
||||
} else {
|
||||
dir = 1;
|
||||
element.classList.add('ascSorted');
|
||||
element.innerText += " ↑";
|
||||
}
|
||||
sortTable(table, columnNo, 0, dir, 1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function sortTable(table, priCol, secCol, priDir, secDir) {
|
||||
const tableBody = table.querySelector('tbody');
|
||||
const tableData = table2data(tableBody);
|
||||
tableData.sort((a, b) => {
|
||||
if(a[priCol] === b[priCol]) {
|
||||
if(a[secCol] > b[secCol]) {
|
||||
return secDir;
|
||||
} else {
|
||||
return -secDir;
|
||||
}
|
||||
} else if(a[priCol] > b[priCol]) {
|
||||
return priDir;
|
||||
} else {
|
||||
return -priDir;
|
||||
}
|
||||
});
|
||||
data2table(tableBody, tableData);
|
||||
}
|
||||
|
||||
function table2data(tableBody) {
|
||||
const tableData = [];
|
||||
tableBody.querySelectorAll('tr')
|
||||
.forEach(row => {
|
||||
const rowData = [];
|
||||
row.querySelectorAll('td')
|
||||
.forEach(cell => {
|
||||
rowData.push(cell.innerHTML);
|
||||
});
|
||||
rowData.classList = row.classList.toString();
|
||||
tableData.push(rowData);
|
||||
});
|
||||
return tableData;
|
||||
}
|
||||
|
||||
function data2table(tableBody, tableData) {
|
||||
tableBody.querySelectorAll('tr')
|
||||
.forEach((row, i) => {
|
||||
const rowData = tableData[i];
|
||||
row.classList = rowData.classList;
|
||||
row.querySelectorAll('td')
|
||||
.forEach((cell, j) => {
|
||||
cell.innerHTML = rowData[j];
|
||||
});
|
||||
tableData.push(rowData);
|
||||
});
|
||||
}
|
@@ -2,11 +2,11 @@
|
||||
<div id="bgpmap">
|
||||
</div>
|
||||
|
||||
<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 src="/static/jsdelivr/npm/viz.js@2.1.2/viz.min.js" crossorigin="anonymous"></script>
|
||||
<script src="/static/jsdelivr/npm/viz.js@2.1.2/lite.render.js" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
var viz = new Viz();
|
||||
viz.renderSVGElement(`{{ .Result }}`)
|
||||
viz.renderSVGElement(atob({{ .Result }}))
|
||||
.then(element => {
|
||||
document.getElementById("bgpmap").appendChild(element);
|
||||
})
|
@@ -1,18 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en-US">
|
||||
<head>
|
||||
<link rel="icon" href="/favicon.ico" type="image/x-icon" />
|
||||
<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>{{ html .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">
|
||||
<link rel="stylesheet" href="/static/jsdelivr/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>
|
||||
<a class="navbar-brand" href="{{ .BrandURL }}">{{ .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>
|
||||
@@ -28,13 +29,24 @@
|
||||
{{ end }}
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li class="nav-item">
|
||||
{{ if eq .AllServersURLCustom "all" }}
|
||||
<a class="nav-link{{ if .AllServersLinkActive }} active{{ end }}"
|
||||
href="/{{ $option }}/{{ .AllServersURL }}/{{ $target }}"> All Servers </a>
|
||||
href="/{{ $option }}/{{ .AllServersURL }}/{{ $target }}"> {{ .AllServerTitle }} </a>
|
||||
{{ else }}
|
||||
<a class="nav-link active"
|
||||
href="{{ .AllServersURLCustom }}"> {{ .AllServerTitle }} </a>
|
||||
{{ end }}
|
||||
</li>
|
||||
{{ range $k, $v := .ServersEscaped }}
|
||||
{{ $length := len .Servers }}
|
||||
{{ range $k, $v := .Servers }}
|
||||
<li class="nav-item">
|
||||
{{ if gt $length 1 }}
|
||||
<a class="nav-link{{ if eq $server $v }} active{{ end }}"
|
||||
href="/{{ $option }}/{{ $v }}/{{ $target }}">{{ html (index $.ServersDisplay $k) }}</a>
|
||||
{{ else }}
|
||||
<a class="nav-link{{ if eq $server $v }} active{{ end }}"
|
||||
href="/">{{ html (index $.ServersDisplay $k) }}</a>
|
||||
{{ end }}
|
||||
</li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
@@ -62,8 +74,9 @@
|
||||
{{ .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>
|
||||
<script src="/static/jsdelivr/npm/jquery@3.5.1/dist/jquery.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
|
||||
<script src="/static/jsdelivr/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js" integrity="sha256-0IiaoZCI++9oAAvmCb5Y0r93XkuhvJpRalZLffQXLok=" crossorigin="anonymous"></script>
|
||||
<script src="/static/sortTable.js"></script>
|
||||
|
||||
<script>
|
||||
function goto() {
|
@@ -1,6 +1,6 @@
|
||||
{{ $ServerName := urlquery .ServerName }}
|
||||
|
||||
<table class="table table-striped table-bordered table-sm">
|
||||
<table class="table table-striped table-bordered table-sm sortable">
|
||||
<thead>
|
||||
{{ range .Header }}
|
||||
<th scope="col">{{ html . }}</th>
|
File diff suppressed because it is too large
Load Diff
@@ -5,8 +5,32 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGetASNRepresentation(t *testing.T) {
|
||||
func contains(s []string, str string) bool {
|
||||
for _, v := range s {
|
||||
if v == str {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func TestGetASNRepresentationDNS(t *testing.T) {
|
||||
checkNetwork(t)
|
||||
|
||||
setting.dnsInterface = "asn.cymru.com"
|
||||
setting.whoisServer = ""
|
||||
result := getASNRepresentation("6939")
|
||||
if !strings.Contains(result, "HURRICANE") {
|
||||
t.Errorf("Lookup AS6939 failed, got %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetASNRepresentationWhois(t *testing.T) {
|
||||
checkNetwork(t)
|
||||
|
||||
setting.dnsInterface = ""
|
||||
setting.whoisServer = "whois.arin.net"
|
||||
result := getASNRepresentation("6939")
|
||||
if !strings.Contains(result, "HURRICANE") {
|
||||
t.Errorf("Lookup AS6939 failed, got %s", result)
|
||||
@@ -15,12 +39,14 @@ func TestGetASNRepresentation(t *testing.T) {
|
||||
|
||||
func TestGetASNRepresentationFallback(t *testing.T) {
|
||||
setting.dnsInterface = ""
|
||||
setting.whoisServer = ""
|
||||
result := getASNRepresentation("6939")
|
||||
if result != "AS6939" {
|
||||
t.Errorf("Lookup AS6939 failed, got %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
// Broken due to random order of attributes
|
||||
func TestBirdRouteToGraphviz(t *testing.T) {
|
||||
setting.dnsInterface = ""
|
||||
|
||||
@@ -33,14 +59,13 @@ func TestBirdRouteToGraphviz(t *testing.T) {
|
||||
BGP.as_path: 4242422601
|
||||
BGP.next_hop: 172.18.0.2`
|
||||
|
||||
expectedResult := `digraph {
|
||||
"Nexthop:\n172.18.0.2" -> "AS4242422601" [color=red];
|
||||
"Nexthop:\n172.18.0.2" [shape=diamond];
|
||||
"AS4242422601" -> "Target: 192.168.0.1" [color=red];
|
||||
"Target: 192.168.0.1" [color=red,shape=diamond];
|
||||
"alpha" [color=blue,shape=box];
|
||||
"alpha" -> "Nexthop:\n172.18.0.2" [color=red];
|
||||
}`
|
||||
expectedLinesInResult := []string{
|
||||
`"AS4242422601" [`,
|
||||
`"AS4242422601" -> "Target: 192.168.0.1" [`,
|
||||
`"Target: 192.168.0.1" [`,
|
||||
`"alpha" [`,
|
||||
`"alpha" -> "AS4242422601" [`,
|
||||
}
|
||||
|
||||
result := birdRouteToGraphviz([]string{
|
||||
"alpha",
|
||||
@@ -48,9 +73,10 @@ func TestBirdRouteToGraphviz(t *testing.T) {
|
||||
fakeResult,
|
||||
}, "192.168.0.1")
|
||||
|
||||
for _, line := range strings.Split(result, "\n") {
|
||||
if !strings.Contains(expectedResult, line) {
|
||||
t.Errorf("Unexpected line in result: %s", line)
|
||||
|
||||
for _, line := range expectedLinesInResult {
|
||||
if !strings.Contains(result, line) {
|
||||
t.Errorf("Expected line in result not found: %s", line)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -49,3 +49,53 @@ func dn42WhoisFilter(whois string) string {
|
||||
return commandResult
|
||||
}
|
||||
}
|
||||
|
||||
/* experimental, behavior may change */
|
||||
func shortenWhoisFilter(whois string) string {
|
||||
commandResult := ""
|
||||
commandResultLonger := ""
|
||||
lines := 0
|
||||
linesLonger := 0
|
||||
skippedLines := 0
|
||||
skippedLinesLonger := 0
|
||||
|
||||
for _, s := range strings.Split(whois, "\n") {
|
||||
s = strings.TrimSpace(s)
|
||||
|
||||
shouldSkip := false
|
||||
shouldSkip = shouldSkip || len(s) == 0
|
||||
shouldSkip = shouldSkip || len(s) > 0 && s[0] == '#'
|
||||
shouldSkip = shouldSkip || strings.Contains(strings.ToUpper(s), "REDACTED FOR PRIVACY")
|
||||
|
||||
if shouldSkip {
|
||||
skippedLinesLonger++
|
||||
continue
|
||||
}
|
||||
|
||||
commandResultLonger += s + "\n"
|
||||
linesLonger++
|
||||
|
||||
shouldSkip = shouldSkip || len(s) > 80
|
||||
shouldSkip = shouldSkip || !strings.Contains(s, ":")
|
||||
shouldSkip = shouldSkip || strings.Index(s, ":") > 20
|
||||
|
||||
if shouldSkip {
|
||||
skippedLines++
|
||||
continue
|
||||
}
|
||||
|
||||
commandResult += s + "\n"
|
||||
lines++
|
||||
}
|
||||
|
||||
if lines < 5 {
|
||||
commandResult = commandResultLonger
|
||||
skippedLines = skippedLinesLonger
|
||||
}
|
||||
|
||||
if skippedLines > 0 {
|
||||
return commandResult + fmt.Sprintf("\n%d line(s) skipped.\n", skippedLines)
|
||||
} else {
|
||||
return commandResult
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,10 @@
|
||||
module github.com/xddxdd/bird-lg-go/frontend
|
||||
|
||||
go 1.15
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1
|
||||
github.com/felixge/httpsnoop v1.0.3 // indirect
|
||||
github.com/gorilla/handlers v1.5.1
|
||||
github.com/kevinburke/go-bindata v3.22.0+incompatible // indirect
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.14.0
|
||||
)
|
||||
|
1094
frontend/go.sum
1094
frontend/go.sum
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,12 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type channelData struct {
|
||||
@@ -46,13 +47,20 @@ func batchRequest(servers []string, endpoint string, command string) []string {
|
||||
}
|
||||
url := "http://" + hostname + ":" + strconv.Itoa(setting.proxyPort) + "/" + url.PathEscape(endpoint) + "?q=" + url.QueryEscape(command)
|
||||
go func(url string, i int) {
|
||||
response, err := http.Get(url)
|
||||
client := http.Client{Timeout: time.Duration(setting.timeOut) * time.Second}
|
||||
response, err := client.Get(url)
|
||||
if err != nil {
|
||||
ch <- channelData{i, "request failed: " + err.Error() + "\n"}
|
||||
return
|
||||
}
|
||||
text, _ := ioutil.ReadAll(response.Body)
|
||||
ch <- channelData{i, string(text)}
|
||||
|
||||
buf := make([]byte, 65536)
|
||||
n, err := io.ReadFull(response.Body, buf)
|
||||
if err != nil && err != io.ErrUnexpectedEOF {
|
||||
ch <- channelData{i, err.Error()}
|
||||
} else {
|
||||
ch <- channelData{i, string(buf[:n])}
|
||||
}
|
||||
}(url, i)
|
||||
}
|
||||
}
|
||||
|
119
frontend/main.go
119
frontend/main.go
@@ -1,15 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// binary data
|
||||
//go:generate go-bindata -prefix bindata -o bindata.go bindata/...
|
||||
|
||||
type settingType struct {
|
||||
servers []string
|
||||
serversDisplay []string
|
||||
@@ -21,93 +17,40 @@ type settingType struct {
|
||||
netSpecificMode string
|
||||
titleBrand string
|
||||
navBarBrand string
|
||||
navBarBrandURL string
|
||||
navBarAllServer string
|
||||
navBarAllURL string
|
||||
bgpmapInfo string
|
||||
telegramBotName string
|
||||
protocolFilter []string
|
||||
nameFilter string
|
||||
timeOut int
|
||||
}
|
||||
|
||||
var setting settingType
|
||||
|
||||
func main() {
|
||||
var settingDefault = settingType{
|
||||
servers: []string{""},
|
||||
proxyPort: 8000,
|
||||
whoisServer: "whois.verisign-grs.com",
|
||||
listen: ":5000",
|
||||
dnsInterface: "asn.cymru.com",
|
||||
titleBrand: "Bird-lg Go",
|
||||
navBarBrand: "Bird-lg Go",
|
||||
}
|
||||
|
||||
if env := os.Getenv("BIRDLG_SERVERS"); env != "" {
|
||||
settingDefault.servers = strings.Split(env, ",")
|
||||
}
|
||||
if env := os.Getenv("BIRDLG_DOMAIN"); env != "" {
|
||||
settingDefault.domain = env
|
||||
}
|
||||
if env := os.Getenv("BIRDLG_PROXY_PORT"); env != "" {
|
||||
var err error
|
||||
if settingDefault.proxyPort, err = strconv.Atoi(env); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
if env := os.Getenv("BIRDLG_WHOIS"); env != "" {
|
||||
settingDefault.whoisServer = env
|
||||
}
|
||||
if env := os.Getenv("BIRDLG_LISTEN"); env != "" {
|
||||
settingDefault.listen = env
|
||||
}
|
||||
if env := os.Getenv("BIRDLG_DNS_INTERFACE"); env != "" {
|
||||
settingDefault.dnsInterface = env
|
||||
}
|
||||
if env := os.Getenv("BIRDLG_NET_SPECIFIC_MODE"); env != "" {
|
||||
settingDefault.netSpecificMode = env
|
||||
}
|
||||
if env := os.Getenv("BIRDLG_TITLE_BRAND"); env != "" {
|
||||
settingDefault.titleBrand = env
|
||||
settingDefault.navBarBrand = env
|
||||
}
|
||||
if env := os.Getenv("BIRDLG_NAVBAR_BRAND"); env != "" {
|
||||
settingDefault.navBarBrand = env
|
||||
}
|
||||
|
||||
serversPtr := flag.String("servers", strings.Join(settingDefault.servers, ","), "server name prefixes, separated by comma")
|
||||
domainPtr := flag.String("domain", settingDefault.domain, "server name domain suffixes")
|
||||
proxyPortPtr := flag.Int("proxy-port", settingDefault.proxyPort, "port bird-lgproxy is running on")
|
||||
whoisPtr := flag.String("whois", settingDefault.whoisServer, "whois server for queries")
|
||||
listenPtr := flag.String("listen", settingDefault.listen, "address bird-lg is listening on")
|
||||
dnsInterfacePtr := flag.String("dns-interface", settingDefault.dnsInterface, "dns zone to query ASN information")
|
||||
netSpecificModePtr := flag.String("net-specific-mode", settingDefault.netSpecificMode, "network specific operation mode, [(none)|dn42]")
|
||||
titleBrandPtr := flag.String("title-brand", settingDefault.titleBrand, "prefix of page titles in browser tabs")
|
||||
navBarBrandPtr := flag.String("navbar-brand", settingDefault.navBarBrand, "brand to show in the navigation bar")
|
||||
flag.Parse()
|
||||
|
||||
if *serversPtr == "" {
|
||||
panic("no server set")
|
||||
}
|
||||
|
||||
servers := strings.Split(*serversPtr, ",")
|
||||
serversDisplay := strings.Split(*serversPtr, ",")
|
||||
|
||||
// Split server names of the form "DisplayName<Hostname>"
|
||||
for i, server := range servers {
|
||||
pos := strings.Index(server, "<")
|
||||
if pos != -1 {
|
||||
serversDisplay[i] = server[0:pos]
|
||||
servers[i] = server[pos+1:len(server)-1]
|
||||
}
|
||||
}
|
||||
|
||||
setting = settingType{
|
||||
servers,
|
||||
serversDisplay,
|
||||
*domainPtr,
|
||||
*proxyPortPtr,
|
||||
*whoisPtr,
|
||||
*listenPtr,
|
||||
*dnsInterfacePtr,
|
||||
strings.ToLower(*netSpecificModePtr),
|
||||
*titleBrandPtr,
|
||||
*navBarBrandPtr,
|
||||
}
|
||||
|
||||
parseSettings()
|
||||
ImportTemplates()
|
||||
webServerStart()
|
||||
|
||||
var l net.Listener
|
||||
var err error
|
||||
|
||||
if strings.HasPrefix(setting.listen, "/") {
|
||||
// Delete existing socket file, ignore errors (will fail later anyway)
|
||||
os.Remove(setting.listen)
|
||||
l, err = net.Listen("unix", setting.listen)
|
||||
} else {
|
||||
listenAddr := setting.listen
|
||||
if !strings.Contains(listenAddr, ":") {
|
||||
listenAddr = ":" + listenAddr
|
||||
}
|
||||
l, err = net.Listen("tcp", listenAddr)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
webServerStart(l)
|
||||
}
|
||||
|
30
frontend/network_test.go
Normal file
30
frontend/network_test.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
NETWORK_UNKNOWN = 0
|
||||
NETWORK_DOWN = 1
|
||||
NETWORK_UP = 2
|
||||
)
|
||||
|
||||
var networkState int = NETWORK_UNKNOWN
|
||||
func checkNetwork(t *testing.T) {
|
||||
if networkState == NETWORK_UNKNOWN {
|
||||
conn, err := net.DialTimeout("tcp", "8.8.8.8:53", 1*time.Second)
|
||||
if err != nil {
|
||||
networkState = NETWORK_DOWN
|
||||
} else {
|
||||
networkState = NETWORK_UP
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
|
||||
if networkState == NETWORK_DOWN {
|
||||
t.Skipf("Test skipped for network error")
|
||||
}
|
||||
}
|
@@ -4,28 +4,35 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
// 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 ...",
|
||||
"summary": "show protocols",
|
||||
"detail": "show protocols all ...",
|
||||
"route_from_protocol": "show route protocol ...",
|
||||
"route_from_protocol_all": "show route protocol ... all",
|
||||
"route_from_protocol_all_primary": "show route protocol ... all primary",
|
||||
"route_filtered_from_protocol": "show route filtered protocol ...",
|
||||
"route_filtered_from_protocol_all": "show route filtered protocol ... all",
|
||||
"route_from_origin": "show route where bgp_path.last = ...",
|
||||
"route_from_origin_all": "show route where bgp_path.last = ... all",
|
||||
"route_from_origin_all_primary": "show route where bgp_path.last = ... all primary",
|
||||
"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
|
||||
@@ -38,7 +45,7 @@ var summaryStateMap = map[string]string{
|
||||
}
|
||||
|
||||
// render the page template
|
||||
func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, content string) {
|
||||
func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, content template.HTML) {
|
||||
path := r.URL.Path[1:]
|
||||
split := strings.SplitN(path, "/", 3)
|
||||
|
||||
@@ -48,33 +55,30 @@ func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, co
|
||||
// Use a default URL if the request URL is too short
|
||||
// The URL is for return to summary page
|
||||
if len(split) < 2 {
|
||||
path = "summary/" + url.PathEscape(strings.Join(setting.servers, "+")) + "/"
|
||||
path = "summary/" + strings.Join(setting.servers, "+") + "/"
|
||||
} else if len(split) == 2 {
|
||||
path += "/"
|
||||
}
|
||||
|
||||
split = strings.SplitN(path, "/", 3)
|
||||
|
||||
serversEscaped := make([]string, len(setting.servers))
|
||||
for i, v := range setting.servers {
|
||||
serversEscaped[i] = url.PathEscape(v)
|
||||
}
|
||||
|
||||
args := TemplatePage{
|
||||
Options: optionsMap,
|
||||
Servers: setting.servers,
|
||||
ServersEscaped: serversEscaped,
|
||||
ServersDisplay: setting.serversDisplay,
|
||||
AllServersLinkActive: strings.ToLower(split[1]) == strings.ToLower(strings.Join(setting.servers, "+")),
|
||||
AllServersURL: url.PathEscape(strings.Join(setting.servers, "+")),
|
||||
AllServersLinkActive: strings.EqualFold(split[1], strings.Join(setting.servers, "+")),
|
||||
AllServersURL: strings.Join(setting.servers, "+"),
|
||||
AllServerTitle: setting.navBarAllServer,
|
||||
AllServersURLCustom: setting.navBarAllURL,
|
||||
IsWhois: isWhois,
|
||||
WhoisTarget: whoisTarget,
|
||||
|
||||
URLOption: strings.ToLower(split[0]),
|
||||
URLServer: url.PathEscape(strings.ToLower(split[1])),
|
||||
URLServer: strings.ToLower(split[1]),
|
||||
URLCommand: split[2],
|
||||
Title: setting.titleBrand + title,
|
||||
Brand: setting.navBarBrand,
|
||||
BrandURL: setting.navBarBrandURL,
|
||||
Content: content,
|
||||
}
|
||||
|
||||
@@ -88,7 +92,7 @@ func renderPageTemplate(w http.ResponseWriter, r *http.Request, title string, co
|
||||
|
||||
// Write the given text to http response, and add whois links for
|
||||
// ASNs and IP addresses
|
||||
func smartFormatter(s string) string {
|
||||
func smartFormatter(s string) template.HTML {
|
||||
var result string
|
||||
result += "<pre>"
|
||||
s = template.HTMLEscapeString(s)
|
||||
@@ -105,7 +109,7 @@ func smartFormatter(s string) string {
|
||||
result += lineFormatted + "\n"
|
||||
}
|
||||
result += "</pre>"
|
||||
return result
|
||||
return template.HTML(result)
|
||||
}
|
||||
|
||||
// Parse bird show protocols result
|
||||
@@ -130,6 +134,9 @@ func summaryParse(data string, serverName string) (TemplateSummary, error) {
|
||||
args.Header = append(args.Header, col)
|
||||
}
|
||||
|
||||
// Build regexp for nameFilter
|
||||
nameFilterRegexp := regexp.MustCompile(setting.nameFilter)
|
||||
|
||||
// sort the remaining rows
|
||||
rows := lines[1:]
|
||||
sort.Strings(rows)
|
||||
@@ -153,9 +160,24 @@ func summaryParse(data string, serverName string) (TemplateSummary, error) {
|
||||
|
||||
if len(lineSplitted) >= 2 {
|
||||
row.Name = strings.TrimSpace(lineSplitted[1])
|
||||
if setting.nameFilter != "" && nameFilterRegexp.MatchString(row.Name) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(lineSplitted) >= 4 {
|
||||
row.Proto = strings.TrimSpace(lineSplitted[3])
|
||||
// Filter away unwanted protocol types, if setting.protocolFilter is non-empty
|
||||
found := false
|
||||
for _, protocol := range setting.protocolFilter {
|
||||
if strings.EqualFold(row.Proto, protocol) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(setting.protocolFilter) > 0 && !found {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(lineSplitted) >= 6 {
|
||||
row.Table = strings.TrimSpace(lineSplitted[5])
|
||||
@@ -179,11 +201,11 @@ func summaryParse(data string, serverName string) (TemplateSummary, error) {
|
||||
}
|
||||
|
||||
// Output a table for the summary page
|
||||
func summaryTable(data string, serverName string) string {
|
||||
func summaryTable(data string, serverName string) template.HTML {
|
||||
result, err := summaryParse(data, serverName)
|
||||
|
||||
if err != nil {
|
||||
return "<pre>" + template.HTMLEscapeString(err.Error()) + "</pre>"
|
||||
return template.HTML("<pre>" + template.HTMLEscapeString(err.Error()) + "</pre>")
|
||||
}
|
||||
|
||||
// render the summary template
|
||||
@@ -194,5 +216,5 @@ func summaryTable(data string, serverName string) string {
|
||||
fmt.Println("Error rendering summary:", err.Error())
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
return template.HTML(buffer.String())
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user