mirror of
				https://github.com/oneclickvirt/ecs.git
				synced 2025-10-31 02:46:40 +08:00 
			
		
		
		
	Compare commits
	
		
			43 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 487dd7c1d2 | ||
|   | 2dbf97de8c | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | 97e7cae2c2 | ||
|   | d61a1879f5 | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | 29dd4ac57e | ||
|   | dc3eff1fe3 | ||
|   | 0a0f2199bc | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | 91004d87f5 | ||
|   | 8f41c37203 | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | 12b1ae0702 | ||
|   | 653cd75a97 | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | ea36e88c9f | ||
|   | c81ebb3c7a | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | 7896b3ead5 | ||
|   | eb98a7b857 | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | d4d86229de | ||
|   | 651a183382 | ||
|   | afc313a2a8 | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | 39ac8d198d | ||
|   | a70dc2bab1 | ||
|   | 5041a16a9a | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | 21deb3587e | ||
|   | 3bac30edc2 | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | 9ef2ec4a9e | ||
|   | ac33e00e0a | ||
|   | 7a439f7095 | ||
|   | e3bfa65f66 | ||
|   | 74e33a212c | ||
|   | bba8595033 | ||
|   | 530181be87 | ||
| ![github-actions[bot]](/assets/img/avatar_default.png)  | 7deb986209 | ||
|   | ea8e5efbd3 | ||
|   | db88ee8479 | ||
|   | 67a3bfdaad | ||
|   | e57ce05c0c | ||
|   | 62137f0bb1 | ||
|   | f9d8c9ee4c | ||
|   | 913a1725c1 | ||
|   | f1acbd361b | ||
|   | d6f62f8624 | ||
|   | 94da890522 | ||
|   | 42b94260de | ||
|   | 16e2603176 | 
							
								
								
									
										2
									
								
								.github/workflows/build_docker.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/build_docker.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,8 @@ | ||||
| name: Build and Push Docker Image | ||||
|  | ||||
| on: | ||||
|   release: | ||||
|     types: [ published ] | ||||
|   workflow_dispatch: | ||||
|  | ||||
| jobs: | ||||
|   | ||||
							
								
								
									
										31
									
								
								.github/workflows/main.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										31
									
								
								.github/workflows/main.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -40,3 +40,34 @@ jobs: | ||||
