mirror of
https://github.com/TheSmallHanCat/sora2api.git
synced 2026-02-14 01:54:41 +08:00
feat: 为token支持独立client_id绑定
This commit is contained in:
@@ -62,6 +62,7 @@ class AddTokenRequest(BaseModel):
|
||||
token: str # Access Token (AT)
|
||||
st: Optional[str] = None # Session Token (optional, for storage)
|
||||
rt: Optional[str] = None # Refresh Token (optional, for storage)
|
||||
client_id: Optional[str] = None # Client ID (optional)
|
||||
remark: Optional[str] = None
|
||||
image_enabled: bool = True # Enable image generation
|
||||
video_enabled: bool = True # Enable video generation
|
||||
@@ -81,6 +82,7 @@ class UpdateTokenRequest(BaseModel):
|
||||
token: Optional[str] = None # Access Token
|
||||
st: Optional[str] = None
|
||||
rt: Optional[str] = None
|
||||
client_id: Optional[str] = None # Client ID
|
||||
remark: Optional[str] = None
|
||||
image_enabled: Optional[bool] = None # Enable image generation
|
||||
video_enabled: Optional[bool] = None # Enable video generation
|
||||
@@ -169,6 +171,7 @@ async def get_tokens(token: str = Depends(verify_admin_token)) -> List[dict]:
|
||||
"token": token.token, # 完整的Access Token
|
||||
"st": token.st, # 完整的Session Token
|
||||
"rt": token.rt, # 完整的Refresh Token
|
||||
"client_id": token.client_id, # Client ID
|
||||
"email": token.email,
|
||||
"name": token.name,
|
||||
"remark": token.remark,
|
||||
@@ -210,6 +213,7 @@ async def add_token(request: AddTokenRequest, token: str = Depends(verify_admin_
|
||||
token_value=request.token,
|
||||
st=request.st,
|
||||
rt=request.rt,
|
||||
client_id=request.client_id,
|
||||
remark=request.remark,
|
||||
update_if_exists=False,
|
||||
image_enabled=request.image_enabled,
|
||||
@@ -412,6 +416,7 @@ async def update_token(
|
||||
token=request.token,
|
||||
st=request.st,
|
||||
rt=request.rt,
|
||||
client_id=request.client_id,
|
||||
remark=request.remark,
|
||||
image_enabled=request.image_enabled,
|
||||
video_enabled=request.video_enabled,
|
||||
|
||||
@@ -194,6 +194,7 @@ class Database:
|
||||
("video_enabled", "BOOLEAN DEFAULT 1"),
|
||||
("image_concurrency", "INTEGER DEFAULT -1"),
|
||||
("video_concurrency", "INTEGER DEFAULT -1"),
|
||||
("client_id", "TEXT"),
|
||||
]
|
||||
|
||||
for col_name, col_type in columns_to_add:
|
||||
@@ -269,6 +270,7 @@ class Database:
|
||||
name TEXT NOT NULL,
|
||||
st TEXT,
|
||||
rt TEXT,
|
||||
client_id TEXT,
|
||||
remark TEXT,
|
||||
expiry_time TIMESTAMP,
|
||||
is_active BOOLEAN DEFAULT 1,
|
||||
@@ -561,12 +563,12 @@ class Database:
|
||||
"""Add a new token"""
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
cursor = await db.execute("""
|
||||
INSERT INTO tokens (token, email, username, name, st, rt, remark, expiry_time, is_active,
|
||||
INSERT INTO tokens (token, email, username, name, st, rt, client_id, remark, expiry_time, is_active,
|
||||
plan_type, plan_title, subscription_end, sora2_supported, sora2_invite_code,
|
||||
sora2_redeemed_count, sora2_total_count, sora2_remaining_count, sora2_cooldown_until,
|
||||
image_enabled, video_enabled, image_concurrency, video_concurrency)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""", (token.token, token.email, "", token.name, token.st, token.rt,
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""", (token.token, token.email, "", token.name, token.st, token.rt, token.client_id,
|
||||
token.remark, token.expiry_time, token.is_active,
|
||||
token.plan_type, token.plan_title, token.subscription_end,
|
||||
token.sora2_supported, token.sora2_invite_code,
|
||||
@@ -701,6 +703,7 @@ class Database:
|
||||
token: Optional[str] = None,
|
||||
st: Optional[str] = None,
|
||||
rt: Optional[str] = None,
|
||||
client_id: Optional[str] = None,
|
||||
remark: Optional[str] = None,
|
||||
expiry_time: Optional[datetime] = None,
|
||||
plan_type: Optional[str] = None,
|
||||
@@ -710,7 +713,7 @@ class Database:
|
||||
video_enabled: Optional[bool] = None,
|
||||
image_concurrency: Optional[int] = None,
|
||||
video_concurrency: Optional[int] = None):
|
||||
"""Update token (AT, ST, RT, remark, expiry_time, subscription info, image_enabled, video_enabled)"""
|
||||
"""Update token (AT, ST, RT, client_id, remark, expiry_time, subscription info, image_enabled, video_enabled)"""
|
||||
async with aiosqlite.connect(self.db_path) as db:
|
||||
# Build dynamic update query
|
||||
updates = []
|
||||
@@ -728,6 +731,10 @@ class Database:
|
||||
updates.append("rt = ?")
|
||||
params.append(rt)
|
||||
|
||||
if client_id is not None:
|
||||
updates.append("client_id = ?")
|
||||
params.append(client_id)
|
||||
|
||||
if remark is not None:
|
||||
updates.append("remark = ?")
|
||||
params.append(remark)
|
||||
|
||||
@@ -11,6 +11,7 @@ class Token(BaseModel):
|
||||
name: Optional[str] = ""
|
||||
st: Optional[str] = None
|
||||
rt: Optional[str] = None
|
||||
client_id: Optional[str] = None
|
||||
remark: Optional[str] = None
|
||||
expiry_time: Optional[datetime] = None
|
||||
is_active: bool = True
|
||||
|
||||
@@ -513,9 +513,18 @@ class TokenManager:
|
||||
debug_logger.log_info(f"[ST_TO_AT] 🔴 异常: {str(e)}")
|
||||
raise
|
||||
|
||||
async def rt_to_at(self, refresh_token: str) -> dict:
|
||||
"""Convert Refresh Token to Access Token"""
|
||||
async def rt_to_at(self, refresh_token: str, client_id: Optional[str] = None) -> dict:
|
||||
"""Convert Refresh Token to Access Token
|
||||
|
||||
Args:
|
||||
refresh_token: Refresh Token
|
||||
client_id: Client ID (optional, uses default if not provided)
|
||||
"""
|
||||
# Use provided client_id or default
|
||||
effective_client_id = client_id or "app_LlGpXReQgckcGGUo2JrYvtJK"
|
||||
|
||||
debug_logger.log_info(f"[RT_TO_AT] 开始转换 Refresh Token 为 Access Token...")
|
||||
debug_logger.log_info(f"[RT_TO_AT] 使用 Client ID: {effective_client_id[:20]}...")
|
||||
proxy_url = await self.proxy_manager.get_proxy_url()
|
||||
|
||||
async with AsyncSession() as session:
|
||||
@@ -527,7 +536,7 @@ class TokenManager:
|
||||
kwargs = {
|
||||
"headers": headers,
|
||||
"json": {
|
||||
"client_id": "app_LlGpXReQgckcGGUo2JrYvtJK",
|
||||
"client_id": effective_client_id,
|
||||
"grant_type": "refresh_token",
|
||||
"redirect_uri": "com.openai.chat://auth0.openai.com/ios/com.openai.chat/callback",
|
||||
"refresh_token": refresh_token
|
||||
@@ -600,6 +609,7 @@ class TokenManager:
|
||||
async def add_token(self, token_value: str,
|
||||
st: Optional[str] = None,
|
||||
rt: Optional[str] = None,
|
||||
client_id: Optional[str] = None,
|
||||
remark: Optional[str] = None,
|
||||
update_if_exists: bool = False,
|
||||
image_enabled: bool = True,
|
||||
@@ -612,6 +622,7 @@ class TokenManager:
|
||||
token_value: Access Token
|
||||
st: Session Token (optional)
|
||||
rt: Refresh Token (optional)
|
||||
client_id: Client ID (optional)
|
||||
remark: Remark (optional)
|
||||
update_if_exists: If True, update existing token instead of raising error
|
||||
image_enabled: Enable image generation (default: True)
|
||||
@@ -747,6 +758,7 @@ class TokenManager:
|
||||
name=name,
|
||||
st=st,
|
||||
rt=rt,
|
||||
client_id=client_id,
|
||||
remark=remark,
|
||||
expiry_time=expiry_time,
|
||||
is_active=True,
|
||||
@@ -831,12 +843,13 @@ class TokenManager:
|
||||
token: Optional[str] = None,
|
||||
st: Optional[str] = None,
|
||||
rt: Optional[str] = None,
|
||||
client_id: Optional[str] = None,
|
||||
remark: Optional[str] = None,
|
||||
image_enabled: Optional[bool] = None,
|
||||
video_enabled: Optional[bool] = None,
|
||||
image_concurrency: Optional[int] = None,
|
||||
video_concurrency: Optional[int] = None):
|
||||
"""Update token (AT, ST, RT, remark, image_enabled, video_enabled, concurrency limits)"""
|
||||
"""Update token (AT, ST, RT, client_id, remark, image_enabled, video_enabled, concurrency limits)"""
|
||||
# If token (AT) is updated, decode JWT to get new expiry time
|
||||
expiry_time = None
|
||||
if token:
|
||||
@@ -846,7 +859,7 @@ class TokenManager:
|
||||
except Exception:
|
||||
pass # If JWT decode fails, keep expiry_time as None
|
||||
|
||||
await self.db.update_token(token_id, token=token, st=st, rt=rt, remark=remark, expiry_time=expiry_time,
|
||||
await self.db.update_token(token_id, token=token, st=st, rt=rt, client_id=client_id, remark=remark, expiry_time=expiry_time,
|
||||
image_enabled=image_enabled, video_enabled=video_enabled,
|
||||
image_concurrency=image_concurrency, video_concurrency=video_concurrency)
|
||||
|
||||
@@ -1063,7 +1076,7 @@ class TokenManager:
|
||||
if not new_at and token_data.rt:
|
||||
try:
|
||||
debug_logger.log_info(f"[AUTO_REFRESH] 📝 Token {token_id}: 尝试使用 RT 刷新...")
|
||||
result = await self.rt_to_at(token_data.rt)
|
||||
result = await self.rt_to_at(token_data.rt, client_id=token_data.client_id)
|
||||
new_at = result.get("access_token")
|
||||
new_rt = result.get("refresh_token", token_data.rt) # RT might be updated
|
||||
refresh_method = "RT"
|
||||
|
||||
Reference in New Issue
Block a user