fix(security): Tailscale内网(100.64/10)免API_KEY+外部强制校验;修复账号重复提交400(提交锁+唯一约束兜底)
This commit is contained in:
@@ -9,6 +9,7 @@ from typing import Dict, List
|
||||
|
||||
from fastapi import HTTPException, status
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlmodel import Session, select
|
||||
|
||||
from app.core import crypto
|
||||
@@ -53,8 +54,15 @@ def _norm_platform(platform: str | None) -> str:
|
||||
|
||||
|
||||
def _check_name_taken(session: Session, name: str, platform: str, exclude_id: int | None = None) -> None:
|
||||
"""校验 (platform, name) 联合唯一:同平台内不允许重名,跨平台可复用"""
|
||||
stmt = select(Account).where(Account.name == name, Account.platform == platform)
|
||||
"""校验 (platform, name) 联合唯一:同平台内不允许重名,跨平台可复用
|
||||
|
||||
platform 用 coalesce 归一化匹配:历史数据的 NULL 与空串视为同一平台,
|
||||
避免出现同名账号在不同"空平台"上各存一条。
|
||||
"""
|
||||
stmt = select(Account).where(
|
||||
Account.name == name,
|
||||
func.coalesce(Account.platform, "") == platform,
|
||||
)
|
||||
if exclude_id is not None:
|
||||
stmt = stmt.where(Account.id != exclude_id)
|
||||
if session.exec(stmt).first():
|
||||
@@ -73,8 +81,16 @@ def create_account(session: Session, data: AccountCreate) -> AccountRead:
|
||||
account = Account(name=name, platform=platform, remark=data.remark, login_user=data.login_user)
|
||||
account.login_password_encrypted = crypto.encrypt(data.login_password)
|
||||
account.api_config_encrypted = crypto.encrypt(data.api_config)
|
||||
session.add(account)
|
||||
session.commit()
|
||||
try:
|
||||
session.add(account)
|
||||
session.commit()
|
||||
except IntegrityError:
|
||||
# 并发创建同名账号时唯一约束兜底:检查与提交之间存在竞态窗口
|
||||
session.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"该平台下账号已存在:{name}",
|
||||
)
|
||||
session.refresh(account)
|
||||
return _to_read(account, _asset_counts(session))
|
||||
|
||||
@@ -101,8 +117,16 @@ def update_account(session: Session, account_id: int, data: AccountUpdate) -> Ac
|
||||
account.login_password_encrypted = crypto.encrypt(data.login_password)
|
||||
if data.api_config is not None:
|
||||
account.api_config_encrypted = crypto.encrypt(data.api_config)
|
||||
session.add(account)
|
||||
session.commit()
|
||||
try:
|
||||
session.add(account)
|
||||
session.commit()
|
||||
except IntegrityError:
|
||||
# 重命名撞上已有账号时唯一约束兜底,与 _check_name_taken 存在竞态窗口
|
||||
session.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=f"该平台下账号已存在:{account.name}",
|
||||
)
|
||||
session.refresh(account)
|
||||
return _to_read(account, _asset_counts(session))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user