feat(server): API relay gateway backend M0-M4
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>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
b0c7439c01
commit
d0e31b198f
@@ -0,0 +1,99 @@
|
||||
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})
|
||||
}
|
||||
Reference in New Issue
Block a user