mirror of
				https://github.com/oneclickvirt/ecs.git
				synced 2025-10-31 10:56:37 +08:00 
			
		
		
		
	Compare commits
	
		
			259 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 | 
							
								
								
									
										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}} -checklinkname=0 | ||||
|     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}} -checklinkname=0 | ||||
|     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}} -checklinkname=0 | ||||
|     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>. | ||||
							
								
								
									
										315
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										315
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,300 +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: 每个测试项目有对应的维护仓库,自行点击查看仓库说明 | ||||
|  | ||||
| #### Q: 测试进行到一半如何手动终止? | ||||
|  | ||||
| #### A: 按ctrl键和c键终止程序,终止后依然会在当前目录下生成goecs.txt文件和分享链接,里面是已经测试到的信息。 | ||||
|  | ||||
| #### Q: 非Root环境如何进行测试? | ||||
|  | ||||
| #### A: 手动执行安装命令,实在装不上也没问题,直接在release中下载对应架构的压缩包解压后执行即可,只要你能执行的了文件。或者你能使用docker的话用docker执行。 | ||||
| ## 数据来源 | ||||
|  | ||||
| 数据来源于用户提交的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.23.4 | ||||
|  | ||||
| require ( | ||||
| 	github.com/imroc/req/v3 v3.49.0 | ||||
| 	github.com/oneclickvirt/CommonMediaTests v0.0.4-20240704024502 | ||||
| 	github.com/oneclickvirt/UnlockTests v0.0.23-20250118070445 | ||||
| 	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-20250118032755 | ||||
| 	github.com/oneclickvirt/pingtest v0.0.5-20241006123443 | ||||
| 	github.com/oneclickvirt/portchecker v0.0.3-20250118031146 | ||||
| 	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.14 // 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.7 // 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.7.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.12.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.32.0 // indirect | ||||
| 	golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect | ||||
| 	golang.org/x/mod v0.22.0 // indirect | ||||
| 	golang.org/x/net v0.34.0 // indirect | ||||
| 	golang.org/x/sync v0.10.0 // indirect | ||||
| 	golang.org/x/sys v0.29.0 // indirect | ||||
| 	golang.org/x/term v0.28.0 // indirect | ||||
| 	golang.org/x/text v0.21.0 // indirect | ||||
| 	golang.org/x/tools v0.29.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 | ||||
| ) | ||||
							
								
								
									
										328
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										328
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,328 +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.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= | ||||
| github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= | ||||
| 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.7 h1:ZnTbPrPqpyeraCvUyNbQTNyl4Gz3NRQDh06WdIIHh90= | ||||
| github.com/nxtrace/NTrace-core v1.3.7/go.mod h1:aW2owz9I+W5i+gJEDmnWli75mB+fuO4UTwdOPMcQHpE= | ||||
| 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.23-20250118070445 h1:lQmYC12cbE6kMNs+onwS+aC+34aXQToSiAf+oOXweBc= | ||||
| github.com/oneclickvirt/UnlockTests v0.0.23-20250118070445/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-20250118032755 h1:phF5kngqoPnS6onIbKII9ndw/N5l3ciIgR7fK5mfSBk= | ||||
| github.com/oneclickvirt/nt3 v0.0.4-20250118032755/go.mod h1:lDpHmjWwLZd6pO8cZCwzrc5rwd8dK+TIYk+DGmRpSGg= | ||||
| 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.3-20250118031146 h1:tXTcpq8P3KgQupEZhPcAwdiyXYy//jXGb28bp4bSt4g= | ||||
| github.com/oneclickvirt/portchecker v0.0.3-20250118031146/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.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= | ||||
| github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= | ||||
| 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.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= | ||||
| github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= | ||||
| 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.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= | ||||
| golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= | ||||
| golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA= | ||||
| golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU= | ||||
| 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.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= | ||||
| golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= | ||||
| 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.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.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= | ||||
| golang.org/x/sys v0.29.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.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= | ||||
| golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= | ||||
| 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.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= | ||||
| golang.org/x/time v0.8.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.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE= | ||||
| golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= | ||||
| 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.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= | ||||
| google.golang.org/protobuf v1.36.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.7" | ||||
| 	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.7" | ||||
|         ECS_VERSION="0.1.7" | ||||
|     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