feat: 自适应测速时长(阶段最短5秒,负载翻倍至512MB)+ 公网IP探测与内网标注
This commit is contained in:
@@ -91,12 +91,14 @@ func maskIP(ip string) string {
|
||||
return ip
|
||||
}
|
||||
|
||||
// Ping 延迟探测:返回最小响应,供前端计算 RTT
|
||||
// Ping 延迟探测:返回最小响应,供前端计算 RTT;同时回传服务器看到的客户端 IP
|
||||
// (NAT 环境下可能是内网 IP,前端会用公网探测结果替代)
|
||||
func (h *Handler) Ping(c *gin.Context) {
|
||||
c.Header("Cache-Control", "no-store")
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"pong": true,
|
||||
"ts": time.Now().UnixMilli(),
|
||||
"pong": true,
|
||||
"ts": time.Now().UnixMilli(),
|
||||
"client_ip": clientIP(c),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -177,6 +179,7 @@ type resultReq struct {
|
||||
JitterMs float64 `json:"jitter_ms"`
|
||||
DownloadMbps float64 `json:"download_mbps"`
|
||||
UploadMbps float64 `json:"upload_mbps"`
|
||||
ClientIP string `json:"client_ip"` // 可选:前端探测到的公网出口 IP
|
||||
}
|
||||
|
||||
// Result 保存一次测速结果
|
||||
@@ -196,8 +199,15 @@ func (h *Handler) Result(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// 客户端 IP:优先采用前端探测的公网出口 IP(NAT 环境下服务器只能看到内网 IP);
|
||||
// 未提供或格式非法(含回环地址伪造)时回退到 Caddy 传递的 X-Real-IP
|
||||
ip := strings.TrimSpace(req.ClientIP)
|
||||
if parsed := net.ParseIP(ip); parsed == nil || parsed.IsLoopback() {
|
||||
ip = clientIP(c)
|
||||
}
|
||||
|
||||
record := &db.SpeedTestResult{
|
||||
ClientIP: clientIP(c),
|
||||
ClientIP: ip,
|
||||
LatencyMs: req.LatencyMs,
|
||||
JitterMs: req.JitterMs,
|
||||
DownloadMbps: req.DownloadMbps,
|
||||
@@ -215,6 +225,7 @@ func (h *Handler) Result(c *gin.Context) {
|
||||
type RankItem struct {
|
||||
ID uint `json:"id"`
|
||||
ClientIP string `json:"client_ip"`
|
||||
IsPrivateIP bool `json:"is_private_ip"` // NAT/内网环境(如路由器网关),前端标注展示
|
||||
LatencyMs float64 `json:"latency_ms"`
|
||||
JitterMs float64 `json:"jitter_ms"`
|
||||
DownloadMbps float64 `json:"download_mbps"`
|
||||
@@ -222,12 +233,29 @@ type RankItem struct {
|
||||
CreatedAt string `json:"created_at"`
|
||||
}
|
||||
|
||||
// isPrivateIP 判断 IP 是否为内网/保留地址(NAT 网关、局域网、回环等)
|
||||
func isPrivateIP(ip string) bool {
|
||||
parsed := net.ParseIP(ip)
|
||||
if parsed == nil {
|
||||
return true
|
||||
}
|
||||
if parsed.IsLoopback() || parsed.IsLinkLocalUnicast() || parsed.IsLinkLocalMulticast() {
|
||||
return true
|
||||
}
|
||||
if parsed.IsPrivate() || parsed.IsUnspecified() {
|
||||
return true
|
||||
}
|
||||
// IPv4 兼容段(IsPrivate 已覆盖 10/8、172.16/12、192.168/16)
|
||||
return false
|
||||
}
|
||||
|
||||
func toRankItems(rows []db.SpeedTestResult) []RankItem {
|
||||
items := make([]RankItem, 0, len(rows))
|
||||
for _, r := range rows {
|
||||
items = append(items, RankItem{
|
||||
ID: r.ID,
|
||||
ClientIP: maskIP(r.ClientIP),
|
||||
IsPrivateIP: isPrivateIP(r.ClientIP),
|
||||
LatencyMs: r.LatencyMs,
|
||||
JitterMs: r.JitterMs,
|
||||
DownloadMbps: r.DownloadMbps,
|
||||
|
||||
Reference in New Issue
Block a user