|
|
@@ -35,6 +35,9 @@ from app.services.mapping_service import MappingService
|
|
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
+def generate_access_token():
|
|
|
+ return secrets.token_urlsafe(32)
|
|
|
+
|
|
|
def generate_app_credentials():
|
|
|
# Generate a random 16-char App ID (hex or alphanumeric)
|
|
|
app_id = "app_" + secrets.token_hex(8)
|
|
|
@@ -86,12 +89,15 @@ def create_app(
|
|
|
"""
|
|
|
# 1. Generate ID and Secret
|
|
|
app_id, app_secret = generate_app_credentials()
|
|
|
+ # 2. Generate Access Token
|
|
|
+ access_token = generate_access_token()
|
|
|
|
|
|
- # 2. Store Secret (Plain text needed for HMAC verification)
|
|
|
+ # 3. Store Secret (Plain text needed for HMAC verification)
|
|
|
|
|
|
db_app = Application(
|
|
|
app_id=app_id,
|
|
|
app_secret=app_secret,
|
|
|
+ access_token=access_token,
|
|
|
app_name=app_in.app_name,
|
|
|
icon_url=app_in.icon_url,
|
|
|
protocol_type=app_in.protocol_type,
|
|
|
@@ -104,7 +110,7 @@ def create_app(
|
|
|
db.commit()
|
|
|
db.refresh(db_app)
|
|
|
|
|
|
- return ApplicationSecretDisplay(app_id=app_id, app_secret=app_secret)
|
|
|
+ return ApplicationSecretDisplay(app_id=app_id, app_secret=app_secret, access_token=access_token)
|
|
|
|
|
|
@router.put("/{app_id}", response_model=ApplicationResponse, summary="更新应用")
|
|
|
def update_app(
|
|
|
@@ -181,7 +187,7 @@ def regenerate_secret(
|
|
|
db.add(app)
|
|
|
db.commit()
|
|
|
|
|
|
- return ApplicationSecretDisplay(app_id=app.app_id, app_secret=new_secret)
|
|
|
+ return ApplicationSecretDisplay(app_id=app.app_id, app_secret=new_secret, access_token=app.access_token)
|
|
|
|
|
|
@router.post("/{app_id}/view-secret", response_model=ApplicationSecretDisplay, summary="查看密钥")
|
|
|
def view_secret(
|
|
|
@@ -206,7 +212,7 @@ def view_secret(
|
|
|
if current_user.role != "SUPER_ADMIN" and app.owner_id != current_user.id:
|
|
|
raise HTTPException(status_code=403, detail="权限不足")
|
|
|
|
|
|
- return ApplicationSecretDisplay(app_id=app.app_id, app_secret=app.app_secret)
|
|
|
+ return ApplicationSecretDisplay(app_id=app.app_id, app_secret=app.app_secret, access_token=app.access_token)
|
|
|
|
|
|
# ==========================================
|
|
|
# Mappings
|
|
|
@@ -269,11 +275,21 @@ def create_mapping(
|
|
|
if current_user.role != "SUPER_ADMIN" and app.owner_id != current_user.id:
|
|
|
raise HTTPException(status_code=403, detail="权限不足")
|
|
|
|
|
|
- # 1. Check if user exists
|
|
|
+ # 1. Find User or Create
|
|
|
user = db.query(User).filter(User.mobile == mapping_in.mobile).first()
|
|
|
if not user:
|
|
|
- raise HTTPException(status_code=404, detail=f"手机号为 {mapping_in.mobile} 的用户未找到")
|
|
|
-
|
|
|
+ # Auto create user
|
|
|
+ password = secrets.token_urlsafe(8) # Random password
|
|
|
+ user = User(
|
|
|
+ mobile=mapping_in.mobile,
|
|
|
+ password_hash=security.get_password_hash(password),
|
|
|
+ status="ACTIVE",
|
|
|
+ role="DEVELOPER"
|
|
|
+ )
|
|
|
+ db.add(user)
|
|
|
+ db.commit()
|
|
|
+ db.refresh(user)
|
|
|
+
|
|
|
# 2. Check if mapping exists
|
|
|
existing = db.query(AppUserMapping).filter(
|
|
|
AppUserMapping.app_id == app_id,
|
|
|
@@ -281,8 +297,26 @@ def create_mapping(
|
|
|
).first()
|
|
|
if existing:
|
|
|
raise HTTPException(status_code=400, detail="该用户的映射已存在")
|
|
|
+
|
|
|
+ # 3. Check Uniqueness for mapped_email (if provided)
|
|
|
+ if mapping_in.mapped_email:
|
|
|
+ email_exists = db.query(AppUserMapping).filter(
|
|
|
+ AppUserMapping.app_id == app_id,
|
|
|
+ AppUserMapping.mapped_email == mapping_in.mapped_email
|
|
|
+ ).first()
|
|
|
+ if email_exists:
|
|
|
+ raise HTTPException(status_code=400, detail=f"该应用下邮箱 {mapping_in.mapped_email} 已被使用")
|
|
|
+
|
|
|
+ # 4. Check Uniqueness for mapped_key
|
|
|
+ if mapping_in.mapped_key:
|
|
|
+ key_exists = db.query(AppUserMapping).filter(
|
|
|
+ AppUserMapping.app_id == app_id,
|
|
|
+ AppUserMapping.mapped_key == mapping_in.mapped_key
|
|
|
+ ).first()
|
|
|
+ if key_exists:
|
|
|
+ raise HTTPException(status_code=400, detail=f"该应用下账号 {mapping_in.mapped_key} 已被使用")
|
|
|
|
|
|
- # 3. Create
|
|
|
+ # 5. Create
|
|
|
mapping = AppUserMapping(
|
|
|
app_id=app_id,
|
|
|
user_id=user.id,
|
|
|
@@ -434,12 +468,22 @@ def sync_mapping(
|
|
|
"""
|
|
|
从外部平台同步用户映射关系(机器对机器)。
|
|
|
只同步映射关系,不创建或更新用户本身。
|
|
|
- 需要应用访问令牌。
|
|
|
+ 需要应用访问令牌 (Authorization Bearer JWT 或 X-App-Access-Token)。
|
|
|
"""
|
|
|
- # 1. Find User
|
|
|
+ # 1. Find User or Create
|
|
|
user = db.query(User).filter(User.mobile == sync_in.mobile).first()
|
|
|
if not user:
|
|
|
- raise HTTPException(status_code=404, detail=f"User with mobile {sync_in.mobile} not found")
|
|
|
+ # Auto create user
|
|
|
+ password = secrets.token_urlsafe(8) # Random password
|
|
|
+ user = User(
|
|
|
+ mobile=sync_in.mobile,
|
|
|
+ password_hash=security.get_password_hash(password),
|
|
|
+ status="ACTIVE",
|
|
|
+ role="DEVELOPER"
|
|
|
+ )
|
|
|
+ db.add(user)
|
|
|
+ db.commit()
|
|
|
+ db.refresh(user)
|
|
|
|
|
|
# 2. Handle Mapping
|
|
|
mapping = db.query(AppUserMapping).filter(
|
|
|
@@ -447,10 +491,31 @@ def sync_mapping(
|
|
|
AppUserMapping.user_id == user.id
|
|
|
).first()
|
|
|
|
|
|
+ # Check Uniqueness for mapped_key (if changing or new, and provided)
|
|
|
+ if sync_in.mapped_key and (not mapping or mapping.mapped_key != sync_in.mapped_key):
|
|
|
+ key_exists = db.query(AppUserMapping).filter(
|
|
|
+ AppUserMapping.app_id == current_app.id,
|
|
|
+ AppUserMapping.mapped_key == sync_in.mapped_key
|
|
|
+ ).first()
|
|
|
+ if key_exists:
|
|
|
+ raise HTTPException(status_code=400, detail=f"该应用下账号 {sync_in.mapped_key} 已被使用")
|
|
|
+
|
|
|
+ # Check Uniqueness for mapped_email (if changing or new, and provided)
|
|
|
+ if sync_in.mapped_email and (not mapping or mapping.mapped_email != sync_in.mapped_email):
|
|
|
+ email_exists = db.query(AppUserMapping).filter(
|
|
|
+ AppUserMapping.app_id == current_app.id,
|
|
|
+ AppUserMapping.mapped_email == sync_in.mapped_email
|
|
|
+ ).first()
|
|
|
+ if email_exists:
|
|
|
+ raise HTTPException(status_code=400, detail=f"该应用下邮箱 {sync_in.mapped_email} 已被使用")
|
|
|
+
|
|
|
if mapping:
|
|
|
# Update existing mapping
|
|
|
- mapping.mapped_key = sync_in.mapped_key
|
|
|
- if sync_in.mapped_email:
|
|
|
+ if sync_in.mapped_key is not None:
|
|
|
+ mapping.mapped_key = sync_in.mapped_key
|
|
|
+ if sync_in.is_active is not None:
|
|
|
+ mapping.is_active = sync_in.is_active
|
|
|
+ if sync_in.mapped_email is not None:
|
|
|
mapping.mapped_email = sync_in.mapped_email
|
|
|
else:
|
|
|
# Create new mapping
|
|
|
@@ -458,7 +523,8 @@ def sync_mapping(
|
|
|
app_id=current_app.id,
|
|
|
user_id=user.id,
|
|
|
mapped_key=sync_in.mapped_key,
|
|
|
- mapped_email=sync_in.mapped_email
|
|
|
+ mapped_email=sync_in.mapped_email,
|
|
|
+ is_active=sync_in.is_active if sync_in.is_active is not None else True
|
|
|
)
|
|
|
db.add(mapping)
|
|
|
|
|
|
@@ -471,5 +537,6 @@ def sync_mapping(
|
|
|
user_id=mapping.user_id,
|
|
|
mapped_key=mapping.mapped_key,
|
|
|
mapped_email=mapping.mapped_email,
|
|
|
- user_mobile=user.mobile
|
|
|
+ user_mobile=user.mobile,
|
|
|
+ is_active=mapping.is_active
|
|
|
)
|