Compare commits

..

24 Commits

Author SHA1 Message Date
spiritlhl
f6ee1e40ec fix: 添加退出程序的选项,添加开始执行前的清屏逻辑 2025-08-11 11:37:43 +00:00
spiritlhl
cb2bf0a7e5 fix: 主体逻辑将backtrace和三网路由详情检测加入整体的并发检测 2025-08-11 11:25:32 +00:00
spiritlhl
ad017db5a6 fix: 更新版本 2025-08-11 11:15:13 +00:00
spiritlhl
a99f58518a fix: 升级backtrace依赖,支持多重路由测试取平均结果,避免单次路由波动分析失败 2025-08-11 11:13:07 +00:00
spiritlhl
2e59bac322 fix: 提取三网详细路由的测试逻辑至于单独的包,添加单项测试 2025-08-11 10:59:17 +00:00
spiritlhl
4132b1daff feat: 详细三网路由测试支持并发测试,大幅缩短测试所需时间 2025-08-11 10:53:48 +00:00
github-actions[bot]
53296b745a chore: update ECS_VERSION to 0.1.76 in goecs.sh 2025-08-08 14:53:41 +00:00
spiritlhl
74630e9615 fix: 更新说明 2025-08-08 14:31:09 +00:00
spiritlhl
5ec7924214 fix: 更新版本(mbw已回落可用但未能测出真实性能) 2025-08-08 14:26:31 +00:00
spiritlhl
7a7fdc26a0 fix: memorytest更新依赖 2025-08-08 14:23:16 +00:00
spiritlhl
d4c855de92 fix: disktest去除无效的条件筛选 2025-08-08 14:16:50 +00:00
spiritlhl
7c22dee443 fix: 修复windows系统非Admin权限下的硬盘测试(FIO测试无误,DD测试缺少C依赖) 2025-08-08 14:15:59 +00:00
github-actions[bot]
797496b640 chore: update ECS_VERSION to 0.1.75 in goecs.sh 2025-08-06 09:12:25 +00:00
spiritlhl
5b686abdc8 fix: 规范输出 2025-08-06 09:06:48 +00:00
spiritlhl
f99a37edbe fix: 更新版本 2025-08-06 09:04:38 +00:00
spiritlhl
4ff49c8b90 fix: 离线模式下不检测程序版本,不统计使用次数 2025-08-06 09:02:57 +00:00
spiritlhl
1d9257beb3 fix: 合并缓存IP的存储位置 2025-08-06 08:51:50 +00:00
spiritlhl
fc6ccb9f92 fix: 修复无系统信息检测时无对应IP信息检测导致后续依赖IP信息查询的函数异常输出的问题 2025-08-06 08:45:00 +00:00
github-actions[bot]
88a2a7fdc9 chore: update ECS_VERSION to 0.1.74 in goecs.sh 2025-08-05 14:28:15 +00:00
spiritlhl
5ff18ed7c7 feat: 更新版本 2025-08-05 14:13:46 +00:00
spiritlhl
df897db244 feat: 添加支持如非最新版本使用时提示版本更新,添加使用统计 2025-08-05 14:13:20 +00:00
spiritlhl
9a8680491c fix: 支持三网回程详细路可指定ALL对四个主要城市全测 2025-08-05 13:37:43 +00:00
spiritlhl
33f81fd6aa fix: 修复笔误 2025-08-05 21:02:05 +08:00
github-actions[bot]
940703c3f9 chore: update ECS_VERSION to 0.1.73 in goecs.sh 2025-08-05 09:42:55 +00:00
16 changed files with 468 additions and 127 deletions

View File

