mirror of
				https://github.com/oneclickvirt/ecs.git
				synced 2025-11-01 03:12:44 +08:00 
			
		
		
		
	Compare commits
	
		
			266 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | a075c8cf3d | ||
|   | 3b72b4c86e | ||
|   | 3aed20fe37 | ||
|   | 02da203b50 | ||
|   | b427078db8 | ||
|   | f433a4b117 | ||
|   | 425abcd206 | ||
|   | fe48a944c8 | ||
|   | c5d2527763 | ||
|   | 22e60b4124 | ||
|   | 8f1f71809f | ||
|   | d20523371c | ||
|   | a046238dbc | ||
|   | c917036cb6 | ||
|   | 7736554620 | ||
|   | b8502d5886 | ||
|   | b5393f0454 | ||
|   | eb9043856a | ||
|   | 11f589b622 | ||
|   | 7997f83ba5 | ||
|   | 6c555095b3 | ||
|   | 41954b9522 | ||
|   | 4994b2b8af | ||
|   | d26bb65377 | ||
|   | b030a1f2ed | ||
|   | 5edb53d957 | ||
|   | 2dd100b1da | ||
|   | 310fbb676e | ||
|   | 8db6aaf026 | ||
|   | f2c80c30a6 | ||
|   | e422765293 | ||
|   | 338491c585 | ||
|   | e0fc9f7f0f | ||
|   | 8b57282d7f | ||
|   | 8394f3a78d | ||
|   | 01e6bac876 | ||
|   | 5a0bba48ae | ||
|   | 49a4ef0543 | ||
|   | 8f74567cbf | ||
|   | 0f01bd359d | ||
|   | d40bc77ba9 | ||
|   | c730aa59b4 | ||
|   | 8a77cff853 | ||
|   | 8aaa05d45f | ||
|   | 7e7b65c2b7 | ||
|   | 65296157d9 | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 61d1607366 | ||
| ![copilot-swe-agent[bot]](/assets/img/avatar_default.png)  | 8533cfd108 | ||
|   | 8512f5e34b | ||
|   | 2c509714a5 | ||
|   | a042b66c1e | ||
|   | 5edf6ca24d | ||
|   | adf6fcbcd9 | ||
|   | c5445ed679 | ||
|   | a3c116a25d | ||
|   | 84f2b41b05 | ||
|   | fca661388d | ||
|   | e5cf95b05f | ||
|   | dbceca7169 | ||
|   | 86b28ef383 | ||
|   | c1a8695149 | ||
|   | ebcf2da2d8 | ||
|   | a7207afb50 | ||
|   | 5aeaa0218a | ||
|   | 8ac7230b06 | ||
|   | 3c468794a3 | ||
|   | c764f60de9 | ||
|   | 850c3cb129 | ||
|   | cee4e66ad0 | ||
|   | 4cb5c67183 | ||
|   | ff040b1ed2 | ||
|   | d739f44bf3 | ||
|   | 2be35bdffe | ||
|   | 10e0d09c6f | ||
|   | 6616da58cd | ||
|   | afa684f042 | ||
|   | 25c1d60d2b | ||
|   | f4beb22fb2 | ||
|   | df9e4d2a78 | ||
|   | 4facdad7b5 | ||
|   | 5faea697b0 | ||
|   | 07d09e4b41 | ||
|   | eb41920720 | ||
|   | 7a4208ecd3 | ||
|   | 416170ec30 | ||
|   | e26fa8312f | ||
|   | 288a71f13f | ||
|   | ea1b982c0d | ||
|   | 005bcd750d | ||
|   | d08008a51e | ||
|   | a5153cbdb4 | ||
|   | c20bcc2515 | ||
|   | 691e934aa9 | ||
|   | 578fc5338f | ||
|   | 48c6d76863 | ||
|   | ed69abb382 | ||
|   | c807be29d8 | ||
|   | 8554b9184a | ||
|   | e787c934c0 | ||
|   | a363ee6ee3 | ||
|   | ec731a4775 | ||
|   | cb8d8226b6 | ||
|   | 6bd0a1c02d | ||
|   | a86f7c4d6c | ||
|   | 12c5ece7bf | ||
|   | f34e182ae4 | ||
|   | 9f3d1a1960 | ||
|   | c19b29e88a | ||
|   | 2ccf5a9b09 | ||
|   | 8d1a1c8b3b | ||
|   | c65b236a89 | ||
|   | c78ba25f02 | ||
|   | 0a406e744e | ||
|   | acb8699211 | ||
|   | 3b2493f770 | ||
|   | af6bda9b86 | ||
|   | 4608e1bf3a | ||
|   | 5f3c257158 | ||
|   | 441b4d28ef | ||
|   | 6c2c2a6791 | ||
|   | 4eb982a22d | ||
|   | 88c50e3c1c | ||
|   | c272da23fb | ||
|   | 094b05ffbd | ||
|   | 63d2c2be15 | ||
|   | 9d540c1f94 | ||
|   | f9b588007f | ||
|   | cb261032fd | ||
|   | 1fca387a39 | ||
|   | 370695c670 | ||
|   | fa0cb5f194 | ||
|   | ba9a9e7cbd | ||
|   | 66d907a68a | ||
|   | 8f99d36b4f | ||
|   | 76af1e9600 | ||
|   | 6044e328f2 | ||
|   | fca03354bb | ||
|   | 795eaede40 | ||
|   | 2b7991df96 | ||
|   | 90d664194b | ||
|   | fd62156564 | ||
|   | ebd581306c | ||
|   | 695f13a129 | ||
|   | 3941537061 | ||
|   | daf8f65c12 | ||
|   | 06addade41 | ||
|   | 8fda92a19c | ||
|   | 03b7c1541c | ||
|   | 7273321854 | ||
|   | 0556680e61 | ||
|   | 805809795e | ||
|   | e7ac550065 | ||
|   | 2be8e0ee88 | ||
|   | e338f1a512 | ||
|   | 48023f9ed1 | ||
|   | 7f9fad3fea | ||
|   | d5d139e218 | ||
|   | 35e0fd426d | ||
|   | 7bf141e303 | ||
|   | 12dbceca0d | ||
|   | e07d8b5114 | ||
|   | 0ceb45d17c | ||
|   | 0bbfa95b9c | ||
|   | f927185459 | ||
|   | c8271b713c | ||
|   | 27f3bad9ef | ||
|   | dd85b0fd2d | ||
|   | 47fdb43e54 | ||
|   | 6e19ab79d7 | ||
|   | dee3211d3b | ||
|   | 67a3bfdaad | ||
|   | e57ce05c0c | ||
|   | 62137f0bb1 | ||
|   | f9d8c9ee4c | ||
|   | 913a1725c1 | ||
|   | f1acbd361b | ||
|   | d6f62f8624 | ||
|   | 94da890522 | ||
|   | 42b94260de | ||
|   | 16e2603176 | ||
|   | 0ed9840038 | ||
|   | 31b53e13b0 | ||
|   | fb697fa25f | ||
|   | 2510b3ead0 | ||
|   | b64b8a2d96 | ||
|   | 663614117d | ||
|   | a96c9b6c7d | ||
|   | e8f73ba4b8 | ||
|   | dd6fbff943 | ||
|   | e473851a02 | ||
|   | 86fc407ccb | ||
|   | 8e6037c340 | ||
|   | be59af4411 | ||
|   | 7ae753a4e2 | ||
|   | 3956420ffe | ||
|   | 6ebe25af2d | ||
|   | fe02bd295a | ||
|   | 172b520702 | ||
|   | d460e2b167 | ||
|   | 03f5fd9e48 | ||
|   | 0b0972cc3e | ||
|   | 8273895def | ||
|   | 3e31671502 | ||
|   | 42b3ab3cff | ||
|   | 0bef8161e7 | ||
|   | 4f7e37f37c | ||
|   | c0dcea53e3 | ||
|   | 02034da2d7 | ||
|   | aa6c835aed | ||
|   | 2ebfdf7533 | ||
|   | 28e21f0838 | ||
|   | 23aae0b32b | ||
|   | abaf021e1c | ||
|   | be4e46ceba | ||
|   | 4746ae14cf | ||
|   | eb702c4d97 | ||
|   | 86bde156a4 | ||
|   | 08be6238d2 | ||
|   | c0472209b4 | ||
|   | e751b76099 | ||
|   | bf653ab620 | ||
|   | c4b7ba00a1 | ||
|   | 87a288907a | ||
|   | c3aa62753b | ||
|   | 83b8efd7ee | ||
|   | 2fa0270e4e | ||
|   | 3bd1136683 | ||
|   | d5837ed535 | ||
|   | 080c62cd93 | ||
|   | ce690aab2d | ||
|   | 05ec4d75b8 | ||
|   | 553e0ddef6 | ||
|   | 819d155fd5 | ||
|   | d7519801b0 | ||
|   | d6302be4ae | ||
|   | a8366005dc | ||
|   | 4e3249b590 | ||
|   | 8a52f6df76 | ||
|   | ebe85216bb | ||
|   | 7909d4ba78 | ||
|   | 4939d155d8 | ||
|   | d69f85d0db | ||
|   | f4dea6ce6e | ||
|   | bc596e8e48 | ||
|   | 116ae33505 | ||
|   | a32846638b | ||
|   | d84fb21f82 | ||
|   | f199336cf0 | ||
|   | a1196ab8d2 | ||
|   | 9431fe3944 | ||
|   | 5dcef9f4c3 | ||
|   | 865ca62f06 | ||
|   | eea78a7c30 | ||
|   | 2f5feace7b | ||
|   | 615ffca90f | ||
|   | 685fe735f6 | ||
|   | 5545990854 | ||
|   | 91d1aa4461 | ||
|   | 94f9576383 | ||
|   | ef91a61d22 | ||
|   | bafd77e2f5 | ||
|   | eb03d50cd2 | ||
|   | dbd921d8ca | ||
|   | 8b9df2c385 | ||
|   | ce57c141a0 | ||
|   | da3dc7e11e | 
							
								
								
									
										44
									
								
								.github/workflows/build_docker.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										44
									
								
								.github/workflows/build_docker.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -1,44 +0,0 @@ | |||||||
| name: Build and Push Docker Image |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   workflow_dispatch: |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   build: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: Checkout repository |  | ||||||
|         uses: actions/checkout@v2 |  | ||||||
|  |  | ||||||
|       - name: Set up QEMU |  | ||||||
|         uses: docker/setup-qemu-action@v2 |  | ||||||
|         with: |  | ||||||
|           platforms: all |  | ||||||
|  |  | ||||||
|       - name: Set up Docker Buildx |  | ||||||
|         uses: docker/setup-buildx-action@v2 |  | ||||||
|  |  | ||||||
|       - name: Log in to Docker Hub |  | ||||||
|         uses: docker/login-action@v2 |  | ||||||
|         with: |  | ||||||
|           username: ${{ secrets.DOCKER_USERNAME }} |  | ||||||
|           password: ${{ secrets.DOCKER_PASSWORD }} |  | ||||||
|  |  | ||||||
|       - name: Login to CNB Registry |  | ||||||
|         uses: docker/login-action@v2 |  | ||||||
|         with: |  | ||||||
|           registry: ${{ secrets.CNB_DOCKER_REGISTRY }} |  | ||||||
|           username: ${{ secrets.CNB_USERNAME }} |  | ||||||
|           password: ${{ secrets.CNB_TOKEN }} |  | ||||||
|  |  | ||||||
|       - name: Build and push Docker images |  | ||||||
|         uses: docker/build-push-action@v4 |  | ||||||
|         with: |  | ||||||
|           context: . |  | ||||||
|           file: ./Dockerfile |  | ||||||
|           platforms: linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64,linux/386,linux/riscv64 |  | ||||||
|           # linux/mips,linux/mipsle 暂不支持 alpine, linux/s390x 编译卡死 |  | ||||||
|           push: true |  | ||||||
|           tags: | |  | ||||||
|             ${{ secrets.DOCKER_USERNAME }}/goecs:latest |  | ||||||
|             ${{ secrets.CNB_DOCKER_REGISTRY }}/oneclickvirt/ecs:latest |  | ||||||
							
								
								
									
										66
									
								
								.github/workflows/ci.yaml_back
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										66
									
								
								.github/workflows/ci.yaml_back
									
									
									
									
										vendored
									
									
								
							| @@ -1,66 +0,0 @@ | |||||||
| name: go-ci |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   workflow_dispatch: |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   setup: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: set up |  | ||||||
|         uses: actions/setup-go@v3 |  | ||||||
|         with: |  | ||||||
|           go-version: 1.22.4 |  | ||||||
|         id: go |  | ||||||
|       - name: check out |  | ||||||
|         uses: actions/checkout@v3 |  | ||||||
|       - name: Cache |  | ||||||
|         uses: actions/cache@v2.1.0 |  | ||||||
|         with: |  | ||||||
|           path: ~/go/pkg/mod |  | ||||||
|           key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} |  | ||||||
|           restore-keys: | |  | ||||||
|             ${{ runner.os }}-go- |  | ||||||
|       - name: Configure Git for Private Modules |  | ||||||
|         run: | |  | ||||||
|           git config --global url."https://${{ secrets.GHT }}@github.com/".insteadOf "https://github.com/" |  | ||||||
|           git config --global url."git@github.com:".insteadOf "https://github.com/" |  | ||||||
|         env: |  | ||||||
|           GITHUB_TOKEN: ${{ secrets.GHT }} |  | ||||||
|     env: |  | ||||||
|       GOPRIVATE: github.com/oneclickvirt/security |  | ||||||
|       GITHUB_TOKEN: ${{ secrets.GHT }} |  | ||||||
|  |  | ||||||
|   build: |  | ||||||
|     needs: setup |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - uses: actions/checkout@v3 |  | ||||||
|       - name: Configure Git for Private Modules |  | ||||||
|         run: | |  | ||||||
|           git config --global url."https://${{ secrets.GHT }}@github.com/".insteadOf "https://github.com/" |  | ||||||
|           git config --global url."git@github.com:".insteadOf "https://github.com/" |  | ||||||
|         env: |  | ||||||
|           GITHUB_TOKEN: ${{ secrets.GHT }} |  | ||||||
|       - name: build |  | ||||||
|         run: go build ./... |  | ||||||
|         env: |  | ||||||
|           GOPRIVATE: github.com/oneclickvirt/security |  | ||||||
|           GITHUB_TOKEN: ${{ secrets.GHT }} |  | ||||||
|  |  | ||||||
| #  test: |  | ||||||
| #    needs: setup |  | ||||||
| #    runs-on: ubuntu-latest |  | ||||||
| #    steps: |  | ||||||
| #      - uses: actions/checkout@v3 |  | ||||||
| #      - name: Configure Git for Private Modules |  | ||||||
| #        run: | |  | ||||||
| #          git config --global url."https://${{ secrets.GHT }}@github.com/".insteadOf "https://github.com/" |  | ||||||
| #          git config --global url."git@github.com:".insteadOf "https://github.com/" |  | ||||||
| #        env: |  | ||||||
| #          GITHUB_TOKEN: ${{ secrets.GHT }} |  | ||||||
| #      - name: test |  | ||||||
| #        run: go test ./goecs_test.go |  | ||||||
| #        env: |  | ||||||
| #          GOPRIVATE: github.com/oneclickvirt/security |  | ||||||
| #          GITHUB_TOKEN: ${{ secrets.GHT }} |  | ||||||
							
								
								
									
										42
									
								
								.github/workflows/main.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										42
									
								
								.github/workflows/main.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -1,42 +0,0 @@ | |||||||
| name: goreleaser |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   workflow_dispatch: |  | ||||||
|     tags: |  | ||||||
|       - "v*.*.*" |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   goreleaser: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     container: |  | ||||||
|       # 1.20 是 Windows 7/8 Server 2008/2012 最后一个支持版本 |  | ||||||
|       image: goreleaser/goreleaser-cross:v1.20 |  | ||||||
|     steps: |  | ||||||
|       - run: | |  | ||||||
|           git config --global --add safe.directory /__w/ecs/ecs |  | ||||||
|       - name: Checkout |  | ||||||
|         uses: actions/checkout@v3 |  | ||||||
|         with: |  | ||||||
|           fetch-depth: 0 |  | ||||||
|  |  | ||||||
|       - name: Set up Go |  | ||||||
|         uses: actions/setup-go@v4 |  | ||||||
|         with: |  | ||||||
|           go-version: 1.22.10 |  | ||||||
|  |  | ||||||
|       - name: Configure Git for Private Modules |  | ||||||
|         run: | |  | ||||||
|           git config --global url."https://${{ secrets.GHT }}@github.com/".insteadOf "https://github.com/" |  | ||||||
|           git config --global url."git@github.com:".insteadOf "https://github.com/" |  | ||||||
|         env: |  | ||||||
|           GITHUB_TOKEN: ${{ secrets.GHT }} |  | ||||||
|  |  | ||||||
|       - name: Run GoReleaser |  | ||||||
|         uses: goreleaser/goreleaser-action@v2 |  | ||||||
|         with: |  | ||||||
|           distribution: goreleaser |  | ||||||
|           version: latest |  | ||||||
|           args: release |  | ||||||
|         env: |  | ||||||
|           GITHUB_TOKEN: ${{ secrets.GHT }} |  | ||||||
|           GOPRIVATE: github.com/oneclickvirt/security |  | ||||||
							
								
								
									
										56
									
								
								.github/workflows/sync.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										56
									
								
								.github/workflows/sync.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -1,56 +0,0 @@ | |||||||
| name: Sync Latest Release |  | ||||||
|  |  | ||||||
| on: |  | ||||||
|   release: |  | ||||||
|     types: [published] |  | ||||||
|   workflow_dispatch: |  | ||||||
|  |  | ||||||
| jobs: |  | ||||||
|   sync-release: |  | ||||||
|     runs-on: ubuntu-latest |  | ||||||
|     steps: |  | ||||||
|       - name: Checkout source repository |  | ||||||
|         uses: actions/checkout@v4 |  | ||||||
|         with: |  | ||||||
|           fetch-depth: 0 |  | ||||||
|        |  | ||||||
|       - name: Get latest release |  | ||||||
|         id: get_release |  | ||||||
|         run: | |  | ||||||
|           echo "RELEASE_TAG=$(git describe --tags --abbrev=0)" >> $GITHUB_ENV |  | ||||||
|            |  | ||||||
|       - name: Create temporary directory |  | ||||||
|         run: | |  | ||||||
|           mkdir -p temp_repo |  | ||||||
|           cd temp_repo |  | ||||||
|           git init |  | ||||||
|           git config --local user.name "GitHub Action" |  | ||||||
|           git config --local user.email "action@github.com" |  | ||||||
|            |  | ||||||
|       - name: Copy repository files |  | ||||||
|         run: | |  | ||||||
|           cp goecs.sh temp_repo/ |  | ||||||
|           cp README_EN.md temp_repo/ |  | ||||||
|           cp README.md temp_repo/ |  | ||||||
|           cp LICENSE temp_repo/ |  | ||||||
|            |  | ||||||
|       - name: Download release assets |  | ||||||
|         run: | |  | ||||||
|           cd temp_repo |  | ||||||
|           gh release download ${{ env.RELEASE_TAG }} --repo ${{ github.repository }} --dir . |  | ||||||
|         env: |  | ||||||
|           GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} |  | ||||||
|            |  | ||||||
|       - name: Push to target repository |  | ||||||
|         run: | |  | ||||||
|           cd temp_repo |  | ||||||
|           git add . |  | ||||||
|           git commit -m "Sync release ${{ env.RELEASE_TAG }} from ${{ github.repository }}" |  | ||||||
|           git branch -M main |  | ||||||
|           git remote add target https://cnb.cool/oneclickvirt/ecs.git |  | ||||||
|           echo "machine cnb.cool login ${{ secrets.CNB_USERNAME }} password ${{ secrets.CNB_TOKEN }}" > ~/.netrc |  | ||||||
|           chmod 600 ~/.netrc |  | ||||||
|           git push -f target main |  | ||||||
|         env: |  | ||||||
|           CNB_USERNAME: ${{ secrets.CNB_USERNAME }} |  | ||||||
|           CNB_TOKEN: ${{ secrets.CNB_TOKEN }} |  | ||||||
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | |||||||
| vendor/ |  | ||||||
| @@ -1,78 +0,0 @@ | |||||||
| before: |  | ||||||
|   hooks: |  | ||||||
|     - go mod tidy -v |  | ||||||
| builds: |  | ||||||
|   - id: universal |  | ||||||
|     env: |  | ||||||
|       - CGO_ENABLED=0 |  | ||||||
|     ldflags: |  | ||||||
|       - -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} |  | ||||||
|     goos: |  | ||||||
|       - linux |  | ||||||
|       - windows |  | ||||||
|       - freebsd |  | ||||||
|     goarch: |  | ||||||
|       - arm |  | ||||||
|       - arm64 |  | ||||||
|       - 386 |  | ||||||
|       - amd64 |  | ||||||
|       - mips |  | ||||||
|       - mipsle |  | ||||||
|       - s390x |  | ||||||
|       - riscv64 |  | ||||||
|     gomips: |  | ||||||
|       - softfloat |  | ||||||
|     ignore: |  | ||||||
|       - goos: windows |  | ||||||
|         goarch: arm |  | ||||||
|     main: ./ |  | ||||||
|     binary: goecs |  | ||||||
|   - id: darwin-amd64 |  | ||||||
|     env: |  | ||||||
|       - CGO_ENABLED=1 |  | ||||||
|       - CC=o64-clang |  | ||||||
|       - CXX=o64-clang++ |  | ||||||
|     ldflags: |  | ||||||
|       - -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} |  | ||||||
|     goos: |  | ||||||
|       - darwin |  | ||||||
|     goarch: |  | ||||||
|       - amd64 |  | ||||||
|     main: ./ |  | ||||||
|     binary: goecs |  | ||||||
|   - id: darwin-arm64 |  | ||||||
|     env: |  | ||||||
|       - CGO_ENABLED=1 |  | ||||||
|       - CC=oa64-clang |  | ||||||
|       - CXX=oa64-clang++ |  | ||||||
|     ldflags: |  | ||||||
|       - -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} |  | ||||||
|     goos: |  | ||||||
|       - darwin |  | ||||||
|     goarch: |  | ||||||
|       - arm64 |  | ||||||
|     main: ./ |  | ||||||
|     binary: goecs |  | ||||||
| universal_binaries: |  | ||||||
|   - name_template: "goecs" |  | ||||||
|     replace: false |  | ||||||
| checksum: |  | ||||||
|   name_template: "checksums.txt" |  | ||||||
| snapshot: |  | ||||||
|   name_template: "goecs" |  | ||||||
| archives: |  | ||||||
|   - name_template: "goecs_{{ .Os }}_{{ .Arch }}" |  | ||||||
|     format: zip |  | ||||||
|     files: |  | ||||||
|       - none* |  | ||||||
| changelog: |  | ||||||
|   sort: asc |  | ||||||
|   filters: |  | ||||||
|     exclude: |  | ||||||
|       - "^docs:" |  | ||||||
|       - "^test:" |  | ||||||
|       - "^chore" |  | ||||||
|       - Merge pull request |  | ||||||
|       - Merge branch |  | ||||||
|       - go mod tidy |  | ||||||
|       - New translations |  | ||||||
							
								
								
									
										8
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
								
							| @@ -1,8 +0,0 @@ | |||||||
| # Default ignored files |  | ||||||
| /shelf/ |  | ||||||
| /workspace.xml |  | ||||||
| # Editor-based HTTP Client requests |  | ||||||
| /httpRequests/ |  | ||||||
| # Datasource local storage ignored files |  | ||||||
| /dataSources/ |  | ||||||
| /dataSources.local.xml |  | ||||||
							
								
								
									
										9
									
								
								.idea/ecs.iml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										9
									
								
								.idea/ecs.iml
									
									
									
										generated
									
									
									
								
							| @@ -1,9 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> |  | ||||||
| <module type="WEB_MODULE" version="4"> |  | ||||||
|   <component name="Go" enabled="true" /> |  | ||||||
|   <component name="NewModuleRootManager"> |  | ||||||
|     <content url="file://$MODULE_DIR$" /> |  | ||||||
|     <orderEntry type="inheritedJdk" /> |  | ||||||
|     <orderEntry type="sourceFolder" forTests="false" /> |  | ||||||
|   </component> |  | ||||||
| </module> |  | ||||||
							
								
								
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								.idea/modules.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,8 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> |  | ||||||
| <project version="4"> |  | ||||||
|   <component name="ProjectModuleManager"> |  | ||||||
|     <modules> |  | ||||||
|       <module fileurl="file://$PROJECT_DIR$/.idea/ecs.iml" filepath="$PROJECT_DIR$/.idea/ecs.iml" /> |  | ||||||
|     </modules> |  | ||||||
|   </component> |  | ||||||
| </project> |  | ||||||
							
								
								
									
										8
									
								
								.idea/sshConfigs.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								.idea/sshConfigs.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,8 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> |  | ||||||
| <project version="4"> |  | ||||||
|   <component name="SshConfigs"> |  | ||||||
|     <configs> |  | ||||||
|       <sshConfig authType="PASSWORD" host="49.234.158.14" id="950b8219-3ab3-47e7-ad76-6b6a5d0857b0" port="22" nameFormat="DESCRIPTIVE" username="root" useOpenSSHConfig="true" /> |  | ||||||
|     </configs> |  | ||||||
|   </component> |  | ||||||
| </project> |  | ||||||
							
								
								
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								.idea/vcs.xml
									
									
									
										generated
									
									
									
								
							| @@ -1,6 +0,0 @@ | |||||||
| <?xml version="1.0" encoding="UTF-8"?> |  | ||||||
| <project version="4"> |  | ||||||
|   <component name="VcsDirectoryMappings"> |  | ||||||
|     <mapping directory="$PROJECT_DIR$" vcs="Git" /> |  | ||||||
|   </component> |  | ||||||
| </project> |  | ||||||
							
								
								
									
										17
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -1,17 +0,0 @@ | |||||||
| # syntax=docker/dockerfile:1 |  | ||||||
|  |  | ||||||
| FROM alpine:latest |  | ||||||
|  |  | ||||||
| # 安装必要的工具 |  | ||||||
| RUN apk add --no-cache wget curl bash |  | ||||||
| RUN apk add --no-cache bind-tools --repository=http://dl-cdn.alpinelinux.org/alpine/edge/main |  | ||||||
| RUN apk add --no-cache grep openssl ca-certificates uuidgen |  | ||||||
| RUN export noninteractive=true |  | ||||||
| # 下载并执行 goecs.sh 脚本 |  | ||||||
| RUN curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && \ |  | ||||||
|     chmod +x goecs.sh && \ |  | ||||||
|     bash goecs.sh env && \ |  | ||||||
|     bash goecs.sh install |  | ||||||
|  |  | ||||||
| # 设置 goecs 为入口点 |  | ||||||
| ENTRYPOINT ["goecs"] |  | ||||||
							
								
								
									
										674
									
								
								LICENSE
									
									
									
									
									
								
							
							
						
						
									
										674
									
								
								LICENSE
									
									
									
									
									
								
							| @@ -1,674 +0,0 @@ | |||||||
