mirror of
https://github.com/EZ-Api/ez-api.git
synced 2026-01-13 17:47:51 +00:00
feat(key): extend key metadata and validation
This commit is contained in:
@@ -425,13 +425,31 @@ func (h *AdminHandler) IssueChildKeyForMaster(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
key, rawChildKey, err := h.masterService.IssueChildKeyAsAdmin(masterID, req.Group, req.Scopes)
|
||||
modelLimits := strings.TrimSpace(req.ModelLimits)
|
||||
modelLimitsEnabled := false
|
||||
if req.ModelLimitsEnabled != nil {
|
||||
modelLimitsEnabled = *req.ModelLimitsEnabled
|
||||
} else if modelLimits != "" {
|
||||
modelLimitsEnabled = true
|
||||
}
|
||||
|
||||
key, rawChildKey, err := h.masterService.IssueChildKeyAsAdmin(masterID, service.IssueKeyOptions{
|
||||
Group: strings.TrimSpace(req.Group),
|
||||
Scopes: strings.TrimSpace(req.Scopes),
|
||||
ModelLimits: modelLimits,
|
||||
ModelLimitsEnabled: modelLimitsEnabled,
|
||||
ExpiresAt: req.ExpiresAt,
|
||||
AllowIPs: strings.TrimSpace(req.AllowIPs),
|
||||
DenyIPs: strings.TrimSpace(req.DenyIPs),
|
||||
})
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, service.ErrMasterNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
case errors.Is(err, service.ErrMasterNotActive):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
case errors.Is(err, service.ErrModelLimitForbidden):
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
case errors.Is(err, service.ErrChildKeyGroupForbidden):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
case errors.Is(err, service.ErrChildKeyLimitReached):
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ez-api/ez-api/internal/model"
|
||||
"github.com/ez-api/ez-api/internal/service"
|
||||
@@ -23,8 +24,13 @@ func NewMasterHandler(db *gorm.DB, masterService *service.MasterService, syncSer
|
||||
}
|
||||
|
||||
type IssueChildKeyRequest struct {
|
||||
Group string `json:"group"`
|
||||
Scopes string `json:"scopes"`
|
||||
Group string `json:"group"`
|
||||
Scopes string `json:"scopes"`
|
||||
ModelLimits string `json:"model_limits,omitempty"`
|
||||
ModelLimitsEnabled *bool `json:"model_limits_enabled,omitempty"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty"`
|
||||
AllowIPs string `json:"allow_ips,omitempty"`
|
||||
DenyIPs string `json:"deny_ips,omitempty"`
|
||||
}
|
||||
|
||||
// IssueChildKey godoc
|
||||
@@ -68,13 +74,31 @@ func (h *MasterHandler) IssueChildKey(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
key, rawChildKey, err := h.masterService.IssueChildKey(masterModel.ID, group, req.Scopes)
|
||||
modelLimits := strings.TrimSpace(req.ModelLimits)
|
||||
modelLimitsEnabled := false
|
||||
if req.ModelLimitsEnabled != nil {
|
||||
modelLimitsEnabled = *req.ModelLimitsEnabled
|
||||
} else if modelLimits != "" {
|
||||
modelLimitsEnabled = true
|
||||
}
|
||||
|
||||
key, rawChildKey, err := h.masterService.IssueChildKey(masterModel.ID, service.IssueKeyOptions{
|
||||
Group: group,
|
||||
Scopes: strings.TrimSpace(req.Scopes),
|
||||
ModelLimits: modelLimits,
|
||||
ModelLimitsEnabled: modelLimitsEnabled,
|
||||
ExpiresAt: req.ExpiresAt,
|
||||
AllowIPs: strings.TrimSpace(req.AllowIPs),
|
||||
DenyIPs: strings.TrimSpace(req.DenyIPs),
|
||||
})
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, service.ErrMasterNotFound):
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
||||
case errors.Is(err, service.ErrMasterNotActive):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
case errors.Is(err, service.ErrModelLimitForbidden):
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
case errors.Is(err, service.ErrChildKeyGroupForbidden):
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
case errors.Is(err, service.ErrChildKeyLimitReached):
|
||||
@@ -118,30 +142,54 @@ func (h *MasterHandler) GetSelf(c *gin.Context) {
|
||||
}
|
||||
|
||||
type TokenView struct {
|
||||
ID uint `json:"id"`
|
||||
Group string `json:"group"`
|
||||
Scopes string `json:"scopes"`
|
||||
Status string `json:"status"`
|
||||
IssuedBy string `json:"issued_by"`
|
||||
IssuedAtEpoch int64 `json:"issued_at_epoch"`
|
||||
DefaultNamespace string `json:"default_namespace"`
|
||||
Namespaces string `json:"namespaces"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
ID uint `json:"id"`
|
||||
Group string `json:"group"`
|
||||
Scopes string `json:"scopes"`
|
||||
Status string `json:"status"`
|
||||
IssuedBy string `json:"issued_by"`
|
||||
IssuedAtEpoch int64 `json:"issued_at_epoch"`
|
||||
DefaultNamespace string `json:"default_namespace"`
|
||||
Namespaces string `json:"namespaces"`
|
||||
ModelLimits string `json:"model_limits,omitempty"`
|
||||
ModelLimitsEnabled bool `json:"model_limits_enabled"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty"`
|
||||
AllowIPs string `json:"allow_ips,omitempty"`
|
||||
DenyIPs string `json:"deny_ips,omitempty"`
|
||||
LastAccessedAt *time.Time `json:"last_accessed_at,omitempty"`
|
||||
RequestCount int64 `json:"request_count"`
|
||||
UsedTokens int64 `json:"used_tokens"`
|
||||
QuotaLimit int64 `json:"quota_limit"`
|
||||
QuotaUsed int64 `json:"quota_used"`
|
||||
QuotaResetAt *time.Time `json:"quota_reset_at,omitempty"`
|
||||
QuotaResetType string `json:"quota_reset_type,omitempty"`
|
||||
CreatedAt int64 `json:"created_at"`
|
||||
UpdatedAt int64 `json:"updated_at"`
|
||||
}
|
||||
|
||||
func toTokenView(k model.Key) TokenView {
|
||||
return TokenView{
|
||||
ID: k.ID,
|
||||
Group: k.Group,
|
||||
Scopes: k.Scopes,
|
||||
Status: k.Status,
|
||||
IssuedBy: k.IssuedBy,
|
||||
IssuedAtEpoch: k.IssuedAtEpoch,
|
||||
DefaultNamespace: strings.TrimSpace(k.DefaultNamespace),
|
||||
Namespaces: strings.TrimSpace(k.Namespaces),
|
||||
CreatedAt: k.CreatedAt.UTC().Unix(),
|
||||
UpdatedAt: k.UpdatedAt.UTC().Unix(),
|
||||
ID: k.ID,
|
||||
Group: k.Group,
|
||||
Scopes: k.Scopes,
|
||||
Status: k.Status,
|
||||
IssuedBy: k.IssuedBy,
|
||||
IssuedAtEpoch: k.IssuedAtEpoch,
|
||||
DefaultNamespace: strings.TrimSpace(k.DefaultNamespace),
|
||||
Namespaces: strings.TrimSpace(k.Namespaces),
|
||||
ModelLimits: strings.TrimSpace(k.ModelLimits),
|
||||
ModelLimitsEnabled: k.ModelLimitsEnabled,
|
||||
ExpiresAt: k.ExpiresAt,
|
||||
AllowIPs: strings.TrimSpace(k.AllowIPs),
|
||||
DenyIPs: strings.TrimSpace(k.DenyIPs),
|
||||
LastAccessedAt: k.LastAccessedAt,
|
||||
RequestCount: k.RequestCount,
|
||||
UsedTokens: k.UsedTokens,
|
||||
QuotaLimit: k.QuotaLimit,
|
||||
QuotaUsed: k.QuotaUsed,
|
||||
QuotaResetAt: k.QuotaResetAt,
|
||||
QuotaResetType: strings.TrimSpace(k.QuotaResetType),
|
||||
CreatedAt: k.CreatedAt.UTC().Unix(),
|
||||
UpdatedAt: k.UpdatedAt.UTC().Unix(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,8 +260,13 @@ func (h *MasterHandler) GetToken(c *gin.Context) {
|
||||
}
|
||||
|
||||
type UpdateTokenRequest struct {
|
||||
Scopes *string `json:"scopes,omitempty"`
|
||||
Status *string `json:"status,omitempty"` // active/suspended
|
||||
Scopes *string `json:"scopes,omitempty"`
|
||||
Status *string `json:"status,omitempty"` // active/suspended
|
||||
ModelLimits *string `json:"model_limits,omitempty"`
|
||||
ModelLimitsEnabled *bool `json:"model_limits_enabled,omitempty"`
|
||||
ExpiresAt *time.Time `json:"expires_at,omitempty"`
|
||||
AllowIPs *string `json:"allow_ips,omitempty"`
|
||||
DenyIPs *string `json:"deny_ips,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateToken godoc
|
||||
@@ -256,6 +309,31 @@ func (h *MasterHandler) UpdateToken(c *gin.Context) {
|
||||
if req.Scopes != nil {
|
||||
update["scopes"] = strings.TrimSpace(*req.Scopes)
|
||||
}
|
||||
if req.ModelLimits != nil {
|
||||
modelLimits := strings.TrimSpace(*req.ModelLimits)
|
||||
if modelLimits != "" {
|
||||
if err := h.masterService.ValidateModelLimits(m, modelLimits); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
update["model_limits"] = modelLimits
|
||||
if req.ModelLimitsEnabled == nil {
|
||||
update["model_limits_enabled"] = modelLimits != ""
|
||||
}
|
||||
}
|
||||
if req.ModelLimitsEnabled != nil {
|
||||
update["model_limits_enabled"] = *req.ModelLimitsEnabled
|
||||
}
|
||||
if req.ExpiresAt != nil {
|
||||
update["expires_at"] = req.ExpiresAt
|
||||
}
|
||||
if req.AllowIPs != nil {
|
||||
update["allow_ips"] = strings.TrimSpace(*req.AllowIPs)
|
||||
}
|
||||
if req.DenyIPs != nil {
|
||||
update["deny_ips"] = strings.TrimSpace(*req.DenyIPs)
|
||||
}
|
||||
if req.Status != nil {
|
||||
st := strings.ToLower(strings.TrimSpace(*req.Status))
|
||||
if st != "active" && st != "suspended" {
|
||||
|
||||
Reference in New Issue
Block a user