feat: 添加访问令牌管理功能并升级至 0.2.4

- 新增 Management Token(访问令牌)功能,支持创建、更新、删除和管理
- 前端添加访问令牌管理页面,支持普通用户和管理员
- 后端实现完整的令牌生命周期管理 API
- 添加数据库迁移脚本创建 management_tokens 表
- Nginx 配置添加 gzip 压缩,优化响应传输
- Dialog 组件添加 persistent 属性,防止意外关闭
- 为管理后台 API 添加详细的中文文档注释
- 简化多处类型注解,统一代码风格
This commit is contained in:
fawney19
2026-01-07 14:55:07 +08:00
parent f6a6410626
commit 0061fc04b7
59 changed files with 6265 additions and 648 deletions

View File

@@ -49,7 +49,36 @@ async def list_provider_models(
limit: int = 100,
db: Session = Depends(get_db),
) -> List[ModelResponse]:
"""获取提供商的所有模型(管理员)"""
"""
获取提供商的所有模型
获取指定提供商的模型列表,支持分页和状态过滤。
**路径参数**:
- `provider_id`: 提供商 ID
**查询参数**:
- `is_active`: 可选的活跃状态过滤true 仅返回活跃模型false 返回禁用模型,不传则返回全部
- `skip`: 跳过的记录数,默认为 0
- `limit`: 返回的最大记录数,默认为 100
**返回字段**(数组,每项包含):
- `id`: 模型 ID
- `provider_id`: 提供商 ID
- `global_model_id`: 全局模型 ID
- `provider_model_name`: 提供商模型名称
- `is_active`: 是否启用
- `input_price_per_1m`: 输入价格(每百万 token
- `output_price_per_1m`: 输出价格(每百万 token
- `cache_creation_price_per_1m`: 缓存创建价格(每百万 token
- `cache_read_price_per_1m`: 缓存读取价格(每百万 token
- `price_per_request`: 每次请求价格
- `supports_vision`: 是否支持视觉
- `supports_function_calling`: 是否支持函数调用
- `supports_streaming`: 是否支持流式输出
- `created_at`: 创建时间
- `updated_at`: 更新时间
"""
adapter = AdminListProviderModelsAdapter(
provider_id=provider_id,
is_active=is_active,
@@ -66,7 +95,29 @@ async def create_provider_model(
request: Request,
db: Session = Depends(get_db),
) -> ModelResponse:
"""创建模型(管理员)"""
"""
创建模型
为指定提供商创建一个新的模型配置。
**路径参数**:
- `provider_id`: 提供商 ID
**请求体字段**:
- `provider_model_name`: 提供商模型名称(必填)
- `global_model_id`: 全局模型 ID可选关联到全局模型
- `is_active`: 是否启用(默认 true
- `input_price_per_1m`: 输入价格(每百万 token可选
- `output_price_per_1m`: 输出价格(每百万 token可选
- `cache_creation_price_per_1m`: 缓存创建价格(每百万 token可选
- `cache_read_price_per_1m`: 缓存读取价格(每百万 token可选
- `price_per_request`: 每次请求价格(可选)
- `supports_vision`: 是否支持视觉(可选)
- `supports_function_calling`: 是否支持函数调用(可选)
- `supports_streaming`: 是否支持流式输出(可选)
**返回字段**: 返回创建的模型详细信息(与 GET 单个模型接口返回格式相同)
"""
adapter = AdminCreateProviderModelAdapter(provider_id=provider_id, model_data=model_data)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@@ -78,7 +129,32 @@ async def get_provider_model(
request: Request,
db: Session = Depends(get_db),
) -> ModelResponse:
"""获取模型详情(管理员)"""
"""
获取模型详情
获取指定模型的详细配置信息。
**路径参数**:
- `provider_id`: 提供商 ID
- `model_id`: 模型 ID
**返回字段**:
- `id`: 模型 ID
- `provider_id`: 提供商 ID
- `global_model_id`: 全局模型 ID
- `provider_model_name`: 提供商模型名称
- `is_active`: 是否启用
- `input_price_per_1m`: 输入价格(每百万 token
- `output_price_per_1m`: 输出价格(每百万 token
- `cache_creation_price_per_1m`: 缓存创建价格(每百万 token
- `cache_read_price_per_1m`: 缓存读取价格(每百万 token
- `price_per_request`: 每次请求价格
- `supports_vision`: 是否支持视觉
- `supports_function_calling`: 是否支持函数调用
- `supports_streaming`: 是否支持流式输出
- `created_at`: 创建时间
- `updated_at`: 更新时间
"""
adapter = AdminGetProviderModelAdapter(provider_id=provider_id, model_id=model_id)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@@ -91,7 +167,30 @@ async def update_provider_model(
request: Request,
db: Session = Depends(get_db),
) -> ModelResponse:
"""更新模型(管理员)"""
"""
更新模型配置
更新指定模型的配置信息。只需传入需要更新的字段,未传入的字段保持不变。
**路径参数**:
- `provider_id`: 提供商 ID
- `model_id`: 模型 ID
**请求体字段**(所有字段可选):
- `provider_model_name`: 提供商模型名称
- `global_model_id`: 全局模型 ID
- `is_active`: 是否启用
- `input_price_per_1m`: 输入价格(每百万 token
- `output_price_per_1m`: 输出价格(每百万 token
- `cache_creation_price_per_1m`: 缓存创建价格(每百万 token
- `cache_read_price_per_1m`: 缓存读取价格(每百万 token
- `price_per_request`: 每次请求价格
- `supports_vision`: 是否支持视觉
- `supports_function_calling`: 是否支持函数调用
- `supports_streaming`: 是否支持流式输出
**返回字段**: 返回更新后的模型详细信息(与 GET 单个模型接口返回格式相同)
"""
adapter = AdminUpdateProviderModelAdapter(
provider_id=provider_id,
model_id=model_id,
@@ -107,7 +206,18 @@ async def delete_provider_model(
request: Request,
db: Session = Depends(get_db),
):
"""删除模型(管理员)"""
"""
删除模型
删除指定的模型配置。注意:此操作不可逆。
**路径参数**:
- `provider_id`: 提供商 ID
- `model_id`: 模型 ID
**返回字段**:
- `message`: 删除成功提示信息
"""
adapter = AdminDeleteProviderModelAdapter(provider_id=provider_id, model_id=model_id)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@@ -119,7 +229,29 @@ async def batch_create_provider_models(
request: Request,
db: Session = Depends(get_db),
) -> List[ModelResponse]:
"""批量创建模型(管理员)"""
"""
批量创建模型
为指定提供商批量创建多个模型配置。
**路径参数**:
- `provider_id`: 提供商 ID
**请求体**: 模型数据数组,每项包含:
- `provider_model_name`: 提供商模型名称(必填)
- `global_model_id`: 全局模型 ID可选
- `is_active`: 是否启用(默认 true
- `input_price_per_1m`: 输入价格(每百万 token可选
- `output_price_per_1m`: 输出价格(每百万 token可选
- `cache_creation_price_per_1m`: 缓存创建价格(每百万 token可选
- `cache_read_price_per_1m`: 缓存读取价格(每百万 token可选
- `price_per_request`: 每次请求价格(可选)
- `supports_vision`: 是否支持视觉(可选)
- `supports_function_calling`: 是否支持函数调用(可选)
- `supports_streaming`: 是否支持流式输出(可选)
**返回字段**: 返回创建的模型列表(与 GET 模型列表接口返回格式相同)
"""
adapter = AdminBatchCreateModelsAdapter(provider_id=provider_id, models_data=models_data)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@@ -134,10 +266,23 @@ async def get_provider_available_source_models(
db: Session = Depends(get_db),
):
"""
获取该 Provider 支持的所有统一模型名source_model
获取提供商支持的可用源模型
包括:
1. 直连模型Model.provider_model_name 直接作为统一模型名)
获取该提供商支持的所有统一模型名source_model包含价格和能力信息。
**路径参数**:
- `provider_id`: 提供商 ID
**返回字段**:
- `models`: 可用源模型数组,每项包含:
- `global_model_name`: 全局模型名称
- `display_name`: 显示名称
- `provider_model_name`: 提供商模型名称
- `model_id`: 模型 ID
- `price`: 价格信息(包含 input_price_per_1m, output_price_per_1m, cache_creation_price_per_1m, cache_read_price_per_1m, price_per_request
- `capabilities`: 能力信息(包含 supports_vision, supports_function_calling, supports_streaming
- `is_active`: 是否启用
- `total`: 总数
"""
adapter = AdminGetProviderAvailableSourceModelsAdapter(provider_id=provider_id)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@@ -153,7 +298,27 @@ async def batch_assign_global_models_to_provider(
request: Request,
db: Session = Depends(get_db),
) -> BatchAssignModelsToProviderResponse:
"""批量为 Provider 关联 GlobalModels自动继承价格和能力配置"""
"""
批量关联全局模型
批量为提供商关联全局模型,自动继承全局模型的价格和能力配置。
**路径参数**:
- `provider_id`: 提供商 ID
**请求体字段**:
- `global_model_ids`: 全局模型 ID 数组(必填)
**返回字段**:
- `success`: 成功关联的模型数组,每项包含:
- `global_model_id`: 全局模型 ID
- `global_model_name`: 全局模型名称
- `model_id`: 新创建的模型 ID
- `errors`: 失败的模型数组,每项包含:
- `global_model_id`: 全局模型 ID
- `global_model_name`: 全局模型名称(如果可用)
- `error`: 错误信息
"""
adapter = AdminBatchAssignModelsToProviderAdapter(
provider_id=provider_id, payload=payload
)
@@ -173,10 +338,30 @@ async def import_models_from_upstream(
"""
从上游提供商导入模型
流程:
从上游提供商导入模型列表。如果全局模型不存在,将自动创建。
**流程说明**:
1. 根据 model_ids 检查全局模型是否存在(按 name 匹配)
2. 如不存在,自动创建新的 GlobalModel使用默认配置
2. 如不存在,自动创建新的 GlobalModel使用默认免费配置)
3. 创建 Model 关联到当前 Provider
4. 如模型已关联,则记录到成功列表中
**路径参数**:
- `provider_id`: 提供商 ID
**请求体字段**:
- `model_ids`: 模型 ID 数组(必填,每个 ID 长度 1-100 字符)
**返回字段**:
- `success`: 成功导入的模型数组,每项包含:
- `model_id`: 模型 ID
- `global_model_id`: 全局模型 ID
- `global_model_name`: 全局模型名称
- `provider_model_id`: 提供商模型 ID
- `created_global_model`: 是否新创建了全局模型
- `errors`: 失败的模型数组,每项包含:
- `model_id`: 模型 ID
- `error`: 错误信息
"""
adapter = AdminImportFromUpstreamAdapter(provider_id=provider_id, payload=payload)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)

View File

@@ -27,24 +27,116 @@ async def list_providers(
is_active: Optional[bool] = None,
db: Session = Depends(get_db),
):
"""
获取提供商列表
获取所有提供商的基本信息列表,支持分页和状态过滤。
**查询参数**:
- `skip`: 跳过的记录数,用于分页,默认为 0
- `limit`: 返回的最大记录数,范围 1-500默认为 100
- `is_active`: 可选的活跃状态过滤true 仅返回活跃提供商false 返回禁用提供商,不传则返回全部
**返回字段**:
- `id`: 提供商 ID
- `name`: 提供商名称(唯一标识)
- `display_name`: 显示名称
- `api_format`: API 格式(如 claude、openai、gemini 等)
- `base_url`: API 基础 URL
- `api_key`: API 密钥(脱敏显示)
- `priority`: 优先级
- `is_active`: 是否活跃
- `created_at`: 创建时间
- `updated_at`: 更新时间
"""
adapter = AdminListProvidersAdapter(skip=skip, limit=limit, is_active=is_active)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@router.post("/")
async def create_provider(request: Request, db: Session = Depends(get_db)):
"""
创建新提供商
创建一个新的 AI 模型提供商配置。
**请求体字段**:
- `name`: 提供商名称(必填,唯一,用于系统标识)
- `display_name`: 显示名称(必填)
- `description`: 描述信息(可选)
- `website`: 官网地址(可选)
- `billing_type`: 计费类型可选pay_as_you_go/subscription/prepaid默认 pay_as_you_go
- `monthly_quota_usd`: 月度配额(美元)(可选)
- `quota_reset_day`: 配额重置日期1-31可选
- `quota_last_reset_at`: 上次配额重置时间(可选)
- `quota_expires_at`: 配额过期时间(可选)
- `rpm_limit`: 每分钟请求数限制(可选)
- `provider_priority`: 提供商优先级(数字越小优先级越高,默认 100
- `is_active`: 是否启用(默认 true
- `rate_limit`: 速率限制配置(可选)
- `concurrent_limit`: 并发限制(可选)
- `config`: 额外配置信息JSON可选
**返回字段**:
- `id`: 新创建的提供商 ID
- `name`: 提供商名称
- `display_name`: 显示名称
- `message`: 成功提示信息
"""
adapter = AdminCreateProviderAdapter()
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@router.put("/{provider_id}")
async def update_provider(provider_id: str, request: Request, db: Session = Depends(get_db)):
"""
更新提供商配置
更新指定提供商的配置信息。只需传入需要更新的字段,未传入的字段保持不变。
**路径参数**:
- `provider_id`: 提供商 ID
**请求体字段**(所有字段可选):
- `name`: 提供商名称
- `display_name`: 显示名称
- `description`: 描述信息
- `website`: 官网地址
- `billing_type`: 计费类型pay_as_you_go/subscription/prepaid
- `monthly_quota_usd`: 月度配额(美元)
- `quota_reset_day`: 配额重置日期1-31
- `quota_last_reset_at`: 上次配额重置时间
- `quota_expires_at`: 配额过期时间
- `rpm_limit`: 每分钟请求数限制
- `provider_priority`: 提供商优先级
- `is_active`: 是否启用
- `rate_limit`: 速率限制配置
- `concurrent_limit`: 并发限制
- `config`: 额外配置信息JSON
**返回字段**:
- `id`: 提供商 ID
- `name`: 提供商名称
- `is_active`: 是否启用
- `message`: 成功提示信息
"""
adapter = AdminUpdateProviderAdapter(provider_id=provider_id)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@router.delete("/{provider_id}")
async def delete_provider(provider_id: str, request: Request, db: Session = Depends(get_db)):
"""
删除提供商
删除指定的提供商。注意:此操作会级联删除关联的端点、密钥和模型配置。
**路径参数**:
- `provider_id`: 提供商 ID
**返回字段**:
- `message`: 删除成功提示信息
"""
adapter = AdminDeleteProviderAdapter(provider_id=provider_id)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)

View File

@@ -40,7 +40,41 @@ async def get_providers_summary(
request: Request,
db: Session = Depends(get_db),
) -> List[ProviderWithEndpointsSummary]:
"""获取所有 Providers 的摘要信息(包含 Endpoints 和 Keys 统计)"""
"""
获取所有提供商摘要信息
获取所有提供商的详细摘要信息,包含端点、密钥、模型统计和健康状态。
**返回字段**(数组,每项包含):
- `id`: 提供商 ID
- `name`: 提供商名称
- `display_name`: 显示名称
- `description`: 描述信息
- `website`: 官网地址
- `provider_priority`: 优先级
- `is_active`: 是否启用
- `billing_type`: 计费类型
- `monthly_quota_usd`: 月度配额(美元)
- `monthly_used_usd`: 本月已使用金额(美元)
- `quota_reset_day`: 配额重置日期
- `quota_last_reset_at`: 上次配额重置时间
- `quota_expires_at`: 配额过期时间
- `rpm_limit`: RPM 限制
- `rpm_used`: 已使用 RPM
- `rpm_reset_at`: RPM 重置时间
- `total_endpoints`: 端点总数
- `active_endpoints`: 活跃端点数
- `total_keys`: 密钥总数
- `active_keys`: 活跃密钥数
- `total_models`: 模型总数
- `active_models`: 活跃模型数
- `avg_health_score`: 平均健康分数0-1
- `unhealthy_endpoints`: 不健康端点数(健康分数 < 0.5
- `api_formats`: 支持的 API 格式列表
- `endpoint_health_details`: 端点健康详情(包含 api_format, health_score, is_active, active_keys
- `created_at`: 创建时间
- `updated_at`: 更新时间
"""
adapter = AdminProviderSummaryAdapter()
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@@ -51,7 +85,44 @@ async def get_provider_summary(
request: Request,
db: Session = Depends(get_db),
) -> ProviderWithEndpointsSummary:
"""获取单个 Provider 的摘要信息(包含 Endpoints 和 Keys 统计)"""
"""
获取单个提供商摘要信息
获取指定提供商的详细摘要信息,包含端点、密钥、模型统计和健康状态。
**路径参数**:
- `provider_id`: 提供商 ID
**返回字段**:
- `id`: 提供商 ID
- `name`: 提供商名称
- `display_name`: 显示名称
- `description`: 描述信息
- `website`: 官网地址
- `provider_priority`: 优先级
- `is_active`: 是否启用
- `billing_type`: 计费类型
- `monthly_quota_usd`: 月度配额(美元)
- `monthly_used_usd`: 本月已使用金额(美元)
- `quota_reset_day`: 配额重置日期
- `quota_last_reset_at`: 上次配额重置时间
- `quota_expires_at`: 配额过期时间
- `rpm_limit`: RPM 限制
- `rpm_used`: 已使用 RPM
- `rpm_reset_at`: RPM 重置时间
- `total_endpoints`: 端点总数
- `active_endpoints`: 活跃端点数
- `total_keys`: 密钥总数
- `active_keys`: 活跃密钥数
- `total_models`: 模型总数
- `active_models`: 活跃模型数
- `avg_health_score`: 平均健康分数0-1
- `unhealthy_endpoints`: 不健康端点数(健康分数 < 0.5
- `api_formats`: 支持的 API 格式列表
- `endpoint_health_details`: 端点健康详情(包含 api_format, health_score, is_active, active_keys
- `created_at`: 创建时间
- `updated_at`: 更新时间
"""
provider = db.query(Provider).filter(Provider.id == provider_id).first()
if not provider:
raise NotFoundException(f"Provider {provider_id} not found")
@@ -67,7 +138,34 @@ async def get_provider_health_monitor(
per_endpoint_limit: int = Query(48, ge=10, le=200, description="每个端点的事件数量"),
db: Session = Depends(get_db),
) -> ProviderEndpointHealthMonitorResponse:
"""获取 Provider 下所有端点的健康监控时间线"""
"""
获取提供商健康监控数据
获取指定提供商下所有端点的健康监控时间线,包含请求成功率、延迟、错误信息等。
**路径参数**:
- `provider_id`: 提供商 ID
**查询参数**:
- `lookback_hours`: 回溯的小时数,范围 1-72默认为 6
- `per_endpoint_limit`: 每个端点返回的事件数量,范围 10-200默认为 48
**返回字段**:
- `provider_id`: 提供商 ID
- `provider_name`: 提供商名称
- `generated_at`: 生成时间
- `endpoints`: 端点健康监控数据数组,每项包含:
- `endpoint_id`: 端点 ID
- `api_format`: API 格式
- `is_active`: 是否活跃
- `total_attempts`: 总请求次数
- `success_count`: 成功次数
- `failed_count`: 失败次数
- `skipped_count`: 跳过次数
- `success_rate`: 成功率0-1
- `last_event_at`: 最后事件时间
- `events`: 事件详情数组(包含 timestamp, status, status_code, latency_ms, error_type, error_message
"""
adapter = AdminProviderHealthMonitorAdapter(
provider_id=provider_id,
@@ -84,7 +182,29 @@ async def update_provider_settings(
request: Request,
db: Session = Depends(get_db),
) -> ProviderWithEndpointsSummary:
"""更新 Provider 基础配置display_name, description, priority, weight 等)"""
"""
更新提供商基础配置
更新提供商的基础配置信息,如显示名称、描述、优先级等。只需传入需要更新的字段。
**路径参数**:
- `provider_id`: 提供商 ID
**请求体字段**(所有字段可选):
- `display_name`: 显示名称
- `description`: 描述信息
- `website`: 官网地址
- `provider_priority`: 优先级
- `is_active`: 是否启用
- `billing_type`: 计费类型
- `monthly_quota_usd`: 月度配额(美元)
- `quota_reset_day`: 配额重置日期
- `quota_last_reset_at`: 上次配额重置时间
- `quota_expires_at`: 配额过期时间
- `rpm_limit`: RPM 限制
**返回字段**: 返回更新后的提供商摘要信息(与 GET /summary 接口返回格式相同)
"""
adapter = AdminUpdateProviderSettingsAdapter(provider_id=provider_id, update_data=update_data)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)