diff --git a/api/panel/user.go b/api/panel/user.go index 35b2308..1e80e7b 100644 --- a/api/panel/user.go +++ b/api/panel/user.go @@ -5,6 +5,8 @@ import ( "encoding/json/jsontext" "encoding/json/v2" + + log "github.com/sirupsen/logrus" ) type OnlineUser struct { @@ -84,26 +86,44 @@ func (c *Client) GetUserList() ([]UserInfo, error) { } func (c *Client) GetUserAlive() (map[int]int, error) { - c.AliveMap = &AliveMap{} - c.AliveMap.Alive = make(map[int]int) - /*const path = "/v1/server/alivelist" - r, err := c.client.R(). + const path = "/v1/server/alivelist" + c.AliveMap = &AliveMap{Alive: make(map[int]int)} + + r, err := c.Client.R(). ForceContentType("application/json"). Get(path) - if err != nil || r.StatusCode() >= 399 { - c.AliveMap.Alive = make(map[int]int) + if err != nil || r == nil { + log.WithFields(log.Fields{ + "path": path, + "err": err, + }).Warn("alivelist request failed; falling back to empty map") + return c.AliveMap.Alive, nil } - if r == nil || r.RawResponse == nil { - fmt.Printf("received nil response or raw response") - c.AliveMap.Alive = make(map[int]int) + if r.StatusCode() == 304 { + return c.AliveMap.Alive, nil } - defer r.RawResponse.Body.Close() - if err := json.Unmarshal(r.Body(), c.AliveMap); err != nil { - //fmt.Printf("unmarshal user alive list error: %s", err) - c.AliveMap.Alive = make(map[int]int) + if r.StatusCode() >= 400 { + log.WithFields(log.Fields{ + "path": path, + "status": r.StatusCode(), + "body": string(r.Body()), + }).Warn("alivelist returned non-2xx; falling back to empty map") + return c.AliveMap.Alive, nil + } + body := &AliveMap{} + if err := json.Unmarshal(r.Body(), body); err != nil { + log.WithFields(log.Fields{ + "path": path, + "err": err, + }).Warn("alivelist response unmarshal failed; falling back to empty map") + return c.AliveMap.Alive, nil + } + if body.Alive == nil { + log.WithField("path", path).Warn("alivelist response missing 'alive' field; falling back to empty map") + return c.AliveMap.Alive, nil } - */ - return c.AliveMap.Alive, nil + c.AliveMap = body + return body.Alive, nil } type ServerPushUserTrafficRequest struct { diff --git a/node/node.go b/node/node.go index 94b845f..95baf88 100644 --- a/node/node.go +++ b/node/node.go @@ -48,13 +48,14 @@ func (n *Node) StartNodes(apiConfig *conf.ServerApiConfig, core vCore.Core) erro return err } var nodeinfos []*panel.NodeInfo - n.controllers = make([]*Controller, len(nodeinfos)) + n.controllers = make([]*Controller, len(protocols)) + pushI, pullI := resolveIntervals(basic) for i, p := range protocols { node := &panel.NodeInfo{ Id: apiConfig.ServerId, Type: p.Type, - PushInterval: time.Duration(basic.PushInterval.(int)) * time.Second, - PullInterval: time.Duration(basic.PullInterval.(int)) * time.Second, + PushInterval: pushI, + PullInterval: pullI, Common: &panel.CommonNode{ Protocol: p.Type, }, @@ -233,3 +234,48 @@ func (n *Node) Close() { } n.controllers = nil } + +// resolveIntervals returns push/pull intervals from BasicConfig with fallbacks. +// JSON decoders default numeric fields to float64, so the original .(int) +// assertion on basic.PushInterval would panic. Also tolerates a nil basic +// (server without the basic field, or pre-fix server build). +func resolveIntervals(basic *panel.BasicConfig) (push, pull time.Duration) { + const defaultPush = 30 * time.Second + const defaultPull = 60 * time.Second + if basic == nil { + return defaultPush, defaultPull + } + push = intervalSec(basic.PushInterval, defaultPush) + pull = intervalSec(basic.PullInterval, defaultPull) + return +} + +func intervalSec(v any, def time.Duration) time.Duration { + if v == nil { + return def + } + switch x := v.(type) { + case int: + if x <= 0 { + return def + } + return time.Duration(x) * time.Second + case int64: + if x <= 0 { + return def + } + return time.Duration(x) * time.Second + case float64: + if x <= 0 { + return def + } + return time.Duration(x) * time.Second + case string: + n, err := strconv.Atoi(x) + if err != nil || n <= 0 { + return def + } + return time.Duration(n) * time.Second + } + return def +}