mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
feat(retention): 删除用户/Key 时保留历史记录,外键改 SET NULL 并添加名称快照
- Usage/RequestCandidate/VideoTask/Stats 等表的 user_id/api_key_id 外键从
CASCADE 改为 SET NULL,删除用户或 Key 后历史记录不再丢失
- 各表添加 username/api_key_name 快照字段,删除后仍可追溯归属
- 新增 bulk_cleanup 模块,分批置空大表外键避免长事务锁
- 删除用户/Key 流程集成预清理步骤,先置空再删除
- 精简 candidate_builder 冗余 debug 日志
- 修复 proxy_nodes 启动日志 format 占位符错误({} -> %s)
- 前端批量操作请求增加 5 分钟超时配置
This commit is contained in:
@@ -23,6 +23,7 @@ from src.database import get_db
|
||||
from src.models.api import CreateApiKeyRequest
|
||||
from src.models.database import ApiKey, Wallet
|
||||
from src.services.user.apikey import ApiKeyService
|
||||
from src.services.user.bulk_cleanup import pre_clean_api_key
|
||||
from src.services.wallet import WalletService
|
||||
|
||||
# 应用时区配置,默认为 Asia/Shanghai
|
||||
@@ -349,10 +350,7 @@ class AdminCreateStandaloneKeyAdapter(AdminApiAdapter):
|
||||
db = context.db
|
||||
|
||||
# 独立Key支持无限制额度(initial_balance_usd = null)
|
||||
if (
|
||||
self.key_data.initial_balance_usd is not None
|
||||
and self.key_data.initial_balance_usd <= 0
|
||||
):
|
||||
if self.key_data.initial_balance_usd is not None and self.key_data.initial_balance_usd <= 0:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail="创建独立余额Key时,初始余额必须大于 0(或设置为 null 表示无限制)",
|
||||
@@ -568,6 +566,7 @@ class AdminDeleteApiKeyAdapter(AdminApiAdapter):
|
||||
raise InvalidRequestException("仅支持删除独立密钥")
|
||||
|
||||
user = api_key.user
|
||||
pre_clean_api_key(db, api_key.id)
|
||||
db.delete(api_key)
|
||||
db.commit()
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ from src.models.api import CreateApiKeyRequest, CreateUserRequest
|
||||
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.bulk_cleanup import pre_clean_api_key
|
||||
from src.services.user.service import UserService
|
||||
from src.services.wallet import WalletService
|
||||
from src.utils.cache_decorator import cache_result
|
||||
@@ -584,6 +585,7 @@ class AdminDeleteUserKeyAdapter(AdminApiAdapter):
|
||||
if not api_key:
|
||||
raise NotFoundException("API Key不存在或不属于该用户", "api_key")
|
||||
|
||||
pre_clean_api_key(db, api_key.id)
|
||||
db.delete(api_key)
|
||||
db.commit()
|
||||
|
||||
|
||||
@@ -634,6 +634,8 @@ class GeminiVeoHandler(VideoHandlerBase):
|
||||
external_task_id=external_task_id,
|
||||
user_id=self.user.id,
|
||||
api_key_id=self.api_key.id,
|
||||
username=self.user.username,
|
||||
api_key_name=self.api_key.name,
|
||||
provider_id=candidate.provider.id,
|
||||
endpoint_id=candidate.endpoint.id,
|
||||
key_id=candidate.key.id,
|
||||
|
||||
@@ -887,6 +887,8 @@ class OpenAIVideoHandler(VideoHandlerBase):
|
||||
external_task_id=external_task_id,
|
||||
user_id=self.user.id,
|
||||
api_key_id=self.api_key.id,
|
||||
username=self.user.username,
|
||||
api_key_name=self.api_key.name,
|
||||
provider_id=candidate.provider.id,
|
||||
endpoint_id=candidate.endpoint.id,
|
||||
key_id=candidate.key.id,
|
||||
@@ -1049,6 +1051,8 @@ class OpenAIVideoHandler(VideoHandlerBase):
|
||||
external_task_id=None,
|
||||
user_id=self.user.id,
|
||||
api_key_id=self.api_key.id,
|
||||
username=self.user.username,
|
||||
api_key_name=self.api_key.name,
|
||||
provider_id=None,
|
||||
endpoint_id=None,
|
||||
key_id=None,
|
||||
|
||||
@@ -46,6 +46,7 @@ from src.models.database import (
|
||||
from src.services.system.time_range import TimeRangeParams
|
||||
from src.services.usage.service import UsageService
|
||||
from src.services.user.apikey import ApiKeyService
|
||||
from src.services.user.bulk_cleanup import pre_clean_api_key
|
||||
from src.services.user.preference import PreferenceService
|
||||
from src.services.wallet import WalletService
|
||||
from src.utils.cache_decorator import cache_result
|
||||
@@ -731,6 +732,7 @@ class DeleteMyApiKeyAdapter(AuthenticatedApiAdapter):
|
||||
raise NotFoundException("API密钥不存在", "api_key")
|
||||
if api_key.is_locked:
|
||||
raise ForbiddenException("该密钥已被管理员锁定,无法删除")
|
||||
pre_clean_api_key(context.db, api_key.id)
|
||||
context.db.delete(api_key)
|
||||
context.db.commit()
|
||||
return {"message": "API密钥已删除"}
|
||||
|
||||
Reference in New Issue
Block a user