feat(ui): 重构应用布局和添加客户端更新功能
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 41s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 1m31s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m38s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 1m27s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 2m0s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 1m42s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 1m13s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 1m48s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 2m10s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 1m12s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 1m51s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 1m29s
All checks were successful
Build Multi-Platform Binaries / build-frontend (push) Successful in 41s
Build Multi-Platform Binaries / build-binaries (amd64, linux, client, true) (push) Successful in 1m31s
Build Multi-Platform Binaries / build-binaries (amd64, darwin, server, false) (push) Successful in 1m38s
Build Multi-Platform Binaries / build-binaries (amd64, windows, client, true) (push) Successful in 1m27s
Build Multi-Platform Binaries / build-binaries (amd64, linux, server, true) (push) Successful in 2m0s
Build Multi-Platform Binaries / build-binaries (amd64, windows, server, true) (push) Successful in 1m42s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, client, true) (push) Successful in 1m13s
Build Multi-Platform Binaries / build-binaries (arm64, darwin, server, false) (push) Successful in 1m48s
Build Multi-Platform Binaries / build-binaries (arm, 7, linux, server, true) (push) Successful in 2m10s
Build Multi-Platform Binaries / build-binaries (arm64, linux, client, true) (push) Successful in 1m12s
Build Multi-Platform Binaries / build-binaries (arm64, linux, server, true) (push) Successful in 1m51s
Build Multi-Platform Binaries / build-binaries (arm64, windows, server, false) (push) Successful in 1m29s
- 将侧边栏菜单改为顶部标签页导航设计 - 添加客户端操作系统和架构信息显示 - 实现客户端自动更新检查和应用功能 - 添加底部页脚显示版本和GitHub链接 - 更新主题颜色为紫色渐变风格 - 优化首页和插件页面的UI布局结构 - 修改路由配置将更新页面重命名为设置页面 - 在认证协议中添加客户端平台信息字段 - 重构App.vue中的导航和状态管理逻辑
This commit is contained in:
@@ -176,7 +176,12 @@ func (c *Client) connect() error {
|
||||
return err
|
||||
}
|
||||
|
||||
authReq := protocol.AuthRequest{ClientID: c.ID, Token: c.Token}
|
||||
authReq := protocol.AuthRequest{
|
||||
ClientID: c.ID,
|
||||
Token: c.Token,
|
||||
OS: runtime.GOOS,
|
||||
Arch: runtime.GOARCH,
|
||||
}
|
||||
msg, _ := protocol.NewMessage(protocol.MsgTypeAuth, authReq)
|
||||
if err := protocol.WriteMessage(conn, msg); err != nil {
|
||||
conn.Close()
|
||||
|
||||
@@ -30,6 +30,8 @@ type ClientResponse struct {
|
||||
Online bool `json:"online" example:"true"`
|
||||
LastPing string `json:"last_ping,omitempty" example:"2025-01-02T10:30:00Z"`
|
||||
RemoteAddr string `json:"remote_addr,omitempty" example:"192.168.1.100:54321"`
|
||||
OS string `json:"os,omitempty" example:"linux"`
|
||||
Arch string `json:"arch,omitempty" example:"amd64"`
|
||||
}
|
||||
|
||||
// ClientListItem 客户端列表项
|
||||
@@ -41,6 +43,8 @@ type ClientListItem struct {
|
||||
LastPing string `json:"last_ping,omitempty"`
|
||||
RemoteAddr string `json:"remote_addr,omitempty"`
|
||||
RuleCount int `json:"rule_count" example:"3"`
|
||||
OS string `json:"os,omitempty" example:"linux"`
|
||||
Arch string `json:"arch,omitempty" example:"amd64"`
|
||||
}
|
||||
|
||||
// InstallPluginsRequest 安装插件到客户端请求
|
||||
|
||||
@@ -47,6 +47,8 @@ func (h *ClientHandler) List(c *gin.Context) {
|
||||
item.Online = status.Online
|
||||
item.LastPing = status.LastPing
|
||||
item.RemoteAddr = status.RemoteAddr
|
||||
item.OS = status.OS
|
||||
item.Arch = status.Arch
|
||||
}
|
||||
result = append(result, item)
|
||||
}
|
||||
@@ -113,7 +115,7 @@ func (h *ClientHandler) Get(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
online, lastPing, remoteAddr := h.app.GetServer().GetClientStatus(clientID)
|
||||
online, lastPing, remoteAddr, clientOS, clientArch := h.app.GetServer().GetClientStatus(clientID)
|
||||
|
||||
// 复制插件列表
|
||||
plugins := make([]db.ClientPlugin, len(client.Plugins))
|
||||
@@ -151,6 +153,8 @@ func (h *ClientHandler) Get(c *gin.Context) {
|
||||
Online: online,
|
||||
LastPing: lastPing,
|
||||
RemoteAddr: remoteAddr,
|
||||
OS: clientOS,
|
||||
Arch: clientArch,
|
||||
}
|
||||
|
||||
Success(c, resp)
|
||||
@@ -237,7 +241,7 @@ func (h *ClientHandler) Delete(c *gin.Context) {
|
||||
func (h *ClientHandler) PushConfig(c *gin.Context) {
|
||||
clientID := c.Param("id")
|
||||
|
||||
online, _, _ := h.app.GetServer().GetClientStatus(clientID)
|
||||
online, _, _, _, _ := h.app.GetServer().GetClientStatus(clientID)
|
||||
if !online {
|
||||
ClientNotOnline(c)
|
||||
return
|
||||
@@ -306,7 +310,7 @@ func (h *ClientHandler) Restart(c *gin.Context) {
|
||||
func (h *ClientHandler) InstallPlugins(c *gin.Context) {
|
||||
clientID := c.Param("id")
|
||||
|
||||
online, _, _ := h.app.GetServer().GetClientStatus(clientID)
|
||||
online, _, _, _, _ := h.app.GetServer().GetClientStatus(clientID)
|
||||
if !online {
|
||||
ClientNotOnline(c)
|
||||
return
|
||||
|
||||
@@ -18,11 +18,13 @@ type AppInterface interface {
|
||||
|
||||
// ServerInterface 服务端接口
|
||||
type ServerInterface interface {
|
||||
GetClientStatus(clientID string) (online bool, lastPing string, remoteAddr string)
|
||||
GetClientStatus(clientID string) (online bool, lastPing, remoteAddr, clientOS, clientArch string)
|
||||
GetAllClientStatus() map[string]struct {
|
||||
Online bool
|
||||
LastPing string
|
||||
RemoteAddr string
|
||||
OS string
|
||||
Arch string
|
||||
}
|
||||
ReloadConfig() error
|
||||
GetBindAddr() string
|
||||
|
||||
@@ -177,7 +177,7 @@ func (h *JSPluginHandler) PushToClient(c *gin.Context) {
|
||||
c.ShouldBindJSON(&pushReq) // 忽略错误,允许空请求体
|
||||
|
||||
// 检查客户端是否在线
|
||||
online, _, _ := h.app.GetServer().GetClientStatus(clientID)
|
||||
online, _, _, _, _ := h.app.GetServer().GetClientStatus(clientID)
|
||||
if !online {
|
||||
ClientNotOnline(c)
|
||||
return
|
||||
|
||||
@@ -35,7 +35,7 @@ func (h *LogHandler) StreamLogs(c *gin.Context) {
|
||||
clientID := c.Param("id")
|
||||
|
||||
// 检查客户端是否在线
|
||||
online, _, _ := h.app.GetServer().GetClientStatus(clientID)
|
||||
online, _, _, _, _ := h.app.GetServer().GetClientStatus(clientID)
|
||||
if !online {
|
||||
c.JSON(400, gin.H{"code": 400, "message": "client not online"})
|
||||
return
|
||||
|
||||
@@ -371,7 +371,7 @@ func (h *PluginHandler) UpdateClientConfig(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 如果客户端在线,同步配置
|
||||
online, _, _ := h.app.GetServer().GetClientStatus(clientID)
|
||||
online, _, _, _, _ := h.app.GetServer().GetClientStatus(clientID)
|
||||
if online {
|
||||
if err := h.app.GetServer().SyncPluginConfigToClient(clientID, pluginName, req.Config); err != nil {
|
||||
PartialSuccess(c, gin.H{"status": "partial", "port_changed": portChanged}, "config saved but sync failed: "+err.Error())
|
||||
|
||||
@@ -45,7 +45,7 @@ func (h *PluginAPIHandler) ProxyRequest(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 检查客户端是否在线
|
||||
online, _, _ := h.app.GetServer().GetClientStatus(clientID)
|
||||
online, _, _, _, _ := h.app.GetServer().GetClientStatus(clientID)
|
||||
if !online {
|
||||
ClientNotOnline(c)
|
||||
return
|
||||
|
||||
@@ -82,7 +82,7 @@ func (h *StoreHandler) Install(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 检查客户端是否在线
|
||||
online, _, _ := h.app.GetServer().GetClientStatus(req.ClientID)
|
||||
online, _, _, _, _ := h.app.GetServer().GetClientStatus(req.ClientID)
|
||||
if !online {
|
||||
ClientNotOnline(c)
|
||||
return
|
||||
|
||||
@@ -84,6 +84,8 @@ type JSPluginEntry struct {
|
||||
type ClientSession struct {
|
||||
ID string
|
||||
RemoteAddr string // 客户端 IP 地址
|
||||
OS string // 客户端操作系统
|
||||
Arch string // 客户端架构
|
||||
Session *yamux.Session
|
||||
Rules []protocol.ProxyRule
|
||||
Listeners map[int]net.Listener
|
||||
@@ -287,11 +289,11 @@ func (s *Server) handleConnection(conn net.Conn) {
|
||||
}
|
||||
|
||||
security.LogAuthSuccess(clientIP, clientID)
|
||||
s.setupClientSession(conn, clientID, rules)
|
||||
s.setupClientSession(conn, clientID, authReq.OS, authReq.Arch, rules)
|
||||
}
|
||||
|
||||
// setupClientSession 建立客户端会话
|
||||
func (s *Server) setupClientSession(conn net.Conn, clientID string, rules []protocol.ProxyRule) {
|
||||
func (s *Server) setupClientSession(conn net.Conn, clientID, clientOS, clientArch string, rules []protocol.ProxyRule) {
|
||||
session, err := yamux.Server(conn, nil)
|
||||
if err != nil {
|
||||
log.Printf("[Server] Yamux error: %v", err)
|
||||
@@ -307,6 +309,8 @@ func (s *Server) setupClientSession(conn net.Conn, clientID string, rules []prot
|
||||
cs := &ClientSession{
|
||||
ID: clientID,
|
||||
RemoteAddr: remoteAddr,
|
||||
OS: clientOS,
|
||||
Arch: clientArch,
|
||||
Session: session,
|
||||
Rules: rules,
|
||||
Listeners: make(map[int]net.Listener),
|
||||
@@ -567,16 +571,16 @@ func (s *Server) sendHeartbeat(cs *ClientSession) bool {
|
||||
}
|
||||
|
||||
// GetClientStatus 获取客户端状态
|
||||
func (s *Server) GetClientStatus(clientID string) (online bool, lastPing string, remoteAddr string) {
|
||||
func (s *Server) GetClientStatus(clientID string) (online bool, lastPing, remoteAddr, clientOS, clientArch string) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
if cs, ok := s.clients[clientID]; ok {
|
||||
cs.mu.Lock()
|
||||
defer cs.mu.Unlock()
|
||||
return true, cs.LastPing.Format(time.RFC3339), cs.RemoteAddr
|
||||
return true, cs.LastPing.Format(time.RFC3339), cs.RemoteAddr, cs.OS, cs.Arch
|
||||
}
|
||||
return false, "", ""
|
||||
return false, "", "", "", ""
|
||||
}
|
||||
|
||||
// GetClientPluginStatus 获取客户端插件运行状态
|
||||
@@ -627,6 +631,8 @@ func (s *Server) GetAllClientStatus() map[string]struct {
|
||||
Online bool
|
||||
LastPing string
|
||||
RemoteAddr string
|
||||
OS string
|
||||
Arch string
|
||||
} {
|
||||
// 先复制客户端引用,避免嵌套锁
|
||||
s.mu.RLock()
|
||||
@@ -640,6 +646,8 @@ func (s *Server) GetAllClientStatus() map[string]struct {
|
||||
Online bool
|
||||
LastPing string
|
||||
RemoteAddr string
|
||||
OS string
|
||||
Arch string
|
||||
})
|
||||
|
||||
for _, cs := range clients {
|
||||
@@ -648,10 +656,14 @@ func (s *Server) GetAllClientStatus() map[string]struct {
|
||||
Online bool
|
||||
LastPing string
|
||||
RemoteAddr string
|
||||
OS string
|
||||
Arch string
|
||||
}{
|
||||
Online: true,
|
||||
LastPing: cs.LastPing.Format(time.RFC3339),
|
||||
RemoteAddr: cs.RemoteAddr,
|
||||
OS: cs.OS,
|
||||
Arch: cs.Arch,
|
||||
}
|
||||
cs.mu.Unlock()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user