add persistent proxy/visitor store with CRUD API and web UI (#5188)

This commit is contained in:
fatedier
2026-03-02 01:09:59 +08:00
committed by GitHub
parent d0347325fc
commit 01997deb98
89 changed files with 13960 additions and 3864 deletions

View File

@@ -236,9 +236,6 @@ func (c *Controller) APIProxyByName(ctx *httppkg.Context) (any, error) {
return nil, httppkg.NewError(http.StatusBadRequest, "parse conf error")
}
proxyInfo.Status = "online"
c.fillProxyClientInfo(&proxyClientInfo{
clientVersion: &proxyInfo.ClientVersion,
}, pxy)
} else {
proxyInfo.Status = "offline"
}
@@ -277,9 +274,6 @@ func (c *Controller) getProxyStatsByType(proxyType string) (proxyInfos []*ProxyS
continue
}
proxyInfo.Status = "online"
c.fillProxyClientInfo(&proxyClientInfo{
clientVersion: &proxyInfo.ClientVersion,
}, pxy)
} else {
proxyInfo.Status = "offline"
}
@@ -339,6 +333,7 @@ func buildClientInfoResp(info registry.ClientInfo) ClientInfoResp {
User: info.User,
ClientID: info.ClientID(),
RunID: info.RunID,
Version: info.Version,
Hostname: info.Hostname,
ClientIP: info.IP,
FirstConnectedAt: toUnix(info.FirstConnectedAt),
@@ -351,37 +346,6 @@ func buildClientInfoResp(info registry.ClientInfo) ClientInfoResp {
return resp
}
type proxyClientInfo struct {
user *string
clientID *string
clientVersion *string
}
func (c *Controller) fillProxyClientInfo(proxyInfo *proxyClientInfo, pxy proxy.Proxy) {
loginMsg := pxy.GetLoginMsg()
if loginMsg == nil {
return
}
if proxyInfo.user != nil {
*proxyInfo.user = loginMsg.User
}
if proxyInfo.clientVersion != nil {
*proxyInfo.clientVersion = loginMsg.Version
}
if info, ok := c.clientRegistry.GetByRunID(loginMsg.RunID); ok {
if proxyInfo.clientID != nil {
*proxyInfo.clientID = info.ClientID()
}
return
}
if proxyInfo.clientID != nil {
*proxyInfo.clientID = loginMsg.ClientID
if *proxyInfo.clientID == "" {
*proxyInfo.clientID = loginMsg.RunID
}
}
}
func toUnix(t time.Time) int64 {
if t.IsZero() {
return 0

View File

@@ -45,6 +45,7 @@ type ClientInfoResp struct {
User string `json:"user"`
ClientID string `json:"clientID"`
RunID string `json:"runID"`
Version string `json:"version,omitempty"`
Hostname string `json:"hostname"`
ClientIP string `json:"clientIP,omitempty"`
FirstConnectedAt int64 `json:"firstConnectedAt"`
@@ -100,7 +101,6 @@ type ProxyStatsInfo struct {
Conf any `json:"conf"`
User string `json:"user,omitempty"`
ClientID string `json:"clientID,omitempty"`
ClientVersion string `json:"clientVersion,omitempty"`
TodayTrafficIn int64 `json:"todayTrafficIn"`
TodayTrafficOut int64 `json:"todayTrafficOut"`
CurConns int64 `json:"curConns"`
@@ -119,7 +119,6 @@ type GetProxyStatsResp struct {
Conf any `json:"conf"`
User string `json:"user,omitempty"`
ClientID string `json:"clientID,omitempty"`
ClientVersion string `json:"clientVersion,omitempty"`
TodayTrafficIn int64 `json:"todayTrafficIn"`
TodayTrafficOut int64 `json:"todayTrafficOut"`
CurConns int64 `json:"curConns"`

View File

@@ -28,6 +28,7 @@ type ClientInfo struct {
RunID string
Hostname string
IP string
Version string
FirstConnectedAt time.Time
LastConnectedAt time.Time
DisconnectedAt time.Time
@@ -50,7 +51,7 @@ func NewClientRegistry() *ClientRegistry {
}
// Register stores/updates metadata for a client and returns the registry key plus whether it conflicts with an online client.
func (cr *ClientRegistry) Register(user, rawClientID, runID, hostname, remoteAddr string) (key string, conflict bool) {
func (cr *ClientRegistry) Register(user, rawClientID, runID, hostname, version, remoteAddr string) (key string, conflict bool) {
if runID == "" {
return "", false
}
@@ -86,6 +87,7 @@ func (cr *ClientRegistry) Register(user, rawClientID, runID, hostname, remoteAdd
info.RunID = runID
info.Hostname = hostname
info.IP = remoteAddr
info.Version = version
if info.FirstConnectedAt.IsZero() {
info.FirstConnectedAt = now
}
@@ -151,22 +153,6 @@ func (info ClientInfo) ClientID() string {
return info.RunID
}
// GetByRunID retrieves a client by its run ID.
func (cr *ClientRegistry) GetByRunID(runID string) (ClientInfo, bool) {
cr.mu.RLock()
defer cr.mu.RUnlock()
key, ok := cr.runIndex[runID]
if !ok {
return ClientInfo{}, false
}
info, ok := cr.clients[key]
if !ok {
return ClientInfo{}, false
}
return *info, true
}
func (cr *ClientRegistry) composeClientKey(user, id string) string {
switch {
case user == "":

View File

@@ -622,7 +622,7 @@ func (svr *Service) RegisterControl(ctlConn net.Conn, loginMsg *msg.Login, inter
if host, _, err := net.SplitHostPort(remoteAddr); err == nil {
remoteAddr = host
}
_, conflict := svr.clientRegistry.Register(loginMsg.User, loginMsg.ClientID, loginMsg.RunID, loginMsg.Hostname, remoteAddr)
_, conflict := svr.clientRegistry.Register(loginMsg.User, loginMsg.ClientID, loginMsg.RunID, loginMsg.Hostname, loginMsg.Version, remoteAddr)
if conflict {
svr.ctlManager.Del(loginMsg.RunID, ctl)
ctl.Close()