mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
feat: 支持固定类型 Provider OAuth 授权
- 新增 provider_type 字段区分自定义/预置 Provider 类型(claude_code/codex/gemini_cli/antigravity) - 实现完整 OAuth 2.0 授权流程:start(生成授权 URL + PKCE)、complete(换取 token)、refresh - 前端 KeyFormDialog 添加 OAuth 授权 UI,支持开始授权、粘贴回调 URL、完成授权、强制刷新 - 请求时自动检测 token 过期并刷新(120s 预留窗口 + Redis 分布式锁防并发) - 固定类型 Provider 自动创建预置端点并锁定 base_url/custom_path - 数据库迁移:添加 providers.provider_type,扩展 api_key 列为 TEXT - 可选依赖 tls-client 用于 Claude token 请求的 TLS 指纹伪装
This commit is contained in:
@@ -55,6 +55,11 @@ class CreateProviderRequest(BaseModel):
|
||||
"""创建 Provider 请求"""
|
||||
|
||||
name: str = Field(..., min_length=1, max_length=100, description="提供商名称(唯一)")
|
||||
provider_type: str | None = Field(
|
||||
default="custom",
|
||||
max_length=20,
|
||||
description="Provider 类型:custom/claude_code/codex/gemini_cli/antigravity",
|
||||
)
|
||||
description: str | None = Field(None, max_length=1000, description="描述")
|
||||
website: str | None = Field(None, max_length=500, description="官网地址")
|
||||
|
||||
@@ -116,6 +121,17 @@ class CreateProviderRequest(BaseModel):
|
||||
)
|
||||
config: dict[str, Any] | None = Field(None, description="其他配置")
|
||||
|
||||
@field_validator("provider_type")
|
||||
@classmethod
|
||||
def validate_provider_type(cls, v: str | None) -> str | None:
|
||||
if v is None:
|
||||
return "custom"
|
||||
v = v.strip()
|
||||
allowed = {"custom", "claude_code", "codex", "gemini_cli", "antigravity"}
|
||||
if v not in allowed:
|
||||
raise ValueError(f"无效的 provider_type,有效值为: {', '.join(sorted(allowed))}")
|
||||
return v
|
||||
|
||||
@field_validator("name", "description")
|
||||
@classmethod
|
||||
def sanitize_text(cls, v: str | None) -> str | None:
|
||||
@@ -171,6 +187,11 @@ class UpdateProviderRequest(BaseModel):
|
||||
"""更新 Provider 请求"""
|
||||
|
||||
name: str | None = Field(None, min_length=1, max_length=100)
|
||||
provider_type: str | None = Field(
|
||||
None,
|
||||
max_length=20,
|
||||
description="Provider 类型:custom/claude_code/codex/gemini_cli/antigravity",
|
||||
)
|
||||
description: str | None = Field(None, max_length=1000)
|
||||
website: str | None = Field(None, max_length=500)
|
||||
billing_type: str | None = None
|
||||
@@ -201,6 +222,9 @@ class UpdateProviderRequest(BaseModel):
|
||||
_validate_billing_type = field_validator("billing_type")(
|
||||
CreateProviderRequest.validate_billing_type.__func__
|
||||
)
|
||||
_validate_provider_type = field_validator("provider_type")(
|
||||
CreateProviderRequest.validate_provider_type.__func__
|
||||
)
|
||||
|
||||
|
||||
class CreateEndpointRequest(BaseModel):
|
||||
|
||||
@@ -638,6 +638,11 @@ class Provider(Base):
|
||||
description = Column(Text, nullable=True) # 提供商描述
|
||||
website = Column(String(500), nullable=True) # 主站网站
|
||||
|
||||
# Provider 类型(用于模板化固定 Provider / 自定义 Provider)
|
||||
# - custom: 自定义
|
||||
# - claude_code / codex / gemini_cli / antigravity: 固定类型
|
||||
provider_type = Column(String(20), default="custom", nullable=False)
|
||||
|
||||
# 计费类型配置
|
||||
billing_type = Column(
|
||||
Enum(
|
||||
@@ -1298,7 +1303,7 @@ class ProviderAPIKey(Base):
|
||||
# API密钥(加密存储)
|
||||
# - auth_type="api_key" 时:存储 API Key 字符串
|
||||
# - auth_type="vertex_ai" 等:可为空,敏感凭证存在 auth_config 中
|
||||
api_key = Column(String(500), nullable=False) # 保持 NOT NULL 兼容历史数据
|
||||
api_key = Column(Text, nullable=False) # 使用 Text 支持加密后的 OAuth token
|
||||
|
||||
# 认证配置(加密存储)
|
||||
# - auth_type="api_key" 时:可为空
|
||||
|
||||
@@ -200,12 +200,16 @@ class EndpointAPIKeyCreate(BaseModel):
|
||||
api_key: str = Field(
|
||||
default="", max_length=500, description="API Key(标准认证时必填,将自动加密)"
|
||||
)
|
||||
auth_type: Literal["api_key", "vertex_ai"] = Field(
|
||||
auth_type: Literal["api_key", "vertex_ai", "oauth"] = Field(
|
||||
default="api_key",
|
||||
description="认证类型:api_key(标准 API Key)或 vertex_ai(Vertex AI Service Account)",
|
||||
description="认证类型:api_key(标准 API Key)/ vertex_ai(Vertex AI Service Account)/ oauth(OAuth access_token)",
|
||||
)
|
||||
auth_config: dict[str, Any] | None = Field(
|
||||
default=None, description="认证配置(JSON):vertex_ai 时存储完整 Service Account JSON"
|
||||
default=None,
|
||||
description=(
|
||||
"认证配置(JSON):vertex_ai 时存储完整 Service Account JSON;"
|
||||
"oauth 时存储 token/refresh/expires_at 等(后端加密存储,不在响应中返回)"
|
||||
),
|
||||
)
|
||||
name: str = Field(..., min_length=1, max_length=100, description="密钥名称(必填,用于识别)")
|
||||
|
||||
@@ -360,12 +364,16 @@ class EndpointAPIKeyUpdate(BaseModel):
|
||||
max_length=500,
|
||||
description="API Key(标准认证时使用,将自动加密)",
|
||||
)
|
||||
auth_type: Literal["api_key", "vertex_ai"] | None = Field(
|
||||
auth_type: Literal["api_key", "vertex_ai", "oauth"] | None = Field(
|
||||
default=None,
|
||||
description="认证类型:api_key(标准 API Key)或 vertex_ai(Vertex AI Service Account)",
|
||||
description="认证类型:api_key(标准 API Key)/ vertex_ai(Vertex AI Service Account)/ oauth(OAuth access_token)",
|
||||
)
|
||||
auth_config: dict[str, Any] | None = Field(
|
||||
default=None, description="认证配置(JSON):vertex_ai 时存储完整 Service Account JSON"
|
||||
default=None,
|
||||
description=(
|
||||
"认证配置(JSON):vertex_ai 时存储完整 Service Account JSON;"
|
||||
"oauth 时存储 token/refresh/expires_at 等(后端加密存储,不在响应中返回)"
|
||||
),
|
||||
)
|
||||
name: str | None = Field(default=None, min_length=1, max_length=100, description="密钥名称")
|
||||
rate_multipliers: dict[str, float] | None = Field(
|
||||
@@ -690,6 +698,7 @@ class ProviderWithEndpointsSummary(BaseModel):
|
||||
# Provider 基本信息
|
||||
id: str
|
||||
name: str
|
||||
provider_type: str | None = Field(default=None, description="Provider 类型(custom/claude_code/codex/gemini_cli/antigravity)")
|
||||
description: str | None = None
|
||||
website: str | None = None
|
||||
provider_priority: int = Field(default=100, description="提供商优先级(数字越小越优先)")
|
||||
|
||||
Reference in New Issue
Block a user