mirror of
https://github.com/fawney19/Aether.git
synced 2026-01-03 00:02:28 +08:00
313 lines
11 KiB
Python
313 lines
11 KiB
Python
|
|
"""系统设置API端点。"""
|
||
|
|
|
||
|
|
from dataclasses import dataclass
|
||
|
|
from typing import Optional
|
||
|
|
|
||
|
|
from fastapi import APIRouter, Depends, HTTPException, Request, status
|
||
|
|
from pydantic import ValidationError
|
||
|
|
from sqlalchemy.orm import Session
|
||
|
|
|
||
|
|
from src.api.base.admin_adapter import AdminApiAdapter
|
||
|
|
from src.api.base.pipeline import ApiRequestPipeline
|
||
|
|
from src.core.exceptions import InvalidRequestException, NotFoundException, translate_pydantic_error
|
||
|
|
from src.database import get_db
|
||
|
|
from src.models.api import SystemSettingsRequest, SystemSettingsResponse
|
||
|
|
from src.models.database import ApiKey, Provider, Usage, User
|
||
|
|
from src.services.system.config import SystemConfigService
|
||
|
|
|
||
|
|
router = APIRouter(prefix="/api/admin/system", tags=["Admin - System"])
|
||
|
|
pipeline = ApiRequestPipeline()
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/settings")
|
||
|
|
async def get_system_settings(request: Request, db: Session = Depends(get_db)):
|
||
|
|
"""获取系统设置(管理员)"""
|
||
|
|
|
||
|
|
adapter = AdminGetSystemSettingsAdapter()
|
||
|
|
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||
|
|
|
||
|
|
|
||
|
|
@router.put("/settings")
|
||
|
|
async def update_system_settings(http_request: Request, db: Session = Depends(get_db)):
|
||
|
|
"""更新系统设置(管理员)"""
|
||
|
|
|
||
|
|
adapter = AdminUpdateSystemSettingsAdapter()
|
||
|
|
return await pipeline.run(adapter=adapter, http_request=http_request, db=db, mode=adapter.mode)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/configs")
|
||
|
|
async def get_all_system_configs(request: Request, db: Session = Depends(get_db)):
|
||
|
|
"""获取所有系统配置(管理员)"""
|
||
|
|
|
||
|
|
adapter = AdminGetAllConfigsAdapter()
|
||
|
|
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/configs/{key}")
|
||
|
|
async def get_system_config(key: str, request: Request, db: Session = Depends(get_db)):
|
||
|
|
"""获取特定系统配置(管理员)"""
|
||
|
|
|
||
|
|
adapter = AdminGetSystemConfigAdapter(key=key)
|
||
|
|
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||
|
|
|
||
|
|
|
||
|
|
@router.put("/configs/{key}")
|
||
|
|
async def set_system_config(
|
||
|
|
key: str,
|
||
|
|
request: Request,
|
||
|
|
db: Session = Depends(get_db),
|
||
|
|
):
|
||
|
|
"""设置系统配置(管理员)"""
|
||
|
|
|
||
|
|
adapter = AdminSetSystemConfigAdapter(key=key)
|
||
|
|
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||
|
|
|
||
|
|
|
||
|
|
@router.delete("/configs/{key}")
|
||
|
|
async def delete_system_config(key: str, request: Request, db: Session = Depends(get_db)):
|
||
|
|
"""删除系统配置(管理员)"""
|
||
|
|
|
||
|
|
adapter = AdminDeleteSystemConfigAdapter(key=key)
|
||
|
|
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/stats")
|
||
|
|
async def get_system_stats(request: Request, db: Session = Depends(get_db)):
|
||
|
|
adapter = AdminSystemStatsAdapter()
|
||
|
|
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||
|
|
|
||
|
|
|
||
|
|
@router.post("/cleanup")
|
||
|
|
async def trigger_cleanup(request: Request, db: Session = Depends(get_db)):
|
||
|
|
"""Manually trigger usage record cleanup task"""
|
||
|
|
adapter = AdminTriggerCleanupAdapter()
|
||
|
|
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||
|
|
|
||
|
|
|
||
|
|
@router.get("/api-formats")
|
||
|
|
async def get_api_formats(request: Request, db: Session = Depends(get_db)):
|
||
|
|
"""获取所有可用的API格式列表"""
|
||
|
|
adapter = AdminGetApiFormatsAdapter()
|
||
|
|
return await pipeline.run(adapter=adapter, http_request=request, db=db, mode=adapter.mode)
|
||
|
|
|
||
|
|
|
||
|
|
# -------- 系统设置适配器 --------
|
||
|
|
|
||
|
|
|
||
|
|
class AdminGetSystemSettingsAdapter(AdminApiAdapter):
|
||
|
|
async def handle(self, context): # type: ignore[override]
|
||
|
|
db = context.db
|
||
|
|
default_provider = SystemConfigService.get_default_provider(db)
|
||
|
|
default_model = SystemConfigService.get_config(db, "default_model")
|
||
|
|
enable_usage_tracking = (
|
||
|
|
SystemConfigService.get_config(db, "enable_usage_tracking", "true") == "true"
|
||
|
|
)
|
||
|
|
|
||
|
|
return SystemSettingsResponse(
|
||
|
|
default_provider=default_provider,
|
||
|
|
default_model=default_model,
|
||
|
|
enable_usage_tracking=enable_usage_tracking,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
class AdminUpdateSystemSettingsAdapter(AdminApiAdapter):
|
||
|
|
async def handle(self, context): # type: ignore[override]
|
||
|
|
db = context.db
|
||
|
|
payload = context.ensure_json_body()
|
||
|
|
try:
|
||
|
|
settings_request = SystemSettingsRequest.model_validate(payload)
|
||
|
|
except ValidationError as e:
|
||
|
|
errors = e.errors()
|
||
|
|
if errors:
|
||
|
|
raise InvalidRequestException(translate_pydantic_error(errors[0]))
|
||
|
|
raise InvalidRequestException("请求数据验证失败")
|
||
|
|
|
||
|
|
if settings_request.default_provider is not None:
|
||
|
|
provider = (
|
||
|
|
db.query(Provider)
|
||
|
|
.filter(
|
||
|
|
Provider.name == settings_request.default_provider,
|
||
|
|
Provider.is_active.is_(True),
|
||
|
|
)
|
||
|
|
.first()
|
||
|
|
)
|
||
|
|
|
||
|
|
if not provider and settings_request.default_provider != "":
|
||
|
|
raise HTTPException(
|
||
|
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||
|
|
detail=f"提供商 '{settings_request.default_provider}' 不存在或未启用",
|
||
|
|
)
|
||
|
|
|
||
|
|
if settings_request.default_provider:
|
||
|
|
SystemConfigService.set_default_provider(db, settings_request.default_provider)
|
||
|
|
else:
|
||
|
|
SystemConfigService.delete_config(db, "default_provider")
|
||
|
|
|
||
|
|
if settings_request.default_model is not None:
|
||
|
|
if settings_request.default_model:
|
||
|
|
SystemConfigService.set_config(db, "default_model", settings_request.default_model)
|
||
|
|
else:
|
||
|
|
SystemConfigService.delete_config(db, "default_model")
|
||
|
|
|
||
|
|
if settings_request.enable_usage_tracking is not None:
|
||
|
|
SystemConfigService.set_config(
|
||
|
|
db,
|
||
|
|
"enable_usage_tracking",
|
||
|
|
str(settings_request.enable_usage_tracking).lower(),
|
||
|
|
)
|
||
|
|
|
||
|
|
return {"message": "系统设置更新成功"}
|
||
|
|
|
||
|
|
|
||
|
|
class AdminGetAllConfigsAdapter(AdminApiAdapter):
|
||
|
|
async def handle(self, context): # type: ignore[override]
|
||
|
|
return SystemConfigService.get_all_configs(context.db)
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass
|
||
|
|
class AdminGetSystemConfigAdapter(AdminApiAdapter):
|
||
|
|
key: str
|
||
|
|
|
||
|
|
async def handle(self, context): # type: ignore[override]
|
||
|
|
value = SystemConfigService.get_config(context.db, self.key)
|
||
|
|
if value is None:
|
||
|
|
raise NotFoundException(f"配置项 '{self.key}' 不存在")
|
||
|
|
return {"key": self.key, "value": value}
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass
|
||
|
|
class AdminSetSystemConfigAdapter(AdminApiAdapter):
|
||
|
|
key: str
|
||
|
|
|
||
|
|
async def handle(self, context): # type: ignore[override]
|
||
|
|
payload = context.ensure_json_body()
|
||
|
|
config = SystemConfigService.set_config(
|
||
|
|
context.db,
|
||
|
|
self.key,
|
||
|
|
payload.get("value"),
|
||
|
|
payload.get("description"),
|
||
|
|
)
|
||
|
|
|
||
|
|
return {
|
||
|
|
"key": config.key,
|
||
|
|
"value": config.value,
|
||
|
|
"description": config.description,
|
||
|
|
"updated_at": config.updated_at.isoformat(),
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
@dataclass
|
||
|
|
class AdminDeleteSystemConfigAdapter(AdminApiAdapter):
|
||
|
|
key: str
|
||
|
|
|
||
|
|
async def handle(self, context): # type: ignore[override]
|
||
|
|
deleted = SystemConfigService.delete_config(context.db, self.key)
|
||
|
|
if not deleted:
|
||
|
|
raise NotFoundException(f"配置项 '{self.key}' 不存在")
|
||
|
|
return {"message": f"配置项 '{self.key}' 已删除"}
|
||
|
|
|
||
|
|
|
||
|
|
class AdminSystemStatsAdapter(AdminApiAdapter):
|
||
|
|
async def handle(self, context): # type: ignore[override]
|
||
|
|
db = context.db
|
||
|
|
total_users = db.query(User).count()
|
||
|
|
active_users = db.query(User).filter(User.is_active.is_(True)).count()
|
||
|
|
total_providers = db.query(Provider).count()
|
||
|
|
active_providers = db.query(Provider).filter(Provider.is_active.is_(True)).count()
|
||
|
|
total_api_keys = db.query(ApiKey).count()
|
||
|
|
total_requests = db.query(Usage).count()
|
||
|
|
|
||
|
|
return {
|
||
|
|
"users": {"total": total_users, "active": active_users},
|
||
|
|
"providers": {"total": total_providers, "active": active_providers},
|
||
|
|
"api_keys": total_api_keys,
|
||
|
|
"requests": total_requests,
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
class AdminTriggerCleanupAdapter(AdminApiAdapter):
|
||
|
|
async def handle(self, context): # type: ignore[override]
|
||
|
|
"""手动触发清理任务"""
|
||
|
|
from datetime import datetime, timedelta, timezone
|
||
|
|
|
||
|
|
from sqlalchemy import func
|
||
|
|
|
||
|
|
from src.services.system.cleanup_scheduler import get_cleanup_scheduler
|
||
|
|
|
||
|
|
db = context.db
|
||
|
|
|
||
|
|
# 获取清理前的统计信息
|
||
|
|
total_before = db.query(Usage).count()
|
||
|
|
with_body_before = (
|
||
|
|
db.query(Usage)
|
||
|
|
.filter((Usage.request_body.isnot(None)) | (Usage.response_body.isnot(None)))
|
||
|
|
.count()
|
||
|
|
)
|
||
|
|
with_headers_before = (
|
||
|
|
db.query(Usage)
|
||
|
|
.filter((Usage.request_headers.isnot(None)) | (Usage.response_headers.isnot(None)))
|
||
|
|
.count()
|
||
|
|
)
|
||
|
|
|
||
|
|
# 触发清理
|
||
|
|
cleanup_scheduler = get_cleanup_scheduler()
|
||
|
|
await cleanup_scheduler._perform_cleanup()
|
||
|
|
|
||
|
|
# 获取清理后的统计信息
|
||
|
|
total_after = db.query(Usage).count()
|
||
|
|
with_body_after = (
|
||
|
|
db.query(Usage)
|
||
|
|
.filter((Usage.request_body.isnot(None)) | (Usage.response_body.isnot(None)))
|
||
|
|
.count()
|
||
|
|
)
|
||
|
|
with_headers_after = (
|
||
|
|
db.query(Usage)
|
||
|
|
.filter((Usage.request_headers.isnot(None)) | (Usage.response_headers.isnot(None)))
|
||
|
|
.count()
|
||
|
|
)
|
||
|
|
|
||
|
|
return {
|
||
|
|
"message": "清理任务执行完成",
|
||
|
|
"stats": {
|
||
|
|
"total_records": {
|
||
|
|
"before": total_before,
|
||
|
|
"after": total_after,
|
||
|
|
"deleted": total_before - total_after,
|
||
|
|
},
|
||
|
|
"body_fields": {
|
||
|
|
"before": with_body_before,
|
||
|
|
"after": with_body_after,
|
||
|
|
"cleaned": with_body_before - with_body_after,
|
||
|
|
},
|
||
|
|
"header_fields": {
|
||
|
|
"before": with_headers_before,
|
||
|
|
"after": with_headers_after,
|
||
|
|
"cleaned": with_headers_before - with_headers_after,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
class AdminGetApiFormatsAdapter(AdminApiAdapter):
|
||
|
|
async def handle(self, context): # type: ignore[override]
|
||
|
|
"""获取所有可用的API格式"""
|
||
|
|
from src.core.api_format_metadata import API_FORMAT_DEFINITIONS
|
||
|
|
from src.core.enums import APIFormat
|
||
|
|
|
||
|
|
_ = context # 参数保留以符合接口规范
|
||
|
|
|
||
|
|
formats = []
|
||
|
|
for api_format in APIFormat:
|
||
|
|
definition = API_FORMAT_DEFINITIONS.get(api_format)
|
||
|
|
formats.append(
|
||
|
|
{
|
||
|
|
"value": api_format.value,
|
||
|
|
"label": api_format.value,
|
||
|
|
"default_path": definition.default_path if definition else "/",
|
||
|
|
"aliases": list(definition.aliases) if definition else [],
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
return {"formats": formats}
|