You've already forked bird-lg-go
							
							
				mirror of
				https://github.com/xddxdd/bird-lg-go
				synced 2025-10-30 09:40:51 +01:00 
			
		
		
		
	Compare commits
	
		
			208 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 1b2573d87c | ||
|   | 0d5337508b | ||
|   | b9094d3d6c | ||
|   | ec7f348418 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | a632739443 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | a9e278357a | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | e4c00c897f | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 4df3918b35 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 45dc24470d | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 55ea5c3b28 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 7eb44c3828 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 124fdedbda | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | e6a98358b5 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 761eb2160a | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | cc2a146a88 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 3db9454350 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | c30bed112c | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | af5ab3c78f | ||
|   | 0fdde8afc7 | ||
|   | 39a129db9d | ||
|   | 0dd1c07b66 | ||
|   | f0f072c4a6 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 657565857b | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 7ac2158e70 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 5c433bc27a | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 1b0b923da9 | ||
|   | 01438edaef | ||
|   | 90f36610dc | ||
|   | 6174208d07 | ||
|   | 76174cdc08 | ||
|   | 088bb6fe5a | ||
|   | 3951eed011 | ||
|   | 91c0a8962b | ||
|   | 5f7850a903 | ||
|   | 6a78cf2e80 | ||
|   | 5b5a44bcb6 | ||
|   | ac31862237 | ||
|   | 86129190ab | ||
|   | ff55064a20 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | dbb02c04ed | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | c2b7de2e17 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | c1b578e8db | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 7b0e5689d4 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 3c46bda49d | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 32e00d2ce3 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | a19750cdef | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 7f1cdaa4ee | ||
|   | 2d2193041e | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | aad8ee98d7 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 00b5c12787 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 55a1eb54fd | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 0594edc69d | ||
|   | 38bf6aba09 | ||
|   | d261c22235 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 19aa8c77c5 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | fe07ebb5a5 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 66547ebfa9 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | d253e4311b | ||
|   | 026498ba2f | ||
|   | 27c348a864 | ||
|   | 43b4ad93dd | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 6176c45006 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 47113184f4 | ||
|   | 3c9a3e4339 | ||
|   | 8457b18d46 | ||
|   | f8f64b03a6 | ||
|   | cc818c1cc0 | ||
|   | 6224b43808 | ||
|   | 17e0b14243 | ||
|   | b4c1bed9ba | ||
|   | abb32abff3 | ||
|   | b368c75aa3 | ||
|   | 09405cdb38 | ||
|   | f999d47d9f | ||
|   | 005dfb1435 | ||
|   | 4bd7a6bb95 | ||
|   | 462d76a2d0 | ||
|   | 58f217578c | ||
|   | 0e95727de1 | ||
|   | a48f1c8040 | ||
|   | 81acde3a37 | ||
|   | 7c0fe0d512 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | a5f4452d02 | ||
|   | b237185ef7 | ||
|   | e949646790 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | bb479d22ae | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | d40f41b4d5 | ||
|   | cdc34704b5 | ||
|   | db58bd3354 | ||
|   | a0246ccee2 | ||
|   | ccd14af0c8 | ||
|   | 594ca80f50 | ||
|   | 5625058e71 | ||
|   | 7efa3237a9 | ||
|   | 7b0c8c0556 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | ffd9165062 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 24fd5203e8 | ||
|   | 49a05767c1 | ||
|   | e7010f75f8 | ||
|   | dba2af7634 | ||
|   | 049775319b | ||
|   | 47c66b125c | ||
|   | 9e17b116f1 | ||
|   | 335ad40634 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 6ec0f2e7a6 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 4b73cf0fcb | ||
|   | 3b1d001543 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 675cb26ed1 | ||
| ![dependabot[bot]](/assets/img/avatar_default.png)  | 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 | ||
| ![dependabot-preview[bot]](/assets/img/avatar_default.png)  | 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 | ||||||
							
								
								
									
										16
									
								
								.github/workflows/auto-merge.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/auto-merge.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | |||||||
|  | name: auto-merge | ||||||
|  |  | ||||||
|  | on: | ||||||
|  |   pull_request_target: | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   auto-merge: | ||||||
|  |     name: Dependabot Auto Merge | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     if: github.actor == 'dependabot[bot]' || github.actor == 'dependabot-preview[bot]' | ||||||
|  |     steps: | ||||||
|  |       - uses: actions/checkout@v2 | ||||||
|  |       - uses: ahmadnassri/action-dependabot-auto-merge@v2 | ||||||
|  |         with: | ||||||
|  |           target: minor | ||||||
|  |           github-token: ${{ secrets.AUTOMERGE_TOKEN }} | ||||||
							
								
								
									
										127
									
								
								.github/workflows/develop.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								.github/workflows/develop.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,127 @@ | |||||||
