package server import ( "database/sql" "fmt" "net/http" "os" "strings" "time" "github.com/gin-gonic/gin" "github.com/google/uuid" "golang.org/x/crypto/bcrypt" "gophergate/internal/db" "gophergate/internal/models" "gophergate/internal/utils" "log/slog" "github.com/shirou/gopsutil/v3/cpu" func (s *Server) handleGetClients(c *gin.Context) { var clients []db.Client err := s.database.Select(&clients, "SELECT * FROM clients ORDER BY created_at DESC") if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(err.Error())) return } 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 } var lastUsedTime sql.NullTime _ = s.database.Get(&lastUsedTime, "SELECT MAX(last_used_at) FROM client_tokens WHERE client_id = ?", cl.ClientID) var lastUsed *time.Time if lastUsedTime.Valid && !lastUsedTime.Time.IsZero() { t := lastUsedTime.Time lastUsed = &t } 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 { Name string `json:"name" binding:"required"` ClientID *string `json:"client_id"` } func (s *Server) handleCreateClient(c *gin.Context) { var req CreateClientRequest if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, ErrorResponse("Invalid request")) return } clientID := "" if req.ClientID != nil { clientID = *req.ClientID } else { clientID = "client-" + uuid.New().String()[:8] } _, err := s.database.Exec("INSERT INTO clients (client_id, name, is_active) VALUES (?, ?, 1)", clientID, req.Name) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(err.Error())) return } token := "sk-" + uuid.New().String() + uuid.New().String() token = token[:51] _, err = s.database.Exec("INSERT INTO client_tokens (client_id, token, name) VALUES (?, ?, 'default')", clientID, token) if err != nil { // Log error } c.JSON(http.StatusOK, SuccessResponse(gin.H{ "id": clientID, "name": req.Name, "status": "active", "token": token, "created_at": time.Now(), })) } func (s *Server) handleDeleteClient(c *gin.Context) { id := c.Param("id") if id == "default" { c.JSON(http.StatusBadRequest, ErrorResponse("Cannot delete default client")) return } _, err := s.database.Exec("DELETE FROM clients WHERE client_id = ?", id) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(err.Error())) return } c.JSON(http.StatusOK, SuccessResponse(gin.H{"message": "Client deleted"})) } func (s *Server) handleGetClientTokens(c *gin.Context) { id := c.Param("id") var tokens []db.ClientToken err := s.database.Select(&tokens, "SELECT * FROM client_tokens WHERE client_id = ? ORDER BY created_at DESC", id) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(err.Error())) return } type MaskedToken struct { ID int `json:"id"` TokenMasked string `json:"token_masked"` Name string `json:"name"` IsActive bool `json:"is_active"` CreatedAt time.Time `json:"created_at"` LastUsedAt *time.Time `json:"last_used_at"` } masked := make([]MaskedToken, len(tokens)) for i, t := range tokens { maskedToken := "••••" if len(t.Token) > 8 { maskedToken = t.Token[:3] + "••••" + t.Token[len(t.Token)-8:] } masked[i] = MaskedToken{ ID: t.ID, TokenMasked: maskedToken, Name: t.Name, IsActive: t.IsActive, CreatedAt: t.CreatedAt, LastUsedAt: t.LastUsedAt, } } c.JSON(http.StatusOK, SuccessResponse(masked)) } type CreateTokenRequest struct { Name string `json:"name"` } func (s *Server) handleCreateClientToken(c *gin.Context) { clientID := c.Param("id") var req CreateTokenRequest if err := c.ShouldBindJSON(&req); err != nil { // optional name } name := "default" if req.Name != "" { name = req.Name } token := "sk-" + uuid.New().String() + uuid.New().String() token = token[:51] _, err := s.database.Exec("INSERT INTO client_tokens (client_id, token, name) VALUES (?, ?, ?)", clientID, token, name) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(err.Error())) return } c.JSON(http.StatusOK, SuccessResponse(gin.H{ "token": token, "name": name, "created_at": time.Now(), })) } func (s *Server) handleDeleteClientToken(c *gin.Context) { tokenID := c.Param("token_id") _, err := s.database.Exec("DELETE FROM client_tokens WHERE id = ?", tokenID) if err != nil { c.JSON(http.StatusInternalServerError, ErrorResponse(err.Error())) return } c.JSON(http.StatusOK, SuccessResponse(gin.H{"message": "Token revoked"})) }