mirror of
https://github.com/oneclickvirt/basics.git
synced 2025-10-05 16:48:09 +08:00
fix: 添加离线模式自动切换
This commit is contained in:
@@ -11,11 +11,10 @@ Include: https://github.com/oneclickvirt/gostun
|
|||||||
## 说明
|
## 说明
|
||||||
|
|
||||||
- [x] 以```-l```指定输出的语言类型,可指定```zh```或```en```,默认不指定时使用中文输出
|
- [x] 以```-l```指定输出的语言类型,可指定```zh```或```en```,默认不指定时使用中文输出
|
||||||
- [x] 使用```sysctl```获取CPU信息-特化适配freebsd、openbsd系统
|
- [x] 使用```sysctl```获取CPU信息,特化适配freebsd、openbsd系统
|
||||||
- [x] 适配```MacOS```与```Windows```系统的信息查询
|
- [x] 适配```MacOS```与```Windows```系统的信息查询
|
||||||
- [x] 部分Windows10系统下打勾打叉编码错误显示,已判断是Win时使用Y/N显示而不是勾叉
|
|
||||||
- [x] 检测GPU相关信息,参考[ghw](https://github.com/jaypipes/ghw)
|
- [x] 检测GPU相关信息,参考[ghw](https://github.com/jaypipes/ghw)
|
||||||
- [x] 适配MACOS系统的相关信息识别
|
- [x] 支持自动切换为离线模式仅检测系统基础信息,不再检测网络信息
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
14
cmd/main.go
14
cmd/main.go
@@ -7,10 +7,12 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/oneclickvirt/basics/model"
|
"github.com/oneclickvirt/basics/model"
|
||||||
"github.com/oneclickvirt/basics/network"
|
"github.com/oneclickvirt/basics/network"
|
||||||
"github.com/oneclickvirt/basics/system"
|
"github.com/oneclickvirt/basics/system"
|
||||||
|
"github.com/oneclickvirt/basics/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -38,8 +40,16 @@ func main() {
|
|||||||
go func() {
|
go func() {
|
||||||
http.Get("https://hits.spiritlhl.net/basics.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false")
|
http.Get("https://hits.spiritlhl.net/basics.svg?action=hit&title=Hits&title_bg=%23555555&count_bg=%230eecf8&edge_flat=false")
|
||||||
}()
|
}()
|
||||||
fmt.Println("项目地址:", "https://github.com/oneclickvirt/basics")
|
fmt.Println("Repo:", "https://github.com/oneclickvirt/basics")
|
||||||
ipInfo, _, _ := network.NetworkCheck("both", false, language)
|
preCheck := utils.CheckPublicAccess(3 * time.Second)
|
||||||
|
var ipInfo string
|
||||||
|
if preCheck.Connected && preCheck.StackType == "DualStack" {
|
||||||
|
ipInfo, _, _ = network.NetworkCheck("both", false, language)
|
||||||
|
} else if preCheck.Connected && preCheck.StackType == "IPv4" {
|
||||||
|
ipInfo, _, _ = network.NetworkCheck("ipv4", false, language)
|
||||||
|
} else if preCheck.Connected && preCheck.StackType == "IPv6" {
|
||||||
|
ipInfo, _, _ = network.NetworkCheck("ipv6", false, language)
|
||||||
|
}
|
||||||
res := system.CheckSystemInfo(language)
|
res := system.CheckSystemInfo(language)
|
||||||
fmt.Println("--------------------------------------------------")
|
fmt.Println("--------------------------------------------------")
|
||||||
fmt.Printf(strings.ReplaceAll(res+ipInfo, "\n\n", "\n"))
|
fmt.Printf(strings.ReplaceAll(res+ipInfo, "\n\n", "\n"))
|
||||||
|
160
utils/utils.go
Normal file
160
utils/utils.go
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
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