You've already forked bird-lg-go
							
							
				mirror of
				https://github.com/xddxdd/bird-lg-go
				synced 2025-11-02 13:02:29 +01:00 
			
		
		
		
	Compare commits
	
		
			138 Commits
		
	
	
		
			bird1
			...
			lantian-de
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					abb32abff3 | ||
| 
						 | 
					b368c75aa3 | ||
| 
						 | 
					09405cdb38 | ||
| 
						 | 
					f999d47d9f | ||
| 
						 | 
					005dfb1435 | ||
| 
						 | 
					4bd7a6bb95 | ||
| 
						 | 
					462d76a2d0 | ||
| 
						 | 
					58f217578c | ||
| 
						 | 
					0e95727de1 | ||
| 
						 | 
					a48f1c8040 | ||
| 
						 | 
					81acde3a37 | ||
| 
						 | 
					7c0fe0d512 | ||
| 
						 | 
					a5f4452d02 | ||
| 
						 | 
					b237185ef7 | ||
| 
						 | 
					e949646790 | ||
| 
						 | 
					bb479d22ae | ||
| 
						 | 
					d40f41b4d5 | ||
| 
						 | 
					cdc34704b5 | ||
| 
						 | 
					db58bd3354 | ||
| 
						 | 
					a0246ccee2 | ||
| 
						 | 
					ccd14af0c8 | ||
| 
						 | 
					594ca80f50 | ||
| 
						 | 
					5625058e71 | ||
| 
						 | 
					7efa3237a9 | ||
| 
						 | 
					7b0c8c0556 | ||
| 
						 | 
					ffd9165062 | ||
| 
						 | 
					24fd5203e8 | ||
| 
						 | 
					49a05767c1 | ||
| 
						 | 
					e7010f75f8 | ||
| 
						 | 
					dba2af7634 | ||
| 
						 | 
					049775319b | ||
| 
						 | 
					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 | ||
| 
						 | 
					fa827502cf | ||
| 
						 | 
					794125a96f | ||
| 
						 | 
					de9d9101b1 | ||
| 
						 | 
					056ef3769e | ||
| 
						 | 
					974e809deb | ||
| 
						 | 
					6e19b5ae64 | ||
| 
						 | 
					874089117b | ||
| 
						 | 
					f77a8a28fe | ||
| 
						 | 
					9e8a845658 | ||
| 
						 | 
					fd3e7b8379 | ||
| 
						 | 
					8765189deb | ||
| 
						 | 
					492942cce1 | ||
| 
						 | 
					f81a5308ae | ||
| 
						 | 
					5b5a09ccbd | ||
| 
						 | 
					dc4d7e6532 | ||
| 
						 | 
					28a7d2a53f | ||
| 
						 | 
					4413f1032f | ||
| 
						 | 
					007b66e036 | ||
| 
						 | 
					3f612d2e76 | ||
| 
						 | 
					f49f8bac5e | ||
| 
						 | 
					f6ddc5761b | ||
| 
						 | 
					1c3d9ec594 | ||
| 
						 | 
					6cc0c617b4 | ||
| 
						 | 
					e2cc580da3 | ||
| 
						 | 
					472cec74b0 | ||
| 
						 | 
					da2c3d9aed | ||
| 
						 | 
					aa76bc3de7 | ||
| 
						 | 
					a984095282 | ||
| 
						 | 
					1baf325149 | ||
| 
						 | 
					72946e1113 | ||
| 
						 | 
					90e5012840 | ||
| 
						 | 
					8d5eb56199 | ||
| 
						 | 
					8d0618fed9 | ||
| 
						 | 
					f8ea511d44 | ||
| 
						 | 
					b99eb60c30 | ||
| 
						 | 
					9f934ca53c | ||
| 
						 | 
					ee7cc1675b | ||
| 
						 | 
					f4b6955343 | ||
| 
						 | 
					78ce724171 | ||
| 
						 | 
					6179c688be | ||
| 
						 | 
					8d0e210572 | ||
| 
						 | 
					26c51176e4 | ||
| 
						 | 
					5cf2ac57b8 | ||
| 
						 | 
					75bc63ffa7 | ||
| 
						 | 
					438c6a1f82 | ||
| 
						 | 
					b98d783739 | ||
| 
						 | 
					5000ad1bbf | ||
| 
						 | 
					538699ccd2 | ||
| 
						 | 
					9e77de6b46 | ||
| 
						 | 
					c15942cc32 | ||
| 
						 | 
					3bcfc3d36c | 
							
								
								
									
										16
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.github/dependabot.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
version: 2
 | 
			
		||||
updates:
 | 
			
		||||
- package-ecosystem: gomod
 | 
			
		||||
  directory: "/frontend"
 | 
			
		||||
  schedule:
 | 
			
		||||
    interval: daily
 | 
			
		||||
    time: "08:00"
 | 
			
		||||
    timezone: Asia/Shanghai
 | 
			
		||||
  open-pull-requests-limit: 10
 | 
			
		||||
- package-ecosystem: gomod
 | 
			
		||||
  directory: "/proxy"
 | 
			
		||||
  schedule:
 | 
			
		||||
    interval: daily
 | 
			
		||||
    time: "08:00"
 | 
			
		||||
    timezone: Asia/Shanghai
 | 
			
		||||
  open-pull-requests-limit: 10
 | 
			
		||||
							
								
								
									
										108
									
								
								.github/workflows/develop.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								.github/workflows/develop.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,108 @@
 | 
			
		||||