|         env: | ||||
|           GITHUB_TOKEN: ${{ secrets.GHT }} | ||||
|           GOPRIVATE: github.com/oneclickvirt/security | ||||
|  | ||||
|       - name: Update goecs.sh with new version | ||||
|         run: | | ||||
|           if [[ "$GITHUB_REF" == refs/tags/* ]]; then | ||||
|             VERSION="${GITHUB_REF#refs/tags/v}" | ||||
|           else | ||||
|             VERSION=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//' || echo "0.1.37") | ||||
|           fi | ||||
|           echo "Using version: $VERSION" | ||||
|           FILE="goecs.sh" | ||||
|           BRANCH="master" | ||||
|           git config --global user.name "github-actions[bot]" | ||||
|           git config --global user.email "github-actions[bot]@users.noreply.github.com" | ||||
|           git config --global --unset url."git@github.com:".insteadOf || true | ||||
|           git fetch origin $BRANCH | ||||
|           git checkout $BRANCH | ||||
|           if [ ! -f "$FILE" ]; then | ||||
|             echo "Error: $FILE not found" | ||||
|             exit 1 | ||||
|           fi | ||||
|           sed -i "s/\(_yellow \"Unable to get version info, using default version \).*\(\".*\)/\1$VERSION\2/" "$FILE" | ||||
|           sed -i "s/\(ECS_VERSION=\"\).*\(\"\)/\1$VERSION\2/" "$FILE" | ||||
|           if git diff --quiet "$FILE"; then | ||||
|             echo "No changes detected in $FILE" | ||||
|             exit 0 | ||||
|           fi | ||||
|           git add "$FILE" | ||||
|           git commit -m "chore: update ECS_VERSION to $VERSION in goecs.sh" | ||||
|           git push origin $BRANCH | ||||
|         env: | ||||
|           GITHUB_TOKEN: ${{ secrets.GHT }} | ||||
							
								
								
									
										8
									
								
								.github/workflows/public-build.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.github/workflows/public-build.yml
									
									
									
									
										vendored
									
									
								
							| @@ -1,6 +1,8 @@ | ||||
| name: Public Build | ||||
|  | ||||
| on: | ||||
|   release: | ||||
|     types: [ published ] | ||||
|   workflow_dispatch: | ||||
|  | ||||
| jobs: | ||||
| @@ -67,10 +69,10 @@ jobs: | ||||
|     - name: Build and Test | ||||
|       run: | | ||||
|         # 构建二进制文件 | ||||
|         go build -o main | ||||
|          | ||||
|         go build -o maintest | ||||
|         # 测试无菜单模式是否正常运行(禁用 security 检测) | ||||
|         ./main -menu=false -l en -security=false -upload=false || exit 1 | ||||
|         ./maintest -menu=false -l en -security=false -upload=false || exit 1 | ||||
|         rm -rf maintest | ||||
|          | ||||
|     - name: Commit and push changes | ||||
|       run: | | ||||
|   | ||||
| @@ -6,7 +6,9 @@ builds: | ||||
|     env: | ||||
|       - CGO_ENABLED=0 | ||||
|     ldflags: | ||||
|       - -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 | ||||
|       - -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 -extldflags=-static | ||||
|     flags: | ||||
|       - -trimpath | ||||
|     goos: | ||||
|       - linux | ||||
|       - windows | ||||
| @@ -34,6 +36,8 @@ builds: | ||||
|       - CXX=o64-clang++ | ||||
|     ldflags: | ||||
|       - -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 | ||||
|     flags: | ||||
|       - -trimpath | ||||
|     goos: | ||||
|       - darwin | ||||
|     goarch: | ||||
| @@ -47,6 +51,8 @@ builds: | ||||
|       - CXX=oa64-clang++ | ||||
|     ldflags: | ||||
|       - -s -w -X main.version={{.Version}} -X main.arch={{.Arch}} -checklinkname=0 | ||||
|     flags: | ||||
|       - -trimpath | ||||
|     goos: | ||||
|       - darwin | ||||
|     goarch: | ||||
|   | ||||
							
								
								
									
										36
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										36
									
								
								README.md
									
									
									
									
									
								
							| @@ -2,6 +2,8 @@ | ||||
|  | ||||
| [](https://github.com/oneclickvirt/ecs/actions/workflows/main.yaml) | ||||
|  | ||||
| [](https://app.fossa.com/projects/git%2Bgithub.com%2Foneclickvirt%2Fecs?ref=badge_shield) | ||||
|  | ||||
| [](https://hits.spiritlhl.net) | ||||
|  | ||||
| 融合怪测评项目 - GO版本 | ||||
| @@ -31,7 +33,7 @@ Shell 版本:[https://github.com/spiritLHLS/ecs](https://github.com/spiritLHLS | ||||
| | arm                       | arm       | Windows | Windows   | | ||||
| | arm64                     | arm64     | FreeBSD | FreeBSD   | | ||||
| | 386                       | 386       | OpenBSD |           | | ||||
| | mips                      |           | MacOS   |           | | ||||
| | mips                      |           | MacOS   | MacOS     | | ||||
| | mipsle                    |           |         |           | | ||||
| | s390x                     | s390x     |         |           | | ||||
| | riscv64                   |           |         |           | | ||||
| @@ -41,8 +43,8 @@ Shell 版本:[https://github.com/spiritLHLS/ecs](https://github.com/spiritLHLS | ||||
|  | ||||
| ### **待支持的系统** | ||||
| | 系统 | 说明                        | | ||||
| |-----|--------------------------| | ||||
| | MacOS | 存在硬件测试 BUG 未修复,存在环境依赖未修复 | | ||||
| |-----|---------------------------| | ||||
| | Android(arm64) | 存在权限问题未修复,非安卓系统的ARM架构无问题  | | ||||
|  | ||||
| --- | ||||
|  | ||||
| @@ -91,6 +93,12 @@ Shell 版本:[https://github.com/spiritLHLS/ecs](https://github.com/spiritLHLS | ||||
|   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 | ||||
|   ``` | ||||
|  | ||||
| - **短链接:** | ||||
|  | ||||
|   ```bash | ||||
|   export noninteractive=true && curl -L https://bash.spiritlhl.net/goecs -o goecs.sh && chmod +x goecs.sh && bash goecs.sh env && bash goecs.sh install && goecs | ||||
|   ``` | ||||
|  | ||||
| #### **详细说明** | ||||
|  | ||||
| **详细说明**中的命令**可控制是否安装依赖**,**是否更新包管理器**,**默认互动模式可进行选择** | ||||
| @@ -130,19 +138,19 @@ Shell 版本:[https://github.com/spiritLHLS/ecs](https://github.com/spiritLHLS | ||||
|    export noninteractive=true && ./goecs.sh env | ||||
|    ``` | ||||
|  | ||||
| 3. **安装 `goecs`** | ||||
| 3. **安装 `goecs` 本体(仅下载二进制文件无依赖安装)** | ||||
|  | ||||
|    ```bash | ||||
|    ./goecs.sh install | ||||
|    ``` | ||||
|  | ||||
| 4. **升级 `goecs`** | ||||
| 4. **升级 `goecs` 本体** | ||||
|  | ||||
|    ```bash | ||||
|    ./goecs.sh upgrade | ||||
|    ``` | ||||
|  | ||||
| 5. **卸载 `goecs`** | ||||
| 5. **卸载 `goecs` 本体** | ||||
|  | ||||
|    ```bash | ||||
|    ./goecs.sh uninstall | ||||
| @@ -334,6 +342,7 @@ GOOS=darwin GOARCH=amd64 go build -o goecs_darwin | ||||
| | 测试稳定性       | 核心测试组件10年以上未变 | 每个大版本更新测试项,分数不同版本间难以对比(每个版本对标当前最好的CPU) | | ||||
| | 测试内容         | 仅测试计算性能 | 覆盖多种性能测试,分数加权计算,但部分测试实际不常用 | | ||||
| | 适用场景         | 适合快速测试,仅测试计算性能 | 适合综合全面的测试 | | ||||
| | 排行榜         | [sysbench.spiritlhl.net](https://sysbench.spiritlhl.net/) | [browser.geekbench.com](https://browser.geekbench.com/) | | ||||
|  | ||||
| 且```goecs```测试使用何种CPU测试方式可使用参数指定,默认只是为了更多用户快速测试的需求 | ||||
|  | ||||
| @@ -364,7 +373,7 @@ GOOS=darwin GOARCH=amd64 go build -o goecs_darwin | ||||
| 感谢 | ||||
|  | ||||
| <a href="https://h501.io/?from=69" target="_blank"> | ||||
|   <img src="https://github.com/spiritLHLS/ecs/assets/103393591/dfd47230-2747-4112-be69-b5636b34f07f" alt="h501"> | ||||
|   <img src="https://github.com/spiritLHLS/ecs/assets/103393591/dfd47230-2747-4112-be69-b5636b34f07f" alt="h501" style="height: 50px;"> | ||||
| </a> | ||||
|  | ||||
| 提供的免费托管支持本开源项目的共享测试结果存储 | ||||
| @@ -372,13 +381,20 @@ GOOS=darwin GOARCH=amd64 go build -o goecs_darwin | ||||
| 同时感谢以下平台提供编辑和测试支持 | ||||
|  | ||||
| <a href="https://www.jetbrains.com/go/" target="_blank"> | ||||
|   <img src="https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png" alt="goland"> | ||||
|   <img src="https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png" alt="goland" style="height: 50px;"> | ||||
| </a> | ||||
|  | ||||
| <a href="https://community.ibm.com/zsystems/form/l1cc-oss-vm-request/" target="_blank"> | ||||
|   <img src="https://linuxone.cloud.marist.edu/oss/resources/images/linuxonelogo03.png" alt="ibm"> | ||||
|   <img src="https://linuxone.cloud.marist.edu/oss/resources/images/linuxonelogo03.png" alt="ibm" style="height: 50px;"> | ||||
| </a> | ||||
|  | ||||
| <a href="https://console.zmto.com/?affid=1524" target="_blank"> | ||||
|   <img src="https://console.zmto.com/templates/2019/dist/images/logo_dark.svg" alt="zmto" style="height: 50px;"> | ||||
| </a> | ||||
|  | ||||
| ## Stargazers over time | ||||
|  | ||||
| [](https://starchart.cc/oneclickvirt/ecs) | ||||
| [](https://www.spiritlhl.net) | ||||
|  | ||||
| ## License | ||||
| [](https://app.fossa.com/projects/git%2Bgithub.com%2Foneclickvirt%2Fecs?ref=badge_large) | ||||
							
								
								
									
										25
									
								
								README_EN.md
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								README_EN.md
									
									
									
									
									
								
							| @@ -31,7 +31,7 @@ Shell version: [https://github.com/spiritLHLS/ecs/blob/main/README_EN.md](https: | ||||
| | arm                       | arm       | Windows                      | Windows   | | ||||
| | arm64                     | arm64     | FreeBSD                      | FreeBSD   | | ||||
| | 386                       | 386       | OpenBSD                      |           | | ||||
| | mips                      |           | MacOS                        |           | | ||||
| | mips                      |           | MacOS                        | MacOS     | | ||||
| | mipsle                    |           |                              |           | | ||||
| | s390x                     | s390x     |                              |           | | ||||
| | riscv64                   |           |                              |           | | ||||
| @@ -40,9 +40,8 @@ Shell version: [https://github.com/spiritLHLS/ecs/blob/main/README_EN.md](https: | ||||
|  | ||||
| ### **Systems Pending Support** | ||||
| | OS     | Notes                                                                                           | | ||||
| |--------|--------------------------------------------------------| | ||||
| | MacOS  | Hardware testing bugs and environment dependencies unresolved | | ||||
|  | ||||
| |--------|-------------------------------------------------------------------------------------------------| | ||||
| | Android(arm64) | Permission issues that are not fixed, no problems with ARM architecture for non-Android systems | | ||||
| --- | ||||
|  | ||||
| ## **Features** | ||||
| @@ -90,6 +89,12 @@ Shell version: [https://github.com/spiritLHLS/ecs/blob/main/README_EN.md](https: | ||||
|   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 -l en | ||||
|   ``` | ||||
|  | ||||
| - **Short Link:** | ||||
|  | ||||
|   ```bash | ||||
|   export noninteractive=true && curl -L https://bash.spiritlhl.net/goecs -o goecs.sh && chmod +x goecs.sh && bash goecs.sh env && bash goecs.sh install && goecs | ||||
|   `` | ||||
|  | ||||
| #### **Detailed instructions** | ||||
|  | ||||
| **Detailed description** of the commands in **Command **Controls whether to install dependencies**, **Whether to update the package manager**, **Default interaction mode can be selected*** | ||||
| @@ -362,7 +367,7 @@ Thank [he.net](https://he.net) [bgp.tools](https://bgp.tools) [ipinfo.io](https: | ||||
| Thank | ||||
|  | ||||
| <a href="https://h501.io/?from=69" target="_blank"> | ||||
|   <img src="https://github.com/spiritLHLS/ecs/assets/103393591/dfd47230-2747-4112-be69-b5636b34f07f" alt="h501"> | ||||
|   <img src="https://github.com/spiritLHLS/ecs/assets/103393591/dfd47230-2747-4112-be69-b5636b34f07f" alt="h501" style="height: 50px;"> | ||||
| </a> | ||||
|  | ||||
| provided free hosting support for this open source project's shared test results storage | ||||
| @@ -370,13 +375,17 @@ provided free hosting support for this open source project's shared test results | ||||
| Thanks also to the following platforms for editorial and testing support | ||||
|  | ||||
| <a href="https://www.jetbrains.com/go/" target="_blank"> | ||||
|   <img src="https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png" alt="goland"> | ||||
|   <img src="https://resources.jetbrains.com/storage/products/company/brand/logos/GoLand.png" alt="goland" style="height: 50px;"> | ||||
| </a> | ||||
|  | ||||
| <a href="https://community.ibm.com/zsystems/form/l1cc-oss-vm-request/" target="_blank"> | ||||
|   <img src="https://linuxone.cloud.marist.edu/oss/resources/images/linuxonelogo03.png" alt="ibm"> | ||||
|   <img src="https://linuxone.cloud.marist.edu/oss/resources/images/linuxonelogo03.png" alt="ibm" style="height: 50px;"> | ||||
| </a> | ||||
|  | ||||
| <a href="https://console.zmto.com/?affid=1524" target="_blank"> | ||||
|   <img src="https://console.zmto.com/templates/2019/dist/images/logo_dark.svg" alt="zmto" style="height: 50px;"> | ||||
| </a> | ||||
|  | ||||
| ## Stargazers over time | ||||
|  | ||||
| [](https://starchart.cc/oneclickvirt/ecs) | ||||
| [](https://www.spiritlhl.net) | ||||
| @@ -110,6 +110,8 @@ AMD的7950x单核满血性能得分在6500左右,AMD的5950x单核满血性能 | ||||
|  | ||||
| 有时候多核得分和单核得分一样,证明商家在限制程序并发使用CPU,典型例子腾讯云。 | ||||
|  | ||||
| ```Sysbench```的基准可见 [CPU Performance Ladder For Sysbench](https://sysbench.spiritlhl.net/) 天梯图,具体得分不分测试的sysbench的版本。 | ||||
|  | ||||
| ```GeekBench```的基准可见 [官方网站](https://browser.geekbench.com/processor-benchmarks/) 天梯图,具体得分每个```GeekBench```版本都不一样,注意使用时测试的```GeekBench```版本是什么。 | ||||
|  | ||||
| 多说一句,```GeekBench```测的很多内容,实际在服务器使用过程中根本用不到,测试仅供参考。当然```Sysbench```非常不全面,但它基于最基础的计算性能可以大致比较CPU的性能。 | ||||
| @@ -279,6 +281,8 @@ AMD's 7950x single-core full performance score is around 6500, AMD's 5950x singl | ||||
|  | ||||
| Sometimes multi-core scores are the same as single-core scores, proving that the vendor is limiting program concurrent use of CPU, a typical example being Tencent Cloud. | ||||
|  | ||||
| Benchmarks for ```Sysbench`` can be found in the [CPU Performance Ladder For Sysbench](https://sysbench.spiritlhl.net/) ladder chart, with specific scores regardless of the version of sysbench tested. | ||||
|  | ||||
| For `GeekBench` baselines, see the [official website](https://browser.geekbench.com/processor-benchmarks/) ladder chart. Specific scores differ for each `GeekBench` version, so note which `GeekBench` version is being used when testing. | ||||
|  | ||||
| As an additional note, many things tested by `GeekBench` are not actually used in server usage processes, so the test is for reference only. Of course, `Sysbench` is very incomplete, but it can roughly compare CPU performance based on the most basic computational performance. | ||||
| @@ -424,6 +428,8 @@ AMDの7950xシングルコアのフルパフォーマンススコアは約6500 | ||||
|  | ||||
| 時々、マルチコアスコアとシングルコアスコアが同じ場合があります。これは販売者がプログラムの並列CPU使用を制限していることを示しています。典型的な例はTencent Cloudです。 | ||||
|  | ||||
| Sysbenchのベンチマークは[CPU Performance Ladder For Sysbench](https://sysbench.spiritlhl.net/)のラダーチャートで見ることができる。 | ||||
|  | ||||
| ```GeekBench```の基準は[公式ウェブサイト](https://browser.geekbench.com/processor-benchmarks/)の階層チャートを参照してください。具体的なスコアは各```GeekBench```バージョンで異なるため、テスト時の```GeekBench```バージョンに注意してください。 | ||||
|  | ||||
| 補足ですが、```GeekBench```がテストする多くの内容は、サーバー使用過程で実際には必要ないことが多いです。テストは参考程度にしてください。もちろん```Sysbench```は非常に包括的ではありませんが、基本的な計算性能に基づいてCPUのパフォーマンスを大まかに比較できます。 | ||||
|   | ||||
| @@ -36,6 +36,6 @@ func DiskTest(language, testMethod, testPath string, isMultiCheck bool, autoChan | ||||
| 	if !strings.Contains(res, "\n") && res != "" { | ||||
| 		res += "\n" | ||||
| 	} | ||||
| 	fmt.Printf(res) | ||||
| 	fmt.Printf("%s", res) | ||||
| 	//fmt.Println("--------------------------------------------------") | ||||
| } | ||||
|   | ||||
| @@ -3,5 +3,5 @@ package disktest | ||||
| import "testing" | ||||
|  | ||||
| func TestDiskIoTest(t *testing.T) { | ||||
| 	DiskTest("zh", "sysbench", "", false) | ||||
| 	DiskTest("zh", "sysbench", "", false, false) | ||||
| } | ||||
|   | ||||
							
								
								
									
										21
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								go.mod
									
									
									
									
									
								
							| @@ -7,19 +7,19 @@ toolchain go1.24.2 | ||||
| require ( | ||||
| 	github.com/imroc/req/v3 v3.50.0 | ||||
| 	github.com/oneclickvirt/CommonMediaTests v0.0.4-20250329123841 | ||||
| 	github.com/oneclickvirt/UnlockTests v0.0.26-20250329125926 | ||||
| 	github.com/oneclickvirt/backtrace v0.0.5-20250413040820 | ||||
| 	github.com/oneclickvirt/basics v0.0.11-20250404123515 | ||||
| 	github.com/oneclickvirt/UnlockTests v0.0.27-20250628125053 | ||||
| 	github.com/oneclickvirt/backtrace v0.0.5-20250629024536 | ||||
| 	github.com/oneclickvirt/basics v0.0.13-20250629023612 | ||||
| 	github.com/oneclickvirt/cputest v0.0.10-20250404151448 | ||||
| 	github.com/oneclickvirt/defaultset v0.0.2-20240624082446 | ||||
| 	github.com/oneclickvirt/disktest v0.0.8-20250425015826 | ||||
| 	github.com/oneclickvirt/disktest v0.0.8-20250701092629 | ||||
| 	github.com/oneclickvirt/gostun v0.0.3-20250329105202 | ||||
| 	github.com/oneclickvirt/memorytest v0.0.5-20250406063420 | ||||
| 	github.com/oneclickvirt/memorytest v0.0.6-20250630141424.0.20250701022859-5967f9d8d3eb | ||||
| 	github.com/oneclickvirt/nt3 v0.0.5-20250416131047 | ||||
| 	github.com/oneclickvirt/pingtest v0.0.7-20250413051539 | ||||
| 	github.com/oneclickvirt/portchecker v0.0.3-20250329125750 | ||||
| 	github.com/oneclickvirt/security v0.0.4-20250401123241 | ||||
| 	github.com/oneclickvirt/speedtest v0.0.9-20250329130205 | ||||
| 	github.com/oneclickvirt/security v0.0.4-20250629033626 | ||||
| 	github.com/oneclickvirt/speedtest v0.0.9-20250521034111 | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| @@ -61,8 +61,9 @@ require ( | ||||
| 	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.4.0 // indirect | ||||
| 	github.com/oneclickvirt/dd v0.0.1-20250406062523 // indirect | ||||
| 	github.com/oneclickvirt/fio v0.0.1-20250406060851 // indirect | ||||
| 	github.com/oneclickvirt/dd v0.0.2-20250701085922 // indirect | ||||
| 	github.com/oneclickvirt/fio v0.0.2-20250701085933 // indirect | ||||
| 	github.com/oneclickvirt/mbw v0.0.1-20250630140849 // 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.4 // indirect | ||||
| @@ -106,7 +107,7 @@ require ( | ||||
| 	golang.org/x/mod v0.22.0 // indirect | ||||
| 	golang.org/x/net v0.39.0 // indirect | ||||
| 	golang.org/x/sync v0.13.0 // indirect | ||||
| 	golang.org/x/sys v0.32.0 // indirect | ||||
| 	golang.org/x/sys v0.33.0 // indirect | ||||
| 	golang.org/x/term v0.31.0 // indirect | ||||
| 	golang.org/x/text v0.24.0 // indirect | ||||
| 	golang.org/x/tools v0.29.0 // indirect | ||||
|   | ||||
							
								
								
									
										42
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										42
									
								
								go.sum
									
									
									
									
									
								
							| @@ -99,36 +99,38 @@ github.com/nxtrace/NTrace-core v1.4.0 h1:pDN2BqxIYjedDKCDDOFBcDNOBnavGcx+4wbiG65 | ||||
| github.com/nxtrace/NTrace-core v1.4.0/go.mod h1:0AWqbqiIJbpbFG6W48vtJ6pWM8PPF+lQ1fi2371y+zA= | ||||
| github.com/oneclickvirt/CommonMediaTests v0.0.4-20250329123841 h1:Zef93z9UiZQwRAKnnZYALmpBKvvuVaq34MEsuWwk6nc= | ||||
| github.com/oneclickvirt/CommonMediaTests v0.0.4-20250329123841/go.mod h1:DAmFPRjFV5p9fEzUUSml5jJGn2f1NZJQCzTxITHDjc4= | ||||
| github.com/oneclickvirt/UnlockTests v0.0.26-20250329125926 h1:H5//xwVjDR02bQ1hLa3G7LnwccsudPMjBVt7WCx2y/U= | ||||
| github.com/oneclickvirt/UnlockTests v0.0.26-20250329125926/go.mod h1:yXWIZB6iLS88pEd9m4QJi1GENn+7I91zA72y5ONz2Oc= | ||||
| github.com/oneclickvirt/backtrace v0.0.5-20250413040820 h1:kaAlk3A3IQOz9tuzc3H1ABIvAZnv81L1Ave5PaMuELY= | ||||
| github.com/oneclickvirt/backtrace v0.0.5-20250413040820/go.mod h1:5AH00bo41hH3d2/JVuCTlBkZUs3AXX4nlKVXb6piZcI= | ||||
| github.com/oneclickvirt/basics v0.0.11-20250404123515 h1:2kisK9tBG/Km/HLQFA82lm/B+AWFJp3drjrtGvz8lhw= | ||||
| github.com/oneclickvirt/basics v0.0.11-20250404123515/go.mod h1:yN1IEOXN6v/GJqJSA70Pooo6nXBI/6rq72vTY72wJMQ= | ||||
| github.com/oneclickvirt/UnlockTests v0.0.27-20250628125053 h1:Ug8kySZR1weRUcsnGOv+f3HAl791AfkA7EWV3JmiMQA= | ||||
| github.com/oneclickvirt/UnlockTests v0.0.27-20250628125053/go.mod h1:yXWIZB6iLS88pEd9m4QJi1GENn+7I91zA72y5ONz2Oc= | ||||
| github.com/oneclickvirt/backtrace v0.0.5-20250629024536 h1:caHCa0DHmbYWBFN1bqKxpvPnN0wOxDEqJv1VDvDdLWs= | ||||
| github.com/oneclickvirt/backtrace v0.0.5-20250629024536/go.mod h1:5AH00bo41hH3d2/JVuCTlBkZUs3AXX4nlKVXb6piZcI= | ||||
| github.com/oneclickvirt/basics v0.0.13-20250629023612 h1:cQg+cGBt2NMRrjhJPH+CbevZrwtMU8pIZIY2Lp6zmt4= | ||||
| github.com/oneclickvirt/basics v0.0.13-20250629023612/go.mod h1:yN1IEOXN6v/GJqJSA70Pooo6nXBI/6rq72vTY72wJMQ= | ||||
| github.com/oneclickvirt/cputest v0.0.10-20250404151448 h1:ovGtCwFXG0qmpyNDRqcNDIiAmhrtemCjIUXTJ1fPH0o= | ||||
| github.com/oneclickvirt/cputest v0.0.10-20250404151448/go.mod h1:MmaHN9+XMntI3rLycwj8Ne31fG18IfNoa8N2utDK1CY= | ||||
| github.com/oneclickvirt/dd v0.0.1-20250406062523 h1:jegTww4fuoFEqwFozvGJEqUNI/5ew3QJ0XcKZZ/zuTs= | ||||
| github.com/oneclickvirt/dd v0.0.1-20250406062523/go.mod h1:tImu9sPTkLWo2tf1dEN1xQzrylWKauj9hbU8PHfyAeU= | ||||
| github.com/oneclickvirt/dd v0.0.2-20250701085922 h1:WiWZwcnCPhRc8hLZdvkjD2kOEpnqn1S31z1j0x3V4l0= | ||||
| github.com/oneclickvirt/dd v0.0.2-20250701085922/go.mod h1:tImu9sPTkLWo2tf1dEN1xQzrylWKauj9hbU8PHfyAeU= | ||||
| 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.8-20250425015826 h1:bwVg0zysB3uCwQV+KIIQpuq2IJXWdIcdjD2+FiKPo5w= | ||||
| github.com/oneclickvirt/disktest v0.0.8-20250425015826/go.mod h1:sqVu6HwbnLmbnRj4389Xn08c301IhLnWCcbaEk2WzEc= | ||||
| github.com/oneclickvirt/fio v0.0.1-20250406060851 h1:b7xHKpPmU4q0NmvigRCEr3tQuAV/83ZIAGtHycLegw8= | ||||
| github.com/oneclickvirt/fio v0.0.1-20250406060851/go.mod h1:NIq+XYTey68KNERGIy/oRDlzpwLzBVoHOCiqX8didsE= | ||||
| github.com/oneclickvirt/disktest v0.0.8-20250701092629 h1:B/gA6SOr4qL5pQmVpHl9m5bn3paDcL7wJ1SZ7aY66M8= | ||||
| github.com/oneclickvirt/disktest v0.0.8-20250701092629/go.mod h1:6YCvGr+Z0tvcP4Ue8bezZqm/GqS/dSyEnSUhvS3Q03o= | ||||
| github.com/oneclickvirt/fio v0.0.2-20250701085933 h1:4P7QcOTxbqyx5DhHdFvyeRSsdNajSo9l/H2XK0vICIc= | ||||
| github.com/oneclickvirt/fio v0.0.2-20250701085933/go.mod h1:NIq+XYTey68KNERGIy/oRDlzpwLzBVoHOCiqX8didsE= | ||||
| github.com/oneclickvirt/gostun v0.0.3-20250329105202 h1:aJ6E91Lp94lq8iWRcCaxpXTjqOOaWvufr5oras6cFtM= | ||||
| github.com/oneclickvirt/gostun v0.0.3-20250329105202/go.mod h1:f7DPEXAxbmwXSW33dbxtb0/KzqvOBWhTs2Or5xBerQA= | ||||
| github.com/oneclickvirt/memorytest v0.0.5-20250406063420 h1:eHqpqFIx8Ss062uyNf7Ruv7FC4AdZbElR7u9vX2Oj3g= | ||||
| github.com/oneclickvirt/memorytest v0.0.5-20250406063420/go.mod h1:HTd0sSxRjT4BcV8kcCh4fF2Nia0xgZNaVjhefsnypic= | ||||
| github.com/oneclickvirt/mbw v0.0.1-20250630140849 h1:p6RMhOPBnQKAm9+VEQ2axAFsidrdSdrhXMyheIyv2a8= | ||||
| github.com/oneclickvirt/mbw v0.0.1-20250630140849/go.mod h1:0Vq6NRpyLmGUdfHfL3uDcFsuZhi7KlG+OCs5ky2757Y= | ||||
| github.com/oneclickvirt/memorytest v0.0.6-20250630141424.0.20250701022859-5967f9d8d3eb h1:EKYQWLw4um/C2xm6z8aDl+owWtvaIp0z7nlyMj62kkk= | ||||
| github.com/oneclickvirt/memorytest v0.0.6-20250630141424.0.20250701022859-5967f9d8d3eb/go.mod h1:7xMacjQobvFAtODht2hxTsB9hM2IFS7vZk3gxx+bsjo= | ||||
| github.com/oneclickvirt/nt3 v0.0.5-20250416131047 h1:KL0xowq19cW+FMBGMJxdqpRNoeyR+eEmb+jYSubmlTk= | ||||
| github.com/oneclickvirt/nt3 v0.0.5-20250416131047/go.mod h1:CVsDJEaIdyyZHn3WKbhU8Wn6GOfmBNvJlC/dDLRqcSQ= | ||||
| github.com/oneclickvirt/pingtest v0.0.7-20250413051539 h1:mYOsEmMtwKr40hwM2NimVLpnbR2cjwuOh1c/9fQr2Dw= | ||||
| github.com/oneclickvirt/pingtest v0.0.7-20250413051539/go.mod h1:d3Ntx5m9lMll3a/k3+2B+5emj//vgDh4/NHTxs2qQE8= | ||||
| github.com/oneclickvirt/portchecker v0.0.3-20250329125750 h1:TTNL0pnQlRsn046kW59I/9UWRpihttFHWnU7Ixycggk= | ||||
| github.com/oneclickvirt/portchecker v0.0.3-20250329125750/go.mod h1:HQxSTrqM8/QFqHMTBZ7S8H9eEO5FkUXU1eb7ZX5Mk+k= | ||||
| github.com/oneclickvirt/security v0.0.4-20250401123241 h1:myeAQ1wOKIHx5r9qs9dCwx/5FepY+hZu/fDNquMwKaw= | ||||
| github.com/oneclickvirt/security v0.0.4-20250401123241/go.mod h1:Cyo3hwh1irn6yWnTh+YVKtkkVzHlwZgm7t7qR3IPRQA= | ||||
| github.com/oneclickvirt/speedtest v0.0.9-20250329130205 h1:XWM6FhObi+2bEkntPcAAKkiS9w7r6j79DOtmlbq4hhs= | ||||
| github.com/oneclickvirt/speedtest v0.0.9-20250329130205/go.mod h1:zd5ZgIGslmtQLQehEfRjyumlvgDHTpCSMchKfKXoASI= | ||||
| github.com/oneclickvirt/security v0.0.4-20250629033626 h1:DEchQ7WKKz4CzQpMlweoqA993BGncvmp1rL1ICNDJ2g= | ||||
| github.com/oneclickvirt/security v0.0.4-20250629033626/go.mod h1:/5eVnZLvP7RUjwhoI6d8iIMP7msbkHC5So3ZxM+A7Zg= | ||||
| github.com/oneclickvirt/speedtest v0.0.9-20250521034111 h1:yygDk+s5qFhPMDRzdMfyopm1xU512peNqY6WYyvYcfY= | ||||
| github.com/oneclickvirt/speedtest v0.0.9-20250521034111/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= | ||||
| @@ -278,8 +280,8 @@ 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.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= | ||||
| golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= | ||||
| golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= | ||||
| golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= | ||||
| 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= | ||||
|   | ||||
							
								
								
									
										580
									
								
								goecs.go
									
									
									
									
									
								
							
							
						
						
									
										580
									
								
								goecs.go
									
									
									
									
									
								
							| @@ -5,9 +5,19 @@ import ( | ||||
| 	"context" | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"os/signal" | ||||
| 	"regexp" | ||||
| 	"runtime" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 	"syscall" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/oneclickvirt/CommonMediaTests/commediatests" | ||||
| 	unlocktestmodel "github.com/oneclickvirt/UnlockTests/model" | ||||
| 	"github.com/oneclickvirt/backtrace/bk" | ||||
| 	backtrace "github.com/oneclickvirt/backtrace/bk" | ||||
| 	backtracemodel "github.com/oneclickvirt/backtrace/model" | ||||
| 	basicmodel "github.com/oneclickvirt/basics/model" | ||||
| 	cputestmodel "github.com/oneclickvirt/cputest/model" | ||||
| @@ -26,19 +36,10 @@ import ( | ||||
| 	"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.33" | ||||
| 	ecsVersion                                                        = "v0.1.48" | ||||
| 	menuMode                                                          bool | ||||
| 	onlyChinaTest                                                     bool | ||||
| 	input, choice                                                     string | ||||
| @@ -79,7 +80,6 @@ func getMenuChoice(language string) string { | ||||
| 			return | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	for { | ||||
| 		go func() { | ||||
| 			var input string | ||||
| @@ -95,7 +95,7 @@ func getMenuChoice(language string) string { | ||||
| 		}() | ||||
| 		select { | ||||
| 		case input := <-inputChan: | ||||
| 			re := regexp.MustCompile(`^\d+$`) // 正则表达式匹配纯数字 | ||||
| 			re := regexp.MustCompile(`^\d+$`) | ||||
| 			if re.MatchString(input) { | ||||
| 				inChoice := input | ||||
| 				switch inChoice { | ||||
| @@ -121,7 +121,7 @@ func getMenuChoice(language string) string { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| func parseFlags() { | ||||
| 	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 默认启用菜单栏模式 | ||||
| @@ -149,15 +149,22 @@ func main() { | ||||
| 	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:]) | ||||
| } | ||||
|  | ||||
| func handleHelpAndVersion() bool { | ||||
| 	if help { | ||||
| 		fmt.Printf("Usage: %s [options]\n", os.Args[0]) | ||||
| 		goecsFlag.PrintDefaults() | ||||
| 		return | ||||
| 		return true | ||||
| 	} | ||||
| 	if showVersion { | ||||
| 		fmt.Println(ecsVersion) | ||||
| 		return | ||||
| 		return true | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|  | ||||
| func initLogger() { | ||||
| 	if enableLogger { | ||||
| 		gostunmodel.EnableLoger = true | ||||
| 		basicmodel.EnableLoger = true | ||||
| @@ -171,14 +178,72 @@ func main() { | ||||
| 		nt3model.EnableLoger = true | ||||
| 		speedtestmodel.EnableLoger = true | ||||
| 	} | ||||
| 	go func() { | ||||
| 		http.Get("https://hits.spiritlhl.net/goecs.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false") | ||||
| 	}() | ||||
| 	if menuMode { | ||||
| } | ||||
|  | ||||
| func handleMenuMode(preCheck utils.NetCheckResult) { | ||||
| 	basicStatus, cpuTestStatus, memoryTestStatus, diskTestStatus = false, false, false, false | ||||
| 	commTestStatus, utTestStatus, securityTestStatus, emailTestStatus = false, false, false, false | ||||
| 	backtraceStatus, nt3Status, speedTestStatus = false, false, false | ||||
| 	autoChangeDiskTestMethod = true | ||||
| 	printMenuOptions() | ||||
| Loop: | ||||
| 	for { | ||||
| 		choice = getMenuChoice(language) | ||||
| 		switch choice { | ||||
| 		case "1": | ||||
| 			setFullTestStatus(preCheck) | ||||
| 			onlyChinaTest = utils.CheckChina(enableLogger) | ||||
| 			break Loop | ||||
| 		case "2": | ||||
| 			setMinimalTestStatus(preCheck) | ||||
| 			break Loop | ||||
| 		case "3": | ||||
| 			setStandardTestStatus(preCheck) | ||||
| 			break Loop | ||||
| 		case "4": | ||||
| 			setNetworkFocusedTestStatus(preCheck) | ||||
| 			break Loop | ||||
| 		case "5": | ||||
| 			setUnlockFocusedTestStatus(preCheck) | ||||
| 			break Loop | ||||
| 		case "6": | ||||
| 			if !preCheck.Connected { | ||||
| 				fmt.Println("Can not test without network connection!") | ||||
| 				return | ||||
| 			} | ||||
| 			setNetworkOnlyTestStatus() | ||||
| 			break Loop | ||||
| 		case "7": | ||||
| 			if !preCheck.Connected { | ||||
| 				fmt.Println("Can not test without network connection!") | ||||
| 				return | ||||
| 			} | ||||
| 			setUnlockOnlyTestStatus() | ||||
| 			break Loop | ||||
| 		case "8": | ||||
| 			setHardwareOnlyTestStatus(preCheck) | ||||
| 			break Loop | ||||
| 		case "9": | ||||
| 			if !preCheck.Connected { | ||||
| 				fmt.Println("Can not test without network connection!") | ||||
| 				return | ||||
| 			} | ||||
| 			setIPQualityTestStatus() | ||||
| 			break Loop | ||||
| 		case "10": | ||||
| 			if !preCheck.Connected { | ||||
| 				fmt.Println("Can not test without network connection!") | ||||
| 				return | ||||
| 			} | ||||
| 			setRouteTestStatus() | ||||
| 			break Loop | ||||
| 		default: | ||||
| 			printInvalidChoice() | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func printMenuOptions() { | ||||
| 	switch language { | ||||
| 	case "zh": | ||||
| 		fmt.Println("VPS融合怪版本: ", ecsVersion) | ||||
| @@ -204,15 +269,14 @@ func main() { | ||||
| 		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": | ||||
| } | ||||
|  | ||||
| func setFullTestStatus(preCheck utils.NetCheckResult) { | ||||
| 	basicStatus = true | ||||
| 	cpuTestStatus = true | ||||
| 	memoryTestStatus = true | ||||
| 	diskTestStatus = true | ||||
| 	if preCheck.Connected { | ||||
| 		commTestStatus = true | ||||
| 		utTestStatus = true | ||||
| 		securityTestStatus = true | ||||
| @@ -220,80 +284,100 @@ func main() { | ||||
| 		backtraceStatus = true | ||||
| 		nt3Status = true | ||||
| 		speedTestStatus = true | ||||
| 				onlyChinaTest = utils.CheckChina(enableLogger) | ||||
| 				break Loop | ||||
| 			case "2": | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func setMinimalTestStatus(preCheck utils.NetCheckResult) { | ||||
| 	basicStatus = true | ||||
| 	cpuTestStatus = true | ||||
| 	memoryTestStatus = true | ||||
| 	diskTestStatus = true | ||||
| 	if preCheck.Connected { | ||||
| 		speedTestStatus = true | ||||
| 				break Loop | ||||
| 			case "3": | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func setStandardTestStatus(preCheck utils.NetCheckResult) { | ||||
| 	basicStatus = true | ||||
| 	cpuTestStatus = true | ||||
| 	memoryTestStatus = true | ||||
| 	diskTestStatus = true | ||||
| 	if preCheck.Connected { | ||||
| 		utTestStatus = true | ||||
| 		nt3Status = true | ||||
| 		speedTestStatus = true | ||||
| 				break Loop | ||||
| 			case "4": | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func setNetworkFocusedTestStatus(preCheck utils.NetCheckResult) { | ||||
| 	basicStatus = true | ||||
| 	cpuTestStatus = true | ||||
| 	memoryTestStatus = true | ||||
| 	diskTestStatus = true | ||||
| 	if preCheck.Connected { | ||||
| 		backtraceStatus = true | ||||
| 		nt3Status = true | ||||
| 		speedTestStatus = true | ||||
| 				break Loop | ||||
| 			case "5": | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func setUnlockFocusedTestStatus(preCheck utils.NetCheckResult) { | ||||
| 	basicStatus = true | ||||
| 	cpuTestStatus = true | ||||
| 	memoryTestStatus = true | ||||
| 	diskTestStatus = true | ||||
| 	if preCheck.Connected { | ||||
| 		commTestStatus = true | ||||
| 		utTestStatus = true | ||||
| 		speedTestStatus = true | ||||
| 				break Loop | ||||
| 			case "6": | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func setNetworkOnlyTestStatus() { | ||||
| 	securityTestStatus = true | ||||
| 	speedTestStatus = true | ||||
| 	backtraceStatus = true | ||||
| 	nt3Status = true | ||||
| 				break Loop | ||||
| 			case "7": | ||||
| } | ||||
|  | ||||
| func setUnlockOnlyTestStatus() { | ||||
| 	commTestStatus = true | ||||
| 	utTestStatus = true | ||||
| 	enabelUpload = false | ||||
| 				break Loop | ||||
| 			case "8": | ||||
| } | ||||
|  | ||||
| func setHardwareOnlyTestStatus(preCheck utils.NetCheckResult) { | ||||
| 	basicStatus = true | ||||
| 	cpuTestStatus = true | ||||
| 	memoryTestStatus = true | ||||
| 	diskTestStatus = true | ||||
| 	if preCheck.Connected { | ||||
| 		securityTestStatus = false | ||||
| 		autoChangeDiskTestMethod = false | ||||
| 				break Loop | ||||
| 			case "9": | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func setIPQualityTestStatus() { | ||||
| 	securityTestStatus = true | ||||
| 	emailTestStatus = true | ||||
| 				break Loop | ||||
| 			case "10": | ||||
| } | ||||
|  | ||||
| func setRouteTestStatus() { | ||||
| 	backtraceStatus = true | ||||
| 	nt3Status = true | ||||
| 	pingTestStatus = true | ||||
| 	enabelUpload = false | ||||
| 				break Loop | ||||
| 			default: | ||||
| } | ||||
|  | ||||
| func printInvalidChoice() { | ||||
| 	if language == "zh" { | ||||
| 		fmt.Println("无效的选项") | ||||
| 	} else { | ||||
| 		fmt.Println("Invalid choice") | ||||
| 	} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func handleLanguageSpecificSettings() { | ||||
| 	if language == "en" { | ||||
| 		backtraceStatus = false | ||||
| 		nt3Status = false | ||||
| @@ -301,51 +385,40 @@ func main() { | ||||
| 	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() | ||||
| } | ||||
|  | ||||
| func handleSignalInterrupt(sig chan os.Signal, startTime *time.Time, output *string, tempOutput string, uploadDone chan bool, outputMutex *sync.Mutex) { | ||||
| 	select { | ||||
| 	case <-sig: | ||||
| 		if !finish { | ||||
| 			endTime := time.Now() | ||||
| 				duration := endTime.Sub(startTime) | ||||
| 			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() { | ||||
| 			outputMutex.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() | ||||
| 				// 创建一个通道来传递上传结果 | ||||
| 			}, tempOutput, *output) | ||||
| 			outputMutex.Unlock() | ||||
| 			resultChan := make(chan struct { | ||||
| 				httpURL  string | ||||
| 				httpsURL string | ||||
| 				}, 1) // 使用带缓冲的通道,避免可能的阻塞 | ||||
| 				// 启动上传 | ||||
| 			}, 1) | ||||
| 			go func() { | ||||
| 					httpURL, httpsURL := utils.ProcessAndUpload(output, filePath, enabelUpload) | ||||
| 				outputMutex.Lock() | ||||
| 				finalOutput := *output | ||||
| 				outputMutex.Unlock() | ||||
| 				httpURL, httpsURL := utils.ProcessAndUpload(finalOutput, filePath, enabelUpload) | ||||
| 				resultChan <- struct { | ||||
| 					httpURL  string | ||||
| 					httpsURL string | ||||
| 				}{httpURL, httpsURL} | ||||
| 				uploadDone <- true | ||||
| 			}() | ||||
| 				// 等待上传完成或超时 | ||||
| 			select { | ||||
| 			case result := <-resultChan: | ||||
| 				if result.httpURL != "" || result.httpsURL != "" { | ||||
| @@ -355,7 +428,6 @@ func main() { | ||||
| 						fmt.Printf("上传成功!\nHttp URL:  %s\nHttps URL: %s\n", result.httpURL, result.httpsURL) | ||||
| 					} | ||||
| 				} | ||||
| 					// 给打印操作一些时间完成 | ||||
| 				time.Sleep(100 * time.Millisecond) | ||||
| 				if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { | ||||
| 					fmt.Println("Press Enter to exit...") | ||||
| @@ -363,7 +435,11 @@ func main() { | ||||
| 				} | ||||
| 				os.Exit(0) | ||||
| 			case <-time.After(30 * time.Second): | ||||
| 				if language == "en" { | ||||
| 					fmt.Println("Upload timeout, program exit") | ||||
| 				} else { | ||||
| 					fmt.Println("上传超时,程序退出") | ||||
| 				} | ||||
| 				if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { | ||||
| 					fmt.Println("Press Enter to exit...") | ||||
| 					fmt.Scanln() | ||||
| @@ -373,20 +449,101 @@ func main() { | ||||
| 		} | ||||
| 		os.Exit(0) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func runChineseTests(preCheck utils.NetCheckResult, wg1, wg2, wg3 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo *string, output, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) string { | ||||
| 	output = runBasicTests(preCheck, basicInfo, securityInfo, output, tempOutput, outputMutex) | ||||
| 	output = runCPUTest(output, tempOutput, outputMutex) | ||||
| 	output = runMemoryTest(output, tempOutput, outputMutex) | ||||
| 	output = runDiskTest(output, tempOutput, outputMutex) | ||||
| 	if (onlyChinaTest || pingTestStatus) && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" { | ||||
| 		wg3.Add(1) | ||||
| 		go func() { | ||||
| 			defer wg3.Done() | ||||
| 			*ptInfo = pt.PingTest() | ||||
| 		}() | ||||
| 	switch language { | ||||
| 	case "zh": | ||||
| 		output = utils.PrintAndCapture(func() { | ||||
| 	} | ||||
| 	if emailTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" { | ||||
| 		wg2.Add(1) | ||||
| 		go func() { | ||||
| 			defer wg2.Done() | ||||
| 			*emailInfo = email.EmailCheck() | ||||
| 		}() | ||||
| 	} | ||||
| 	if utTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" && !onlyChinaTest { | ||||
| 		wg1.Add(1) | ||||
| 		go func() { | ||||
| 			defer wg1.Done() | ||||
| 			*mediaInfo = unlocktest.MediaTest(language) | ||||
| 		}() | ||||
| 	} | ||||
| 	if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" { | ||||
| 		output = runStreamingTests(wg1, mediaInfo, output, tempOutput, outputMutex) | ||||
| 		output = runSecurityTests(*securityInfo, output, tempOutput, outputMutex) | ||||
| 		output = runEmailTests(wg2, emailInfo, output, tempOutput, outputMutex) | ||||
| 	} | ||||
| 	if runtime.GOOS != "windows" && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" { | ||||
| 		output = runNetworkTests(wg3, ptInfo, output, tempOutput, outputMutex) | ||||
| 	} | ||||
| 	if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" { | ||||
| 		output = runSpeedTests(output, tempOutput, outputMutex) | ||||
| 	} | ||||
| 	return appendTimeInfo(output, tempOutput, startTime, outputMutex) | ||||
| } | ||||
|  | ||||
| func runEnglishTests(preCheck utils.NetCheckResult, wg1, wg2 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo *string, output, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) string { | ||||
| 	output = runBasicTests(preCheck, basicInfo, securityInfo, output, tempOutput, outputMutex) | ||||
| 	output = runCPUTest(output, tempOutput, outputMutex) | ||||
| 	output = runMemoryTest(output, tempOutput, outputMutex) | ||||
| 	output = runDiskTest(output, tempOutput, outputMutex) | ||||
| 	if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" { | ||||
| 		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 = runStreamingTests(wg1, mediaInfo, output, tempOutput, outputMutex)  // 传递指针 | ||||
| 		output = runSecurityTests(*securityInfo, output, tempOutput, outputMutex) | ||||
| 		output = runEmailTests(wg2, emailInfo, output, tempOutput, outputMutex) | ||||
| 		output = runEnglishSpeedTests(output, tempOutput, outputMutex) | ||||
| 	} | ||||
| 	return appendTimeInfo(output, tempOutput, startTime, outputMutex) | ||||
| } | ||||
|  | ||||
| func runBasicTests(preCheck utils.NetCheckResult, basicInfo, securityInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string { | ||||
| 	return utils.PrintAndCapture(func() { | ||||
| 		utils.PrintHead(language, width, ecsVersion) | ||||
| 		if basicStatus || securityTestStatus { | ||||
| 			if basicStatus { | ||||
| 				if language == "zh" { | ||||
| 					utils.PrintCenteredTitle("系统基础信息", width) | ||||
| 				} else { | ||||
| 					utils.PrintCenteredTitle("System-Basic-Information", width) | ||||
| 				} | ||||
| 			} | ||||
| 			if preCheck.Connected && preCheck.StackType == "DualStack" { | ||||
| 				*basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, nt3CheckType, securityTestStatus) | ||||
| 			} else if preCheck.Connected && preCheck.StackType == "IPv4" { | ||||
| 				*basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "ipv4", securityTestStatus) | ||||
| 			} else if preCheck.Connected && preCheck.StackType == "IPv6" { | ||||
| 				*basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "ipv6", securityTestStatus) | ||||
| 			} else { | ||||
| 				*basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "", false) | ||||
| 				securityTestStatus = false | ||||
| 			} | ||||
| 				basicInfo, securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, nt3CheckType, securityTestStatus) | ||||
| 			if basicStatus { | ||||
| 					fmt.Printf(basicInfo) | ||||
| 				fmt.Printf("%s", *basicInfo) | ||||
| 			} else if (input == "6" || input == "9") && securityTestStatus { | ||||
| 					scanner := bufio.NewScanner(strings.NewReader(basicInfo)) | ||||
| 				scanner := bufio.NewScanner(strings.NewReader(*basicInfo)) | ||||
| 				for scanner.Scan() { | ||||
| 					line := scanner.Text() | ||||
| 					if strings.Contains(line, "IPV") { | ||||
| @@ -396,77 +553,107 @@ func main() { | ||||
| 			} | ||||
| 		} | ||||
| 	}, tempOutput, output) | ||||
| 		output = utils.PrintAndCapture(func() { | ||||
| } | ||||
|  | ||||
| func runCPUTest(output, tempOutput string, outputMutex *sync.Mutex) string { | ||||
| 	return utils.PrintAndCapture(func() { | ||||
| 		if cpuTestStatus { | ||||
| 			if language == "zh" { | ||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("CPU测试-通过%s测试", cpuTestMethod), width) | ||||
| 			} else { | ||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("CPU-Test--%s-Method", cpuTestMethod), width) | ||||
| 			} | ||||
| 			cputest.CpuTest(language, cpuTestMethod, cpuTestThreadMode) | ||||
| 		} | ||||
| 	}, tempOutput, output) | ||||
| 		output = utils.PrintAndCapture(func() { | ||||
| } | ||||
|  | ||||
| func runMemoryTest(output, tempOutput string, outputMutex *sync.Mutex) string { | ||||
| 	return utils.PrintAndCapture(func() { | ||||
| 		if memoryTestStatus { | ||||
| 			if language == "zh" { | ||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("内存测试-通过%s测试", memoryTestMethod), width) | ||||
| 			} else { | ||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("Memory-Test--%s-Method", memoryTestMethod), width) | ||||
| 			} | ||||
| 			memorytest.MemoryTest(language, memoryTestMethod) | ||||
| 		} | ||||
| 	}, tempOutput, output) | ||||
| 		output = utils.PrintAndCapture(func() { | ||||
| } | ||||
|  | ||||
| func runDiskTest(output, tempOutput string, outputMutex *sync.Mutex) string { | ||||
| 	return utils.PrintAndCapture(func() { | ||||
| 		if diskTestStatus && autoChangeDiskTestMethod { | ||||
| 			if language == "zh" { | ||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("硬盘测试-通过%s测试", diskTestMethod), width) | ||||
| 			} else { | ||||
| 				utils.PrintCenteredTitle(fmt.Sprintf("Disk-Test--%s-Method", diskTestMethod), width) | ||||
| 			} | ||||
| 			disktest.DiskTest(language, diskTestMethod, diskTestPath, diskMultiCheck, autoChangeDiskTestMethod) | ||||
| 		} else if diskTestStatus && !autoChangeDiskTestMethod { | ||||
| 			if language == "zh" { | ||||
| 				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) | ||||
| 			} else { | ||||
| 				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 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() { | ||||
| } | ||||
|  | ||||
| func runStreamingTests(wg1 *sync.WaitGroup, mediaInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string { | ||||
| 	return utils.PrintAndCapture(func() { | ||||
| 		if language == "zh" { | ||||
| 			if commTestStatus && !onlyChinaTest { | ||||
| 				utils.PrintCenteredTitle("御三家流媒体解锁", width) | ||||
| 				fmt.Printf(commediatests.MediaTests(language)) | ||||
| 				fmt.Printf("%s", commediatests.MediaTests(language)) | ||||
| 			} | ||||
| 		}, tempOutput, output) | ||||
| 		output = utils.PrintAndCapture(func() { | ||||
| 			if utTestStatus && !onlyChinaTest { | ||||
| 				utils.PrintCenteredTitle("跨国流媒体解锁", width) | ||||
| 		} | ||||
| 		if utTestStatus && (language == "zh" && !onlyChinaTest || language == "en") { | ||||
| 			wg1.Wait() | ||||
| 				fmt.Printf(mediaInfo) | ||||
| 			if language == "zh" { | ||||
| 				utils.PrintCenteredTitle("跨国流媒体解锁", width) | ||||
| 			} else { | ||||
| 				utils.PrintCenteredTitle("Cross-Border-Streaming-Media-Unlock", width) | ||||
| 			} | ||||
| 			fmt.Printf("%s", *mediaInfo) | ||||
| 		} | ||||
| 	}, tempOutput, output) | ||||
| 		output = utils.PrintAndCapture(func() { | ||||
| } | ||||
|  | ||||
| func runSecurityTests(securityInfo, output, tempOutput string, outputMutex *sync.Mutex) string { | ||||
| 	return utils.PrintAndCapture(func() { | ||||
| 		if securityTestStatus { | ||||
| 			if language == "zh" { | ||||
| 				utils.PrintCenteredTitle("IP质量检测", width) | ||||
| 				fmt.Printf(securityInfo) | ||||
| 			} else { | ||||
| 				utils.PrintCenteredTitle("IP-Quality-Check", width) | ||||
| 			} | ||||
| 			fmt.Printf("%s", securityInfo) | ||||
| 		} | ||||
| 	}, tempOutput, output) | ||||
| 		output = utils.PrintAndCapture(func() { | ||||
| } | ||||
|  | ||||
| func runEmailTests(wg2 *sync.WaitGroup, emailInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string { | ||||
| 	return utils.PrintAndCapture(func() { | ||||
| 		if emailTestStatus { | ||||
| 				utils.PrintCenteredTitle("邮件端口检测", width) | ||||
| 			wg2.Wait() | ||||
| 				fmt.Println(emailInfo) | ||||
| 			if language == "zh" { | ||||
| 				utils.PrintCenteredTitle("邮件端口检测", width) | ||||
| 			} else { | ||||
| 				utils.PrintCenteredTitle("Email-Port-Check", width) | ||||
| 			} | ||||
| 			fmt.Println(*emailInfo) | ||||
| 		} | ||||
| 	}, tempOutput, output) | ||||
| 		if runtime.GOOS != "windows" { | ||||
| } | ||||
|  | ||||
| func runNetworkTests(wg3 *sync.WaitGroup, ptInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string { | ||||
| 	output = utils.PrintAndCapture(func() { | ||||
| 		if backtraceStatus && !onlyChinaTest { | ||||
| 			utils.PrintCenteredTitle("三网回程线路检测", width) | ||||
| @@ -477,22 +664,23 @@ func main() { | ||||
| 			} | ||||
| 		} | ||||
| 	}, tempOutput, output) | ||||
| 			// nexttrace 在win上不支持检测,报错 bind: An invalid argument was supplied. | ||||
| 	output = utils.PrintAndCapture(func() { | ||||
| 		if nt3Status && !onlyChinaTest { | ||||
| 			utils.PrintCenteredTitle("三网回程路由检测", width) | ||||
| 			nt.TraceRoute(language, nt3Location, nt3CheckType) | ||||
| 		} | ||||
| 	}, tempOutput, output) | ||||
| 			output = utils.PrintAndCapture(func() { | ||||
| 	return utils.PrintAndCapture(func() { | ||||
| 		if onlyChinaTest || pingTestStatus { | ||||
| 					utils.PrintCenteredTitle("三网ICMP的PING值检测", width) | ||||
| 			wg3.Wait() | ||||
| 					fmt.Println(ptInfo) | ||||
| 			utils.PrintCenteredTitle("三网ICMP的PING值检测", width) | ||||
| 			fmt.Println(*ptInfo) | ||||
| 		} | ||||
| 	}, tempOutput, output) | ||||
| 		} | ||||
| 		output = utils.PrintAndCapture(func() { | ||||
| } | ||||
|  | ||||
| func runSpeedTests(output, tempOutput string, outputMutex *sync.Mutex) string { | ||||
| 	return utils.PrintAndCapture(func() { | ||||
| 		if speedTestStatus { | ||||
| 			utils.PrintCenteredTitle("就近节点测速", width) | ||||
| 			speedtest.ShowHead(language) | ||||
| @@ -509,96 +697,10 @@ func main() { | ||||
| 			} | ||||
| 		} | ||||
| 	}, 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() { | ||||
| } | ||||
|  | ||||
| func runEnglishSpeedTests(output, tempOutput string, outputMutex *sync.Mutex) string { | ||||
| 	return utils.PrintAndCapture(func() { | ||||
| 		if speedTestStatus { | ||||
| 			utils.PrintCenteredTitle("Speed-Test", width) | ||||
| 			speedtest.ShowHead(language) | ||||
| @@ -606,20 +708,28 @@ func main() { | ||||
| 			speedtest.CustomSP("net", "global", -1, language) | ||||
| 		} | ||||
| 	}, tempOutput, output) | ||||
| } | ||||
|  | ||||
| func appendTimeInfo(output, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) string { | ||||
| 	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() { | ||||
| 	return utils.PrintAndCapture(func() { | ||||
| 		utils.PrintCenteredTitle("", width) | ||||
| 		if language == "zh" { | ||||
| 			fmt.Printf("花费          : %d 分 %d 秒\n", minutes, seconds) | ||||
| 			fmt.Printf("时间          : %s\n", currentTime) | ||||
| 		} else { | ||||
| 			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") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func handleUploadResults(output string) { | ||||
| 	httpURL, httpsURL := utils.ProcessAndUpload(output, filePath, enabelUpload) | ||||
| 	if httpURL != "" || httpsURL != "" { | ||||
| 		if language == "en" { | ||||
| @@ -628,6 +738,42 @@ func main() { | ||||
| 			fmt.Printf("上传成功!\nHttp URL:  %s\nHttps URL: %s\n", httpURL, httpsURL) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| 	parseFlags() | ||||
| 	if handleHelpAndVersion() { | ||||
| 		return | ||||
| 	} | ||||
| 	initLogger() | ||||
| 	go func() { | ||||
| 		http.Get("https://hits.spiritlhl.net/goecs.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false") | ||||
| 	}() | ||||
| 	preCheck := utils.CheckPublicAccess(3 * time.Second) | ||||
| 	if menuMode { | ||||
| 		handleMenuMode(preCheck) | ||||
| 	} | ||||
| 	handleLanguageSpecificSettings() | ||||
| 	var ( | ||||
| 		wg1, wg2, wg3                                         sync.WaitGroup | ||||
| 		basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo string | ||||
| 		output, tempOutput                                    string | ||||
| 		outputMutex                                           sync.Mutex | ||||
| 	) | ||||
| 	startTime := time.Now() | ||||
| 	uploadDone := make(chan bool, 1) | ||||
| 	sig := make(chan os.Signal, 1) | ||||
| 	signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) | ||||
| 	go handleSignalInterrupt(sig, &startTime, &output, tempOutput, uploadDone, &outputMutex) | ||||
| 	switch language { | ||||
| 	case "zh": | ||||
| 		output = runChineseTests(preCheck, &wg1, &wg2, &wg3, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo, output, tempOutput, startTime, &outputMutex) | ||||
| 	case "en": | ||||
| 		output = runEnglishTests(preCheck, &wg1, &wg2, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, output, tempOutput, startTime, &outputMutex) | ||||
| 	default: | ||||
| 		fmt.Println("Unsupported language") | ||||
| 	} | ||||
| 	handleUploadResults(output) | ||||
| 	finish = true | ||||
| 	if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { | ||||
| 		fmt.Println("Press Enter to exit...") | ||||
|   | ||||
							
								
								
									
										64
									
								
								goecs.sh
									
									
									
									
									
								
							
							
						
						
									
										64
									
								
								goecs.sh
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| #!/bin/bash | ||||
| # From https://github.com/oneclickvirt/ecs | ||||
| # 2025.04.07 | ||||
| # 2025.06.29 | ||||
|  | ||||
| # curl -L https://raw.githubusercontent.com/oneclickvirt/ecs/master/goecs.sh -o goecs.sh && chmod +x goecs.sh | ||||
| # 或 | ||||
| @@ -119,13 +119,31 @@ cleanup_epel() { | ||||
| } | ||||
|  | ||||
| goecs_check() { | ||||
|     # Get system and architecture info with error handling | ||||
|     if command -v apt-get >/dev/null 2>&1; then | ||||
|         INSTALL_CMD="apt-get -y install" | ||||
|     elif command -v yum >/dev/null 2>&1; then | ||||
|         INSTALL_CMD="yum -y install" | ||||
|     elif command -v dnf >/dev/null 2>&1; then | ||||
|         INSTALL_CMD="dnf -y install" | ||||
|     elif command -v pacman >/dev/null 2>&1; then | ||||
|         INSTALL_CMD="pacman -S --noconfirm" | ||||
|     elif command -v apk >/dev/null 2>&1; then | ||||
|         INSTALL_CMD="apk add" | ||||
|     elif command -v zypper >/dev/null 2>&1; then | ||||
|         INSTALL_CMD="zypper install -y" | ||||
|     fi | ||||
|     if ! command -v unzip >/dev/null 2>&1; then | ||||
|         _green "Installing unzip" | ||||
|         ${INSTALL_CMD} unzip | ||||
|     fi | ||||
|     if ! command -v curl >/dev/null 2>&1; then | ||||
|         _green "Installing curl" | ||||
|         ${INSTALL_CMD} curl | ||||
|     fi | ||||
|     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="" | ||||
|     ECS_VERSION="0.1.47" | ||||
|     for api in \ | ||||
|         "https://api.github.com/repos/oneclickvirt/ecs/releases/latest" \ | ||||
|         "https://githubapi.spiritlhl.workers.dev/repos/oneclickvirt/ecs/releases/latest" \ | ||||
| @@ -137,10 +155,9 @@ goecs_check() { | ||||
|         sleep 1 | ||||
|     done | ||||
|     if [ -z "$ECS_VERSION" ]; then | ||||
|         _yellow "Unable to get version info, using default version 0.1.33" | ||||
|         ECS_VERSION="0.1.33" | ||||
|         _yellow "Unable to get version info, using default version 0.1.47" | ||||
|         ECS_VERSION="0.1.47" | ||||
|     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 | ||||
| @@ -164,7 +181,6 @@ goecs_check() { | ||||
|         _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" | ||||
| @@ -177,7 +193,6 @@ goecs_check() { | ||||
|             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) | ||||
| @@ -215,7 +230,6 @@ goecs_check() { | ||||
|     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 | ||||
| @@ -230,29 +244,11 @@ goecs_check() { | ||||
|         _red "Download failed, please check your network connection or download manually" | ||||
|         return 1 | ||||
|     fi | ||||
|     if ! command -v unzip >/dev/null 2>&1; then | ||||
|         _green "Installing $cmd" | ||||
|         if command -v apt-get >/dev/null 2>&1; then | ||||
|             INSTALL_CMD="apt-get -y install" | ||||
|         elif command -v yum >/dev/null 2>&1; then | ||||
|             INSTALL_CMD="yum -y install" | ||||
|         elif command -v dnf >/dev/null 2>&1; then | ||||
|             INSTALL_CMD="dnf -y install" | ||||
|         elif command -v pacman >/dev/null 2>&1; then | ||||
|             INSTALL_CMD="pacman -S --noconfirm" | ||||
|         elif command -v apk >/dev/null 2>&1; then | ||||
|             INSTALL_CMD="apk add" | ||||
|         elif command -v zypper >/dev/null 2>&1; then | ||||
|             INSTALL_CMD="zypper install -y" | ||||
|         fi | ||||
|         ${INSTALL_CMD} "$cmd" | ||||
|     fi | ||||
|     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 | ||||
| @@ -260,7 +256,6 @@ goecs_check() { | ||||
|             break | ||||
|         fi | ||||
|     done | ||||
|     # Set system parameters | ||||
|     if [ "$os" != "Darwin" ]; then | ||||
|         PARAM="net.ipv4.ping_group_range" | ||||
|         NEW_VALUE="0 2147483647" | ||||
| @@ -273,7 +268,6 @@ goecs_check() { | ||||
|             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 | ||||
| @@ -442,7 +436,6 @@ env_check() { | ||||
|     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 | ||||
| @@ -460,7 +453,6 @@ env_check() { | ||||
|     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 | ||||
| @@ -472,7 +464,6 @@ env_check() { | ||||
|             break | ||||
|         fi | ||||
|     done | ||||
|     # If system is unrecognized, try common package managers | ||||
|     if [ -z "$SYSTEM" ]; then | ||||
|         _yellow "Unable to recognize system, trying common package managers..." | ||||
|         if command -v apt-get >/dev/null 2>&1; then | ||||
| @@ -539,14 +530,12 @@ env_check() { | ||||
|             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 | ||||
| @@ -561,7 +550,6 @@ env_check() { | ||||
|             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 | ||||
| @@ -580,7 +568,6 @@ env_check() { | ||||
|         ${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..." | ||||
|         brew install --force sysbench iproute2mac | ||||
| @@ -677,4 +664,3 @@ case "$1" in | ||||
|     show_help | ||||
|     ;; | ||||
| esac | ||||
|  | ||||
|   | ||||
| @@ -31,5 +31,5 @@ func MemoryTest(language, testMethod string) { | ||||
| 	if !strings.Contains(res, "\n") && res != "" { | ||||
| 		res += "\n" | ||||
| 	} | ||||
| 	fmt.Printf(res) | ||||
| 	fmt.Printf("%s", res) | ||||
| } | ||||
|   | ||||
| @@ -6,5 +6,5 @@ import ( | ||||
| ) | ||||
|  | ||||
| func Test(t *testing.T) { | ||||
| 	fmt.Printf(MediaTest("zh")) | ||||
| 	fmt.Printf("%s", MediaTest("zh")) | ||||
| } | ||||
|   | ||||
							
								
								
									
										174
									
								
								utils/utils.go
									
									
									
									
									
								
							
							
						
						
									
										174
									
								
								utils/utils.go
									
									
									
									
									
								
							| @@ -3,13 +3,11 @@ package utils | ||||
| import ( | ||||
| 	"bufio" | ||||
| 	"bytes" | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"github.com/imroc/req/v3" | ||||
| 	"github.com/oneclickvirt/UnlockTests/uts" | ||||
| 	"github.com/oneclickvirt/basics/system" | ||||
| 	. "github.com/oneclickvirt/defaultset" | ||||
| 	"github.com/oneclickvirt/security/network" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"net/http" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"regexp" | ||||
| @@ -17,6 +15,12 @@ import ( | ||||
| 	"sync" | ||||
| 	"time" | ||||
| 	"unicode/utf8" | ||||
|  | ||||
| 	"github.com/imroc/req/v3" | ||||
| 	"github.com/oneclickvirt/UnlockTests/uts" | ||||
| 	"github.com/oneclickvirt/basics/system" | ||||
| 	. "github.com/oneclickvirt/defaultset" | ||||
| 	"github.com/oneclickvirt/security/network" | ||||
| ) | ||||
|  | ||||
| // PrintCenteredTitle 根据指定的宽度打印居中标题 | ||||
| @@ -95,14 +99,14 @@ func CheckChina(enableLogger bool) bool { | ||||
| } | ||||
|  | ||||
| // BasicsAndSecurityCheck 执行安全检查 | ||||
| func BasicsAndSecurityCheck(language, nt3CheckType string, securtyCheckStatus bool) (string, string, string) { | ||||
| func BasicsAndSecurityCheck(language, nt3CheckType string, securityCheckStatus 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) | ||||
| 		ipInfo, securityInfo, err = network.NetworkCheck("both", securityCheckStatus, language) | ||||
| 		if err != nil { | ||||
| 			fmt.Println(err.Error()) | ||||
| 		} | ||||
| @@ -330,3 +334,159 @@ func ProcessAndUpload(output string, filePath string, enableUplaod bool) (string | ||||
| 	} | ||||
| 	return "", "" | ||||
| } | ||||
|  | ||||
| // ============================= 前置联网能力检测 ============================= | ||||
|  | ||||
| var StackType string | ||||
|  | ||||
| type NetCheckResult struct { | ||||
| 	HasIPv4   bool | ||||
| 	HasIPv6   bool | ||||
| 	Connected bool | ||||
| 	StackType string // "IPv4", "IPv6", "DualStack", "None" | ||||
| } | ||||
|  | ||||
| func makeResolver(proto, dnsAddr string) *net.Resolver { | ||||
| 	return &net.Resolver{ | ||||
| 		PreferGo: true, | ||||
| 		Dial: func(ctx context.Context, network, address string) (net.Conn, error) { | ||||
| 			d := net.Dialer{ | ||||
| 				Timeout: 5 * time.Second, | ||||
| 			} | ||||
| 			return d.DialContext(ctx, proto, dnsAddr) | ||||
| 		}, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func CheckPublicAccess(timeout time.Duration) NetCheckResult { | ||||
| 	if timeout < 2*time.Second { | ||||
| 		timeout = 2 * time.Second | ||||
| 	} | ||||
| 	var wg sync.WaitGroup | ||||
| 	resultChan := make(chan string, 8) | ||||
| 	ctx, cancel := context.WithTimeout(context.Background(), timeout) | ||||
| 	defer cancel() | ||||
| 	checks := []struct { | ||||
| 		Tag  string | ||||
| 		Addr string | ||||
| 		Kind string // udp4, udp6, http4, http6 | ||||
| 	}{ | ||||
| 		// UDP DNS | ||||
| 		{"IPv4", "223.5.5.5:53", "udp4"},              // 阿里 DNS | ||||
| 		{"IPv4", "8.8.8.8:53", "udp4"},                // Google DNS | ||||
| 		{"IPv6", "[2400:3200::1]:53", "udp6"},         // 阿里 IPv6 DNS | ||||
| 		{"IPv6", "[2001:4860:4860::8888]:53", "udp6"}, // Google IPv6 DNS | ||||
| 		// HTTP HEAD | ||||
| 		{"IPv4", "https://www.baidu.com", "http4"},     // 百度 | ||||
| 		{"IPv4", "https://1.1.1.1", "http4"},           // Cloudflare | ||||
| 		{"IPv6", "https://[2400:3200::1]", "http6"},    // 阿里 IPv6 | ||||
| 		{"IPv6", "https://[2606:4700::1111]", "http6"}, // Cloudflare IPv6 | ||||
| 	} | ||||
| 	for _, check := range checks { | ||||
| 		wg.Add(1) | ||||
| 		go func(tag, addr, kind string) { | ||||
| 			defer wg.Done() | ||||
| 			defer func() { | ||||
| 				if r := recover(); r != nil { | ||||
| 				} | ||||
| 			}() | ||||
| 			switch kind { | ||||
| 			case "udp4", "udp6": | ||||
| 				dialer := &net.Dialer{ | ||||
| 					Timeout: timeout / 4, | ||||
| 				} | ||||
| 				conn, err := dialer.DialContext(ctx, kind, addr) | ||||
| 				if err == nil && conn != nil { | ||||
| 					conn.Close() | ||||
| 					select { | ||||
| 					case resultChan <- tag: | ||||
| 					case <-ctx.Done(): | ||||
| 						return | ||||
| 					} | ||||
| 				} | ||||
| 			case "http4", "http6": | ||||
| 				var resolver *net.Resolver | ||||
| 				if kind == "http4" { | ||||
| 					resolver = makeResolver("udp4", "223.5.5.5:53") | ||||
| 				} else { | ||||
| 					resolver = makeResolver("udp6", "[2400:3200::1]:53") | ||||
| 				} | ||||
| 				dialer := &net.Dialer{ | ||||
| 					Timeout:  timeout / 4, | ||||
| 					Resolver: resolver, | ||||
| 				} | ||||
| 				transport := &http.Transport{ | ||||
| 					DialContext:           dialer.DialContext, | ||||
| 					MaxIdleConns:          1, | ||||
| 					MaxIdleConnsPerHost:   1, | ||||
| 					IdleConnTimeout:       time.Second, | ||||
| 					TLSHandshakeTimeout:   timeout / 4, | ||||
| 					ResponseHeaderTimeout: timeout / 4, | ||||
| 					DisableKeepAlives:     true, | ||||
| 				} | ||||
| 				client := &http.Client{ | ||||
| 					Timeout:   timeout / 4, | ||||
| 					Transport: transport, | ||||
| 					CheckRedirect: func(req *http.Request, via []*http.Request) error { | ||||
| 						return http.ErrUseLastResponse | ||||
| 					}, | ||||
| 				} | ||||
| 				req, err := http.NewRequestWithContext(ctx, "HEAD", addr, nil) | ||||
| 				if err != nil { | ||||
| 					return | ||||
| 				} | ||||
| 				resp, err := client.Do(req) | ||||
| 				if err == nil && resp != nil { | ||||
| 					if resp.Body != nil { | ||||
| 						resp.Body.Close() | ||||
| 					} | ||||
| 					if resp.StatusCode < 500 { | ||||
| 						select { | ||||
| 						case resultChan <- tag: | ||||
| 						case <-ctx.Done(): | ||||
| 							return | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		}(check.Tag, check.Addr, check.Kind) | ||||
| 	} | ||||
| 	go func() { | ||||
| 		wg.Wait() | ||||
| 		close(resultChan) | ||||
| 	}() | ||||
| 	hasV4 := false | ||||
| 	hasV6 := false | ||||
| 	for { | ||||
| 		select { | ||||
| 		case res, ok := <-resultChan: | ||||
| 			if !ok { | ||||
| 				goto result | ||||
| 			} | ||||
| 			if res == "IPv4" { | ||||
| 				hasV4 = true | ||||
| 			} | ||||
| 			if res == "IPv6" { | ||||
| 				hasV6 = true | ||||
| 			} | ||||
| 		case <-ctx.Done(): | ||||
| 			goto result | ||||
| 		} | ||||
| 	} | ||||
| result: | ||||
| 	stack := "None" | ||||
| 	if hasV4 && hasV6 { | ||||
| 		stack = "DualStack" | ||||
| 	} else if hasV4 { | ||||
| 		stack = "IPv4" | ||||
| 	} else if hasV6 { | ||||
| 		stack = "IPv6" | ||||
| 	} | ||||
| 	StackType = stack | ||||
| 	return NetCheckResult{ | ||||
| 		HasIPv4:   hasV4, | ||||
| 		HasIPv6:   hasV6, | ||||
| 		Connected: hasV4 || hasV6, | ||||
| 		StackType: stack, | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										17
									
								
								utils/utils_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								utils/utils_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| package utils | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| func TestCheckPublicAccess(t *testing.T) { | ||||
| 	timeout := 3 * time.Second | ||||
| 	result := CheckPublicAccess(timeout) | ||||
| 	if result.Connected { | ||||
| 		fmt.Printf("✅ 本机有公网连接,类型: %s\n", result.StackType) | ||||
| 	} else { | ||||
| 		fmt.Println("❌ 本机未检测到公网连接") | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user