File size: 4,209 Bytes
c47ec10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
"""验证 OAuth 账号的自动刷新是否生效。

用法(在容器内,工作目录 /app):
    uv run python scripts/verify_oauth_refresh.py            # 只读诊断,安全
    uv run python scripts/verify_oauth_refresh.py --force    # 真正触发一次刷新

只读模式:列出每个账号的 access_token 剩余有效期、是否带 refresh_token、
          上次刷新时间与上次刷新错误,判断"有没有料可刷"。
--force :对每个带 refresh_token 的账号强制走一次 refresh_access_token(force=True),
          直接验证 refresh_token + 本项目 client_id 能否从 OpenAI 换出新 access_token。
          这会真实轮换 access_token(新 token 有效,不损坏账号)。
"""
from __future__ import annotations

import sys

from services.account_service import account_service


def _fmt_remaining(seconds: int | None) -> str:
    if seconds is None:
        return "无法解析 exp"
    if seconds <= 0:
        return f"已过期 {(-seconds) // 3600}h"
    return f"{seconds // 3600}h{(seconds % 3600) // 60}m 后过期"


def diagnose() -> list[str]:
    """只读:打印每个账号的刷新就绪状态,返回带 refresh_token 的 token 列表。"""
    tokens = account_service.list_tokens()
    print(f"账号总数: {len(tokens)}\n")
    refreshable: list[str] = []
    for token in tokens:
        account = account_service.get_account(token) or {}
        remaining = account_service._token_expires_in(token)
        has_rt = bool(str(account.get("refresh_token") or "").strip())
        if has_rt:
            refreshable.append(token)
        print(f"- email={account.get('email') or '(未知)'}")
        print(f"    access_token[:20]   = {token[:20]}...")
        print(f"    距过期              = {_fmt_remaining(remaining)}")
        print(f"    refresh_token       = {'有 ✅' if has_rt else '无 ❌(无法自动刷新)'}")
        print(f"    last_token_refresh_at    = {account.get('last_token_refresh_at')}")
        print(f"    last_token_refresh_error = {account.get('last_token_refresh_error')}")
        print()
    return refreshable


def force_refresh(tokens: list[str]) -> None:
    """对每个账号 force 刷新一次,并对比前后状态判断成败。"""
    if not tokens:
        print("没有带 refresh_token 的账号,无法验证刷新。")
        return
    print("=" * 60)
    print(f"开始对 {len(tokens)} 个账号 force 刷新(真实调用 OpenAI)...\n")
    ok = 0
    for token in tokens:
        before = account_service.get_account(token) or {}
        new_token = account_service.refresh_access_token(token, force=True, event="manual_verify")
        after = account_service.get_account(new_token) or {}
        err = str(after.get("last_token_refresh_error") or "").strip()
        rotated = new_token != token
        success = bool(new_token) and not err
        if success:
            ok += 1
        print(f"- email={before.get('email') or '(未知)'}")
        print(f"    旧 access_token[:20] = {token[:20]}...")
        print(f"    新 access_token[:20] = {new_token[:20]}...")
        print(f"    token 是否轮换       = {'是' if rotated else '否(exp 未到刷新窗口时可能返回原值)'}")
        print(f"    last_token_refresh_at    = {after.get('last_token_refresh_at')}")
        print(f"    last_token_refresh_error = {after.get('last_token_refresh_error') or '无'}")
        print(f"    >>> 刷新结果         = {'成功 ✅' if success else '失败 ❌'}")
        print()
    print("=" * 60)
    print(f"汇总: {ok}/{len(tokens)} 个账号刷新成功")
    if ok == len(tokens):
        print("✅ 自动刷新机制对这些账号完全可用——refresh_token 与 client_id 匹配。")
    else:
        print("❌ 有账号刷新失败,看上面的 last_token_refresh_error,或 docker logs 里的 [oauth-login]/refresh 日志。")


def main() -> None:
    do_force = "--force" in sys.argv[1:]
    refreshable = diagnose()
    if do_force:
        force_refresh(refreshable)
    else:
        print("提示: 加 --force 参数可真正触发一次刷新以验证能否成功。")


if __name__ == "__main__":
    main()