修复节点信息API
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user