完善 API Key 能力范围可视化和维护
This commit is contained in:
@@ -22,9 +22,34 @@ type Store struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
|
||||
func defaultAPIKeyScopes() []string {
|
||||
return []string{"chat", "embedding", "rerank", "image", "video", "music", "audio"}
|
||||
}
|
||||
|
||||
func normalizeAPIKeyScopes(scopes []string) []string {
|
||||
out := make([]string, 0, len(scopes))
|
||||
seen := make(map[string]struct{}, len(scopes))
|
||||
for _, scope := range scopes {
|
||||
scope = strings.ToLower(strings.TrimSpace(scope))
|
||||
if scope == "" {
|
||||
continue
|
||||
}
|
||||
if scope == "*" {
|
||||
scope = "all"
|
||||
}
|
||||
if _, ok := seen[scope]; ok {
|
||||
continue
|
||||
}
|
||||
seen[scope] = struct{}{}
|
||||
out = append(out, scope)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidCredentials = errors.New("invalid account or password")
|
||||
ErrInvalidInvitation = errors.New("invalid or expired invitation code")
|
||||
ErrInvalidAPIKeyScopes = errors.New("api key scopes must not be empty")
|
||||
ErrAccessRuleResourceDenied = errors.New("access rule resource is not available")
|
||||
ErrInsufficientWalletBalance = errors.New("insufficient wallet balance")
|
||||
ErrLocalUserRequired = errors.New("local gateway user is required")
|
||||
@@ -106,6 +131,10 @@ type CreateAPIKeyInput struct {
|
||||
ExpiresAt string `json:"expiresAt"`
|
||||
}
|
||||
|
||||
type UpdateAPIKeyScopesInput struct {
|
||||
Scopes []string `json:"scopes"`
|
||||
}
|
||||
|
||||
type APIKey struct {
|
||||
ID string `json:"id"`
|
||||
GatewayTenantID string `json:"gatewayTenantId,omitempty"`
|
||||
@@ -1156,6 +1185,32 @@ ORDER BY priority ASC, group_key ASC`)
|
||||
return items, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Store) ListUserGroupsBySubject(ctx context.Context, groupIDs []string, groupKeys []string) ([]UserGroup, error) {
|
||||
rows, err := s.pool.Query(ctx, `
|
||||
SELECT `+userGroupColumns+`
|
||||
FROM gateway_user_groups
|
||||
WHERE status = 'active'
|
||||
AND (
|
||||
id::text = ANY($1::text[])
|
||||
OR group_key = ANY($2::text[])
|
||||
)
|
||||
ORDER BY priority ASC, group_key ASC`, groupIDs, groupKeys)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
items := make([]UserGroup, 0)
|
||||
for rows.Next() {
|
||||
item, err := scanUserGroup(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
return items, rows.Err()
|
||||
}
|
||||
|
||||
func (s *Store) ListAPIKeys(ctx context.Context, user *auth.User) ([]APIKey, error) {
|
||||
gatewayUserID := localGatewayUserID(user)
|
||||
if gatewayUserID == "" {
|
||||
@@ -1201,7 +1256,7 @@ func (s *Store) ListPlayableAPIKeys(ctx context.Context, user *auth.User) ([]Pla
|
||||
}
|
||||
created, err := s.CreateAPIKey(ctx, CreateAPIKeyInput{
|
||||
Name: "Playground API Key",
|
||||
Scopes: []string{"chat", "image", "video"},
|
||||
Scopes: defaultAPIKeyScopes(),
|
||||
}, user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -1256,8 +1311,9 @@ func (s *Store) CreateAPIKey(ctx context.Context, input CreateAPIKeyInput, user
|
||||
}
|
||||
scopes := input.Scopes
|
||||
if len(scopes) == 0 {
|
||||
scopes = []string{"chat", "image", "video"}
|
||||
scopes = defaultAPIKeyScopes()
|
||||
}
|
||||
scopes = normalizeAPIKeyScopes(scopes)
|
||||
secret, err := generateAPIKeySecret()
|
||||
if err != nil {
|
||||
return CreatedAPIKey{}, err
|
||||
@@ -1317,6 +1373,32 @@ RETURNING id::text, COALESCE(gateway_tenant_id::text, ''), gateway_user_id::text
|
||||
return CreatedAPIKey{APIKey: item, Secret: secret}, nil
|
||||
}
|
||||
|
||||
func (s *Store) UpdateAPIKeyScopes(ctx context.Context, apiKeyID string, input UpdateAPIKeyScopesInput, user *auth.User) (APIKey, error) {
|
||||
gatewayUserID := localGatewayUserID(user)
|
||||
if gatewayUserID == "" {
|
||||
return APIKey{}, ErrLocalUserRequired
|
||||
}
|
||||
scopes := normalizeAPIKeyScopes(input.Scopes)
|
||||
if len(scopes) == 0 {
|
||||
return APIKey{}, ErrInvalidAPIKeyScopes
|
||||
}
|
||||
scopesJSON, err := json.Marshal(scopes)
|
||||
if err != nil {
|
||||
return APIKey{}, err
|
||||
}
|
||||
return scanAPIKey(s.pool.QueryRow(ctx, `
|
||||
UPDATE gateway_api_keys
|
||||
SET scopes = $3::jsonb, updated_at = now()
|
||||
WHERE id = $1::uuid AND gateway_user_id = $2::uuid AND deleted_at IS NULL
|
||||
RETURNING id::text, COALESCE(gateway_tenant_id::text, ''), gateway_user_id::text,
|
||||
COALESCE(tenant_id, ''), COALESCE(tenant_key, ''), COALESCE(user_id, ''),
|
||||
key_prefix, name, scopes, COALESCE(user_group_id::text, ''),
|
||||
rate_limit_policy, quota_policy, status, COALESCE(expires_at::text, ''),
|
||||
COALESCE(last_used_at::text, ''), created_at, updated_at`,
|
||||
apiKeyID, gatewayUserID, string(scopesJSON),
|
||||
))
|
||||
}
|
||||
|
||||
func (s *Store) DisableAPIKey(ctx context.Context, apiKeyID string, user *auth.User) (APIKey, error) {
|
||||
gatewayUserID := localGatewayUserID(user)
|
||||
if gatewayUserID == "" {
|
||||
|
||||
Reference in New Issue
Block a user