|                     GNU GENERAL PUBLIC LICENSE |  | ||||||
|                        Version 3, 29 June 2007 |  | ||||||
|  |  | ||||||
|  Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> |  | ||||||
|  Everyone is permitted to copy and distribute verbatim copies |  | ||||||
|  of this license document, but changing it is not allowed. |  | ||||||
|  |  | ||||||
|                             Preamble |  | ||||||
|  |  | ||||||
|   The GNU General Public License is a free, copyleft license for |  | ||||||
| software and other kinds of works. |  | ||||||
|  |  | ||||||
|   The licenses for most software and other practical works are designed |  | ||||||
| to take away your freedom to share and change the works.  By contrast, |  | ||||||
| the GNU General Public License is intended to guarantee your freedom to |  | ||||||
| share and change all versions of a program--to make sure it remains free |  | ||||||
| software for all its users.  We, the Free Software Foundation, use the |  | ||||||
| GNU General Public License for most of our software; it applies also to |  | ||||||
| any other work released this way by its authors.  You can apply it to |  | ||||||
| your programs, too. |  | ||||||
|  |  | ||||||
|   When we speak of free software, we are referring to freedom, not |  | ||||||
| price.  Our General Public Licenses are designed to make sure that you |  | ||||||
| have the freedom to distribute copies of free software (and charge for |  | ||||||
| them if you wish), that you receive source code or can get it if you |  | ||||||
| want it, that you can change the software or use pieces of it in new |  | ||||||
| free programs, and that you know you can do these things. |  | ||||||
|  |  | ||||||
|   To protect your rights, we need to prevent others from denying you |  | ||||||
| these rights or asking you to surrender the rights.  Therefore, you have |  | ||||||
| certain responsibilities if you distribute copies of the software, or if |  | ||||||
| you modify it: responsibilities to respect the freedom of others. |  | ||||||
|  |  | ||||||
|   For example, if you distribute copies of such a program, whether |  | ||||||
| gratis or for a fee, you must pass on to the recipients the same |  | ||||||
| freedoms that you received.  You must make sure that they, too, receive |  | ||||||
| or can get the source code.  And you must show them these terms so they |  | ||||||
| know their rights. |  | ||||||
|  |  | ||||||
|   Developers that use the GNU GPL protect your rights with two steps: |  | ||||||
| (1) assert copyright on the software, and (2) offer you this License |  | ||||||
| giving you legal permission to copy, distribute and/or modify it. |  | ||||||
|  |  | ||||||
|   For the developers' and authors' protection, the GPL clearly explains |  | ||||||
| that there is no warranty for this free software.  For both users' and |  | ||||||
| authors' sake, the GPL requires that modified versions be marked as |  | ||||||
| changed, so that their problems will not be attributed erroneously to |  | ||||||
| authors of previous versions. |  | ||||||
|  |  | ||||||
|   Some devices are designed to deny users access to install or run |  | ||||||
| modified versions of the software inside them, although the manufacturer |  | ||||||
| can do so.  This is fundamentally incompatible with the aim of |  | ||||||
| protecting users' freedom to change the software.  The systematic |  | ||||||
| pattern of such abuse occurs in the area of products for individuals to |  | ||||||
| use, which is precisely where it is most unacceptable.  Therefore, we |  | ||||||
| have designed this version of the GPL to prohibit the practice for those |  | ||||||
| products.  If such problems arise substantially in other domains, we |  | ||||||
| stand ready to extend this provision to those domains in future versions |  | ||||||
| of the GPL, as needed to protect the freedom of users. |  | ||||||
|  |  | ||||||
|   Finally, every program is threatened constantly by software patents. |  | ||||||
| States should not allow patents to restrict development and use of |  | ||||||
| software on general-purpose computers, but in those that do, we wish to |  | ||||||
| avoid the special danger that patents applied to a free program could |  | ||||||
| make it effectively proprietary.  To prevent this, the GPL assures that |  | ||||||
| patents cannot be used to render the program non-free. |  | ||||||
|  |  | ||||||
|   The precise terms and conditions for copying, distribution and |  | ||||||
| modification follow. |  | ||||||
|  |  | ||||||
|                        TERMS AND CONDITIONS |  | ||||||
|  |  | ||||||
|   0. Definitions. |  | ||||||
|  |  | ||||||
|   "This License" refers to version 3 of the GNU General Public License. |  | ||||||
|  |  | ||||||
|   "Copyright" also means copyright-like laws that apply to other kinds of |  | ||||||
| works, such as semiconductor masks. |  | ||||||
|  |  | ||||||
|   "The Program" refers to any copyrightable work licensed under this |  | ||||||
| License.  Each licensee is addressed as "you".  "Licensees" and |  | ||||||
| "recipients" may be individuals or organizations. |  | ||||||
|  |  | ||||||
|   To "modify" a work means to copy from or adapt all or part of the work |  | ||||||
| in a fashion requiring copyright permission, other than the making of an |  | ||||||
| exact copy.  The resulting work is called a "modified version" of the |  | ||||||
| earlier work or a work "based on" the earlier work. |  | ||||||
|  |  | ||||||
|   A "covered work" means either the unmodified Program or a work based |  | ||||||
| on the Program. |  | ||||||
|  |  | ||||||
|   To "propagate" a work means to do anything with it that, without |  | ||||||
| permission, would make you directly or secondarily liable for |  | ||||||
| infringement under applicable copyright law, except executing it on a |  | ||||||
| computer or modifying a private copy.  Propagation includes copying, |  | ||||||
| distribution (with or without modification), making available to the |  | ||||||
| public, and in some countries other activities as well. |  | ||||||
|  |  | ||||||
|   To "convey" a work means any kind of propagation that enables other |  | ||||||
| parties to make or receive copies.  Mere interaction with a user through |  | ||||||
| a computer network, with no transfer of a copy, is not conveying. |  | ||||||
|  |  | ||||||
|   An interactive user interface displays "Appropriate Legal Notices" |  | ||||||
| to the extent that it includes a convenient and prominently visible |  | ||||||
| feature that (1) displays an appropriate copyright notice, and (2) |  | ||||||
| tells the user that there is no warranty for the work (except to the |  | ||||||
| extent that warranties are provided), that licensees may convey the |  | ||||||
| work under this License, and how to view a copy of this License.  If |  | ||||||
| the interface presents a list of user commands or options, such as a |  | ||||||
| menu, a prominent item in the list meets this criterion. |  | ||||||
|  |  | ||||||
|   1. Source Code. |  | ||||||
|  |  | ||||||
|   The "source code" for a work means the preferred form of the work |  | ||||||
| for making modifications to it.  "Object code" means any non-source |  | ||||||
| form of a work. |  | ||||||
|  |  | ||||||
|   A "Standard Interface" means an interface that either is an official |  | ||||||
| standard defined by a recognized standards body, or, in the case of |  | ||||||
| interfaces specified for a particular programming language, one that |  | ||||||
| is widely used among developers working in that language. |  | ||||||
|  |  | ||||||
|   The "System Libraries" of an executable work include anything, other |  | ||||||
| than the work as a whole, that (a) is included in the normal form of |  | ||||||
| packaging a Major Component, but which is not part of that Major |  | ||||||
| Component, and (b) serves only to enable use of the work with that |  | ||||||
| Major Component, or to implement a Standard Interface for which an |  | ||||||
| implementation is available to the public in source code form.  A |  | ||||||
| "Major Component", in this context, means a major essential component |  | ||||||
| (kernel, window system, and so on) of the specific operating system |  | ||||||
| (if any) on which the executable work runs, or a compiler used to |  | ||||||
| produce the work, or an object code interpreter used to run it. |  | ||||||
|  |  | ||||||
|   The "Corresponding Source" for a work in object code form means all |  | ||||||
| the source code needed to generate, install, and (for an executable |  | ||||||
| work) run the object code and to modify the work, including scripts to |  | ||||||
| control those activities.  However, it does not include the work's |  | ||||||
| System Libraries, or general-purpose tools or generally available free |  | ||||||
| programs which are used unmodified in performing those activities but |  | ||||||
| which are not part of the work.  For example, Corresponding Source |  | ||||||
| includes interface definition files associated with source files for |  | ||||||
| the work, and the source code for shared libraries and dynamically |  | ||||||
| linked subprograms that the work is specifically designed to require, |  | ||||||
| such as by intimate data communication or control flow between those |  | ||||||
| subprograms and other parts of the work. |  | ||||||
|  |  | ||||||
|   The Corresponding Source need not include anything that users |  | ||||||
| can regenerate automatically from other parts of the Corresponding |  | ||||||
| Source. |  | ||||||
|  |  | ||||||
|   The Corresponding Source for a work in source code form is that |  | ||||||
| same work. |  | ||||||
|  |  | ||||||
|   2. Basic Permissions. |  | ||||||
|  |  | ||||||
|   All rights granted under this License are granted for the term of |  | ||||||
| copyright on the Program, and are irrevocable provided the stated |  | ||||||
| conditions are met.  This License explicitly affirms your unlimited |  | ||||||
| permission to run the unmodified Program.  The output from running a |  | ||||||
| covered work is covered by this License only if the output, given its |  | ||||||
| content, constitutes a covered work.  This License acknowledges your |  | ||||||
| rights of fair use or other equivalent, as provided by copyright law. |  | ||||||
|  |  | ||||||
|   You may make, run and propagate covered works that you do not |  | ||||||
| convey, without conditions so long as your license otherwise remains |  | ||||||
| in force.  You may convey covered works to others for the sole purpose |  | ||||||
| of having them make modifications exclusively for you, or provide you |  | ||||||
| with facilities for running those works, provided that you comply with |  | ||||||
| the terms of this License in conveying all material for which you do |  | ||||||
| not control copyright.  Those thus making or running the covered works |  | ||||||
| for you must do so exclusively on your behalf, under your direction |  | ||||||
| and control, on terms that prohibit them from making any copies of |  | ||||||
| your copyrighted material outside their relationship with you. |  | ||||||
|  |  | ||||||
|   Conveying under any other circumstances is permitted solely under |  | ||||||
| the conditions stated below.  Sublicensing is not allowed; section 10 |  | ||||||
| makes it unnecessary. |  | ||||||
|  |  | ||||||
|   3. Protecting Users' Legal Rights From Anti-Circumvention Law. |  | ||||||
|  |  | ||||||
|   No covered work shall be deemed part of an effective technological |  | ||||||
| measure under any applicable law fulfilling obligations under article |  | ||||||
| 11 of the WIPO copyright treaty adopted on 20 December 1996, or |  | ||||||
| similar laws prohibiting or restricting circumvention of such |  | ||||||
| measures. |  | ||||||
|  |  | ||||||
|   When you convey a covered work, you waive any legal power to forbid |  | ||||||
| circumvention of technological measures to the extent such circumvention |  | ||||||
| is effected by exercising rights under this License with respect to |  | ||||||
| the covered work, and you disclaim any intention to limit operation or |  | ||||||
| modification of the work as a means of enforcing, against the work's |  | ||||||
| users, your or third parties' legal rights to forbid circumvention of |  | ||||||
| technological measures. |  | ||||||
|  |  | ||||||
|   4. Conveying Verbatim Copies. |  | ||||||
|  |  | ||||||
|   You may convey verbatim copies of the Program's source code as you |  | ||||||
| receive it, in any medium, provided that you conspicuously and |  | ||||||
| appropriately publish on each copy an appropriate copyright notice; |  | ||||||
| keep intact all notices stating that this License and any |  | ||||||
| non-permissive terms added in accord with section 7 apply to the code; |  | ||||||
| keep intact all notices of the absence of any warranty; and give all |  | ||||||
| recipients a copy of this License along with the Program. |  | ||||||
|  |  | ||||||
|   You may charge any price or no price for each copy that you convey, |  | ||||||
| and you may offer support or warranty protection for a fee. |  | ||||||
|  |  | ||||||
|   5. Conveying Modified Source Versions. |  | ||||||
|  |  | ||||||
|   You may convey a work based on the Program, or the modifications to |  | ||||||
| produce it from the Program, in the form of source code under the |  | ||||||
| terms of section 4, provided that you also meet all of these conditions: |  | ||||||
|  |  | ||||||
|     a) The work must carry prominent notices stating that you modified |  | ||||||
|     it, and giving a relevant date. |  | ||||||
|  |  | ||||||
|     b) The work must carry prominent notices stating that it is |  | ||||||
|     released under this License and any conditions added under section |  | ||||||
|     7.  This requirement modifies the requirement in section 4 to |  | ||||||
|     "keep intact all notices". |  | ||||||
|  |  | ||||||
|     c) You must license the entire work, as a whole, under this |  | ||||||
|     License to anyone who comes into possession of a copy.  This |  | ||||||
|     License will therefore apply, along with any applicable section 7 |  | ||||||
|     additional terms, to the whole of the work, and all its parts, |  | ||||||
|     regardless of how they are packaged.  This License gives no |  | ||||||
|     permission to license the work in any other way, but it does not |  | ||||||
|     invalidate such permission if you have separately received it. |  | ||||||
|  |  | ||||||
|     d) If the work has interactive user interfaces, each must display |  | ||||||
|     Appropriate Legal Notices; however, if the Program has interactive |  | ||||||
|     interfaces that do not display Appropriate Legal Notices, your |  | ||||||
|     work need not make them do so. |  | ||||||
|  |  | ||||||
|   A compilation of a covered work with other separate and independent |  | ||||||
| works, which are not by their nature extensions of the covered work, |  | ||||||
| and which are not combined with it such as to form a larger program, |  | ||||||
| in or on a volume of a storage or distribution medium, is called an |  | ||||||
| "aggregate" if the compilation and its resulting copyright are not |  | ||||||
| used to limit the access or legal rights of the compilation's users |  | ||||||
| beyond what the individual works permit.  Inclusion of a covered work |  | ||||||
| in an aggregate does not cause this License to apply to the other |  | ||||||
| parts of the aggregate. |  | ||||||
|  |  | ||||||
|   6. Conveying Non-Source Forms. |  | ||||||
|  |  | ||||||
|   You may convey a covered work in object code form under the terms |  | ||||||
| of sections 4 and 5, provided that you also convey the |  | ||||||
| machine-readable Corresponding Source under the terms of this License, |  | ||||||
| in one of these ways: |  | ||||||
|  |  | ||||||
|     a) Convey the object code in, or embodied in, a physical product |  | ||||||
|     (including a physical distribution medium), accompanied by the |  | ||||||
|     Corresponding Source fixed on a durable physical medium |  | ||||||
|     customarily used for software interchange. |  | ||||||
|  |  | ||||||
|     b) Convey the object code in, or embodied in, a physical product |  | ||||||
|     (including a physical distribution medium), accompanied by a |  | ||||||
|     written offer, valid for at least three years and valid for as |  | ||||||
|     long as you offer spare parts or customer support for that product |  | ||||||
|     model, to give anyone who possesses the object code either (1) a |  | ||||||
|     copy of the Corresponding Source for all the software in the |  | ||||||
|     product that is covered by this License, on a durable physical |  | ||||||
|     medium customarily used for software interchange, for a price no |  | ||||||
|     more than your reasonable cost of physically performing this |  | ||||||
|     conveying of source, or (2) access to copy the |  | ||||||
|     Corresponding Source from a network server at no charge. |  | ||||||
|  |  | ||||||
|     c) Convey individual copies of the object code with a copy of the |  | ||||||
|     written offer to provide the Corresponding Source.  This |  | ||||||
|     alternative is allowed only occasionally and noncommercially, and |  | ||||||
|     only if you received the object code with such an offer, in accord |  | ||||||
|     with subsection 6b. |  | ||||||
|  |  | ||||||
|     d) Convey the object code by offering access from a designated |  | ||||||
|     place (gratis or for a charge), and offer equivalent access to the |  | ||||||
|     Corresponding Source in the same way through the same place at no |  | ||||||
|     further charge.  You need not require recipients to copy the |  | ||||||
|     Corresponding Source along with the object code.  If the place to |  | ||||||
|     copy the object code is a network server, the Corresponding Source |  | ||||||
|     may be on a different server (operated by you or a third party) |  | ||||||
|     that supports equivalent copying facilities, provided you maintain |  | ||||||
|     clear directions next to the object code saying where to find the |  | ||||||
|     Corresponding Source.  Regardless of what server hosts the |  | ||||||
|     Corresponding Source, you remain obligated to ensure that it is |  | ||||||
|     available for as long as needed to satisfy these requirements. |  | ||||||
|  |  | ||||||
|     e) Convey the object code using peer-to-peer transmission, provided |  | ||||||
|     you inform other peers where the object code and Corresponding |  | ||||||
|     Source of the work are being offered to the general public at no |  | ||||||
|     charge under subsection 6d. |  | ||||||
|  |  | ||||||
|   A separable portion of the object code, whose source code is excluded |  | ||||||
| from the Corresponding Source as a System Library, need not be |  | ||||||
| included in conveying the object code work. |  | ||||||
|  |  | ||||||
|   A "User Product" is either (1) a "consumer product", which means any |  | ||||||
| tangible personal property which is normally used for personal, family, |  | ||||||
| or household purposes, or (2) anything designed or sold for incorporation |  | ||||||
| into a dwelling.  In determining whether a product is a consumer product, |  | ||||||
| doubtful cases shall be resolved in favor of coverage.  For a particular |  | ||||||
| product received by a particular user, "normally used" refers to a |  | ||||||
| typical or common use of that class of product, regardless of the status |  | ||||||
| of the particular user or of the way in which the particular user |  | ||||||
| actually uses, or expects or is expected to use, the product.  A product |  | ||||||
| is a consumer product regardless of whether the product has substantial |  | ||||||
| commercial, industrial or non-consumer uses, unless such uses represent |  | ||||||
| the only significant mode of use of the product. |  | ||||||
|  |  | ||||||
|   "Installation Information" for a User Product means any methods, |  | ||||||
| procedures, authorization keys, or other information required to install |  | ||||||
| and execute modified versions of a covered work in that User Product from |  | ||||||
| a modified version of its Corresponding Source.  The information must |  | ||||||
| suffice to ensure that the continued functioning of the modified object |  | ||||||
| code is in no case prevented or interfered with solely because |  | ||||||
| modification has been made. |  | ||||||
|  |  | ||||||
|   If you convey an object code work under this section in, or with, or |  | ||||||
| specifically for use in, a User Product, and the conveying occurs as |  | ||||||
| part of a transaction in which the right of possession and use of the |  | ||||||
| User Product is transferred to the recipient in perpetuity or for a |  | ||||||
| fixed term (regardless of how the transaction is characterized), the |  | ||||||
| Corresponding Source conveyed under this section must be accompanied |  | ||||||
| by the Installation Information.  But this requirement does not apply |  | ||||||
| if neither you nor any third party retains the ability to install |  | ||||||
| modified object code on the User Product (for example, the work has |  | ||||||
| been installed in ROM). |  | ||||||
|  |  | ||||||
|   The requirement to provide Installation Information does not include a |  | ||||||
| requirement to continue to provide support service, warranty, or updates |  | ||||||
| for a work that has been modified or installed by the recipient, or for |  | ||||||
| the User Product in which it has been modified or installed.  Access to a |  | ||||||
| network may be denied when the modification itself materially and |  | ||||||
| adversely affects the operation of the network or violates the rules and |  | ||||||
| protocols for communication across the network. |  | ||||||
|  |  | ||||||
|   Corresponding Source conveyed, and Installation Information provided, |  | ||||||
| in accord with this section must be in a format that is publicly |  | ||||||
| documented (and with an implementation available to the public in |  | ||||||
| source code form), and must require no special password or key for |  | ||||||
| unpacking, reading or copying. |  | ||||||
|  |  | ||||||
|   7. Additional Terms. |  | ||||||
|  |  | ||||||
|   "Additional permissions" are terms that supplement the terms of this |  | ||||||
| License by making exceptions from one or more of its conditions. |  | ||||||
| Additional permissions that are applicable to the entire Program shall |  | ||||||
| be treated as though they were included in this License, to the extent |  | ||||||
| that they are valid under applicable law.  If additional permissions |  | ||||||
| apply only to part of the Program, that part may be used separately |  | ||||||
| under those permissions, but the entire Program remains governed by |  | ||||||
| this License without regard to the additional permissions. |  | ||||||
|  |  | ||||||
|   When you convey a copy of a covered work, you may at your option |  | ||||||
| remove any additional permissions from that copy, or from any part of |  | ||||||
| it.  (Additional permissions may be written to require their own |  | ||||||
| removal in certain cases when you modify the work.)  You may place |  | ||||||
| additional permissions on material, added by you to a covered work, |  | ||||||
| for which you have or can give appropriate copyright permission. |  | ||||||
|  |  | ||||||
|   Notwithstanding any other provision of this License, for material you |  | ||||||
| add to a covered work, you may (if authorized by the copyright holders of |  | ||||||
| that material) supplement the terms of this License with terms: |  | ||||||
|  |  | ||||||
|     a) Disclaiming warranty or limiting liability differently from the |  | ||||||
|     terms of sections 15 and 16 of this License; or |  | ||||||
|  |  | ||||||
|     b) Requiring preservation of specified reasonable legal notices or |  | ||||||
|     author attributions in that material or in the Appropriate Legal |  | ||||||
|     Notices displayed by works containing it; or |  | ||||||
|  |  | ||||||
|     c) Prohibiting misrepresentation of the origin of that material, or |  | ||||||
|     requiring that modified versions of such material be marked in |  | ||||||
|     reasonable ways as different from the original version; or |  | ||||||
|  |  | ||||||
|     d) Limiting the use for publicity purposes of names of licensors or |  | ||||||
|     authors of the material; or |  | ||||||
|  |  | ||||||
|     e) Declining to grant rights under trademark law for use of some |  | ||||||
|     trade names, trademarks, or service marks; or |  | ||||||
|  |  | ||||||
|     f) Requiring indemnification of licensors and authors of that |  | ||||||
|     material by anyone who conveys the material (or modified versions of |  | ||||||
|     it) with contractual assumptions of liability to the recipient, for |  | ||||||
|     any liability that these contractual assumptions directly impose on |  | ||||||
|     those licensors and authors. |  | ||||||
|  |  | ||||||
|   All other non-permissive additional terms are considered "further |  | ||||||
| restrictions" within the meaning of section 10.  If the Program as you |  | ||||||
| received it, or any part of it, contains a notice stating that it is |  | ||||||
| governed by this License along with a term that is a further |  | ||||||
| restriction, you may remove that term.  If a license document contains |  | ||||||
| a further restriction but permits relicensing or conveying under this |  | ||||||
| License, you may add to a covered work material governed by the terms |  | ||||||
| of that license document, provided that the further restriction does |  | ||||||
| not survive such relicensing or conveying. |  | ||||||
|  |  | ||||||
|   If you add terms to a covered work in accord with this section, you |  | ||||||
| must place, in the relevant source files, a statement of the |  | ||||||
| additional terms that apply to those files, or a notice indicating |  | ||||||
| where to find the applicable terms. |  | ||||||
|  |  | ||||||
|   Additional terms, permissive or non-permissive, may be stated in the |  | ||||||
| form of a separately written license, or stated as exceptions; |  | ||||||
| the above requirements apply either way. |  | ||||||
|  |  | ||||||
|   8. Termination. |  | ||||||
|  |  | ||||||
|   You may not propagate or modify a covered work except as expressly |  | ||||||
| provided under this License.  Any attempt otherwise to propagate or |  | ||||||
| modify it is void, and will automatically terminate your rights under |  | ||||||
| this License (including any patent licenses granted under the third |  | ||||||
| paragraph of section 11). |  | ||||||
|  |  | ||||||
|   However, if you cease all violation of this License, then your |  | ||||||
| license from a particular copyright holder is reinstated (a) |  | ||||||
| provisionally, unless and until the copyright holder explicitly and |  | ||||||
| finally terminates your license, and (b) permanently, if the copyright |  | ||||||
| holder fails to notify you of the violation by some reasonable means |  | ||||||
| prior to 60 days after the cessation. |  | ||||||
|  |  | ||||||
|   Moreover, your license from a particular copyright holder is |  | ||||||
| reinstated permanently if the copyright holder notifies you of the |  | ||||||
| violation by some reasonable means, this is the first time you have |  | ||||||
| received notice of violation of this License (for any work) from that |  | ||||||
| copyright holder, and you cure the violation prior to 30 days after |  | ||||||
| your receipt of the notice. |  | ||||||
|  |  | ||||||
|   Termination of your rights under this section does not terminate the |  | ||||||
| licenses of parties who have received copies or rights from you under |  | ||||||
| this License.  If your rights have been terminated and not permanently |  | ||||||
| reinstated, you do not qualify to receive new licenses for the same |  | ||||||
| material under section 10. |  | ||||||
|  |  | ||||||
|   9. Acceptance Not Required for Having Copies. |  | ||||||
|  |  | ||||||
|   You are not required to accept this License in order to receive or |  | ||||||
| run a copy of the Program.  Ancillary propagation of a covered work |  | ||||||
| occurring solely as a consequence of using peer-to-peer transmission |  | ||||||
| to receive a copy likewise does not require acceptance.  However, |  | ||||||
| nothing other than this License grants you permission to propagate or |  | ||||||
| modify any covered work.  These actions infringe copyright if you do |  | ||||||
| not accept this License.  Therefore, by modifying or propagating a |  | ||||||
| covered work, you indicate your acceptance of this License to do so. |  | ||||||
|  |  | ||||||
|   10. Automatic Licensing of Downstream Recipients. |  | ||||||
|  |  | ||||||
|   Each time you convey a covered work, the recipient automatically |  | ||||||
| receives a license from the original licensors, to run, modify and |  | ||||||
| propagate that work, subject to this License.  You are not responsible |  | ||||||
| for enforcing compliance by third parties with this License. |  | ||||||
|  |  | ||||||
|   An "entity transaction" is a transaction transferring control of an |  | ||||||
| organization, or substantially all assets of one, or subdividing an |  | ||||||
| organization, or merging organizations.  If propagation of a covered |  | ||||||
| work results from an entity transaction, each party to that |  | ||||||
| transaction who receives a copy of the work also receives whatever |  | ||||||
| licenses to the work the party's predecessor in interest had or could |  | ||||||
| give under the previous paragraph, plus a right to possession of the |  | ||||||
| Corresponding Source of the work from the predecessor in interest, if |  | ||||||
| the predecessor has it or can get it with reasonable efforts. |  | ||||||
|  |  | ||||||
|   You may not impose any further restrictions on the exercise of the |  | ||||||
| rights granted or affirmed under this License.  For example, you may |  | ||||||
| not impose a license fee, royalty, or other charge for exercise of |  | ||||||
| rights granted under this License, and you may not initiate litigation |  | ||||||
| (including a cross-claim or counterclaim in a lawsuit) alleging that |  | ||||||
| any patent claim is infringed by making, using, selling, offering for |  | ||||||
| sale, or importing the Program or any portion of it. |  | ||||||
|  |  | ||||||
|   11. Patents. |  | ||||||
|  |  | ||||||
|   A "contributor" is a copyright holder who authorizes use under this |  | ||||||
| License of the Program or a work on which the Program is based.  The |  | ||||||
| work thus licensed is called the contributor's "contributor version". |  | ||||||
|  |  | ||||||
|   A contributor's "essential patent claims" are all patent claims |  | ||||||
| owned or controlled by the contributor, whether already acquired or |  | ||||||
| hereafter acquired, that would be infringed by some manner, permitted |  | ||||||
| by this License, of making, using, or selling its contributor version, |  | ||||||
| but do not include claims that would be infringed only as a |  | ||||||
| consequence of further modification of the contributor version.  For |  | ||||||
| purposes of this definition, "control" includes the right to grant |  | ||||||
| patent sublicenses in a manner consistent with the requirements of |  | ||||||
| this License. |  | ||||||
|  |  | ||||||
|   Each contributor grants you a non-exclusive, worldwide, royalty-free |  | ||||||
| patent license under the contributor's essential patent claims, to |  | ||||||
| make, use, sell, offer for sale, import and otherwise run, modify and |  | ||||||
| propagate the contents of its contributor version. |  | ||||||
|  |  | ||||||
|   In the following three paragraphs, a "patent license" is any express |  | ||||||
| agreement or commitment, however denominated, not to enforce a patent |  | ||||||
| (such as an express permission to practice a patent or covenant not to |  | ||||||
| sue for patent infringement).  To "grant" such a patent license to a |  | ||||||
| party means to make such an agreement or commitment not to enforce a |  | ||||||
| patent against the party. |  | ||||||
|  |  | ||||||
|   If you convey a covered work, knowingly relying on a patent license, |  | ||||||
| and the Corresponding Source of the work is not available for anyone |  | ||||||
| to copy, free of charge and under the terms of this License, through a |  | ||||||
| publicly available network server or other readily accessible means, |  | ||||||
| then you must either (1) cause the Corresponding Source to be so |  | ||||||
| available, or (2) arrange to deprive yourself of the benefit of the |  | ||||||
| patent license for this particular work, or (3) arrange, in a manner |  | ||||||
| consistent with the requirements of this License, to extend the patent |  | ||||||
| license to downstream recipients.  "Knowingly relying" means you have |  | ||||||
| actual knowledge that, but for the patent license, your conveying the |  | ||||||
| covered work in a country, or your recipient's use of the covered work |  | ||||||
| in a country, would infringe one or more identifiable patents in that |  | ||||||
| country that you have reason to believe are valid. |  | ||||||
|  |  | ||||||
|   If, pursuant to or in connection with a single transaction or |  | ||||||
| arrangement, you convey, or propagate by procuring conveyance of, a |  | ||||||
| covered work, and grant a patent license to some of the parties |  | ||||||
| receiving the covered work authorizing them to use, propagate, modify |  | ||||||
| or convey a specific copy of the covered work, then the patent license |  | ||||||
| you grant is automatically extended to all recipients of the covered |  | ||||||
| work and works based on it. |  | ||||||
|  |  | ||||||
|   A patent license is "discriminatory" if it does not include within |  | ||||||
| the scope of its coverage, prohibits the exercise of, or is |  | ||||||
| conditioned on the non-exercise of one or more of the rights that are |  | ||||||
| specifically granted under this License.  You may not convey a covered |  | ||||||
| work if you are a party to an arrangement with a third party that is |  | ||||||
| in the business of distributing software, under which you make payment |  | ||||||
| to the third party based on the extent of your activity of conveying |  | ||||||
| the work, and under which the third party grants, to any of the |  | ||||||
| parties who would receive the covered work from you, a discriminatory |  | ||||||
| patent license (a) in connection with copies of the covered work |  | ||||||
| conveyed by you (or copies made from those copies), or (b) primarily |  | ||||||
| for and in connection with specific products or compilations that |  | ||||||
| contain the covered work, unless you entered into that arrangement, |  | ||||||
| or that patent license was granted, prior to 28 March 2007. |  | ||||||
|  |  | ||||||
|   Nothing in this License shall be construed as excluding or limiting |  | ||||||
| any implied license or other defenses to infringement that may |  | ||||||
| otherwise be available to you under applicable patent law. |  | ||||||
|  |  | ||||||
|   12. No Surrender of Others' Freedom. |  | ||||||
|  |  | ||||||
|   If conditions are imposed on you (whether by court order, agreement or |  | ||||||
| otherwise) that contradict the conditions of this License, they do not |  | ||||||
| excuse you from the conditions of this License.  If you cannot convey a |  | ||||||
| covered work so as to satisfy simultaneously your obligations under this |  | ||||||
| License and any other pertinent obligations, then as a consequence you may |  | ||||||
| not convey it at all.  For example, if you agree to terms that obligate you |  | ||||||
| to collect a royalty for further conveying from those to whom you convey |  | ||||||
| the Program, the only way you could satisfy both those terms and this |  | ||||||
| License would be to refrain entirely from conveying the Program. |  | ||||||
|  |  | ||||||
|   13. Use with the GNU Affero General Public License. |  | ||||||
|  |  | ||||||
|   Notwithstanding any other provision of this License, you have |  | ||||||
| permission to link or combine any covered work with a work licensed |  | ||||||
| under version 3 of the GNU Affero General Public License into a single |  | ||||||
| combined work, and to convey the resulting work.  The terms of this |  | ||||||
| License will continue to apply to the part which is the covered work, |  | ||||||
| but the special requirements of the GNU Affero General Public License, |  | ||||||
| section 13, concerning interaction through a network will apply to the |  | ||||||
| combination as such. |  | ||||||
|  |  | ||||||
|   14. Revised Versions of this License. |  | ||||||
|  |  | ||||||
|   The Free Software Foundation may publish revised and/or new versions of |  | ||||||
| the GNU General Public License from time to time.  Such new versions will |  | ||||||
| be similar in spirit to the present version, but may differ in detail to |  | ||||||
| address new problems or concerns. |  | ||||||
|  |  | ||||||
|   Each version is given a distinguishing version number.  If the |  | ||||||
| Program specifies that a certain numbered version of the GNU General |  | ||||||
| Public License "or any later version" applies to it, you have the |  | ||||||
| option of following the terms and conditions either of that numbered |  | ||||||
| version or of any later version published by the Free Software |  | ||||||
| Foundation.  If the Program does not specify a version number of the |  | ||||||
| GNU General Public License, you may choose any version ever published |  | ||||||
| by the Free Software Foundation. |  | ||||||
|  |  | ||||||
|   If the Program specifies that a proxy can decide which future |  | ||||||
| versions of the GNU General Public License can be used, that proxy's |  | ||||||
| public statement of acceptance of a version permanently authorizes you |  | ||||||
| to choose that version for the Program. |  | ||||||
|  |  | ||||||
|   Later license versions may give you additional or different |  | ||||||
| permissions.  However, no additional obligations are imposed on any |  | ||||||
| author or copyright holder as a result of your choosing to follow a |  | ||||||
| later version. |  | ||||||
|  |  | ||||||
|   15. Disclaimer of Warranty. |  | ||||||
|  |  | ||||||
|   THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY |  | ||||||
| APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT |  | ||||||
| HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY |  | ||||||
| OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, |  | ||||||
| THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |  | ||||||
| PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM |  | ||||||
| IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF |  | ||||||
| ALL NECESSARY SERVICING, REPAIR OR CORRECTION. |  | ||||||
|  |  | ||||||
|   16. Limitation of Liability. |  | ||||||
|  |  | ||||||
|   IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |  | ||||||
| WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS |  | ||||||
| THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY |  | ||||||
| GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE |  | ||||||
| USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF |  | ||||||
| DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD |  | ||||||
| PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), |  | ||||||
| EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF |  | ||||||
| SUCH DAMAGES. |  | ||||||
|  |  | ||||||
|   17. Interpretation of Sections 15 and 16. |  | ||||||
|  |  | ||||||
|   If the disclaimer of warranty and limitation of liability provided |  | ||||||
| above cannot be given local legal effect according to their terms, |  | ||||||
| reviewing courts shall apply local law that most closely approximates |  | ||||||
| an absolute waiver of all civil liability in connection with the |  | ||||||
| Program, unless a warranty or assumption of liability accompanies a |  | ||||||
| copy of the Program in return for a fee. |  | ||||||
|  |  | ||||||
|                      END OF TERMS AND CONDITIONS |  | ||||||
|  |  | ||||||
|             How to Apply These Terms to Your New Programs |  | ||||||
|  |  | ||||||
|   If you develop a new program, and you want it to be of the greatest |  | ||||||
| possible use to the public, the best way to achieve this is to make it |  | ||||||
| free software which everyone can redistribute and change under these terms. |  | ||||||
|  |  | ||||||
|   To do so, attach the following notices to the program.  It is safest |  | ||||||
| to attach them to the start of each source file to most effectively |  | ||||||
| state the exclusion of warranty; and each file should have at least |  | ||||||
| the "copyright" line and a pointer to where the full notice is found. |  | ||||||
|  |  | ||||||
|     <one line to give the program's name and a brief idea of what it does.> |  | ||||||
|     Copyright (C) <year>  <name of author> |  | ||||||
|  |  | ||||||
|     This program is free software: you can redistribute it and/or modify |  | ||||||
|     it under the terms of the GNU General Public License as published by |  | ||||||
|     the Free Software Foundation, either version 3 of the License, or |  | ||||||
|     (at your option) any later version. |  | ||||||
|  |  | ||||||
|     This program is distributed in the hope that it will be useful, |  | ||||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of |  | ||||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the |  | ||||||
|     GNU General Public License for more details. |  | ||||||
|  |  | ||||||
|     You should have received a copy of the GNU General Public License |  | ||||||
|     along with this program.  If not, see <https://www.gnu.org/licenses/>. |  | ||||||
|  |  | ||||||
| Also add information on how to contact you by electronic and paper mail. |  | ||||||
|  |  | ||||||
|   If the program does terminal interaction, make it output a short |  | ||||||
| notice like this when it starts in an interactive mode: |  | ||||||
|  |  | ||||||
|     <program>  Copyright (C) <year>  <name of author> |  | ||||||
|     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |  | ||||||
|     This is free software, and you are welcome to redistribute it |  | ||||||
|     under certain conditions; type `show c' for details. |  | ||||||
|  |  | ||||||
| The hypothetical commands `show w' and `show c' should show the appropriate |  | ||||||
| parts of the General Public License.  Of course, your program's commands |  | ||||||
| might be different; for a GUI interface, you would use an "about box". |  | ||||||
|  |  | ||||||
|   You should also get your employer (if you work as a programmer) or school, |  | ||||||
| if any, to sign a "copyright disclaimer" for the program, if necessary. |  | ||||||
| For more information on this, and how to apply and follow the GNU GPL, see |  | ||||||
| <https://www.gnu.org/licenses/>. |  | ||||||
|  |  | ||||||
|   The GNU General Public License does not permit incorporating your program |  | ||||||
| into proprietary programs.  If your program is a subroutine library, you |  | ||||||
| may consider it more useful to permit linking proprietary applications with |  | ||||||
| the library.  If this is what you want to do, use the GNU Lesser General |  | ||||||
| Public License instead of this License.  But first, please read |  | ||||||
| <https://www.gnu.org/licenses/why-not-lgpl.html>. |  | ||||||
							
								
								
									
										307
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										307
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,292 +1,39 @@ | |||||||
| # ECS | # CPU性能测试排行榜 | ||||||
|  |  | ||||||
| [](https://github.com/oneclickvirt/ecs/actions/workflows/main.yaml) [](https://www.spiritlhl.net/) | 本目录包含CPU性能测试的结果和排行榜数据。 | ||||||
|  |  | ||||||
| 融合怪测评项目 - GO 重构版本   | ## 文件说明 | ||||||
|  |  | ||||||
| 如有问题请 [issues](https://github.com/oneclickvirt/ecs/issues) 反馈。 | ### 主要文件 | ||||||
|  | - `index.html`: 排行榜网页展示页面 | ||||||
|  | - `all_cpu_results.json`: 包含所有CPU测试结果的完整数据 | ||||||
|  | - `CNAME`: GitHub Pages自定义域名配置文件 | ||||||
|  |  | ||||||
| Go 版本:[https://github.com/oneclickvirt/ecs](https://github.com/oneclickvirt/ecs) | ### CSV文件夹 (csvs/) | ||||||
|  | - `all_cpus_single_core_ranking.csv`: 所有CPU单核性能排行榜 | ||||||
|  | - `all_cpus_multi_core_ranking.csv`: 所有CPU多核性能排行榜 | ||||||
|  | - `{CPU型号}_single_core.csv`: 特定CPU型号的单核性能排行 | ||||||
|  | - `{CPU型号}_multi_core.csv`: 特定CPU型号的多核性能排行 | ||||||
|  |  | ||||||
| Shell 版本:[https://github.com/spiritLHLS/ecs](https://github.com/spiritLHLS/ecs) | ## 在线查看 | ||||||
|  |  | ||||||
| --- | 可通过 https://sysbench.spiritlhl.net 访问。 | ||||||
|  |  | ||||||
| ## **语言** | ## 数据字段说明 | ||||||
|  |  | ||||||
| [中文文档](README.md) | [English Docs](README_EN.md) | | 字段名 | 说明 | | ||||||
|  | |--------|------| | ||||||
|  | | 排名 | 在当前排序中的排名 | | ||||||
|  | | CPU型号 | 完整的CPU型号信息 | | ||||||
|  | | CPU核心数 | CPU的核心数量 | | ||||||
|  | | 单核得分 | 单核性能测试得分 | | ||||||
|  | | 多核得分 | 多核性能测试得分 | | ||||||
|  | | 多核线程数 | 多核测试使用的线程数 | | ||||||
|  |  | ||||||
| --- | ## 更新时间 | ||||||
|  |  | ||||||
| ## **适配系统和架构** | 最后更新时间: 2025-10-31 01:38:10 UTC | ||||||
|  |  | ||||||
| ### **编译支持的架构** | ## 数据来源 | ||||||
|  |  | ||||||
| - amd64、arm、arm64、386、mips、mipsle、s390x、riscv64 |  | ||||||
|  |  | ||||||
| ### **测试支持的架构** |  | ||||||
|  |  | ||||||
| - amd64、arm64 |  | ||||||
|  |  | ||||||
| > 更多架构请自行测试。 |  | ||||||
|  |  | ||||||
| ### **编译支持的系统** |  | ||||||
|  |  | ||||||
| - Linux、Windows、MacOS、FreeBSD、OpenBSD |  | ||||||
|  |  | ||||||
| ### **测试支持的系统** |  | ||||||
|  |  | ||||||
| - Linux、Windows |  | ||||||
|  |  | ||||||
| > 更多系统请自行测试。 |  | ||||||
|  |  | ||||||
| ### **待支持的系统** |  | ||||||
|  |  | ||||||
| - MacOS、FreeBSD、OpenBSD(存在硬件测试 BUG 未修复) |  | ||||||
|  |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| ## **功能** |  | ||||||
|  |  | ||||||
| - 系统基础信息查询:   |  | ||||||
|   自研 [basics](https://github.com/oneclickvirt/basics)、[gostun](https://github.com/oneclickvirt/gostun) |  | ||||||
| - IP 基础信息并发查询:   |  | ||||||
|   自研 [basics](https://github.com/oneclickvirt/basics) |  | ||||||
| - CPU 测试:   |  | ||||||
|   自研 [cputest](https://github.com/oneclickvirt/cputest),支持 sysbench、geekbench、winsat |  | ||||||
| - 内存测试:   |  | ||||||
|   自研 [memorytest](https://github.com/oneclickvirt/memorytest),支持 sysbench、dd |  | ||||||
| - 硬盘测试:   |  | ||||||
|   自研 [disktest](https://github.com/oneclickvirt/disktest),支持 dd、fio、winsat |  | ||||||
| - 流媒体解锁信息并发查询:   |  | ||||||
|   借鉴 [netflix-verify](https://github.com/sjlleo/netflix-verify) 等逻辑,开发至 [CommonMediaTests](https://github.com/oneclickvirt/CommonMediaTests) |  | ||||||
| - 常见流媒体测试并发查询:   |  | ||||||
|   自研至 [UnlockTests](https://github.com/oneclickvirt/UnlockTests),逻辑借鉴 [RegionRestrictionCheck](https://github.com/lmc999/RegionRestrictionCheck) 等 |  | ||||||
| - IP 质量/安全信息并发查询:   |  | ||||||
|   自研,二进制文件编译至 [securityCheck](https://github.com/oneclickvirt/securityCheck) |  | ||||||
| - 邮件端口测试:   |  | ||||||
|   自研 [portchecker](https://github.com/oneclickvirt/portchecker) |  | ||||||
| - 三网回程测试:   |  | ||||||
|   借鉴 [zhanghanyun/backtrace](https://github.com/zhanghanyun/backtrace),二次开发至 [oneclickvirt/backtrace](https://github.com/oneclickvirt/backtrace) |  | ||||||
| - 三网路由测试:   |  | ||||||
|   借鉴 [NTrace-core](https://github.com/nxtrace/NTrace-core),二次开发至 [nt3](https://github.com/oneclickvirt/nt3) |  | ||||||
| - 网速测试:   |  | ||||||
|   基于 [speedtest.net](https://github.com/spiritLHLS/speedtest.net-CN-ID) 和 [speedtest.cn](https://github.com/spiritLHLS/speedtest.cn-CN-ID) 数据,开发至 [oneclickvirt/speedtest](https://github.com/oneclickvirt/speedtest) |  | ||||||
| - 三网 Ping 值测试:   |  | ||||||
|   借鉴 [ecsspeed](https://github.com/spiritLHLS/ecsspeed),二次开发至 [pingtest](https://github.com/oneclickvirt/pingtest) |  | ||||||
|  |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| ## **使用说明** |  | ||||||
|  |  | ||||||
| ### **Linux/FreeBSD/MacOS** |  | ||||||
|  |  | ||||||
| #### **一键命令** |  | ||||||
|  |  | ||||||
| - **国际用户无加速:** |  | ||||||
|  |  | ||||||
|   ```bash |  | ||||||
|   export noninteractive=true && curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh && bash goecs.sh env && bash goecs.sh install && goecs |  | ||||||
|   ``` |  | ||||||
|  |  | ||||||
| - **国际/国内使用 CDN 加速:** |  | ||||||
|  |  | ||||||
|   ```bash |  | ||||||
|   export noninteractive=true && curl -L https://cdn.spiritlhl.net/https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh && bash goecs.sh env && bash goecs.sh install && goecs |  | ||||||
|   ``` |  | ||||||
|  |  | ||||||
| - **国内用户使用 CNB 加速:** |  | ||||||
|  |  | ||||||
|   ```bash |  | ||||||
|   export noninteractive=true && curl -L https://cnb.cool/oneclickvirt/ecs/-/git/raw/main/goecs.sh -o goecs.sh && chmod +x goecs.sh && bash goecs.sh env && bash goecs.sh install && goecs |  | ||||||
|   ``` |  | ||||||
|  |  | ||||||
| #### **详细说明** |  | ||||||
|  |  | ||||||
| 1. **下载脚本** |  | ||||||
|  |  | ||||||
|    **国际用户无加速:** |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
|    **国际/国内使用 CDN 加速:** |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    curl -L https://cdn.spiritlhl.net/https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
|    **国内用户使用 CNB 加速:** |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    curl -L https://cnb.cool/oneclickvirt/ecs/-/git/raw/main/goecs.sh -o goecs.sh && chmod +x goecs.sh |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 2. **更新包管理器(可选择)并安装环境** |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    ./goecs.sh env |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
|    **非互动模式:** |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    export noninteractive=true && ./goecs.sh env |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 3. **安装 `goecs`** |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    ./goecs.sh install |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 4. **升级 `goecs`** |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    ./goecs.sh upgrade |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 5. **卸载 `goecs`** |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    ./goecs.sh uninstall |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| 6. **唤起菜单** |  | ||||||
|  |  | ||||||
|    ```bash |  | ||||||
|    goecs |  | ||||||
|    ``` |  | ||||||
|  |  | ||||||
| #### **命令参数化** |  | ||||||
|  |  | ||||||
| ```bash |  | ||||||
| Usage: goecs [options] |  | ||||||
|   -backtrace |  | ||||||
|         Enable/Disable backtrace test (in 'en' language or on windows it always false) (default true) |  | ||||||
|   -basic |  | ||||||
|         Enable/Disable basic test (default true) |  | ||||||
|   -comm |  | ||||||
|         Enable/Disable common media test (default true) |  | ||||||
|   -cpu |  | ||||||
|         Enable/Disable CPU test (default true) |  | ||||||
|   -cpum string |  | ||||||
|         Set CPU test method (supported: sysbench, geekbench, winsat) (default "sysbench") |  | ||||||
|   -cput string |  | ||||||
|         Set CPU test thread mode (supported: single, multi) (default "multi") |  | ||||||
|   -disk |  | ||||||
|         Enable/Disable disk test (default true) |  | ||||||
|   -diskm string |  | ||||||
|         Set disk test method (supported: fio, dd, winsat) (default "fio") |  | ||||||
|   -diskmc |  | ||||||
|         Enable/Disable multiple disk checks, e.g., -diskmc=false |  | ||||||
|   -diskp string |  | ||||||
|         Set disk test path, e.g., -diskp /root |  | ||||||
|   -email |  | ||||||
|         Enable/Disable email port test (default true) |  | ||||||
|   -h    Show help information |  | ||||||
|   -l string |  | ||||||
|         Set language (supported: en, zh) (default "zh") |  | ||||||
|   -log |  | ||||||
|         Enable/Disable logging in the current path |  | ||||||
|   -memory |  | ||||||
|         Enable/Disable memory test (default true) |  | ||||||
|   -memorym string |  | ||||||
|         Set memory test method (supported: sysbench, dd, winsat) (default "sysbench") |  | ||||||
|   -menu |  | ||||||
|         Enable/Disable menu mode, disable example: -menu=false (default true) |  | ||||||
|   -nt3 |  | ||||||
|         Enable/Disable NT3 test (in 'en' language or on windows it always false) (default true) |  | ||||||
|   -nt3loc string |  | ||||||
|         Specify NT3 test location (supported: GZ, SH, BJ, CD for Guangzhou, Shanghai, Beijing, Chengdu) (default "GZ") |  | ||||||
|   -nt3t string |  | ||||||
|         Set NT3 test type (supported: both, ipv4, ipv6) (default "ipv4") |  | ||||||
|   -security |  | ||||||
|         Enable/Disable security test (default true) |  | ||||||
|   -speed |  | ||||||
|         Enable/Disable speed test (default true) |  | ||||||
|   -spnum int |  | ||||||
|         Set the number of servers per operator for speed test (default 2) |  | ||||||
|   -upload |  | ||||||
|         Enable/Disable upload the result (default true) |  | ||||||
|   -ut |  | ||||||
|         Enable/Disable unlock media test (default true) |  | ||||||
|   -v    Display version information |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| ### **Windows** |  | ||||||
|  |  | ||||||
| 1. 下载带 exe 文件的压缩包:[Releases](https://github.com/oneclickvirt/ecs/releases) |  | ||||||
| 2. 解压后,右键以管理员模式运行。 |  | ||||||
|  |  | ||||||
| --- |  | ||||||
|  |  | ||||||
| ### **Docker** |  | ||||||
|  |  | ||||||
| 国际镜像地址:https://hub.docker.com/r/spiritlhl/goecs |  | ||||||
|  |  | ||||||
| 请确保执行下述命令前本机已安装Docker |  | ||||||
|  |  | ||||||
| 特权模式+host网络 |  | ||||||
|  |  | ||||||
| ```shell |  | ||||||
| docker run --rm --privileged --network host spiritlhl/goecs:latest -menu=false -l zh |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 非特权模式+非host网络 |  | ||||||
|  |  | ||||||
| ```shell |  | ||||||
| docker run --rm spiritlhl/goecs:latest -menu=false -l zh |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 使用Docker执行测试,硬件测试会有一些偏差和虚拟化架构判断失效,还是推荐直接测试而不使用Docker测试。 |  | ||||||
|  |  | ||||||
| 国内镜像地址:https://cnb.cool/oneclickvirt/ecs/-/packages/docker/ecs |  | ||||||
|  |  | ||||||
| 请确保执行下述命令前本机已安装Docker |  | ||||||
|  |  | ||||||
| 特权模式+host网络 |  | ||||||
|  |  | ||||||
| ```shell |  | ||||||
| docker run --rm --privileged --network host docker.cnb.cool/oneclickvirt/ecs:latest -menu=false -l zh |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 非特权模式+非host网络 |  | ||||||
|  |  | ||||||
| ```shell |  | ||||||
| docker run --rm docker.cnb.cool/oneclickvirt/ecs:latest -menu=false -l zh |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## QA |  | ||||||
|  |  | ||||||
| #### Q: 为什么默认使用sysbench而不是geekbench |  | ||||||
|  |  | ||||||
| #### A: 比较二者特点 |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| sysbench                          geekbench |  | ||||||
| 轻量几乎所有服务器都能跑             重型小机器跑不动 |  | ||||||
| 测试无联网需求,无硬件需求           测试必须联网,且必须IPV4环境并有内存大小1G的最低需求 |  | ||||||
| LUA编写且开源,各架构系统可自行编译   仅官方二进制文件且不开源,无对应架构时无法自行编译 |  | ||||||
| 核心测试组件十多年不变               每次大版本更新对标的CPU,不同版本间得分互相之间难转化,你只能以对标的CPU为准 |  | ||||||
| 测试仅测试计算性能                  测试涵盖多种性能测试,得分以权重计算,但实际很多测试项目实际是用不到的 |  | ||||||
| 适合快速测试                       适合全面测试 |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| 且```goecs```测试使用何种CPU测试方式可使用参数指定,默认只是为了更多用户快速测试的需求 |  | ||||||
|  |  | ||||||
| #### Q: 为什么使用Golang而不是Rust重构 |  | ||||||
|  |  | ||||||
| #### A: 因为网络相关的项目目前以Golang语言为趋势,大多组件有开源生态维护,Rust很多得自己手搓,~~我懒得搞~~我没那个技术力 |  | ||||||
|  |  | ||||||
| #### Q: 为什么不继续开发Shell版本而是选择重构 |  | ||||||
|  |  | ||||||
| #### A: 因为太多千奇百怪的环境问题了,还是提前编译好测试的二进制文件比较容易解决环境问题(泛化性更好) |  | ||||||
|  |  | ||||||
| #### Q: 每个测试项目的说明有吗? |  | ||||||
|  |  | ||||||
| #### A: 每个测试项目有对应的维护仓库,自行点击查看仓库说明 |  | ||||||
|  |  | ||||||
|  | 数据来源于用户提交的CPU性能测试结果,经过自动化脚本处理和排序生成。 | ||||||
|   | |||||||
							
								
								
									
										224
									
								
								README_EN.md
									
									
									
									
									
								
							
							
						
						
									
										224
									
								
								README_EN.md
									
									
									
									
									
								
							| @@ -1,224 +0,0 @@ | |||||||
| # ecs |  | ||||||
|  |  | ||||||
| [](https://github.com/oneclickvirt/ecs/actions/workflows/main.yaml) [](https://www.spiritlhl.net/) |  | ||||||
|  |  | ||||||
| Fusion Monster Evaluation Script - GO Refactored Version |  | ||||||
|  |  | ||||||
| Please report any issues via issues. |  | ||||||
|  |  | ||||||
| Go version:[https://github.com/oneclickvirt/ecs](https://github.com/oneclickvirt/ecs) |  | ||||||
|  |  | ||||||
| Shell version: [https://github.com/spiritLHLS/ecs/blob/main/README_EN.md](https://github.com/spiritLHLS/ecs/blob/main/README_EN.md) |  | ||||||
|  |  | ||||||
| ## Language |  | ||||||
|  |  | ||||||
| [中文文档](README.md) | [English Docs](README_EN.md) |  | ||||||
|  |  | ||||||
| ## Supported Systems and Architectures |  | ||||||
|  |  | ||||||
| Architectures supported for compilation: amd64、arm、arm64、386、mips、mipsle、s390x、riscv64 |  | ||||||
|  |  | ||||||
| Tested architectures: amd64, arm64 |  | ||||||
|  |  | ||||||
| More architectures please test by yourself |  | ||||||
|  |  | ||||||
| Compilation support: Linux, Windows、MacOS、FreeBSD、OpenBSD |  | ||||||
|  |  | ||||||
| Tested on: Linux, Windows |  | ||||||
|  |  | ||||||
| More systems to be tested |  | ||||||
|  |  | ||||||
| Systems to be supported (hardware testing bugs not yet fixed): MacOS、FreeBSD、OpenBSD |  | ||||||
|  |  | ||||||
| ## Features |  | ||||||
|  |  | ||||||
| - [x] System basic information query [Self-developed [basics](https://github.com/oneclickvirt/basics), [gostun](https://github.com/oneclickvirt/gostun)] |  | ||||||
| - [x] Concurrent IP basic information query [Self-developed [basics](https://github.com/oneclickvirt/basics)] |  | ||||||
| - [x] CPU test [Self-developed [cputest](https://github.com/oneclickvirt/cputest) supporting sysbench, geekbench, winsat] |  | ||||||
| - [x] Memory test [Self-developed [memorytest](https://github.com/oneclickvirt/memorytest) supporting sysbench, dd] |  | ||||||
| - [x] Disk test [Self-developed [disktest](https://github.com/oneclickvirt/disktest) supporting dd, fio, winsat] |  | ||||||
| - [x] Concurrent streaming media unlock information query for three major platforms [Modified from [netflix-verify](https://github.com/sjlleo/netflix-verify), [VerifyDisneyPlus](https://github.com/sjlleo/VerifyDisneyPlus), [TubeCheck](https://github.com/sjlleo/TubeCheck) to [CommonMediaTests](https://github.com/oneclickvirt/CommonMediaTests)] |  | ||||||
| - [x] Concurrent common streaming media tests [Self-developed code, logic modified from [RegionRestrictionCheck](https://github.com/lmc999/RegionRestrictionCheck), [MediaUnlockTest](https://github.com/HsukqiLee/MediaUnlockTest) to [UnlockTests](https://github.com/oneclickvirt/UnlockTests)] |  | ||||||
| - [x] Concurrent IP quality/security information query [Self-developed, due to testing with key information, privately developed, but binary files compiled in [securityCheck](https://github.com/oneclickvirt/securityCheck)] |  | ||||||
| - [x] Email port test [Self-developed [portchecker](https://github.com/oneclickvirt/portchecker)] |  | ||||||
| - [x] Three-network return path test [Modified from [zhanghanyun/backtrace](https://github.com/zhanghanyun/backtrace) to [oneclickvirt/backtrace](https://github.com/oneclickvirt/backtrace)] |  | ||||||
| - [x] Three-network route test [Modified from [NTrace-core](https://github.com/nxtrace/NTrace-core) to [nt3](https://github.com/oneclickvirt/nt3)] |  | ||||||
| - [x] Speed test [Based on data from [speedtest.net-crawler](https://github.com/spiritLHLS/speedtest.net-CN-ID), [speedtest.cn-crawler](https://github.com/spiritLHLS/speedtest.cn-CN-ID), modified from [speedtest-go](https://github.com/showwin/speedtest-go) to [oneclickvirt/speedtest](https://github.com/oneclickvirt/speedtest)] |  | ||||||
| - [x] Three-network Ping test [Modified from [ecsspeed](https://github.com/spiritLHLS/ecsspeed) logic to [pingtest](https://github.com/oneclickvirt/pingtest)] |  | ||||||
|  |  | ||||||
| ## Instructions for Use on Linux/FreeBSD/MacOS |  | ||||||
|  |  | ||||||
| ### one-click command |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| export noninteractive=true && curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh && bash goecs.sh env && bash goecs.sh install && goecs -l en |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ### explain in detail |  | ||||||
|  |  | ||||||
| Download the script |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Install environment |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| ./goecs.sh env |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| If you don't want interaction, use ```export noninteractive=true``` and then execute the ```env``` command |  | ||||||
|  |  | ||||||
| Install goecs |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| ./goecs.sh install |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Upgrade goecs |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| ./goecs.sh upgrade |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Uninstall goecs |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| ./goecs.sh uninstall |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Explanation of the shell script |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| Available commands: |  | ||||||
|  |  | ||||||
| ./goecs.sh env             Check and Install dependencies |  | ||||||
|                            Warning: This command performs system update(optional), which may: |  | ||||||
|                            1. Take considerable time |  | ||||||
|                            2. Cause temporary network interruptions |  | ||||||
|                            3. Impact system stability |  | ||||||
|                            4. Affect subsequent system startups |  | ||||||
|                            For systems with less than 1GB RAM, additional risks: |  | ||||||
|                            1. System freeze |  | ||||||
|                            2. SSH connection loss |  | ||||||
|                            3. Critical service failures |  | ||||||
|                            Recommended: |  | ||||||
|                            Hanging execution during environment dependency installation |  | ||||||
|                             |  | ||||||
|                            Required components: |  | ||||||
|                            sysbench/geekbench (Required for CPU testing) |  | ||||||
|                             |  | ||||||
|                            Optional components: |  | ||||||
|                            sudo, tar, unzip, dd, fio |  | ||||||
|                            speedtest (Network testing) |  | ||||||
|                            ping (Network connectivity) |  | ||||||
|                            systemd-detect-virt/dmidecode (System info detection) |  | ||||||
|  |  | ||||||
| ./goecs.sh install         Install goecs command |  | ||||||
| ./goecs.sh upgrade         Upgrade goecs command |  | ||||||
| ./goecs.sh uninstall       Uninstall goecs command |  | ||||||
| ./goecs.sh help            Show this message |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Invoke the goecs menu |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| goecs -l en |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| or |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| ./goecs -l en |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Parameterized goecs command |  | ||||||
|  |  | ||||||
| ``` |  | ||||||
| Usage: goecs [options] |  | ||||||
|   -backtrace |  | ||||||
|         Enable/Disable backtrace test (in 'en' language or on windows it always false) (default true) |  | ||||||
|   -basic |  | ||||||
|         Enable/Disable basic test (default true) |  | ||||||
|   -comm |  | ||||||
|         Enable/Disable common media test (default true) |  | ||||||
|   -cpu |  | ||||||
|         Enable/Disable CPU test (default true) |  | ||||||
|   -cpum string |  | ||||||
|         Set CPU test method (supported: sysbench, geekbench, winsat) (default "sysbench") |  | ||||||
|   -cput string |  | ||||||
|         Set CPU test thread mode (supported: single, multi) (default "multi") |  | ||||||
|   -disk |  | ||||||
|         Enable/Disable disk test (default true) |  | ||||||
|   -diskm string |  | ||||||
|         Set disk test method (supported: fio, dd, winsat) (default "fio") |  | ||||||
|   -diskmc |  | ||||||
|         Enable/Disable multiple disk checks, e.g., -diskmc=false |  | ||||||
|   -diskp string |  | ||||||
|         Set disk test path, e.g., -diskp /root |  | ||||||
|   -email |  | ||||||
|         Enable/Disable email port test (default true) |  | ||||||
|   -h    Show help information |  | ||||||
|   -l string |  | ||||||
|         Set language (supported: en, zh) (default "zh") |  | ||||||
|   -log |  | ||||||
|         Enable/Disable logging in the current path |  | ||||||
|   -memory |  | ||||||
|         Enable/Disable memory test (default true) |  | ||||||
|   -memorym string |  | ||||||
|         Set memory test method (supported: sysbench, dd, winsat) (default "sysbench") |  | ||||||
|   -menu |  | ||||||
|         Enable/Disable menu mode, disable example: -menu=false (default true) |  | ||||||
|   -nt3 |  | ||||||
|         Enable/Disable NT3 test (in 'en' language or on windows it always false) (default true) |  | ||||||
|   -nt3loc string |  | ||||||
|         Specify NT3 test location (supported: GZ, SH, BJ, CD for Guangzhou, Shanghai, Beijing, Chengdu) (default "GZ") |  | ||||||
|   -nt3t string |  | ||||||
|         Set NT3 test type (supported: both, ipv4, ipv6) (default "ipv4") |  | ||||||
|   -security |  | ||||||
|         Enable/Disable security test (default true) |  | ||||||
|   -speed |  | ||||||
|         Enable/Disable speed test (default true) |  | ||||||
|   -spnum int |  | ||||||
|         Set the number of servers per operator for speed test (default 2) |  | ||||||
|   -upload |  | ||||||
|         Enable/Disable upload the result (default true) |  | ||||||
|   -ut |  | ||||||
|         Enable/Disable unlock media test (default true) |  | ||||||
|   -v    Display version information |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| ## Instructions for Use on Windows |  | ||||||
|  |  | ||||||
| Download the compressed file with the exe file |  | ||||||
|  |  | ||||||
| https://github.com/oneclickvirt/ecs/releases |  | ||||||
|  |  | ||||||
| Find the latest version, download the .zip file corresponding to your architecture, and unzip it to get an exe file. |  | ||||||
|  |  | ||||||
| Right-click the exe file and select Run as administrator (running without administrator mode will not allow hardware testing), and invoke the menu to choose. |  | ||||||
|  |  | ||||||
| No environment installation is required for Windows testing. |  | ||||||
|  |  | ||||||
| ## Instructions for Use in Docker |  | ||||||
|  |  | ||||||
| Link: https://hub.docker.com/r/spiritlhl/goecs |  | ||||||
|  |  | ||||||
| Please make sure that Docker is installed on your machine before executing the following commands |  | ||||||
|  |  | ||||||
| Privileged Mode + host network |  | ||||||
|  |  | ||||||
| ```shell |  | ||||||
| docker run --rm --privileged --network host spiritlhl/goecs:latest -menu=false -l en |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Unprivileged mode + non-host network |  | ||||||
|  |  | ||||||
| ```shell |  | ||||||
| docker run --rm spiritlhl/goecs:latest |  | ||||||
| ``` |  | ||||||
|  |  | ||||||
| Using Docker to execute tests, hardware testing will have some bias and virtualization architecture to determine the failure. |  | ||||||
|  |  | ||||||
| Recommended direct testing without using Docker testing. |  | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| package backtrace |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/oneclickvirt/backtrace/bk" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func BackTrace() { |  | ||||||
| 	backtrace.BackTrace() |  | ||||||
| } |  | ||||||
| @@ -1,20 +0,0 @@ | |||||||
| package backtrace |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| //func TestGeneratePrefixMap(t *testing.T) { |  | ||||||
| //	prefix := "223.119.8.0/21" |  | ||||||
| //	prefixList := GeneratePrefixList(prefix) |  | ||||||
| //	if prefixList != nil { |  | ||||||
| //		// 打印生成的IP地址前缀列表 |  | ||||||
| //		for _, ip := range prefixList { |  | ||||||
| //			fmt.Println(ip) |  | ||||||
| //		} |  | ||||||
| //	} |  | ||||||
| //} |  | ||||||
|  |  | ||||||
| func TestBackTrace(t *testing.T) { |  | ||||||
| 	BackTrace() |  | ||||||
| } |  | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| package basic1 |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func Test_basic(t *testing.T) { |  | ||||||
| 	Basic("zh") |  | ||||||
| } |  | ||||||
| @@ -1,16 +0,0 @@ | |||||||
| package basic1 |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"github.com/oneclickvirt/basics/network" |  | ||||||
| 	"github.com/oneclickvirt/basics/system" |  | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // 本包不在main中使用,仅做测试使用,真正调用的在 utils 中的 BasicsAndSecurityCheck |  | ||||||
| func Basic(language string) { |  | ||||||
| 	ipInfo, _, _ := network.NetworkCheck("both", false, language) |  | ||||||
| 	systemInfo := system.CheckSystemInfo(language) |  | ||||||
| 	basicInfo := strings.ReplaceAll(systemInfo+ipInfo, "\n\n", "\n") |  | ||||||
| 	fmt.Printf(basicInfo) |  | ||||||
| } |  | ||||||
| @@ -1,11 +0,0 @@ | |||||||
| package commediatest |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"github.com/oneclickvirt/CommonMediaTests/commediatests" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func ComMediaTest(language string) { |  | ||||||
| 	res := commediatests.MediaTests(language) |  | ||||||
| 	fmt.Printf(res) |  | ||||||
| } |  | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| package commediatest |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestMedia(t *testing.T) { |  | ||||||
| 	ComMediaTest("zh") |  | ||||||
| } |  | ||||||
							
								
								
									
										19454
									
								
								cpu_statistics.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19454
									
								
								cpu_statistics.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,39 +0,0 @@ | |||||||
| package cputest |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"github.com/oneclickvirt/cputest/cpu" |  | ||||||
| 	"runtime" |  | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func CpuTest(language, testMethod, testThread string) { |  | ||||||
| 	var res string |  | ||||||
| 	if runtime.GOOS == "windows" { |  | ||||||
| 		if testMethod != "winsat" && testMethod != "" { |  | ||||||
| 			res = "Detected host is Windows, using Winsat for testing.\n" |  | ||||||
| 		} |  | ||||||
| 		res += cpu.WinsatTest(language, testThread) |  | ||||||
| 	} else { |  | ||||||
| 		switch testMethod { |  | ||||||
| 		case "sysbench": |  | ||||||
| 			res = cpu.SysBenchTest(language, testThread) |  | ||||||
| 			if res == "" { |  | ||||||
| 				res = "Sysbench test failed, switching to Geekbench for testing.\n" |  | ||||||
| 				res += cpu.GeekBenchTest(language, testThread) |  | ||||||
| 			} |  | ||||||
| 		case "geekbench": |  | ||||||
| 			res = cpu.GeekBenchTest(language, testThread) |  | ||||||
| 			if res == "" { |  | ||||||
| 				res = "Geekbench test failed, switching to Sysbench for testing.\n" |  | ||||||
| 				res += cpu.SysBenchTest(language, testThread) |  | ||||||
| 			} |  | ||||||
| 		default: |  | ||||||
| 			res = "Invalid test method specified.\n" |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if !strings.Contains(res, "\n") && res != "" { |  | ||||||
| 		res += "\n" |  | ||||||
| 	} |  | ||||||
| 	fmt.Print(res) |  | ||||||
| } |  | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| package cputest |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func Test(t *testing.T) { |  | ||||||
| 	CpuTest("zh", "sysbench", "1") |  | ||||||
| } |  | ||||||
| @@ -1,41 +0,0 @@ | |||||||
| package disktest |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"github.com/oneclickvirt/disktest/disk" |  | ||||||
| 	"runtime" |  | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func DiskTest(language, testMethod, testPath string, isMultiCheck bool, autoChange bool) { |  | ||||||
| 	var res string |  | ||||||
| 	if runtime.GOOS == "windows" { |  | ||||||
| 		if testMethod != "winsat" && testMethod != "" { |  | ||||||
| 			res = "Detected host is Windows, using Winsat for testing.\n" |  | ||||||
| 		} |  | ||||||
| 		res = disk.WinsatTest(language, isMultiCheck, testPath) |  | ||||||
| 	} else { |  | ||||||
| 		switch testMethod { |  | ||||||
| 		case "fio": |  | ||||||
| 			res = disk.FioTest(language, isMultiCheck, testPath) |  | ||||||
| 			if res == "" && autoChange { |  | ||||||
| 				res = "Fio test failed, switching to DD for testing.\n" |  | ||||||
| 				res += disk.DDTest(language, isMultiCheck, testPath) |  | ||||||
| 			} |  | ||||||
| 		case "dd": |  | ||||||
| 			res = disk.DDTest(language, isMultiCheck, testPath) |  | ||||||
| 			if res == "" && autoChange { |  | ||||||
| 				res = "DD test failed, switching to Fio for testing.\n" |  | ||||||
| 				res += disk.FioTest(language, isMultiCheck, testPath) |  | ||||||
| 			} |  | ||||||
| 		default: |  | ||||||
| 			res = "Unsupported test method specified.\n" |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	//fmt.Println("--------------------------------------------------") |  | ||||||
| 	if !strings.Contains(res, "\n") && res != "" { |  | ||||||
| 		res += "\n" |  | ||||||
| 	} |  | ||||||
| 	fmt.Printf(res) |  | ||||||
| 	//fmt.Println("--------------------------------------------------") |  | ||||||
| } |  | ||||||
| @@ -1,7 +0,0 @@ | |||||||
| package disktest |  | ||||||
|  |  | ||||||
| import "testing" |  | ||||||
|  |  | ||||||
| func TestDiskIoTest(t *testing.T) { |  | ||||||
| 	DiskTest("zh", "sysbench", "", false) |  | ||||||
| } |  | ||||||
							
								
								
									
										116
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										116
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,116 +0,0 @@ | |||||||
| module github.com/oneclickvirt/ecs |  | ||||||
|  |  | ||||||
| go 1.22.10 |  | ||||||
|  |  | ||||||
| require ( |  | ||||||
| 	github.com/imroc/req/v3 v3.49.0 |  | ||||||
| 	github.com/oneclickvirt/CommonMediaTests v0.0.4-20240704024502 |  | ||||||
| 	github.com/oneclickvirt/UnlockTests v0.0.22-20250103060305 |  | ||||||
| 	github.com/oneclickvirt/backtrace v0.0.4-20240702140722 |  | ||||||
| 	github.com/oneclickvirt/basics v0.0.8-20241108124433 |  | ||||||
| 	github.com/oneclickvirt/cputest v0.0.9-20250103063414 |  | ||||||
| 	github.com/oneclickvirt/defaultset v0.0.2-20240624082446 |  | ||||||
| 	github.com/oneclickvirt/disktest v0.0.4-20240809053456 |  | ||||||
| 	github.com/oneclickvirt/gostun v0.0.3-20240702054621 |  | ||||||
| 	github.com/oneclickvirt/memorytest v0.0.4-20240820095126 |  | ||||||
| 	github.com/oneclickvirt/nt3 v0.0.4-20250103052821 |  | ||||||
| 	github.com/oneclickvirt/pingtest v0.0.5-20241006123443 |  | ||||||
| 	github.com/oneclickvirt/portchecker v0.0.2-20240803151204 |  | ||||||
| 	github.com/oneclickvirt/security v0.0.4-20240729065854 |  | ||||||
| 	github.com/oneclickvirt/speedtest v0.0.8-20241005164804 |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| require ( |  | ||||||
| 	github.com/PuerkitoBio/goquery v1.9.2 // indirect |  | ||||||
| 	github.com/StackExchange/wmi v1.2.1 // indirect |  | ||||||
| 	github.com/andybalholm/brotli v1.1.1 // indirect |  | ||||||
| 	github.com/andybalholm/cascadia v1.3.2 // indirect |  | ||||||
| 	github.com/cloudflare/circl v1.5.0 // indirect |  | ||||||
| 	github.com/fatih/color v1.18.0 // indirect |  | ||||||
| 	github.com/fsnotify/fsnotify v1.8.0 // indirect |  | ||||||
| 	github.com/ghodss/yaml v1.0.0 // indirect |  | ||||||
| 	github.com/go-ole/go-ole v1.2.6 // indirect |  | ||||||
| 	github.com/go-task/slim-sprig/v3 v3.0.0 // indirect |  | ||||||
| 	github.com/gofrs/uuid/v5 v5.2.0 // indirect |  | ||||||
| 	github.com/google/gopacket v1.1.19 // indirect |  | ||||||
| 	github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad // indirect |  | ||||||
| 	github.com/google/uuid v1.6.0 // indirect |  | ||||||
| 	github.com/gorilla/websocket v1.5.3 // indirect |  | ||||||
| 	github.com/hashicorp/errwrap v1.1.0 // indirect |  | ||||||
| 	github.com/hashicorp/go-multierror v1.1.1 // indirect |  | ||||||
| 	github.com/hashicorp/hcl v1.0.0 // indirect |  | ||||||
| 	github.com/huin/goupnp v1.2.0 // indirect |  | ||||||
| 	github.com/jackpal/go-nat-pmp v1.0.2 // indirect |  | ||||||
| 	github.com/jaypipes/ghw v0.12.0 // indirect |  | ||||||
| 	github.com/jaypipes/pcidb v1.0.0 // indirect |  | ||||||
| 	github.com/json-iterator/go v1.1.12 // indirect |  | ||||||
| 	github.com/klauspost/compress v1.17.11 // indirect |  | ||||||
| 	github.com/koron/go-ssdp v0.0.4 // indirect |  | ||||||
| 	github.com/libp2p/go-nat v0.2.0 // indirect |  | ||||||
| 	github.com/libp2p/go-netroute v0.2.1 // indirect |  | ||||||
| 	github.com/lionsoul2014/ip2region v2.11.2+incompatible // indirect |  | ||||||
| 	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect |  | ||||||
| 	github.com/magiconair/properties v1.8.9 // indirect |  | ||||||
| 	github.com/mattn/go-colorable v0.1.13 // indirect |  | ||||||
| 	github.com/mattn/go-isatty v0.0.20 // indirect |  | ||||||
| 	github.com/mattn/go-runewidth v0.0.16 // indirect |  | ||||||
| 	github.com/miekg/dns v1.1.61 // indirect |  | ||||||
| 	github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect |  | ||||||
| 	github.com/mitchellh/go-homedir v1.1.0 // indirect |  | ||||||
| 	github.com/mitchellh/mapstructure v1.5.0 // indirect |  | ||||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect |  | ||||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect |  | ||||||
| 	github.com/nxtrace/NTrace-core v1.3.6 // indirect |  | ||||||
| 	github.com/onsi/ginkgo/v2 v2.22.1 // indirect |  | ||||||
| 	github.com/oschwald/maxminddb-golang v1.13.1 // indirect |  | ||||||
| 	github.com/pelletier/go-toml/v2 v2.2.3 // indirect |  | ||||||
| 	github.com/pion/dtls/v2 v2.2.7 // indirect |  | ||||||
| 	github.com/pion/logging v0.2.2 // indirect |  | ||||||
| 	github.com/pion/stun/v2 v2.0.0 // indirect |  | ||||||
| 	github.com/pion/transport/v2 v2.2.1 // indirect |  | ||||||
| 	github.com/pion/transport/v3 v3.0.1 // indirect |  | ||||||
| 	github.com/pkg/errors v0.9.1 // indirect |  | ||||||
| 	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect |  | ||||||
| 	github.com/prometheus-community/pro-bing v0.4.1 // indirect |  | ||||||
| 	github.com/quic-go/qpack v0.5.1 // indirect |  | ||||||
| 	github.com/quic-go/quic-go v0.48.2 // indirect |  | ||||||
| 	github.com/refraction-networking/utls v1.6.7 // indirect |  | ||||||
| 	github.com/rivo/uniseg v0.4.7 // indirect |  | ||||||
| 	github.com/rodaine/table v1.3.0 // indirect |  | ||||||
| 	github.com/sagikazarmark/locafero v0.6.0 // indirect |  | ||||||
| 	github.com/sagikazarmark/slog-shim v0.1.0 // indirect |  | ||||||
| 	github.com/schollz/progressbar/v3 v3.14.4 // indirect |  | ||||||
| 	github.com/shirou/gopsutil v3.21.11+incompatible // indirect |  | ||||||
| 	github.com/shirou/gopsutil/v4 v4.24.5 // indirect |  | ||||||
| 	github.com/shoenig/go-m1cpu v0.1.6 // indirect |  | ||||||
| 	github.com/showwin/speedtest-go v1.7.7 // indirect |  | ||||||
| 	github.com/sourcegraph/conc v0.3.0 // indirect |  | ||||||
| 	github.com/spf13/afero v1.11.0 // indirect |  | ||||||
| 	github.com/spf13/cast v1.7.1 // indirect |  | ||||||
| 	github.com/spf13/pflag v1.0.5 // indirect |  | ||||||
| 	github.com/spf13/viper v1.19.0 // indirect |  | ||||||
| 	github.com/subosito/gotenv v1.6.0 // indirect |  | ||||||
| 	github.com/tidwall/gjson v1.18.0 // indirect |  | ||||||
| 	github.com/tidwall/match v1.1.1 // indirect |  | ||||||
| 	github.com/tidwall/pretty v1.2.1 // indirect |  | ||||||
| 	github.com/tklauser/go-sysconf v0.3.14 // indirect |  | ||||||
| 	github.com/tklauser/numcpus v0.8.0 // indirect |  | ||||||
| 	github.com/tsosunchia/powclient v0.1.5 // indirect |  | ||||||
| 	github.com/yusufpapurcu/wmi v1.2.4 // indirect |  | ||||||
| 	go.uber.org/mock v0.5.0 // indirect |  | ||||||
| 	go.uber.org/multierr v1.11.0 // indirect |  | ||||||
| 	go.uber.org/zap v1.27.0 // indirect |  | ||||||
| 	golang.org/x/crypto v0.31.0 // indirect |  | ||||||
| 	golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 // indirect |  | ||||||
| 	golang.org/x/mod v0.22.0 // indirect |  | ||||||
| 	golang.org/x/net v0.33.0 // indirect |  | ||||||
| 	golang.org/x/sync v0.10.0 // indirect |  | ||||||
| 	golang.org/x/sys v0.28.0 // indirect |  | ||||||
| 	golang.org/x/term v0.27.0 // indirect |  | ||||||
| 	golang.org/x/text v0.21.0 // indirect |  | ||||||
| 	golang.org/x/tools v0.28.0 // indirect |  | ||||||
| 	gopkg.in/ini.v1 v1.67.0 // indirect |  | ||||||
| 	gopkg.in/yaml.v2 v2.4.0 // indirect |  | ||||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect |  | ||||||
| 	howett.net/plist v1.0.0 // indirect |  | ||||||
| ) |  | ||||||
							
								
								
									
										330
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										330
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,330 +0,0 @@ | |||||||
| github.com/PuerkitoBio/goquery v1.9.2 h1:4/wZksC3KgkQw7SQgkKotmKljk0M6V8TUvA8Wb4yPeE= |  | ||||||
| github.com/PuerkitoBio/goquery v1.9.2/go.mod h1:GHPCaP0ODyyxqcNoFGYlAprUFH81NuRPd0GX3Zu2Mvk= |  | ||||||
| github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= |  | ||||||
| github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= |  | ||||||
| github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= |  | ||||||
| github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= |  | ||||||
| github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= |  | ||||||
| github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= |  | ||||||
| github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= |  | ||||||
| github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= |  | ||||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |  | ||||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |  | ||||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= |  | ||||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |  | ||||||
| github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= |  | ||||||
| github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= |  | ||||||
| github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= |  | ||||||
| github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= |  | ||||||
| github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= |  | ||||||
| github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= |  | ||||||
| github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= |  | ||||||
| github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= |  | ||||||
| github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= |  | ||||||
| github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= |  | ||||||
| github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= |  | ||||||
| github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= |  | ||||||
| github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= |  | ||||||
| github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= |  | ||||||
| github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= |  | ||||||
| github.com/gofrs/uuid/v5 v5.2.0 h1:qw1GMx6/y8vhVsx626ImfKMuS5CvJmhIKKtuyvfajMM= |  | ||||||
| github.com/gofrs/uuid/v5 v5.2.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8= |  | ||||||
| github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= |  | ||||||
| github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= |  | ||||||
| github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= |  | ||||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= |  | ||||||
| github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= |  | ||||||
| github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= |  | ||||||
| github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= |  | ||||||
| github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= |  | ||||||
| github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= |  | ||||||
| github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= |  | ||||||
| github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= |  | ||||||
| github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= |  | ||||||
| github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= |  | ||||||
| github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= |  | ||||||
| github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= |  | ||||||
| github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= |  | ||||||
| github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= |  | ||||||
| github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= |  | ||||||
| github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= |  | ||||||
| github.com/huin/goupnp v1.2.0 h1:uOKW26NG1hsSSbXIZ1IR7XP9Gjd1U8pnLaCMgntmkmY= |  | ||||||
| github.com/huin/goupnp v1.2.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= |  | ||||||
| github.com/imroc/req/v3 v3.49.0 h1:5Rac2qvz7Dq0E3PeBo/c2szV3hagPQIGLoHtfBmYhu4= |  | ||||||
| github.com/imroc/req/v3 v3.49.0/go.mod h1:XZf4t94DNJzcA0UOBlA68hmSrWsAyvN407ADdH4mzCA= |  | ||||||
| github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= |  | ||||||
| github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= |  | ||||||
| github.com/jaypipes/ghw v0.12.0 h1:xU2/MDJfWmBhJnujHY9qwXQLs3DBsf0/Xa9vECY0Tho= |  | ||||||
| github.com/jaypipes/ghw v0.12.0/go.mod h1:jeJGbkRB2lL3/gxYzNYzEDETV1ZJ56OKr+CSeSEym+g= |  | ||||||
| github.com/jaypipes/pcidb v1.0.0 h1:vtZIfkiCUE42oYbJS0TAq9XSfSmcsgo9IdxSm9qzYU8= |  | ||||||
| github.com/jaypipes/pcidb v1.0.0/go.mod h1:TnYUvqhPBzCKnH34KrIX22kAeEbDCSRJ9cqLRCuNDfk= |  | ||||||
| github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= |  | ||||||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= |  | ||||||
| github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= |  | ||||||
| github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= |  | ||||||
| github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= |  | ||||||
| github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= |  | ||||||
| github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0= |  | ||||||
| github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk= |  | ||||||
| github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= |  | ||||||
| github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= |  | ||||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= |  | ||||||
| github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= |  | ||||||
| github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk= |  | ||||||
| github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk= |  | ||||||
| github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU= |  | ||||||
| github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ= |  | ||||||
| github.com/lionsoul2014/ip2region v2.11.2+incompatible h1:+VRsGcrHz8ewXI/2UzTptJlACsxD/p4xCxuql4u2nKU= |  | ||||||
| github.com/lionsoul2014/ip2region v2.11.2+incompatible/go.mod h1:+ZBN7PBoh5gG6/y0ZQ85vJDBe21WnfbRrQQwTfliJJI= |  | ||||||
| github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= |  | ||||||
| github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= |  | ||||||
| github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= |  | ||||||
| github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= |  | ||||||
| github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= |  | ||||||
| github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= |  | ||||||
| github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= |  | ||||||
| github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= |  | ||||||
| github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= |  | ||||||
| github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= |  | ||||||
| github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= |  | ||||||
| github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs= |  | ||||||
| github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ= |  | ||||||
| github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= |  | ||||||
| github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= |  | ||||||
| github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= |  | ||||||
| github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= |  | ||||||
| github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= |  | ||||||
| github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= |  | ||||||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= |  | ||||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= |  | ||||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= |  | ||||||
| github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= |  | ||||||
| github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= |  | ||||||
| github.com/nxtrace/NTrace-core v1.3.6 h1:2VbytjCUUBKNb0MwPg/KwF3NfZdg/mup0KPUVbwL4eg= |  | ||||||
| github.com/nxtrace/NTrace-core v1.3.6/go.mod h1:gnArhBqgO1j29LH9KIy/tddQY8PpYdwsZJCuaelrJXw= |  | ||||||
| github.com/oneclickvirt/CommonMediaTests v0.0.4-20240704024502 h1:hRIYJ2uEp2N3AH5bP5X6bwfdwWfZQO/2WoqpUJ8+WsY= |  | ||||||
| github.com/oneclickvirt/CommonMediaTests v0.0.4-20240704024502/go.mod h1:DAmFPRjFV5p9fEzUUSml5jJGn2f1NZJQCzTxITHDjc4= |  | ||||||
| github.com/oneclickvirt/UnlockTests v0.0.22-20250103060305 h1:kQnpBZUS3yfS50MyPykMpmyJFG1cYSTMSl5VdEJFvSI= |  | ||||||
| github.com/oneclickvirt/UnlockTests v0.0.22-20250103060305/go.mod h1:yXWIZB6iLS88pEd9m4QJi1GENn+7I91zA72y5ONz2Oc= |  | ||||||
| github.com/oneclickvirt/backtrace v0.0.4-20240702140722 h1:UJ/VWf+ZbhGarc9HcHMIyenpmX+b2LxkXu0hlLk3Gxs= |  | ||||||
| github.com/oneclickvirt/backtrace v0.0.4-20240702140722/go.mod h1:zvsC7xY/WZqs5KL2JB967OVnuqjNbxu9bW6wXRLo5h8= |  | ||||||
| github.com/oneclickvirt/basics v0.0.8-20241108124433 h1:eKZcoNoa9uLWmfisk+qN/QOvSGT4ajCmZdZfIAcHG+o= |  | ||||||
| github.com/oneclickvirt/basics v0.0.8-20241108124433/go.mod h1:fUdVpU8gdjaZsTCyqnQBAbHc9BbbN8Fxr3sGPKooUpU= |  | ||||||
| github.com/oneclickvirt/cputest v0.0.9-20250103063414 h1:G69wbgHs3oDL1PraQ+8k0Gtefpj5eMTfCQGhz2dcfA8= |  | ||||||
| github.com/oneclickvirt/cputest v0.0.9-20250103063414/go.mod h1:MmaHN9+XMntI3rLycwj8Ne31fG18IfNoa8N2utDK1CY= |  | ||||||
| github.com/oneclickvirt/defaultset v0.0.2-20240624082446 h1:5Pg3mK/u/vQvSz7anu0nxzrNdELi/AcDAU1mMsmPzyc= |  | ||||||
| github.com/oneclickvirt/defaultset v0.0.2-20240624082446/go.mod h1:e9Jt4tf2sbemCtc84/XgKcHy9EZ2jkc5x2sW1NiJS+E= |  | ||||||
| github.com/oneclickvirt/disktest v0.0.4-20240809053456 h1:g6fKzvImIV8YQZKKEJ2FdvooL+EUf9NndAU8c4aGCX4= |  | ||||||
| github.com/oneclickvirt/disktest v0.0.4-20240809053456/go.mod h1:wIZy8G6Mbcy8Op8tc0HmJNpbJQQ5A15fvnUqMJXIdO0= |  | ||||||
| github.com/oneclickvirt/gostun v0.0.3-20240702054621 h1:IE89eEYV9TJbF94SakQDAxTLIaqX+Tb6ZhJ/CCIP+90= |  | ||||||
| github.com/oneclickvirt/gostun v0.0.3-20240702054621/go.mod h1:f7DPEXAxbmwXSW33dbxtb0/KzqvOBWhTs2Or5xBerQA= |  | ||||||
| github.com/oneclickvirt/memorytest v0.0.4-20240820095126 h1:Il3rvWkrZy/6B2iO3HRe9039/qRllA4CzcZ/dI8aG2A= |  | ||||||
| github.com/oneclickvirt/memorytest v0.0.4-20240820095126/go.mod h1:+YNzy+NeVg61d0kNwSyVDqHyVtKzjuRe1NvMzsDLg0I= |  | ||||||
| github.com/oneclickvirt/nt3 v0.0.4-20250103052821 h1:0c7pcAriroa12VOSro+/CfzN0mbiKJG+cvqbsPtH+BM= |  | ||||||
| github.com/oneclickvirt/nt3 v0.0.4-20250103052821/go.mod h1:4OURspZirunYgme1bvImFCPgZW2XzNtkvnBNKvIlFUU= |  | ||||||
| github.com/oneclickvirt/pingtest v0.0.5-20241006123443 h1:Ywrh0Yd/PYXOKGE8VaNiKGWlIqdgLLgsthAzxy0t7s8= |  | ||||||
| github.com/oneclickvirt/pingtest v0.0.5-20241006123443/go.mod h1:d3Ntx5m9lMll3a/k3+2B+5emj//vgDh4/NHTxs2qQE8= |  | ||||||
| github.com/oneclickvirt/portchecker v0.0.2-20240803151204 h1:ZruxRgyIv3d6Y8n0Ney5FHhQtcQLxCvs+xJmGsh9/7E= |  | ||||||
| github.com/oneclickvirt/portchecker v0.0.2-20240803151204/go.mod h1:HQxSTrqM8/QFqHMTBZ7S8H9eEO5FkUXU1eb7ZX5Mk+k= |  | ||||||
| github.com/oneclickvirt/security v0.0.4-20240729065854 h1:I27XtMUEHmXw1RN0jNDQmFqNdu6vL4v1g8UZtXiCuBY= |  | ||||||
| github.com/oneclickvirt/security v0.0.4-20240729065854/go.mod h1:384ZpNE3H6T6rtl0QhA4eQn8xGw7tc0rLD8ZH47qNGc= |  | ||||||
| github.com/oneclickvirt/speedtest v0.0.8-20241005164804 h1:qguczGucxyRCRaeI2Av7+QpgYb6o8lTJJmOjPkmGiSg= |  | ||||||
| github.com/oneclickvirt/speedtest v0.0.8-20241005164804/go.mod h1:zd5ZgIGslmtQLQehEfRjyumlvgDHTpCSMchKfKXoASI= |  | ||||||
| github.com/onsi/ginkgo/v2 v2.22.1 h1:QW7tbJAUDyVDVOM5dFa7qaybo+CRfR7bemlQUN6Z8aM= |  | ||||||
| github.com/onsi/ginkgo/v2 v2.22.1/go.mod h1:S6aTpoRsSq2cZOd+pssHAlKW/Q/jZt6cPrPlnj4a1xM= |  | ||||||
| github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= |  | ||||||
| github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= |  | ||||||
| github.com/oschwald/maxminddb-golang v1.13.1 h1:G3wwjdN9JmIK2o/ermkHM+98oX5fS+k5MbwsmL4MRQE= |  | ||||||
| github.com/oschwald/maxminddb-golang v1.13.1/go.mod h1:K4pgV9N/GcK694KSTmVSDTODk4IsCNThNdTmnaBZ/F8= |  | ||||||
| github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= |  | ||||||
| github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= |  | ||||||
| github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= |  | ||||||
| github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= |  | ||||||
| github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= |  | ||||||
| github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= |  | ||||||
| github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0= |  | ||||||
| github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ= |  | ||||||
| github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c= |  | ||||||
| github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= |  | ||||||
| github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= |  | ||||||
| github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= |  | ||||||
| github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= |  | ||||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |  | ||||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |  | ||||||
| github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= |  | ||||||
| github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= |  | ||||||
| github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= |  | ||||||
| github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= |  | ||||||
| github.com/prometheus-community/pro-bing v0.4.1 h1:aMaJwyifHZO0y+h8+icUz0xbToHbia0wdmzdVZ+Kl3w= |  | ||||||
| github.com/prometheus-community/pro-bing v0.4.1/go.mod h1:aLsw+zqCaDoa2RLVVSX3+UiCkBBXTMtZC3c7EkfWnAE= |  | ||||||
| github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= |  | ||||||
| github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= |  | ||||||
| github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE= |  | ||||||
| github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs= |  | ||||||
| github.com/refraction-networking/utls v1.6.7 h1:zVJ7sP1dJx/WtVuITug3qYUq034cDq9B2MR1K67ULZM= |  | ||||||
| github.com/refraction-networking/utls v1.6.7/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0= |  | ||||||
| github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= |  | ||||||
| github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= |  | ||||||
| github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= |  | ||||||
| github.com/rodaine/table v1.3.0 h1:4/3S3SVkHnVZX91EHFvAMV7K42AnJ0XuymRR2C5HlGE= |  | ||||||
| github.com/rodaine/table v1.3.0/go.mod h1:47zRsHar4zw0jgxGxL9YtFfs7EGN6B/TaS+/Dmk4WxU= |  | ||||||
| github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= |  | ||||||
| github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= |  | ||||||
| github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk= |  | ||||||
| github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0= |  | ||||||
| github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= |  | ||||||
| github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= |  | ||||||
| github.com/schollz/progressbar/v3 v3.14.4 h1:W9ZrDSJk7eqmQhd3uxFNNcTr0QL+xuGNI9dEMrw0r74= |  | ||||||
| github.com/schollz/progressbar/v3 v3.14.4/go.mod h1:aT3UQ7yGm+2ZjeXPqsjTenwL3ddUiuZ0kfQ/2tHlyNI= |  | ||||||
| github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= |  | ||||||
| github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= |  | ||||||
| github.com/shirou/gopsutil/v4 v4.24.5 h1:gGsArG5K6vmsh5hcFOHaPm87UD003CaDMkAOweSQjhM= |  | ||||||
| github.com/shirou/gopsutil/v4 v4.24.5/go.mod h1:aoebb2vxetJ/yIDZISmduFvVNPHqXQ9SEJwRXxkf0RA= |  | ||||||
| github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= |  | ||||||
| github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= |  | ||||||
| github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= |  | ||||||
| github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= |  | ||||||
| github.com/showwin/speedtest-go v1.7.7 h1:VmK75SZOTKiuWjIVrs+mo7ZoKEw0utiGCvpnurS0olU= |  | ||||||
| github.com/showwin/speedtest-go v1.7.7/go.mod h1:uLgdWCNarXxlYsL2E5TOZpCIwpgSWnEANZp7gfHXHu0= |  | ||||||
| github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= |  | ||||||
| github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= |  | ||||||
| github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= |  | ||||||
| github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= |  | ||||||
| github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= |  | ||||||
| github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= |  | ||||||
| github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= |  | ||||||
| github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= |  | ||||||
| github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= |  | ||||||
| github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= |  | ||||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= |  | ||||||
| github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= |  | ||||||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= |  | ||||||
| github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= |  | ||||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= |  | ||||||
| github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= |  | ||||||
| github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= |  | ||||||
| github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= |  | ||||||
| github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= |  | ||||||
| github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= |  | ||||||
| github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= |  | ||||||
| github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= |  | ||||||
| github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= |  | ||||||
| github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= |  | ||||||
| github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= |  | ||||||
| github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= |  | ||||||
| github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= |  | ||||||
| github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= |  | ||||||
| github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= |  | ||||||
| github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= |  | ||||||
| github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= |  | ||||||
| github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU= |  | ||||||
| github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY= |  | ||||||
| github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY= |  | ||||||
| github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE= |  | ||||||
| github.com/tsosunchia/powclient v0.1.5 h1:hpixFWoPbWSEC0zc9osSltyjtr1+SnhCueZVLkEpyyU= |  | ||||||
| github.com/tsosunchia/powclient v0.1.5/go.mod h1:yNlzyq+w9llYZV+0q7nrX83ULy4ghq2mCjpTLJFJ2pg= |  | ||||||
| github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= |  | ||||||
| github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= |  | ||||||
| github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= |  | ||||||
| github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= |  | ||||||
| github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= |  | ||||||
| go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= |  | ||||||
| go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= |  | ||||||
| go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= |  | ||||||
| go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= |  | ||||||
| go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= |  | ||||||
| go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= |  | ||||||
| go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= |  | ||||||
| go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= |  | ||||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= |  | ||||||
| golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= |  | ||||||
| golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= |  | ||||||
| golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= |  | ||||||
| golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= |  | ||||||
| golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= |  | ||||||
| golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= |  | ||||||
| golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67 h1:1UoZQm6f0P/ZO0w1Ri+f+ifG/gXhegadRdwBIXEFWDo= |  | ||||||
| golang.org/x/exp v0.0.0-20241217172543-b2144cdd0a67/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= |  | ||||||
| golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= |  | ||||||
| golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= |  | ||||||
| golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= |  | ||||||
| golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= |  | ||||||
| golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4= |  | ||||||
| golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= |  | ||||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= |  | ||||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= |  | ||||||
| golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= |  | ||||||
| golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= |  | ||||||
| golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= |  | ||||||
| golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= |  | ||||||
| golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= |  | ||||||
| golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= |  | ||||||
| golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= |  | ||||||
| golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= |  | ||||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |  | ||||||
| golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |  | ||||||
| golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |  | ||||||
| golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= |  | ||||||
| golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= |  | ||||||
| golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= |  | ||||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= |  | ||||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |  | ||||||
| golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |  | ||||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |  | ||||||
| golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |  | ||||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |  | ||||||
| golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= |  | ||||||
| golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= |  | ||||||
| golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= |  | ||||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= |  | ||||||
| golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= |  | ||||||
| golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= |  | ||||||
| golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= |  | ||||||
| golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= |  | ||||||
| golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= |  | ||||||
| golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= |  | ||||||
| golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= |  | ||||||
| golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= |  | ||||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |  | ||||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= |  | ||||||
| golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= |  | ||||||
| golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= |  | ||||||
| golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= |  | ||||||
| golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= |  | ||||||
| golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= |  | ||||||
| golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= |  | ||||||
| golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= |  | ||||||
| golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= |  | ||||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= |  | ||||||
| golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= |  | ||||||
| golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= |  | ||||||
| golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= |  | ||||||
| golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= |  | ||||||
| golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= |  | ||||||
| golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= |  | ||||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |  | ||||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |  | ||||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= |  | ||||||
| google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= |  | ||||||
| google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= |  | ||||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= |  | ||||||
| gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= |  | ||||||
| gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= |  | ||||||
| gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= |  | ||||||
| gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= |  | ||||||
| gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg= |  | ||||||
| gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= |  | ||||||
| gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= |  | ||||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |  | ||||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= |  | ||||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= |  | ||||||
| howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM= |  | ||||||
| howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g= |  | ||||||
							
								
								
									
										625
									
								
								goecs.go
									
									
									
									
									
								
							
							
						
						
									
										625
									
								
								goecs.go
									
									
									
									
									
								
							| @@ -1,625 +0,0 @@ | |||||||
| package main |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"bufio" |  | ||||||
| 	"context" |  | ||||||
| 	"flag" |  | ||||||
| 	"fmt" |  | ||||||
| 	"github.com/oneclickvirt/CommonMediaTests/commediatests" |  | ||||||
| 	unlocktestmodel "github.com/oneclickvirt/UnlockTests/model" |  | ||||||
| 	backtraceori "github.com/oneclickvirt/backtrace/bk" |  | ||||||
| 	basicmodel "github.com/oneclickvirt/basics/model" |  | ||||||
| 	cputestmodel "github.com/oneclickvirt/cputest/model" |  | ||||||
| 	disktestmodel "github.com/oneclickvirt/disktest/disk" |  | ||||||
| 	"github.com/oneclickvirt/ecs/backtrace" |  | ||||||
| 	"github.com/oneclickvirt/ecs/commediatest" |  | ||||||
| 	"github.com/oneclickvirt/ecs/cputest" |  | ||||||
| 	"github.com/oneclickvirt/ecs/disktest" |  | ||||||
| 	"github.com/oneclickvirt/ecs/memorytest" |  | ||||||
| 	"github.com/oneclickvirt/ecs/ntrace" |  | ||||||
| 	"github.com/oneclickvirt/ecs/speedtest" |  | ||||||
| 	"github.com/oneclickvirt/ecs/unlocktest" |  | ||||||
| 	"github.com/oneclickvirt/ecs/utils" |  | ||||||
| 	gostunmodel "github.com/oneclickvirt/gostun/model" |  | ||||||
| 	memorytestmodel "github.com/oneclickvirt/memorytest/memory" |  | ||||||
| 	nt3model "github.com/oneclickvirt/nt3/model" |  | ||||||
| 	ptmodel "github.com/oneclickvirt/pingtest/model" |  | ||||||
| 	"github.com/oneclickvirt/pingtest/pt" |  | ||||||
| 	"github.com/oneclickvirt/portchecker/email" |  | ||||||
| 	speedtestmodel "github.com/oneclickvirt/speedtest/model" |  | ||||||
| 	"net/http" |  | ||||||
| 	"os" |  | ||||||
| 	"os/signal" |  | ||||||
| 	"regexp" |  | ||||||
| 	"runtime" |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
| 	"syscall" |  | ||||||
| 	"time" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	ecsVersion                                                        = "v0.1.4" |  | ||||||
| 	menuMode                                                          bool |  | ||||||
| 	onlyChinaTest                                                     bool |  | ||||||
| 	input, choice                                                     string |  | ||||||
| 	showVersion                                                       bool |  | ||||||
| 	enableLogger                                                      bool |  | ||||||
| 	language                                                          string |  | ||||||
| 	cpuTestMethod, cpuTestThreadMode                                  string |  | ||||||
| 	memoryTestMethod                                                  string |  | ||||||
| 	diskTestMethod, diskTestPath                                      string |  | ||||||
| 	diskMultiCheck                                                    bool |  | ||||||
| 	nt3CheckType, nt3Location                                         string |  | ||||||
| 	spNum                                                             int |  | ||||||
| 	width                                                             = 82 |  | ||||||
| 	basicStatus, cpuTestStatus, memoryTestStatus, diskTestStatus      bool |  | ||||||
| 	commTestStatus, utTestStatus, securityTestStatus, emailTestStatus bool |  | ||||||
| 	backtraceStatus, nt3Status, speedTestStatus, pingTestStatus       bool |  | ||||||
| 	autoChangeDiskTestMethod                                          = true |  | ||||||
| 	filePath                                                          = "goecs.txt" |  | ||||||
| 	enabelUpload                                                      = true |  | ||||||
| 	help                                                              bool |  | ||||||
| 	goecsFlag                                                         = flag.NewFlagSet("goecs", flag.ContinueOnError) |  | ||||||
| 	finish                                                            bool |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func getMenuChoice(language string) string { |  | ||||||
| 	ctx, cancel := context.WithCancel(context.Background()) |  | ||||||
| 	defer cancel() |  | ||||||
| 	sigChan := make(chan os.Signal, 1) |  | ||||||
| 	signal.Notify(sigChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) |  | ||||||
| 	defer signal.Stop(sigChan) |  | ||||||
| 	inputChan := make(chan string, 1) |  | ||||||
| 	go func() { |  | ||||||
| 		select { |  | ||||||
| 		case <-sigChan: |  | ||||||
| 			fmt.Println("\n程序在选择过程中被用户中断") |  | ||||||
| 			os.Exit(0) |  | ||||||
| 		case <-ctx.Done(): |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
|  |  | ||||||
| 	for { |  | ||||||
| 		go func() { |  | ||||||
| 			var input string |  | ||||||
| 			fmt.Print("请输入选项 / Please enter your choice: ") |  | ||||||
| 			fmt.Scanln(&input) |  | ||||||
| 			input = strings.TrimSpace(input) |  | ||||||
| 			input = strings.TrimRight(input, "\n") |  | ||||||
| 			select { |  | ||||||
| 			case inputChan <- input: |  | ||||||
| 			case <-ctx.Done(): |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 		}() |  | ||||||
| 		select { |  | ||||||
| 		case input := <-inputChan: |  | ||||||
| 			re := regexp.MustCompile(`^\d+$`) // 正则表达式匹配纯数字 |  | ||||||
| 			if re.MatchString(input) { |  | ||||||
| 				inChoice := input |  | ||||||
| 				switch inChoice { |  | ||||||
| 				case "1", "2", "3", "4", "5", "6", "7", "8", "9", "10": |  | ||||||
| 					return inChoice |  | ||||||
| 				default: |  | ||||||
| 					if language == "zh" { |  | ||||||
| 						fmt.Println("无效的选项") |  | ||||||
| 					} else { |  | ||||||
| 						fmt.Println("Invalid choice") |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} else { |  | ||||||
| 				if language == "zh" { |  | ||||||
| 					fmt.Println("输入错误,请输入一个纯数字") |  | ||||||
| 				} else { |  | ||||||
| 					fmt.Println("Invalid input, please enter a number") |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		case <-ctx.Done(): |  | ||||||
| 			return "" |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func main() { |  | ||||||
| 	goecsFlag.BoolVar(&help, "h", false, "Show help information") |  | ||||||
| 	goecsFlag.BoolVar(&showVersion, "v", false, "Display version information") |  | ||||||
| 	goecsFlag.BoolVar(&menuMode, "menu", true, "Enable/Disable menu mode, disable example: -menu=false") // true 默认启用菜单栏模式 |  | ||||||
| 	goecsFlag.StringVar(&language, "l", "zh", "Set language (supported: en, zh)") |  | ||||||
| 	goecsFlag.BoolVar(&basicStatus, "basic", true, "Enable/Disable basic test") |  | ||||||
| 	goecsFlag.BoolVar(&cpuTestStatus, "cpu", true, "Enable/Disable CPU test") |  | ||||||
| 	goecsFlag.BoolVar(&memoryTestStatus, "memory", true, "Enable/Disable memory test") |  | ||||||
| 	goecsFlag.BoolVar(&diskTestStatus, "disk", true, "Enable/Disable disk test") |  | ||||||
| 	goecsFlag.BoolVar(&commTestStatus, "comm", true, "Enable/Disable common media test") |  | ||||||
| 	goecsFlag.BoolVar(&utTestStatus, "ut", true, "Enable/Disable unlock media test") |  | ||||||
| 	goecsFlag.BoolVar(&securityTestStatus, "security", true, "Enable/Disable security test") |  | ||||||
| 	goecsFlag.BoolVar(&emailTestStatus, "email", true, "Enable/Disable email port test") |  | ||||||
| 	goecsFlag.BoolVar(&backtraceStatus, "backtrace", true, "Enable/Disable backtrace test (in 'en' language or on windows it always false)") |  | ||||||
| 	goecsFlag.BoolVar(&nt3Status, "nt3", true, "Enable/Disable NT3 test (in 'en' language or on windows it always false)") |  | ||||||
| 	goecsFlag.BoolVar(&speedTestStatus, "speed", true, "Enable/Disable speed test") |  | ||||||
| 	goecsFlag.StringVar(&cpuTestMethod, "cpum", "sysbench", "Set CPU test method (supported: sysbench, geekbench, winsat)") |  | ||||||
| 	goecsFlag.StringVar(&cpuTestThreadMode, "cput", "multi", "Set CPU test thread mode (supported: single, multi)") |  | ||||||
| 	goecsFlag.StringVar(&memoryTestMethod, "memorym", "sysbench", "Set memory test method (supported: sysbench, dd, winsat)") |  | ||||||
| 	goecsFlag.StringVar(&diskTestMethod, "diskm", "fio", "Set disk test method (supported: fio, dd, winsat)") |  | ||||||
| 	goecsFlag.StringVar(&diskTestPath, "diskp", "", "Set disk test path, e.g., -diskp /root") |  | ||||||
| 	goecsFlag.BoolVar(&diskMultiCheck, "diskmc", false, "Enable/Disable multiple disk checks, e.g., -diskmc=false") |  | ||||||
| 	goecsFlag.StringVar(&nt3Location, "nt3loc", "GZ", "Specify NT3 test location (supported: GZ, SH, BJ, CD for Guangzhou, Shanghai, Beijing, Chengdu)") |  | ||||||
| 	goecsFlag.StringVar(&nt3CheckType, "nt3t", "ipv4", "Set NT3 test type (supported: both, ipv4, ipv6)") |  | ||||||
| 	goecsFlag.IntVar(&spNum, "spnum", 2, "Set the number of servers per operator for speed test") |  | ||||||
| 	goecsFlag.BoolVar(&enableLogger, "log", false, "Enable/Disable logging in the current path") |  | ||||||
| 	goecsFlag.BoolVar(&enabelUpload, "upload", true, "Enable/Disable upload the result") |  | ||||||
| 	goecsFlag.Parse(os.Args[1:]) |  | ||||||
| 	if help { |  | ||||||
| 		fmt.Printf("Usage: %s [options]\n", os.Args[0]) |  | ||||||
| 		goecsFlag.PrintDefaults() |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if showVersion { |  | ||||||
| 		fmt.Println(ecsVersion) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	if enableLogger { |  | ||||||
| 		gostunmodel.EnableLoger = true |  | ||||||
| 		basicmodel.EnableLoger = true |  | ||||||
| 		cputestmodel.EnableLoger = true |  | ||||||
| 		memorytestmodel.EnableLoger = true |  | ||||||
| 		disktestmodel.EnableLoger = true |  | ||||||
| 		commediatests.EnableLoger = true |  | ||||||
| 		unlocktestmodel.EnableLoger = true |  | ||||||
| 		ptmodel.EnableLoger = true |  | ||||||
| 		backtraceori.EnableLoger = true |  | ||||||
| 		nt3model.EnableLoger = true |  | ||||||
| 		speedtestmodel.EnableLoger = true |  | ||||||
| 	} |  | ||||||
| 	go func() { |  | ||||||
| 		http.Get("https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Foneclickvirt%2Fecs&count_bg=%2357DEFF&title_bg=%23000000&icon=cliqz.svg&icon_color=%23E7E7E7&title=hits&edge_flat=false") |  | ||||||
| 	}() |  | ||||||
| 	if menuMode { |  | ||||||
| 		basicStatus, cpuTestStatus, memoryTestStatus, diskTestStatus = false, false, false, false |  | ||||||
| 		commTestStatus, utTestStatus, securityTestStatus, emailTestStatus = false, false, false, false |  | ||||||
| 		backtraceStatus, nt3Status, speedTestStatus = false, false, false |  | ||||||
| 		autoChangeDiskTestMethod = true |  | ||||||
| 		switch language { |  | ||||||
| 		case "zh": |  | ||||||
| 			fmt.Println("VPS融合怪版本: ", ecsVersion) |  | ||||||
| 			fmt.Println("1. 融合怪完全体") |  | ||||||
| 			fmt.Println("2. 极简版(系统信息+CPU+内存+磁盘+测速节点5个)") |  | ||||||
| 			fmt.Println("3. 精简版(系统信息+CPU+内存+磁盘+常用流媒体+路由+测速节点5个)") |  | ||||||
| 			fmt.Println("4. 精简网络版(系统信息+CPU+内存+磁盘+回程+路由+测速节点5个)") |  | ||||||
| 			fmt.Println("5. 精简解锁版(系统信息+CPU+内存+磁盘IO+御三家+常用流媒体+测速节点5个)") |  | ||||||
| 			fmt.Println("6. 网络单项(IP质量检测+三网回程+三网路由与延迟+测速节点11个)") |  | ||||||
| 			fmt.Println("7. 解锁单项(御三家解锁+常用流媒体解锁)") |  | ||||||
| 			fmt.Println("8. 硬件单项(系统信息+CPU+内存+dd磁盘测试+fio磁盘测试)") |  | ||||||
| 			fmt.Println("9. IP质量检测(15个数据库的IP检测+邮件端口检测)") |  | ||||||
| 			fmt.Println("10. 三网回程线路+广州三网路由+全国三网延迟") |  | ||||||
| 		case "en": |  | ||||||
| 			fmt.Println("VPS Fusion Monster Test Version: ", ecsVersion) |  | ||||||
| 			fmt.Println("1. VPS Fusion Monster Test Comprehensive Test Suite") |  | ||||||
| 			fmt.Println("2. Minimal Test Suite (System Info + CPU + Memory + Disk + 5 Speed Test Nodes)") |  | ||||||
| 			fmt.Println("3. Standard Test Suite (System Info + CPU + Memory + Disk + Basic Unlock Tests + 5 Speed Test Nodes)") |  | ||||||
| 			fmt.Println("4. Network-Focused Test Suite (System Info + CPU + Memory + Disk + 5 Speed Test Nodes)") |  | ||||||
| 			fmt.Println("5. Unlock-Focused Test Suite (System Info + CPU + Memory + Disk IO + Basic Unlock Tests + Common Streaming Services + 5 Speed Test Nodes)") |  | ||||||
| 			fmt.Println("6. Network-Only Test (IP Quality Test + 5 Speed Test Nodes)") |  | ||||||
| 			fmt.Println("7. Unlock-Only Test (Basic Unlock Tests + Common Streaming Services Unlock)") |  | ||||||
| 			fmt.Println("8. Hardware-Only Test (System Info + CPU + Memory + dd Disk Test + fio Disk Test)") |  | ||||||
| 			fmt.Println("9. IP Quality Test (IP Test with 15 Databases + Email Port Test)") |  | ||||||
| 		} |  | ||||||
| 	Loop: |  | ||||||
| 		for { |  | ||||||
| 			choice = getMenuChoice(language) |  | ||||||
| 			switch choice { |  | ||||||
| 			case "1": |  | ||||||
| 				basicStatus = true |  | ||||||
| 				cpuTestStatus = true |  | ||||||
| 				memoryTestStatus = true |  | ||||||
| 				diskTestStatus = true |  | ||||||
| 				commTestStatus = true |  | ||||||
| 				utTestStatus = true |  | ||||||
| 				securityTestStatus = true |  | ||||||
| 				emailTestStatus = true |  | ||||||
| 				backtraceStatus = true |  | ||||||
| 				nt3Status = true |  | ||||||
| 				speedTestStatus = true |  | ||||||
| 				onlyChinaTest = utils.CheckChina(enableLogger) |  | ||||||
| 				break Loop |  | ||||||
| 			case "2": |  | ||||||
| 				basicStatus = true |  | ||||||
| 				cpuTestStatus = true |  | ||||||
| 				memoryTestStatus = true |  | ||||||
| 				diskTestStatus = true |  | ||||||
| 				speedTestStatus = true |  | ||||||
| 				break Loop |  | ||||||
| 			case "3": |  | ||||||
| 				basicStatus = true |  | ||||||
| 				cpuTestStatus = true |  | ||||||
| 				memoryTestStatus = true |  | ||||||
| 				diskTestStatus = true |  | ||||||
| 				utTestStatus = true |  | ||||||
| 				nt3Status = true |  | ||||||
| 				speedTestStatus = true |  | ||||||
| 				break Loop |  | ||||||
| 			case "4": |  | ||||||
| 				basicStatus = true |  | ||||||
| 				cpuTestStatus = true |  | ||||||
| 				memoryTestStatus = true |  | ||||||
| 				diskTestStatus = true |  | ||||||
| 				backtraceStatus = true |  | ||||||
| 				nt3Status = true |  | ||||||
| 				speedTestStatus = true |  | ||||||
| 				break Loop |  | ||||||
| 			case "5": |  | ||||||
| 				basicStatus = true |  | ||||||
| 				cpuTestStatus = true |  | ||||||
| 				memoryTestStatus = true |  | ||||||
| 				diskTestStatus = true |  | ||||||
| 				commTestStatus = true |  | ||||||
| 				utTestStatus = true |  | ||||||
| 				speedTestStatus = true |  | ||||||
| 				break Loop |  | ||||||
| 			case "6": |  | ||||||
| 				securityTestStatus = true |  | ||||||
| 				speedTestStatus = true |  | ||||||
| 				backtraceStatus = true |  | ||||||
| 				nt3Status = true |  | ||||||
| 				break Loop |  | ||||||
| 			case "7": |  | ||||||
| 				commTestStatus = true |  | ||||||
| 				utTestStatus = true |  | ||||||
| 				enabelUpload = false |  | ||||||
| 				break Loop |  | ||||||
| 			case "8": |  | ||||||
| 				basicStatus = true |  | ||||||
| 				cpuTestStatus = true |  | ||||||
| 				memoryTestStatus = true |  | ||||||
| 				diskTestStatus = true |  | ||||||
| 				securityTestStatus = false |  | ||||||
| 				autoChangeDiskTestMethod = false |  | ||||||
| 				break Loop |  | ||||||
| 			case "9": |  | ||||||
| 				securityTestStatus = true |  | ||||||
| 				emailTestStatus = true |  | ||||||
| 				break Loop |  | ||||||
| 			case "10": |  | ||||||
| 				backtraceStatus = true |  | ||||||
| 				nt3Status = true |  | ||||||
| 				pingTestStatus = true |  | ||||||
| 				enabelUpload = false |  | ||||||
| 				break Loop |  | ||||||
| 			default: |  | ||||||
| 				if language == "zh" { |  | ||||||
| 					fmt.Println("无效的选项") |  | ||||||
| 				} else { |  | ||||||
| 					fmt.Println("Invalid choice") |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if language == "en" { |  | ||||||
| 		backtraceStatus = false |  | ||||||
| 		nt3Status = false |  | ||||||
| 	} |  | ||||||
| 	if !enabelUpload { |  | ||||||
| 		securityTestStatus = false |  | ||||||
| 	} |  | ||||||
| 	var ( |  | ||||||
| 		startTime                                             time.Time |  | ||||||
| 		wg1, wg2, wg3                                         sync.WaitGroup |  | ||||||
| 		basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo string |  | ||||||
| 		output, tempOutput                                    string |  | ||||||
| 	) |  | ||||||
| 	// 信号处理部分 |  | ||||||
| 	uploadDone := make(chan bool, 1) |  | ||||||
| 	sig := make(chan os.Signal, 1) |  | ||||||
| 	signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) |  | ||||||
| 	// 启动一个goroutine来等待信号 |  | ||||||
| 	go func() { |  | ||||||
| 		startTime = time.Now() |  | ||||||
| 		select { |  | ||||||
| 		case <-sig: |  | ||||||
| 			if !finish { |  | ||||||
| 				endTime := time.Now() |  | ||||||
| 				duration := endTime.Sub(startTime) |  | ||||||
| 				minutes := int(duration.Minutes()) |  | ||||||
| 				seconds := int(duration.Seconds()) % 60 |  | ||||||
| 				currentTime := time.Now().Format("Mon Jan 2 15:04:05 MST 2006") |  | ||||||
| 				var mu sync.Mutex |  | ||||||
| 				mu.Lock() |  | ||||||
| 				output = utils.PrintAndCapture(func() { |  | ||||||
| 					utils.PrintCenteredTitle("", width) |  | ||||||
| 					fmt.Printf("Cost    Time          : %d min %d sec\n", minutes, seconds) |  | ||||||
| 					fmt.Printf("Current Time          : %s\n", currentTime) |  | ||||||
| 					utils.PrintCenteredTitle("", width) |  | ||||||
| 				}, tempOutput, output) |  | ||||||
| 				mu.Unlock() |  | ||||||
| 				// 创建一个通道来传递上传结果 |  | ||||||
| 				resultChan := make(chan struct { |  | ||||||
| 					httpURL  string |  | ||||||
| 					httpsURL string |  | ||||||
| 				}, 1) // 使用带缓冲的通道,避免可能的阻塞 |  | ||||||
| 				// 启动上传 |  | ||||||
| 				go func() { |  | ||||||
| 					httpURL, httpsURL := utils.ProcessAndUpload(output, filePath, enabelUpload) |  | ||||||
| 					resultChan <- struct { |  | ||||||
| 						httpURL  string |  | ||||||
| 						httpsURL string |  | ||||||
| 					}{httpURL, httpsURL} |  | ||||||
| 					uploadDone <- true |  | ||||||
| 				}() |  | ||||||
| 				// 等待上传完成或超时 |  | ||||||
| 				select { |  | ||||||
| 				case result := <-resultChan: |  | ||||||
| 					if result.httpURL != "" || result.httpsURL != "" { |  | ||||||
| 						if language == "en" { |  | ||||||
| 							fmt.Printf("Upload successfully!\nHttp URL:  %s\nHttps URL: %s\n", result.httpURL, result.httpsURL) |  | ||||||
| 						} else { |  | ||||||
| 							fmt.Printf("上传成功!\nHttp URL:  %s\nHttps URL: %s\n", result.httpURL, result.httpsURL) |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 					// 给打印操作一些时间完成 |  | ||||||
| 					time.Sleep(100 * time.Millisecond) |  | ||||||
| 					os.Exit(0) |  | ||||||
| 				case <-time.After(30 * time.Second): |  | ||||||
| 					fmt.Println("上传超时,程序退出") |  | ||||||
| 					os.Exit(1) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			os.Exit(0) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| 	switch language { |  | ||||||
| 	case "zh": |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			utils.PrintHead(language, width, ecsVersion) |  | ||||||
| 			if basicStatus || securityTestStatus { |  | ||||||
| 				if basicStatus { |  | ||||||
| 					utils.PrintCenteredTitle("系统基础信息", width) |  | ||||||
| 				} |  | ||||||
| 				basicInfo, securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, nt3CheckType, securityTestStatus) |  | ||||||
| 				if basicStatus { |  | ||||||
| 					fmt.Printf(basicInfo) |  | ||||||
| 				} else if (input == "6" || input == "9") && securityTestStatus { |  | ||||||
| 					scanner := bufio.NewScanner(strings.NewReader(basicInfo)) |  | ||||||
| 					for scanner.Scan() { |  | ||||||
| 						line := scanner.Text() |  | ||||||
| 						if strings.Contains(line, "IPV") { |  | ||||||
| 							fmt.Println(line) |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if cpuTestStatus { |  | ||||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("CPU测试-通过%s测试", cpuTestMethod), width) |  | ||||||
| 				cputest.CpuTest(language, cpuTestMethod, cpuTestThreadMode) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if memoryTestStatus { |  | ||||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("内存测试-通过%s测试", memoryTestMethod), width) |  | ||||||
| 				memorytest.MemoryTest(language, memoryTestMethod) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if diskTestStatus && autoChangeDiskTestMethod { |  | ||||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", diskTestMethod), width) |  | ||||||
| 				disktest.DiskTest(language, diskTestMethod, diskTestPath, diskMultiCheck, autoChangeDiskTestMethod) |  | ||||||
| 			} else if diskTestStatus && !autoChangeDiskTestMethod { |  | ||||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", "dd"), width) |  | ||||||
| 				disktest.DiskTest(language, "dd", diskTestPath, diskMultiCheck, autoChangeDiskTestMethod) |  | ||||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", "fio"), width) |  | ||||||
| 				disktest.DiskTest(language, "fio", diskTestPath, diskMultiCheck, autoChangeDiskTestMethod) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		if onlyChinaTest || pingTestStatus { |  | ||||||
| 			wg3.Add(1) |  | ||||||
| 			go func() { |  | ||||||
| 				defer wg3.Done() |  | ||||||
| 				ptInfo = pt.PingTest() |  | ||||||
| 			}() |  | ||||||
| 		} |  | ||||||
| 		if emailTestStatus { |  | ||||||
| 			wg2.Add(1) |  | ||||||
| 			go func() { |  | ||||||
| 				defer wg2.Done() |  | ||||||
| 				emailInfo = email.EmailCheck() |  | ||||||
| 			}() |  | ||||||
| 		} |  | ||||||
| 		if utTestStatus && !onlyChinaTest { |  | ||||||
| 			wg1.Add(1) |  | ||||||
| 			go func() { |  | ||||||
| 				defer wg1.Done() |  | ||||||
| 				mediaInfo = unlocktest.MediaTest(language) |  | ||||||
| 			}() |  | ||||||
| 		} |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if commTestStatus && !onlyChinaTest { |  | ||||||
| 				utils.PrintCenteredTitle("御三家流媒体解锁", width) |  | ||||||
| 				commediatest.ComMediaTest(language) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if utTestStatus && !onlyChinaTest { |  | ||||||
| 				utils.PrintCenteredTitle("跨国流媒体解锁", width) |  | ||||||
| 				wg1.Wait() |  | ||||||
| 				fmt.Printf(mediaInfo) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if securityTestStatus { |  | ||||||
| 				utils.PrintCenteredTitle("IP质量检测", width) |  | ||||||
| 				fmt.Printf(securityInfo) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if emailTestStatus { |  | ||||||
| 				utils.PrintCenteredTitle("邮件端口检测", width) |  | ||||||
| 				wg2.Wait() |  | ||||||
| 				fmt.Println(emailInfo) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		if runtime.GOOS != "windows" { |  | ||||||
| 			output = utils.PrintAndCapture(func() { |  | ||||||
| 				if backtraceStatus && !onlyChinaTest { |  | ||||||
| 					utils.PrintCenteredTitle("三网回程线路检测", width) |  | ||||||
| 					backtrace.BackTrace() |  | ||||||
| 				} |  | ||||||
| 			}, tempOutput, output) |  | ||||||
| 			// nexttrace 在win上不支持检测,报错 bind: An invalid argument was supplied. |  | ||||||
| 			output = utils.PrintAndCapture(func() { |  | ||||||
| 				if nt3Status && !onlyChinaTest { |  | ||||||
| 					utils.PrintCenteredTitle("三网回程路由检测", width) |  | ||||||
| 					ntrace.TraceRoute3(language, nt3Location, nt3CheckType) |  | ||||||
| 				} |  | ||||||
| 			}, tempOutput, output) |  | ||||||
| 			output = utils.PrintAndCapture(func() { |  | ||||||
| 				if onlyChinaTest || pingTestStatus { |  | ||||||
| 					utils.PrintCenteredTitle("三网ICMP的PING值检测", width) |  | ||||||
| 					wg3.Wait() |  | ||||||
| 					fmt.Println(ptInfo) |  | ||||||
| 				} |  | ||||||
| 			}, tempOutput, output) |  | ||||||
| 		} |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if speedTestStatus { |  | ||||||
| 				utils.PrintCenteredTitle("就近节点测速", width) |  | ||||||
| 				speedtest.ShowHead(language) |  | ||||||
| 				if choice == "1" || !menuMode { |  | ||||||
| 					speedtest.NearbySP() |  | ||||||
| 					speedtest.CustomSP("net", "global", 2, language) |  | ||||||
| 					speedtest.CustomSP("net", "cu", spNum, language) |  | ||||||
| 					speedtest.CustomSP("net", "ct", spNum, language) |  | ||||||
| 					speedtest.CustomSP("net", "cmcc", spNum, language) |  | ||||||
| 				} else if choice == "2" || choice == "3" || choice == "4" || choice == "5" { |  | ||||||
| 					speedtest.CustomSP("net", "global", 4, language) |  | ||||||
| 				} else if choice == "6" { |  | ||||||
| 					speedtest.CustomSP("net", "global", 11, language) |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		endTime := time.Now() |  | ||||||
| 		duration := endTime.Sub(startTime) |  | ||||||
| 		minutes := int(duration.Minutes()) |  | ||||||
| 		seconds := int(duration.Seconds()) % 60 |  | ||||||
| 		currentTime := time.Now().Format("Mon Jan 2 15:04:05 MST 2006") |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			utils.PrintCenteredTitle("", width) |  | ||||||
| 			fmt.Printf("花费          : %d 分 %d 秒\n", minutes, seconds) |  | ||||||
| 			fmt.Printf("时间          : %s\n", currentTime) |  | ||||||
| 			utils.PrintCenteredTitle("", width) |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 	case "en": |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			utils.PrintHead(language, width, ecsVersion) |  | ||||||
| 			if basicStatus || securityTestStatus { |  | ||||||
| 				if basicStatus { |  | ||||||
| 					utils.PrintCenteredTitle("System-Basic-Information", width) |  | ||||||
| 				} |  | ||||||
| 				basicInfo, securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, nt3CheckType, securityTestStatus) |  | ||||||
| 				if basicStatus { |  | ||||||
| 					fmt.Printf(basicInfo) |  | ||||||
| 				} else if (input == "6" || input == "9") && securityTestStatus { |  | ||||||
| 					scanner := bufio.NewScanner(strings.NewReader(basicInfo)) |  | ||||||
| 					for scanner.Scan() { |  | ||||||
| 						line := scanner.Text() |  | ||||||
| 						if strings.Contains(line, "IPV") { |  | ||||||
| 							fmt.Println(line) |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if cpuTestStatus { |  | ||||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("CPU-Test--%s-Method", cpuTestMethod), width) |  | ||||||
| 				cputest.CpuTest(language, cpuTestMethod, cpuTestThreadMode) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if memoryTestStatus { |  | ||||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("Memory-Test--%s-Method", memoryTestMethod), width) |  | ||||||
| 				memorytest.MemoryTest(language, memoryTestMethod) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if diskTestStatus && autoChangeDiskTestMethod { |  | ||||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", diskTestMethod), width) |  | ||||||
| 				disktest.DiskTest(language, diskTestMethod, diskTestPath, diskMultiCheck, autoChangeDiskTestMethod) |  | ||||||
| 			} else if diskTestStatus && !autoChangeDiskTestMethod { |  | ||||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", "dd"), width) |  | ||||||
| 				disktest.DiskTest(language, "dd", diskTestPath, diskMultiCheck, autoChangeDiskTestMethod) |  | ||||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", "fio"), width) |  | ||||||
| 				disktest.DiskTest(language, "fio", diskTestPath, diskMultiCheck, autoChangeDiskTestMethod) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		if utTestStatus { |  | ||||||
| 			wg1.Add(1) |  | ||||||
| 			go func() { |  | ||||||
| 				defer wg1.Done() |  | ||||||
| 				mediaInfo = unlocktest.MediaTest(language) |  | ||||||
| 			}() |  | ||||||
| 		} |  | ||||||
| 		if emailTestStatus { |  | ||||||
| 			wg2.Add(1) |  | ||||||
| 			go func() { |  | ||||||
| 				defer wg2.Done() |  | ||||||
| 				emailInfo = email.EmailCheck() |  | ||||||
| 			}() |  | ||||||
| 		} |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if utTestStatus { |  | ||||||
| 				utils.PrintCenteredTitle("Cross-Border-Streaming-Media-Unlock", width) |  | ||||||
| 				wg1.Wait() |  | ||||||
| 				fmt.Printf(mediaInfo) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if securityTestStatus { |  | ||||||
| 				utils.PrintCenteredTitle("IP-Quality-Check", width) |  | ||||||
| 				fmt.Printf(securityInfo) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if emailTestStatus { |  | ||||||
| 				utils.PrintCenteredTitle("Email-Port-Check", width) |  | ||||||
| 				wg2.Wait() |  | ||||||
| 				fmt.Println(emailInfo) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			if speedTestStatus { |  | ||||||
| 				utils.PrintCenteredTitle("Speed-Test", width) |  | ||||||
| 				speedtest.ShowHead(language) |  | ||||||
| 				speedtest.NearbySP() |  | ||||||
| 				speedtest.CustomSP("net", "global", -1, language) |  | ||||||
| 			} |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 		endTime := time.Now() |  | ||||||
| 		duration := endTime.Sub(startTime) |  | ||||||
| 		minutes := int(duration.Minutes()) |  | ||||||
| 		seconds := int(duration.Seconds()) % 60 |  | ||||||
| 		currentTime := time.Now().Format("Mon Jan 2 15:04:05 MST 2006") |  | ||||||
| 		output = utils.PrintAndCapture(func() { |  | ||||||
| 			utils.PrintCenteredTitle("", width) |  | ||||||
| 			fmt.Printf("Cost    Time          : %d min %d sec\n", minutes, seconds) |  | ||||||
| 			fmt.Printf("Current Time          : %s\n", currentTime) |  | ||||||
| 			utils.PrintCenteredTitle("", width) |  | ||||||
| 		}, tempOutput, output) |  | ||||||
| 	default: |  | ||||||
| 		fmt.Println("Unsupported language") |  | ||||||
| 	} |  | ||||||
| 	httpURL, httpsURL := utils.ProcessAndUpload(output, filePath, enabelUpload) |  | ||||||
| 	if httpURL != "" || httpsURL != "" { |  | ||||||
| 		if language == "en" { |  | ||||||
| 			fmt.Printf("Upload successfully!\nHttp URL:  %s\nHttps URL: %s\n", httpURL, httpsURL) |  | ||||||
| 		} else { |  | ||||||
| 			fmt.Printf("上传成功!\nHttp URL:  %s\nHttps URL: %s\n", httpURL, httpsURL) |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	finish = true |  | ||||||
| 	if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { |  | ||||||
| 		fmt.Println("Press Enter to exit...") |  | ||||||
| 		fmt.Scanln() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
							
								
								
									
										700
									
								
								goecs.sh
									
									
									
									
									
								
							
							
						
						
									
										700
									
								
								goecs.sh
									
									
									
									
									
								
							| @@ -1,700 +0,0 @@ | |||||||
| #!/bin/bash |  | ||||||
| # From https://github.com/oneclickvirt/ecs |  | ||||||
| # 2024.12.08 |  | ||||||
|  |  | ||||||
| # curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh |  | ||||||
| # 或 |  | ||||||
| # curl -L https://cnb.cool/oneclickvirt/ecs/-/git/raw/main/goecs.sh -o goecs.sh && chmod +x goecs.sh |  | ||||||
|  |  | ||||||
| cat <<"EOF" |  | ||||||
|        GGGGGGGG        OOOOOOO         EEEEEEEE      CCCCCCCCC    SSSSSSSSSS |  | ||||||
|      GG        GG    OO       OO      EE           CC           SS |  | ||||||
|     GG              OO         OO     EE          CC           SS |  | ||||||
|     GG              OO         OO     EE          CC            SS |  | ||||||
|     GG              OO         OO     EEEEEEEE    CC             SSSSSSSSSS |  | ||||||
|     GG     GGGGGG   OO         OO     EE          CC                      SS |  | ||||||
|     GG        GG    OO         OO     EE          CC                       SS |  | ||||||
|      GG      GG      OO       OO      EE           CC                     SS |  | ||||||
|       GGGGGGGG         OOOOOOO         EEEEEEEE     CCCCCCCCC    SSSSSSSSSS |  | ||||||
| EOF |  | ||||||
|  |  | ||||||
| cd /root >/dev/null 2>&1 |  | ||||||
| if [ ! -d "/usr/bin/" ]; then |  | ||||||
|     mkdir -p "/usr/bin/" |  | ||||||
| fi |  | ||||||
| _red() { echo -e "\033[31m\033[01m$@\033[0m"; } |  | ||||||
| _green() { echo -e "\033[32m\033[01m$@\033[0m"; } |  | ||||||
| _yellow() { echo -e "\033[33m\033[01m$@\033[0m"; } |  | ||||||
| _blue() { echo -e "\033[36m\033[01m$@\033[0m"; } |  | ||||||
| reading() { read -rp "$(_green "$1")" "$2"; } |  | ||||||
|  |  | ||||||
| check_cdn() { |  | ||||||
|     local o_url=$1 |  | ||||||
|     for cdn_url in "${cdn_urls[@]}"; do |  | ||||||
|         if curl -sL -k "$cdn_url$o_url" --max-time 6 | grep -q "success" >/dev/null 2>&1; then |  | ||||||
|             export cdn_success_url="$cdn_url" |  | ||||||
|             return |  | ||||||
|         fi |  | ||||||
|         sleep 0.5 |  | ||||||
|     done |  | ||||||
|     export cdn_success_url="" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| check_cdn_file() { |  | ||||||
|     check_cdn "https://raw.githubusercontent.com/spiritLHLS/ecs/main/back/test" |  | ||||||
|     if [ -n "$cdn_success_url" ]; then |  | ||||||
|         _green "CDN available, using CDN" |  | ||||||
|     else |  | ||||||
|         _yellow "No CDN available, no use CDN" |  | ||||||
|     fi |  | ||||||
| } |  | ||||||
|  |  | ||||||
| download_file() { |  | ||||||
|     local url="$1" |  | ||||||
|     local output="$2" |  | ||||||
|     if ! wget -O "$output" "$url"; then |  | ||||||
|         _yellow "wget failed, trying curl..." |  | ||||||
|         if ! curl -L -o "$output" "$url"; then |  | ||||||
|             _red "Both wget and curl failed. Unable to download the file." |  | ||||||
|             return 1 |  | ||||||
|         fi |  | ||||||
|     fi |  | ||||||
|     return 0 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| check_china() { |  | ||||||
|     _yellow "正在检测IP所在区域......" |  | ||||||
|     if [[ -z "${CN}" ]]; then |  | ||||||
|         # 首先尝试通过 ipapi.co 检测 |  | ||||||
|         if curl -m 6 -s https://ipapi.co/json | grep -q 'China'; then |  | ||||||
|             _yellow "根据ipapi.co提供的信息,当前IP可能在中国" |  | ||||||
|             if [ "$noninteractive" != "true" ]; then |  | ||||||
|                 reading "是否使用中国镜像完成安装? ([y]/n) " input |  | ||||||
|                 case $input in |  | ||||||
|                     [yY][eE][sS] | [yY] | "") |  | ||||||
|                         _green "已选择使用中国镜像" |  | ||||||
|                         CN=true |  | ||||||
|                         ;; |  | ||||||
|                     [nN][oO] | [nN]) |  | ||||||
|                         _yellow "已选择不使用中国镜像" |  | ||||||
|                         CN=false |  | ||||||
|                         ;; |  | ||||||
|                     *) |  | ||||||
|                         _green "已选择使用中国镜像" |  | ||||||
|                         CN=true |  | ||||||
|                         ;; |  | ||||||
|                 esac |  | ||||||
|             else |  | ||||||
|                 # 在非交互模式下默认使用中国镜像 |  | ||||||
|                 CN=true |  | ||||||
|             fi |  | ||||||
|         else |  | ||||||
|             # 如果 ipapi.co 检测失败,尝试使用 cip.cc |  | ||||||
|             if ! curl -m 6 -s https://ipapi.co/json >/dev/null 2>&1; then |  | ||||||
|                 if curl -m 6 -s cip.cc | grep -q "中国"; then |  | ||||||
|                     _yellow "根据cip.cc提供的信息,当前IP可能在中国" |  | ||||||
|                     if [ "$noninteractive" != "true" ]; then |  | ||||||
|                         reading "是否使用中国镜像完成安装? ([y]/n) " input |  | ||||||
|                         case $input in |  | ||||||
|                             [yY][eE][sS] | [yY] | "") |  | ||||||
|                                 _green "已选择使用中国镜像" |  | ||||||
|                                 CN=true |  | ||||||
|                                 ;; |  | ||||||
|                             [nN][oO] | [nN]) |  | ||||||
|                                 _yellow "已选择不使用中国镜像" |  | ||||||
|                                 CN=false |  | ||||||
|                                 ;; |  | ||||||
|                             *) |  | ||||||
|                                 _green "已选择使用中国镜像" |  | ||||||
|                                 CN=true |  | ||||||
|                                 ;; |  | ||||||
|                         esac |  | ||||||
|                     else |  | ||||||
|                         # 在非交互模式下默认使用中国镜像 |  | ||||||
|                         CN=true |  | ||||||
|                     fi |  | ||||||
|                 else |  | ||||||
|                     CN=false |  | ||||||
|                 fi |  | ||||||
|             else |  | ||||||
|                 CN=false |  | ||||||
|             fi |  | ||||||
|         fi |  | ||||||
|     fi |  | ||||||
| } |  | ||||||
|  |  | ||||||
| get_memory_size() { |  | ||||||
|     if [ -f /proc/meminfo ]; then |  | ||||||
|         local mem_kb=$(grep MemTotal /proc/meminfo | awk '{print $2}') |  | ||||||
|         echo $((mem_kb / 1024)) # Convert to MB |  | ||||||
|         return |  | ||||||
|     fi |  | ||||||
|     if command -v free >/dev/null 2>&1; then |  | ||||||
|         local mem_kb=$(free -m | awk '/^Mem:/ {print $2}') |  | ||||||
|         echo "$mem_kb" # Already in MB |  | ||||||
|         return |  | ||||||
|     fi |  | ||||||
|     if command -v sysctl >/dev/null 2>&1; then |  | ||||||
|         local mem_bytes=$(sysctl -n hw.memsize 2>/dev/null || sysctl -n hw.physmem 2>/dev/null) |  | ||||||
|         if [ -n "$mem_bytes" ]; then |  | ||||||
|             echo $((mem_bytes / 1024 / 1024)) # Convert to MB |  | ||||||
|             return |  | ||||||
|         fi |  | ||||||
|     fi |  | ||||||
| } |  | ||||||
|  |  | ||||||
| cleanup_epel() { |  | ||||||
|     _yellow "Cleaning up EPEL repositories..." |  | ||||||
|     rm -f /etc/yum.repos.d/*epel* |  | ||||||
|     yum clean all |  | ||||||
| } |  | ||||||
|  |  | ||||||
| goecs_check() { |  | ||||||
|     # Get system and architecture info with error handling |  | ||||||
|     os=$(uname -s 2>/dev/null || echo "Unknown") |  | ||||||
|     arch=$(uname -m 2>/dev/null || echo "Unknown") |  | ||||||
|     # First check for China IP |  | ||||||
|     check_china |  | ||||||
|     # Get latest version number with multiple backup sources |  | ||||||
|     ECS_VERSION="" |  | ||||||
|     for api in \ |  | ||||||
|         "https://api.github.com/repos/oneclickvirt/ecs/releases/latest" \ |  | ||||||
|         "https://githubapi.spiritlhl.workers.dev/repos/oneclickvirt/ecs/releases/latest" \ |  | ||||||
|         "https://githubapi.spiritlhl.top/repos/oneclickvirt/ecs/releases/latest"; do |  | ||||||
|         ECS_VERSION=$(curl -m 6 -sSL "$api" | awk -F \" '/tag_name/{gsub(/^v/,"",$4); print $4}') |  | ||||||
|         if [ -n "$ECS_VERSION" ]; then |  | ||||||
|             break |  | ||||||
|         fi |  | ||||||
|         sleep 1 |  | ||||||
|     done |  | ||||||
|     if [ -z "$ECS_VERSION" ]; then |  | ||||||
|         _yellow "Unable to get version info, using default version 0.1.3" |  | ||||||
|         ECS_VERSION="0.1.3" |  | ||||||
|     fi |  | ||||||
|     # Check if original goecs command exists |  | ||||||
|     version_output="" |  | ||||||
|     for cmd_path in "goecs" "./goecs" "/usr/bin/goecs" "/usr/local/bin/goecs"; do |  | ||||||
|         if [ -x "$(command -v $cmd_path 2>/dev/null)" ]; then |  | ||||||
|             version_output=$($cmd_path -v command 2>/dev/null) |  | ||||||
|             break |  | ||||||
|         fi |  | ||||||
|     done |  | ||||||
|     if [ -n "$version_output" ]; then |  | ||||||
|         extracted_version=${version_output//v/} |  | ||||||
|         if [ -n "$extracted_version" ]; then |  | ||||||
|             ecs_version=$ECS_VERSION |  | ||||||
|             if [[ "$(echo -e "$extracted_version\n$ecs_version" | sort -V | tail -n 1)" == "$extracted_version" ]]; then |  | ||||||
|                 _green "goecs version ($extracted_version) is up to date, no upgrade needed" |  | ||||||
|                 return |  | ||||||
|             else |  | ||||||
|                 _yellow "goecs version ($extracted_version) < $ecs_version, upgrade needed, starting in 5 seconds" |  | ||||||
|                 rm -rf /usr/bin/goecs /usr/local/bin/goecs ./goecs |  | ||||||
|             fi |  | ||||||
|         fi |  | ||||||
|     else |  | ||||||
|         _green "goecs not found, installation needed, starting in 5 seconds" |  | ||||||
|     fi |  | ||||||
|     sleep 5 |  | ||||||
|     # Download corresponding version with error handling |  | ||||||
|     if [[ "$CN" == true ]]; then |  | ||||||
|         _yellow "Using China mirror for download..." |  | ||||||
|         base_url="https://cnb.cool/oneclickvirt/ecs/-/git/raw/main" |  | ||||||
|     else |  | ||||||
|         cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn3.spiritlhl.net/" "http://cdn1.spiritlhl.net/" "http://cdn2.spiritlhl.net/") |  | ||||||
|         check_cdn_file |  | ||||||
|         if [ -n "$cdn_success_url" ]; then |  | ||||||
|             base_url="${cdn_success_url}https://github.com/oneclickvirt/ecs/releases/download/v${ECS_VERSION}" |  | ||||||
|         else |  | ||||||
|             base_url="https://github.com/oneclickvirt/ecs/releases/download/v${ECS_VERSION}" |  | ||||||
|         fi |  | ||||||
|     fi |  | ||||||
|     # Build download URL with architecture support |  | ||||||
|     local zip_file="" |  | ||||||
|     case $os in |  | ||||||
|         Linux|linux|LINUX) |  | ||||||
|             case $arch in |  | ||||||
|                 x86_64|amd64|x64) zip_file="goecs_linux_amd64.zip" ;; |  | ||||||
|                 i386|i686) zip_file="goecs_linux_386.zip" ;; |  | ||||||
|                 aarch64|arm64|armv8|armv8l) zip_file="goecs_linux_arm64.zip" ;; |  | ||||||
|                 arm|armv7l) zip_file="goecs_linux_arm.zip" ;; |  | ||||||
|                 mips) zip_file="goecs_linux_mips.zip" ;; |  | ||||||
|                 mipsle) zip_file="goecs_linux_mipsle.zip" ;; |  | ||||||
|                 s390x) zip_file="goecs_linux_s390x.zip" ;; |  | ||||||
|                 riscv64) zip_file="goecs_linux_riscv64.zip" ;; |  | ||||||
|                 *) zip_file="goecs_linux_amd64.zip" ;; |  | ||||||
|             esac |  | ||||||
|             ;; |  | ||||||
|         FreeBSD|freebsd) |  | ||||||
|             case $arch in |  | ||||||
|                 x86_64|amd64) zip_file="goecs_freebsd_amd64.zip" ;; |  | ||||||
|                 i386|i686) zip_file="goecs_freebsd_386.zip" ;; |  | ||||||
|                 arm64|aarch64) zip_file="goecs_freebsd_arm64.zip" ;; |  | ||||||
|                 *) zip_file="goecs_freebsd_amd64.zip" ;; |  | ||||||
|             esac |  | ||||||
|             ;; |  | ||||||
|         Darwin|darwin) |  | ||||||
|             case $arch in |  | ||||||
|                 x86_64|amd64) zip_file="goecs_darwin_amd64.zip" ;; |  | ||||||
|                 arm64|aarch64) zip_file="goecs_darwin_arm64.zip" ;; |  | ||||||
|                 *) zip_file="goecs_darwin_amd64.zip" ;; |  | ||||||
|             esac |  | ||||||
|             ;; |  | ||||||
|         *) |  | ||||||
|             _yellow "Unknown system $os, trying amd64 version" |  | ||||||
|             zip_file="goecs_linux_amd64.zip" |  | ||||||
|             ;; |  | ||||||
|     esac |  | ||||||
|     download_url="${base_url}/${zip_file}" |  | ||||||
|     _green "Downloading $download_url" |  | ||||||
|     # Download file with retry mechanism |  | ||||||
|     local max_retries=3 |  | ||||||
|     local retry_count=0 |  | ||||||
|     while [ $retry_count -lt $max_retries ]; do |  | ||||||
|         if download_file "$download_url" "goecs.zip"; then |  | ||||||
|             break |  | ||||||
|         fi |  | ||||||
|         _yellow "Download failed, retrying (${retry_count}/${max_retries})..." |  | ||||||
|         retry_count=$((retry_count + 1)) |  | ||||||
|         sleep 2 |  | ||||||
|     done |  | ||||||
|     if [ $retry_count -eq $max_retries ]; then |  | ||||||
|         _red "Download failed, please check your network connection or download manually" |  | ||||||
|         return 1 |  | ||||||
|     fi |  | ||||||
|     # Extract and install with error handling |  | ||||||
|     if ! unzip -o goecs.zip >/dev/null 2>&1; then |  | ||||||
|         _red "Extraction failed" |  | ||||||
|         return 1 |  | ||||||
|     fi |  | ||||||
|     rm -f goecs.zip README.md LICENSE README_EN.md |  | ||||||
|     # Set execution permissions and install |  | ||||||
|     chmod 777 goecs |  | ||||||
|     for install_path in "/usr/bin" "/usr/local/bin"; do |  | ||||||
|         if [ -d "$install_path" ]; then |  | ||||||
|             cp -f goecs "$install_path/" |  | ||||||
|             break |  | ||||||
|         fi |  | ||||||
|     done |  | ||||||
|     # Set system parameters |  | ||||||
|     if [ "$os" != "Darwin" ]; then |  | ||||||
|         PARAM="net.ipv4.ping_group_range" |  | ||||||
|         NEW_VALUE="0 2147483647" |  | ||||||
|         if [ -f /etc/sysctl.conf ]; then |  | ||||||
|             if grep -q "^$PARAM" /etc/sysctl.conf; then |  | ||||||
|                 sed -i "s/^$PARAM.*/$PARAM = $NEW_VALUE/" /etc/sysctl.conf |  | ||||||
|             else |  | ||||||
|                 echo "$PARAM = $NEW_VALUE" >> /etc/sysctl.conf |  | ||||||
|             fi |  | ||||||
|             sysctl -p >/dev/null 2>&1 |  | ||||||
|         fi |  | ||||||
|     fi |  | ||||||
|     # Set special permissions |  | ||||||
|     setcap cap_net_raw=+ep goecs 2>/dev/null |  | ||||||
|     setcap cap_net_raw=+ep /usr/bin/goecs 2>/dev/null |  | ||||||
|     setcap cap_net_raw=+ep /usr/local/bin/goecs 2>/dev/null |  | ||||||
|     _green "goecs installation complete, current version:" |  | ||||||
|     goecs -v || ./goecs -v |  | ||||||
| } |  | ||||||
|  |  | ||||||
| InstallSysbench() { |  | ||||||
|     if [ -f "/etc/opencloudos-release" ]; then # OpenCloudOS |  | ||||||
|         Var_OSRelease="opencloudos" |  | ||||||
|     elif [ -f "/etc/centos-release" ]; then # CentOS |  | ||||||
|         Var_OSRelease="centos" |  | ||||||
|     elif [ -f "/etc/fedora-release" ]; then # Fedora |  | ||||||
|         Var_OSRelease="fedora" |  | ||||||
|     elif [ -f "/etc/redhat-release" ]; then # RedHat |  | ||||||
|         Var_OSRelease="rhel" |  | ||||||
|     elif [ -f "/etc/astra_version" ]; then # Astra |  | ||||||
|         Var_OSRelease="astra" |  | ||||||
|     elif [ -f "/etc/lsb-release" ]; then # Ubuntu |  | ||||||
|         Var_OSRelease="ubuntu" |  | ||||||
|     elif [ -f "/etc/debian_version" ]; then # Debian |  | ||||||
|         Var_OSRelease="debian" |  | ||||||
|     elif [ -f "/etc/alpine-release" ]; then # Alpine Linux |  | ||||||
|         Var_OSRelease="alpinelinux" |  | ||||||
|     elif [ -f "/etc/almalinux-release" ]; then # almalinux |  | ||||||
|         Var_OSRelease="almalinux" |  | ||||||
|     elif [ -f "/etc/arch-release" ]; then # archlinux |  | ||||||
|         Var_OSRelease="arch" |  | ||||||
|     elif [ -f "/etc/freebsd-update.conf" ]; then # freebsd |  | ||||||
|         Var_OSRelease="freebsd" |  | ||||||
|     else |  | ||||||
|         Var_OSRelease="unknown" # 未知系统分支 |  | ||||||
|     fi |  | ||||||
|     local mem_size=$(get_memory_size) |  | ||||||
|     if [ -z "$mem_size" ] || [ "$mem_size" -eq 0 ]; then |  | ||||||
|         echo "Error: Unable to determine memory size or memory size is zero." |  | ||||||
|     elif [ $mem_size -lt 1024 ]; then |  | ||||||
|         _red "Warning: Your system has less than 1GB RAM (${mem_size}MB)" |  | ||||||
|         if [ "$noninteractive" != "true" ]; then |  | ||||||
|             reading "Do you want to continue with EPEL installation? (y/N): " confirm |  | ||||||
|             if [[ ! $confirm =~ ^[Yy]$ ]]; then |  | ||||||
|                 _yellow "Skipping EPEL installation" |  | ||||||
|                 return 1 |  | ||||||
|             fi |  | ||||||
|         fi |  | ||||||
|         case "$Var_OSRelease" in |  | ||||||
|         ubuntu | debian | astra)  |  | ||||||
|             ! apt-get install -y sysbench && apt-get --fix-broken install -y && apt-get install --no-install-recommends -y sysbench ;; |  | ||||||
|         centos | rhel | almalinux | redhat | opencloudos)  |  | ||||||
|             (yum -y install epel-release && yum -y install sysbench) || (dnf install epel-release -y && dnf install sysbench -y) ;; |  | ||||||
|         fedora)  |  | ||||||
|             dnf -y install sysbench ;; |  | ||||||
|         arch)  |  | ||||||
|             pacman -S --needed --noconfirm sysbench && pacman -S --needed --noconfirm libaio && ldconfig ;; |  | ||||||
|         freebsd)  |  | ||||||
|             pkg install -y sysbench ;; |  | ||||||
|         alpinelinux) |  | ||||||
|             if [ "$noninteractive" != "true" ]; then |  | ||||||
|                 reading "Do you want to continue with sysbench installation? (y/N): " confirm |  | ||||||
|                 if [[ ! $confirm =~ ^[Yy]$ ]]; then |  | ||||||
|                     _yellow "Skipping sysbench installation" |  | ||||||
|                     return 1 |  | ||||||
|                 fi |  | ||||||
|             fi |  | ||||||
|             ALPINE_VERSION=$(grep -o '^[0-9]\+\.[0-9]\+' /etc/alpine-release) |  | ||||||
|             COMMUNITY_REPO="http://dl-cdn.alpinelinux.org/alpine/v${ALPINE_VERSION}/community" |  | ||||||
|             if grep -q "^${COMMUNITY_REPO}" /etc/apk/repositories; then |  | ||||||
|                 echo "Community repository is already enabled." |  | ||||||
|             else |  | ||||||
|                 echo "Enabling community repository..." |  | ||||||
|                 echo "${COMMUNITY_REPO}" >> /etc/apk/repositories |  | ||||||
|                 echo "Community repository has been added." |  | ||||||
|                 echo "Updating apk package index..." |  | ||||||
|                 apk update && echo "Package index updated successfully." |  | ||||||
|             fi |  | ||||||
|             if apk info sysbench >/dev/null 2>&1; then |  | ||||||
|                 echo -e "${Msg_Info}Sysbench already installed." |  | ||||||
|             else |  | ||||||
|                 apk add --no-cache sysbench |  | ||||||
|                 if [ $? -ne 0 ]; then |  | ||||||
|                     echo -e "${Msg_Warning}Sysbench Module not found, installing ..." && echo -e "${Msg_Warning}SysBench Current not support Alpine Linux, Skipping..." && Var_Skip_SysBench="1" |  | ||||||
|                 else |  | ||||||
|                     echo -e "${Msg_Success}Sysbench installed successfully." |  | ||||||
|                 fi |  | ||||||
|             fi ;; |  | ||||||
|         *)  |  | ||||||
|             _red "Sysbench Install Error: Unknown OS release: $Var_OSRelease" ;; |  | ||||||
|         esac |  | ||||||
|         if [[ $SYSTEM =~ ^(CentOS|RHEL|AlmaLinux)$ ]]; then |  | ||||||
|         _yellow "Installing EPEL repository..." |  | ||||||
|             if ! yum -y install epel-release; then |  | ||||||
|                 _red "EPEL installation failed!" |  | ||||||
|                 cleanup_epel |  | ||||||
|                 _yellow "Attempting to continue without EPEL..." |  | ||||||
|             fi |  | ||||||
|         fi |  | ||||||
|     fi |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Check_SysBench() { |  | ||||||
|     if [ ! -f "/usr/bin/sysbench" ] && [ ! -f "/usr/local/bin/sysbench" ]; then |  | ||||||
|         InstallSysbench |  | ||||||
|     fi |  | ||||||
|     # 尝试编译安装 |  | ||||||
|     if [ ! -f "/usr/bin/sysbench" ] && [ ! -f "/usr/local/bin/sysbench" ]; then |  | ||||||
|         echo -e "${Msg_Warning}Sysbench Module install Failure, trying compile modules ..." |  | ||||||
|         Check_Sysbench_InstantBuild |  | ||||||
|     fi |  | ||||||
|     source ~/.bashrc |  | ||||||
|     # 最终检测 |  | ||||||
|     if [ "$(command -v sysbench)" ] || [ -f "/usr/bin/sysbench" ] || [ -f "/usr/local/bin/sysbench" ]; then |  | ||||||
|         _yellow "Install sysbench successfully!" |  | ||||||
|     else |  | ||||||
|         _red "SysBench Moudle install Failure! Try Restart Bench or Manually install it! (/usr/bin/sysbench)" |  | ||||||
|         _blue "Will try to test with geekbench5 instead later." |  | ||||||
|     fi |  | ||||||
|     sleep 3 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| Check_Sysbench_InstantBuild() { |  | ||||||
|     if [ "${Var_OSRelease}" = "centos" ] || [ "${Var_OSRelease}" = "rhel" ] || [ "${Var_OSRelease}" = "almalinux" ] || [ "${Var_OSRelease}" = "ubuntu" ] || [ "${Var_OSRelease}" = "debian" ] || [ "${Var_OSRelease}" = "fedora" ] || [ "${Var_OSRelease}" = "arch" ] || [ "${Var_OSRelease}" = "astra" ]; then |  | ||||||
|         local os_sysbench=${Var_OSRelease} |  | ||||||
|         if [ "$os_sysbench" = "astra" ]; then |  | ||||||
|             os_sysbench="debian" |  | ||||||
|         fi |  | ||||||
|         if [ "$os_sysbench" = "opencloudos" ]; then |  | ||||||
|             os_sysbench="centos" |  | ||||||
|         fi |  | ||||||
|         echo -e "${Msg_Info}Release Detected: ${os_sysbench}" |  | ||||||
|         echo -e "${Msg_Info}Preparing compile enviorment ..." |  | ||||||
|         prepare_compile_env "${os_sysbench}" |  | ||||||
|         echo -e "${Msg_Info}Downloading Source code (Version 1.0.20)..." |  | ||||||
|         mkdir -p /tmp/sysbench_install/src/ |  | ||||||
|         mv /tmp/sysbench-1.0.20 /tmp/sysbench_install/src/ |  | ||||||
|         echo -e "${Msg_Info}Compiling Sysbench Module ..." |  | ||||||
|         cd /tmp/sysbench_install/src/sysbench-1.0.20 |  | ||||||
|         ./autogen.sh && ./configure --without-mysql && make -j8 && make install |  | ||||||
|         echo -e "${Msg_Info}Cleaning up ..." |  | ||||||
|         cd /tmp && rm -rf /tmp/sysbench_install/src/sysbench* |  | ||||||
|     else |  | ||||||
|         echo -e "${Msg_Warning}Unsupported operating system: ${Var_OSRelease}" |  | ||||||
|     fi |  | ||||||
| } |  | ||||||
|  |  | ||||||
| prepare_compile_env() { |  | ||||||
|     local system="$1" |  | ||||||
|     if [ "${system}" = "centos" ] || [ "${system}" = "rhel" ] || [ "${system}" = "almalinux" ]; then |  | ||||||
|         yum install -y epel-release |  | ||||||
|         yum install -y wget curl make gcc gcc-c++ make automake libtool pkgconfig libaio-devel |  | ||||||
|     elif [ "${system}" = "ubuntu" ] || [ "${system}" = "debian" ]; then |  | ||||||
|         ! apt-get update && apt-get --fix-broken install -y && apt-get update |  | ||||||
|         ! apt-get -y install --no-install-recommends curl wget make automake libtool pkg-config libaio-dev unzip && apt-get --fix-broken install -y && apt-get -y install --no-install-recommends curl wget make automake libtool pkg-config libaio-dev unzip |  | ||||||
|     elif [ "${system}" = "fedora" ]; then |  | ||||||
|         dnf install -y wget curl gcc gcc-c++ make automake libtool pkgconfig libaio-devel |  | ||||||
|     elif [ "${system}" = "arch" ]; then |  | ||||||
|         pacman -S --needed --noconfirm wget curl gcc gcc make automake libtool pkgconfig libaio lib32-libaio |  | ||||||
|     else |  | ||||||
|         echo -e "${Msg_Warning}Unsupported operating system: ${system}" |  | ||||||
|     fi |  | ||||||
| } |  | ||||||
|  |  | ||||||
| env_check() { |  | ||||||
|     REGEX=("debian|astra" "ubuntu" "centos|red hat|kernel|oracle linux|alma|rocky" "'amazon linux'" "fedora" "arch" "freebsd" "alpine" "openbsd" "opencloudos") |  | ||||||
|     RELEASE=("Debian" "Ubuntu" "CentOS" "CentOS" "Fedora" "Arch" "FreeBSD" "Alpine" "OpenBSD" "OpenCloudOS") |  | ||||||
|     PACKAGE_UPDATE=("apt-get update" "apt-get update" "yum -y update" "yum -y update" "yum -y update" "pacman -Sy" "pkg update" "apk update" "pkg_add -qu" "yum -y update") |  | ||||||
|     PACKAGE_INSTALL=("apt-get -y install" "apt-get -y install" "yum -y install" "yum -y install" "yum -y install" "pacman -Sy --noconfirm --needed" "pkg install -y" "apk add --no-cache" "pkg_add -I" "yum -y install") |  | ||||||
|     PACKAGE_REMOVE=("apt-get -y remove" "apt-get -y remove" "yum -y remove" "yum -y remove" "yum -y remove" "pacman -Rsc --noconfirm" "pkg delete" "apk del" "pkg_delete -I" "yum -y remove") |  | ||||||
|     PACKAGE_UNINSTALL=("apt-get -y autoremove" "apt-get -y autoremove" "yum -y autoremove" "yum -y autoremove" "yum -y autoremove" "pacman -Rns --noconfirm" "pkg autoremove" "apk autoremove" "pkg_delete -a" "yum -y autoremove") |  | ||||||
|     # Check system information |  | ||||||
|     if [ -f /etc/opencloudos-release ]; then |  | ||||||
|         SYS="opencloudos" |  | ||||||
|     elif [ -s /etc/os-release ]; then |  | ||||||
|         SYS="$(grep -i pretty_name /etc/os-release | cut -d \" -f2)" |  | ||||||
|     elif [ -x "$(type -p hostnamectl)" ]; then |  | ||||||
|         SYS="$(hostnamectl | grep -i system | cut -d : -f2 | xargs)" |  | ||||||
|     elif [ -x "$(type -p lsb_release)" ]; then |  | ||||||
|         SYS="$(lsb_release -sd)" |  | ||||||
|     elif [ -s /etc/lsb-release ]; then |  | ||||||
|         SYS="$(grep -i description /etc/lsb-release | cut -d \" -f2)" |  | ||||||
|     elif [ -s /etc/redhat-release ]; then |  | ||||||
|         SYS="$(grep . /etc/redhat-release)" |  | ||||||
|     elif [ -s /etc/issue ]; then |  | ||||||
|         SYS="$(grep . /etc/issue | cut -d '\' -f1 | sed '/^[ ]*$/d')" |  | ||||||
|     else |  | ||||||
|         SYS="$(uname -s)" |  | ||||||
|     fi |  | ||||||
|     # Match operating system |  | ||||||
|     SYSTEM="" |  | ||||||
|     for ((int = 0; int < ${#REGEX[@]}; int++)); do |  | ||||||
|         if [[ $(echo "$SYS" | tr '[:upper:]' '[:lower:]') =~ ${REGEX[int]} ]]; then |  | ||||||
|             SYSTEM="${RELEASE[int]}" |  | ||||||
|             UPDATE_CMD=${PACKAGE_UPDATE[int]} |  | ||||||
|             INSTALL_CMD=${PACKAGE_INSTALL[int]} |  | ||||||
|             REMOVE_CMD=${PACKAGE_REMOVE[int]} |  | ||||||
|             UNINSTALL_CMD=${PACKAGE_UNINSTALL[int]} |  | ||||||
|             break |  | ||||||
|         fi |  | ||||||
|     done |  | ||||||
|     # If system is unrecognized, try common package managers |  | ||||||
|     if [ -z "$SYSTEM" ]; then |  | ||||||
|         _yellow "Unable to recognize system, trying common package managers..." |  | ||||||
|         # Try apt |  | ||||||
|         if command -v apt-get >/dev/null 2>&1; then |  | ||||||
|             SYSTEM="Unknown-Debian" |  | ||||||
|             UPDATE_CMD="apt-get update" |  | ||||||
|             INSTALL_CMD="apt-get -y install" |  | ||||||
|             REMOVE_CMD="apt-get -y remove" |  | ||||||
|             UNINSTALL_CMD="apt-get -y autoremove" |  | ||||||
|         # Try yum |  | ||||||
|         elif command -v yum >/dev/null 2>&1; then |  | ||||||
|             SYSTEM="Unknown-RHEL" |  | ||||||
|             UPDATE_CMD="yum -y update" |  | ||||||
|             INSTALL_CMD="yum -y install" |  | ||||||
|             REMOVE_CMD="yum -y remove" |  | ||||||
|             UNINSTALL_CMD="yum -y autoremove" |  | ||||||
|         # Try dnf |  | ||||||
|         elif command -v dnf >/dev/null 2>&1; then |  | ||||||
|             SYSTEM="Unknown-Fedora" |  | ||||||
|             UPDATE_CMD="dnf -y update" |  | ||||||
|             INSTALL_CMD="dnf -y install" |  | ||||||
|             REMOVE_CMD="dnf -y remove" |  | ||||||
|             UNINSTALL_CMD="dnf -y autoremove" |  | ||||||
|         # Try pacman |  | ||||||
|         elif command -v pacman >/dev/null 2>&1; then |  | ||||||
|             SYSTEM="Unknown-Arch" |  | ||||||
|             UPDATE_CMD="pacman -Sy" |  | ||||||
|             INSTALL_CMD="pacman -S --noconfirm" |  | ||||||
|             REMOVE_CMD="pacman -R --noconfirm" |  | ||||||
|             UNINSTALL_CMD="pacman -Rns --noconfirm" |  | ||||||
|         # Try apk |  | ||||||
|         elif command -v apk >/dev/null 2>&1; then |  | ||||||
|             SYSTEM="Unknown-Alpine" |  | ||||||
|             UPDATE_CMD="apk update" |  | ||||||
|             INSTALL_CMD="apk add" |  | ||||||
|             REMOVE_CMD="apk del" |  | ||||||
|             UNINSTALL_CMD="apk del" |  | ||||||
|         else |  | ||||||
|             _red "Unable to recognize package manager, exiting installation" |  | ||||||
|             exit 1 |  | ||||||
|         fi |  | ||||||
|     fi |  | ||||||
|     _green "System information: $SYSTEM" |  | ||||||
|     _green "Update command: $UPDATE_CMD" |  | ||||||
|     _green "Install command: $INSTALL_CMD" |  | ||||||
|     cdn_urls=("https://cdn0.spiritlhl.top/" "http://cdn3.spiritlhl.net/" "http://cdn1.spiritlhl.net/" "http://cdn2.spiritlhl.net/") |  | ||||||
|     check_cdn_file |  | ||||||
|     _yellow "Warning: System update will be performed" |  | ||||||
|     _yellow "This operation may:" |  | ||||||
|     _yellow "1. Take considerable time" |  | ||||||
|     _yellow "2. Cause temporary network interruptions" |  | ||||||
|     _yellow "3. Impact system stability" |  | ||||||
|     _yellow "4. Affect subsequent system startups" |  | ||||||
|     if [ "$noninteractive" != "true" ]; then |  | ||||||
|         reading "Continue with system update? (y/N): " update_confirm |  | ||||||
|         if [[ ! $update_confirm =~ ^[Yy]$ ]]; then |  | ||||||
|             _yellow "Skipping system update" |  | ||||||
|             _yellow "Note: Some packages may fail to install" |  | ||||||
|         else |  | ||||||
|             _green "Updating system package manager..." |  | ||||||
|             if ! ${UPDATE_CMD} 2>/dev/null; then |  | ||||||
|                 _red "System update failed!" |  | ||||||
|             fi |  | ||||||
|         fi |  | ||||||
|     fi |  | ||||||
|     # Install necessary commands |  | ||||||
|     for cmd in sudo wget tar unzip iproute2 systemd-detect-virt dd fio; do |  | ||||||
|         if ! command -v "$cmd" >/dev/null 2>&1; then |  | ||||||
|             _green "Installing $cmd" |  | ||||||
|             ${INSTALL_CMD} "$cmd" |  | ||||||
|         fi |  | ||||||
|     done |  | ||||||
|     # sysbench installation |  | ||||||
|     if ! command -v sysbench >/dev/null 2>&1; then |  | ||||||
|         _green "Installing sysbench" |  | ||||||
|         ${INSTALL_CMD} sysbench |  | ||||||
|         if [ $? -ne 0 ]; then |  | ||||||
|             echo "Unable to download sysbench through package manager, attempting compilation..." |  | ||||||
|             wget -O /tmp/sysbench.zip "${cdn_success_url}https://github.com/akopytov/sysbench/archive/1.0.20.zip" || curl -Lk -o /tmp/sysbench.zip "${cdn_success_url}https://github.com/akopytov/sysbench/archive/1.0.20.zip" |  | ||||||
|             if [ ! -f /tmp/sysbench.zip ]; then |  | ||||||
|                 wget -q -O /tmp/sysbench.zip "https://hub.fgit.cf/akopytov/sysbench/archive/1.0.20.zip" |  | ||||||
|             fi |  | ||||||
|             chmod +x /tmp/sysbench.zip |  | ||||||
|             unzip /tmp/sysbench.zip -d /tmp |  | ||||||
|             Check_SysBench |  | ||||||
|         fi |  | ||||||
|     fi |  | ||||||
|     # geekbench and speedtest installation |  | ||||||
|     if ! command -v geekbench >/dev/null 2>&1; then |  | ||||||
|         _green "Installing geekbench" |  | ||||||
|         curl -L "${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/cputest/main/dgb.sh" -o dgb.sh && chmod +x dgb.sh |  | ||||||
|         bash dgb.sh -v gb5 |  | ||||||
|         rm -rf dgb.sh |  | ||||||
|     fi |  | ||||||
|     if ! command -v speedtest >/dev/null 2>&1; then |  | ||||||
|         _green "Installing speedtest" |  | ||||||
|         curl -L "${cdn_success_url}https://raw.githubusercontent.com/oneclickvirt/speedtest/main/dspt.sh" -o dspt.sh && chmod +x dspt.sh |  | ||||||
|         bash dspt.sh |  | ||||||
|         rm -rf dspt.sh |  | ||||||
|         rm -rf speedtest.tar.gz |  | ||||||
|     fi |  | ||||||
|     if ! command -v ping >/dev/null 2>&1; then |  | ||||||
|         _green "Installing ping" |  | ||||||
|         ${INSTALL_CMD} iputils-ping >/dev/null 2>&1 |  | ||||||
|         ${INSTALL_CMD} ping >/dev/null 2>&1 |  | ||||||
|     fi |  | ||||||
|     # MacOS support |  | ||||||
|     if [ "$(uname -s)" = "Darwin" ]; then |  | ||||||
|         echo "Detected MacOS, installing sysbench iproute2mac fio..." |  | ||||||
|         brew install --force sysbench iproute2mac fio |  | ||||||
|     else |  | ||||||
|         if ! grep -q "^net.ipv4.ping_group_range = 0 2147483647$" /etc/sysctl.conf; then |  | ||||||
|             echo "net.ipv4.ping_group_range = 0 2147483647" >> /etc/sysctl.conf |  | ||||||
|             sysctl -p |  | ||||||
|         fi |  | ||||||
|     fi |  | ||||||
|     _green "Environment preparation complete." |  | ||||||
|     _green "Next command is: ./goecs.sh install" |  | ||||||
| } |  | ||||||
|  |  | ||||||
| uninstall_goecs() { |  | ||||||
|     rm -rf /root/goecs |  | ||||||
|     rm -rf /usr/bin/goecs |  | ||||||
|     _green "The command (goecs) has been uninstalled." |  | ||||||
| } |  | ||||||
|  |  | ||||||
| show_help() { |  | ||||||
|     cat <<"EOF" |  | ||||||
| 可用命令: |  | ||||||
|  |  | ||||||
| ./goecs.sh env            检查并安装依赖包 |  | ||||||
|                           警告: 此命令会执行系统更新(可选择),可能: |  | ||||||
|                           1. 耗时较长 |  | ||||||
|                           2. 导致网络短暂中断 |  | ||||||
|                           3. 影响系统稳定性 |  | ||||||
|                           4. 影响后续系统启动 |  | ||||||
|                           对于内存小于1GB的系统,还可能导致: |  | ||||||
|                           1. 系统卡死 |  | ||||||
|                           2. SSH连接中断 |  | ||||||
|                           3. 关键服务失败 |  | ||||||
|                           推荐: |  | ||||||
|                           环境依赖安装过程中挂起执行 |  | ||||||
|  |  | ||||||
|                           必需组件: |  | ||||||
|                           sysbench/geekbench (CPU性能测试必需) |  | ||||||
|                            |  | ||||||
|                           可选组件: |  | ||||||
|                           sudo, tar, unzip, dd, fio |  | ||||||
|                           speedtest (网络测试) |  | ||||||
|                           ping (网络连通性测试) |  | ||||||
|                           systemd-detect-virt/dmidecode (系统信息检测) |  | ||||||
|  |  | ||||||
| ./goecs.sh install        安装 goecs 命令 |  | ||||||
| ./goecs.sh upgrade        升级 goecs 命令 |  | ||||||
| ./goecs.sh uninstall      卸载 goecs 命令 |  | ||||||
| ./goecs.sh help           显示此消息 |  | ||||||
|  |  | ||||||
| [English version follows...] |  | ||||||
|  |  | ||||||
| Available commands: |  | ||||||
|  |  | ||||||
| ./goecs.sh env             Check and Install dependencies |  | ||||||
|                            Warning: This command performs system update(optional), which may: |  | ||||||
|                            1. Take considerable time |  | ||||||
|                            2. Cause temporary network interruptions |  | ||||||
|                            3. Impact system stability |  | ||||||
|                            4. Affect subsequent system startups |  | ||||||
|                            For systems with less than 1GB RAM, additional risks: |  | ||||||
|                            1. System freeze |  | ||||||
|                            2. SSH connection loss |  | ||||||
|                            3. Critical service failures |  | ||||||
|                            Recommended: |  | ||||||
|                            Hanging execution during environment dependency installation |  | ||||||
|                             |  | ||||||
|                            Required components: |  | ||||||
|                            sysbench/geekbench (Required for CPU testing) |  | ||||||
|                             |  | ||||||
|                            Optional components: |  | ||||||
|                            sudo, tar, unzip, dd, fio |  | ||||||
|                            speedtest (Network testing) |  | ||||||
|                            ping (Network connectivity) |  | ||||||
|                            systemd-detect-virt/dmidecode (System info detection) |  | ||||||
|  |  | ||||||
| ./goecs.sh install         Install goecs command |  | ||||||
| ./goecs.sh upgrade         Upgrade goecs command |  | ||||||
| ./goecs.sh uninstall       Uninstall goecs command |  | ||||||
| ./goecs.sh help            Show this message |  | ||||||
| EOF |  | ||||||
| } |  | ||||||
|  |  | ||||||
| case "$1" in |  | ||||||
| "help") |  | ||||||
|     show_help |  | ||||||
|     ;; |  | ||||||
| "env") |  | ||||||
|     env_check |  | ||||||
|     ;; |  | ||||||
| "install" | "upgrade") |  | ||||||
|     goecs_check |  | ||||||
|     ;; |  | ||||||
| "uninstall") |  | ||||||
|     uninstall_goecs |  | ||||||
|     ;; |  | ||||||
| *) |  | ||||||
|     echo "No command found." |  | ||||||
|     echo |  | ||||||
|     show_help |  | ||||||
|     ;; |  | ||||||
| esac |  | ||||||
|  |  | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| package main |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func Test(t *testing.T) { |  | ||||||
| 	main() |  | ||||||
| } |  | ||||||
							
								
								
									
										816
									
								
								index.html
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										816
									
								
								index.html
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,816 @@ | |||||||
|  | <!DOCTYPE html> | ||||||
|  | <html lang="zh-CN"> | ||||||
|  | <head> | ||||||
|  |     <meta charset="UTF-8"> | ||||||
|  |     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||||
|  |     <meta name="google-site-verification" content="wdrGBim_2XmtMrqxivze70saMiPQAiOhpmN3KAWb0Sw" /> | ||||||
|  |     <title>CPU Performance Ladder For Sysbench</title> | ||||||
|  |     <style> | ||||||
|  |         * { | ||||||
|  |             margin: 0; | ||||||
|  |             padding: 0; | ||||||
|  |             box-sizing: border-box; | ||||||
|  |         } | ||||||
|  |         body { | ||||||
|  |             font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; | ||||||
|  |             background: #fafafa; | ||||||
|  |             color: #37352f; | ||||||
|  |             line-height: 1.5; | ||||||
|  |         } | ||||||
|  |         .container { | ||||||
|  |             max-width: 1200px; | ||||||
|  |             margin: 0 auto; | ||||||
|  |             padding: 40px 20px; | ||||||
|  |         } | ||||||
|  |         .header { | ||||||
|  |             text-align: center; | ||||||
|  |             margin-bottom: 48px; | ||||||
|  |             padding-bottom: 24px; | ||||||
|  |             border-bottom: 1px solid #e9e9e7; | ||||||
|  |             position: relative; | ||||||
|  |         } | ||||||
|  |         .header h1 { | ||||||
|  |             font-size: 32px; | ||||||
|  |             font-weight: 700; | ||||||
|  |             color: #2d2d2d; | ||||||
|  |             margin-bottom: 8px; | ||||||
|  |         } | ||||||
|  |         .header p { | ||||||
|  |             font-size: 16px; | ||||||
|  |             color: #6b6b6b; | ||||||
|  |         } | ||||||
|  |         .language-toggle { | ||||||
|  |             position: absolute; | ||||||
|  |             top: 0; | ||||||
|  |             right: 0; | ||||||
|  |             background: white; | ||||||
|  |             border: 1px solid #e9e9e7; | ||||||
|  |             border-radius: 8px; | ||||||
|  |             overflow: hidden; | ||||||
|  |             display: flex; | ||||||
|  |         } | ||||||
|  |         .language-toggle button { | ||||||
|  |             padding: 8px 16px; | ||||||
|  |             border: none; | ||||||
|  |             background: transparent; | ||||||
|  |             cursor: pointer; | ||||||
|  |             font-size: 12px; | ||||||
|  |             transition: all 0.2s ease; | ||||||
|  |             color: #6b6b6b; | ||||||
|  |         } | ||||||
|  |         .language-toggle button.active { | ||||||
|  |             background: #0066cc; | ||||||
|  |             color: white; | ||||||
|  |         } | ||||||
|  |         .language-toggle button:hover:not(.active) { | ||||||
|  |             background: #f7f7f5; | ||||||
|  |         } | ||||||
|  |         .controls { | ||||||
|  |             display: flex; | ||||||
|  |             gap: 16px; | ||||||
|  |             margin-bottom: 32px; | ||||||
|  |             flex-wrap: wrap; | ||||||
|  |             align-items: center; | ||||||
|  |         } | ||||||
|  |         .search-box { | ||||||
|  |             flex: 1; | ||||||
|  |             min-width: 200px; | ||||||
|  |             position: relative; | ||||||
|  |             display: flex; | ||||||
|  |             gap: 8px; | ||||||
|  |         } | ||||||
|  |         .search-box input { | ||||||
|  |             flex: 1; | ||||||
|  |             padding: 12px 16px; | ||||||
|  |             border: 1px solid #e9e9e7; | ||||||
|  |             border-radius: 8px; | ||||||
|  |             font-size: 14px; | ||||||
|  |             background: white; | ||||||
|  |             transition: all 0.2s ease; | ||||||
|  |         } | ||||||
|  |         .search-box input:focus { | ||||||
|  |             outline: none; | ||||||
|  |             border-color: #0066cc; | ||||||
|  |             box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1); | ||||||
|  |         } | ||||||
|  |         .search-buttons { | ||||||
|  |             display: flex; | ||||||
|  |             gap: 4px; | ||||||
|  |         } | ||||||
|  |         .search-btn { | ||||||
|  |             padding: 12px 16px; | ||||||
|  |             border: 1px solid #e9e9e7; | ||||||
|  |             background: white; | ||||||
|  |             border-radius: 8px; | ||||||
|  |             cursor: pointer; | ||||||
|  |             font-size: 14px; | ||||||
|  |             transition: all 0.2s ease; | ||||||
|  |             color: #6b6b6b; | ||||||
|  |         } | ||||||
|  |         .search-btn:hover { | ||||||
|  |             background: #f7f7f5; | ||||||
|  |         } | ||||||
|  |         .search-btn.search { | ||||||
|  |             background: #0066cc; | ||||||
|  |             color: white; | ||||||
|  |             border-color: #0066cc; | ||||||
|  |         } | ||||||
|  |         .search-btn.clear { | ||||||
|  |             background: #e91e63; | ||||||
|  |             color: white; | ||||||
|  |             border-color: #e91e63; | ||||||
|  |         } | ||||||
|  |         .view-toggle { | ||||||
|  |             display: flex; | ||||||
|  |             background: white; | ||||||
|  |             border: 1px solid #e9e9e7; | ||||||
|  |             border-radius: 8px; | ||||||
|  |             overflow: hidden; | ||||||
|  |         } | ||||||
|  |         .view-toggle button { | ||||||
|  |             padding: 12px 20px; | ||||||
|  |             border: none; | ||||||
|  |             background: transparent; | ||||||
|  |             cursor: pointer; | ||||||
|  |             font-size: 14px; | ||||||
|  |             transition: all 0.2s ease; | ||||||
|  |         } | ||||||
|  |         .view-toggle button.active { | ||||||
|  |             background: #0066cc; | ||||||
|  |             color: white; | ||||||
|  |         } | ||||||
|  |         .view-toggle button:hover:not(.active) { | ||||||
|  |             background: #f7f7f5; | ||||||
|  |         } | ||||||
|  |         .stats-grid { | ||||||
|  |             display: grid; | ||||||
|  |             grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); | ||||||
|  |             gap: 20px; | ||||||
|  |             margin-bottom: 32px; | ||||||
|  |         } | ||||||
|  |         .stat-card { | ||||||
|  |             background: white; | ||||||
|  |             padding: 24px; | ||||||
|  |             border-radius: 12px; | ||||||
|  |             border: 1px solid #e9e9e7; | ||||||
|  |             text-align: center; | ||||||
|  |             transition: transform 0.2s ease; | ||||||
|  |         } | ||||||
|  |         .stat-card:hover { | ||||||
|  |             transform: translateY(-2px); | ||||||
|  |             box-shadow: 0 8px 25px -8px rgba(0, 0, 0, 0.1); | ||||||
|  |         } | ||||||
|  |         .stat-value { | ||||||
|  |             font-size: 28px; | ||||||
|  |             font-weight: 700; | ||||||
|  |             color: #0066cc; | ||||||
|  |             margin-bottom: 4px; | ||||||
|  |         } | ||||||
|  |         .stat-label { | ||||||
|  |             font-size: 14px; | ||||||
|  |             color: #6b6b6b; | ||||||
|  |         } | ||||||
|  |         .pagination { | ||||||
|  |             display: flex; | ||||||
|  |             justify-content: center; | ||||||
|  |             align-items: center; | ||||||
|  |             gap: 16px; | ||||||
|  |             margin-bottom: 32px; | ||||||
|  |             flex-wrap: wrap; | ||||||
|  |         } | ||||||
|  |         .pagination button { | ||||||
|  |             padding: 8px 16px; | ||||||
|  |             border: 1px solid #e9e9e7; | ||||||
|  |             background: white; | ||||||
|  |             border-radius: 6px; | ||||||
|  |             cursor: pointer; | ||||||
|  |             font-size: 14px; | ||||||
|  |             transition: all 0.2s ease; | ||||||
|  |         } | ||||||
|  |         .pagination button:hover:not(:disabled) { | ||||||
|  |             background: #f7f7f5; | ||||||
|  |         } | ||||||
|  |         .pagination button:disabled { | ||||||
|  |             opacity: 0.5; | ||||||
|  |             cursor: not-allowed; | ||||||
|  |         } | ||||||
|  |         .pagination button.active { | ||||||
|  |             background: #0066cc; | ||||||
|  |             color: white; | ||||||
|  |             border-color: #0066cc; | ||||||
|  |         } | ||||||
|  |         .pagination-info { | ||||||
|  |             font-size: 14px; | ||||||
|  |             color: #6b6b6b; | ||||||
|  |         } | ||||||
|  |         .page-jump { | ||||||
|  |             display: flex; | ||||||
|  |             align-items: center; | ||||||
|  |             gap: 8px; | ||||||
|  |         } | ||||||
|  |         .page-jump input { | ||||||
|  |             width: 60px; | ||||||
|  |             padding: 6px 8px; | ||||||
|  |             border: 1px solid #e9e9e7; | ||||||
|  |             border-radius: 4px; | ||||||
|  |             text-align: center; | ||||||
|  |             font-size: 14px; | ||||||
|  |         } | ||||||
|  |         .page-jump button { | ||||||
|  |             padding: 6px 12px; | ||||||
|  |             font-size: 12px; | ||||||
|  |         } | ||||||
|  |         .cpu-groups { | ||||||
|  |             display: grid; | ||||||
|  |             gap: 24px; | ||||||
|  |         } | ||||||
|  |         .cpu-group { | ||||||
|  |             background: white; | ||||||
|  |             border-radius: 12px; | ||||||
|  |             border: 1px solid #e9e9e7; | ||||||
|  |             overflow: hidden; | ||||||
|  |             transition: all 0.2s ease; | ||||||
|  |         } | ||||||
|  |         .cpu-group:hover { | ||||||
|  |             box-shadow: 0 4px 20px -4px rgba(0, 0, 0, 0.1); | ||||||
|  |         } | ||||||
|  |         .group-header { | ||||||
|  |             padding: 20px 24px; | ||||||
|  |             background: #f7f7f5; | ||||||
|  |             border-bottom: 1px solid #e9e9e7; | ||||||
|  |             cursor: pointer; | ||||||
|  |             display: flex; | ||||||
|  |             justify-content: space-between; | ||||||
|  |             align-items: center; | ||||||
|  |         } | ||||||
|  |         .group-header:hover { | ||||||
|  |             background: #f0f0ef; | ||||||
|  |         } | ||||||
|  |         .group-title-section { | ||||||
|  |             display: flex; | ||||||
|  |             align-items: center; | ||||||
|  |             gap: 16px; | ||||||
|  |         } | ||||||
|  |         .rank-badge { | ||||||
|  |             background: #0066cc; | ||||||
|  |             color: white; | ||||||
|  |             padding: 4px 12px; | ||||||
|  |             border-radius: 20px; | ||||||
|  |             font-size: 12px; | ||||||
|  |             font-weight: 600; | ||||||
|  |             min-width: 40px; | ||||||
|  |             text-align: center; | ||||||
|  |         } | ||||||
|  |         .group-title { | ||||||
|  |             font-size: 18px; | ||||||
|  |             font-weight: 600; | ||||||
|  |             color: #2d2d2d; | ||||||
|  |         } | ||||||
|  |         .group-stats { | ||||||
|  |             display: flex; | ||||||
|  |             gap: 24px; | ||||||
|  |             font-size: 14px; | ||||||
|  |             color: #6b6b6b; | ||||||
|  |         } | ||||||
|  |         .expand-icon { | ||||||
|  |             transition: transform 0.2s ease; | ||||||
|  |             font-size: 12px; | ||||||
|  |         } | ||||||
|  |         .cpu-group.expanded .expand-icon { | ||||||
|  |             transform: rotate(180deg); | ||||||
|  |         } | ||||||
|  |         .group-content { | ||||||
|  |             max-height: 0; | ||||||
|  |             overflow: hidden; | ||||||
|  |             transition: max-height 0.3s ease; | ||||||
|  |         } | ||||||
|  |         .cpu-group.expanded .group-content { | ||||||
|  |             max-height: 2000px; | ||||||
|  |         } | ||||||
|  |         .cpu-table { | ||||||
|  |             width: 100%; | ||||||
|  |             border-collapse: collapse; | ||||||
|  |         } | ||||||
|  |         .cpu-table th, | ||||||
|  |         .cpu-table td { | ||||||
|  |             padding: 16px 24px; | ||||||
|  |             text-align: left; | ||||||
|  |             border-bottom: 1px solid #f0f0ef; | ||||||
|  |         } | ||||||
|  |         .cpu-table th { | ||||||
|  |             font-weight: 600; | ||||||
|  |             color: #6b6b6b; | ||||||
|  |             font-size: 12px; | ||||||
|  |             text-transform: uppercase; | ||||||
|  |             letter-spacing: 0.5px; | ||||||
|  |             background: #fafafa; | ||||||
|  |         } | ||||||
|  |         .cpu-table td { | ||||||
|  |             font-size: 14px; | ||||||
|  |         } | ||||||
|  |         .cpu-table tbody tr:hover { | ||||||
|  |             background: #fafafa; | ||||||
|  |         } | ||||||
|  |         .score-cell { | ||||||
|  |             font-weight: 600; | ||||||
|  |         } | ||||||
|  |         .score-single { | ||||||
|  |             color: #0066cc; | ||||||
|  |         } | ||||||
|  |         .score-multi { | ||||||
|  |             color: #e91e63; | ||||||
|  |         } | ||||||
|  |         .loading { | ||||||
|  |             text-align: center; | ||||||
|  |             padding: 60px 20px; | ||||||
|  |             color: #6b6b6b; | ||||||
|  |         } | ||||||
|  |         .spinner { | ||||||
|  |             display: inline-block; | ||||||
|  |             width: 32px; | ||||||
|  |             height: 32px; | ||||||
|  |             border: 3px solid #f0f0ef; | ||||||
|  |             border-top: 3px solid #0066cc; | ||||||
|  |             border-radius: 50%; | ||||||
|  |             animation: spin 1s linear infinite; | ||||||
|  |             margin-bottom: 16px; | ||||||
|  |         } | ||||||
|  |         @keyframes spin { | ||||||
|  |             0% { | ||||||
|  |                 transform: rotate(0deg); | ||||||
|  |             } | ||||||
|  |             100% { | ||||||
|  |                 transform: rotate(360deg); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         .no-data { | ||||||
|  |             text-align: center; | ||||||
|  |             padding: 60px 20px; | ||||||
|  |             color: #6b6b6b; | ||||||
|  |         } | ||||||
|  |         .footer { | ||||||
|  |             margin-top: 60px; | ||||||
|  |             padding: 32px 0; | ||||||
|  |             border-top: 1px solid #e9e9e7; | ||||||
|  |             text-align: center; | ||||||
|  |             background: white; | ||||||
|  |             border-radius: 12px; | ||||||
|  |         } | ||||||
|  |         .footer-content { | ||||||
|  |             display: flex; | ||||||
|  |             flex-direction: column; | ||||||
|  |             gap: 16px; | ||||||
|  |             align-items: center; | ||||||
|  |         } | ||||||
|  |         .footer-links { | ||||||
|  |             display: flex; | ||||||
|  |             gap: 32px; | ||||||
|  |             align-items: center; | ||||||
|  |             flex-wrap: wrap; | ||||||
|  |             justify-content: center; | ||||||
|  |         } | ||||||
|  |         .footer-link { | ||||||
|  |             color: #0066cc; | ||||||
|  |             text-decoration: none; | ||||||
|  |             font-size: 14px; | ||||||
|  |             display: flex; | ||||||
|  |             align-items: center; | ||||||
|  |             gap: 8px; | ||||||
|  |             transition: color 0.2s ease; | ||||||
|  |         } | ||||||
|  |         .footer-link:hover { | ||||||
|  |             color: #004499; | ||||||
|  |         } | ||||||
|  |         .footer-text { | ||||||
|  |             font-size: 13px; | ||||||
|  |             color: #6b6b6b; | ||||||
|  |         } | ||||||
|  |         @media (max-width: 768px) { | ||||||
|  |             .container { | ||||||
|  |                 padding: 20px 16px; | ||||||
|  |             } | ||||||
|  |             .header { | ||||||
|  |                 position: static; | ||||||
|  |             } | ||||||
|  |             .language-toggle { | ||||||
|  |                 position: static; | ||||||
|  |                 margin-bottom: 16px; | ||||||
|  |                 align-self: center; | ||||||
|  |             } | ||||||
|  |             .header h1 { | ||||||
|  |                 font-size: 24px; | ||||||
|  |             } | ||||||
|  |             .controls { | ||||||
|  |                 flex-direction: column; | ||||||
|  |                 align-items: stretch; | ||||||
|  |             } | ||||||
|  |             .search-box { | ||||||
|  |                 min-width: auto; | ||||||
|  |                 flex-direction: column; | ||||||
|  |             } | ||||||
|  |             .search-buttons { | ||||||
|  |                 justify-content: center; | ||||||
|  |             } | ||||||
|  |             .stats-grid { | ||||||
|  |                 grid-template-columns: repeat(2, 1fr); | ||||||
|  |                 gap: 16px; | ||||||
|  |             } | ||||||
|  |             .stat-card { | ||||||
|  |                 padding: 16px; | ||||||
|  |             } | ||||||
|  |             .stat-value { | ||||||
|  |                 font-size: 20px; | ||||||
|  |             } | ||||||
|  |             .group-stats { | ||||||
|  |                 flex-direction: column; | ||||||
|  |                 gap: 8px; | ||||||
|  |             } | ||||||
|  |             .group-title-section { | ||||||
|  |                 flex-direction: column; | ||||||
|  |                 gap: 8px; | ||||||
|  |                 align-items: flex-start; | ||||||
|  |             } | ||||||
|  |             .cpu-table { | ||||||
|  |                 font-size: 12px; | ||||||
|  |             } | ||||||
|  |             .cpu-table th, | ||||||
|  |             .cpu-table td { | ||||||
|  |                 padding: 12px 16px; | ||||||
|  |             } | ||||||
|  |             .pagination { | ||||||
|  |                 flex-wrap: wrap; | ||||||
|  |                 gap: 8px; | ||||||
|  |             } | ||||||
|  |             .footer-links { | ||||||
|  |                 flex-direction: column; | ||||||
|  |                 gap: 16px; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     </style> | ||||||
|  | </head> | ||||||
|  | <body> | ||||||
|  |     <div class="container"> | ||||||
|  |         <div class="header"> | ||||||
|  |             <div class="language-toggle"> | ||||||
|  |                 <button id="langZh" class="active">中文</button> | ||||||
|  |                 <button id="langEn">English</button> | ||||||
|  |             </div> | ||||||
|  |             <h1 data-zh="CPU Performance Ladder For Sysbench" data-en="CPU Performance Ladder For Sysbench">CPU Performance Ladder For Sysbench</h1> | ||||||
|  |             <p data-zh="Sysbench天梯图" data-en="Sysbench CPU Benchmark Rankings">Sysbench天梯图</p> | ||||||
|  |         </div> | ||||||
|  |         <div class="controls"> | ||||||
|  |             <div class="search-box"> | ||||||
|  |                 <input type="text" id="searchInput" placeholder="搜索CPU型号或关键词..." data-placeholder-zh="搜索CPU型号或关键词..." data-placeholder-en="Search CPU model or keywords..."> | ||||||
|  |                 <div class="search-buttons"> | ||||||
|  |                     <button id="searchBtn" class="search-btn search" data-zh="搜索" data-en="Search">搜索</button> | ||||||
|  |                     <button id="clearBtn" class="search-btn clear" data-zh="清除" data-en="Clear">清除</button> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |             <div class="view-toggle"> | ||||||
|  |                 <button id="compactView" class="active" data-zh="紧凑视图" data-en="Compact View">紧凑视图</button> | ||||||
|  |                 <button id="detailView" data-zh="详细视图" data-en="Detail View">详细视图</button> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |         <div class="stats-grid" id="statsGrid"> | ||||||
|  |             <div class="stat-card"> | ||||||
|  |                 <div class="stat-value" id="totalCpus">-</div> | ||||||
|  |                 <div class="stat-label" data-zh="CPU型号总数" data-en="Total CPU Models">CPU型号总数</div> | ||||||
|  |             </div> | ||||||
|  |             <div class="stat-card"> | ||||||
|  |                 <div class="stat-value" id="totalTests">-</div> | ||||||
|  |                 <div class="stat-label" data-zh="测试样本数" data-en="Test Samples">测试样本数</div> | ||||||
|  |             </div> | ||||||
|  |             <div class="stat-card"> | ||||||
|  |                 <div class="stat-value" id="topSingle">-</div> | ||||||
|  |                 <div class="stat-label" data-zh="最高单核分数" data-en="Highest Single Score">最高单核分数</div> | ||||||
|  |             </div> | ||||||
|  |             <div class="stat-card"> | ||||||
|  |                 <div class="stat-value" id="topMulti">-</div> | ||||||
|  |                 <div class="stat-label" data-zh="最高多核分数" data-en="Highest Multi Score">最高多核分数</div> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |         <div id="loadingIndicator" class="loading"> | ||||||
|  |             <div class="spinner"></div> | ||||||
|  |             <div data-zh="正在加载CPU数据..." data-en="Loading CPU data...">正在加载CPU数据...</div> | ||||||
|  |         </div> | ||||||
|  |         <div class="pagination" id="pagination" style="display: none;"> | ||||||
|  |             <button id="prevPage" data-zh="上一页" data-en="Previous">上一页</button> | ||||||
|  |             <div class="pagination-info" id="pageInfo">-</div> | ||||||
|  |             <div class="page-jump"> | ||||||
|  |                 <span data-zh="跳转到" data-en="Go to">跳转到</span> | ||||||
|  |                 <input type="number" id="pageInput" min="1"> | ||||||
|  |                 <span data-zh="页" data-en="page">页</span> | ||||||
|  |                 <button id="jumpBtn" data-zh="跳转" data-en="Go">跳转</button> | ||||||
|  |             </div> | ||||||
|  |             <button id="nextPage" data-zh="下一页" data-en="Next">下一页</button> | ||||||
|  |         </div> | ||||||
|  |         <div id="cpuGroups" class="cpu-groups" style="display: none;"></div> | ||||||
|  |         <div id="noData" class="no-data" style="display: none;"> | ||||||
|  |             <div data-zh="📄 未找到CPU数据文件" data-en="📄 CPU data file not found">📄 未找到CPU数据文件</div> | ||||||
|  |             <p data-zh="请确保 cpu_statistics.json 文件存在于当前目录" data-en="Please ensure cpu_statistics.json file exists in current directory">请确保 cpu_statistics.json 文件存在于当前目录</p> | ||||||
|  |         </div> | ||||||
|  |         <div class="footer"> | ||||||
|  |             <div class="footer-content"> | ||||||
|  |                 <div class="footer-links"> | ||||||
|  |                     <a href="https://github.com/oneclickvirt/ecs" class="footer-link" target="_blank" rel="noopener"> | ||||||
|  |                         <span>🚀</span> | ||||||
|  |                         <span data-zh="测试脚本" data-en="Test Script">测试脚本</span> | ||||||
|  |                     </a> | ||||||
|  |                     <a href="https://www.spiritlhl.net/" class="footer-link" target="_blank" rel="noopener"> | ||||||
|  |                         <span>🌐</span> | ||||||
|  |                         <span data-zh="spiritlhl 官网" data-en="spiritlhl Official">spiritlhl 官网</span> | ||||||
|  |                     </a> | ||||||
|  |                 </div> | ||||||
|  |                 <div class="footer-text" data-zh="本项目隶属于 spiritlhl 旗下" data-en="This project is under spiritlhl">本项目隶属于 spiritlhl 旗下</div> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |     </div> | ||||||
|  |     <script> | ||||||
|  |         class CPUDashboard { | ||||||
|  |             constructor() { | ||||||
|  |                 this.data = []; | ||||||
|  |                 this.groupedData = {}; | ||||||
|  |                 this.filteredGroups = {}; | ||||||
|  |                 this.sortedPrefixes = []; | ||||||
|  |                 this.allSortedPrefixes = []; | ||||||
|  |                 this.prefixRanks = {}; | ||||||
|  |                 this.isDetailView = false; | ||||||
|  |                 this.searchTerm = ''; | ||||||
|  |                 this.currentPage = 1; | ||||||
|  |                 this.itemsPerPage = 10; | ||||||
|  |                 this.currentLang = 'zh'; | ||||||
|  |                 this.total_samples = 0; | ||||||
|  |                 this.initializeEventListeners(); | ||||||
|  |                 this.loadData(); | ||||||
|  |             } | ||||||
|  |             initializeEventListeners() { | ||||||
|  |                 const searchInput = document.getElementById('searchInput'); | ||||||
|  |                 const searchBtn = document.getElementById('searchBtn'); | ||||||
|  |                 const clearBtn = document.getElementById('clearBtn'); | ||||||
|  |                 const performSearch = () => { | ||||||
|  |                     this.searchTerm = searchInput.value.toLowerCase(); | ||||||
|  |                     this.currentPage = 1; | ||||||
|  |                     this.filterAndRender(); | ||||||
|  |                 }; | ||||||
|  |                 searchInput.addEventListener('keypress', (e) => { | ||||||
|  |                     if (e.key === 'Enter') performSearch(); | ||||||
|  |                 }); | ||||||
|  |                 searchBtn.addEventListener('click', performSearch); | ||||||
|  |                 clearBtn.addEventListener('click', () => { | ||||||
|  |                     searchInput.value = ''; | ||||||
|  |                     this.searchTerm = ''; | ||||||
|  |                     this.currentPage = 1; | ||||||
|  |                     this.filterAndRender(); | ||||||
|  |                 }); | ||||||
|  |                 document.getElementById('compactView').addEventListener('click', () => { | ||||||
|  |                     this.setViewMode(false); | ||||||
|  |                 }); | ||||||
|  |                 document.getElementById('detailView').addEventListener('click', () => { | ||||||
|  |                     this.setViewMode(true); | ||||||
|  |                 }); | ||||||
|  |                 document.getElementById('prevPage').addEventListener('click', () => { | ||||||
|  |                     if (this.currentPage > 1) { | ||||||
|  |                         this.currentPage--; | ||||||
|  |                         this.renderGroups(); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 document.getElementById('nextPage').addEventListener('click', () => { | ||||||
|  |                     const totalPages = Math.ceil(this.sortedPrefixes.length / this.itemsPerPage); | ||||||
|  |                     if (this.currentPage < totalPages) { | ||||||
|  |                         this.currentPage++; | ||||||
|  |                         this.renderGroups(); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 document.getElementById('jumpBtn').addEventListener('click', () => { | ||||||
|  |                     const page = parseInt(document.getElementById('pageInput').value); | ||||||
|  |                     const totalPages = Math.ceil(this.sortedPrefixes.length / this.itemsPerPage); | ||||||
|  |                     if (page >= 1 && page <= totalPages) { | ||||||
|  |                         this.currentPage = page; | ||||||
|  |                         this.renderGroups(); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 document.getElementById('langZh').addEventListener('click', () => { | ||||||
|  |                     this.setLanguage('zh'); | ||||||
|  |                 }); | ||||||
|  |                 document.getElementById('langEn').addEventListener('click', () => { | ||||||
|  |                     this.setLanguage('en'); | ||||||
|  |                 }); | ||||||
|  |             } | ||||||
|  |             setLanguage(lang) { | ||||||
|  |                 this.currentLang = lang; | ||||||
|  |                 document.getElementById('langZh').classList.toggle('active', lang === 'zh'); | ||||||
|  |                 document.getElementById('langEn').classList.toggle('active', lang === 'en'); | ||||||
|  |                 document.querySelectorAll('[data-zh]').forEach(el => { | ||||||
|  |                     if (el.tagName === 'INPUT') { | ||||||
|  |                         el.placeholder = el.getAttribute(`data-placeholder-${lang}`); | ||||||
|  |                     } else { | ||||||
|  |                         el.textContent = el.getAttribute(`data-${lang}`); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |                 this.updatePagination(); | ||||||
|  |                 this.renderGroups(); | ||||||
|  |             } | ||||||
|  |             async loadData() { | ||||||
|  |                 const cdnUrls = [ | ||||||
|  |                     "http://cdn1.spiritlhl.net/", | ||||||
|  |                     "http://cdn2.spiritlhl.net/",  | ||||||
|  |                     "http://cdn3.spiritlhl.net/", | ||||||
|  |                     "http://cdn4.spiritlhl.net/" | ||||||
|  |                 ]; | ||||||
|  |                 const urls = [ | ||||||
|  |                     'cpu_statistics.json', | ||||||
|  |                     ...cdnUrls.map(cdn => `${cdn}https://raw.githubusercontent.com/oneclickvirt/ecs/refs/heads/ranks/cpu_statistics.json`) | ||||||
|  |                 ]; | ||||||
|  |                 for (let url of urls) { | ||||||
|  |                     try { | ||||||
|  |                         const response = await fetch(url); | ||||||
|  |                         if (!response.ok) throw new Error('请求失败'); | ||||||
|  |                         const data = await response.json(); | ||||||
|  |                         if (data.cpu_statistics) { | ||||||
|  |                             this.data = data.cpu_statistics.map(stat => ({ | ||||||
|  |                                 cpu_prefix: stat.cpu_prefix, | ||||||
|  |                                 cpu_model: stat.cpu_model, | ||||||
|  |                                 cpu_cores: stat.typical_cores, | ||||||
|  |                                 single_score: stat.max_single_score, | ||||||
|  |                                 multi_score: stat.max_multi_score, | ||||||
|  |                                 multi_threads: stat.typical_threads | ||||||
|  |                             })); | ||||||
|  |                             if (data.total_samples) { | ||||||
|  |                                 this.total_samples = data.total_samples; | ||||||
|  |                             } | ||||||
|  |                         } else { | ||||||
|  |                             this.data = Array.isArray(data) ? data : []; | ||||||
|  |                         } | ||||||
|  |                         this.processData(); | ||||||
|  |                         this.updateStats(); | ||||||
|  |                         this.filterAndRender(); | ||||||
|  |                         document.getElementById('loadingIndicator').style.display = 'none'; | ||||||
|  |                         document.getElementById('pagination').style.display = 'flex'; | ||||||
|  |                         document.getElementById('cpuGroups').style.display = 'block'; | ||||||
|  |                         return; | ||||||
|  |                     } catch (error) { | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |                 document.getElementById('loadingIndicator').style.display = 'none'; | ||||||
|  |                 document.getElementById('noData').style.display = 'block'; | ||||||
|  |             } | ||||||
|  |             processData() { | ||||||
|  |                 this.groupedData = {}; | ||||||
|  |                 this.data.forEach(cpu => { | ||||||
|  |                     const prefix = cpu.cpu_prefix; | ||||||
|  |                     if (!this.groupedData[prefix]) { | ||||||
|  |                         this.groupedData[prefix] = []; | ||||||
|  |                     } | ||||||
|  |                     this.groupedData[prefix].push(cpu); | ||||||
|  |                 }); | ||||||
|  |                 Object.keys(this.groupedData).forEach(prefix => { | ||||||
|  |                     this.groupedData[prefix].sort((a, b) => b.single_score - a.single_score); | ||||||
|  |                 }); | ||||||
|  |                 this.allSortedPrefixes = Object.keys(this.groupedData).sort((a, b) => { | ||||||
|  |                     const maxSingleA = Math.max(...this.groupedData[a].map(cpu => cpu.single_score)); | ||||||
|  |                     const maxSingleB = Math.max(...this.groupedData[b].map(cpu => cpu.single_score)); | ||||||
|  |                     return maxSingleB - maxSingleA; | ||||||
|  |                 }); | ||||||
|  |                 this.allSortedPrefixes.forEach((prefix, index) => { | ||||||
|  |                     this.prefixRanks[prefix] = index + 1; | ||||||
|  |                 }); | ||||||
|  |                 this.sortedPrefixes = [...this.allSortedPrefixes]; | ||||||
|  |             } | ||||||
|  |             updateStats() { | ||||||
|  |                 const totalCpus = Object.keys(this.groupedData).length; | ||||||
|  |                 const totalTests = this.total_samples; | ||||||
|  |                 const topSingle = Math.max(...this.data.map(cpu => cpu.single_score)); | ||||||
|  |                 const topMulti = Math.max(...this.data.map(cpu => cpu.multi_score)); | ||||||
|  |                 document.getElementById('totalCpus').textContent = totalCpus.toLocaleString(); | ||||||
|  |                 document.getElementById('totalTests').textContent = totalTests.toLocaleString(); | ||||||
|  |                 document.getElementById('topSingle').textContent = topSingle.toLocaleString(); | ||||||
|  |                 document.getElementById('topMulti').textContent = topMulti.toLocaleString(); | ||||||
|  |             } | ||||||
|  |             filterAndRender() { | ||||||
|  |                 if (this.searchTerm) { | ||||||
|  |                     this.filteredGroups = {}; | ||||||
|  |                     const filteredPrefixes = []; | ||||||
|  |                     this.allSortedPrefixes.forEach(prefix => { | ||||||
|  |                         const filteredCpus = this.groupedData[prefix].filter(cpu =>  | ||||||
|  |                             cpu.cpu_model.toLowerCase().includes(this.searchTerm) || | ||||||
|  |                             cpu.cpu_prefix.toLowerCase().includes(this.searchTerm) | ||||||
|  |                         ); | ||||||
|  |                         if (filteredCpus.length > 0) { | ||||||
|  |                             this.filteredGroups[prefix] = filteredCpus; | ||||||
|  |                             filteredPrefixes.push(prefix); | ||||||
|  |                         } | ||||||
|  |                     }); | ||||||
|  |                     this.sortedPrefixes = filteredPrefixes; | ||||||
|  |                 } else { | ||||||
|  |                     this.filteredGroups = this.groupedData; | ||||||
|  |                     this.sortedPrefixes = [...this.allSortedPrefixes]; | ||||||
|  |                 } | ||||||
|  |                 this.renderGroups(); | ||||||
|  |             } | ||||||
|  |             renderGroups() { | ||||||
|  |                 const container = document.getElementById('cpuGroups'); | ||||||
|  |                 const startIndex = (this.currentPage - 1) * this.itemsPerPage; | ||||||
|  |                 const endIndex = startIndex + this.itemsPerPage; | ||||||
|  |                 const currentPagePrefixes = this.sortedPrefixes.slice(startIndex, endIndex); | ||||||
|  |                 container.innerHTML = currentPagePrefixes.map(prefix => { | ||||||
|  |                     const cpus = this.filteredGroups[prefix]; | ||||||
|  |                     const topCpu = cpus[0]; | ||||||
|  |                     const maxSingle = Math.max(...cpus.map(cpu => cpu.single_score)); | ||||||
|  |                     const maxMulti = Math.max(...cpus.map(cpu => cpu.multi_score)); | ||||||
|  |                     const rank = this.prefixRanks[prefix]; | ||||||
|  |                     const sampleText = this.currentLang === 'zh' ? '样本' : 'Samples'; | ||||||
|  |                     const singleText = this.currentLang === 'zh' ? '单核最高' : 'Max Single'; | ||||||
|  |                     const multiText = this.currentLang === 'zh' ? '多核最高' : 'Max Multi'; | ||||||
|  |                     return ` | ||||||
|  |                         <div class="cpu-group" data-prefix="${prefix}"> | ||||||
|  |                             <div class="group-header"> | ||||||
|  |                                 <div class="group-title-section"> | ||||||
|  |                                     <div class="rank-badge">#${rank}</div> | ||||||
|  |                                     <div class="group-title">${topCpu.cpu_model}</div> | ||||||
|  |                                 </div> | ||||||
|  |                                 <div class="group-stats"> | ||||||
|  |                                     <span>${sampleText}: ${cpus.length}</span> | ||||||
|  |                                     <span>${singleText}: ${maxSingle.toLocaleString()}</span> | ||||||
|  |                                     <span>${multiText}: ${maxMulti.toLocaleString()}</span> | ||||||
|  |                                 </div> | ||||||
|  |                                 <div class="expand-icon">▼</div> | ||||||
|  |                             </div> | ||||||
|  |                             <div class="group-content"> | ||||||
|  |                                 ${this.renderTable(cpus)} | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     `; | ||||||
|  |                 }).join(''); | ||||||
|  |                 container.querySelectorAll('.group-header').forEach(header => { | ||||||
|  |                     header.addEventListener('click', () => { | ||||||
|  |                         const group = header.parentElement; | ||||||
|  |                         group.classList.toggle('expanded'); | ||||||
|  |                     }); | ||||||
|  |                 }); | ||||||
|  |                 this.updatePagination(); | ||||||
|  |             } | ||||||
|  |             updatePagination() { | ||||||
|  |                 const totalPages = Math.ceil(this.sortedPrefixes.length / this.itemsPerPage); | ||||||
|  |                 const pageInfo = document.getElementById('pageInfo'); | ||||||
|  |                 const pageText = this.currentLang === 'zh'  | ||||||
|  |                     ? `第 ${this.currentPage} 页,共 ${totalPages} 页` | ||||||
|  |                     : `Page ${this.currentPage} of ${totalPages}`;   | ||||||
|  |                 pageInfo.textContent = pageText; | ||||||
|  |                 const pageInput = document.getElementById('pageInput'); | ||||||
|  |                 pageInput.max = totalPages; | ||||||
|  |                 pageInput.value = this.currentPage; | ||||||
|  |                 const prevBtn = document.getElementById('prevPage'); | ||||||
|  |                 const nextBtn = document.getElementById('nextPage'); | ||||||
|  |                 prevBtn.disabled = this.currentPage === 1; | ||||||
|  |                 nextBtn.disabled = this.currentPage === totalPages; | ||||||
|  |             } | ||||||
|  |             renderTable(cpus) { | ||||||
|  |                 const headers = this.isDetailView  | ||||||
|  |                     ? (this.currentLang === 'zh'  | ||||||
|  |                         ? ['CPU型号', '核心数', '单核分数', '多核分数', '多核线程'] | ||||||
|  |                         : ['CPU Model', 'Cores', 'Single Score', 'Multi Score', 'Multi Threads']) | ||||||
|  |                     : (this.currentLang === 'zh' | ||||||
|  |                         ? ['CPU型号', '核心数', '单核分数', '多核分数'] | ||||||
|  |                         : ['CPU Model', 'Cores', 'Single Score', 'Multi Score']); | ||||||
|  |                 const rows = cpus.slice(0, this.isDetailView ? cpus.length : 10).map(cpu => { | ||||||
|  |                     const cells = this.isDetailView | ||||||
|  |                         ? [cpu.cpu_model, cpu.cpu_cores, cpu.single_score.toLocaleString(),  | ||||||
|  |                            cpu.multi_score.toLocaleString(), cpu.multi_threads] | ||||||
|  |                         : [cpu.cpu_model, cpu.cpu_cores, cpu.single_score.toLocaleString(),  | ||||||
|  |                            cpu.multi_score.toLocaleString()]; | ||||||
|  |                     return ` | ||||||
|  |                         <tr> | ||||||
|  |                             ${cells.map((cell, index) => { | ||||||
|  |                                 let className = ''; | ||||||
|  |                                 if (index === 2) className = 'score-cell score-single'; | ||||||
|  |                                 else if (index === 3) className = 'score-cell score-multi'; | ||||||
|  |                                 return `<td class="${className}">${cell}</td>`; | ||||||
|  |                             }).join('')} | ||||||
|  |                         </tr> | ||||||
|  |                     `; | ||||||
|  |                 }).join(''); | ||||||
|  |                 return ` | ||||||
|  |                     <table class="cpu-table"> | ||||||
|  |                         <thead> | ||||||
|  |                             <tr> | ||||||
|  |                                 ${headers.map(header => `<th>${header}</th>`).join('')} | ||||||
|  |                             </tr> | ||||||
|  |                         </thead> | ||||||
|  |                         <tbody> | ||||||
|  |                             ${rows} | ||||||
|  |                         </tbody> | ||||||
|  |                     </table> | ||||||
|  |                 `; | ||||||
|  |             } | ||||||
|  |             setViewMode(isDetail) { | ||||||
|  |                 this.isDetailView = isDetail; | ||||||
|  |                 document.getElementById('compactView').classList.toggle('active', !isDetail); | ||||||
|  |                 document.getElementById('detailView').classList.toggle('active', isDetail); | ||||||
|  |                 this.renderGroups(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         new CPUDashboard(); | ||||||
|  |     </script> | ||||||
|  | </body> | ||||||
|  | </html> | ||||||
| @@ -1,35 +0,0 @@ | |||||||
| package memorytest |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"github.com/oneclickvirt/memorytest/memory" |  | ||||||
| 	"runtime" |  | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func MemoryTest(language, testMethod string) { |  | ||||||
| 	var res string |  | ||||||
| 	if runtime.GOOS == "windows" { |  | ||||||
| 		if testMethod != "winsat" && testMethod != "" { |  | ||||||
| 			res = "Detected host is Windows, using Winsat for testing.\n" |  | ||||||
| 		} |  | ||||||
| 		res += memory.WinsatTest(language) |  | ||||||
| 	} else { |  | ||||||
| 		switch testMethod { |  | ||||||
| 		case "sysbench": |  | ||||||
| 			res = memory.SysBenchTest(language) |  | ||||||
| 			if res == "" { |  | ||||||
| 				res = "sysbench test failed, switch to use dd test.\n" |  | ||||||
| 				res += memory.DDTest(language) |  | ||||||
| 			} |  | ||||||
| 		case "dd": |  | ||||||
| 			res = memory.DDTest(language) |  | ||||||
| 		default: |  | ||||||
| 			res = "Unsupported test method" |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if !strings.Contains(res, "\n") && res != "" { |  | ||||||
| 		res += "\n" |  | ||||||
| 	} |  | ||||||
| 	fmt.Printf(res) |  | ||||||
| } |  | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| package memorytest |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func Test(t *testing.T) { |  | ||||||
| 	MemoryTest("zh", "sysbench") |  | ||||||
| } |  | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| package network1 |  | ||||||
|  |  | ||||||
| import "github.com/oneclickvirt/security/network" |  | ||||||
|  |  | ||||||
| // 本包在main中不使用 |  | ||||||
| func NetworkCheck(checkType string, enableSecurityCheck bool, language string) (string, string, error) { |  | ||||||
| 	return network.NetworkCheck(checkType, enableSecurityCheck, language) |  | ||||||
| } |  | ||||||
| @@ -1,22 +0,0 @@ | |||||||
| package network1 |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TestIpv4SecurityCheck(t *testing.T) { |  | ||||||
| 	// 单项测试 |  | ||||||
| 	//result1, _ := Ipv4SecurityCheck("8.8.8.8", nil, "zh") |  | ||||||
| 	//fmt.Println(result1) |  | ||||||
| 	//result2, _ := Ipv6SecurityCheck("2001:4860:4860::8844", nil, "zh") |  | ||||||
| 	//fmt.Println(result2) |  | ||||||
|  |  | ||||||
| 	// 全项测试 |  | ||||||
| 	ipInfo, securityInfo, _ := NetworkCheck("both", true, "zh") |  | ||||||
| 	fmt.Println("--------------------------------------------------") |  | ||||||
| 	fmt.Printf(ipInfo) |  | ||||||
| 	fmt.Println("--------------------------------------------------") |  | ||||||
| 	fmt.Printf(securityInfo) |  | ||||||
| 	fmt.Println("--------------------------------------------------") |  | ||||||
| } |  | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| package ntrace |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/oneclickvirt/nt3/nt" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func TraceRoute3(language, location, checkType string) { |  | ||||||
| 	nt.TraceRoute(language, location, checkType) |  | ||||||
| } |  | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| package ntrace |  | ||||||
|  |  | ||||||
| import "testing" |  | ||||||
|  |  | ||||||
| // https://github.com/nxtrace/NTrace-core/blob/main/fast_trace/fast_trace.go |  | ||||||
| func TestTraceRoute(t *testing.T) { |  | ||||||
| 	TraceRoute3("en", "GZ", "ipv4") |  | ||||||
| } |  | ||||||
							
								
								
									
										13
									
								
								port/port.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								port/port.go
									
									
									
									
									
								
							| @@ -1,13 +0,0 @@ | |||||||
| package port |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"github.com/oneclickvirt/portchecker/email" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // 常用端口阻断检测 TCP/UDP/ICMP 协议 |  | ||||||
| // 本包不在main中使用 |  | ||||||
| func EmailCheck() { |  | ||||||
| 	res := email.EmailCheck() |  | ||||||
| 	fmt.Println(res) |  | ||||||
| } |  | ||||||
| @@ -1,9 +0,0 @@ | |||||||
| package port |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func Test(t *testing.T) { |  | ||||||
| 	EmailCheck() |  | ||||||
| } |  | ||||||
| @@ -1,66 +0,0 @@ | |||||||
| package speedtest |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/oneclickvirt/speedtest/model" |  | ||||||
| 	"github.com/oneclickvirt/speedtest/sp" |  | ||||||
| 	"runtime" |  | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func ShowHead(language string) { |  | ||||||
| 	sp.ShowHead(language) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func NearbySP() { |  | ||||||
| 	if runtime.GOOS == "windows" || sp.OfficialAvailableTest() != nil { |  | ||||||
| 		sp.NearbySpeedTest() |  | ||||||
| 	} else { |  | ||||||
| 		sp.OfficialNearbySpeedTest() |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func CustomSP(platform, operator string, num int, language string) { |  | ||||||
| 	var url, parseType string |  | ||||||
| 	if strings.ToLower(platform) == "cn" { |  | ||||||
| 		if strings.ToLower(operator) == "cmcc" { |  | ||||||
| 			url = model.CnCMCC |  | ||||||
| 		} else if strings.ToLower(operator) == "cu" { |  | ||||||
| 			url = model.CnCU |  | ||||||
| 		} else if strings.ToLower(operator) == "ct" { |  | ||||||
| 			url = model.CnCT |  | ||||||
| 		} else if strings.ToLower(operator) == "hk" { |  | ||||||
| 			url = model.CnHK |  | ||||||
| 		} else if strings.ToLower(operator) == "tw" { |  | ||||||
| 			url = model.CnTW |  | ||||||
| 		} else if strings.ToLower(operator) == "jp" { |  | ||||||
| 			url = model.CnJP |  | ||||||
| 		} else if strings.ToLower(operator) == "sg" { |  | ||||||
| 			url = model.CnSG |  | ||||||
| 		} |  | ||||||
| 		parseType = "url" |  | ||||||
| 	} else if strings.ToLower(platform) == "net" { |  | ||||||
| 		if strings.ToLower(operator) == "cmcc" { |  | ||||||
| 			url = model.NetCMCC |  | ||||||
| 		} else if strings.ToLower(operator) == "cu" { |  | ||||||
| 			url = model.NetCU |  | ||||||
| 		} else if strings.ToLower(operator) == "ct" { |  | ||||||
| 			url = model.NetCT |  | ||||||
| 		} else if strings.ToLower(operator) == "hk" { |  | ||||||
| 			url = model.NetHK |  | ||||||
| 		} else if strings.ToLower(operator) == "tw" { |  | ||||||
| 			url = model.NetTW |  | ||||||
| 		} else if strings.ToLower(operator) == "jp" { |  | ||||||
| 			url = model.NetJP |  | ||||||
| 		} else if strings.ToLower(operator) == "sg" { |  | ||||||
| 			url = model.NetSG |  | ||||||
| 		} else if strings.ToLower(operator) == "global" { |  | ||||||
| 			url = model.NetGlobal |  | ||||||
| 		} |  | ||||||
| 		parseType = "id" |  | ||||||
| 	} |  | ||||||
| 	if runtime.GOOS == "windows" || sp.OfficialAvailableTest() != nil { |  | ||||||
| 		sp.CustomSpeedTest(url, parseType, num, language) |  | ||||||
| 	} else { |  | ||||||
| 		sp.OfficialCustomSpeedTest(url, parseType, num, language) |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
| @@ -1,8 +0,0 @@ | |||||||
| package speedtest |  | ||||||
|  |  | ||||||
| import "testing" |  | ||||||
|  |  | ||||||
| func Test(t *testing.T) { |  | ||||||
| 	ShowHead("en") |  | ||||||
| 	NearbySP() |  | ||||||
| } |  | ||||||
| @@ -1,26 +0,0 @@ | |||||||
| package unlocktest |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"github.com/oneclickvirt/UnlockTests/utils" |  | ||||||
| 	"github.com/oneclickvirt/UnlockTests/uts" |  | ||||||
| 	"github.com/oneclickvirt/defaultset" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func MediaTest(language string) string { |  | ||||||
| 	var res string |  | ||||||
| 	readStatus := uts.ReadSelect(language, "0") |  | ||||||
| 	if !readStatus { |  | ||||||
| 		return "" |  | ||||||
| 	} |  | ||||||
| 	if uts.IPV4 { |  | ||||||
| 		res += defaultset.Blue("IPV4:") + "\n" |  | ||||||
| 		res += uts.RunTests(utils.Ipv4HttpClient, "ipv4", language, false) |  | ||||||
| 		return res |  | ||||||
| 	} |  | ||||||
| 	if uts.IPV6 { |  | ||||||
| 		res += defaultset.Blue("IPV6:") + "\n" |  | ||||||
| 		res += uts.RunTests(utils.Ipv6HttpClient, "ipv6", language, false) |  | ||||||
| 		return res |  | ||||||
| 	} |  | ||||||
| 	return "" |  | ||||||
| } |  | ||||||
| @@ -1,10 +0,0 @@ | |||||||
| package unlocktest |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"fmt" |  | ||||||
| 	"testing" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func Test(t *testing.T) { |  | ||||||
| 	fmt.Printf(MediaTest("zh")) |  | ||||||
| } |  | ||||||
							
								
								
									
										360
									
								
								utils/utils.go
									
									
									
									
									
								
							
							
						
						
									
										360
									
								
								utils/utils.go
									
									
									
									
									
								
							| @@ -1,360 +0,0 @@ | |||||||
| package utils |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"bufio" |  | ||||||
| 	"bytes" |  | ||||||
| 	"fmt" |  | ||||||
| 	"github.com/imroc/req/v3" |  | ||||||
| 	"github.com/oneclickvirt/UnlockTests/uts" |  | ||||||
| 	"github.com/oneclickvirt/basics/ipv6" |  | ||||||
| 	"github.com/oneclickvirt/basics/system" |  | ||||||
| 	. "github.com/oneclickvirt/defaultset" |  | ||||||
| 	"github.com/oneclickvirt/security/network" |  | ||||||
| 	"io" |  | ||||||
| 	"os" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"regexp" |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
| 	"time" |  | ||||||
| 	"unicode/utf8" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| // PrintCenteredTitle 根据指定的宽度打印居中标题 |  | ||||||
| func PrintCenteredTitle(title string, width int) { |  | ||||||
| 	// 计算字符串的字符数 |  | ||||||
| 	titleLength := utf8.RuneCountInString(title) |  | ||||||
| 	totalPadding := width - titleLength |  | ||||||
| 	padding := totalPadding / 2 |  | ||||||
| 	paddingStr := strings.Repeat("-", padding) |  | ||||||
| 	fmt.Println(paddingStr + title + paddingStr + strings.Repeat("-", totalPadding%2)) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // PrintHead 根据语言打印头部信息 |  | ||||||
| func PrintHead(language string, width int, ecsVersion string) { |  | ||||||
| 	if language == "zh" { |  | ||||||
| 		PrintCenteredTitle("VPS融合怪测试", width) |  | ||||||
| 		fmt.Printf("版本:%s\n", ecsVersion) |  | ||||||
| 		fmt.Println("测评频道: https://t.me/vps_reviews\n" + |  | ||||||
| 			"Go项目地址:https://github.com/oneclickvirt/ecs\n" + |  | ||||||
| 			"Shell项目地址:https://github.com/spiritLHLS/ecs") |  | ||||||
| 	} else { |  | ||||||
| 		PrintCenteredTitle("VPS Fusion Monster Test", width) |  | ||||||
| 		fmt.Printf("Version: %s\n", ecsVersion) |  | ||||||
| 		fmt.Println("Review Channel: https://t.me/vps_reviews\n" + |  | ||||||
| 			"Go Project: https://github.com/oneclickvirt/ecs\n" + |  | ||||||
| 			"Shell Project: https://github.com/spiritLHLS/ecs") |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func CheckChina(enableLogger bool) bool { |  | ||||||
| 	if enableLogger { |  | ||||||
| 		InitLogger() |  | ||||||
| 		defer Logger.Sync() |  | ||||||
| 	} |  | ||||||
| 	var selectChina bool |  | ||||||
| 	client := req.C() |  | ||||||
| 	client.SetTimeout(6 * time.Second) |  | ||||||
| 	client.R(). |  | ||||||
| 		SetRetryCount(2). |  | ||||||
| 		SetRetryBackoffInterval(1*time.Second, 3*time.Second). |  | ||||||
| 		SetRetryFixedInterval(2 * time.Second) |  | ||||||
| 	ipapiURL := "https://ipapi.co/json" |  | ||||||
| 	cipccURL := "http://cip.cc" |  | ||||||
| 	ipapiResp, err := client.R().Get(ipapiURL) |  | ||||||
| 	if err != nil { |  | ||||||
| 		if enableLogger { |  | ||||||
| 			Logger.Info("无法获取IP信息:" + err.Error()) |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		defer ipapiResp.Body.Close() |  | ||||||
| 		var ipapiBody string |  | ||||||
| 		ipapiBody, err = ipapiResp.ToString() |  | ||||||
| 		if err != nil { |  | ||||||
| 			if enableLogger { |  | ||||||
| 				Logger.Info("无法读取IP信息响应:" + err.Error()) |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			isInChina := strings.Contains(ipapiBody, "China") |  | ||||||
| 			if isInChina { |  | ||||||
| 				fmt.Println("根据ipapi.co提供的信息,当前IP可能在中国") |  | ||||||
| 				var input string |  | ||||||
| 				fmt.Print("是否选用中国专项测试(无流媒体测试,有三网Ping值测试)? ([y]/n) ") |  | ||||||
| 				fmt.Scanln(&input) |  | ||||||
| 				switch strings.ToLower(input) { |  | ||||||
| 				case "yes", "y": |  | ||||||
| 					fmt.Println("使用中国专项测试") |  | ||||||
| 					selectChina = true |  | ||||||
| 				case "no", "n": |  | ||||||
| 					fmt.Println("不使用中国专项测试") |  | ||||||
| 				default: |  | ||||||
| 					fmt.Println("使用中国专项测试") |  | ||||||
| 					selectChina = true |  | ||||||
| 				} |  | ||||||
| 				return selectChina |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	cipccResp, err := client.R().Get(cipccURL) |  | ||||||
| 	if err != nil { |  | ||||||
| 		if enableLogger { |  | ||||||
| 			Logger.Info("无法获取IP信息:" + err.Error()) |  | ||||||
| 		} |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	defer cipccResp.Body.Close() |  | ||||||
| 	cipccBody, err := cipccResp.ToString() |  | ||||||
| 	if err != nil { |  | ||||||
| 		if enableLogger { |  | ||||||
| 			Logger.Info("无法读取IP信息响应:" + err.Error()) |  | ||||||
| 		} |  | ||||||
| 		return false |  | ||||||
| 	} |  | ||||||
| 	isInChina := strings.Contains(cipccBody, "中国") |  | ||||||
| 	if isInChina { |  | ||||||
| 		fmt.Println("根据cip.cc提供的信息,当前IP可能在中国") |  | ||||||
| 		var input string |  | ||||||
| 		fmt.Print("是否选用中国专项测试(无流媒体测试,有三网Ping值测试)? ([y]/n) ") |  | ||||||
| 		fmt.Scanln(&input) |  | ||||||
| 		switch strings.ToLower(input) { |  | ||||||
| 		case "yes", "y": |  | ||||||
| 			fmt.Println("使用中国专项测试") |  | ||||||
| 			selectChina = true |  | ||||||
| 		case "no", "n": |  | ||||||
| 			fmt.Println("不使用中国专项测试") |  | ||||||
| 		default: |  | ||||||
| 			fmt.Println("不使用中国专项测试") |  | ||||||
| 			selectChina = true |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return selectChina |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // BasicsAndSecurityCheck 执行安全检查 |  | ||||||
| func BasicsAndSecurityCheck(language, nt3CheckType string, securtyCheckStatus bool) (string, string, string) { |  | ||||||
| 	var wgt sync.WaitGroup |  | ||||||
| 	var ipInfo, securityInfo, systemInfo string |  | ||||||
| 	var err error |  | ||||||
| 	wgt.Add(1) |  | ||||||
| 	go func() { |  | ||||||
| 		defer wgt.Done() |  | ||||||
| 		ipInfo, securityInfo, err = network.NetworkCheck("both", securtyCheckStatus, language) |  | ||||||
| 		if err != nil { |  | ||||||
| 			fmt.Println(err.Error()) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| 	wgt.Add(1) |  | ||||||
| 	go func() { |  | ||||||
| 		defer wgt.Done() |  | ||||||
| 		systemInfo = system.CheckSystemInfo(language) |  | ||||||
| 	}() |  | ||||||
| 	wgt.Wait() |  | ||||||
| 	ipv6Info, errv6 := ipv6.GetIPv6Mask(language) |  | ||||||
| 	basicInfo := systemInfo + ipInfo |  | ||||||
| 	if errv6 == nil && ipv6Info != "" { |  | ||||||
| 		basicInfo += ipv6Info |  | ||||||
| 	} |  | ||||||
| 	if strings.Contains(ipInfo, "IPV4") && strings.Contains(ipInfo, "IPV6") { |  | ||||||
| 		uts.IPV4 = true |  | ||||||
| 		uts.IPV6 = true |  | ||||||
| 		if nt3CheckType == "" { |  | ||||||
| 			nt3CheckType = "ipv4" |  | ||||||
| 		} |  | ||||||
| 	} else if strings.Contains(ipInfo, "IPV4") { |  | ||||||
| 		uts.IPV4 = true |  | ||||||
| 		uts.IPV6 = false |  | ||||||
| 		if nt3CheckType == "" { |  | ||||||
| 			nt3CheckType = "ipv4" |  | ||||||
| 		} |  | ||||||
| 	} else if strings.Contains(ipInfo, "IPV6") { |  | ||||||
| 		uts.IPV6 = true |  | ||||||
| 		uts.IPV4 = false |  | ||||||
| 		if nt3CheckType == "" { |  | ||||||
| 			nt3CheckType = "ipv6" |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	if nt3CheckType == "ipv4" && !strings.Contains(ipInfo, "IPV4") && strings.Contains(ipInfo, "IPV6") { |  | ||||||
| 		nt3CheckType = "ipv6" |  | ||||||
| 	} else if nt3CheckType == "ipv6" && !strings.Contains(ipInfo, "IPV6") && strings.Contains(ipInfo, "IPV4") { |  | ||||||
| 		nt3CheckType = "ipv4" |  | ||||||
| 	} |  | ||||||
| 	basicInfo = strings.ReplaceAll(basicInfo, "\n\n", "\n") |  | ||||||
| 	return basicInfo, securityInfo, nt3CheckType |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // CaptureOutput 捕获函数输出和错误输出,实时输出,并返回字符串 |  | ||||||
| func CaptureOutput(f func()) string { |  | ||||||
| 	// 保存旧的 stdout 和 stderr |  | ||||||
| 	oldStdout := os.Stdout |  | ||||||
| 	oldStderr := os.Stderr |  | ||||||
| 	// 创建管道 |  | ||||||
| 	stdoutPipeR, stdoutPipeW, err := os.Pipe() |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "Error creating stdout pipe" |  | ||||||
| 	} |  | ||||||
| 	stderrPipeR, stderrPipeW, err := os.Pipe() |  | ||||||
| 	if err != nil { |  | ||||||
| 		stdoutPipeW.Close() |  | ||||||
| 		stdoutPipeR.Close() |  | ||||||
| 		return "Error creating stderr pipe" |  | ||||||
| 	} |  | ||||||
| 	// 替换标准输出和标准错误输出为管道写入端 |  | ||||||
| 	os.Stdout = stdoutPipeW |  | ||||||
| 	os.Stderr = stderrPipeW |  | ||||||
| 	// 恢复标准输出和标准错误输出 |  | ||||||
| 	defer func() { |  | ||||||
| 		os.Stdout = oldStdout |  | ||||||
| 		os.Stderr = oldStderr |  | ||||||
| 		stdoutPipeW.Close() |  | ||||||
| 		stderrPipeW.Close() |  | ||||||
| 		stdoutPipeR.Close() |  | ||||||
| 		stderrPipeR.Close() |  | ||||||
| 	}() |  | ||||||
| 	// 缓冲区 |  | ||||||
| 	var stdoutBuf, stderrBuf bytes.Buffer |  | ||||||
| 	// 并发读取 stdout 和 stderr |  | ||||||
| 	done := make(chan struct{}) |  | ||||||
| 	go func() { |  | ||||||
| 		multiWriter := io.MultiWriter(&stdoutBuf, oldStdout) |  | ||||||
| 		io.Copy(multiWriter, stdoutPipeR) |  | ||||||
| 		done <- struct{}{} |  | ||||||
| 	}() |  | ||||||
| 	go func() { |  | ||||||
| 		multiWriter := io.MultiWriter(&stderrBuf, oldStderr) |  | ||||||
| 		io.Copy(multiWriter, stderrPipeR) |  | ||||||
| 		done <- struct{}{} |  | ||||||
| 	}() |  | ||||||
| 	// 执行函数 |  | ||||||
| 	f() |  | ||||||
| 	// 关闭管道写入端,让管道读取端可以读取所有数据 |  | ||||||
| 	stdoutPipeW.Close() |  | ||||||
| 	stderrPipeW.Close() |  | ||||||
| 	// 等待两个 goroutine 完成 |  | ||||||
| 	<-done |  | ||||||
| 	<-done |  | ||||||
| 	// 返回捕获的输出字符串 |  | ||||||
| 	// stderrBuf.String() |  | ||||||
| 	return stdoutBuf.String() |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // PrintAndCapture 捕获函数输出的同时打印内容 |  | ||||||
| func PrintAndCapture(f func(), tempOutput, output string) string { |  | ||||||
| 	tempOutput = CaptureOutput(f) |  | ||||||
| 	output += tempOutput |  | ||||||
| 	return output |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // UploadText 上传文本内容到指定URL |  | ||||||
| func UploadText(absPath string) (string, string, error) { |  | ||||||
| 	primaryURL := "http://hpaste.spiritlhl.net/api/UL/upload" |  | ||||||
| 	backupURL := "https://paste.spiritlhl.net/api/UL/upload" |  | ||||||
| 	token := network.SecurityUploadToken |  | ||||||
| 	client := req.C().SetTimeout(6 * time.Second) |  | ||||||
| 	client.R(). |  | ||||||
| 		SetRetryCount(2). |  | ||||||
| 		SetRetryBackoffInterval(1*time.Second, 5*time.Second). |  | ||||||
| 		SetRetryFixedInterval(2 * time.Second) |  | ||||||
| 	file, err := os.Open(absPath) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", "", fmt.Errorf("failed to open file: %w", err) |  | ||||||
| 	} |  | ||||||
| 	defer file.Close() |  | ||||||
| 	upload := func(url string) (string, string, error) { |  | ||||||
| 		file, err := os.Open(absPath) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return "", "", fmt.Errorf("failed to re-open file for %s: %w", url, err) |  | ||||||
| 		} |  | ||||||
| 		defer file.Close() |  | ||||||
| 		content, err := io.ReadAll(file) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return "", "", fmt.Errorf("failed to read file content for %s: %w", url, err) |  | ||||||
| 		} |  | ||||||
| 		resp, err := client.R(). |  | ||||||
| 			SetHeader("Authorization", token). |  | ||||||
| 			SetFileBytes("file", filepath.Base(absPath), content). |  | ||||||
| 			Post(url) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return "", "", fmt.Errorf("failed to make request to %s: %w", url, err) |  | ||||||
| 		} |  | ||||||
| 		if resp.StatusCode >= 200 && resp.StatusCode <= 299 && resp.String() != "" { |  | ||||||
| 			fileID := strings.TrimSpace(resp.String()) |  | ||||||
| 			if strings.Contains(fileID, "show") { |  | ||||||
| 				fileID = fileID[strings.LastIndex(fileID, "/")+1:] |  | ||||||
| 			} |  | ||||||
| 			httpURL := fmt.Sprintf("http://hpaste.spiritlhl.net/#/show/%s", fileID) |  | ||||||
| 			httpsURL := fmt.Sprintf("https://paste.spiritlhl.net/#/show/%s", fileID) |  | ||||||
| 			return httpURL, httpsURL, nil |  | ||||||
| 		} |  | ||||||
| 		return "", "", fmt.Errorf("upload failed for %s with status code: %d", url, resp.StatusCode) |  | ||||||
| 	} |  | ||||||
| 	httpURL, httpsURL, err := upload(primaryURL) |  | ||||||
| 	if err == nil { |  | ||||||
| 		return httpURL, httpsURL, nil |  | ||||||
| 	} |  | ||||||
| 	httpURL, httpsURL, err = upload(backupURL) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return "", "", fmt.Errorf("failed to upload to both primary and backup URLs: %w", err) |  | ||||||
| 	} |  | ||||||
| 	return httpURL, httpsURL, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // ProcessAndUpload 创建结果文件并上传文件 |  | ||||||
| func ProcessAndUpload(output string, filePath string, enableUplaod bool) (string, string) { |  | ||||||
| 	// 使用 defer 来处理 panic |  | ||||||
| 	defer func() { |  | ||||||
| 		if r := recover(); r != nil { |  | ||||||
| 			fmt.Printf("处理上传时发生错误: %v\n", r) |  | ||||||
| 		} |  | ||||||
| 	}() |  | ||||||
| 	// 检查文件是否存在 |  | ||||||
| 	if _, err := os.Stat(filePath); err == nil { |  | ||||||
| 		// 文件存在,删除文件 |  | ||||||
| 		err = os.Remove(filePath) |  | ||||||
| 		if err != nil { |  | ||||||
| 			fmt.Println("无法删除文件:", err) |  | ||||||
| 			return "", "" |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	// 创建文件 |  | ||||||
| 	file, err := os.Create(filePath) |  | ||||||
| 	if err != nil { |  | ||||||
| 		fmt.Println("无法创建文件:", err) |  | ||||||
| 		return "", "" |  | ||||||
| 	} |  | ||||||
| 	defer file.Close() |  | ||||||
| 	// 匹配 ANSI 转义序列 |  | ||||||
| 	ansiRegex := regexp.MustCompile("\x1B\\[[0-9;]+[a-zA-Z]") |  | ||||||
| 	// 移除 ANSI 转义序列 |  | ||||||
| 	cleanedOutput := ansiRegex.ReplaceAllString(output, "") |  | ||||||
| 	// 使用 bufio.Writer 提高写入效率 |  | ||||||
| 	writer := bufio.NewWriter(file) |  | ||||||
| 	_, err = writer.WriteString(cleanedOutput) |  | ||||||
| 	if err != nil { |  | ||||||
| 		fmt.Println("无法写入文件:", err) |  | ||||||
| 		return "", "" |  | ||||||
| 	} |  | ||||||
| 	// 确保写入缓冲区的数据都刷新到文件中 |  | ||||||
| 	err = writer.Flush() |  | ||||||
| 	if err != nil { |  | ||||||
| 		fmt.Println("无法刷新文件缓冲:", err) |  | ||||||
| 		return "", "" |  | ||||||
| 	} |  | ||||||
| 	fmt.Printf("测试结果已写入 %s\n", filePath) |  | ||||||
| 	if enableUplaod { |  | ||||||
| 		// 获取文件的绝对路径 |  | ||||||
| 		absPath, err := filepath.Abs(filePath) |  | ||||||
| 		if err != nil { |  | ||||||
| 			fmt.Println("无法获取文件绝对路径:", err) |  | ||||||
| 			return "", "" |  | ||||||
| 		} |  | ||||||
| 		// 上传文件并生成短链接 |  | ||||||
| 		http_url, https_url, err := UploadText(absPath) |  | ||||||
| 		if err != nil { |  | ||||||
| 			fmt.Println("上传失败,无法生成链接") |  | ||||||
| 			fmt.Println(err.Error()) |  | ||||||
| 			return "", "" |  | ||||||
| 		} |  | ||||||
| 		return http_url, https_url |  | ||||||
| 	} |  | ||||||
| 	return "", "" |  | ||||||
| } |  | ||||||
		Reference in New Issue
	
	Block a user