fix: 修复 mypy 类型检查错误并升级到 Python 3.14

主要变更:
- 修复 1483 个 mypy 类型检查错误
- 添加缺失的类型注解 (Any, Callable, Session 等)
- 修复隐式 Optional 类型 (param: Type = None -> param: Type | None = None)
- 修复 __new__ 单例模式返回类型
- 添加 type: ignore 注释处理第三方库类型问题
- 更新 pyproject.toml 依赖到 Python 3.14 兼容版本
- 更新 mypy/black 配置为 Python 3.14
This commit is contained in:
fawney19
2026-01-30 14:30:57 +08:00
parent 7066166757
commit 5603c72f40
142 changed files with 2864 additions and 1853 deletions

View File

@@ -9,6 +9,9 @@
- adaptive_mode 是计算字段,基于 rpm_limit 是否为 NULL
"""
from __future__ import annotations
from typing import Any
from dataclasses import dataclass
from fastapi import APIRouter, Depends, HTTPException, Query, Request
@@ -21,6 +24,7 @@ from src.core.exceptions import InvalidRequestException, translate_pydantic_erro
from src.database import get_db
from src.models.database import ProviderAPIKey
from src.services.rate_limit.adaptive_rpm import get_adaptive_rpm_manager
from src.api.base.context import ApiRequestContext
router = APIRouter(prefix="/api/admin/adaptive", tags=["Adaptive RPM"])
pipeline = ApiRequestPipeline()
@@ -82,7 +86,7 @@ async def list_adaptive_keys(
request: Request,
provider_id: str | None = Query(None, description="按 Provider 过滤"),
db: Session = Depends(get_db),
):
) -> Any:
"""
获取所有启用自适应模式的Key列表
@@ -101,7 +105,7 @@ async def toggle_adaptive_mode(
key_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
Toggle the RPM control mode for a specific key
@@ -122,7 +126,7 @@ async def get_adaptive_stats(
key_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
获取指定Key的自适应 RPM 统计信息
@@ -144,7 +148,7 @@ async def reset_adaptive_learning(
key_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
Reset the adaptive learning state for a specific key
@@ -169,7 +173,7 @@ async def set_rpm_limit(
request: Request,
limit: int = Query(..., ge=1, le=100, description="RPM limit value (1-100)"),
db: Session = Depends(get_db),
):
) -> Any:
"""
Set key to fixed RPM limit mode
@@ -188,7 +192,7 @@ async def set_rpm_limit(
async def get_adaptive_summary(
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
获取自适应 RPM 的全局统计摘要
@@ -208,7 +212,7 @@ async def get_adaptive_summary(
class ListAdaptiveKeysAdapter(AdminApiAdapter):
provider_id: str | None = None
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
# 自适应模式rpm_limit = NULL
query = context.db.query(ProviderAPIKey).filter(ProviderAPIKey.rpm_limit.is_(None))
if self.provider_id:
@@ -240,7 +244,7 @@ class ListAdaptiveKeysAdapter(AdminApiAdapter):
class ToggleAdaptiveModeAdapter(AdminApiAdapter):
key_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
key = context.db.query(ProviderAPIKey).filter(ProviderAPIKey.id == self.key_id).first()
if not key:
raise HTTPException(status_code=404, detail="Key not found")
@@ -288,7 +292,7 @@ class ToggleAdaptiveModeAdapter(AdminApiAdapter):
class GetAdaptiveStatsAdapter(AdminApiAdapter):
key_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
key = context.db.query(ProviderAPIKey).filter(ProviderAPIKey.id == self.key_id).first()
if not key:
raise HTTPException(status_code=404, detail="Key not found")
@@ -315,7 +319,7 @@ class GetAdaptiveStatsAdapter(AdminApiAdapter):
class ResetAdaptiveLearningAdapter(AdminApiAdapter):
key_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
key = context.db.query(ProviderAPIKey).filter(ProviderAPIKey.id == self.key_id).first()
if not key:
raise HTTPException(status_code=404, detail="Key not found")
@@ -330,7 +334,7 @@ class SetRPMLimitAdapter(AdminApiAdapter):
key_id: str
limit: int
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
key = context.db.query(ProviderAPIKey).filter(ProviderAPIKey.id == self.key_id).first()
if not key:
raise HTTPException(status_code=404, detail="Key not found")
@@ -350,7 +354,7 @@ class SetRPMLimitAdapter(AdminApiAdapter):
class AdaptiveSummaryAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
# 自适应模式rpm_limit = NULL
adaptive_keys = (
context.db.query(ProviderAPIKey).filter(ProviderAPIKey.rpm_limit.is_(None)).all()

View File

@@ -3,6 +3,9 @@
独立余额Key不关联用户配额有独立余额限制用于给非注册用户使用。
"""
from __future__ import annotations
from typing import Any
import os
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo
@@ -18,6 +21,7 @@ from src.database import get_db
from src.models.api import CreateApiKeyRequest
from src.models.database import ApiKey
from src.services.user.apikey import ApiKeyService
from src.api.base.context import ApiRequestContext
# 应用时区配置,默认为 Asia/Shanghai
@@ -71,7 +75,7 @@ async def list_standalone_api_keys(
limit: int = Query(100, ge=1, le=500),
is_active: bool | None = None,
db: Session = Depends(get_db),
):
) -> Any:
"""
列出所有独立余额 API Keys
@@ -101,7 +105,7 @@ async def create_standalone_api_key(
request: Request,
key_data: CreateApiKeyRequest,
db: Session = Depends(get_db),
):
) -> Any:
"""
创建独立余额 API Key
@@ -138,7 +142,7 @@ async def create_standalone_api_key(
@router.put("/{key_id}")
async def update_api_key(
key_id: str, request: Request, key_data: CreateApiKeyRequest, db: Session = Depends(get_db)
):
) -> Any:
"""
更新独立余额 API Key
@@ -174,7 +178,7 @@ async def update_api_key(
@router.patch("/{key_id}")
async def toggle_api_key(key_id: str, request: Request, db: Session = Depends(get_db)):
async def toggle_api_key(key_id: str, request: Request, db: Session = Depends(get_db)) -> Any:
"""
切换 API Key 启用状态
@@ -193,7 +197,7 @@ async def toggle_api_key(key_id: str, request: Request, db: Session = Depends(ge
@router.delete("/{key_id}")
async def delete_api_key(key_id: str, request: Request, db: Session = Depends(get_db)):
async def delete_api_key(key_id: str, request: Request, db: Session = Depends(get_db)) -> None:
"""
删除 API Key
@@ -210,7 +214,7 @@ async def delete_api_key(key_id: str, request: Request, db: Session = Depends(ge
@router.patch("/{key_id}/lock")
async def toggle_lock_api_key(key_id: str, request: Request, db: Session = Depends(get_db)):
async def toggle_lock_api_key(key_id: str, request: Request, db: Session = Depends(get_db)) -> Any:
"""
切换 API Key 锁定状态
@@ -233,7 +237,7 @@ async def add_balance_to_key(
key_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
调整独立余额 API Key 的余额
@@ -295,7 +299,7 @@ async def get_api_key_detail(
request: Request,
include_key: bool = Query(False, description="Include full decrypted key in response"),
db: Session = Depends(get_db),
):
) -> Any:
"""
获取 API Key 详情
@@ -335,7 +339,7 @@ class AdminListStandaloneKeysAdapter(AdminApiAdapter):
self.limit = limit
self.is_active = is_active
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
# 只查询独立余额Keys
query = db.query(ApiKey).filter(ApiKey.is_standalone == True)
@@ -396,7 +400,7 @@ class AdminCreateStandaloneKeyAdapter(AdminApiAdapter):
def __init__(self, key_data: CreateApiKeyRequest):
self.key_data = key_data
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
# 独立Key必须设置初始余额
@@ -458,7 +462,7 @@ class AdminUpdateApiKeyAdapter(AdminApiAdapter):
self.key_id = key_id
self.key_data = key_data
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
api_key = db.query(ApiKey).filter(ApiKey.id == self.key_id).first()
if not api_key:
@@ -533,7 +537,7 @@ class AdminToggleApiKeyAdapter(AdminApiAdapter):
def __init__(self, key_id: str):
self.key_id = key_id
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
api_key = db.query(ApiKey).filter(ApiKey.id == self.key_id).first()
if not api_key:
@@ -566,7 +570,7 @@ class AdminToggleLockApiKeyAdapter(AdminApiAdapter):
def __init__(self, key_id: str):
self.key_id = key_id
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
api_key = db.query(ApiKey).filter(ApiKey.id == self.key_id).first()
if not api_key:
@@ -597,7 +601,7 @@ class AdminDeleteApiKeyAdapter(AdminApiAdapter):
def __init__(self, key_id: str):
self.key_id = key_id
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
api_key = db.query(ApiKey).filter(ApiKey.id == self.key_id).first()
if not api_key:
@@ -625,7 +629,7 @@ class AdminAddBalanceAdapter(AdminApiAdapter):
self.key_id = key_id
self.amount_usd = amount_usd
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
# 使用 ApiKeyService 增加余额
@@ -658,7 +662,7 @@ class AdminGetFullKeyAdapter(AdminApiAdapter):
def __init__(self, key_id: str):
self.key_id = key_id
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
from src.core.crypto import crypto_service
db = context.db
@@ -697,7 +701,7 @@ class AdminGetKeyDetailAdapter(AdminApiAdapter):
def __init__(self, key_id: str):
self.key_id = key_id
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
api_key = db.query(ApiKey).filter(ApiKey.id == self.key_id).first()

View File

@@ -2,6 +2,7 @@
Key RPM 限制管理 API
"""
from typing import Any
from dataclasses import dataclass
from fastapi import APIRouter, Depends, Request
@@ -14,6 +15,7 @@ from src.database import get_db
from src.models.database import ProviderAPIKey
from src.models.endpoint_models import KeyRpmStatusResponse
from src.services.rate_limit.concurrency_manager import get_concurrency_manager
from src.api.base.context import ApiRequestContext
router = APIRouter(tags=["RPM Control"])
pipeline = ApiRequestPipeline()
@@ -71,7 +73,7 @@ async def reset_key_rpm(
class AdminKeyRpmAdapter(AdminApiAdapter):
key_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
key = db.query(ProviderAPIKey).filter(ProviderAPIKey.id == self.key_id).first()
if not key:
@@ -91,7 +93,7 @@ class AdminKeyRpmAdapter(AdminApiAdapter):
class AdminResetKeyRpmAdapter(AdminApiAdapter):
key_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
concurrency_manager = await get_concurrency_manager()
await concurrency_manager.reset_key_rpm(key_id=self.key_id)
return {"message": "RPM 计数已重置"}

View File

@@ -2,6 +2,9 @@
Endpoint 健康监控 API
"""
from __future__ import annotations
from typing import Any
from collections import defaultdict
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
@@ -25,6 +28,7 @@ from src.models.endpoint_models import (
)
from src.services.health.endpoint import EndpointHealthService
from src.services.health.monitor import health_monitor
from src.api.base.context import ApiRequestContext
router = APIRouter(tags=["Endpoint Health"])
pipeline = ApiRequestPipeline()
@@ -58,7 +62,7 @@ async def get_endpoint_health_status(
request: Request,
lookback_hours: int = Query(6, ge=1, le=72, description="回溯的小时数"),
db: Session = Depends(get_db),
):
) -> Any:
"""
获取端点健康状态(简化视图,与用户端点统一)
@@ -218,7 +222,7 @@ async def recover_all_keys_health(
class AdminHealthSummaryAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
summary = health_monitor.get_all_health_status(context.db)
return HealthSummaryResponse(**summary)
@@ -229,7 +233,7 @@ class AdminEndpointHealthStatusAdapter(AdminApiAdapter):
lookback_hours: int
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
from src.services.health.endpoint import EndpointHealthService
db = context.db
@@ -256,7 +260,7 @@ class AdminApiFormatHealthMonitorAdapter(AdminApiAdapter):
lookback_hours: int
per_format_limit: int
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
now = datetime.now(timezone.utc)
since = now - timedelta(hours=self.lookback_hours)
@@ -463,7 +467,7 @@ class AdminKeyHealthAdapter(AdminApiAdapter):
key_id: str
api_format: str | None = None
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
health_data = health_monitor.get_key_health(context.db, self.key_id, self.api_format)
if not health_data:
raise NotFoundException(f"Key {self.key_id} 不存在")
@@ -501,7 +505,7 @@ class AdminRecoverKeyHealthAdapter(AdminApiAdapter):
key_id: str
api_format: str | None = None
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
key = db.query(ProviderAPIKey).filter(ProviderAPIKey.id == self.key_id).first()
if not key:
@@ -544,7 +548,7 @@ class AdminRecoverKeyHealthAdapter(AdminApiAdapter):
class AdminRecoverAllKeysHealthAdapter(AdminApiAdapter):
"""批量恢复所有熔断 Key 的健康状态"""
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
# 查找所有有熔断格式的 Key检查 circuit_breaker_by_format JSON 字段)

View File

@@ -2,6 +2,9 @@
Provider API Keys 管理
"""
from __future__ import annotations
from typing import Any
import json
import uuid
from dataclasses import dataclass
@@ -25,6 +28,7 @@ from src.models.endpoint_models import (
EndpointAPIKeyUpdate,
)
from src.services.cache.provider_cache import ProviderCacheService
from src.api.base.context import ApiRequestContext
router = APIRouter(tags=["Provider Keys"])
pipeline = ApiRequestPipeline()
@@ -207,7 +211,7 @@ class AdminUpdateEndpointKeyAdapter(AdminApiAdapter):
key_id: str
key_data: EndpointAPIKeyUpdate
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
key = db.query(ProviderAPIKey).filter(ProviderAPIKey.id == self.key_id).first()
if not key:
@@ -378,7 +382,7 @@ class AdminRevealEndpointKeyAdapter(AdminApiAdapter):
key_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
key = db.query(ProviderAPIKey).filter(ProviderAPIKey.id == self.key_id).first()
if not key:
@@ -436,7 +440,7 @@ class AdminRevealEndpointKeyAdapter(AdminApiAdapter):
class AdminDeleteEndpointKeyAdapter(AdminApiAdapter):
key_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
key = db.query(ProviderAPIKey).filter(ProviderAPIKey.id == self.key_id).first()
if not key:
@@ -472,7 +476,7 @@ class AdminDeleteEndpointKeyAdapter(AdminApiAdapter):
class AdminGetKeysGroupedByFormatAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
# Key 属于 Provider按 key.api_formats 分组展示
@@ -677,7 +681,7 @@ class AdminListProviderKeysAdapter(AdminApiAdapter):
skip: int
limit: int
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:
@@ -702,7 +706,7 @@ class AdminCreateProviderKeyAdapter(AdminApiAdapter):
provider_id: str
key_data: EndpointAPIKeyCreate
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:

View File

@@ -2,6 +2,9 @@
ProviderEndpoint CRUD 管理 API
"""
from __future__ import annotations
from typing import Any
import uuid
from dataclasses import dataclass
from datetime import datetime, timezone
@@ -18,6 +21,7 @@ from src.core.exceptions import InvalidRequestException, NotFoundException
from src.core.logger import logger
from src.database import get_db
from src.models.database import Provider, ProviderAPIKey, ProviderEndpoint
from src.api.base.context import ApiRequestContext
from src.models.endpoint_models import (
ProviderEndpointCreate,
ProviderEndpointResponse,
@@ -215,7 +219,7 @@ class AdminListProviderEndpointsAdapter(AdminApiAdapter):
skip: int
limit: int
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:
@@ -270,7 +274,7 @@ class AdminCreateProviderEndpointAdapter(AdminApiAdapter):
provider_id: str
endpoint_data: ProviderEndpointCreate
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:
@@ -340,7 +344,7 @@ class AdminCreateProviderEndpointAdapter(AdminApiAdapter):
class AdminGetProviderEndpointAdapter(AdminApiAdapter):
endpoint_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
endpoint = (
db.query(ProviderEndpoint, Provider)
@@ -390,7 +394,7 @@ class AdminUpdateProviderEndpointAdapter(AdminApiAdapter):
endpoint_id: str
endpoint_data: ProviderEndpointUpdate
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
endpoint = (
db.query(ProviderEndpoint).filter(ProviderEndpoint.id == self.endpoint_id).first()
@@ -459,7 +463,7 @@ class AdminUpdateProviderEndpointAdapter(AdminApiAdapter):
class AdminDeleteProviderEndpointAdapter(AdminApiAdapter):
endpoint_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
endpoint = (
db.query(ProviderEndpoint).filter(ProviderEndpoint.id == self.endpoint_id).first()

View File

@@ -1,5 +1,7 @@
"""LDAP配置管理API端点。"""
from __future__ import annotations
import re
from typing import Any
@@ -15,6 +17,7 @@ from src.core.exceptions import InvalidRequestException, translate_pydantic_erro
from src.core.logger import logger
from src.database import get_db
from src.models.database import AuditEventType, LDAPConfig, User, UserRole
from src.api.base.context import ApiRequestContext
from src.services.system.audit import AuditService
router = APIRouter(prefix="/api/admin/ldap", tags=["Admin - LDAP"])
@@ -263,7 +266,7 @@ async def test_ldap_connection(request: Request, db: Session = Depends(get_db))
class AdminGetLDAPConfigAdapter(AdminApiAdapter):
async def handle(self, context) -> dict[str, Any]: # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> dict[str, Any]: # type: ignore[override]
db = context.db
config = db.query(LDAPConfig).first()
@@ -300,7 +303,7 @@ class AdminGetLDAPConfigAdapter(AdminApiAdapter):
class AdminUpdateLDAPConfigAdapter(AdminApiAdapter):
async def handle(self, context) -> dict[str, str]: # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> dict[str, str]: # type: ignore[override]
db = context.db
payload = context.ensure_json_body()
@@ -421,7 +424,7 @@ class AdminUpdateLDAPConfigAdapter(AdminApiAdapter):
class AdminTestLDAPConnectionAdapter(AdminApiAdapter):
async def handle(self, context) -> dict[str, Any]: # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> dict[str, Any]: # type: ignore[override]
from src.services.auth.ldap import LDAPService
db = context.db

View File

@@ -1,5 +1,8 @@
"""管理员 Management Token 管理端点"""
from __future__ import annotations
from typing import Any
from dataclasses import dataclass
from fastapi import APIRouter, Depends, HTTPException, Query, Request
@@ -50,7 +53,7 @@ async def list_all_management_tokens(
skip: int = Query(0, ge=0),
limit: int = Query(50, ge=1, le=100),
db: Session = Depends(get_db),
):
) -> Any:
"""列出所有 Management Tokens管理员
管理员查看所有用户的 Management Tokens支持筛选和分页。
@@ -90,7 +93,7 @@ async def get_management_token(
token_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""获取 Management Token 详情(管理员)
管理员查看任意 Management Token 的详细信息。
@@ -119,7 +122,7 @@ async def get_management_token(
@router.delete("/{token_id}")
async def delete_management_token(
token_id: str, request: Request, db: Session = Depends(get_db)
):
) -> Any:
"""删除任意 Management Token管理员
管理员可以删除任意用户的 Management Token。
@@ -137,7 +140,7 @@ async def delete_management_token(
@router.patch("/{token_id}/status")
async def toggle_management_token(
token_id: str, request: Request, db: Session = Depends(get_db)
):
) -> Any:
"""切换任意 Management Token 状态(管理员)
管理员可以启用/禁用任意用户的 Management Token。
@@ -178,7 +181,7 @@ class AdminListManagementTokensAdapter(AdminManagementTokenApiAdapter):
skip: int = 0
limit: int = 50
async def handle(self, context: ApiRequestContext):
async def handle(self, context: ApiRequestContext) -> Any:
# 构建查询
query = context.db.query(ManagementToken)
@@ -218,7 +221,7 @@ class AdminGetManagementTokenAdapter(AdminManagementTokenApiAdapter):
name: str = "admin_get_management_token"
token_id: str = ""
async def handle(self, context: ApiRequestContext):
async def handle(self, context: ApiRequestContext) -> Any:
token = ManagementTokenService.get_token_by_id(
db=context.db, token_id=self.token_id
)
@@ -240,7 +243,7 @@ class AdminDeleteManagementTokenAdapter(AdminManagementTokenApiAdapter):
token_id: str = ""
audit_success_event = AuditEventType.MANAGEMENT_TOKEN_DELETED
async def handle(self, context: ApiRequestContext):
async def handle(self, context: ApiRequestContext) -> Any:
# 先获取 token 信息用于审计
token = ManagementTokenService.get_token_by_id(
db=context.db, token_id=self.token_id
@@ -273,7 +276,7 @@ class AdminToggleManagementTokenAdapter(AdminManagementTokenApiAdapter):
token_id: str = ""
audit_success_event = AuditEventType.MANAGEMENT_TOKEN_UPDATED
async def handle(self, context: ApiRequestContext):
async def handle(self, context: ApiRequestContext) -> Any:
token = ManagementTokenService.toggle_status(
db=context.db, token_id=self.token_id
)

View File

@@ -4,6 +4,7 @@
基于 GlobalModel 的聚合视图
"""
from typing import Any
from dataclasses import dataclass
from fastapi import APIRouter, Depends, Request
@@ -13,6 +14,7 @@ from src.api.base.admin_adapter import AdminApiAdapter
from src.api.base.pipeline import ApiRequestPipeline
from src.database import get_db
from src.models.database import GlobalModel, Model
from src.api.base.context import ApiRequestContext
from src.models.pydantic_models import (
ModelCapabilities,
ModelCatalogItem,
@@ -59,7 +61,7 @@ class AdminGetModelCatalogAdapter(AdminApiAdapter):
2. Model 表提供关联提供商和价格
"""
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db: Session = context.db
# 1. 获取所有活跃的 GlobalModel

View File

@@ -4,9 +4,12 @@ GlobalModel Admin API
提供 GlobalModel 的 CRUD 操作接口
"""
from __future__ import annotations
from typing import Any
from dataclasses import dataclass
from fastapi import APIRouter, Depends, Query, Request
from fastapi import APIRouter, Depends, Query, Request, Response
from sqlalchemy.orm import Session
from src.api.base.admin_adapter import AdminApiAdapter
@@ -26,6 +29,7 @@ from src.models.pydantic_models import (
ModelCatalogProviderDetail,
)
from src.services.model.global_model import GlobalModelService
from src.api.base.context import ApiRequestContext
router = APIRouter(prefix="/global", tags=["Admin - Global Models"])
pipeline = ApiRequestPipeline()
@@ -154,12 +158,12 @@ async def update_global_model(
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@router.delete("/{global_model_id}", status_code=204)
@router.delete("/{global_model_id}", status_code=204, response_class=Response)
async def delete_global_model(
request: Request,
global_model_id: str,
db: Session = Depends(get_db),
):
) -> Response:
"""
删除 GlobalModel
@@ -174,7 +178,7 @@ async def delete_global_model(
"""
adapter = AdminDeleteGlobalModelAdapter(global_model_id=global_model_id)
await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
return None
return Response(status_code=204)
@router.post(
@@ -256,7 +260,7 @@ class AdminListGlobalModelsAdapter(AdminApiAdapter):
is_active: bool | None
search: str | None
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
from sqlalchemy import func
from src.models.database import Model
@@ -302,7 +306,7 @@ class AdminGetGlobalModelAdapter(AdminApiAdapter):
global_model_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
global_model = GlobalModelService.get_global_model(context.db, self.global_model_id)
stats = GlobalModelService.get_global_model_stats(context.db, self.global_model_id)
@@ -320,7 +324,7 @@ class AdminCreateGlobalModelAdapter(AdminApiAdapter):
payload: GlobalModelCreate
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
from src.core.exceptions import InvalidRequestException
from src.core.model_permissions import validate_and_extract_model_mappings
@@ -359,7 +363,7 @@ class AdminUpdateGlobalModelAdapter(AdminApiAdapter):
global_model_id: str
payload: GlobalModelUpdate
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
from src.core.exceptions import InvalidRequestException
from src.core.model_permissions import validate_and_extract_model_mappings
@@ -418,7 +422,7 @@ class AdminDeleteGlobalModelAdapter(AdminApiAdapter):
global_model_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
# 使用行级锁获取 GlobalModel 信息,防止并发操作导致的竞态条件
# 设置 2 秒锁超时,允许短暂等待而非立即失败
from sqlalchemy import text
@@ -467,7 +471,7 @@ class AdminBatchAssignToProvidersAdapter(AdminApiAdapter):
global_model_id: str
payload: BatchAssignToProvidersRequest
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
result = GlobalModelService.batch_assign_to_providers(
db=context.db,
global_model_id=self.global_model_id,
@@ -492,7 +496,7 @@ class AdminGetGlobalModelProvidersAdapter(AdminApiAdapter):
global_model_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
from sqlalchemy.orm import joinedload
from src.models.database import Model

View File

@@ -8,6 +8,8 @@ GlobalModel 请求链路预览 API
- Key 的并发配置和健康状态
"""
from __future__ import annotations
from dataclasses import dataclass
from fastapi import APIRouter, Depends, Request
@@ -30,6 +32,7 @@ from src.models.database import (
ProviderEndpoint,
)
from src.services.cache.aware_scheduler import CacheAwareScheduler
from src.api.base.context import ApiRequestContext
from src.services.system.config import SystemConfigService
router = APIRouter(prefix="/global", tags=["Admin - Global Models"])
@@ -204,7 +207,7 @@ class AdminGetModelRoutingPreviewAdapter(AdminApiAdapter):
global_model_id: str
async def handle(self, context) -> ModelRoutingPreviewResponse: # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> ModelRoutingPreviewResponse: # type: ignore[override]
db = context.db
# 获取 GlobalModel

View File

@@ -12,6 +12,7 @@ from src.api.base.admin_adapter import AdminApiAdapter
from src.api.base.pipeline import ApiRequestPipeline
from src.core.exceptions import InvalidRequestException, NotFoundException
from src.core.modules import ModuleStatus, get_module_registry
from src.api.base.context import ApiRequestContext
from src.database import get_db
router = APIRouter(prefix="/api/admin/modules", tags=["Admin - Modules"])
@@ -69,7 +70,7 @@ class SetModuleEnabledRequest(BaseModel):
@router.get("/status")
async def get_all_modules_status(request: Request, db: Session = Depends(get_db)):
async def get_all_modules_status(request: Request, db: Session = Depends(get_db)) -> Any:
"""
获取所有模块状态
@@ -86,7 +87,7 @@ async def get_all_modules_status(request: Request, db: Session = Depends(get_db)
@router.get("/status/{module_name}")
async def get_module_status(
module_name: str, request: Request, db: Session = Depends(get_db)
):
) -> Any:
"""
获取单个模块状态
@@ -105,7 +106,7 @@ async def get_module_status(
@router.put("/status/{module_name}/enabled")
async def set_module_enabled(
module_name: str, request: Request, db: Session = Depends(get_db)
):
) -> Any:
"""
设置模块启用状态
@@ -131,7 +132,7 @@ async def set_module_enabled(
class AdminGetAllModulesStatusAdapter(AdminApiAdapter):
"""获取所有模块状态"""
async def handle(self, context) -> dict[str, Any]:
async def handle(self, context: ApiRequestContext) -> dict[str, Any]:
registry = get_module_registry()
all_status = await registry.get_all_status_async(context.db)
@@ -147,7 +148,7 @@ class AdminGetModuleStatusAdapter(AdminApiAdapter):
module_name: str
async def handle(self, context) -> dict[str, Any]:
async def handle(self, context: ApiRequestContext) -> dict[str, Any]:
registry = get_module_registry()
status = await registry.get_module_status_async(self.module_name, context.db)
@@ -163,7 +164,7 @@ class AdminSetModuleEnabledAdapter(AdminApiAdapter):
module_name: str
async def handle(self, context) -> dict[str, Any]:
async def handle(self, context: ApiRequestContext) -> dict[str, Any]:
registry = get_module_registry()
# 检查模块是否存在

View File

@@ -1,5 +1,8 @@
"""管理员监控与审计端点。"""
from __future__ import annotations
from typing import Any
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
@@ -23,6 +26,7 @@ from src.models.database import User as DBUser
from src.services.health.monitor import HealthMonitor
from src.services.system.audit import audit_service
from src.utils.database_helpers import escape_like_pattern
from src.api.base.context import ApiRequestContext
router = APIRouter(prefix="/api/admin/monitoring", tags=["Admin - Monitoring"])
@@ -38,7 +42,7 @@ async def get_audit_logs(
limit: int = Query(100, description="返回数量限制"),
offset: int = Query(0, description="偏移量"),
db: Session = Depends(get_db),
):
) -> Any:
"""
获取审计日志
@@ -78,7 +82,7 @@ async def get_audit_logs(
@router.get("/system-status")
async def get_system_status(request: Request, db: Session = Depends(get_db)):
async def get_system_status(request: Request, db: Session = Depends(get_db)) -> Any:
"""
获取系统状态
@@ -101,7 +105,7 @@ async def get_suspicious_activities(
request: Request,
hours: int = Query(24, description="时间范围(小时)"),
db: Session = Depends(get_db),
):
) -> Any:
"""
获取可疑活动记录
@@ -132,7 +136,7 @@ async def analyze_user_behavior(
request: Request,
days: int = Query(30, description="分析天数"),
db: Session = Depends(get_db),
):
) -> Any:
"""
分析用户行为
@@ -152,7 +156,7 @@ async def analyze_user_behavior(
@router.get("/resilience-status")
async def get_resilience_status(request: Request, db: Session = Depends(get_db)):
async def get_resilience_status(request: Request, db: Session = Depends(get_db)) -> Any:
"""
获取韧性系统状态
@@ -171,7 +175,7 @@ async def get_resilience_status(request: Request, db: Session = Depends(get_db))
@router.delete("/resilience/error-stats")
async def reset_error_stats(request: Request, db: Session = Depends(get_db)):
async def reset_error_stats(request: Request, db: Session = Depends(get_db)) -> None:
"""
重置错误统计
@@ -192,7 +196,7 @@ async def get_circuit_history(
request: Request,
limit: int = Query(50, ge=1, le=200),
db: Session = Depends(get_db),
):
) -> Any:
"""
获取熔断器历史记录
@@ -220,7 +224,7 @@ class AdminGetAuditLogsAdapter(AdminApiAdapter):
# 查看审计日志本身不应该产生审计记录,避免刷新页面时产生大量无意义的日志
audit_log_enabled: bool = False
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
cutoff_time = datetime.now(timezone.utc) - timedelta(days=self.days)
@@ -284,7 +288,7 @@ class AdminGetAuditLogsAdapter(AdminApiAdapter):
class AdminSystemStatusAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
total_users = db.query(func.count(DBUser.id)).scalar()
@@ -361,7 +365,7 @@ class AdminSystemStatusAdapter(AdminApiAdapter):
class AdminSuspiciousActivitiesAdapter(AdminApiAdapter):
hours: int
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
activities = audit_service.get_suspicious_activities(db=db, hours=self.hours, limit=100)
response = {
@@ -393,7 +397,7 @@ class AdminUserBehaviorAdapter(AdminApiAdapter):
user_id: str
days: int
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
result = audit_service.analyze_user_behavior(
db=context.db,
user_id=self.user_id,
@@ -409,7 +413,7 @@ class AdminUserBehaviorAdapter(AdminApiAdapter):
class AdminResilienceStatusAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
try:
from src.core.resilience import resilience_manager
except ImportError as exc:
@@ -454,7 +458,7 @@ class AdminResilienceStatusAdapter(AdminApiAdapter):
class AdminResetErrorStatsAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
try:
from src.core.resilience import resilience_manager
except ImportError as exc:
@@ -486,7 +490,7 @@ class AdminCircuitHistoryAdapter(AdminApiAdapter):
super().__init__()
self.limit = limit
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
history = HealthMonitor.get_circuit_history(self.limit)
context.add_audit_metadata(
action="circuit_history",

View File

@@ -4,6 +4,8 @@
提供缓存亲和性统计、管理和监控功能
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any

View File

@@ -2,6 +2,9 @@
请求链路追踪 API 端点
"""
from __future__ import annotations
from typing import Any
from dataclasses import dataclass
from datetime import datetime
@@ -15,6 +18,7 @@ from src.database import get_db
from src.models.database import Provider, ProviderEndpoint, ProviderAPIKey
from src.core.crypto import crypto_service
from src.services.request.candidate import RequestCandidateService
from src.api.base.context import ApiRequestContext
router = APIRouter(prefix="/api/admin/monitoring/trace", tags=["Admin - Monitoring: Trace"])
pipeline = ApiRequestPipeline()
@@ -69,7 +73,7 @@ async def get_request_trace(
request_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
获取请求的完整追踪信息
@@ -122,7 +126,7 @@ async def get_provider_failure_rate(
request: Request,
limit: int = Query(100, ge=1, le=1000, description="统计最近的尝试数量"),
db: Session = Depends(get_db),
):
) -> Any:
"""
获取提供商的失败率统计
@@ -153,7 +157,7 @@ async def get_provider_failure_rate(
class AdminGetRequestTraceAdapter(AdminApiAdapter):
request_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
# 只查询 candidates
@@ -326,7 +330,7 @@ class AdminProviderFailureRateAdapter(AdminApiAdapter):
provider_id: str
limit: int
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
result = RequestCandidateService.get_candidate_stats_by_provider(
db=context.db,
provider_id=self.provider_id,

View File

@@ -8,6 +8,8 @@ Provider 操作 API 路由
- 配置管理
"""
from __future__ import annotations
from dataclasses import asdict, is_dataclass
from typing import Any
@@ -146,14 +148,14 @@ def _serialize_data(data: Any) -> Any:
@router.get("/architectures", response_model=list[ArchitectureInfo])
async def list_architectures(_: User = Depends(require_admin)):
async def list_architectures(_: User = Depends(require_admin)) -> Any:
"""获取所有可用的架构"""
registry = get_registry()
return registry.to_dict_list()
@router.get("/architectures/{architecture_id}", response_model=ArchitectureInfo)
async def get_architecture(architecture_id: str, _: User = Depends(require_admin)):
async def get_architecture(architecture_id: str, _: User = Depends(require_admin)) -> Any:
"""获取指定架构的详情"""
registry = get_registry()
arch = registry.get(architecture_id)
@@ -167,7 +169,7 @@ async def get_provider_ops_status(
provider_id: str,
db: Session = Depends(get_db),
_: User = Depends(require_admin),
):
) -> Any:
"""获取 Provider 的操作状态"""
service = ProviderOpsService(db)
@@ -200,7 +202,7 @@ async def get_provider_ops_config(
provider_id: str,
db: Session = Depends(get_db),
_: User = Depends(require_admin),
):
) -> Any:
"""
获取 Provider 的操作配置(脱敏)
@@ -251,7 +253,7 @@ async def save_provider_ops_config(
request: SaveConfigRequest,
db: Session = Depends(get_db),
_: User = Depends(require_admin),
):
) -> Any:
"""保存 Provider 的操作配置"""
service = ProviderOpsService(db)
@@ -287,7 +289,7 @@ async def verify_provider_auth(
request: SaveConfigRequest,
db: Session = Depends(get_db),
_: User = Depends(require_admin),
):
) -> Any:
"""
验证 Provider 认证配置
@@ -343,7 +345,7 @@ async def delete_provider_ops_config(
provider_id: str,
db: Session = Depends(get_db),
_: User = Depends(require_admin),
):
) -> Any:
"""删除 Provider 的操作配置"""
service = ProviderOpsService(db)
success = service.delete_config(provider_id)
@@ -360,7 +362,7 @@ async def connect_provider(
request: ConnectRequest,
db: Session = Depends(get_db),
_: User = Depends(require_admin),
):
) -> Any:
"""建立与 Provider 的连接"""
service = ProviderOpsService(db)
@@ -377,7 +379,7 @@ async def disconnect_provider(
provider_id: str,
db: Session = Depends(get_db),
_: User = Depends(require_admin),
):
) -> Any:
"""断开与 Provider 的连接"""
service = ProviderOpsService(db)
await service.disconnect(provider_id)
@@ -395,7 +397,7 @@ async def execute_action(
request: ExecuteActionRequest,
db: Session = Depends(get_db),
_: User = Depends(require_admin),
):
) -> Any:
"""执行指定操作"""
service = ProviderOpsService(db)
@@ -423,7 +425,7 @@ async def get_balance(
refresh: bool = True,
db: Session = Depends(get_db),
_: User = Depends(require_admin),
):
) -> Any:
"""
获取余额(优先返回缓存,后台异步刷新)
@@ -449,7 +451,7 @@ async def refresh_balance(
provider_id: str,
db: Session = Depends(get_db),
_: User = Depends(require_admin),
):
) -> Any:
"""立即刷新余额(同步等待结果)"""
service = ProviderOpsService(db)
result = await service.query_balance(provider_id)
@@ -470,7 +472,7 @@ async def checkin(
provider_id: str,
db: Session = Depends(get_db),
_: User = Depends(require_admin),
):
) -> Any:
"""签到(快捷方法)"""
service = ProviderOpsService(db)
result = await service.checkin(provider_id)
@@ -491,7 +493,7 @@ async def batch_query_balance(
provider_ids: list[str] | None = None,
db: Session = Depends(get_db),
_: User = Depends(require_admin),
):
) -> Any:
"""批量查询余额"""
service = ProviderOpsService(db)
results = await service.batch_query_balance(provider_ids)

View File

@@ -3,6 +3,9 @@ Provider Query API 端点
用于查询提供商的模型列表等信息
"""
from __future__ import annotations
from typing import Any
import asyncio
import httpx
@@ -63,7 +66,7 @@ async def query_available_models(
request: ModelsQueryRequest,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
) -> Any:
"""
查询提供商可用模型
@@ -118,7 +121,7 @@ async def query_available_models(
raise HTTPException(status_code=400, detail="No active API Key found for this provider")
# 并发获取所有 Key 的模型
async def fetch_for_key(api_key):
async def fetch_for_key(api_key: Any) -> Any:
# 非强制刷新时,先检查缓存
if not request.force_refresh:
cached_models = await get_upstream_models_from_cache(
@@ -246,7 +249,7 @@ async def _fetch_models_for_single_key(
api_key_id: str,
format_to_endpoint: dict[str, ProviderEndpoint],
force_refresh: bool,
):
) -> Any:
"""获取单个 Key 的模型列表"""
# 查找指定的 Key
api_key = next(
@@ -305,7 +308,7 @@ async def test_model(
request: TestModelRequest,
db: Session = Depends(get_db),
current_user: User = Depends(get_current_user),
):
) -> Any:
"""
测试模型连接性

View File

@@ -2,6 +2,9 @@
提供商策略管理 API 端点
"""
from __future__ import annotations
from typing import Any
from datetime import datetime, timedelta, timezone
from fastapi import APIRouter, Depends, HTTPException, Request
@@ -16,6 +19,7 @@ from src.core.exceptions import InvalidRequestException, translate_pydantic_erro
from src.core.logger import logger
from src.database import get_db
from src.models.database import Provider
from src.api.base.context import ApiRequestContext
from src.models.database_extensions import ProviderUsageTracking
router = APIRouter(prefix="/api/admin/provider-strategy", tags=["Provider Strategy"])
@@ -37,7 +41,7 @@ async def update_provider_billing(
provider_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
更新提供商计费配置
@@ -73,7 +77,7 @@ async def get_provider_stats(
request: Request,
hours: int = 24,
db: Session = Depends(get_db),
):
) -> Any:
"""
获取提供商统计数据
@@ -112,14 +116,14 @@ async def reset_provider_quota(
provider_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""Reset provider quota usage to zero"""
adapter = AdminProviderResetQuotaAdapter(provider_id=provider_id)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@router.get("/strategies")
async def list_available_strategies(request: Request, db: Session = Depends(get_db)):
async def list_available_strategies(request: Request, db: Session = Depends(get_db)) -> Any:
"""
获取可用负载均衡策略列表
@@ -142,7 +146,7 @@ class AdminProviderBillingAdapter(AdminApiAdapter):
def __init__(self, provider_id: str):
self.provider_id = provider_id
async def handle(self, context):
async def handle(self, context: ApiRequestContext) -> Any:
db = context.db
payload = context.ensure_json_body()
try:
@@ -215,7 +219,7 @@ class AdminProviderStatsAdapter(AdminApiAdapter):
self.provider_id = provider_id
self.hours = hours
async def handle(self, context):
async def handle(self, context: ApiRequestContext) -> Any:
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:
@@ -271,7 +275,7 @@ class AdminProviderResetQuotaAdapter(AdminApiAdapter):
def __init__(self, provider_id: str):
self.provider_id = provider_id
async def handle(self, context):
async def handle(self, context: ApiRequestContext) -> Any:
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:
@@ -297,7 +301,7 @@ class AdminProviderResetQuotaAdapter(AdminApiAdapter):
class AdminListStrategiesAdapter(AdminApiAdapter):
async def handle(self, context):
async def handle(self, context: ApiRequestContext) -> Any:
from src.plugins.manager import get_plugin_manager
plugin_manager = get_plugin_manager()

View File

@@ -2,6 +2,8 @@
Provider 模型管理 API
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
@@ -35,6 +37,7 @@ from src.models.database import (
Provider,
)
from src.services.model.service import ModelService
from src.api.base.context import ApiRequestContext
router = APIRouter(tags=["Model Management"])
pipeline = ApiRequestPipeline()
@@ -205,7 +208,7 @@ async def delete_provider_model(
model_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
删除模型
@@ -264,7 +267,7 @@ async def get_provider_available_source_models(
provider_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
获取提供商支持的可用源模型
@@ -379,7 +382,7 @@ class AdminListProviderModelsAdapter(AdminApiAdapter):
skip: int
limit: int
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:
@@ -396,7 +399,7 @@ class AdminCreateProviderModelAdapter(AdminApiAdapter):
provider_id: str
model_data: ModelCreate
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:
@@ -416,7 +419,7 @@ class AdminGetProviderModelAdapter(AdminApiAdapter):
provider_id: str
model_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
model = (
db.query(Model)
@@ -435,7 +438,7 @@ class AdminUpdateProviderModelAdapter(AdminApiAdapter):
model_id: str
model_data: ModelUpdate
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
model = (
db.query(Model)
@@ -459,7 +462,7 @@ class AdminDeleteProviderModelAdapter(AdminApiAdapter):
provider_id: str
model_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
model = (
db.query(Model)
@@ -484,7 +487,7 @@ class AdminBatchCreateModelsAdapter(AdminApiAdapter):
provider_id: str
models_data: list[ModelCreate]
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:
@@ -503,7 +506,7 @@ class AdminBatchCreateModelsAdapter(AdminApiAdapter):
class AdminGetProviderAvailableSourceModelsAdapter(AdminApiAdapter):
provider_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
"""
返回 Provider 支持的所有 GlobalModel
@@ -571,7 +574,7 @@ class AdminBatchAssignModelsToProviderAdapter(AdminApiAdapter):
provider_id: str
payload: BatchAssignModelsToProviderRequest
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:
@@ -654,7 +657,7 @@ class AdminImportFromUpstreamAdapter(AdminApiAdapter):
provider_id: str
payload: ImportFromUpstreamRequest
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:

View File

@@ -1,5 +1,8 @@
"""管理员 Provider 管理路由。"""
from __future__ import annotations
from typing import Any
import asyncio
from datetime import datetime, timezone
@@ -19,6 +22,7 @@ from src.database import get_db
from src.models.admin_requests import CreateProviderRequest, UpdateProviderRequest
from src.models.database import GlobalModel, Provider, ProviderAPIKey
from src.services.cache.provider_cache import ProviderCacheService
from src.api.base.context import ApiRequestContext
router = APIRouter(tags=["Provider CRUD"])
pipeline = ApiRequestPipeline()
@@ -96,7 +100,7 @@ async def list_providers(
limit: int = Query(100, ge=1, le=500),
is_active: bool | None = None,
db: Session = Depends(get_db),
):
) -> Any:
"""
获取提供商列表
@@ -123,7 +127,7 @@ async def list_providers(
@router.post("/")
async def create_provider(request: Request, db: Session = Depends(get_db)):
async def create_provider(request: Request, db: Session = Depends(get_db)) -> Any:
"""
创建新提供商
@@ -155,7 +159,7 @@ async def create_provider(request: Request, db: Session = Depends(get_db)):
@router.put("/{provider_id}")
async def update_provider(provider_id: str, request: Request, db: Session = Depends(get_db)):
async def update_provider(provider_id: str, request: Request, db: Session = Depends(get_db)) -> None:
"""
更新提供商配置
@@ -191,7 +195,7 @@ async def update_provider(provider_id: str, request: Request, db: Session = Depe
@router.delete("/{provider_id}")
async def delete_provider(provider_id: str, request: Request, db: Session = Depends(get_db)):
async def delete_provider(provider_id: str, request: Request, db: Session = Depends(get_db)) -> None:
"""
删除提供商
@@ -213,7 +217,7 @@ class AdminListProvidersAdapter(AdminApiAdapter):
self.limit = limit
self.is_active = is_active
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
query = db.query(Provider)
if self.is_active is not None:
@@ -251,7 +255,7 @@ class AdminListProvidersAdapter(AdminApiAdapter):
class AdminCreateProviderAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
payload = context.ensure_json_body()
@@ -333,7 +337,7 @@ class AdminUpdateProviderAdapter(AdminApiAdapter):
def __init__(self, provider_id: str):
self.provider_id = provider_id
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
payload = context.ensure_json_body()
@@ -412,7 +416,7 @@ class AdminDeleteProviderAdapter(AdminApiAdapter):
def __init__(self, provider_id: str):
self.provider_id = provider_id
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:
@@ -483,7 +487,7 @@ class AdminGetProviderMappingPreviewAdapter(AdminApiAdapter):
def __init__(self, provider_id: str):
self.provider_id = provider_id
async def handle(self, context) -> ProviderMappingPreviewResponse: # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> ProviderMappingPreviewResponse: # type: ignore[override]
db = context.db
# 获取 Provider

View File

@@ -2,6 +2,7 @@
Provider 摘要与健康监控 API
"""
from typing import Any
from dataclasses import dataclass
from datetime import datetime, timedelta, timezone
@@ -22,6 +23,7 @@ from src.models.database import (
ProviderEndpoint,
RequestCandidate,
)
from src.api.base.context import ApiRequestContext
from src.models.endpoint_models import (
EndpointHealthEvent,
EndpointHealthMonitor,
@@ -338,7 +340,7 @@ class AdminProviderHealthMonitorAdapter(AdminApiAdapter):
lookback_hours: int
per_endpoint_limit: int
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:
@@ -447,7 +449,7 @@ class AdminProviderHealthMonitorAdapter(AdminApiAdapter):
class AdminProviderSummaryAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
providers = (
db.query(Provider)
@@ -462,7 +464,7 @@ class AdminUpdateProviderSettingsAdapter(AdminApiAdapter):
provider_id: str
update_data: ProviderUpdateRequest
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
provider = db.query(Provider).filter(Provider.id == self.provider_id).first()
if not provider:

View File

@@ -4,6 +4,9 @@ IP 安全管理接口
提供 IP 黑白名单管理和速率限制统计
"""
from __future__ import annotations
from typing import Any
from fastapi import APIRouter, Depends, HTTPException, Request, status
from pydantic import BaseModel, Field, ValidationError
from sqlalchemy.orm import Session
@@ -14,6 +17,7 @@ from src.api.base.pipeline import ApiRequestPipeline
from src.core.exceptions import InvalidRequestException, translate_pydantic_error
from src.database import get_db
from src.services.rate_limit.ip_limiter import IPRateLimiter
from src.api.base.context import ApiRequestContext
router = APIRouter(prefix="/api/admin/security/ip", tags=["Admin - Security"])
pipeline = ApiRequestPipeline()
@@ -52,7 +56,7 @@ class RemoveIPFromWhitelistRequest(BaseModel):
@router.post("/blacklist")
async def add_to_blacklist(request: Request, db: Session = Depends(get_db)):
async def add_to_blacklist(request: Request, db: Session = Depends(get_db)) -> None:
"""
添加 IP 到黑名单
@@ -74,7 +78,7 @@ async def add_to_blacklist(request: Request, db: Session = Depends(get_db)):
@router.delete("/blacklist/{ip_address}")
async def remove_from_blacklist(ip_address: str, request: Request, db: Session = Depends(get_db)):
async def remove_from_blacklist(ip_address: str, request: Request, db: Session = Depends(get_db)) -> None:
"""
从黑名单移除 IP
@@ -92,7 +96,7 @@ async def remove_from_blacklist(ip_address: str, request: Request, db: Session =
@router.get("/blacklist/stats")
async def get_blacklist_stats(request: Request, db: Session = Depends(get_db)):
async def get_blacklist_stats(request: Request, db: Session = Depends(get_db)) -> Any:
"""
获取黑名单统计信息
@@ -111,7 +115,7 @@ async def get_blacklist_stats(request: Request, db: Session = Depends(get_db)):
@router.post("/whitelist")
async def add_to_whitelist(request: Request, db: Session = Depends(get_db)):
async def add_to_whitelist(request: Request, db: Session = Depends(get_db)) -> None:
"""
添加 IP 到白名单
@@ -129,7 +133,7 @@ async def add_to_whitelist(request: Request, db: Session = Depends(get_db)):
@router.delete("/whitelist/{ip_address}")
async def remove_from_whitelist(ip_address: str, request: Request, db: Session = Depends(get_db)):
async def remove_from_whitelist(ip_address: str, request: Request, db: Session = Depends(get_db)) -> None:
"""
从白名单移除 IP
@@ -147,7 +151,7 @@ async def remove_from_whitelist(ip_address: str, request: Request, db: Session =
@router.get("/whitelist")
async def get_whitelist(request: Request, db: Session = Depends(get_db)):
async def get_whitelist(request: Request, db: Session = Depends(get_db)) -> Any:
"""
获取白名单
@@ -167,7 +171,7 @@ async def get_whitelist(request: Request, db: Session = Depends(get_db)):
class AddToBlacklistAdapter(AuthenticatedApiAdapter):
"""添加 IP 到黑名单适配器"""
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
payload = context.ensure_json_body()
try:
req = AddIPToBlacklistRequest.model_validate(payload)
@@ -199,7 +203,7 @@ class RemoveFromBlacklistAdapter(AuthenticatedApiAdapter):
def __init__(self, ip_address: str):
self.ip_address = ip_address
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
success = await IPRateLimiter.remove_from_blacklist(self.ip_address)
if not success:
@@ -213,7 +217,7 @@ class RemoveFromBlacklistAdapter(AuthenticatedApiAdapter):
class GetBlacklistStatsAdapter(AuthenticatedApiAdapter):
"""获取黑名单统计适配器"""
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
stats = await IPRateLimiter.get_blacklist_stats()
return stats
@@ -221,7 +225,7 @@ class GetBlacklistStatsAdapter(AuthenticatedApiAdapter):
class AddToWhitelistAdapter(AuthenticatedApiAdapter):
"""添加 IP 到白名单适配器"""
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
payload = context.ensure_json_body()
try:
req = AddIPToWhitelistRequest.model_validate(payload)
@@ -248,7 +252,7 @@ class RemoveFromWhitelistAdapter(AuthenticatedApiAdapter):
def __init__(self, ip_address: str):
self.ip_address = ip_address
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
success = await IPRateLimiter.remove_from_whitelist(self.ip_address)
if not success:
@@ -262,6 +266,6 @@ class RemoveFromWhitelistAdapter(AuthenticatedApiAdapter):
class GetWhitelistAdapter(AuthenticatedApiAdapter):
"""获取白名单适配器"""
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
whitelist = await IPRateLimiter.get_whitelist()
return {"whitelist": list(whitelist), "total": len(whitelist)}

View File

@@ -1,6 +1,9 @@
"""系统设置API端点。"""
from __future__ import annotations
from typing import Any
import copy
from dataclasses import dataclass
@@ -17,6 +20,7 @@ from src.models.api import SystemSettingsRequest, SystemSettingsResponse
from src.models.database import ApiKey, Provider, Usage, User
from src.services.email.email_template import EmailTemplate
from src.services.system.config import SystemConfigService
from src.api.base.context import ApiRequestContext
router = APIRouter(prefix="/api/admin/system", tags=["Admin - System"])
@@ -79,7 +83,7 @@ def _parse_version(version_str: str) -> tuple:
@router.get("/version")
async def get_system_version():
async def get_system_version() -> Any:
"""
获取系统版本信息
@@ -92,7 +96,7 @@ async def get_system_version():
@router.get("/check-update")
async def check_update():
async def check_update() -> Any:
"""
检查系统更新
@@ -115,7 +119,7 @@ async def check_update():
github_repo = "Aethersailor/Aether"
github_tags_url = f"https://api.github.com/repos/{github_repo}/tags"
def _make_empty_response(error: str | None = None):
def _make_empty_response(error: str | None = None) -> None:
return {
"current_version": current_version,
"latest_version": None,
@@ -238,7 +242,7 @@ pipeline = ApiRequestPipeline()
@router.get("/settings")
async def get_system_settings(request: Request, db: Session = Depends(get_db)):
async def get_system_settings(request: Request, db: Session = Depends(get_db)) -> Any:
"""
获取系统设置
@@ -255,7 +259,7 @@ async def get_system_settings(request: Request, db: Session = Depends(get_db)):
@router.put("/settings")
async def update_system_settings(http_request: Request, db: Session = Depends(get_db)):
async def update_system_settings(http_request: Request, db: Session = Depends(get_db)) -> None:
"""
更新系统设置
@@ -275,7 +279,7 @@ async def update_system_settings(http_request: Request, db: Session = Depends(ge
@router.get("/configs")
async def get_all_system_configs(request: Request, db: Session = Depends(get_db)):
async def get_all_system_configs(request: Request, db: Session = Depends(get_db)) -> Any:
"""
获取所有系统配置
@@ -290,7 +294,7 @@ async def get_all_system_configs(request: Request, db: Session = Depends(get_db)
@router.get("/configs/{key}")
async def get_system_config(key: str, request: Request, db: Session = Depends(get_db)):
async def get_system_config(key: str, request: Request, db: Session = Depends(get_db)) -> Any:
"""
获取特定系统配置
@@ -314,7 +318,7 @@ async def set_system_config(
key: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
设置系统配置
@@ -339,7 +343,7 @@ async def set_system_config(
@router.delete("/configs/{key}")
async def delete_system_config(key: str, request: Request, db: Session = Depends(get_db)):
async def delete_system_config(key: str, request: Request, db: Session = Depends(get_db)) -> None:
"""
删除系统配置
@@ -357,7 +361,7 @@ async def delete_system_config(key: str, request: Request, db: Session = Depends
@router.get("/stats")
async def get_system_stats(request: Request, db: Session = Depends(get_db)):
async def get_system_stats(request: Request, db: Session = Depends(get_db)) -> Any:
"""
获取系统统计信息
@@ -374,7 +378,7 @@ async def get_system_stats(request: Request, db: Session = Depends(get_db)):
@router.post("/cleanup")
async def trigger_cleanup(request: Request, db: Session = Depends(get_db)):
async def trigger_cleanup(request: Request, db: Session = Depends(get_db)) -> Any:
"""
手动触发清理任务
@@ -393,7 +397,7 @@ async def trigger_cleanup(request: Request, db: Session = Depends(get_db)):
@router.get("/api-formats")
async def get_api_formats(request: Request, db: Session = Depends(get_db)):
async def get_api_formats(request: Request, db: Session = Depends(get_db)) -> Any:
"""
获取所有可用的 API 格式列表
@@ -411,35 +415,35 @@ async def get_api_formats(request: Request, db: Session = Depends(get_db)):
@router.get("/config/export")
async def export_config(request: Request, db: Session = Depends(get_db)):
async def export_config(request: Request, db: Session = Depends(get_db)) -> Any:
"""导出提供商和模型配置(管理员)"""
adapter = AdminExportConfigAdapter()
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@router.post("/config/import")
async def import_config(request: Request, db: Session = Depends(get_db)):
async def import_config(request: Request, db: Session = Depends(get_db)) -> Any:
"""导入提供商和模型配置(管理员)"""
adapter = AdminImportConfigAdapter()
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@router.get("/users/export")
async def export_users(request: Request, db: Session = Depends(get_db)):
async def export_users(request: Request, db: Session = Depends(get_db)) -> Any:
"""导出用户数据(管理员)"""
adapter = AdminExportUsersAdapter()
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@router.post("/users/import")
async def import_users(request: Request, db: Session = Depends(get_db)):
async def import_users(request: Request, db: Session = Depends(get_db)) -> Any:
"""导入用户数据(管理员)"""
adapter = AdminImportUsersAdapter()
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@router.post("/smtp/test")
async def test_smtp(request: Request, db: Session = Depends(get_db)):
async def test_smtp(request: Request, db: Session = Depends(get_db)) -> Any:
"""测试 SMTP 连接(管理员)"""
adapter = AdminTestSmtpAdapter()
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@@ -449,7 +453,7 @@ async def test_smtp(request: Request, db: Session = Depends(get_db)):
@router.get("/email/templates")
async def get_email_templates(request: Request, db: Session = Depends(get_db)):
async def get_email_templates(request: Request, db: Session = Depends(get_db)) -> Any:
"""获取所有邮件模板(管理员)"""
adapter = AdminGetEmailTemplatesAdapter()
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@@ -458,7 +462,7 @@ async def get_email_templates(request: Request, db: Session = Depends(get_db)):
@router.get("/email/templates/{template_type}")
async def get_email_template(
template_type: str, request: Request, db: Session = Depends(get_db)
):
) -> Any:
"""获取指定类型的邮件模板(管理员)"""
adapter = AdminGetEmailTemplateAdapter(template_type=template_type)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@@ -467,7 +471,7 @@ async def get_email_template(
@router.put("/email/templates/{template_type}")
async def update_email_template(
template_type: str, request: Request, db: Session = Depends(get_db)
):
) -> Any:
"""更新邮件模板(管理员)"""
adapter = AdminUpdateEmailTemplateAdapter(template_type=template_type)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@@ -476,7 +480,7 @@ async def update_email_template(
@router.post("/email/templates/{template_type}/preview")
async def preview_email_template(
template_type: str, request: Request, db: Session = Depends(get_db)
):
) -> Any:
"""预览邮件模板(管理员)"""
adapter = AdminPreviewEmailTemplateAdapter(template_type=template_type)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@@ -485,7 +489,7 @@ async def preview_email_template(
@router.post("/email/templates/{template_type}/reset")
async def reset_email_template(
template_type: str, request: Request, db: Session = Depends(get_db)
):
) -> Any:
"""重置邮件模板为默认值(管理员)"""
adapter = AdminResetEmailTemplateAdapter(template_type=template_type)
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
@@ -495,7 +499,7 @@ async def reset_email_template(
class AdminGetSystemSettingsAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
default_provider = SystemConfigService.get_default_provider(db)
default_model = SystemConfigService.get_config(db, "default_model")
@@ -511,7 +515,7 @@ class AdminGetSystemSettingsAdapter(AdminApiAdapter):
class AdminUpdateSystemSettingsAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
payload = context.ensure_json_body()
try:
@@ -560,7 +564,7 @@ class AdminUpdateSystemSettingsAdapter(AdminApiAdapter):
class AdminGetAllConfigsAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
return SystemConfigService.get_all_configs(context.db)
@@ -571,7 +575,7 @@ class AdminGetSystemConfigAdapter(AdminApiAdapter):
# 敏感配置项,不返回实际值
SENSITIVE_KEYS = {"smtp_password"}
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
value = SystemConfigService.get_config(context.db, self.key)
if value is None:
raise NotFoundException(f"配置项 '{self.key}' 不存在")
@@ -588,7 +592,7 @@ class AdminSetSystemConfigAdapter(AdminApiAdapter):
# 需要加密存储的配置项
ENCRYPTED_KEYS = {"smtp_password"}
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
payload = context.ensure_json_body()
value = payload.get("value")
@@ -619,7 +623,7 @@ class AdminSetSystemConfigAdapter(AdminApiAdapter):
class AdminDeleteSystemConfigAdapter(AdminApiAdapter):
key: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
deleted = SystemConfigService.delete_config(context.db, self.key)
if not deleted:
raise NotFoundException(f"配置项 '{self.key}' 不存在")
@@ -627,7 +631,7 @@ class AdminDeleteSystemConfigAdapter(AdminApiAdapter):
class AdminSystemStatsAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
total_users = db.query(User).count()
active_users = db.query(User).filter(User.is_active.is_(True)).count()
@@ -645,7 +649,7 @@ class AdminSystemStatsAdapter(AdminApiAdapter):
class AdminTriggerCleanupAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
"""手动触发清理任务"""
from datetime import datetime, timezone
@@ -708,7 +712,7 @@ class AdminTriggerCleanupAdapter(AdminApiAdapter):
class AdminGetApiFormatsAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
"""获取所有可用的API格式"""
from src.core.api_format import API_FORMAT_DEFINITIONS, APIFormat
@@ -738,7 +742,7 @@ class AdminExportConfigAdapter(AdminApiAdapter):
"token_cookie", "auth_cookie", "cookie_string", "cookie"
}
def _decrypt_provider_config(self, config: dict, crypto_service) -> dict:
def _decrypt_provider_config(self, config: dict, crypto_service: Any) -> dict:
"""解密 Provider config 中的 provider_ops credentials"""
if not config:
return config
@@ -762,7 +766,7 @@ class AdminExportConfigAdapter(AdminApiAdapter):
return decrypted_config
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
"""导出提供商和模型配置(解密数据)"""
from datetime import datetime, timezone
@@ -976,7 +980,7 @@ class AdminImportConfigAdapter(AdminApiAdapter):
"token_cookie", "auth_cookie", "cookie_string", "cookie"
}
def _encrypt_provider_config(self, config: dict, crypto_service) -> dict:
def _encrypt_provider_config(self, config: dict, crypto_service: Any) -> dict:
"""加密 Provider config 中的 provider_ops credentials"""
if not config:
return config
@@ -998,7 +1002,7 @@ class AdminImportConfigAdapter(AdminApiAdapter):
return encrypted_config
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
"""导入提供商和模型配置"""
import uuid
from datetime import datetime, timezone
@@ -1611,7 +1615,7 @@ class AdminImportConfigAdapter(AdminApiAdapter):
class AdminExportUsersAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
"""导出用户数据(保留加密数据,排除管理员)"""
from datetime import datetime, timezone
@@ -1689,7 +1693,7 @@ class AdminExportUsersAdapter(AdminApiAdapter):
class AdminImportUsersAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
"""导入用户数据"""
import uuid
from datetime import datetime, timezone
@@ -1891,7 +1895,7 @@ class AdminImportUsersAdapter(AdminApiAdapter):
class AdminTestSmtpAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
"""测试 SMTP 连接"""
from src.core.crypto import crypto_service
from src.services.system.config import SystemConfigService
@@ -1968,7 +1972,7 @@ class AdminTestSmtpAdapter(AdminApiAdapter):
class AdminGetEmailTemplatesAdapter(AdminApiAdapter):
"""获取所有邮件模板"""
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
templates = []
@@ -2003,7 +2007,7 @@ class AdminGetEmailTemplateAdapter(AdminApiAdapter):
template_type: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
# 验证模板类型
if self.template_type not in EmailTemplate.TEMPLATE_TYPES:
raise NotFoundException(f"模板类型 '{self.template_type}' 不存在")
@@ -2036,7 +2040,7 @@ class AdminUpdateEmailTemplateAdapter(AdminApiAdapter):
template_type: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
# 验证模板类型
if self.template_type not in EmailTemplate.TEMPLATE_TYPES:
raise NotFoundException(f"模板类型 '{self.template_type}' 不存在")
@@ -2077,7 +2081,7 @@ class AdminPreviewEmailTemplateAdapter(AdminApiAdapter):
template_type: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
# 验证模板类型
if self.template_type not in EmailTemplate.TEMPLATE_TYPES:
raise NotFoundException(f"模板类型 '{self.template_type}' 不存在")
@@ -2123,7 +2127,7 @@ class AdminResetEmailTemplateAdapter(AdminApiAdapter):
template_type: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
# 验证模板类型
if self.template_type not in EmailTemplate.TEMPLATE_TYPES:
raise NotFoundException(f"模板类型 '{self.template_type}' 不存在")

View File

@@ -1,5 +1,8 @@
"""管理员使用情况统计路由。"""
from __future__ import annotations
from typing import Any
from collections import defaultdict
from dataclasses import dataclass
from datetime import datetime
@@ -21,6 +24,7 @@ from src.models.database import (
User,
)
from src.services.usage.service import UsageService
from src.api.base.context import ApiRequestContext
router = APIRouter(prefix="/api/admin/usage", tags=["Admin - Usage"])
pipeline = ApiRequestPipeline()
@@ -37,7 +41,7 @@ async def get_usage_aggregation(
end_date: datetime | None = None,
limit: int = Query(20, ge=1, le=100),
db: Session = Depends(get_db),
):
) -> Any:
"""
获取使用情况聚合统计
@@ -77,7 +81,7 @@ async def get_usage_stats(
start_date: datetime | None = None,
end_date: datetime | None = None,
db: Session = Depends(get_db),
):
) -> Any:
"""
获取使用情况总体统计
@@ -105,7 +109,7 @@ async def get_usage_stats(
async def get_activity_heatmap(
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
获取活动热力图数据
@@ -132,7 +136,7 @@ async def get_usage_records(
limit: int = Query(100, ge=1, le=500),
offset: int = Query(0, ge=0),
db: Session = Depends(get_db),
):
) -> Any:
"""
获取使用记录列表
@@ -180,7 +184,7 @@ async def get_active_requests(
request: Request,
ids: str | None = Query(None, description="逗号分隔的请求 ID 列表,用于查询特定请求的状态"),
db: Session = Depends(get_db),
):
) -> Any:
"""
获取活跃请求的状态
@@ -207,7 +211,7 @@ async def get_usage_detail(
usage_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
获取使用记录详情
@@ -262,7 +266,7 @@ class AdminUsageStatsAdapter(AdminApiAdapter):
self.start_date = start_date
self.end_date = end_date
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
query = db.query(Usage)
if self.start_date:
@@ -327,7 +331,7 @@ class AdminUsageStatsAdapter(AdminApiAdapter):
class AdminActivityHeatmapAdapter(AdminApiAdapter):
"""Activity heatmap adapter with Redis caching."""
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
result = await UsageService.get_cached_heatmap(
db=context.db,
user_id=None,
@@ -343,7 +347,7 @@ class AdminUsageByModelAdapter(AdminApiAdapter):
self.end_date = end_date
self.limit = limit
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
query = db.query(
Usage.model,
@@ -390,7 +394,7 @@ class AdminUsageByUserAdapter(AdminApiAdapter):
self.end_date = end_date
self.limit = limit
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
query = (
db.query(
@@ -440,7 +444,7 @@ class AdminUsageByProviderAdapter(AdminApiAdapter):
self.end_date = end_date
self.limit = limit
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
# 从 request_candidates 表统计每个 Provider 的尝试次数和成功率
@@ -554,7 +558,7 @@ class AdminUsageByApiFormatAdapter(AdminApiAdapter):
self.end_date = end_date
self.limit = limit
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
query = db.query(
Usage.api_format,
@@ -629,7 +633,7 @@ class AdminUsageRecordsAdapter(AdminApiAdapter):
self.limit = limit
self.offset = offset
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
from sqlalchemy import or_
from src.utils.database_helpers import escape_like_pattern, safe_truncate_escaped
@@ -879,7 +883,7 @@ class AdminActiveRequestsAdapter(AdminApiAdapter):
def __init__(self, ids: str | None):
self.ids = ids
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
from src.services.usage import UsageService
db = context.db
@@ -901,7 +905,7 @@ class AdminUsageDetailAdapter(AdminApiAdapter):
usage_id: str
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
usage_record = db.query(Usage).filter(Usage.id == self.usage_id).first()
if not usage_record:
@@ -972,7 +976,7 @@ class AdminUsageDetailAdapter(AdminApiAdapter):
"tiered_pricing": tiered_pricing_info,
}
async def _get_tiered_pricing_info(self, db, usage_record) -> dict | None:
async def _get_tiered_pricing_info(self, db: Session, usage_record: Any) -> dict | None:
"""获取阶梯计费信息"""
from src.services.model.cost import ModelCostService
@@ -1036,7 +1040,7 @@ async def analyze_cache_affinity_ttl(
api_key_id: str | None = Query(None, description="指定 API Key ID"),
hours: int = Query(168, ge=1, le=720, description="分析最近多少小时的数据"),
db: Session = Depends(get_db),
):
) -> Any:
"""
分析用户请求间隔分布,推荐合适的缓存亲和性 TTL。
@@ -1060,7 +1064,7 @@ async def analyze_cache_hit(
api_key_id: str | None = Query(None, description="指定 API Key ID"),
hours: int = Query(168, ge=1, le=720, description="分析最近多少小时的数据"),
db: Session = Depends(get_db),
):
) -> Any:
"""
分析缓存命中情况。
@@ -1087,7 +1091,7 @@ class CacheAffinityTTLAnalysisAdapter(AdminApiAdapter):
self.api_key_id = api_key_id
self.hours = hours
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
result = UsageService.analyze_cache_affinity_ttl(
@@ -1121,7 +1125,7 @@ class CacheHitAnalysisAdapter(AdminApiAdapter):
self.api_key_id = api_key_id
self.hours = hours
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
result = UsageService.get_cache_hit_analysis(
@@ -1149,7 +1153,7 @@ async def get_interval_timeline(
user_id: str | None = Query(None, description="指定用户 ID"),
include_user_info: bool = Query(False, description="是否包含用户信息(用于管理员多用户视图)"),
db: Session = Depends(get_db),
):
) -> Any:
"""
获取请求间隔时间线数据,用于散点图展示。
@@ -1184,7 +1188,7 @@ class IntervalTimelineAdapter(AdminApiAdapter):
self.user_id = user_id
self.include_user_info = include_user_info
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
result = UsageService.get_interval_timeline(

View File

@@ -1,5 +1,8 @@
"""用户管理 API 端点。"""
from __future__ import annotations
from typing import Any
from datetime import datetime, timezone
from fastapi import APIRouter, Depends, HTTPException, Query, Request, status
@@ -17,6 +20,7 @@ from src.models.database import ApiKey, User, UserRole
from src.services.system.config import SystemConfigService
from src.services.user.apikey import ApiKeyService
from src.services.user.service import UserService
from src.api.base.context import ApiRequestContext
router = APIRouter(prefix="/api/admin/users", tags=["Admin - Users"])
@@ -25,7 +29,7 @@ pipeline = ApiRequestPipeline()
# 管理员端点
@router.post("")
async def create_user_endpoint(request: Request, db: Session = Depends(get_db)):
async def create_user_endpoint(request: Request, db: Session = Depends(get_db)) -> Any:
"""
创建用户
@@ -50,7 +54,7 @@ async def list_users(
role: str | None = Query(None, description="按角色筛选user/admin"),
is_active: bool | None = Query(None, description="按状态筛选"),
db: Session = Depends(get_db),
):
) -> Any:
"""
获取用户列表
@@ -63,7 +67,7 @@ async def list_users(
@router.get("/{user_id}")
async def get_user(user_id: str, request: Request, db: Session = Depends(get_db)):
async def get_user(user_id: str, request: Request, db: Session = Depends(get_db)) -> Any:
"""
获取用户详情
@@ -81,7 +85,7 @@ async def update_user(
user_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
更新用户信息
@@ -104,7 +108,7 @@ async def update_user(
@router.delete("/{user_id}")
async def delete_user(user_id: str, request: Request, db: Session = Depends(get_db)):
async def delete_user(user_id: str, request: Request, db: Session = Depends(get_db)) -> None:
"""
删除用户
@@ -118,7 +122,7 @@ async def delete_user(user_id: str, request: Request, db: Session = Depends(get_
@router.patch("/{user_id}/quota")
async def reset_user_quota(user_id: str, request: Request, db: Session = Depends(get_db)):
async def reset_user_quota(user_id: str, request: Request, db: Session = Depends(get_db)) -> None:
"""
重置用户配额
@@ -137,7 +141,7 @@ async def get_user_api_keys(
request: Request,
is_active: bool | None = Query(None, description="按状态筛选"),
db: Session = Depends(get_db),
):
) -> Any:
"""
获取用户的 API 密钥列表
@@ -155,7 +159,7 @@ async def create_user_api_key(
user_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
为用户创建 API 密钥
@@ -183,7 +187,7 @@ async def delete_user_api_key(
key_id: str,
request: Request,
db: Session = Depends(get_db),
):
) -> Any:
"""
删除用户的 API 密钥
@@ -201,7 +205,7 @@ async def delete_user_api_key(
class AdminCreateUserAdapter(AdminApiAdapter):
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
payload = context.ensure_json_body()
try:
@@ -279,7 +283,7 @@ class AdminListUsersAdapter(AdminApiAdapter):
self.role = role
self.is_active = is_active
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
role_enum = UserRole[self.role.upper()] if self.role else None
users = UserService.list_users(db, self.skip, self.limit, role_enum, self.is_active)
@@ -306,7 +310,7 @@ class AdminGetUserAdapter(AdminApiAdapter):
def __init__(self, user_id: str):
self.user_id = user_id
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
user = UserService.get_user(db, self.user_id)
if not user:
@@ -341,7 +345,7 @@ class AdminUpdateUserAdapter(AdminApiAdapter):
def __init__(self, user_id: str):
self.user_id = user_id
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
existing_user = UserService.get_user(db, self.user_id)
if not existing_user:
@@ -406,7 +410,7 @@ class AdminDeleteUserAdapter(AdminApiAdapter):
def __init__(self, user_id: str):
self.user_id = user_id
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
user = UserService.get_user(db, self.user_id)
if not user:
@@ -435,7 +439,7 @@ class AdminResetUserQuotaAdapter(AdminApiAdapter):
def __init__(self, user_id: str):
self.user_id = user_id
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
user = UserService.get_user(db, self.user_id)
if not user:
@@ -470,7 +474,7 @@ class AdminGetUserKeysAdapter(AdminApiAdapter):
self.user_id = user_id
self.is_active = is_active
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
# 验证用户存在
@@ -518,7 +522,7 @@ class AdminCreateUserKeyAdapter(AdminApiAdapter):
def __init__(self, user_id: str):
self.user_id = user_id
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
payload = context.ensure_json_body()
try:
@@ -574,7 +578,7 @@ class AdminDeleteUserKeyAdapter(AdminApiAdapter):
self.user_id = user_id
self.key_id = key_id
async def handle(self, context): # type: ignore[override]
async def handle(self, context: ApiRequestContext) -> Any: # type: ignore[override]
db = context.db
# 验证Key存在且属于该用户