修复节点信息API
Some checks failed
build / build (api, amd64, linux) (push) Failing after -46s
build / build (api, arm64, linux) (push) Failing after -51s
build / build (api.exe, amd64, windows) (push) Failing after -51s

This commit is contained in:
CN-JS-HuiBai
2026-04-18 00:52:43 +08:00
parent 64655932b1
commit f9c34fcf87
4 changed files with 123 additions and 5 deletions

View File

@@ -174,6 +174,7 @@ func registerServerRoutesV1(v1 *gin.RouterGroup) {
uniProxy.GET("/config", handler.NodeConfig) uniProxy.GET("/config", handler.NodeConfig)
uniProxy.GET("/user", handler.NodeUser) uniProxy.GET("/user", handler.NodeUser)
uniProxy.POST("/push", handler.NodePush) uniProxy.POST("/push", handler.NodePush)
uniProxy.GET("/alive", handler.NodeAlive)
uniProxy.POST("/alive", handler.NodeAlive) uniProxy.POST("/alive", handler.NodeAlive)
uniProxy.GET("/alivelist", handler.NodeAliveList) uniProxy.GET("/alivelist", handler.NodeAliveList)
uniProxy.POST("/status", handler.NodeStatus) uniProxy.POST("/status", handler.NodeStatus)
@@ -198,6 +199,7 @@ func registerServerRoutesV2(v2 *gin.RouterGroup) {
server.GET("/config", handler.NodeConfig) server.GET("/config", handler.NodeConfig)
server.GET("/user", handler.NodeUser) server.GET("/user", handler.NodeUser)
server.POST("/push", handler.NodePush) server.POST("/push", handler.NodePush)
server.GET("/alive", handler.NodeAlive)
server.POST("/alive", handler.NodeAlive) server.POST("/alive", handler.NodeAlive)
server.GET("/alivelist", handler.NodeAliveList) server.GET("/alivelist", handler.NodeAliveList)
server.POST("/status", handler.NodeStatus) server.POST("/status", handler.NodeStatus)

View File

@@ -2,7 +2,9 @@ package handler
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@@ -197,9 +199,19 @@ func NodeTidalabSubmit(c *gin.Context) {
func NodeAlive(c *gin.Context) { func NodeAlive(c *gin.Context) {
node := c.MustGet("node").(*model.Server) node := c.MustGet("node").(*model.Server)
setNodeLastCheck(node)
if c.Request.Method == http.MethodGet {
Success(c, true)
return
}
var payload map[string][]string var payload map[string][]string
if err := c.ShouldBindJSON(&payload); err != nil { if err := c.ShouldBindJSON(&payload); err != nil {
if errors.Is(err, io.EOF) {
Success(c, true)
return
}
Fail(c, 400, "invalid payload") Fail(c, 400, "invalid payload")
return return
} }

View File

@@ -1,17 +1,24 @@
package middleware package middleware
import ( import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http" "net/http"
"strings"
"xboard-go/internal/service" "xboard-go/internal/service"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
const nodeAuthPayloadKey = "_node_auth_payload"
func NodeAuth() gin.HandlerFunc { func NodeAuth() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
token := c.Query("token") token := nodeAuthValue(c, "token")
nodeID := c.Query("node_id") nodeID := nodeAuthValue(c, "node_id")
nodeType := service.NormalizeServerType(c.Query("node_type")) nodeType := service.NormalizeServerType(nodeAuthValue(c, "node_type"))
if token == "" || nodeID == "" { if token == "" || nodeID == "" {
c.JSON(http.StatusUnauthorized, gin.H{"message": "missing node credentials"}) c.JSON(http.StatusUnauthorized, gin.H{"message": "missing node credentials"})
@@ -49,3 +56,98 @@ func NodeAuth() gin.HandlerFunc {
c.Next() c.Next()
} }
} }
func nodeAuthValue(c *gin.Context, key string) string {
if value := strings.TrimSpace(c.Query(key)); value != "" {
return value
}
if value := strings.TrimSpace(c.PostForm(key)); value != "" {
return value
}
switch key {
case "token":
if value := strings.TrimSpace(c.GetHeader("X-Server-Token")); value != "" {
return value
}
if auth := strings.TrimSpace(c.GetHeader("Authorization")); len(auth) > 7 && strings.EqualFold(auth[:7], "Bearer ") {
return strings.TrimSpace(auth[7:])
}
case "node_id":
if value := strings.TrimSpace(c.GetHeader("X-Node-Id")); value != "" {
return value
}
case "node_type":
if value := strings.TrimSpace(c.GetHeader("X-Node-Type")); value != "" {
return value
}
}
payload := nodeAuthPayload(c)
if payload == nil {
return ""
}
return nodeAuthString(payload[key])
}
func nodeAuthPayload(c *gin.Context) map[string]any {
if cached, ok := c.Get(nodeAuthPayloadKey); ok {
if payload, ok := cached.(map[string]any); ok {
return payload
}
return nil
}
if c.Request == nil || c.Request.Body == nil {
c.Set(nodeAuthPayloadKey, map[string]any(nil))
return nil
}
contentType := strings.ToLower(c.GetHeader("Content-Type"))
if !strings.Contains(contentType, "application/json") {
c.Set(nodeAuthPayloadKey, map[string]any(nil))
return nil
}
body, err := io.ReadAll(c.Request.Body)
if err != nil {
c.Request.Body = io.NopCloser(bytes.NewBuffer(nil))
c.Set(nodeAuthPayloadKey, map[string]any(nil))
return nil
}
c.Request.Body = io.NopCloser(bytes.NewBuffer(body))
if len(bytes.TrimSpace(body)) == 0 {
c.Set(nodeAuthPayloadKey, map[string]any(nil))
return nil
}
var payload map[string]any
if err := json.Unmarshal(body, &payload); err != nil {
c.Set(nodeAuthPayloadKey, map[string]any(nil))
return nil
}
c.Set(nodeAuthPayloadKey, payload)
return payload
}
func nodeAuthString(value any) string {
switch typed := value.(type) {
case string:
return strings.TrimSpace(typed)
case json.Number:
return strings.TrimSpace(typed.String())
default:
if value == nil {
return ""
}
text := strings.TrimSpace(fmt.Sprint(value))
if text == "<nil>" {
return ""
}
return text
}
}

View File

@@ -17,6 +17,7 @@ import (
var serverTypeAliases = map[string]string{ var serverTypeAliases = map[string]string{
"v2ray": "vmess", "v2ray": "vmess",
"v2node": "",
"hysteria2": "hysteria", "hysteria2": "hysteria",
} }
@@ -117,10 +118,11 @@ func NormalizeServerType(serverType string) string {
} }
func IsValidServerType(serverType string) bool { func IsValidServerType(serverType string) bool {
if serverType == "" { normalized := NormalizeServerType(serverType)
if normalized == "" {
return true return true
} }
_, ok := validServerTypes[NormalizeServerType(serverType)] _, ok := validServerTypes[normalized]
return ok return ok
} }