Gin + GORM + pure-Go SQLite. Users/auth (JWT), API key management with quotas, proxy gateway with weighted channel failover and health checks, usage/billing ledger, cross-protocol conversion (Anthropic Messages / OpenAI Chat Completions / OpenAI Responses), and channel/model admin API. Channels declare native API formats and auto-convert the rest. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
100 lines
2.2 KiB
Go
100 lines
2.2 KiB
Go
package apikey
|
|
|
|
import (
|
|
"errors"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"go.uber.org/zap"
|
|
|
|
"openteam/server/internal/pkg/httpx"
|
|
"openteam/server/internal/user"
|
|
)
|
|
|
|
type Handler struct {
|
|
svc *Service
|
|
log *zap.Logger
|
|
}
|
|
|
|
func NewHandler(svc *Service, log *zap.Logger) *Handler {
|
|
return &Handler{svc: svc, log: log}
|
|
}
|
|
|
|
// List handles GET /api/v1/keys.
|
|
func (h *Handler) List(c *gin.Context) {
|
|
u := user.Current(c)
|
|
keys, err := h.svc.List(u.ID)
|
|
if err != nil {
|
|
h.log.Warn("list keys failed", zap.Error(err))
|
|
httpx.Fail(c, http.StatusInternalServerError, "list keys failed")
|
|
return
|
|
}
|
|
out := make([]map[string]any, 0, len(keys))
|
|
for i := range keys {
|
|
out = append(out, Public(&keys[i]))
|
|
}
|
|
httpx.OK(c, out)
|
|
}
|
|
|
|
// Create handles POST /api/v1/keys.
|
|
func (h *Handler) Create(c *gin.Context) {
|
|
u := user.Current(c)
|
|
var in CreateInput
|
|
if !httpx.Bind(c, &in) {
|
|
return
|
|
}
|
|
rec, plain, err := h.svc.Create(u.ID, in)
|
|
if err != nil {
|
|
h.log.Warn("create key failed", zap.Error(err))
|
|
httpx.Fail(c, http.StatusInternalServerError, "create key failed")
|
|
return
|
|
}
|
|
data := Public(rec)
|
|
data["key"] = plain // shown only once
|
|
httpx.Created(c, data)
|
|
}
|
|
|
|
// Update handles PATCH /api/v1/keys/:id.
|
|
func (h *Handler) Update(c *gin.Context) {
|
|
u := user.Current(c)
|
|
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
httpx.Fail(c, http.StatusBadRequest, "invalid key id")
|
|
return
|
|
}
|
|
var in UpdateInput
|
|
if !httpx.Bind(c, &in) {
|
|
return
|
|
}
|
|
rec, err := h.svc.Update(u.ID, id, in)
|
|
if err != nil {
|
|
if errors.Is(err, ErrKeyNotFound) {
|
|
httpx.Fail(c, http.StatusNotFound, "api key not found")
|
|
return
|
|
}
|
|
httpx.Fail(c, http.StatusInternalServerError, "update key failed")
|
|
return
|
|
}
|
|
httpx.OK(c, Public(rec))
|
|
}
|
|
|
|
// Delete handles DELETE /api/v1/keys/:id.
|
|
func (h *Handler) Delete(c *gin.Context) {
|
|
u := user.Current(c)
|
|
id, err := strconv.ParseInt(c.Param("id"), 10, 64)
|
|
if err != nil {
|
|
httpx.Fail(c, http.StatusBadRequest, "invalid key id")
|
|
return
|
|
}
|
|
if err := h.svc.Delete(u.ID, id); err != nil {
|
|
if errors.Is(err, ErrKeyNotFound) {
|
|
httpx.Fail(c, http.StatusNotFound, "api key not found")
|
|
return
|
|
}
|
|
httpx.Fail(c, http.StatusInternalServerError, "revoke key failed")
|
|
return
|
|
}
|
|
httpx.OK(c, gin.H{"ok": true})
|
|
}
|