feat: support proxy_url in CPA auth files

This commit is contained in:
shiuing
2026-03-20 17:29:49 +08:00
parent 0059cf97bd
commit fbf7e41b25
11 changed files with 110 additions and 15 deletions

View File

@@ -89,17 +89,23 @@ def _post_cpa_auth_file_raw_json(upload_url: str, filename: str, file_content: b
)
def generate_token_json(account: Account) -> dict:
def generate_token_json(
account: Account,
include_proxy_url: bool = False,
proxy_url: Optional[str] = None,
) -> dict:
"""
生成 CPA 格式的 Token JSON
Args:
account: 账号模型实例
include_proxy_url: 是否将账号代理写入 auth file 的 proxy_url 字段
proxy_url: 当账号本身没有记录代理时使用的兜底代理 URL
Returns:
CPA 格式的 Token 字典
"""
return {
token_data = {
"type": "codex",
"email": account.email,
"expired": account.expires_at.strftime("%Y-%m-%dT%H:%M:%S+08:00") if account.expires_at else "",
@@ -110,6 +116,12 @@ def generate_token_json(account: Account) -> dict:
"refresh_token": account.refresh_token or "",
}
resolved_proxy_url = (getattr(account, "proxy_used", None) or proxy_url or "").strip()
if include_proxy_url and resolved_proxy_url:
token_data["proxy_url"] = resolved_proxy_url
return token_data
def upload_to_cpa(
token_data: dict,
@@ -185,15 +197,17 @@ def batch_upload_to_cpa(
proxy: str = None,
api_url: str = None,
api_token: str = None,
include_proxy_url: bool = False,
) -> dict:
"""
批量上传账号到 CPA 管理平台
Args:
account_ids: 账号 ID 列表
proxy: 可选的代理 URL
proxy: 可选的代理 URL(用于 auth file proxy_url 的兜底值)
api_url: 指定 CPA API URL优先于全局配置
api_token: 指定 CPA API Token优先于全局配置
include_proxy_url: 是否将账号代理写入 auth file 的 proxy_url 字段
Returns:
包含成功/失败统计和详情的字典
@@ -231,7 +245,11 @@ def batch_upload_to_cpa(
continue
# 生成 Token JSON
token_data = generate_token_json(account)
token_data = generate_token_json(
account,
include_proxy_url=include_proxy_url,
proxy_url=proxy,
)
# 上传
success, message = upload_to_cpa(token_data, proxy, api_url=api_url, api_token=api_token)

View File

@@ -527,6 +527,7 @@ def create_cpa_service(
api_url: str,
api_token: str,
enabled: bool = True,
include_proxy_url: bool = False,
priority: int = 0
) -> CpaService:
"""创建 CPA 服务配置"""
@@ -535,6 +536,7 @@ def create_cpa_service(
api_url=api_url,
api_token=api_token,
enabled=enabled,
include_proxy_url=include_proxy_url,
priority=priority
)
db.add(db_service)

View File

@@ -139,6 +139,7 @@ class CpaService(Base):
api_url = Column(String(500), nullable=False) # API URL
api_token = Column(Text, nullable=False) # API Token
enabled = Column(Boolean, default=True)
include_proxy_url = Column(Boolean, default=False) # 是否将账号代理写入 auth file
priority = Column(Integer, default=0) # 优先级
created_at = Column(DateTime, default=datetime.utcnow)
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)

View File

@@ -111,6 +111,7 @@ class DatabaseSessionManager:
("accounts", "subscription_at", "DATETIME"),
("accounts", "cookies", "TEXT"),
("proxies", "is_default", "BOOLEAN DEFAULT 0"),
("cpa_services", "include_proxy_url", "BOOLEAN DEFAULT 0"),
]
# 确保新表存在create_tables 已处理,此处兜底)

View File

@@ -724,11 +724,12 @@ class BatchCPAUploadRequest(BaseModel):
async def batch_upload_accounts_to_cpa(request: BatchCPAUploadRequest):
"""批量上传账号到 CPA"""
proxy = request.proxy if request.proxy else get_settings().proxy_url
proxy = request.proxy
# 解析指定的 CPA 服务
cpa_api_url = None
cpa_api_token = None
include_proxy_url = False
if request.cpa_service_id:
with get_db() as db:
svc = crud.get_cpa_service_by_id(db, request.cpa_service_id)
@@ -736,6 +737,7 @@ async def batch_upload_accounts_to_cpa(request: BatchCPAUploadRequest):
raise HTTPException(status_code=404, detail="指定的 CPA 服务不存在")
cpa_api_url = svc.api_url
cpa_api_token = svc.api_token
include_proxy_url = bool(svc.include_proxy_url)
with get_db() as db:
ids = resolve_account_ids(
@@ -743,7 +745,13 @@ async def batch_upload_accounts_to_cpa(request: BatchCPAUploadRequest):
request.status_filter, request.email_service_filter, request.search_filter
)
results = batch_upload_to_cpa(ids, proxy, api_url=cpa_api_url, api_token=cpa_api_token)
results = batch_upload_to_cpa(
ids,
proxy,
api_url=cpa_api_url,
api_token=cpa_api_token,
include_proxy_url=include_proxy_url,
)
return results
@@ -751,12 +759,13 @@ async def batch_upload_accounts_to_cpa(request: BatchCPAUploadRequest):
async def upload_account_to_cpa(account_id: int, request: Optional[CPAUploadRequest] = Body(default=None)):
"""上传单个账号到 CPA"""
proxy = request.proxy if request and request.proxy else get_settings().proxy_url
proxy = request.proxy if request else None
cpa_service_id = request.cpa_service_id if request else None
# 解析指定的 CPA 服务
cpa_api_url = None
cpa_api_token = None
include_proxy_url = False
if cpa_service_id:
with get_db() as db:
svc = crud.get_cpa_service_by_id(db, cpa_service_id)
@@ -764,6 +773,7 @@ async def upload_account_to_cpa(account_id: int, request: Optional[CPAUploadRequ
raise HTTPException(status_code=404, detail="指定的 CPA 服务不存在")
cpa_api_url = svc.api_url
cpa_api_token = svc.api_token
include_proxy_url = bool(svc.include_proxy_url)
with get_db() as db:
account = crud.get_account_by_id(db, account_id)
@@ -777,7 +787,11 @@ async def upload_account_to_cpa(account_id: int, request: Optional[CPAUploadRequ
}
# 生成 Token JSON
token_data = generate_token_json(account)
token_data = generate_token_json(
account,
include_proxy_url=include_proxy_url,
proxy_url=proxy,
)
# 上传
success, message = upload_to_cpa(token_data, proxy, api_url=cpa_api_url, api_token=cpa_api_token)

View File

@@ -418,7 +418,6 @@ def _run_sync_registration_task(task_uuid: str, email_service_type: str, proxy:
from ...database.models import Account as AccountModel
saved_account = db.query(AccountModel).filter_by(email=result.email).first()
if saved_account and saved_account.access_token:
token_data = generate_token_json(saved_account)
_cpa_ids = cpa_service_ids or []
if not _cpa_ids:
# 未指定则取所有启用的服务
@@ -430,6 +429,10 @@ def _run_sync_registration_task(task_uuid: str, email_service_type: str, proxy:
_svc = crud.get_cpa_service_by_id(db, _sid)
if not _svc:
continue
token_data = generate_token_json(
saved_account,
include_proxy_url=bool(_svc.include_proxy_url),
)
log_callback(f"[CPA] 上传到服务: {_svc.name}")
_ok, _msg = upload_to_cpa(token_data, api_url=_svc.api_url, api_token=_svc.api_token)
if _ok:

View File

@@ -20,6 +20,7 @@ class CpaServiceCreate(BaseModel):
api_url: str
api_token: str
enabled: bool = True
include_proxy_url: bool = False
priority: int = 0
@@ -28,6 +29,7 @@ class CpaServiceUpdate(BaseModel):
api_url: Optional[str] = None
api_token: Optional[str] = None
enabled: Optional[bool] = None
include_proxy_url: Optional[bool] = None
priority: Optional[int] = None
@@ -37,6 +39,7 @@ class CpaServiceResponse(BaseModel):
api_url: str
has_token: bool
enabled: bool
include_proxy_url: bool
priority: int
created_at: Optional[str] = None
updated_at: Optional[str] = None
@@ -57,6 +60,7 @@ def _to_response(svc) -> CpaServiceResponse:
api_url=svc.api_url,
has_token=bool(svc.api_token),
enabled=svc.enabled,
include_proxy_url=bool(svc.include_proxy_url),
priority=svc.priority,
created_at=svc.created_at.isoformat() if svc.created_at else None,
updated_at=svc.updated_at.isoformat() if svc.updated_at else None,
@@ -83,6 +87,7 @@ async def create_cpa_service(request: CpaServiceCreate):
api_url=request.api_url,
api_token=request.api_token,
enabled=request.enabled,
include_proxy_url=request.include_proxy_url,
priority=request.priority,
)
return _to_response(service)
@@ -111,6 +116,7 @@ async def get_cpa_service_full(service_id: int):
"api_url": service.api_url,
"api_token": service.api_token,
"enabled": service.enabled,
"include_proxy_url": bool(service.include_proxy_url),
"priority": service.priority,
}
@@ -133,6 +139,8 @@ async def update_cpa_service(service_id: int, request: CpaServiceUpdate):
update_data["api_token"] = request.api_token
if request.enabled is not None:
update_data["enabled"] = request.enabled
if request.include_proxy_url is not None:
update_data["include_proxy_url"] = request.include_proxy_url
if request.priority is not None:
update_data["priority"] = request.priority