备份: 完整开发状态(含反混淆脚本和临时文件)

This commit is contained in:
ccdojox-crypto
2025-12-17 17:18:02 +08:00
parent 9e2333c90c
commit 7e9ea173a7
2872 changed files with 326818 additions and 249 deletions

View File

@@ -3,16 +3,18 @@
"""
from datetime import datetime
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, status, Query
from fastapi import APIRouter, Depends, HTTPException, status, Query, Header
from sqlalchemy.orm import Session
from app.database import get_db
from app.config import settings
from app.services import AccountService, KeyService, LogService, GlobalSettingsService, BatchService, authenticate_admin, create_access_token, get_current_user
from app.schemas import (
AccountCreate, AccountUpdate, AccountResponse, AccountImport,
KeyCreate, KeyUpdate, KeyResponse,
DashboardStats, Token, LoginRequest,
GlobalSettingsResponse, GlobalSettingsUpdate,
BatchExtendRequest, BatchExtendResponse
BatchExtendRequest, BatchExtendResponse,
ExternalBatchUpload, ExternalBatchResponse
)
from app.models import MembershipType, KeyDevice, UsageLog, ActivationKey
@@ -33,6 +35,138 @@ async def login(request: LoginRequest):
return Token(access_token=access_token)
# ========== 外部系统API (Token认证) ==========
def verify_api_token(x_api_token: str = Header(..., alias="X-API-Token")):
"""验证外部系统API Token"""
if x_api_token != settings.API_TOKEN:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid API Token"
)
return True
@router.post("/external/accounts/batch", response_model=ExternalBatchResponse)
async def external_batch_upload(
data: ExternalBatchUpload,
db: Session = Depends(get_db),
_: bool = Depends(verify_api_token)
):
"""外部系统批量上传账号
使用方法:
POST /admin/external/accounts/batch
Headers: X-API-Token: your-api-token
Body: {
"accounts": [
{
"email": "user@example.com",
"access_token": "xxx",
"refresh_token": "xxx",
"workos_session_token": "xxx",
"membership_type": "free", // free=auto账号, pro=高级账号
"remark": "备注"
}
],
"update_existing": true // 是否更新已存在的账号
}
"""
created = 0
updated = 0
failed = 0
errors = []
for item in data.accounts:
try:
# 转换membership_type
mt = MembershipType.FREE if item.membership_type == "free" else MembershipType.PRO
existing = AccountService.get_by_email(db, item.email)
if existing:
if data.update_existing:
# 更新已存在的账号
AccountService.update(
db, existing.id,
access_token=item.access_token,
refresh_token=item.refresh_token,
workos_session_token=item.workos_session_token,
membership_type=mt,
remark=item.remark or existing.remark
)
updated += 1
else:
failed += 1
errors.append(f"{item.email}: 账号已存在")
else:
# 创建新账号
account_data = AccountCreate(
email=item.email,
access_token=item.access_token,
refresh_token=item.refresh_token,
workos_session_token=item.workos_session_token,
membership_type=mt,
remark=item.remark
)
AccountService.create(db, account_data)
created += 1
except Exception as e:
failed += 1
errors.append(f"{item.email}: {str(e)}")
return ExternalBatchResponse(
success=failed == 0,
total=len(data.accounts),
created=created,
updated=updated,
failed=failed,
errors=errors[:20] # 只返回前20个错误
)
@router.get("/external/accounts/stats")
async def external_account_stats(
db: Session = Depends(get_db),
_: bool = Depends(verify_api_token)
):
"""外部系统获取账号统计"""
stats = AccountService.count(db)
return {
"total": stats["total"],
"active": stats["active"],
"pro": stats["pro"],
"free": stats["total"] - stats["pro"]
}
@router.delete("/external/accounts/batch")
async def external_batch_delete(
emails: List[str],
db: Session = Depends(get_db),
_: bool = Depends(verify_api_token)
):
"""外部系统批量删除账号"""
deleted = 0
failed = 0
for email in emails:
try:
account = AccountService.get_by_email(db, email)
if account:
AccountService.delete(db, account.id)
deleted += 1
else:
failed += 1
except Exception:
failed += 1
return {
"success": failed == 0,
"deleted": deleted,
"failed": failed
}
# ========== 仪表盘 ==========
@router.get("/dashboard", response_model=DashboardStats)
@@ -118,6 +252,16 @@ async def import_accounts(
}
@router.get("/accounts/count")
async def get_accounts_count(
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""获取账号总数"""
stats = AccountService.count(db)
return {"total": stats["total"]}
@router.get("/accounts/{account_id}", response_model=AccountResponse)
async def get_account(
account_id: int,
@@ -157,6 +301,166 @@ async def delete_account(
return {"message": "删除成功"}
@router.post("/accounts/{account_id}/toggle-status")
async def toggle_account_status(
account_id: int,
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""快捷切换账号状态
切换逻辑:
- 使用中(in_use) -> 可用(active) 释放账号
- 可用(active) -> 禁用(disabled)
- 禁用(disabled) -> 可用(active)
- 过期(expired) -> 可用(active)
"""
from app.models import AccountStatus, Account
account = db.query(Account).filter(Account.id == account_id).first()
if not account:
raise HTTPException(status_code=404, detail="账号不存在")
old_status = account.status
# 根据当前状态切换
if account.status == AccountStatus.IN_USE:
account.status = AccountStatus.ACTIVE
account.current_key_id = None # 释放绑定
elif account.status == AccountStatus.ACTIVE:
account.status = AccountStatus.DISABLED
elif account.status == AccountStatus.DISABLED:
account.status = AccountStatus.ACTIVE
elif account.status == AccountStatus.EXPIRED:
account.status = AccountStatus.ACTIVE
db.commit()
return {
"success": True,
"old_status": old_status.value,
"new_status": account.status.value,
"message": f"状态已从 {old_status.value} 切换为 {account.status.value}"
}
@router.post("/accounts/{account_id}/release")
async def release_account(
account_id: int,
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""释放账号(从使用中变为可用)"""
from app.models import AccountStatus, Account
account = db.query(Account).filter(Account.id == account_id).first()
if not account:
raise HTTPException(status_code=404, detail="账号不存在")
if account.status != AccountStatus.IN_USE:
return {"success": False, "message": "账号不在使用中状态"}
account.status = AccountStatus.ACTIVE
account.current_key_id = None
db.commit()
return {"success": True, "message": "账号已释放"}
@router.post("/accounts/batch-enable")
async def batch_enable_accounts(
account_ids: List[int],
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""批量启用账号"""
from app.models import AccountStatus, Account
success = 0
failed = 0
for account_id in account_ids:
try:
account = db.query(Account).filter(Account.id == account_id).first()
if account:
account.status = AccountStatus.ACTIVE
success += 1
else:
failed += 1
except Exception:
failed += 1
db.commit()
return {
"success": success,
"failed": failed,
"message": f"成功启用 {success} 个账号"
}
@router.post("/accounts/batch-disable")
async def batch_disable_accounts(
account_ids: List[int],
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""批量禁用账号"""
from app.models import AccountStatus, Account
success = 0
failed = 0
for account_id in account_ids:
try:
account = db.query(Account).filter(Account.id == account_id).first()
if account:
account.status = AccountStatus.DISABLED
success += 1
else:
failed += 1
except Exception:
failed += 1
db.commit()
return {
"success": success,
"failed": failed,
"message": f"成功禁用 {success} 个账号"
}
@router.post("/accounts/batch-delete")
async def batch_delete_accounts(
account_ids: List[int],
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""批量删除账号"""
success = 0
failed = 0
for account_id in account_ids:
try:
if AccountService.delete(db, account_id):
success += 1
else:
failed += 1
except Exception:
failed += 1
return {
"success": success,
"failed": failed,
"message": f"成功删除 {success} 个账号"
}
@router.get("/accounts/count")
async def get_accounts_count(
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""获取账号总数"""
stats = AccountService.count(db)
return {"total": stats["total"]}
# ========== 激活码管理 ==========
@router.get("/keys", response_model=List[KeyResponse])
@@ -165,7 +469,7 @@ async def list_keys(
limit: int = 100,
search: Optional[str] = Query(None, description="搜索激活码"),
status: Optional[str] = Query(None, description="状态筛选: active/disabled"),
activated: Optional[bool] = Query(None, description="是否已激活"),
activated: Optional[str] = Query(None, description="是否已激活: true/false"),
membership_type: Optional[str] = Query(None, description="套餐类型: pro/free"),
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
@@ -180,12 +484,13 @@ async def list_keys(
# 状态筛选
if status:
query = query.filter(ActivationKey.status == status)
status_enum = KeyStatus.ACTIVE if status == "active" else KeyStatus.DISABLED
query = query.filter(ActivationKey.status == status_enum)
# 是否已激活
if activated is True:
if activated and activated == "true":
query = query.filter(ActivationKey.first_activated_at != None)
elif activated is False:
elif activated and activated == "false":
query = query.filter(ActivationKey.first_activated_at == None)
# 套餐类型筛选
@@ -196,6 +501,43 @@ async def list_keys(
return query.offset(skip).limit(limit).all()
@router.get("/keys/count")
async def get_keys_count(
search: Optional[str] = Query(None, description="搜索激活码"),
status: Optional[str] = Query(None, description="状态筛选: active/disabled"),
activated: Optional[str] = Query(None, description="是否已激活: true/false"),
membership_type: Optional[str] = Query(None, description="套餐类型: pro/free"),
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""获取激活码总数(支持筛选)"""
from app.models import KeyStatus
query = db.query(ActivationKey)
# 搜索激活码
if search:
query = query.filter(ActivationKey.key.contains(search))
# 状态筛选
if status:
status_enum = KeyStatus.ACTIVE if status == "active" else KeyStatus.DISABLED
query = query.filter(ActivationKey.status == status_enum)
# 是否已激活
if activated and activated == "true":
query = query.filter(ActivationKey.first_activated_at != None)
elif activated and activated == "false":
query = query.filter(ActivationKey.first_activated_at == None)
# 套餐类型筛选
if membership_type:
mt = MembershipType.PRO if membership_type.lower() == "pro" else MembershipType.FREE
query = query.filter(ActivationKey.membership_type == mt)
total = query.count()
return {"total": total}
@router.post("/keys", response_model=List[KeyResponse])
async def create_keys(
key_data: KeyCreate,
@@ -344,6 +686,129 @@ async def enable_key(
return {"message": "激活码已启用"}
@router.post("/keys/batch-enable")
async def batch_enable_keys(
key_ids: List[int],
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""批量启用激活码"""
from app.models import KeyStatus
success = 0
failed = 0
for key_id in key_ids:
try:
key = KeyService.get_by_id(db, key_id)
if key:
key.status = KeyStatus.ACTIVE
success += 1
else:
failed += 1
except Exception:
failed += 1
db.commit()
return {
"success": success,
"failed": failed,
"message": f"成功启用 {success} 个激活码"
}
@router.post("/keys/batch-disable")
async def batch_disable_keys(
key_ids: List[int],
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""批量禁用激活码"""
from app.models import KeyStatus
success = 0
failed = 0
for key_id in key_ids:
try:
key = KeyService.get_by_id(db, key_id)
if key:
key.status = KeyStatus.DISABLED
success += 1
else:
failed += 1
except Exception:
failed += 1
db.commit()
return {
"success": success,
"failed": failed,
"message": f"成功禁用 {success} 个激活码"
}
@router.post("/keys/batch-delete")
async def batch_delete_keys(
key_ids: List[int],
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""批量删除激活码"""
success = 0
failed = 0
for key_id in key_ids:
try:
if KeyService.delete(db, key_id):
success += 1
else:
failed += 1
except Exception:
failed += 1
return {
"success": success,
"failed": failed,
"message": f"成功删除 {success} 个激活码"
}
@router.get("/keys/count")
async def get_keys_count(
search: Optional[str] = Query(None, description="搜索激活码"),
status: Optional[str] = Query(None, description="状态筛选: active/disabled"),
activated: Optional[str] = Query(None, description="是否已激活: true/false"),
membership_type: Optional[str] = Query(None, description="套餐类型: pro/free"),
db: Session = Depends(get_db),
current_user: dict = Depends(get_current_user)
):
"""获取激活码总数(支持筛选)"""
from app.models import KeyStatus
query = db.query(ActivationKey)
# 搜索激活码
if search:
query = query.filter(ActivationKey.key.contains(search))
# 状态筛选
if status:
status_enum = KeyStatus.ACTIVE if status == "active" else KeyStatus.DISABLED
query = query.filter(ActivationKey.status == status_enum)
# 是否已激活
if activated and activated == "true":
query = query.filter(ActivationKey.first_activated_at != None)
elif activated and activated == "false":
query = query.filter(ActivationKey.first_activated_at == None)
# 套餐类型筛选
if membership_type:
mt = MembershipType.PRO if membership_type.lower() == "pro" else MembershipType.FREE
query = query.filter(ActivationKey.membership_type == mt)
total = query.count()
return {"total": total}
@router.post("/keys/{key_id}/add-quota", response_model=KeyResponse)
async def add_key_quota(
key_id: int,