141 lines
2.7 KiB
Go
141 lines
2.7 KiB
Go
package util
|
||
|
||
import (
|
||
"context"
|
||
"net"
|
||
"strings"
|
||
|
||
"github.com/gogf/gf/v2/frame/g"
|
||
)
|
||
|
||
// GetLocalIP 获取本机有效的局域网 IPv4 地址
|
||
func GetLocalIP() string {
|
||
addrs, err := net.InterfaceAddrs()
|
||
if err != nil {
|
||
return "127.0.0.1"
|
||
}
|
||
|
||
var validIPs []string
|
||
|
||
for _, addr := range addrs {
|
||
ipnet, ok := addr.(*net.IPNet)
|
||
if !ok {
|
||
continue
|
||
}
|
||
|
||
ip := ipnet.IP
|
||
|
||
if isIPValid(ip) {
|
||
validIPs = append(validIPs, ip.String())
|
||
}
|
||
}
|
||
|
||
// 优先返回非 169.254.x.x 的 IP
|
||
for _, ip := range validIPs {
|
||
if !strings.HasPrefix(ip, "169.254.") {
|
||
return ip
|
||
}
|
||
}
|
||
|
||
// 其次返回 169.254.x.x(最后的选择)
|
||
if len(validIPs) > 0 {
|
||
return validIPs[0]
|
||
}
|
||
|
||
return "127.0.0.1"
|
||
}
|
||
|
||
// isIPValid 判断 IP 是否有效
|
||
func isIPValid(ip net.IP) bool {
|
||
// 不是 loopback (127.0.0.1)
|
||
if ip.IsLoopback() {
|
||
return false
|
||
}
|
||
|
||
// 是 IPv4
|
||
if ip.To4() == nil {
|
||
return false
|
||
}
|
||
|
||
// 不是链路本地地址 (169.254.0.0/16)
|
||
if ip[0] == 169 && ip[1] == 254 {
|
||
return false
|
||
}
|
||
|
||
// 不是组播地址
|
||
if ip.IsMulticast() {
|
||
return false
|
||
}
|
||
|
||
// 不是未指定地址 (0.0.0.0)
|
||
if ip.IsUnspecified() {
|
||
return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
// GetLocalAddress 获取局域网地址(IP:端口)
|
||
func GetLocalAddress(ctx context.Context) string {
|
||
ip := GetLocalIP()
|
||
port := GetServerPort(ctx)
|
||
|
||
if port == "80" || port == "443" {
|
||
return ip
|
||
}
|
||
return ip + ":" + port
|
||
}
|
||
|
||
// GetSchemaFromRequest 从当前请求中获取协议(http/https)
|
||
func GetSchemaFromRequest(ctx context.Context) string {
|
||
r := g.RequestFromCtx(ctx)
|
||
if r == nil {
|
||
return "http"
|
||
}
|
||
|
||
// 1. 代理场景:X-Forwarded-Proto
|
||
if proto := r.Header.Get("X-Forwarded-Proto"); proto != "" {
|
||
return proto
|
||
}
|
||
|
||
// 2. 代理场景:X-Forwarded-Scheme
|
||
if proto := r.Header.Get("X-Forwarded-Scheme"); proto != "" {
|
||
return proto
|
||
}
|
||
|
||
// 3. TLS 连接(直接 HTTPS)
|
||
if r.TLS != nil {
|
||
return "https"
|
||
}
|
||
|
||
// 4. 默认 HTTP(这行很重要!)
|
||
return "http" // ← 确保有这行
|
||
}
|
||
|
||
// GetLocalBaseURL 获取局域网基础 URL(动态协议 + IP + 端口)
|
||
func GetLocalBaseURL(ctx context.Context) string {
|
||
schema := GetSchemaFromRequest(ctx)
|
||
addr := GetLocalAddress(ctx)
|
||
return schema + "://" + addr
|
||
}
|
||
|
||
// GetCallbackURL 获取回调地址(完整 URL)
|
||
func GetCallbackURL(ctx context.Context, path string) string {
|
||
baseURL := GetLocalBaseURL(ctx)
|
||
// 确保 path 以 / 开头
|
||
if !strings.HasPrefix(path, "/") {
|
||
path = "/" + path
|
||
}
|
||
return baseURL + path
|
||
}
|
||
|
||
// GetServerPort 从配置获取服务端口
|
||
func GetServerPort(ctx context.Context) string {
|
||
address := g.Cfg().MustGet(ctx, "server.address", ":8080").String()
|
||
// address 格式如 ":3009",去掉冒号
|
||
if strings.HasPrefix(address, ":") {
|
||
return address[1:]
|
||
}
|
||
return "8080"
|
||
}
|