功能: - 激活码管理 (Pro/Auto 两种类型) - 账号池管理 - 设备绑定记录 - 使用日志 - 搜索/筛选功能 - 禁用/启用功能 (支持退款参考) - 全局设置 (换号间隔、额度消耗等) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
149 lines
5.6 KiB
Python
149 lines
5.6 KiB
Python
"""
|
|
客户端 API - 兼容原 CursorPro 插件
|
|
"""
|
|
from fastapi import APIRouter, Depends, Request
|
|
from sqlalchemy.orm import Session
|
|
from app.database import get_db
|
|
from app.services import AccountService, KeyService, LogService, GlobalSettingsService
|
|
from app.schemas import VerifyKeyRequest, VerifyKeyResponse, SwitchAccountRequest, SwitchAccountResponse
|
|
|
|
router = APIRouter(prefix="/api", tags=["Client API"])
|
|
|
|
|
|
@router.post("/verify-key", response_model=VerifyKeyResponse)
|
|
async def verify_key(request: VerifyKeyRequest, req: Request, db: Session = Depends(get_db)):
|
|
"""验证激活码"""
|
|
key = KeyService.get_by_key(db, request.key)
|
|
|
|
if not key:
|
|
return VerifyKeyResponse(success=False, error="激活码不存在")
|
|
|
|
# 首次激活:设置激活时间和过期时间
|
|
KeyService.activate(db, key)
|
|
|
|
# 检查设备限制
|
|
if request.device_id:
|
|
device_ok, device_msg = KeyService.check_device(db, key, request.device_id)
|
|
if not device_ok:
|
|
LogService.log(db, key.id, "verify", ip_address=req.client.host, success=False, message=device_msg)
|
|
return VerifyKeyResponse(success=False, error=device_msg)
|
|
|
|
# 检查激活码是否有效
|
|
is_valid, message = KeyService.is_valid(key, db)
|
|
if not is_valid:
|
|
LogService.log(db, key.id, "verify", ip_address=req.client.host, success=False, message=message)
|
|
return VerifyKeyResponse(success=False, error=message)
|
|
|
|
# 获取当前绑定的账号,或分配新账号
|
|
account = None
|
|
if key.current_account_id:
|
|
account = AccountService.get_by_id(db, key.current_account_id)
|
|
|
|
if not account or account.status != "active":
|
|
# 分配新账号
|
|
account = AccountService.get_available(db, key.membership_type)
|
|
if not account:
|
|
LogService.log(db, key.id, "verify", ip_address=req.client.host, success=False, message="无可用账号")
|
|
return VerifyKeyResponse(success=False, error="暂无可用账号,请稍后重试")
|
|
|
|
KeyService.bind_account(db, key, account)
|
|
AccountService.mark_used(db, account, key.id)
|
|
|
|
LogService.log(db, key.id, "verify", account.id, ip_address=req.client.host, success=True)
|
|
|
|
expire_date = key.expire_at.strftime("%Y-%m-%d %H:%M:%S") if key.expire_at else None
|
|
quota_cost = KeyService.get_quota_cost(db, key.membership_type)
|
|
|
|
return VerifyKeyResponse(
|
|
success=True,
|
|
email=account.email,
|
|
accessToken=account.access_token,
|
|
refreshToken=account.refresh_token,
|
|
WorkosCursorSessionToken=account.workos_session_token,
|
|
membership_type=account.membership_type.value,
|
|
quota=key.quota,
|
|
quotaUsed=key.quota_used,
|
|
quotaRemaining=key.quota - key.quota_used,
|
|
quotaCost=quota_cost,
|
|
expireDate=expire_date
|
|
)
|
|
|
|
|
|
@router.post("/switch-account", response_model=SwitchAccountResponse)
|
|
async def switch_account(request: SwitchAccountRequest, req: Request, db: Session = Depends(get_db)):
|
|
"""切换账号"""
|
|
key = KeyService.get_by_key(db, request.key)
|
|
|
|
if not key:
|
|
return SwitchAccountResponse(success=False, error="激活码不存在")
|
|
|
|
# 检查设备限制
|
|
if request.device_id:
|
|
device_ok, device_msg = KeyService.check_device(db, key, request.device_id)
|
|
if not device_ok:
|
|
LogService.log(db, key.id, "switch", ip_address=req.client.host, success=False, message=device_msg)
|
|
return SwitchAccountResponse(success=False, error=device_msg)
|
|
|
|
# 检查激活码是否有效
|
|
is_valid, message = KeyService.is_valid(key, db)
|
|
if not is_valid:
|
|
LogService.log(db, key.id, "switch", ip_address=req.client.host, success=False, message=message)
|
|
return SwitchAccountResponse(success=False, error=message)
|
|
|
|
# 检查换号频率限制
|
|
can_switch, switch_msg = KeyService.can_switch(db, key)
|
|
if not can_switch:
|
|
LogService.log(db, key.id, "switch", ip_address=req.client.host, success=False, message=switch_msg)
|
|
return SwitchAccountResponse(success=False, error=switch_msg)
|
|
|
|
# 释放当前账号
|
|
if key.current_account_id:
|
|
old_account = AccountService.get_by_id(db, key.current_account_id)
|
|
if old_account:
|
|
AccountService.release(db, old_account)
|
|
|
|
# 获取新账号
|
|
account = AccountService.get_available(db, key.membership_type)
|
|
if not account:
|
|
LogService.log(db, key.id, "switch", ip_address=req.client.host, success=False, message="无可用账号")
|
|
return SwitchAccountResponse(success=False, error="暂无可用账号,请稍后重试")
|
|
|
|
# 绑定新账号并扣除额度
|
|
KeyService.bind_account(db, key, account)
|
|
KeyService.use_switch(db, key)
|
|
AccountService.mark_used(db, account, key.id)
|
|
|
|
LogService.log(db, key.id, "switch", account.id, ip_address=req.client.host, success=True)
|
|
|
|
return SwitchAccountResponse(
|
|
success=True,
|
|
message="切换成功",
|
|
email=account.email,
|
|
accessToken=account.access_token,
|
|
refreshToken=account.refresh_token,
|
|
WorkosCursorSessionToken=account.workos_session_token,
|
|
membership_type=account.membership_type.value,
|
|
quotaUsed=key.quota_used,
|
|
quotaRemaining=key.quota - key.quota_used
|
|
)
|
|
|
|
|
|
@router.get("/version")
|
|
async def get_version():
|
|
"""获取版本信息"""
|
|
return {
|
|
"version": "1.0.0",
|
|
"update_url": None,
|
|
"message": None
|
|
}
|
|
|
|
|
|
@router.get("/seamless/status")
|
|
async def seamless_status():
|
|
"""无感换号状态(简化实现)"""
|
|
return {
|
|
"success": True,
|
|
"enabled": False,
|
|
"message": "无感换号功能暂未开放"
|
|
}
|