- pyrightconfig.json 钉死 Python 3.12 虚拟环境,消除由于查找系统 Python 3.9 带来的假性报错 - 修复 huya/login.py 引用不存在函数的问题并补充单元测试 - 修复 browser_harvest.py 对 set 下标访问的错误并提升报错信息质量 - 虎牙 6 个子路由显式导入依赖,消除 import * 带来的未定义变量报警 - 规范化 Pydantic Field(default=...) 默认值声明 - 完善 solver.py / app_login.py 等核心路径类型守卫与 @overload 重载 - 在 pyproject.toml 中显式锁定 ruff lint 规则集 (E4, E7, E9, F, B, I, BLE) - 门禁脚本同步增加 ruff check 检查并在 AGENTS.md 补齐实践经验总结
106 lines
3.9 KiB
Python
106 lines
3.9 KiB
Python
"""虎牙设备绑定:画像与指纹状态解绑。"""
|
||
|
||
from fastapi import APIRouter
|
||
|
||
from ._common import (
|
||
_DEVICE_BINDINGS_PERMS_VIEW,
|
||
Depends,
|
||
HTTPException,
|
||
HuyaAccount,
|
||
HuyaDeviceBindingsBatchDeleteRequest,
|
||
Session,
|
||
User,
|
||
_fp_state_dir,
|
||
_load_profile_db,
|
||
_mask_hex,
|
||
_require_huya_perm,
|
||
_save_profile_db,
|
||
_unbind_device_accounts,
|
||
get_current_user,
|
||
get_db,
|
||
shutil,
|
||
user_has_permission,
|
||
)
|
||
|
||
router = APIRouter()
|
||
|
||
|
||
@router.get("/device-bindings")
|
||
def list_device_bindings(
|
||
db: Session = Depends(get_db), current: User = Depends(get_current_user)
|
||
):
|
||
"""列出所有账号 ↔ 设备环境绑定 (画像 + 指纹状态 + 账号表三方合并)。"""
|
||
if not any(user_has_permission(current, p) for p in _DEVICE_BINDINGS_PERMS_VIEW):
|
||
raise HTTPException(status_code=403, detail="无虎牙账号查看权限")
|
||
profiles = _load_profile_db()
|
||
accounts = {a.username: a for a in db.query(HuyaAccount).all() if a.username}
|
||
rows = []
|
||
for account, prof in sorted(profiles.items()):
|
||
acct = accounts.get(account)
|
||
state_dir = _fp_state_dir(account)
|
||
last_login = prof.get("last_login") or {}
|
||
rows.append(
|
||
{
|
||
"account": account,
|
||
"vendor": prof.get("vendor", ""),
|
||
"model": prof.get("model", ""),
|
||
"screen": prof.get("screen", ""),
|
||
"fingerprint_masked": _mask_hex(prof.get("fingerprint")),
|
||
"guid32_masked": _mask_hex(prof.get("guid32")),
|
||
"hebe_count": len(prof.get("hebe") or {}),
|
||
"hdid32": prof.get("hdid", ""), # t1.t0 app 级常量, 非敏感
|
||
"has_hydevice_state": state_dir.exists(),
|
||
"login_channel": (acct.login_channel if acct else "") or "",
|
||
"account_status": (acct.status if acct else "") or "未入库",
|
||
"bound_at": prof.get("bound_at"),
|
||
"last_login_at": last_login.get("at"),
|
||
"last_login_ok": last_login.get("ok"),
|
||
}
|
||
)
|
||
return {"items": rows, "total": len(rows)}
|
||
|
||
|
||
@router.delete("/device-bindings/{account}")
|
||
def delete_device_binding(
|
||
account: str,
|
||
db: Session = Depends(get_db),
|
||
current: User = Depends(get_current_user),
|
||
):
|
||
"""解绑: 删除该账号的设备画像与 hydevice 指纹状态, 下次登录自动生成全新环境。"""
|
||
_require_huya_perm(current, "huya:import")
|
||
profiles = _load_profile_db()
|
||
if account not in profiles:
|
||
raise HTTPException(status_code=404, detail="该账号没有设备绑定记录")
|
||
profiles.pop(account)
|
||
_save_profile_db(profiles)
|
||
state_dir = _fp_state_dir(account)
|
||
removed_state = False
|
||
if state_dir.exists():
|
||
shutil.rmtree(state_dir, ignore_errors=True)
|
||
removed_state = True
|
||
return {
|
||
"ok": True,
|
||
"account": account,
|
||
"profile_removed": True,
|
||
"fp_state_removed": removed_state,
|
||
"message": "已解绑, 下次 App 协议登录将自动生成全新设备环境并重新注册",
|
||
}
|
||
|
||
|
||
@router.post("/device-bindings/batch-delete")
|
||
def delete_device_bindings(
|
||
req: HuyaDeviceBindingsBatchDeleteRequest,
|
||
db: Session = Depends(get_db),
|
||
current: User = Depends(get_current_user),
|
||
):
|
||
"""批量解绑: 一次删除多个账号的设备画像与指纹状态。"""
|
||
_require_huya_perm(current, "huya:import")
|
||
removed, missing = _unbind_device_accounts(req.accounts)
|
||
if not removed:
|
||
raise HTTPException(status_code=404, detail="所选账号均没有设备绑定记录")
|
||
message = f"已批量解绑 {len(removed)} 个账号"
|
||
if missing:
|
||
message += f",{len(missing)} 个账号无绑定记录已跳过"
|
||
message += ",下次 App 协议登录将自动生成全新设备环境并重新注册"
|
||
return {"ok": True, "removed": removed, "missing": missing, "message": message}
|