on:
 | 
			
		||||
  push:
 | 
			
		||||
    branches:
 | 
			
		||||
      - '**'
 | 
			
		||||
  pull_request:
 | 
			
		||||
    branches:
 | 
			
		||||
      - 'master'
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  go-test:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
 | 
			
		||||
      - name: Setup Golang
 | 
			
		||||
        uses: actions/setup-go@v4
 | 
			
		||||
 | 
			
		||||
      - name: Run frontend unit test
 | 
			
		||||
        run: |
 | 
			
		||||
          export GO111MODULE=on
 | 
			
		||||
          cd frontend
 | 
			
		||||
          go get -v -t -d ./...
 | 
			
		||||
          go test -v ./...
 | 
			
		||||
          cd ..
 | 
			
		||||
 | 
			
		||||
      - name: Run proxy unit test
 | 
			
		||||
        run: |
 | 
			
		||||
          export GO111MODULE=on
 | 
			
		||||
          cd proxy
 | 
			
		||||
          go get -v -t -d ./...
 | 
			
		||||
          go test -v ./...
 | 
			
		||||
          cd ..
 | 
			
		||||
 | 
			
		||||
  docker-test:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Checkout
 | 
			
		||||
        uses: actions/checkout@v3
 | 
			
		||||
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v2
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v2
 | 
			
		||||
 | 
			
		||||
      - name: Test whois binary in frontend image
 | 
			
		||||
        run: |
 | 
			
		||||
          docker build -t local/frontend frontend/
 | 
			
		||||
          docker run --rm --net host --entrypoint whois local/frontend github.com || exit 1
 | 
			
		||||
          docker run --rm --net host --entrypoint whois local/frontend -h whois.ripe.net github.com || exit 1
 | 
			
		||||
          docker run --rm --net host --entrypoint whois local/frontend -h whois.ripe.net:43 github.com || exit 1
 | 
			
		||||
 | 
			
		||||
      - name: Test traceroute binary in proxy image
 | 
			
		||||
        run: |
 | 
			
		||||
          docker build -t local/proxy proxy/
 | 
			
		||||
          docker run --rm --net host --entrypoint traceroute local/proxy 127.0.0.1 || exit 1
 | 
			
		||||
          docker run --rm --net host --entrypoint traceroute local/proxy ::1 || exit 1
 | 
			
		||||
 | 
			
		||||
  docker-develop:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    needs:
 | 
			
		||||
      - go-test
 | 
			
		||||
      - docker-test
 | 
			
		||||
    if: github.event_name != 'pull_request'
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v2
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v2
 | 
			
		||||
 | 
			
		||||
      - name: Login to Docker Hub
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ secrets.DOCKERHUB_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKERHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Login to GitHub Container Registry
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ghcr.io
 | 
			
		||||
          username: ${{ github.repository_owner }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Build frontend docker image
 | 
			
		||||
        uses: docker/build-push-action@v4
 | 
			
		||||
        with:
 | 
			
		||||
          context: '{{defaultContext}}:frontend'
 | 
			
		||||
          platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: |
 | 
			
		||||
            xddxdd/bird-lg-go:develop
 | 
			
		||||
            xddxdd/bird-lg-go:develop-${{ github.sha }}
 | 
			
		||||
            ghcr.io/xddxdd/bird-lg-go:frontend-develop
 | 
			
		||||
            ghcr.io/xddxdd/bird-lg-go:frontend-develop-${{ github.sha }}
 | 
			
		||||
 | 
			
		||||
      - name: Build proxy docker image
 | 
			
		||||
        uses: docker/build-push-action@v4
 | 
			
		||||
        with:
 | 
			
		||||
          context: '{{defaultContext}}:proxy'
 | 
			
		||||
          platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: |
 | 
			
		||||
            xddxdd/bird-lgproxy-go:develop
 | 
			
		||||
            xddxdd/bird-lgproxy-go:develop-${{ github.sha }}
 | 
			
		||||
            ghcr.io/xddxdd/bird-lg-go:proxy-develop
 | 
			
		||||
            ghcr.io/xddxdd/bird-lg-go:proxy-develop-${{ github.sha }}
 | 
			
		||||
							
								
								
									
										82
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
on:
 | 
			
		||||
  release:
 | 
			
		||||
    types: [created]
 | 
			
		||||
 | 
			
		||||
jobs:
 | 
			
		||||
  go-release:
 | 
			
		||||
    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:
 | 
			
		||||
    - name: Checkout
 | 
			
		||||
      uses: actions/checkout@v3
 | 
			
		||||
 | 
			
		||||
    - name: Release frontend
 | 
			
		||||
      uses: wangyoucao577/go-release-action@v1.34
 | 
			
		||||
      with:
 | 
			
		||||
        github_token: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
        goos: ${{ matrix.goos }}
 | 
			
		||||
        goarch: ${{ matrix.goarch }}
 | 
			
		||||
        project_path: "./frontend"
 | 
			
		||||
        binary_name: "bird-lg-go"
 | 
			
		||||
 | 
			
		||||
    - name: Release proxy
 | 
			
		||||
      uses: wangyoucao577/go-release-action@v1.34
 | 
			
		||||
      with:
 | 
			
		||||
        github_token: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
        goos: ${{ matrix.goos }}
 | 
			
		||||
        goarch: ${{ matrix.goarch }}
 | 
			
		||||
        project_path: "./proxy"
 | 
			
		||||
        binary_name: "bird-lgproxy-go"
 | 
			
		||||
 | 
			
		||||
  docker-release:
 | 
			
		||||
    runs-on: ubuntu-latest
 | 
			
		||||
    steps:
 | 
			
		||||
      - name: Set up QEMU
 | 
			
		||||
        uses: docker/setup-qemu-action@v2
 | 
			
		||||
 | 
			
		||||
      - name: Set up Docker Buildx
 | 
			
		||||
        uses: docker/setup-buildx-action@v2
 | 
			
		||||
 | 
			
		||||
      - name: Login to Docker Hub
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          username: ${{ secrets.DOCKERHUB_USERNAME }}
 | 
			
		||||
          password: ${{ secrets.DOCKERHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Login to GitHub Container Registry
 | 
			
		||||
        uses: docker/login-action@v2
 | 
			
		||||
        with:
 | 
			
		||||
          registry: ghcr.io
 | 
			
		||||
          username: ${{ github.repository_owner }}
 | 
			
		||||
          password: ${{ secrets.GITHUB_TOKEN }}
 | 
			
		||||
 | 
			
		||||
      - name: Build frontend docker image
 | 
			
		||||
        uses: docker/build-push-action@v4
 | 
			
		||||
        with:
 | 
			
		||||
          context: '{{defaultContext}}:frontend'
 | 
			
		||||
          platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: |
 | 
			
		||||
            xddxdd/bird-lg-go:latest
 | 
			
		||||
            ghcr.io/xddxdd/bird-lg-go:frontend
 | 
			
		||||
 | 
			
		||||
      - name: Build proxy docker image
 | 
			
		||||
        uses: docker/build-push-action@v4
 | 
			
		||||
        with:
 | 
			
		||||
          context: '{{defaultContext}}:proxy'
 | 
			
		||||
          platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7
 | 
			
		||||
          push: true
 | 
			
		||||
          tags: |
 | 
			
		||||
            xddxdd/bird-lgproxy-go:latest
 | 
			
		||||
            ghcr.io/xddxdd/bird-lg-go:proxy
 | 
			
		||||
							
								
								
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -16,4 +16,11 @@
 | 
			
		||||
 | 
			
		||||
.DS_Store
 | 
			
		||||
frontend/frontend
 | 
			
		||||
proxy/proxy
 | 
			
		||||
proxy/proxy
 | 
			
		||||
 | 
			
		||||
# don't include generated bindata file
 | 
			
		||||
frontend/bindata.go
 | 
			
		||||
 | 
			
		||||
# don't include generated Dockerfiles
 | 
			
		||||
frontend/Dockerfile.*
 | 
			
		||||
proxy/Dockerfile.*
 | 
			
		||||
							
								
								
									
										35
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,35 +0,0 @@
 | 
			
		||||
language: minimal
 | 
			
		||||
os: linux
 | 
			
		||||
dist: focal
 | 
			
		||||
services:
 | 
			
		||||
  - docker
 | 
			
		||||
env:
 | 
			
		||||
  - PROGRAM=frontend    IMAGE_NAME=bird-lg-go       IMAGE_ARCH=i386
 | 
			
		||||
  - PROGRAM=frontend    IMAGE_NAME=bird-lg-go       IMAGE_ARCH=amd64
 | 
			
		||||
  - PROGRAM=frontend    IMAGE_NAME=bird-lg-go       IMAGE_ARCH=arm32v7
 | 
			
		||||
  - PROGRAM=frontend    IMAGE_NAME=bird-lg-go       IMAGE_ARCH=arm64v8
 | 
			
		||||
  - PROGRAM=proxy       IMAGE_NAME=bird-lgproxy-go  IMAGE_ARCH=i386
 | 
			
		||||
  - PROGRAM=proxy       IMAGE_NAME=bird-lgproxy-go  IMAGE_ARCH=amd64
 | 
			
		||||
  - PROGRAM=proxy       IMAGE_NAME=bird-lgproxy-go  IMAGE_ARCH=arm32v7
 | 
			
		||||
  - PROGRAM=proxy       IMAGE_NAME=bird-lgproxy-go  IMAGE_ARCH=arm64v8
 | 
			
		||||
 | 
			
		||||
install:
 | 
			
		||||
  - docker run --rm --privileged multiarch/qemu-user-static:register --reset
 | 
			
		||||
  - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
 | 
			
		||||
 | 
			
		||||
script:
 | 
			
		||||
  - |
 | 
			
		||||
    # Build image
 | 
			
		||||
    docker build \
 | 
			
		||||
      -t $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_ARCH \
 | 
			
		||||
      -f $PROGRAM/Dockerfile.$IMAGE_ARCH \
 | 
			
		||||
      $PROGRAM
 | 
			
		||||
 | 
			
		||||
    # Tag image :{arch} and :{arch}-build{build number}
 | 
			
		||||
    docker tag $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_ARCH $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_ARCH-build$TRAVIS_BUILD_NUMBER
 | 
			
		||||
    if [ "$IMAGE_ARCH" = "amd64" ]; then
 | 
			
		||||
      # Tag as latest for amd64 images
 | 
			
		||||
      docker tag $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_ARCH $DOCKER_USERNAME/$IMAGE_NAME:latest
 | 
			
		||||
      docker tag $DOCKER_USERNAME/$IMAGE_NAME:$IMAGE_ARCH $DOCKER_USERNAME/$IMAGE_NAME:build$TRAVIS_BUILD_NUMBER
 | 
			
		||||
    fi
 | 
			
		||||
  - docker push $DOCKER_USERNAME/$IMAGE_NAME
 | 
			
		||||
							
								
								
									
										13
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
frontend:
 | 
			
		||||
	$(MAKE) -C frontend all
 | 
			
		||||
 | 
			
		||||
proxy:
 | 
			
		||||
	$(MAKE) -C proxy all
 | 
			
		||||
 | 
			
		||||
.DEFAULT_GOAL := all
 | 
			
		||||
.PHONY: all frontend proxy
 | 
			
		||||
all: frontend proxy
 | 
			
		||||
 | 
			
		||||
install:
 | 
			
		||||
	install -m 755 frontend/frontend /usr/local/bin/bird-lg-go
 | 
			
		||||
	install -m 755 proxy/proxy /usr/local/bin/bird-lgproxy-go
 | 
			
		||||
							
								
								
									
										202
									
								
								docs/API.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								docs/API.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,202 @@
 | 
			
		||||
# Bird-lg-go API documentation
 | 
			
		||||
 | 
			
		||||
The frontend provides an API for running BIRD/traceroute/whois queries.
 | 
			
		||||
 | 
			
		||||
API Endpoint: `https://your.frontend.com/api/` (the last slash must not be omitted!)
 | 
			
		||||
 | 
			
		||||
Requests are sent as POSTS with JSON bodies.
 | 
			
		||||
 | 
			
		||||
## Table of Contents
 | 
			
		||||
 | 
			
		||||
   * [Bird-lg-go API documentation](#bird-lg-go-api-documentation)
 | 
			
		||||
      * [Table of Contents](#table-of-contents)
 | 
			
		||||
      * [Request fields](#request-fields)
 | 
			
		||||
         * [Example request of type bird](#example-request-of-type-bird)
 | 
			
		||||
         * [Example request of type server_list](#example-request-of-type-server_list)
 | 
			
		||||
      * [Response fields (when type is summary)](#response-fields-when-type-is-summary)
 | 
			
		||||
         * [Fields for apiSummaryResultPair](#fields-for-apisummaryresultpair)
 | 
			
		||||
         * [Fields for SummaryRowData](#fields-for-summaryrowdata)
 | 
			
		||||
         * [Example response](#example-response)
 | 
			
		||||
      * [Response fields (when type is bird, traceroute, whois or server_list)](#response-fields-when-type-is-bird-traceroute-whois-or-server_list)
 | 
			
		||||
         * [Fields for apiGenericResultPair](#fields-for-apigenericresultpair)
 | 
			
		||||
         * [Example response of type bird](#example-response-of-type-bird)
 | 
			
		||||
         * [Example response of type server_list](#example-response-of-type-server_list)
 | 
			
		||||
 | 
			
		||||
Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
 | 
			
		||||
 | 
			
		||||
## Request fields
 | 
			
		||||
 | 
			
		||||
| Name | Type | Value |
 | 
			
		||||
| ---- | ---- | -------- |
 | 
			
		||||
| `servers` | array of `string` | List of servers to be queried |
 | 
			
		||||
| `type` | `string` | Can be `summary`, `bird`, `traceroute`, `whois` or `server_list` |
 | 
			
		||||
| `args` | `string` | Arguments to be passed, see below |
 | 
			
		||||
 | 
			
		||||
Argument examples for each type:
 | 
			
		||||
 | 
			
		||||
- `summary`: `args` is ignored. Recommended to set to empty string.
 | 
			
		||||
- `bird`: `args` is the command to be passed to bird, e.g. `show route for 8.8.8.8`
 | 
			
		||||
- `traceroute`: `args` is the traceroute target, e.g. `8.8.8.8` or `google.com`
 | 
			
		||||
- `whois`: `args` is the whois target, e.g. `8.8.8.8` or `google.com`
 | 
			
		||||
- `server_list`: `args` is ignored. In addition, `servers` is also ignored.
 | 
			
		||||
 | 
			
		||||
### Example request of type `bird`
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "servers": [
 | 
			
		||||
        "alpha"
 | 
			
		||||
    ],
 | 
			
		||||
    "type": "bird",
 | 
			
		||||
    "args": "show route for 8.8.8.8"
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Example request of type `server_list`
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "servers": [],
 | 
			
		||||
    "type": "server_list",
 | 
			
		||||
    "args": ""
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Response fields (when `type` is `summary`)
 | 
			
		||||
 | 
			
		||||
| Name | Type | Value |
 | 
			
		||||
| ---- | ---- | -------- |
 | 
			
		||||
| `error` | `string` | Error message when something is wrong. Empty when everything is good |
 | 
			
		||||
| `result` | array of `apiSummaryResultPair` | See below |
 | 
			
		||||
 | 
			
		||||
### Fields for `apiSummaryResultPair`
 | 
			
		||||
 | 
			
		||||
| Name | Type | Value |
 | 
			
		||||
| ---- | ---- | -------- |
 | 
			
		||||
| `server` | `string` | Name of the server |
 | 
			
		||||
| `data` | array of `SummaryRowData` | Summaries of the server, see below |
 | 
			
		||||
 | 
			
		||||
### Fields for `SummaryRowData`
 | 
			
		||||
 | 
			
		||||
All fields below is 1:1 correspondent to the output of `birdc show protocols`.
 | 
			
		||||
 | 
			
		||||
| Name | Type |
 | 
			
		||||
| ---- | ---- |
 | 
			
		||||
| `name` | `string` |
 | 
			
		||||
| `proto` | `string` |
 | 
			
		||||
| `table` | `string` |
 | 
			
		||||
| `state` | `string` |
 | 
			
		||||
| `since` | `string` |
 | 
			
		||||
| `info` | `string` |
 | 
			
		||||
 | 
			
		||||
### Example response
 | 
			
		||||
 | 
			
		||||
Request:
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "servers": [
 | 
			
		||||
        "alpha"
 | 
			
		||||
    ],
 | 
			
		||||
    "type": "summary",
 | 
			
		||||
    "args": ""
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Response:
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "error": "",
 | 
			
		||||
    "result": [
 | 
			
		||||
        {
 | 
			
		||||
            "server": "alpha",
 | 
			
		||||
            "data": [
 | 
			
		||||
                {
 | 
			
		||||
                    "name": "bgp1",
 | 
			
		||||
                    "proto": "BGP",
 | 
			
		||||
                    "table": "---",
 | 
			
		||||
                    "state": "start",
 | 
			
		||||
                    "since": "2021-01-15 22:40:01",
 | 
			
		||||
                    "info": "Active        Socket: Operation timed out"
 | 
			
		||||
                },
 | 
			
		||||
                {
 | 
			
		||||
                    "name": "bgp2",
 | 
			
		||||
                    "proto": "BGP",
 | 
			
		||||
                    "table": "---",
 | 
			
		||||
                    "state": "start",
 | 
			
		||||
                    "since": "2021-01-03 08:15:48",
 | 
			
		||||
                    "info": "Established"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Response fields (when `type` is `bird`, `traceroute`, `whois` or `server_list`)
 | 
			
		||||
 | 
			
		||||
| Name | Type | Value |
 | 
			
		||||
| ---- | ---- | -------- |
 | 
			
		||||
| `error` | `string` | Error message, empty when everything is good |
 | 
			
		||||
| `result` | array of `apiGenericResultPair` | See below |
 | 
			
		||||
 | 
			
		||||
### Fields for `apiGenericResultPair`
 | 
			
		||||
 | 
			
		||||
| Name | Type | Value |
 | 
			
		||||
| ---- | ---- | -------- |
 | 
			
		||||
| `server` | `string` | Name of the server; is empty when type is `whois` |
 | 
			
		||||
| `data` | `string` | Result from the server; is empty when type is `server_list` |
 | 
			
		||||
 | 
			
		||||
### Example response of type `bird`
 | 
			
		||||
 | 
			
		||||
Request:
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "servers": [
 | 
			
		||||
        "alpha"
 | 
			
		||||
    ],
 | 
			
		||||
    "type": "bird",
 | 
			
		||||
    "args": "show status"
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Response:
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "error": "",
 | 
			
		||||
    "result": [
 | 
			
		||||
        {
 | 
			
		||||
            "server": "alpha",
 | 
			
		||||
            "data": "BIRD v2.0.7-137-g61dae32b\nRouter ID is 1.2.3.4\nCurrent server time is 2021-01-17 04:21:14.792\nLast reboot on 2021-01-03 08:15:48.494\nLast reconfiguration on 2021-01-17 00:49:10.573\nDaemon is up and running\n"
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Example response of type `server_list`
 | 
			
		||||
 | 
			
		||||
Request:
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "servers": [],
 | 
			
		||||
    "type": "server_list",
 | 
			
		||||
    "args": ""
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Response:
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "error": "",
 | 
			
		||||
    "result": [
 | 
			
		||||
        {
 | 
			
		||||
            "server": "gigsgigscloud",
 | 
			
		||||
            "data": ""
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										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
 | 
			
		||||
							
								
								
									
										33
									
								
								frontend/Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								frontend/Dockerfile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
			
		||||
FROM golang AS step_0
 | 
			
		||||
ENV CGO_ENABLED=0 GO111MODULE=on
 | 
			
		||||
WORKDIR /root
 | 
			
		||||
COPY . .
 | 
			
		||||
RUN go build -ldflags "-w -s" -o /frontend
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
 | 
			
		||||
FROM alpine:edge AS step_1
 | 
			
		||||
 | 
			
		||||
WORKDIR /root
 | 
			
		||||
RUN apk add --no-cache build-base pkgconf perl gettext \
 | 
			
		||||
    libidn2-dev libidn2-static libunistring-dev libunistring-static gnu-libiconv-dev
 | 
			
		||||
 | 
			
		||||
RUN wget https://github.com/rfc1036/whois/archive/refs/tags/v5.5.18.tar.gz \
 | 
			
		||||
    -O whois-5.5.18.tar.gz
 | 
			
		||||
 | 
			
		||||
RUN tar xvf whois-5.5.18.tar.gz \
 | 
			
		||||
    && cd whois-5.5.18 \
 | 
			
		||||
    && sed -i "s/#if defined _POSIX_C_SOURCE && _POSIX_C_SOURCE >= 200112L/#if 1/g" config.h \
 | 
			
		||||
    && make whois -j4 \
 | 
			
		||||
      LDFLAGS="-static" CONFIG_FILE="/etc/whois.conf" PKG_CONFIG="pkg-config --static" HAVE_ICONV=1 \
 | 
			
		||||
    && strip /root/whois-5.5.18/whois
 | 
			
		||||
 | 
			
		||||
################################################################################
 | 
			
		||||
 | 
			
		||||
FROM scratch AS step_2
 | 
			
		||||
ENV PATH=/
 | 
			
		||||
ENV BIRDLG_WHOIS=/whois
 | 
			
		||||
COPY --from=step_0 /frontend /
 | 
			
		||||
COPY --from=step_1 /root/whois-5.5.18/whois /
 | 
			
		||||
COPY --from=step_1 /etc/services /etc/services
 | 
			
		||||
ENTRYPOINT ["/frontend"]
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
FROM amd64/debian:buster
 | 
			
		||||
 | 
			
		||||
LABEL Lan Tian "lantian@lantian.pub"
 | 
			
		||||
ENV GOOS=linux GOARCH=amd64
 | 
			
		||||
WORKDIR /root
 | 
			
		||||
COPY . .
 | 
			
		||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y golang git \
 | 
			
		||||
  && cd /root && go get github.com/gorilla/handlers && go build -o /frontend \
 | 
			
		||||
  && cd / && rm -rf /root/* \
 | 
			
		||||
  && apt-get -qq purge -y golang git \
 | 
			
		||||
  && apt-get -qq autoremove --purge -y && apt-get clean && rm -rf /var/lib/apt/lists
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["/frontend"]
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
FROM multiarch/debian-debootstrap:armhf-buster
 | 
			
		||||
 | 
			
		||||
LABEL Lan Tian "lantian@lantian.pub"
 | 
			
		||||
ENV GOOS=linux GOARCH=arm
 | 
			
		||||
WORKDIR /root
 | 
			
		||||
COPY . .
 | 
			
		||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y golang git \
 | 
			
		||||
  && cd /root && go get github.com/gorilla/handlers && go build -o /frontend \
 | 
			
		||||
  && cd / && rm -rf /root/* \
 | 
			
		||||
  && apt-get -qq purge -y golang git \
 | 
			
		||||
  && apt-get -qq autoremove --purge -y && apt-get clean && rm -rf /var/lib/apt/lists
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["/frontend"]
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
FROM multiarch/debian-debootstrap:arm64-buster
 | 
			
		||||
 | 
			
		||||
LABEL Lan Tian "lantian@lantian.pub"
 | 
			
		||||
ENV GOOS=linux GOARCH=arm64
 | 
			
		||||
WORKDIR /root
 | 
			
		||||
COPY . .
 | 
			
		||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y golang git \
 | 
			
		||||
  && cd /root && go get github.com/gorilla/handlers && go build -o /frontend \
 | 
			
		||||
  && cd / && rm -rf /root/* \
 | 
			
		||||
  && apt-get -qq purge -y golang git \
 | 
			
		||||
  && apt-get -qq autoremove --purge -y && apt-get clean && rm -rf /var/lib/apt/lists
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["/frontend"]
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
FROM i386/debian:buster
 | 
			
		||||
 | 
			
		||||
LABEL Lan Tian "lantian@lantian.pub"
 | 
			
		||||
ENV GOOS=linux GOARCH=386
 | 
			
		||||
WORKDIR /root
 | 
			
		||||
COPY . .
 | 
			
		||||
RUN apt-get -qq update && DEBIAN_FRONTEND=noninteractive apt-get -qq install -y golang git \
 | 
			
		||||
  && cd /root && go get github.com/gorilla/handlers && go build -o /frontend \
 | 
			
		||||
  && cd / && rm -rf /root/* \
 | 
			
		||||
  && apt-get -qq purge -y golang git \
 | 
			
		||||
  && apt-get -qq autoremove --purge -y && apt-get clean && rm -rf /var/lib/apt/lists
 | 
			
		||||
 | 
			
		||||
ENTRYPOINT ["/frontend"]
 | 
			
		||||
							
								
								
									
										3
									
								
								frontend/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								frontend/Makefile
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
			
		||||
.PHONY: all
 | 
			
		||||
all:
 | 
			
		||||
	go build -ldflags "-w -s" -o frontend
 | 
			
		||||
							
								
								
									
										130
									
								
								frontend/api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								frontend/api.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,130 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/http"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type apiRequest struct {
 | 
			
		||||
	Servers []string `json:"servers"`
 | 
			
		||||
	Type    string   `json:"type"`
 | 
			
		||||
	Args    string   `json:"args"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type apiGenericResultPair struct {
 | 
			
		||||
	Server string `json:"server"`
 | 
			
		||||
	Data   string `json:"data"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type apiSummaryResultPair struct {
 | 
			
		||||
	Server string           `json:"server"`
 | 
			
		||||
	Data   []SummaryRowData `json:"data"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type apiResponse struct {
 | 
			
		||||
	Error  string        `json:"error"`
 | 
			
		||||
	Result []interface{} `json:"result"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var apiHandlerMap = map[string](func(request apiRequest) apiResponse){
 | 
			
		||||
	"summary":     apiSummaryHandler,
 | 
			
		||||
	"bird":        apiGenericHandlerFactory("bird"),
 | 
			
		||||
	"traceroute":  apiGenericHandlerFactory("traceroute"),
 | 
			
		||||
	"whois":       apiWhoisHandler,
 | 
			
		||||
	"server_list": apiServerListHandler,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func apiGenericHandlerFactory(endpoint string) func(request apiRequest) apiResponse {
 | 
			
		||||
	return func(request apiRequest) apiResponse {
 | 
			
		||||
		results := batchRequest(request.Servers, endpoint, request.Args)
 | 
			
		||||
		var response apiResponse
 | 
			
		||||
 | 
			
		||||
		for i, result := range results {
 | 
			
		||||
			response.Result = append(response.Result, &apiGenericResultPair{
 | 
			
		||||
				Server: request.Servers[i],
 | 
			
		||||
				Data:   result,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return response
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func apiServerListHandler(request apiRequest) apiResponse {
 | 
			
		||||
	var response apiResponse
 | 
			
		||||
 | 
			
		||||
	for _, server := range setting.servers {
 | 
			
		||||
		response.Result = append(response.Result, apiGenericResultPair{
 | 
			
		||||
			Server: server,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return response
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func apiSummaryHandler(request apiRequest) apiResponse {
 | 
			
		||||
	results := batchRequest(request.Servers, "bird", "show protocols")
 | 
			
		||||
	var response apiResponse
 | 
			
		||||
 | 
			
		||||
	for i, result := range results {
 | 
			
		||||
		parsedSummary, err := summaryParse(result, request.Servers[i])
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return apiResponse{
 | 
			
		||||
				Error: err.Error(),
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		response.Result = append(response.Result, &apiSummaryResultPair{
 | 
			
		||||
			Server: request.Servers[i],
 | 
			
		||||
			Data:   parsedSummary.Rows,
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return response
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func apiWhoisHandler(request apiRequest) apiResponse {
 | 
			
		||||
	return apiResponse{
 | 
			
		||||
		Error: "",
 | 
			
		||||
		Result: []interface{}{
 | 
			
		||||
			apiGenericResultPair{
 | 
			
		||||
				Server: "",
 | 
			
		||||
				Data:   whois(request.Args),
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func apiErrorHandler(err error) apiResponse {
 | 
			
		||||
	return apiResponse{
 | 
			
		||||
		Error: err.Error(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func apiHandler(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	var request apiRequest
 | 
			
		||||
	var response apiResponse
 | 
			
		||||
	err := json.NewDecoder(r.Body).Decode(&request)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		response = apiResponse{
 | 
			
		||||
			Error: err.Error(),
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		handler := apiHandlerMap[request.Type]
 | 
			
		||||
		if handler == nil {
 | 
			
		||||
			response = apiErrorHandler(errors.New("invalid request type"))
 | 
			
		||||
		} else {
 | 
			
		||||
			response = handler(request)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	w.Header().Add("Content-Type", "application/json")
 | 
			
		||||
	w.Header().Add("Access-Control-Allow-Origin", "*")
 | 
			
		||||
	bytes, err := json.Marshal(response)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		println(err.Error())
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	w.Write(bytes)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										207
									
								
								frontend/api_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								frontend/api_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,207 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/jarcoal/httpmock"
 | 
			
		||||
	"github.com/magiconair/properties/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestApiServerListHandler(t *testing.T) {
 | 
			
		||||
	setting.servers = []string{"alpha", "beta", "gamma"}
 | 
			
		||||
	response := apiServerListHandler(apiRequest{})
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, len(response.Result), 3)
 | 
			
		||||
	assert.Equal(t, response.Result[0].(apiGenericResultPair).Server, "alpha")
 | 
			
		||||
	assert.Equal(t, response.Result[1].(apiGenericResultPair).Server, "beta")
 | 
			
		||||
	assert.Equal(t, response.Result[2].(apiGenericResultPair).Server, "gamma")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestApiGenericHandlerFactory(t *testing.T) {
 | 
			
		||||
	httpmock.Activate()
 | 
			
		||||
	defer httpmock.DeactivateAndReset()
 | 
			
		||||
 | 
			
		||||
	httpResponse := httpmock.NewStringResponder(200, BirdSummaryData)
 | 
			
		||||
	httpmock.RegisterResponder("GET", "http://alpha:8000/bird?q="+url.QueryEscape("show protocols"), httpResponse)
 | 
			
		||||
 | 
			
		||||
	setting.servers = []string{"alpha"}
 | 
			
		||||
	setting.domain = ""
 | 
			
		||||
	setting.proxyPort = 8000
 | 
			
		||||
 | 
			
		||||
	request := apiRequest{
 | 
			
		||||
		Servers: setting.servers,
 | 
			
		||||
		Type:    "bird",
 | 
			
		||||
		Args:    "show protocols",
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	handler := apiGenericHandlerFactory("bird")
 | 
			
		||||
	response := handler(request)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, response.Error, "")
 | 
			
		||||
 | 
			
		||||
	result := response.Result[0].(*apiGenericResultPair)
 | 
			
		||||
	assert.Equal(t, result.Server, "alpha")
 | 
			
		||||
	assert.Equal(t, result.Data, BirdSummaryData)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestApiSummaryHandler(t *testing.T) {
 | 
			
		||||
	httpmock.Activate()
 | 
			
		||||
	defer httpmock.DeactivateAndReset()
 | 
			
		||||
 | 
			
		||||
	httpResponse := httpmock.NewStringResponder(200, BirdSummaryData)
 | 
			
		||||
	httpmock.RegisterResponder("GET", "http://alpha:8000/bird?q="+url.QueryEscape("show protocols"), httpResponse)
 | 
			
		||||
 | 
			
		||||
	setting.servers = []string{"alpha"}
 | 
			
		||||
	setting.domain = ""
 | 
			
		||||
	setting.proxyPort = 8000
 | 
			
		||||
 | 
			
		||||
	request := apiRequest{
 | 
			
		||||
		Servers: setting.servers,
 | 
			
		||||
		Type:    "summary",
 | 
			
		||||
		Args:    "",
 | 
			
		||||
	}
 | 
			
		||||
	response := apiSummaryHandler(request)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, response.Error, "")
 | 
			
		||||
 | 
			
		||||
	summary := response.Result[0].(*apiSummaryResultPair)
 | 
			
		||||
	assert.Equal(t, summary.Server, "alpha")
 | 
			
		||||
	// Protocol list will be sorted
 | 
			
		||||
	assert.Equal(t, summary.Data[1].Name, "device1")
 | 
			
		||||
	assert.Equal(t, summary.Data[1].Proto, "Device")
 | 
			
		||||
	assert.Equal(t, summary.Data[1].Table, "---")
 | 
			
		||||
	assert.Equal(t, summary.Data[1].State, "up")
 | 
			
		||||
	assert.Equal(t, summary.Data[1].Since, "2021-08-27")
 | 
			
		||||
	assert.Equal(t, summary.Data[1].Info, "")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestApiSummaryHandlerError(t *testing.T) {
 | 
			
		||||
	httpmock.Activate()
 | 
			
		||||
	defer httpmock.DeactivateAndReset()
 | 
			
		||||
 | 
			
		||||
	httpResponse := httpmock.NewStringResponder(200, "Mock backend error")
 | 
			
		||||
	httpmock.RegisterResponder("GET", "http://alpha:8000/bird?q="+url.QueryEscape("show protocols"), httpResponse)
 | 
			
		||||
 | 
			
		||||
	setting.servers = []string{"alpha"}
 | 
			
		||||
	setting.domain = ""
 | 
			
		||||
	setting.proxyPort = 8000
 | 
			
		||||
 | 
			
		||||
	request := apiRequest{
 | 
			
		||||
		Servers: setting.servers,
 | 
			
		||||
		Type:    "summary",
 | 
			
		||||
		Args:    "",
 | 
			
		||||
	}
 | 
			
		||||
	response := apiSummaryHandler(request)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, response.Error, "Mock backend error")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestApiWhoisHandler(t *testing.T) {
 | 
			
		||||
	expectedData := "Mock Data"
 | 
			
		||||
	server := WhoisServer{
 | 
			
		||||
		t:             t,
 | 
			
		||||
		expectedQuery: "AS6939",
 | 
			
		||||
		response:      expectedData,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	server.Listen()
 | 
			
		||||
	go server.Run()
 | 
			
		||||
	defer server.Close()
 | 
			
		||||
 | 
			
		||||
	setting.whoisServer = server.server.Addr().String()
 | 
			
		||||
 | 
			
		||||
	request := apiRequest{
 | 
			
		||||
		Servers: []string{},
 | 
			
		||||
		Type:    "",
 | 
			
		||||
		Args:    "AS6939",
 | 
			
		||||
	}
 | 
			
		||||
	response := apiWhoisHandler(request)
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, response.Error, "")
 | 
			
		||||
 | 
			
		||||
	whoisResult := response.Result[0].(apiGenericResultPair)
 | 
			
		||||
	assert.Equal(t, whoisResult.Server, "")
 | 
			
		||||
	assert.Equal(t, whoisResult.Data, expectedData)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestApiErrorHandler(t *testing.T) {
 | 
			
		||||
	err := errors.New("Mock Error")
 | 
			
		||||
	response := apiErrorHandler(err)
 | 
			
		||||
	assert.Equal(t, response.Error, "Mock Error")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestApiHandler(t *testing.T) {
 | 
			
		||||
	setting.servers = []string{"alpha", "beta", "gamma"}
 | 
			
		||||
 | 
			
		||||
	request := apiRequest{
 | 
			
		||||
		Servers: []string{},
 | 
			
		||||
		Type:    "server_list",
 | 
			
		||||
		Args:    "",
 | 
			
		||||
	}
 | 
			
		||||
	requestJson, err := json.Marshal(request)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := httptest.NewRequest(http.MethodGet, "/api", bytes.NewReader(requestJson))
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
	apiHandler(w, r)
 | 
			
		||||
 | 
			
		||||
	var response apiResponse
 | 
			
		||||
	err = json.Unmarshal(w.Body.Bytes(), &response)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, len(response.Result), 3)
 | 
			
		||||
	// Hard to unmarshal JSON into apiGenericResultPair objects, won't check here
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestApiHandlerBadJSON(t *testing.T) {
 | 
			
		||||
	setting.servers = []string{"alpha", "beta", "gamma"}
 | 
			
		||||
 | 
			
		||||
	r := httptest.NewRequest(http.MethodGet, "/api", strings.NewReader("{bad json}"))
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
	apiHandler(w, r)
 | 
			
		||||
 | 
			
		||||
	var response apiResponse
 | 
			
		||||
	err := json.Unmarshal(w.Body.Bytes(), &response)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, len(response.Result), 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestApiHandlerInvalidType(t *testing.T) {
 | 
			
		||||
	setting.servers = []string{"alpha", "beta", "gamma"}
 | 
			
		||||
 | 
			
		||||
	request := apiRequest{
 | 
			
		||||
		Servers: setting.servers,
 | 
			
		||||
		Type:    "invalid_type",
 | 
			
		||||
		Args:    "",
 | 
			
		||||
	}
 | 
			
		||||
	requestJson, err := json.Marshal(request)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := httptest.NewRequest(http.MethodGet, "/api", bytes.NewReader(requestJson))
 | 
			
		||||
	w := httptest.NewRecorder()
 | 
			
		||||
	apiHandler(w, r)
 | 
			
		||||
 | 
			
		||||
	var response apiResponse
 | 
			
		||||
	err = json.Unmarshal(w.Body.Bytes(), &response)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Error(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, len(response.Result), 0)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										83
									
								
								frontend/asn_cache.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								frontend/asn_cache.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,83 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type ASNCache map[string]string
 | 
			
		||||
 | 
			
		||||
func (cache ASNCache) _lookup(asn string) string {
 | 
			
		||||
	// Try to get ASN representation using DNS
 | 
			
		||||
	if setting.dnsInterface != "" {
 | 
			
		||||
		records, err := net.LookupTXT(fmt.Sprintf("AS%s.%s", asn, setting.dnsInterface))
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			result := strings.Join(records, " ")
 | 
			
		||||
			if resultSplit := strings.Split(result, " | "); len(resultSplit) > 1 {
 | 
			
		||||
				result = strings.Join(resultSplit[1:], "\n")
 | 
			
		||||
			}
 | 
			
		||||
			return fmt.Sprintf("AS%s\n%s", asn, result)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Try to get ASN representation using WHOIS
 | 
			
		||||
	if setting.whoisServer != "" {
 | 
			
		||||
		if setting.bgpmapInfo == "" {
 | 
			
		||||
			setting.bgpmapInfo = "asn,as-name,ASName,descr"
 | 
			
		||||
		}
 | 
			
		||||
		records := whois(fmt.Sprintf("AS%s", asn))
 | 
			
		||||
		if records != "" {
 | 
			
		||||
			recordsSplit := strings.Split(records, "\n")
 | 
			
		||||
			var result []string
 | 
			
		||||
			for _, title := range strings.Split(setting.bgpmapInfo, ",") {
 | 
			
		||||
				if title == "asn" {
 | 
			
		||||
					result = append(result, "AS"+asn)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			for _, title := range strings.Split(setting.bgpmapInfo, ",") {
 | 
			
		||||
				allow_multiline := false
 | 
			
		||||
				if title[0] == ':' && len(title) >= 2 {
 | 
			
		||||
					title = title[1:]
 | 
			
		||||
					allow_multiline = true
 | 
			
		||||
				}
 | 
			
		||||
				for _, line := range recordsSplit {
 | 
			
		||||
					if len(line) == 0 || line[0] == '%' || !strings.Contains(line, ":") {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					linearr := strings.SplitN(line, ":", 2)
 | 
			
		||||
					line_title := linearr[0]
 | 
			
		||||
					content := strings.TrimSpace(linearr[1])
 | 
			
		||||
					if line_title != title {
 | 
			
		||||
						continue
 | 
			
		||||
					}
 | 
			
		||||
					result = append(result, content)
 | 
			
		||||
					if !allow_multiline {
 | 
			
		||||
						break
 | 
			
		||||
					}
 | 
			
		||||
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if len(result) > 0 {
 | 
			
		||||
				return strings.Join(result, "\n")
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return ""
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (cache ASNCache) Lookup(asn string) string {
 | 
			
		||||
	cachedValue, cacheOk := cache[asn]
 | 
			
		||||
	if cacheOk {
 | 
			
		||||
		return cachedValue
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	result := cache._lookup(asn)
 | 
			
		||||
	if len(result) == 0 {
 | 
			
		||||
		result = fmt.Sprintf("AS%s", asn)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cache[asn] = result
 | 
			
		||||
	return result
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										52
									
								
								frontend/asn_cache_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								frontend/asn_cache_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,52 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/magiconair/properties/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestGetASNRepresentationDNS(t *testing.T) {
 | 
			
		||||
	checkNetwork(t)
 | 
			
		||||
 | 
			
		||||
	setting.dnsInterface = "asn.cymru.com"
 | 
			
		||||
	setting.whoisServer = ""
 | 
			
		||||
	cache := make(ASNCache)
 | 
			
		||||
	result := cache.Lookup("6939")
 | 
			
		||||
	if !strings.Contains(result, "HURRICANE") {
 | 
			
		||||
		t.Errorf("Lookup AS6939 failed, got %s", result)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetASNRepresentationDNSFallback(t *testing.T) {
 | 
			
		||||
	checkNetwork(t)
 | 
			
		||||
 | 
			
		||||
	setting.dnsInterface = "invalid.example.com"
 | 
			
		||||
	setting.whoisServer = "whois.arin.net"
 | 
			
		||||
	cache := make(ASNCache)
 | 
			
		||||
	result := cache.Lookup("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"
 | 
			
		||||
	cache := make(ASNCache)
 | 
			
		||||
	result := cache.Lookup("6939")
 | 
			
		||||
	if !strings.Contains(result, "HURRICANE") {
 | 
			
		||||
		t.Errorf("Lookup AS6939 failed, got %s", result)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetASNRepresentationFallback(t *testing.T) {
 | 
			
		||||
	setting.dnsInterface = ""
 | 
			
		||||
	setting.whoisServer = ""
 | 
			
		||||
	cache := make(ASNCache)
 | 
			
		||||
	result := cache.Lookup("6939")
 | 
			
		||||
	assert.Equal(t, result, "AS6939")
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								frontend/assets/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								frontend/assets/favicon.ico
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 32 KiB  | 
							
								
								
									
										2
									
								
								frontend/assets/robots.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								frontend/assets/robots.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
User-agent: *
 | 
			
		||||
Disallow: /
 | 
			
		||||
							
								
								
									
										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);
 | 
			
		||||
        });
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										16
									
								
								frontend/assets/templates/bgpmap.tpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								frontend/assets/templates/bgpmap.tpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
			
		||||
<h2>BGPmap: {{ html .Target }}</h2>
 | 
			
		||||
<div id="bgpmap">
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<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(atob({{ .Result }}))
 | 
			
		||||
  .then(element => {
 | 
			
		||||
    document.getElementById("bgpmap").appendChild(element);
 | 
			
		||||
  })
 | 
			
		||||
  .catch(error => {
 | 
			
		||||
    document.getElementById("bgpmap").innerHTML = "<pre>"+error+"</pre>"
 | 
			
		||||
  });
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										2
									
								
								frontend/assets/templates/bird.tpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								frontend/assets/templates/bird.tpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,2 @@
 | 
			
		||||
<h2>{{ html .ServerName }}: {{ html .Target }}</h2>
 | 
			
		||||
{{ .Result }}
 | 
			
		||||
							
								
								
									
										100
									
								
								frontend/assets/templates/page.tpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								frontend/assets/templates/page.tpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,100 @@
 | 
			
		||||
<!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="/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="{{ .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>
 | 
			
		||||
 | 
			
		||||
	<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">
 | 
			
		||||
				{{ if eq .AllServersURLCustom "all" }}
 | 
			
		||||
				<a class="nav-link{{ if .AllServersLinkActive }} active{{ end }}"
 | 
			
		||||
					href="/{{ $option }}/{{ .AllServersURL }}/{{ $target }}"> {{ .AllServerTitle }} </a>
 | 
			
		||||
				{{ else }}
 | 
			
		||||
				<a class="nav-link active"
 | 
			
		||||
					href="{{ .AllServersURLCustom }}"> {{ .AllServerTitle }} </a>
 | 
			
		||||
				{{ end }}
 | 
			
		||||
			</li>
 | 
			
		||||
			{{ $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>
 | 
			
		||||
		{{ if .IsWhois }}
 | 
			
		||||
			{{ $target = .WhoisTarget }}
 | 
			
		||||
		{{ end }}
 | 
			
		||||
		<form name="goto" class="form-inline" action="javascript:goto();">
 | 
			
		||||
			<div class="input-group">
 | 
			
		||||
				<select name="action" class="form-control">
 | 
			
		||||
					{{ range $k, $v := .Options }}
 | 
			
		||||
					<option value="{{ html $k }}"{{ if eq $k $.URLOption }} selected{{end}}>{{ html $v }}</option>
 | 
			
		||||
					{{ end }}
 | 
			
		||||
				</select>
 | 
			
		||||
				<input name="server" class="d-none" value="{{ html ($server | pathescape) }}">
 | 
			
		||||
				<input name="target" class="form-control" placeholder="Target" aria-label="Target" value="{{ html $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="/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() {
 | 
			
		||||
	let action = $('[name="action"]').val();
 | 
			
		||||
	let server = $('[name="server"]').val();
 | 
			
		||||
	let target = $('[name="target"]').val();
 | 
			
		||||
	let url = "";
 | 
			
		||||
 | 
			
		||||
	if (action == "whois") {
 | 
			
		||||
		url = "/" + action + "/" + target;
 | 
			
		||||
	} else if (action == "summary") {
 | 
			
		||||
		url = "/" + action + "/" + server + "/";
 | 
			
		||||
	} else {
 | 
			
		||||
		url = "/" + action + "/" + server + "/" + target;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	window.location.href = url;
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
							
								
								
									
										21
									
								
								frontend/assets/templates/summary.tpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								frontend/assets/templates/summary.tpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,21 @@
 | 
			
		||||
{{ $ServerName := urlquery .ServerName }}
 | 
			
		||||
 | 
			
		||||
<table class="table table-striped table-bordered table-sm sortable">
 | 
			
		||||
  <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>{{ html .Proto }}</td>
 | 
			
		||||
      <td>{{ html .Table }}</td>
 | 
			
		||||
      <td>{{ html .State }}</td>
 | 
			
		||||
      <td>{{ html .Since }}</td>
 | 
			
		||||
      <td>{{ html .Info  }}</td>
 | 
			
		||||
    </tr>
 | 
			
		||||
{{ end }}
 | 
			
		||||
  </tbody>
 | 
			
		||||
</table>
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user