@@ -47,8 +47,8 @@ Shell 版本:[https://github.com/spiritLHLS/ecs](https://github.com/spiritLHLS
|----------------|---------------------------|
| Android(arm64) | 存在权限问题未修复非安卓系统的ARM架构无问题 |
| OpenBSD/NetBSD | 部分Goalng的官方库未支持本系统(尤其是net相关项目) |
| Windows虚拟机 | 非物理机器不支持winsat测试硬件性能(硬盘和内存测试) |
| Windows物理机(非Admin下) | 无Admin权限不支持winsat测试硬件性能(硬盘和内存测试) |
| Windows虚拟机 | 无Admin权限的mbw测试性能不准确(内存测试) |
| Windows物理机(非Admin下) | 无Admin权限的mbw测试性能不准确(内存测试) |
---
## **功能**

View File

@@ -47,8 +47,8 @@ Shell version: [https://github.com/spiritLHLS/ecs/blob/main/README_EN.md](https:
|--------|-------------------------------------------------------------------------------------------------|
| Android(arm64) | Permission issues that are not fixed, no problems with ARM architecture for non-Android systems |
| OpenBSD/NetBSD | Some of Goalng's official libraries do not support this system (especially net-related items) |
| Windows(Virtual Machines) | Non-Physical Machines do not support winsat to test hardware performance (hard disk and memory tests) |
| Windows(Physical Machines)(not under Admin) | No Admin privileges do not support winsat to test hardware performance (hard disk and memory tests) |
| Windows(Virtual Machines) | Inaccurate mbw test performance without Admin rights (memory tests) |
| Windows(Physical Machines)(not under Admin) | Inaccurate mbw test performance without Admin rights (memory tests) |
---

View File

@@ -15,7 +15,7 @@
- [流媒体解锁](#流媒体解锁)
- [IP质量检测](#IP质量检测)
- [邮件端口检测](#邮件端口检测)
- [三网回城线路检测](#三网回城线路检测)
- [上游及回程线路检测](#上游及回程线路检测)
- [三网回程路由检测](#三网回程路由检测)
- [就近测速](#就近测速)

View File

@@ -8,36 +8,30 @@ import (
)
func DiskTest(language, testMethod, testPath string, isMultiCheck bool, autoChange bool) (realTestMethod, res string) {
if runtime.GOOS == "windows" {
if testMethod != "winsat" && testMethod != "" {
// res = "Detected host is Windows, using Winsat for testing.\n"
realTestMethod = "winsat"
}
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)
realTestMethod = "dd"
} else {
realTestMethod = "fio"
}
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)
realTestMethod = "fio"
} else {
realTestMethod = "dd"
}
default:
// res = "Unsupported test method specified, switching to DD for testing.\n"
switch testMethod {
case "fio":
res = disk.FioTest(language, isMultiCheck, testPath)
if res == "" && autoChange {
res += disk.DDTest(language, isMultiCheck, testPath)
realTestMethod = "dd"
} else {
realTestMethod = "fio"
}
case "dd":
res = disk.DDTest(language, isMultiCheck, testPath)
if res == "" && autoChange {
res += disk.FioTest(language, isMultiCheck, testPath)
realTestMethod = "fio"
} else {
realTestMethod = "dd"
}
default:
if runtime.GOOS == "windows" {
realTestMethod = "winsat"
res = disk.WinsatTest(language, isMultiCheck, testPath)
} else {
res = disk.DDTest(language, isMultiCheck, testPath)
realTestMethod = "dd"
}
}
if !strings.Contains(res, "\n") && res != "" {

14
go.mod
View File

@@ -6,14 +6,14 @@ require (
github.com/imroc/req/v3 v3.54.0
github.com/oneclickvirt/CommonMediaTests v0.0.4-20250329123841
github.com/oneclickvirt/UnlockTests v0.0.28-20250727155204
github.com/oneclickvirt/backtrace v0.0.6-20250805091811
github.com/oneclickvirt/backtrace v0.0.7-20250811023541
github.com/oneclickvirt/basics v0.0.15-20250805084236
github.com/oneclickvirt/cputest v0.0.12-20250720122317
github.com/oneclickvirt/defaultset v0.0.2-20240624082446
github.com/oneclickvirt/disktest v0.0.9-20250801101625
github.com/oneclickvirt/disktest v0.0.10-20250808140407
github.com/oneclickvirt/gostun v0.0.5-20250727155022
github.com/oneclickvirt/memorytest v0.0.9-20250720135728
github.com/oneclickvirt/nt3 v0.0.6-20250726150925
github.com/oneclickvirt/memorytest v0.0.9-20250808065154
github.com/oneclickvirt/nt3 v0.0.8-20250810151538
github.com/oneclickvirt/pingtest v0.0.8-20250728015259
github.com/oneclickvirt/portchecker v0.0.3-20250728015900
github.com/oneclickvirt/security v0.0.6-20250805090112
@@ -58,9 +58,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.2 // 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/oneclickvirt/dd v0.0.2-20250808062818 // indirect
github.com/oneclickvirt/fio v0.0.2-20250808045755 // indirect
github.com/oneclickvirt/mbw v0.0.1-20250808061222 // indirect
github.com/oschwald/maxminddb-golang v1.13.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pion/dtls/v2 v2.2.7 // indirect

28
go.sum
View File

@@ -98,28 +98,28 @@ github.com/oneclickvirt/CommonMediaTests v0.0.4-20250329123841 h1:Zef93z9UiZQwRA
github.com/oneclickvirt/CommonMediaTests v0.0.4-20250329123841/go.mod h1:DAmFPRjFV5p9fEzUUSml5jJGn2f1NZJQCzTxITHDjc4=
github.com/oneclickvirt/UnlockTests v0.0.28-20250727155204 h1:apFaEbHGKflYMZzK17nXzEai4GG873mTd+d9hCO/KdY=
github.com/oneclickvirt/UnlockTests v0.0.28-20250727155204/go.mod h1:oOa6wj/qECtRMxwBO6D7o0L0F0Q/5sQ747OCnFQqoGE=
github.com/oneclickvirt/backtrace v0.0.6-20250805091811 h1:mKXh/SB+8Aud1TdgmekIXiRR1ZTduIwoCpvrwa5gyyo=
github.com/oneclickvirt/backtrace v0.0.6-20250805091811/go.mod h1:/+KUtOWz48TyiTTbhVTsp3D6b5WY+4pCgvFBYtUGtns=
github.com/oneclickvirt/backtrace v0.0.7-20250811023541 h1:GzkzvUC6U9b6Dkz/Bl4JRPeQ7XBGoW7Qw1aWqzhF+MQ=
github.com/oneclickvirt/backtrace v0.0.7-20250811023541/go.mod h1:/+KUtOWz48TyiTTbhVTsp3D6b5WY+4pCgvFBYtUGtns=
github.com/oneclickvirt/basics v0.0.15-20250805084236 h1:guYO6wGooSIOAIutuy/zfJ4sXj525nBITw8cjEPRaK8=
github.com/oneclickvirt/basics v0.0.15-20250805084236/go.mod h1:2PV+1ge01zb0Sqzj2V2I7P0wAdFSLF1XgAiumchJJbg=
github.com/oneclickvirt/cputest v0.0.12-20250720122317 h1:toiwAK1hZE5b8klu2mOQ7J4sv5yV9lpPKwgPahfRYBQ=
github.com/oneclickvirt/cputest v0.0.12-20250720122317/go.mod h1:vjlH8tkPFft1tlLOpeNskXVvurxkHaJ3+dgFxQGLXY4=
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/dd v0.0.2-20250808062818 h1:0KHrKkdpL5oBE1OHsrRd2siRw4/2k6f9LBaP7T4JpOc=
github.com/oneclickvirt/dd v0.0.2-20250808062818/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.9-20250801101625 h1:N8sAZrziexT/4qqVwK1hIvXrIKjAinsGdQ9ceumBxNo=
github.com/oneclickvirt/disktest v0.0.9-20250801101625/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/disktest v0.0.10-20250808140407 h1:oUfi5zF3htUTB1ZJuClmK0o74ufP7icbu8LYCnxEOxE=
github.com/oneclickvirt/disktest v0.0.10-20250808140407/go.mod h1:Vp3iMVBD4ccReDJz5n5SlzUdq0kDuVhpRklQk21KT+8=
github.com/oneclickvirt/fio v0.0.2-20250808045755 h1:eWihCRWcalJjPIdrF8dMe68ZiPnMkSfHC8ENvElp/xE=
github.com/oneclickvirt/fio v0.0.2-20250808045755/go.mod h1:NIq+XYTey68KNERGIy/oRDlzpwLzBVoHOCiqX8didsE=
github.com/oneclickvirt/gostun v0.0.5-20250727155022 h1:/e3gSUrOp1tg/1NTRx+P8B51OGcP26Q6//5EoSIjOvk=
github.com/oneclickvirt/gostun v0.0.5-20250727155022/go.mod h1:pfp7MFZJK9n/KTLAVqqFcCAns4xqMykmjI+1UeF/vdE=
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.9-20250720135728 h1:RusZfaESDXK+k5YYfVXTCW5zLeQEd/dYY80a6xievE0=
github.com/oneclickvirt/memorytest v0.0.9-20250720135728/go.mod h1:7xMacjQobvFAtODht2hxTsB9hM2IFS7vZk3gxx+bsjo=
github.com/oneclickvirt/nt3 v0.0.6-20250726150925 h1:M+9kEpMp+O2a//yp9zJ11DleK7hxEmFd5jOWKNrSlmY=
github.com/oneclickvirt/nt3 v0.0.6-20250726150925/go.mod h1:O7YkaOMFihB8hwQiD74WTbDlyoTieDwTDBR6jbkZaP0=
github.com/oneclickvirt/mbw v0.0.1-20250808061222 h1:WGXOe6QvHiDRhPVMI0VcctjzW08kGvJf50yq5YeZCtw=
github.com/oneclickvirt/mbw v0.0.1-20250808061222/go.mod h1:0Vq6NRpyLmGUdfHfL3uDcFsuZhi7KlG+OCs5ky2757Y=
github.com/oneclickvirt/memorytest v0.0.9-20250808065154 h1:mjYOvpFz2mpDU9MNjj66oIDcc2r6+zoW8veP616/+4Q=
github.com/oneclickvirt/memorytest v0.0.9-20250808065154/go.mod h1:DBxiVZX7mWCe0Fy+qu57ENheLo00sLfjKzvxiICrUtU=
github.com/oneclickvirt/nt3 v0.0.8-20250810151538 h1:pXxtvTwTFWAh/+Hz89wDat42CFjqNdlFyASPRxSdE5g=
github.com/oneclickvirt/nt3 v0.0.8-20250810151538/go.mod h1:F1v+6xInBKnbUa8gV1M40R1HOzxg+obtduNhx3CTnmA=
github.com/oneclickvirt/pingtest v0.0.8-20250728015259 h1:egoxZRZBOWN3JqBwqEsULDyRo2/dpGMeWcmV3U87zig=
github.com/oneclickvirt/pingtest v0.0.8-20250728015259/go.mod h1:gxwsxxwitNQiGq2OI0ZogYoOLwc8DtuOdSRe6/EvRqs=
github.com/oneclickvirt/portchecker v0.0.3-20250728015900 h1:AomzdppSOFB70AJESQhlp0IPbsHTTJGimAWDk2TzCWM=

234
goecs.go
View File

@@ -7,6 +7,7 @@ import (
"fmt"
"net/http"
"os"
"os/exec"
"os/signal"
"regexp"
"runtime"
@@ -24,6 +25,7 @@ import (
"github.com/oneclickvirt/ecs/cputest"
"github.com/oneclickvirt/ecs/disktest"
"github.com/oneclickvirt/ecs/memorytest"
"github.com/oneclickvirt/ecs/nexttrace"
"github.com/oneclickvirt/ecs/speedtest"
"github.com/oneclickvirt/ecs/unlocktest"
"github.com/oneclickvirt/ecs/upstreams"
@@ -31,7 +33,6 @@ import (
gostunmodel "github.com/oneclickvirt/gostun/model"
memorytestmodel "github.com/oneclickvirt/memorytest/memory"
nt3model "github.com/oneclickvirt/nt3/model"
"github.com/oneclickvirt/nt3/nt"
ptmodel "github.com/oneclickvirt/pingtest/model"
"github.com/oneclickvirt/pingtest/pt"
"github.com/oneclickvirt/portchecker/email"
@@ -39,7 +40,7 @@ import (
)
var (
ecsVersion = "v0.1.73"
ecsVersion = "v0.1.77"
menuMode bool
onlyChinaTest bool
input, choice string
@@ -59,10 +60,9 @@ var (
autoChangeDiskTestMethod = true
filePath = "goecs.txt"
enabelUpload = true
help bool
onlyIpInfoCheckStatus, help bool
goecsFlag = flag.NewFlagSet("goecs", flag.ContinueOnError)
finish bool
IPV4, IPV6 string
)
func getMenuChoice(language string) string {
@@ -144,7 +144,7 @@ func parseFlags() {
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(&nt3Location, "nt3loc", "GZ", "Specify NT3 test location (supported: GZ, SH, BJ, CD, ALL for Guangzhou, Shanghai, Beijing, Chengdu and all)")
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")
@@ -186,11 +186,13 @@ func handleMenuMode(preCheck utils.NetCheckResult) {
commTestStatus, utTestStatus, securityTestStatus, emailTestStatus = false, false, false, false
backtraceStatus, nt3Status, speedTestStatus = false, false, false
autoChangeDiskTestMethod = true
printMenuOptions()
printMenuOptions(preCheck)
Loop:
for {
choice = getMenuChoice(language)
switch choice {
case "0":
os.Exit(0)
case "1":
setFullTestStatus(preCheck)
onlyChinaTest = utils.CheckChina(enableLogger)
@@ -236,6 +238,7 @@ Loop:
fmt.Println("Can not test without network connection!")
return
}
nt3Location = "ALL"
setRouteTestStatus()
break Loop
default:
@@ -244,23 +247,99 @@ Loop:
}
}
func printMenuOptions() {
// clearScreen 清屏
func clearScreen() {
var cmd *exec.Cmd
switch runtime.GOOS {
case "windows":
cmd = exec.Command("cmd", "/c", "cls")
case "darwin":
cmd = exec.Command("clear")
default:
cmd = exec.Command("clear")
}
cmd.Stdout = os.Stdout
_ = cmd.Run()
}
func printMenuOptions(preCheck utils.NetCheckResult) {
clearScreen() // 清屏
var stats *utils.StatsResponse
var statsErr error
var githubInfo *utils.GitHubRelease
var githubErr error
// 只有在网络连接正常时才获取统计信息和版本信息
if preCheck.Connected {
var pwg sync.WaitGroup
pwg.Add(2)
go func() {
defer pwg.Done()
stats, statsErr = utils.GetGoescStats()
}()
go func() {
defer pwg.Done()
githubInfo, githubErr = utils.GetLatestEcsRelease()
}()
pwg.Wait()
} else {
statsErr = fmt.Errorf("network not connected")
githubErr = fmt.Errorf("network not connected")
}
var statsInfo string
var cmp int
if preCheck.Connected {
// 网络连接正常时处理统计信息和版本比较
if statsErr != nil {
statsInfo = "NULL"
} else {
switch language {
case "zh":
statsInfo = fmt.Sprintf("总使用量: %s | 今日使用: %s",
utils.FormatGoecsNumber(stats.Total),
utils.FormatGoecsNumber(stats.Daily))
case "en":
statsInfo = fmt.Sprintf("Total Usage: %s | Daily Usage: %s",
utils.FormatGoecsNumber(stats.Total),
utils.FormatGoecsNumber(stats.Daily))
}
}
if githubErr == nil {
cmp = utils.CompareVersions(ecsVersion, githubInfo.TagName)
} else {
cmp = 0
}
}
switch language {
case "zh":
fmt.Println("VPS融合怪版本: ", ecsVersion)
fmt.Println("1. 融合怪完全体")
fmt.Printf("VPS融合怪版本: %s\n", ecsVersion)
if preCheck.Connected {
switch cmp {
case -1:
fmt.Printf("检测到新版本 %s 如有必要请更新!\n", githubInfo.TagName)
}
fmt.Printf("使用统计: %s\n", statsInfo)
}
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("6. 网络单项(IP质量检测+上游及三网回程+广州三网回程详细路由+全国延迟+测速节点11个)")
fmt.Println("7. 解锁单项(御三家解锁+常用流媒体解锁)")
fmt.Println("8. 硬件单项(系统信息+CPU+内存+dd磁盘测试+fio磁盘测试)")
fmt.Println("9. IP质量检测(15个数据库的IP检测+邮件端口检测)")
fmt.Println("10. 三网回程线路+广州三网路由+全国三网延迟")
fmt.Println("8. 硬件单项(系统信息+CPU+dd磁盘测试+fio磁盘测试)")
fmt.Println("9. IP质量检测(15个数据库的IP质量检测+邮件端口检测)")
fmt.Println("10. 三网回程线路检测+三网回程详细路由(北京上海广州成都)+三网延迟测试(全国)")
fmt.Println("0. 退出程序")
case "en":
fmt.Println("VPS Fusion Monster Test Version: ", ecsVersion)
fmt.Println("1. VPS Fusion Monster Test Comprehensive Test Suite")
fmt.Printf("VPS Fusion Monster Test Version: %s\n", ecsVersion)
if preCheck.Connected {
switch cmp {
case -1:
fmt.Printf("New version detected %s update if necessary!\n", githubInfo.TagName)
}
fmt.Printf("%s\n", statsInfo)
}
fmt.Println("1. VPS Fusion Monster Test (Full Test)")
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)")
@@ -269,6 +348,7 @@ func printMenuOptions() {
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)")
fmt.Println("0. Exit Program")
}
}
@@ -335,16 +415,18 @@ func setUnlockFocusedTestStatus(preCheck utils.NetCheckResult) {
}
func setNetworkOnlyTestStatus() {
onlyIpInfoCheckStatus = true
securityTestStatus = true
speedTestStatus = true
backtraceStatus = true
nt3Status = true
pingTestStatus = true
}
func setUnlockOnlyTestStatus() {
onlyIpInfoCheckStatus = true
commTestStatus = true
utTestStatus = true
enabelUpload = false
}
func setHardwareOnlyTestStatus(preCheck utils.NetCheckResult) {
@@ -358,15 +440,16 @@ func setHardwareOnlyTestStatus(preCheck utils.NetCheckResult) {
}
func setIPQualityTestStatus() {
onlyIpInfoCheckStatus = true
securityTestStatus = true
emailTestStatus = true
}
func setRouteTestStatus() {
onlyIpInfoCheckStatus = true
backtraceStatus = true
nt3Status = true
pingTestStatus = true
enabelUpload = false
}
func printInvalidChoice() {
@@ -387,7 +470,7 @@ func handleLanguageSpecificSettings() {
}
}
func handleSignalInterrupt(sig chan os.Signal, startTime *time.Time, output *string, tempOutput string, uploadDone chan bool, outputMutex *sync.Mutex) {
func handleSignalInterrupt(sig chan os.Signal, startTime *time.Time, output *string, _ string, uploadDone chan bool, outputMutex *sync.Mutex) {
select {
case <-sig:
if !finish {
@@ -463,16 +546,20 @@ func handleSignalInterrupt(sig chan os.Signal, startTime *time.Time, output *str
}
}
func runChineseTests(preCheck utils.NetCheckResult, wg1, wg2, wg3 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo *string, output *string, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) {
func runChineseTests(preCheck utils.NetCheckResult, wg1, wg2, wg3, wg4, wg5 *sync.WaitGroup, basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo *string, output *string, tempOutput string, startTime time.Time, outputMutex *sync.Mutex) {
*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)
if onlyIpInfoCheckStatus && !basicStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = runIpInfoCheck(*output, tempOutput, outputMutex)
}
var backtraceInfo, nt3Info string
if utTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" && !onlyChinaTest {
wg1.Add(1)
go func() {
defer wg3.Done()
*ptInfo = pt.PingTest()
defer wg1.Done()
*mediaInfo = unlocktest.MediaTest(language)
}()
}
if emailTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
@@ -482,20 +569,42 @@ func runChineseTests(preCheck utils.NetCheckResult, wg1, wg2, wg3 *sync.WaitGrou
*emailInfo = email.EmailCheck()
}()
}
if utTestStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" && !onlyChinaTest {
wg1.Add(1)
if (onlyChinaTest || pingTestStatus) && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
wg3.Add(1)
go func() {
defer wg1.Done()
*mediaInfo = unlocktest.MediaTest(language)
defer wg3.Done()
*ptInfo = pt.PingTest()
}()
}
if runtime.GOOS != "windows" && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
if backtraceStatus && !onlyChinaTest {
wg4.Add(1)
go func() {
defer wg4.Done()
backtraceInfo = utils.PrintAndCapture(func() {
utils.PrintCenteredTitle("上游及回程线路检测", width)
upstreams.UpstreamsCheck()
}, "", "")
}()
}
if nt3Status && !onlyChinaTest {
wg5.Add(1)
go func() {
defer wg5.Done()
nt3Info = utils.PrintAndCapture(func() {
utils.PrintCenteredTitle("三网回程路由检测", width)
nexttrace.NextTrace3Check(language, nt3Location, nt3CheckType)
}, "", "")
}()
}
}
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)
*output = runNetworkTests(wg3, wg4, wg5, ptInfo, &backtraceInfo, &nt3Info, *output, tempOutput, outputMutex)
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = runSpeedTests(*output, tempOutput, outputMutex)
@@ -508,6 +617,9 @@ func runEnglishTests(preCheck utils.NetCheckResult, wg1, wg2 *sync.WaitGroup, ba
*output = runCPUTest(*output, tempOutput, outputMutex)
*output = runMemoryTest(*output, tempOutput, outputMutex)
*output = runDiskTest(*output, tempOutput, outputMutex)
if onlyIpInfoCheckStatus && !basicStatus && preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
*output = runIpInfoCheck(*output, tempOutput, outputMutex)
}
if preCheck.Connected && preCheck.StackType != "" && preCheck.StackType != "None" {
if utTestStatus {
wg1.Add(1)
@@ -531,6 +643,24 @@ func runEnglishTests(preCheck utils.NetCheckResult, wg1, wg2 *sync.WaitGroup, ba
*output = appendTimeInfo(*output, tempOutput, startTime, outputMutex)
}
// runIpInfoCheck 系统和网络基础信息检测不进行测试的时候该函数检测取得本机IP信息并显示(单项测试中输出)
func runIpInfoCheck(output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
return utils.PrintAndCapture(func() {
var ipinfo string
upstreams.IPV4, upstreams.IPV6, ipinfo = utils.OnlyBasicsIpInfo(language)
if ipinfo != "" {
if language == "zh" {
utils.PrintCenteredTitle("IP信息", width)
} else {
utils.PrintCenteredTitle("IP-Information", width)
}
fmt.Printf("%s", ipinfo)
}
}, tempOutput, output)
}
func runBasicTests(preCheck utils.NetCheckResult, basicInfo, securityInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
@@ -545,13 +675,13 @@ func runBasicTests(preCheck utils.NetCheckResult, basicInfo, securityInfo *strin
}
}
if preCheck.Connected && preCheck.StackType == "DualStack" {
IPV4, IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, nt3CheckType, securityTestStatus)
upstreams.IPV4, upstreams.IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, nt3CheckType, securityTestStatus)
} else if preCheck.Connected && preCheck.StackType == "IPv4" {
IPV4, IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "ipv4", securityTestStatus)
upstreams.IPV4, upstreams.IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "ipv4", securityTestStatus)
} else if preCheck.Connected && preCheck.StackType == "IPv6" {
IPV4, IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "ipv6", securityTestStatus)
upstreams.IPV4, upstreams.IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "ipv6", securityTestStatus)
} else {
IPV4, IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "", false)
upstreams.IPV4, upstreams.IPV6, *basicInfo, *securityInfo, nt3CheckType = utils.BasicsAndSecurityCheck(language, "", false)
securityTestStatus = false
}
if basicStatus {
@@ -686,22 +816,22 @@ func runEmailTests(wg2 *sync.WaitGroup, emailInfo *string, output, tempOutput st
}, tempOutput, output)
}
func runNetworkTests(wg3 *sync.WaitGroup, ptInfo *string, output, tempOutput string, outputMutex *sync.Mutex) string {
func runNetworkTests(wg3, wg4, wg5 *sync.WaitGroup, ptInfo, backtraceInfo, nt3Info *string, output, tempOutput string, outputMutex *sync.Mutex) string {
outputMutex.Lock()
defer outputMutex.Unlock()
output = utils.PrintAndCapture(func() {
if backtraceStatus && !onlyChinaTest {
utils.PrintCenteredTitle("上游及回程线路检测", width)
upstreams.UpstreamsCheck(IPV4)
}
}, tempOutput, output)
output = utils.PrintAndCapture(func() {
if nt3Status && !onlyChinaTest {
utils.PrintCenteredTitle("三网回程路由检测", width)
nt.TraceRoute(language, nt3Location, nt3CheckType)
}
}, tempOutput, output)
if wg4 != nil {
wg4.Wait()
}
if wg5 != nil {
wg5.Wait()
}
return utils.PrintAndCapture(func() {
if backtraceStatus && !onlyChinaTest && *backtraceInfo != "" {
fmt.Print(*backtraceInfo)
}
if nt3Status && !onlyChinaTest && *nt3Info != "" {
fmt.Print(*nt3Info)
}
if onlyChinaTest || pingTestStatus {
wg3.Wait()
utils.PrintCenteredTitle("三网ICMP的PING值检测", width)
@@ -783,19 +913,23 @@ func main() {
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)
go func() {
if preCheck.Connected {
http.Get("https://hits.spiritlhl.net/goecs.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false")
}
}()
if menuMode {
handleMenuMode(preCheck)
} else {
onlyIpInfoCheckStatus = true
}
handleLanguageSpecificSettings()
if !preCheck.Connected {
enabelUpload = false
}
var (
wg1, wg2, wg3 sync.WaitGroup
wg1, wg2, wg3, wg4, wg5 sync.WaitGroup
basicInfo, securityInfo, emailInfo, mediaInfo, ptInfo string
output, tempOutput string
outputMutex sync.Mutex
@@ -807,7 +941,7 @@ func main() {
go handleSignalInterrupt(sig, &startTime, &output, tempOutput, uploadDone, &outputMutex)
switch language {
case "zh":
runChineseTests(preCheck, &wg1, &wg2, &wg3, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo, &output, tempOutput, startTime, &outputMutex)
runChineseTests(preCheck, &wg1, &wg2, &wg3, &wg4, &wg5, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &ptInfo, &output, tempOutput, startTime, &outputMutex)
case "en":
runEnglishTests(preCheck, &wg1, &wg2, &basicInfo, &securityInfo, &emailInfo, &mediaInfo, &output, tempOutput, startTime, &outputMutex)
default:

View File

@@ -143,7 +143,7 @@ goecs_check() {
os=$(uname -s 2>/dev/null || echo "Unknown")
arch=$(uname -m 2>/dev/null || echo "Unknown")
check_china
ECS_VERSION="0.1.72"
ECS_VERSION="0.1.76"
for api in \
"https://api.github.com/repos/oneclickvirt/ecs/releases/latest" \
"https://githubapi.spiritlhl.workers.dev/repos/oneclickvirt/ecs/releases/latest" \
@@ -155,8 +155,8 @@ goecs_check() {
sleep 1
done
if [ -z "$ECS_VERSION" ]; then
_yellow "Unable to get version info, using default version 0.1.72"
ECS_VERSION="0.1.72"
_yellow "Unable to get version info, using default version 0.1.76"
ECS_VERSION="0.1.76"
fi
version_output=""
for cmd_path in "goecs" "./goecs" "/usr/bin/goecs" "/usr/local/bin/goecs"; do

View File

@@ -10,7 +10,6 @@ import (
func MemoryTest(language, testMethod string) (realTestMethod, res string) {
if runtime.GOOS == "windows" {
if testMethod != "winsat" && testMethod != "" {
// res = "Detected host is Windows, using Winsat for testing.\n"
realTestMethod = "winsat"
}
res += memory.WinsatTest(language)
@@ -19,7 +18,6 @@ func MemoryTest(language, testMethod string) (realTestMethod, res string) {
case "sysbench":
res = memory.SysBenchTest(language)
if res == "" {
// res = "sysbench test failed, switch to use dd test.\n"
res += memory.DDTest(language)
realTestMethod = "dd"
} else {
@@ -29,7 +27,6 @@ func MemoryTest(language, testMethod string) (realTestMethod, res string) {
res = memory.DDTest(language)
realTestMethod = "dd"
default:
// res = "Unsupported test method, switch to use dd test.\n"
res += memory.DDTest(language)
realTestMethod = "dd"
}

44
nexttrace/nexttrace.go Normal file
View File

@@ -0,0 +1,44 @@
package nexttrace
import (
"fmt"
"strings"
"github.com/oneclickvirt/nt3/nt"
)
func NextTrace3Check(language, nt3Location, nt3CheckType string) {
resultChan := make(chan nt.TraceResult, 100)
go nt.TraceRoute(language, nt3Location, nt3CheckType, resultChan)
for result := range resultChan {
if result.Index == -1 {
for index, res := range result.Output {
res = strings.TrimSpace(res)
if res != "" && index == 0 {
fmt.Println(res)
}
}
continue
}
if result.ISPName == "Error" {
for _, res := range result.Output {
res = strings.TrimSpace(res)
if res != "" {
fmt.Println(res)
}
}
return
}
for _, res := range result.Output {
res = strings.TrimSpace(res)
if res == "" {
continue
}
if strings.Contains(res, "ICMP") {
fmt.Print(res)
} else {
fmt.Println(res)
}
}
}
}

View File

@@ -0,0 +1,13 @@
package nexttrace
import (
"testing"
"time"
)
func TestNextTrace3Check(t *testing.T) {
start := time.Now()
NextTrace3Check("zh", "ALL", "ipv4")
duration := time.Since(start)
t.Logf("执行耗时: %s", duration)
}

View File

@@ -6,5 +6,5 @@ import (
)
func Test(t *testing.T) {
fmt.Print("%s", MediaTest("zh"))
fmt.Printf("%s", MediaTest("zh"))
}

View File

@@ -2,17 +2,65 @@ package upstreams
import (
"fmt"
"sync"
"time"
"github.com/oneclickvirt/UnlockTests/uts"
bgptools "github.com/oneclickvirt/backtrace/bgptools"
backtrace "github.com/oneclickvirt/backtrace/bk"
. "github.com/oneclickvirt/defaultset"
)
func UpstreamsCheck(ip string) {
if ip != "" {
if result, err := bgptools.GetPoPInfo(ip); err == nil {
fmt.Print(result.Result)
}
}
backtrace.BackTrace(uts.IPV6)
type IpInfo struct {
Ip string `json:"ip"`
City string `json:"city"`
Region string `json:"region"`
Country string `json:"country"`
Org string `json:"org"`
}
type ConcurrentResults struct {
bgpResult string
backtraceResult string
bgpError error
// backtraceError error
}
var IPV4, IPV6 string
func UpstreamsCheck() {
results := ConcurrentResults{}
var wg sync.WaitGroup
if IPV4 != "" {
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < 2; i++ {
result, err := bgptools.GetPoPInfo(IPV4)
results.bgpError = err
if err == nil && result.Result != "" {
results.bgpResult = result.Result
return
}
if i == 0 {
time.Sleep(3 * time.Second)
}
}
}()
}
wg.Add(1)
go func() {
defer wg.Done()
result := backtrace.BackTrace(uts.IPV6)
results.backtraceResult = result
}()
wg.Wait()
if results.bgpResult != "" {
fmt.Print(results.bgpResult)
}
if results.backtraceResult != "" {
fmt.Printf("%s\n", results.backtraceResult)
}
fmt.Println(Yellow("准确线路自行查看详细路由,本测试结果仅作参考"))
fmt.Println(Yellow("同一目标地址多个线路时,检测可能已越过汇聚层,除第一个线路外,后续信息可能无效"))
}

View File

@@ -3,5 +3,6 @@ package upstreams
import "testing"
func TestUpstreamsCheck(t *testing.T) {
UpstreamsCheck("148.100.85.25")
IPV4 = "148.100.85.25"
UpstreamsCheck()
}

View File

@@ -11,6 +11,7 @@ import (
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"
"time"
@@ -18,12 +19,28 @@ import (
"github.com/imroc/req/v3"
"github.com/oneclickvirt/UnlockTests/uts"
bnetwork "github.com/oneclickvirt/basics/network"
"github.com/oneclickvirt/basics/system"
butils "github.com/oneclickvirt/basics/utils"
. "github.com/oneclickvirt/defaultset"
"github.com/oneclickvirt/security/network"
)
// 获取本程序本日及总执行的统计信息
type StatsResponse struct {
Counter string `json:"counter"`
Action string `json:"action"`
Total int `json:"total"`
Daily int `json:"daily"`
Date string `json:"date"`
Timestamp string `json:"timestamp"`
}
// 获取最新的Github的仓库中的版本
type GitHubRelease struct {
TagName string `json:"tag_name"`
}
// PrintCenteredTitle 根据指定的宽度打印居中标题
func PrintCenteredTitle(title string, width int) {
// 计算字符串的字符数
@@ -99,18 +116,38 @@ func CheckChina(enableLogger bool) bool {
return selectChina
}
// OnlyBasicsIpInfo 仅检查和输出IP信息
func OnlyBasicsIpInfo(language string) (string, string, string) {
ipv4, ipv6, ipInfo, _, err := bnetwork.NetworkCheck("both", false, language)
if err != nil {
return "", "", ""
}
basicInfo := ipInfo
if strings.Contains(ipInfo, "IPV4") && strings.Contains(ipInfo, "IPV6") && ipv4 != "" && ipv6 != "" {
uts.IPV4 = true
uts.IPV6 = true
} else if strings.Contains(ipInfo, "IPV4") && ipv4 != "" {
uts.IPV4 = true
uts.IPV6 = false
} else if strings.Contains(ipInfo, "IPV6") && ipv6 != "" {
uts.IPV6 = true
uts.IPV4 = false
}
basicInfo = strings.ReplaceAll(basicInfo, "\n\n", "\n")
return ipv4, ipv6, basicInfo
}
// BasicsAndSecurityCheck 执行安全检查
func BasicsAndSecurityCheck(language, nt3CheckType string, securityCheckStatus bool) (string, string, string, string, string) {
var wgt sync.WaitGroup
var ipv4, ipv6, ipInfo, securityInfo, systemInfo string
var err error
wgt.Add(1)
go func() {
defer wgt.Done()
ipv4, ipv6, ipInfo, securityInfo, err = network.NetworkCheck("both", securityCheckStatus, language)
if err != nil {
fmt.Println(err.Error())
}
ipv4, ipv6, ipInfo, securityInfo, _ = network.NetworkCheck("both", securityCheckStatus, language)
// if err != nil {
// fmt.Println(err.Error())
// }
}()
wgt.Add(1)
go func() {
@@ -336,8 +373,6 @@ func ProcessAndUpload(output string, filePath string, enableUplaod bool) (string
return "", ""
}
// ============================= 前置联网能力检测 =============================
var StackType string
type NetCheckResult struct {
@@ -359,6 +394,7 @@ func makeResolver(proto, dnsAddr string) *net.Resolver {
}
}
// 前置联网能力检测
func CheckPublicAccess(timeout time.Duration) NetCheckResult {
if timeout < 2*time.Second {
timeout = 2 * time.Second
@@ -492,3 +528,77 @@ result:
StackType: stack,
}
}
// 获取每日/总的程序执行统计信息
func GetGoescStats() (*StatsResponse, error) {
client := req.C().SetTimeout(5 * time.Second)
var stats StatsResponse
resp, err := client.R().
SetSuccessResult(&stats).
Get("https://hits.spiritlhl.net/goecs")
if err != nil {
return nil, err
}
if !resp.IsSuccessState() {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
return &stats, nil
}
// 统计结果单位转换
func FormatGoecsNumber(num int) string {
if num >= 1000000 {
return fmt.Sprintf("%.1fM", float64(num)/1000000)
} else if num >= 1000 {
return fmt.Sprintf("%.1fK", float64(num)/1000)
}
return fmt.Sprintf("%d", num)
}
// 通过Github的API检索仓库最新TAG的版本
func GetLatestEcsRelease() (*GitHubRelease, error) {
urls := []string{
"https://api.github.com/repos/oneclickvirt/ecs/releases/latest",
"https://fd.spiritlhl.top/https://api.github.com/repos/oneclickvirt/ecs/releases/latest",
"https://githubapi.spiritlhl.top/repos/oneclickvirt/ecs/releases/latest",
"https://githubapi.spiritlhl.workers.dev/repos/oneclickvirt/ecs/releases/latest",
}
client := req.C().SetTimeout(3 * time.Second)
for _, url := range urls {
var release GitHubRelease
resp, err := client.R().
SetSuccessResult(&release).
Get(url)
if err != nil {
continue
}
if resp.IsSuccessState() && release.TagName != "" {
return &release, nil
}
}
return nil, fmt.Errorf("failed to fetch release from all sources")
}
// 比较程序版本是否需要升级
func CompareVersions(v1, v2 string) int {
normalize := func(s string) []int {
s = strings.TrimPrefix(strings.ToLower(s), "v")
parts := strings.Split(s, ".")
result := make([]int, 3)
for i := 0; i < 3 && i < len(parts); i++ {
n, _ := strconv.Atoi(parts[i])
result[i] = n
}
return result
}
a := normalize(v1)
b := normalize(v2)
for i := 0; i < 3; i++ {
if a[i] < b[i] {
return -1
} else if a[i] > b[i] {
return 1
}
}
return 0
}

View File

@@ -20,7 +20,7 @@ func TestBasicsAndSecurityCheck(t *testing.T) {
timeout := 3 * time.Second
result := CheckPublicAccess(timeout)
if result.Connected {
fmt.Print("✅ 本机有公网连接,类型: %s\n", result.StackType)
fmt.Printf("✅ 本机有公网连接,类型: %s\n", result.StackType)
} else {
fmt.Println("❌ 本机未检测到公网连接")
}