From 3ffd39cc8b9902ca5ddc8b3361d713872f8b83c8 Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Thu, 5 Feb 2026 13:10:56 +0800 Subject: [PATCH] feat: add `public.agents` table and its `set_updated_at` trigger. --- api/user_agents.go | 10 ++++++++++ cmd/accountsvc/main.go | 22 +++++++++++++++++++--- internal/auth/middleware.go | 23 ++++++++++++----------- 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/api/user_agents.go b/api/user_agents.go index 604b5cd..7c59878 100644 --- a/api/user_agents.go +++ b/api/user_agents.go @@ -2,6 +2,7 @@ package api import ( "errors" + "fmt" "net/http" "net/url" "os" @@ -81,8 +82,17 @@ func (h *handler) listAgentNodes(c *gin.Context) { return } + // Add panic recovery for this handler + defer func() { + if r := recover(); r != nil { + fmt.Printf("PANIC in listAgentNodes: %v\n", r) + c.JSON(http.StatusInternalServerError, gin.H{"error": "internal_error", "message": fmt.Sprintf("%v", r)}) + } + }() + registeredHosts, registeredNames := registeredNodeMetadata(h.agentStatusReader) hosts := parseProxyNodeHosts(h.publicURL, registeredHosts) + if len(hosts) == 0 { c.JSON(http.StatusOK, []vlessNode{}) return diff --git a/cmd/accountsvc/main.go b/cmd/accountsvc/main.go index 95bd23d..0faf7db 100644 --- a/cmd/accountsvc/main.go +++ b/cmd/accountsvc/main.go @@ -722,10 +722,26 @@ func runServer(ctx context.Context, cfg *config.Config, logger *slog.Logger) err } else { agents := agentRegistry.Agents() logger.Info("loaded agents from store", "count", len(agents)) - for _, agent := range agents { - logger.Info("loaded agent", "id", agent.ID, "name", agent.Name, "groups", agent.Groups) - } } + + // Start background sync task to keep in-memory registry updated from DB + go func() { + ticker := time.NewTicker(1 * time.Minute) + defer ticker.Stop() + for { + select { + case <-ctx.Done(): + return + case <-ticker.C: + if err := agentRegistry.Load(ctx); err != nil { + logger.Warn("failed to reload agents from store", "err", err) + } else { + // logger.Debug("reloaded agents from store", "count", len(agentRegistry.Agents())) + } + } + } + }() + // Start background cleanup task for stale agents (e.g., those that haven't heartbeated for 10 minutes) go runAgentCleanup(ctx, st, logger) } diff --git a/internal/auth/middleware.go b/internal/auth/middleware.go index a0e80bc..a838078 100644 --- a/internal/auth/middleware.go +++ b/internal/auth/middleware.go @@ -54,8 +54,19 @@ const ( // AuthMiddleware is a middleware that validates JWT access tokens func (s *TokenService) AuthMiddleware() gin.HandlerFunc { return func(c *gin.Context) { + var token string authHeader := c.GetHeader("Authorization") - if authHeader == "" { + + if authHeader != "" && strings.HasPrefix(authHeader, bearerPrefix) { + token = strings.TrimPrefix(authHeader, bearerPrefix) + } else { + // Fallback to session cookie + if cookie, err := c.Cookie("xc_session"); err == nil { + token = cookie + } + } + + if token == "" { c.JSON(http.StatusUnauthorized, gin.H{ "error": "missing authorization header", }) @@ -63,16 +74,6 @@ func (s *TokenService) AuthMiddleware() gin.HandlerFunc { return } - if !strings.HasPrefix(authHeader, bearerPrefix) { - c.JSON(http.StatusUnauthorized, gin.H{ - "error": "invalid authorization header format", - }) - c.Abort() - return - } - - token := strings.TrimPrefix(authHeader, bearerPrefix) - claims, err := s.ValidateAccessToken(token) if err != nil { c.JSON(http.StatusUnauthorized, gin.H{