|  | on: | ||||||
|  |   push: | ||||||
|  |     branches: | ||||||
|  |       - 'master' | ||||||
|  |   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 -I 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 | ||||||
|  |  | ||||||
|  |       - name: Test mtr binary in proxy image | ||||||
|  |         run: | | ||||||
|  |           docker build -t local/proxy:mtr -f proxy/Dockerfile.mtr proxy/ | ||||||
|  |           docker run --rm --net host --entrypoint mtr local/proxy:mtr -w -c1 -Z1 -G1 -b 127.0.0.1 || exit 1 | ||||||
|  |           docker run --rm --net host --entrypoint mtr local/proxy:mtr -w -c1 -Z1 -G1 -b ::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 }} | ||||||
|  |  | ||||||
|  |       - name: Build proxy docker image | ||||||
|  |         uses: docker/build-push-action@v4 | ||||||
|  |         with: | ||||||
|  |           context: '{{defaultContext}}:proxy' | ||||||
|  |           file: 'Dockerfile.mtr' | ||||||
|  |           platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7 | ||||||
|  |           push: true | ||||||
|  |           tags: | | ||||||
|  |             xddxdd/bird-lgproxy-go:develop-mtr | ||||||
|  |             xddxdd/bird-lgproxy-go:develop-${{ github.sha }}-mtr | ||||||
|  |             ghcr.io/xddxdd/bird-lg-go:proxy-develop-mtr | ||||||
|  |             ghcr.io/xddxdd/bird-lg-go:proxy-develop-${{ github.sha }}-mtr | ||||||
							
								
								
									
										100
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								.github/workflows/release.yaml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | |||||||
|  | on: | ||||||
|  |   release: | ||||||
|  |     types: [created] | ||||||
|  |  | ||||||
|  | jobs: | ||||||
|  |   go-release: | ||||||
|  |     name: Release Go Binary | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     strategy: | ||||||
|  |       fail-fast: false | ||||||
|  |       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.40 | ||||||
|  |       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.40 | ||||||
|  |       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 | ||||||
|  |             xddxdd/bird-lg-go:${{ github.event.release.tag_name }} | ||||||
|  |             ghcr.io/xddxdd/bird-lg-go:frontend | ||||||
|  |             ghcr.io/xddxdd/bird-lg-go:frontend-${{ github.event.release.tag_name }} | ||||||
|  |  | ||||||
|  |       - 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 | ||||||
|  |             xddxdd/bird-lgproxy-go:${{ github.event.release.tag_name }} | ||||||
|  |             ghcr.io/xddxdd/bird-lg-go:proxy | ||||||
|  |             ghcr.io/xddxdd/bird-lg-go:proxy-${{ github.event.release.tag_name }} | ||||||
|  |  | ||||||
|  |       - name: Build proxy docker image | ||||||
|  |         uses: docker/build-push-action@v4 | ||||||
|  |         with: | ||||||
|  |           context: '{{defaultContext}}:proxy' | ||||||
|  |           file: 'Dockerfile.mtr' | ||||||
|  |           platforms: linux/amd64,linux/arm64,linux/386,linux/arm/v7 | ||||||
|  |           push: true | ||||||
|  |           tags: | | ||||||
|  |             xddxdd/bird-lgproxy-go:latest-mtr | ||||||
|  |             xddxdd/bird-lgproxy-go:${{ github.event.release.tag_name }}-mtr | ||||||
|  |             ghcr.io/xddxdd/bird-lg-go:proxy-mtr | ||||||
|  |             ghcr.io/xddxdd/bird-lg-go:proxy-${{ github.event.release.tag_name }}-mtr | ||||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -16,4 +16,7 @@ | |||||||
|  |  | ||||||
| .DS_Store | .DS_Store | ||||||
| frontend/frontend | frontend/frontend | ||||||
| proxy/proxy | proxy/proxy | ||||||
|  |  | ||||||
|  | # don't include generated bindata file | ||||||
|  | frontend/bindata.go | ||||||
|   | |||||||
							
								
								
									
										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 | ||||||
							
								
								
									
										134
									
								
								frontend/api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								frontend/api.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | |||||||
|  | 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"` | ||||||
|  | 	Error  string           `json:"error,omitempty"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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 { | ||||||
|  | 			response.Result = append(response.Result, &apiSummaryResultPair{ | ||||||
|  | 				Server: request.Servers[i], | ||||||
|  | 				Data:   []SummaryRowData{}, | ||||||
|  | 				Error:  err.Error(), | ||||||
|  | 			}) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		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) | ||||||
|  | } | ||||||
							
								
								
									
										210
									
								
								frontend/api_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								frontend/api_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,210 @@ | |||||||
|  | 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, "") | ||||||
|  |  | ||||||
|  | 	summary := response.Result[0].(*apiSummaryResultPair) | ||||||
|  | 	assert.Equal(t, summary.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); | ||||||
|  |         }); | ||||||
|  | } | ||||||
							
								
								
									
										27
									
								
								frontend/assets/templates/bgpmap.tpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								frontend/assets/templates/bgpmap.tpl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | <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> | ||||||
|  |   function decodeBase64(base64) { | ||||||
|  |     const text = atob(base64); | ||||||
|  |     const length = text.length; | ||||||
|  |     const bytes = new Uint8Array(length); | ||||||
|  |     for (let i = 0; i < length; i++) { | ||||||
|  |         bytes[i] = text.charCodeAt(i); | ||||||
|  |     } | ||||||
|  |     const decoder = new TextDecoder(); | ||||||
|  |     return decoder.decode(bytes); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   var viz = new Viz(); | ||||||
|  |   viz.renderSVGElement(decodeBase64({{ .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 }} | ||||||
							
								
								
									
										113
									
								
								frontend/assets/templates/page.tpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								frontend/assets/templates/page.tpl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,113 @@ | |||||||
|  | <!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" crossorigin="anonymous"> | ||||||
|  | <style> | ||||||
|  | .navbar-nav { | ||||||
|  | 	flex-wrap: wrap; | ||||||
|  | } | ||||||
|  | @media (min-width: 768px) { | ||||||
|  | 	.navbar form { | ||||||
|  | 		min-width: 400px; | ||||||
|  | 	} | ||||||
|  | 	.nav-link { | ||||||
|  | 		padding: 0.2rem 0.5rem !important; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | </style> | ||||||
|  | <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" crossorigin="anonymous"></script> | ||||||
|  | <script src="/static/jsdelivr/npm/bootstrap@4.5.1/dist/js/bootstrap.min.js" 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> | ||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user