mirror of
https://github.com/cnlimiter/codex-register.git
synced 2026-06-08 17:10:05 +08:00
fix(team manager): 更改team上传router路径
This commit is contained in:
@@ -7,7 +7,7 @@ import logging
|
|||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from fastapi import APIRouter, HTTPException, Query, BackgroundTasks
|
from fastapi import APIRouter, HTTPException, Query, BackgroundTasks, Body
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
@@ -579,28 +579,6 @@ class BatchValidateRequest(BaseModel):
|
|||||||
search_filter: Optional[str] = None
|
search_filter: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{account_id}/refresh")
|
|
||||||
async def refresh_account_token(account_id: int, request: TokenRefreshRequest = None):
|
|
||||||
"""刷新单个账号的 Token"""
|
|
||||||
from ...core.openai.token_refresh import refresh_account_token as do_refresh
|
|
||||||
|
|
||||||
# 使用传入的代理或全局代理配置
|
|
||||||
proxy = request.proxy if request and request.proxy else get_settings().proxy_url
|
|
||||||
result = do_refresh(account_id, proxy)
|
|
||||||
|
|
||||||
if result.success:
|
|
||||||
return {
|
|
||||||
"success": True,
|
|
||||||
"message": "Token 刷新成功",
|
|
||||||
"expires_at": result.expires_at.isoformat() if result.expires_at else None
|
|
||||||
}
|
|
||||||
else:
|
|
||||||
return {
|
|
||||||
"success": False,
|
|
||||||
"error": result.error_message
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/batch-refresh")
|
@router.post("/batch-refresh")
|
||||||
async def batch_refresh_tokens(request: BatchRefreshRequest, background_tasks: BackgroundTasks):
|
async def batch_refresh_tokens(request: BatchRefreshRequest, background_tasks: BackgroundTasks):
|
||||||
"""批量刷新账号 Token"""
|
"""批量刷新账号 Token"""
|
||||||
@@ -636,20 +614,26 @@ async def batch_refresh_tokens(request: BatchRefreshRequest, background_tasks: B
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{account_id}/validate")
|
@router.post("/{account_id}/refresh")
|
||||||
async def validate_account_token(account_id: int, request: TokenValidateRequest = None):
|
async def refresh_account_token(account_id: int, request: Optional[TokenRefreshRequest] = Body(default=None)):
|
||||||
"""验证单个账号的 Token 有效性"""
|
"""刷新单个账号的 Token"""
|
||||||
from ...core.openai.token_refresh import validate_account_token as do_validate
|
from ...core.openai.token_refresh import refresh_account_token as do_refresh
|
||||||
|
|
||||||
# 使用传入的代理或全局代理配置
|
# 使用传入的代理或全局代理配置
|
||||||
proxy = request.proxy if request and request.proxy else get_settings().proxy_url
|
proxy = request.proxy if request and request.proxy else get_settings().proxy_url
|
||||||
is_valid, error = do_validate(account_id, proxy)
|
result = do_refresh(account_id, proxy)
|
||||||
|
|
||||||
return {
|
if result.success:
|
||||||
"id": account_id,
|
return {
|
||||||
"valid": is_valid,
|
"success": True,
|
||||||
"error": error
|
"message": "Token 刷新成功",
|
||||||
}
|
"expires_at": result.expires_at.isoformat() if result.expires_at else None
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"error": result.error_message
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/batch-validate")
|
@router.post("/batch-validate")
|
||||||
@@ -695,6 +679,22 @@ async def batch_validate_tokens(request: BatchValidateRequest):
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{account_id}/validate")
|
||||||
|
async def validate_account_token(account_id: int, request: Optional[TokenValidateRequest] = Body(default=None)):
|
||||||
|
"""验证单个账号的 Token 有效性"""
|
||||||
|
from ...core.openai.token_refresh import validate_account_token as do_validate
|
||||||
|
|
||||||
|
# 使用传入的代理或全局代理配置
|
||||||
|
proxy = request.proxy if request and request.proxy else get_settings().proxy_url
|
||||||
|
is_valid, error = do_validate(account_id, proxy)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id": account_id,
|
||||||
|
"valid": is_valid,
|
||||||
|
"error": error
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# ============== CPA 上传相关 ==============
|
# ============== CPA 上传相关 ==============
|
||||||
|
|
||||||
class CPAUploadRequest(BaseModel):
|
class CPAUploadRequest(BaseModel):
|
||||||
@@ -714,8 +714,36 @@ class BatchCPAUploadRequest(BaseModel):
|
|||||||
cpa_service_id: Optional[int] = None # 指定 CPA 服务 ID,不传则使用全局配置
|
cpa_service_id: Optional[int] = None # 指定 CPA 服务 ID,不传则使用全局配置
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/batch-upload-cpa")
|
||||||
|
async def batch_upload_accounts_to_cpa(request: BatchCPAUploadRequest):
|
||||||
|
"""批量上传账号到 CPA"""
|
||||||
|
from ...core.upload.cpa_upload import batch_upload_to_cpa
|
||||||
|
|
||||||
|
proxy = request.proxy if request.proxy else get_settings().proxy_url
|
||||||
|
|
||||||
|
# 解析指定的 CPA 服务
|
||||||
|
cpa_api_url = None
|
||||||
|
cpa_api_token = None
|
||||||
|
if request.cpa_service_id:
|
||||||
|
with get_db() as db:
|
||||||
|
svc = crud.get_cpa_service_by_id(db, request.cpa_service_id)
|
||||||
|
if not svc:
|
||||||
|
raise HTTPException(status_code=404, detail="指定的 CPA 服务不存在")
|
||||||
|
cpa_api_url = svc.api_url
|
||||||
|
cpa_api_token = svc.api_token
|
||||||
|
|
||||||
|
with get_db() as db:
|
||||||
|
ids = resolve_account_ids(
|
||||||
|
db, request.ids, request.select_all,
|
||||||
|
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)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{account_id}/upload-cpa")
|
@router.post("/{account_id}/upload-cpa")
|
||||||
async def upload_account_to_cpa(account_id: int, request: CPAUploadRequest = None):
|
async def upload_account_to_cpa(account_id: int, request: Optional[CPAUploadRequest] = Body(default=None)):
|
||||||
"""上传单个账号到 CPA"""
|
"""上传单个账号到 CPA"""
|
||||||
from ...core.upload.cpa_upload import upload_to_cpa, generate_token_json
|
from ...core.upload.cpa_upload import upload_to_cpa, generate_token_json
|
||||||
|
|
||||||
@@ -759,34 +787,6 @@ async def upload_account_to_cpa(account_id: int, request: CPAUploadRequest = Non
|
|||||||
return {"success": False, "error": message}
|
return {"success": False, "error": message}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/batch-upload-cpa")
|
|
||||||
async def batch_upload_accounts_to_cpa(request: BatchCPAUploadRequest):
|
|
||||||
"""批量上传账号到 CPA"""
|
|
||||||
from ...core.upload.cpa_upload import batch_upload_to_cpa
|
|
||||||
|
|
||||||
proxy = request.proxy if request.proxy else get_settings().proxy_url
|
|
||||||
|
|
||||||
# 解析指定的 CPA 服务
|
|
||||||
cpa_api_url = None
|
|
||||||
cpa_api_token = None
|
|
||||||
if request.cpa_service_id:
|
|
||||||
with get_db() as db:
|
|
||||||
svc = crud.get_cpa_service_by_id(db, request.cpa_service_id)
|
|
||||||
if not svc:
|
|
||||||
raise HTTPException(status_code=404, detail="指定的 CPA 服务不存在")
|
|
||||||
cpa_api_url = svc.api_url
|
|
||||||
cpa_api_token = svc.api_token
|
|
||||||
|
|
||||||
with get_db() as db:
|
|
||||||
ids = resolve_account_ids(
|
|
||||||
db, request.ids, request.select_all,
|
|
||||||
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)
|
|
||||||
return results
|
|
||||||
|
|
||||||
|
|
||||||
class Sub2ApiUploadRequest(BaseModel):
|
class Sub2ApiUploadRequest(BaseModel):
|
||||||
"""单账号 Sub2API 上传请求"""
|
"""单账号 Sub2API 上传请求"""
|
||||||
service_id: Optional[int] = None
|
service_id: Optional[int] = None
|
||||||
@@ -794,51 +794,6 @@ class Sub2ApiUploadRequest(BaseModel):
|
|||||||
priority: int = 50
|
priority: int = 50
|
||||||
|
|
||||||
|
|
||||||
@router.post("/{account_id}/upload-sub2api")
|
|
||||||
async def upload_account_to_sub2api(account_id: int, request: Sub2ApiUploadRequest = None):
|
|
||||||
"""上传单个账号到 Sub2API"""
|
|
||||||
from ...core.upload.sub2api_upload import upload_to_sub2api
|
|
||||||
|
|
||||||
service_id = request.service_id if request else None
|
|
||||||
concurrency = request.concurrency if request else 3
|
|
||||||
priority = request.priority if request else 50
|
|
||||||
|
|
||||||
api_url = None
|
|
||||||
api_key = None
|
|
||||||
if service_id:
|
|
||||||
with get_db() as db:
|
|
||||||
svc = crud.get_sub2api_service_by_id(db, service_id)
|
|
||||||
if not svc:
|
|
||||||
raise HTTPException(status_code=404, detail="指定的 Sub2API 服务不存在")
|
|
||||||
api_url = svc.api_url
|
|
||||||
api_key = svc.api_key
|
|
||||||
else:
|
|
||||||
with get_db() as db:
|
|
||||||
svcs = crud.get_sub2api_services(db, enabled=True)
|
|
||||||
if svcs:
|
|
||||||
api_url = svcs[0].api_url
|
|
||||||
api_key = svcs[0].api_key
|
|
||||||
|
|
||||||
if not api_url or not api_key:
|
|
||||||
raise HTTPException(status_code=400, detail="未找到可用的 Sub2API 服务,请先在设置中配置")
|
|
||||||
|
|
||||||
with get_db() as db:
|
|
||||||
account = crud.get_account_by_id(db, account_id)
|
|
||||||
if not account:
|
|
||||||
raise HTTPException(status_code=404, detail="账号不存在")
|
|
||||||
if not account.access_token:
|
|
||||||
return {"success": False, "error": "账号缺少 Token,无法上传"}
|
|
||||||
|
|
||||||
success, message = upload_to_sub2api(
|
|
||||||
[account], api_url, api_key,
|
|
||||||
concurrency=concurrency, priority=priority
|
|
||||||
)
|
|
||||||
if success:
|
|
||||||
return {"success": True, "message": message}
|
|
||||||
else:
|
|
||||||
return {"success": False, "error": message}
|
|
||||||
|
|
||||||
|
|
||||||
class BatchSub2ApiUploadRequest(BaseModel):
|
class BatchSub2ApiUploadRequest(BaseModel):
|
||||||
"""批量 Sub2API 上传请求"""
|
"""批量 Sub2API 上传请求"""
|
||||||
ids: List[int] = []
|
ids: List[int] = []
|
||||||
@@ -888,3 +843,118 @@ async def batch_upload_accounts_to_sub2api(request: BatchSub2ApiUploadRequest):
|
|||||||
priority=request.priority,
|
priority=request.priority,
|
||||||
)
|
)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{account_id}/upload-sub2api")
|
||||||
|
async def upload_account_to_sub2api(account_id: int, request: Optional[Sub2ApiUploadRequest] = Body(default=None)):
|
||||||
|
"""上传单个账号到 Sub2API"""
|
||||||
|
from ...core.upload.sub2api_upload import upload_to_sub2api
|
||||||
|
|
||||||
|
service_id = request.service_id if request else None
|
||||||
|
concurrency = request.concurrency if request else 3
|
||||||
|
priority = request.priority if request else 50
|
||||||
|
|
||||||
|
api_url = None
|
||||||
|
api_key = None
|
||||||
|
if service_id:
|
||||||
|
with get_db() as db:
|
||||||
|
svc = crud.get_sub2api_service_by_id(db, service_id)
|
||||||
|
if not svc:
|
||||||
|
raise HTTPException(status_code=404, detail="指定的 Sub2API 服务不存在")
|
||||||
|
api_url = svc.api_url
|
||||||
|
api_key = svc.api_key
|
||||||
|
else:
|
||||||
|
with get_db() as db:
|
||||||
|
svcs = crud.get_sub2api_services(db, enabled=True)
|
||||||
|
if svcs:
|
||||||
|
api_url = svcs[0].api_url
|
||||||
|
api_key = svcs[0].api_key
|
||||||
|
|
||||||
|
if not api_url or not api_key:
|
||||||
|
raise HTTPException(status_code=400, detail="未找到可用的 Sub2API 服务,请先在设置中配置")
|
||||||
|
|
||||||
|
with get_db() as db:
|
||||||
|
account = crud.get_account_by_id(db, account_id)
|
||||||
|
if not account:
|
||||||
|
raise HTTPException(status_code=404, detail="账号不存在")
|
||||||
|
if not account.access_token:
|
||||||
|
return {"success": False, "error": "账号缺少 Token,无法上传"}
|
||||||
|
|
||||||
|
success, message = upload_to_sub2api(
|
||||||
|
[account], api_url, api_key,
|
||||||
|
concurrency=concurrency, priority=priority
|
||||||
|
)
|
||||||
|
if success:
|
||||||
|
return {"success": True, "message": message}
|
||||||
|
else:
|
||||||
|
return {"success": False, "error": message}
|
||||||
|
|
||||||
|
|
||||||
|
# ============== Team Manager 上传 ==============
|
||||||
|
|
||||||
|
class UploadTMRequest(BaseModel):
|
||||||
|
service_id: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
class BatchUploadTMRequest(BaseModel):
|
||||||
|
ids: List[int] = []
|
||||||
|
select_all: bool = False
|
||||||
|
status_filter: Optional[str] = None
|
||||||
|
email_service_filter: Optional[str] = None
|
||||||
|
search_filter: Optional[str] = None
|
||||||
|
service_id: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/batch-upload-tm")
|
||||||
|
async def batch_upload_accounts_to_tm(request: BatchUploadTMRequest):
|
||||||
|
"""批量上传账号到 Team Manager"""
|
||||||
|
from ...core.upload.team_manager_upload import batch_upload_to_team_manager
|
||||||
|
|
||||||
|
with get_db() as db:
|
||||||
|
if request.service_id:
|
||||||
|
svc = crud.get_tm_service_by_id(db, request.service_id)
|
||||||
|
else:
|
||||||
|
svcs = crud.get_tm_services(db, enabled=True)
|
||||||
|
svc = svcs[0] if svcs else None
|
||||||
|
|
||||||
|
if not svc:
|
||||||
|
raise HTTPException(status_code=400, detail="未找到可用的 Team Manager 服务,请先在设置中配置")
|
||||||
|
|
||||||
|
api_url = svc.api_url
|
||||||
|
api_key = svc.api_key
|
||||||
|
|
||||||
|
ids = resolve_account_ids(
|
||||||
|
db, request.ids, request.select_all,
|
||||||
|
request.status_filter, request.email_service_filter, request.search_filter
|
||||||
|
)
|
||||||
|
|
||||||
|
results = batch_upload_to_team_manager(ids, api_url, api_key)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/{account_id}/upload-tm")
|
||||||
|
async def upload_account_to_tm(account_id: int, request: Optional[UploadTMRequest] = Body(default=None)):
|
||||||
|
"""上传单账号到 Team Manager"""
|
||||||
|
from ...core.upload.team_manager_upload import upload_to_team_manager
|
||||||
|
|
||||||
|
service_id = request.service_id if request else None
|
||||||
|
|
||||||
|
with get_db() as db:
|
||||||
|
if service_id:
|
||||||
|
svc = crud.get_tm_service_by_id(db, service_id)
|
||||||
|
else:
|
||||||
|
svcs = crud.get_tm_services(db, enabled=True)
|
||||||
|
svc = svcs[0] if svcs else None
|
||||||
|
|
||||||
|
if not svc:
|
||||||
|
raise HTTPException(status_code=400, detail="未找到可用的 Team Manager 服务,请先在设置中配置")
|
||||||
|
|
||||||
|
api_url = svc.api_url
|
||||||
|
api_key = svc.api_key
|
||||||
|
|
||||||
|
account = crud.get_account_by_id(db, account_id)
|
||||||
|
if not account:
|
||||||
|
raise HTTPException(status_code=404, detail="账号不存在")
|
||||||
|
success, message = upload_to_team_manager(account, api_url, api_key)
|
||||||
|
|
||||||
|
return {"success": success, "message": message}
|
||||||
|
|||||||
@@ -20,10 +20,6 @@ from ...core.openai.payment import (
|
|||||||
open_url_incognito,
|
open_url_incognito,
|
||||||
check_subscription_status,
|
check_subscription_status,
|
||||||
)
|
)
|
||||||
from ...core.upload.team_manager_upload import (
|
|
||||||
upload_to_team_manager,
|
|
||||||
batch_upload_to_team_manager,
|
|
||||||
)
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
@@ -60,20 +56,6 @@ class BatchCheckSubscriptionRequest(BaseModel):
|
|||||||
search_filter: Optional[str] = None
|
search_filter: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
class UploadTMRequest(BaseModel):
|
|
||||||
proxy: Optional[str] = None # 保留,TM 上传不走代理
|
|
||||||
service_id: Optional[int] = None # 指定 TM 服务 ID,不传则使用第一个启用的
|
|
||||||
|
|
||||||
|
|
||||||
class BatchUploadTMRequest(BaseModel):
|
|
||||||
ids: List[int] = []
|
|
||||||
select_all: bool = False
|
|
||||||
status_filter: Optional[str] = None
|
|
||||||
service_id: Optional[int] = None # 指定 TM 服务 ID,不传则使用第一个启用的
|
|
||||||
email_service_filter: Optional[str] = None
|
|
||||||
search_filter: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
# ============== 支付链接生成 ==============
|
# ============== 支付链接生成 ==============
|
||||||
|
|
||||||
@router.post("/generate-link")
|
@router.post("/generate-link")
|
||||||
@@ -140,25 +122,6 @@ def open_browser_incognito(request: OpenIncognitoRequest):
|
|||||||
|
|
||||||
# ============== 订阅状态 ==============
|
# ============== 订阅状态 ==============
|
||||||
|
|
||||||
@router.post("/accounts/{account_id}/mark-subscription")
|
|
||||||
def mark_subscription(account_id: int, request: MarkSubscriptionRequest):
|
|
||||||
"""手动标记账号订阅类型"""
|
|
||||||
allowed = ("free", "plus", "team")
|
|
||||||
if request.subscription_type not in allowed:
|
|
||||||
raise HTTPException(status_code=400, detail=f"subscription_type 必须为 {allowed}")
|
|
||||||
|
|
||||||
with get_db() as db:
|
|
||||||
account = db.query(Account).filter(Account.id == account_id).first()
|
|
||||||
if not account:
|
|
||||||
raise HTTPException(status_code=404, detail="账号不存在")
|
|
||||||
|
|
||||||
account.subscription_type = None if request.subscription_type == "free" else request.subscription_type
|
|
||||||
account.subscription_at = datetime.utcnow() if request.subscription_type != "free" else None
|
|
||||||
db.commit()
|
|
||||||
|
|
||||||
return {"success": True, "subscription_type": request.subscription_type}
|
|
||||||
|
|
||||||
|
|
||||||
@router.post("/accounts/batch-check-subscription")
|
@router.post("/accounts/batch-check-subscription")
|
||||||
def batch_check_subscription(request: BatchCheckSubscriptionRequest):
|
def batch_check_subscription(request: BatchCheckSubscriptionRequest):
|
||||||
"""批量检测账号订阅状态"""
|
"""批量检测账号订阅状态"""
|
||||||
@@ -198,56 +161,22 @@ def batch_check_subscription(request: BatchCheckSubscriptionRequest):
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
# ============== Team Manager 上传 ==============
|
@router.post("/accounts/{account_id}/mark-subscription")
|
||||||
|
def mark_subscription(account_id: int, request: MarkSubscriptionRequest):
|
||||||
@router.post("/accounts/{account_id}/upload-tm")
|
"""手动标记账号订阅类型"""
|
||||||
def upload_account_tm(account_id: int, request: UploadTMRequest = None):
|
allowed = ("free", "plus", "team")
|
||||||
"""上传单账号到 Team Manager"""
|
if request.subscription_type not in allowed:
|
||||||
service_id = request.service_id if request and hasattr(request, 'service_id') else None
|
raise HTTPException(status_code=400, detail=f"subscription_type 必须为 {allowed}")
|
||||||
|
|
||||||
with get_db() as db:
|
with get_db() as db:
|
||||||
if service_id:
|
|
||||||
svc = crud.get_tm_service_by_id(db, service_id)
|
|
||||||
else:
|
|
||||||
svcs = crud.get_tm_services(db, enabled=True)
|
|
||||||
svc = svcs[0] if svcs else None
|
|
||||||
|
|
||||||
if not svc:
|
|
||||||
raise HTTPException(status_code=400, detail="未找到可用的 Team Manager 服务,请先在设置中配置")
|
|
||||||
|
|
||||||
api_url = svc.api_url
|
|
||||||
api_key = svc.api_key
|
|
||||||
|
|
||||||
account = db.query(Account).filter(Account.id == account_id).first()
|
account = db.query(Account).filter(Account.id == account_id).first()
|
||||||
if not account:
|
if not account:
|
||||||
raise HTTPException(status_code=404, detail="账号不存在")
|
raise HTTPException(status_code=404, detail="账号不存在")
|
||||||
success, message = upload_to_team_manager(account, api_url, api_key)
|
|
||||||
|
|
||||||
return {"success": success, "message": message}
|
account.subscription_type = None if request.subscription_type == "free" else request.subscription_type
|
||||||
|
account.subscription_at = datetime.utcnow() if request.subscription_type != "free" else None
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
return {"success": True, "subscription_type": request.subscription_type}
|
||||||
|
|
||||||
|
|
||||||
@router.post("/accounts/batch-upload-tm")
|
|
||||||
def batch_upload_tm(request: BatchUploadTMRequest):
|
|
||||||
"""批量上传账号到 Team Manager"""
|
|
||||||
service_id = request.service_id if hasattr(request, 'service_id') else None
|
|
||||||
|
|
||||||
with get_db() as db:
|
|
||||||
if service_id:
|
|
||||||
svc = crud.get_tm_service_by_id(db, service_id)
|
|
||||||
else:
|
|
||||||
svcs = crud.get_tm_services(db, enabled=True)
|
|
||||||
svc = svcs[0] if svcs else None
|
|
||||||
|
|
||||||
if not svc:
|
|
||||||
raise HTTPException(status_code=400, detail="未找到可用的 Team Manager 服务,请先在设置中配置")
|
|
||||||
|
|
||||||
api_url = svc.api_url
|
|
||||||
api_key = svc.api_key
|
|
||||||
|
|
||||||
ids = resolve_account_ids(
|
|
||||||
db, request.ids, request.select_all,
|
|
||||||
request.status_filter, request.email_service_filter, request.search_filter
|
|
||||||
)
|
|
||||||
|
|
||||||
results = batch_upload_to_team_manager(ids, api_url, api_key)
|
|
||||||
return results
|
|
||||||
|
|||||||
@@ -1075,11 +1075,82 @@ async function uploadToSub2Api(id) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 弹出 Team Manager 服务选择框,返回 Promise<{service_id: number|null}|null>
|
||||||
|
// null 表示用户取消,{service_id: null} 表示自动选择
|
||||||
|
function selectTmService() {
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
const modal = document.getElementById('tm-service-modal');
|
||||||
|
const listEl = document.getElementById('tm-service-list');
|
||||||
|
const closeBtn = document.getElementById('close-tm-modal');
|
||||||
|
const cancelBtn = document.getElementById('cancel-tm-modal-btn');
|
||||||
|
const autoBtn = document.getElementById('tm-use-auto-btn');
|
||||||
|
|
||||||
|
listEl.innerHTML = '<div style="text-align:center;color:var(--text-muted)">加载中...</div>';
|
||||||
|
modal.classList.add('active');
|
||||||
|
|
||||||
|
let services = [];
|
||||||
|
try {
|
||||||
|
services = await api.get('/tm-services?enabled=true');
|
||||||
|
} catch (e) {
|
||||||
|
services = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (services.length === 0) {
|
||||||
|
listEl.innerHTML = '<div style="text-align:center;color:var(--text-muted);padding:12px;">暂无已启用的 Team Manager 服务,将自动选择第一个</div>';
|
||||||
|
} else {
|
||||||
|
listEl.innerHTML = services.map(s => `
|
||||||
|
<div class="tm-service-item" data-id="${s.id}" style="
|
||||||
|
padding: 10px 14px;
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
">
|
||||||
|
<div>
|
||||||
|
<div style="font-weight:500;">${escapeHtml(s.name)}</div>
|
||||||
|
<div style="font-size:0.8rem;color:var(--text-muted);">${escapeHtml(s.api_url)}</div>
|
||||||
|
</div>
|
||||||
|
<span class="badge" style="background:var(--primary);color:#fff;font-size:0.7rem;padding:2px 8px;border-radius:10px;">选择</span>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
|
||||||
|
listEl.querySelectorAll('.tm-service-item').forEach(item => {
|
||||||
|
item.addEventListener('mouseenter', () => item.style.background = 'var(--surface-hover)');
|
||||||
|
item.addEventListener('mouseleave', () => item.style.background = '');
|
||||||
|
item.addEventListener('click', () => {
|
||||||
|
cleanup();
|
||||||
|
resolve({ service_id: parseInt(item.dataset.id) });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
modal.classList.remove('active');
|
||||||
|
closeBtn.removeEventListener('click', onCancel);
|
||||||
|
cancelBtn.removeEventListener('click', onCancel);
|
||||||
|
autoBtn.removeEventListener('click', onAuto);
|
||||||
|
}
|
||||||
|
function onCancel() { cleanup(); resolve(null); }
|
||||||
|
function onAuto() { cleanup(); resolve({ service_id: null }); }
|
||||||
|
|
||||||
|
closeBtn.addEventListener('click', onCancel);
|
||||||
|
cancelBtn.addEventListener('click', onCancel);
|
||||||
|
autoBtn.addEventListener('click', onAuto);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// 上传单账号到 Team Manager
|
// 上传单账号到 Team Manager
|
||||||
async function uploadToTm(id) {
|
async function uploadToTm(id) {
|
||||||
|
const choice = await selectTmService();
|
||||||
|
if (choice === null) return;
|
||||||
try {
|
try {
|
||||||
toast.info('正在上传到 Team Manager...');
|
toast.info('正在上传到 Team Manager...');
|
||||||
const result = await api.post(`/payment/accounts/${id}/upload-tm`);
|
const payload = {};
|
||||||
|
if (choice.service_id != null) payload.service_id = choice.service_id;
|
||||||
|
const result = await api.post(`/accounts/${id}/upload-tm`, payload);
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
toast.success('上传成功');
|
toast.success('上传成功');
|
||||||
} else {
|
} else {
|
||||||
@@ -1094,6 +1165,10 @@ async function uploadToTm(id) {
|
|||||||
async function handleBatchUploadTm() {
|
async function handleBatchUploadTm() {
|
||||||
const count = getEffectiveCount();
|
const count = getEffectiveCount();
|
||||||
if (count === 0) return;
|
if (count === 0) return;
|
||||||
|
|
||||||
|
const choice = await selectTmService();
|
||||||
|
if (choice === null) return; // 用户取消
|
||||||
|
|
||||||
const confirmed = await confirm(`确定要将选中的 ${count} 个账号上传到 Team Manager 吗?`);
|
const confirmed = await confirm(`确定要将选中的 ${count} 个账号上传到 Team Manager 吗?`);
|
||||||
if (!confirmed) return;
|
if (!confirmed) return;
|
||||||
|
|
||||||
@@ -1101,7 +1176,9 @@ async function handleBatchUploadTm() {
|
|||||||
elements.batchUploadBtn.textContent = '上传中...';
|
elements.batchUploadBtn.textContent = '上传中...';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await api.post('/payment/accounts/batch-upload-tm', buildBatchPayload());
|
const payload = buildBatchPayload();
|
||||||
|
if (choice.service_id != null) payload.service_id = choice.service_id;
|
||||||
|
const result = await api.post('/accounts/batch-upload-tm', payload);
|
||||||
let message = `成功: ${result.success_count}`;
|
let message = `成功: ${result.success_count}`;
|
||||||
if (result.failed_count > 0) message += `, 失败: ${result.failed_count}`;
|
if (result.failed_count > 0) message += `, 失败: ${result.failed_count}`;
|
||||||
if (result.skipped_count > 0) message += `, 跳过: ${result.skipped_count}`;
|
if (result.skipped_count > 0) message += `, 跳过: ${result.skipped_count}`;
|
||||||
|
|||||||
@@ -237,6 +237,26 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Team Manager 服务选择模态框 -->
|
||||||
|
<div class="modal" id="tm-service-modal">
|
||||||
|
<div class="modal-content" style="max-width: 480px;">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3>🚀 选择 Team Manager 服务</h3>
|
||||||
|
<button class="modal-close" id="close-tm-modal">×</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<p style="color: var(--text-muted); margin-bottom: 12px; font-size: 0.9rem;">选择要上传到的 Team Manager 服务,或自动选择第一个启用的服务。</p>
|
||||||
|
<div id="tm-service-list" style="display: flex; flex-direction: column; gap: 8px; max-height: 300px; overflow-y: auto;">
|
||||||
|
<div style="text-align: center; color: var(--text-muted);">加载中...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer" style="padding: 12px 20px; border-top: 1px solid var(--border); display: flex; gap: 8px; justify-content: flex-end;">
|
||||||
|
<button class="btn btn-secondary" id="tm-use-auto-btn">自动选择</button>
|
||||||
|
<button class="btn btn-secondary" id="cancel-tm-modal-btn">取消</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Sub2API 服务选择模态框 -->
|
<!-- Sub2API 服务选择模态框 -->
|
||||||
<div class="modal" id="sub2api-service-modal">
|
<div class="modal" id="sub2api-service-modal">
|
||||||
<div class="modal-content" style="max-width: 480px;">
|
<div class="modal-content" style="max-width: 480px;">
|
||||||
|
|||||||
Reference in New Issue
Block a user