274 lines
7.0 KiB
Go
274 lines
7.0 KiB
Go
package server
|
|
|
|
import (
|
|
"database/sql"
|
|
"net/http"
|
|
"time"
|
|
|
|
"gophergate/internal/db"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
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"}))
|
|
}
|