From b04b794705691051b7fc0227be99f885d4c84d85 Mon Sep 17 00:00:00 2001 From: hobokenchicken Date: Thu, 19 Mar 2026 12:06:52 -0400 Subject: [PATCH] fix: restore clients page functionality Updated handleGetClients to return UI-compatible data format and implemented handleGetClient/handleUpdateClient endpoints. --- internal/server/dashboard.go | 111 ++++++++++++++++++++++++++++++++++- internal/server/server.go | 2 + 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/internal/server/dashboard.go b/internal/server/dashboard.go index 9b4a87fc..cd63b101 100644 --- a/internal/server/dashboard.go +++ b/internal/server/dashboard.go @@ -401,7 +401,114 @@ func (s *Server) handleGetClients(c *gin.Context) { c.JSON(http.StatusInternalServerError, ErrorResponse(err.Error())) return } - c.JSON(http.StatusOK, SuccessResponse(clients)) + + type UIClient struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + CreatedAt time.Time `json:"created_at"` + LastUsed *time.Time `json:"last_used"` + RequestsCount int `json:"requests_count"` + TokensCount int `json:"tokens_count"` + Status string `json:"status"` + RateLimitPerMinute int `json:"rate_limit_per_minute"` + } + + uiClients := make([]UIClient, len(clients)) + for i, cl := range clients { + status := "active" + if !cl.IsActive { + status = "disabled" + } + + name := "" + if cl.Name != nil { + name = *cl.Name + } + desc := "" + if cl.Description != nil { + desc = *cl.Description + } + + // Get last used from tokens + var lastUsed *time.Time + _ = s.database.Get(&lastUsed, "SELECT MAX(last_used_at) FROM client_tokens WHERE client_id = ?", cl.ClientID) + + uiClients[i] = UIClient{ + ID: cl.ClientID, + Name: name, + Description: desc, + CreatedAt: cl.CreatedAt, + LastUsed: lastUsed, + RequestsCount: cl.TotalRequests, + TokensCount: cl.TotalTokens, + Status: status, + RateLimitPerMinute: cl.RateLimitPerMinute, + } + } + + c.JSON(http.StatusOK, SuccessResponse(uiClients)) +} + +func (s *Server) handleGetClient(c *gin.Context) { + id := c.Param("id") + var cl db.Client + err := s.database.Get(&cl, "SELECT * FROM clients WHERE client_id = ?", id) + if err != nil { + c.JSON(http.StatusNotFound, ErrorResponse("Client not found")) + return + } + + name := "" + if cl.Name != nil { + name = *cl.Name + } + desc := "" + if cl.Description != nil { + desc = *cl.Description + } + + c.JSON(http.StatusOK, SuccessResponse(gin.H{ + "id": cl.ClientID, + "name": name, + "description": desc, + "is_active": cl.IsActive, + "rate_limit_per_minute": cl.RateLimitPerMinute, + "created_at": cl.CreatedAt, + })) +} + +type UpdateClientRequest struct { + Name string `json:"name"` + Description *string `json:"description"` + IsActive bool `json:"is_active"` + RateLimitPerMinute *int `json:"rate_limit_per_minute"` +} + +func (s *Server) handleUpdateClient(c *gin.Context) { + id := c.Param("id") + var req UpdateClientRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, ErrorResponse("Invalid request")) + return + } + + _, err := s.database.Exec(` + UPDATE clients SET + name = ?, + description = ?, + is_active = ?, + rate_limit_per_minute = COALESCE(?, rate_limit_per_minute), + updated_at = CURRENT_TIMESTAMP + WHERE client_id = ? + `, req.Name, req.Description, req.IsActive, req.RateLimitPerMinute, id) + + if err != nil { + c.JSON(http.StatusInternalServerError, ErrorResponse(err.Error())) + return + } + + c.JSON(http.StatusOK, SuccessResponse(gin.H{"message": "Client updated"})) } type CreateClientRequest struct { @@ -676,7 +783,7 @@ func (s *Server) handleTestProvider(c *gin.Context) { // Prepare a simple test request testReq := &models.UnifiedRequest{ - Model: "gpt-4o-mini", // Default cheap test model for OpenAI + Model: "gpt-4o-mini", // Default cheap test model Messages: []models.UnifiedMessage{ { Role: "user", diff --git a/internal/server/server.go b/internal/server/server.go index cec1e48f..33930083 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -111,6 +111,8 @@ func (s *Server) setupRoutes() { admin.GET("/clients", s.handleGetClients) admin.POST("/clients", s.handleCreateClient) + admin.GET("/clients/:id", s.handleGetClient) + admin.PUT("/clients/:id", s.handleUpdateClient) admin.DELETE("/clients/:id", s.handleDeleteClient) admin.GET("/clients/:id/tokens", s.handleGetClientTokens)