test_refresh.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  1. """Live-test: can the 30-day JWT refresh itself via getByRefresh?
  2. From app.asar JS map: user.getByRefresh = /userApi/v1/UserInfo/getByRefresh
  3. The client calls this to re-fetch user info (and possibly re-issue token)
  4. without re-login. If token renewal works here, the slider is only needed
  5. ONCE per ~month, and maybe never again via refresh chaining.
  6. """
  7. import asyncio
  8. import sys
  9. from pathlib import Path
  10. _HERE = Path(__file__).resolve().parent
  11. sys.path.insert(0, str(_HERE.parent / "src"))
  12. from cq2_sdk.env_loader import load_env
  13. load_env(_HERE.parent / ".env")
  14. from cq2_sdk.login import Box996Login, ManualGeetestSolver
  15. from cq2_sdk.login.box_996 import Box996Error
  16. from cq2_sdk.store import UserStore, connection
  17. async def main() -> int:
  18. with connection() as conn:
  19. u = UserStore(conn).get_by_username("longwu988")
  20. if not u or not u.token:
  21. print("FAIL: no token in MySQL")
  22. return 1
  23. print(f"token (from saz): {u.token[:30]}...")
  24. box = Box996Login(geetest=ManualGeetestSolver(), device_id="00:15:5d:0a:04:00")
  25. box.token = u.token
  26. # host/prefix rule (from captures): user-api host serves paths WITHOUT
  27. # the /userApi gateway prefix — get_phone_code worked at
  28. # user-api.../v1/UserInfo/get_phone_code, so strip the prefix here too.
  29. candidates = [
  30. ("https://user-api.996hezi.com", "/v1/UserInfo/getByRefresh"),
  31. ("https://api.996hezi.com", "/userApi/v1/UserInfo/getByRefresh"),
  32. ("https://api.996hezi.com", "/v1/UserInfo/getByRefresh"),
  33. ]
  34. r = None
  35. for host, path in candidates:
  36. try:
  37. r = await box._request("POST", host, path)
  38. print(f"[HIT] {host}{path}")
  39. break
  40. except Box996Error as e:
  41. print(f"[{e.code}] {host}{path} — {e.msg[:50]}")
  42. r = r # keep last
  43. except Exception as e:
  44. msg = str(e)
  45. tag = "404" if "404" in msg else type(e).__name__
  46. print(f"[{tag}] {host}{path}")
  47. if r is None:
  48. return 1
  49. print(f"code = {r.get('code')}")
  50. print(f"msg = {r.get('msg', '')}")
  51. d = r.get("data") or {}
  52. if isinstance(d, dict):
  53. info = d.get("userinfo") or d
  54. print(f"nick = {info.get('nick_name')} id = {info.get('id')}")
  55. new_tok = d.get("token") or info.get("token")
  56. if new_tok and new_tok != u.token:
  57. print(f"*** NEW TOKEN ISSUED (len={len(new_tok)}) — refresh chain works! ***")
  58. else:
  59. print("(no new token in response — endpoint re-validates but may not renew)")
  60. return 0
  61. if __name__ == "__main__":
  62. sys.exit(asyncio.run(main()))