dsh-plugin-image-workbench 1.1.5 → 1.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/LICENSE +21 -21
  2. package/README.en.md +5 -3
  3. package/README.md +7 -3
  4. package/client.js +241 -241
  5. package/cordis.patch.yml +14 -14
  6. package/host.js +51 -0
  7. package/install.mjs +333 -387
  8. package/package.json +3 -2
  9. package/scripts/fetch-style-images.mjs +104 -0
  10. package/scripts/vendor-snapshot.py +154 -154
  11. package/server/access.py +26 -26
  12. package/server/app.py +1329 -1329
  13. package/server/catalog.py +147 -147
  14. package/server/composer.py +391 -391
  15. package/server/drafts.py +158 -158
  16. package/server/history.py +575 -575
  17. package/server/imaging.py +172 -172
  18. package/server/pool.py +1335 -1335
  19. package/server/pool_admin.py +921 -921
  20. package/server/pool_service.py +1073 -1073
  21. package/server/requirements.txt +6 -6
  22. package/server/smoke_check.py +402 -402
  23. package/server/static/access.html +202 -202
  24. package/vendor/chatgpt2api/source/.env.example +82 -82
  25. package/vendor/chatgpt2api/source/.python-version +1 -1
  26. package/vendor/chatgpt2api/source/LICENSE +21 -21
  27. package/vendor/chatgpt2api/source/README.md +391 -391
  28. package/vendor/chatgpt2api/source/VERSION +1 -1
  29. package/vendor/chatgpt2api/source/api/__init__.py +2 -2
  30. package/vendor/chatgpt2api/source/api/accounts.py +537 -537
  31. package/vendor/chatgpt2api/source/api/ai.py +217 -217
  32. package/vendor/chatgpt2api/source/api/app.py +62 -62
  33. package/vendor/chatgpt2api/source/api/errors.py +46 -46
  34. package/vendor/chatgpt2api/source/api/image_inputs.py +309 -309
  35. package/vendor/chatgpt2api/source/api/image_tasks.py +120 -120
  36. package/vendor/chatgpt2api/source/api/support.py +134 -134
  37. package/vendor/chatgpt2api/source/api/system.py +365 -365
  38. package/vendor/chatgpt2api/source/assets/kunrouai.svg +50 -50
  39. package/vendor/chatgpt2api/source/config.default.json +108 -108
  40. package/vendor/chatgpt2api/source/main.py +9 -9
  41. package/vendor/chatgpt2api/source/pyproject.toml +27 -27
  42. package/vendor/chatgpt2api/source/scripts/init_proxy_config.py +186 -186
  43. package/vendor/chatgpt2api/source/scripts/migrate_storage.py +163 -163
  44. package/vendor/chatgpt2api/source/scripts/privoxy-warp.conf +13 -13
  45. package/vendor/chatgpt2api/source/scripts/test_outlook_token_mailbox.py +385 -385
  46. package/vendor/chatgpt2api/source/scripts/test_storage.py +129 -129
  47. package/vendor/chatgpt2api/source/scripts/verify_oauth_refresh.py +92 -92
  48. package/vendor/chatgpt2api/source/services/account_service.py +1757 -1757
  49. package/vendor/chatgpt2api/source/services/auth_service.py +253 -253
  50. package/vendor/chatgpt2api/source/services/backup_service.py +671 -671
  51. package/vendor/chatgpt2api/source/services/config.py +667 -667
  52. package/vendor/chatgpt2api/source/services/content_filter.py +242 -242
  53. package/vendor/chatgpt2api/source/services/cpa_service.py +315 -315
  54. package/vendor/chatgpt2api/source/services/editable_file_task_service.py +260 -260
  55. package/vendor/chatgpt2api/source/services/image_service.py +376 -376
  56. package/vendor/chatgpt2api/source/services/image_storage_service.py +405 -405
  57. package/vendor/chatgpt2api/source/services/image_tags_service.py +76 -76
  58. package/vendor/chatgpt2api/source/services/image_task_service.py +550 -550
  59. package/vendor/chatgpt2api/source/services/log_service.py +337 -337
  60. package/vendor/chatgpt2api/source/services/model_service.py +182 -182
  61. package/vendor/chatgpt2api/source/services/oauth_login_service.py +267 -267
  62. package/vendor/chatgpt2api/source/services/openai_backend_api.py +2763 -2763
  63. package/vendor/chatgpt2api/source/services/openai_oauth.py +32 -32
  64. package/vendor/chatgpt2api/source/services/protocol/__init__.py +2 -2
  65. package/vendor/chatgpt2api/source/services/protocol/anthropic_v1_messages.py +307 -307
  66. package/vendor/chatgpt2api/source/services/protocol/chat_completion_cache.py +238 -238
  67. package/vendor/chatgpt2api/source/services/protocol/conversation.py +1644 -1644
  68. package/vendor/chatgpt2api/source/services/protocol/error_response.py +124 -124
  69. package/vendor/chatgpt2api/source/services/protocol/openai_search.py +19 -19
  70. package/vendor/chatgpt2api/source/services/protocol/openai_v1_chat_complete.py +316 -316
  71. package/vendor/chatgpt2api/source/services/protocol/openai_v1_image_edit.py +86 -86
  72. package/vendor/chatgpt2api/source/services/protocol/openai_v1_image_generations.py +42 -42
  73. package/vendor/chatgpt2api/source/services/protocol/openai_v1_models.py +53 -53
  74. package/vendor/chatgpt2api/source/services/protocol/openai_v1_response.py +453 -453
  75. package/vendor/chatgpt2api/source/services/protocol/web_search_tool.py +160 -160
  76. package/vendor/chatgpt2api/source/services/proxy_service.py +648 -648
  77. package/vendor/chatgpt2api/source/services/storage/__init__.py +5 -5
  78. package/vendor/chatgpt2api/source/services/storage/base.py +38 -38
  79. package/vendor/chatgpt2api/source/services/storage/database_storage.py +194 -194
  80. package/vendor/chatgpt2api/source/services/storage/factory.py +104 -104
  81. package/vendor/chatgpt2api/source/services/storage/git_storage.py +183 -183
  82. package/vendor/chatgpt2api/source/services/storage/json_storage.py +95 -95
  83. package/vendor/chatgpt2api/source/services/sub2api_service.py +531 -531
  84. package/vendor/chatgpt2api/source/utils/helper.py +469 -469
  85. package/vendor/chatgpt2api/source/utils/image_tokens.py +346 -346
  86. package/vendor/chatgpt2api/source/utils/log.py +110 -110
  87. package/vendor/chatgpt2api/source/utils/pkce.py +19 -19
  88. package/vendor/chatgpt2api/source/utils/pow.py +207 -207
  89. package/vendor/chatgpt2api/source/utils/sentinel.py +159 -159
  90. package/vendor/chatgpt2api/source/utils/turnstile.py +184 -184
  91. package/vendor/chatgpt2api/source/uv.lock +904 -904
  92. package/vendor/chatgpt2api/source/web/bun.lock +1501 -1501
  93. package/vendor/chatgpt2api/source/web/components.json +24 -24
  94. package/vendor/chatgpt2api/source/web/eslint.config.mjs +17 -17
  95. package/vendor/chatgpt2api/source/web/next.config.ts +40 -40
  96. package/vendor/chatgpt2api/source/web/package.json +58 -58
  97. package/vendor/chatgpt2api/source/web/postcss.config.mjs +5 -5
  98. package/vendor/chatgpt2api/source/web/src/app/accounts/components/account-import-dialog.tsx +925 -925
  99. package/vendor/chatgpt2api/source/web/src/app/accounts/page.tsx +1307 -1307
  100. package/vendor/chatgpt2api/source/web/src/app/debug/components/chat-panel.tsx +224 -224
  101. package/vendor/chatgpt2api/source/web/src/app/debug/components/editable-file-panel.tsx +444 -444
  102. package/vendor/chatgpt2api/source/web/src/app/debug/components/ppt-panel.tsx +9 -9
  103. package/vendor/chatgpt2api/source/web/src/app/debug/components/psd-panel.tsx +9 -9
  104. package/vendor/chatgpt2api/source/web/src/app/debug/components/search-panel.tsx +172 -172
  105. package/vendor/chatgpt2api/source/web/src/app/debug/components/skill-panel.tsx +169 -169
  106. package/vendor/chatgpt2api/source/web/src/app/debug/components/types.ts +39 -39
  107. package/vendor/chatgpt2api/source/web/src/app/debug/page.tsx +59 -59
  108. package/vendor/chatgpt2api/source/web/src/app/globals.css +230 -230
  109. package/vendor/chatgpt2api/source/web/src/app/image/components/image-composer.tsx +542 -542
  110. package/vendor/chatgpt2api/source/web/src/app/image/components/image-results.tsx +554 -554
  111. package/vendor/chatgpt2api/source/web/src/app/image/components/image-sidebar.tsx +176 -176
  112. package/vendor/chatgpt2api/source/web/src/app/image/page.tsx +1799 -1799
  113. package/vendor/chatgpt2api/source/web/src/app/image-manager/page.tsx +732 -732
  114. package/vendor/chatgpt2api/source/web/src/app/layout.tsx +50 -50
  115. package/vendor/chatgpt2api/source/web/src/app/login/page.tsx +101 -101
  116. package/vendor/chatgpt2api/source/web/src/app/logs/page.tsx +356 -356
  117. package/vendor/chatgpt2api/source/web/src/app/page.tsx +30 -30
  118. package/vendor/chatgpt2api/source/web/src/app/settings/components/api-docs-card.tsx +341 -341
  119. package/vendor/chatgpt2api/source/web/src/app/settings/components/backup-settings-card.tsx +447 -447
  120. package/vendor/chatgpt2api/source/web/src/app/settings/components/base-url-card.tsx +71 -71
  121. package/vendor/chatgpt2api/source/web/src/app/settings/components/config-card.tsx +491 -491
  122. package/vendor/chatgpt2api/source/web/src/app/settings/components/cpa-pool-dialog.tsx +108 -108
  123. package/vendor/chatgpt2api/source/web/src/app/settings/components/cpa-pools-card.tsx +169 -169
  124. package/vendor/chatgpt2api/source/web/src/app/settings/components/import-browser-dialog.tsx +185 -185
  125. package/vendor/chatgpt2api/source/web/src/app/settings/components/proxy-runtime-card.tsx +379 -379
  126. package/vendor/chatgpt2api/source/web/src/app/settings/components/proxy-settings-card.tsx +127 -127
  127. package/vendor/chatgpt2api/source/web/src/app/settings/components/proxy-settings.tsx +200 -200
  128. package/vendor/chatgpt2api/source/web/src/app/settings/components/settings-header.tsx +12 -12
  129. package/vendor/chatgpt2api/source/web/src/app/settings/components/sub2api-connections.tsx +886 -886
  130. package/vendor/chatgpt2api/source/web/src/app/settings/components/third-party-apps-card.tsx +81 -81
  131. package/vendor/chatgpt2api/source/web/src/app/settings/components/user-keys-card.tsx +422 -422
  132. package/vendor/chatgpt2api/source/web/src/app/settings/page.tsx +135 -135
  133. package/vendor/chatgpt2api/source/web/src/app/settings/store.ts +1069 -1069
  134. package/vendor/chatgpt2api/source/web/src/components/date-range-filter.tsx +49 -49
  135. package/vendor/chatgpt2api/source/web/src/components/header-actions.tsx +24 -24
  136. package/vendor/chatgpt2api/source/web/src/components/image-lightbox.tsx +434 -434
  137. package/vendor/chatgpt2api/source/web/src/components/image-thumbnail.tsx +46 -46
  138. package/vendor/chatgpt2api/source/web/src/components/theme-script.tsx +18 -18
  139. package/vendor/chatgpt2api/source/web/src/components/theme-toggle.tsx +14 -14
  140. package/vendor/chatgpt2api/source/web/src/components/top-nav.tsx +263 -263
  141. package/vendor/chatgpt2api/source/web/src/components/ui/animated-theme-toggler.tsx +256 -256
  142. package/vendor/chatgpt2api/source/web/src/components/ui/badge.tsx +41 -41
  143. package/vendor/chatgpt2api/source/web/src/components/ui/button.tsx +59 -59
  144. package/vendor/chatgpt2api/source/web/src/components/ui/calendar.tsx +46 -46
  145. package/vendor/chatgpt2api/source/web/src/components/ui/card.tsx +61 -61
  146. package/vendor/chatgpt2api/source/web/src/components/ui/checkbox.tsx +27 -27
  147. package/vendor/chatgpt2api/source/web/src/components/ui/dia-text-reveal.tsx +267 -267
  148. package/vendor/chatgpt2api/source/web/src/components/ui/dialog.tsx +132 -132
  149. package/vendor/chatgpt2api/source/web/src/components/ui/field.tsx +13 -13
  150. package/vendor/chatgpt2api/source/web/src/components/ui/input.tsx +19 -19
  151. package/vendor/chatgpt2api/source/web/src/components/ui/label.tsx +24 -24
  152. package/vendor/chatgpt2api/source/web/src/components/ui/morphing-text.tsx +149 -149
  153. package/vendor/chatgpt2api/source/web/src/components/ui/popover.tsx +38 -38
  154. package/vendor/chatgpt2api/source/web/src/components/ui/select.tsx +175 -175
  155. package/vendor/chatgpt2api/source/web/src/components/ui/sheet.tsx +143 -143
  156. package/vendor/chatgpt2api/source/web/src/components/ui/table.tsx +29 -29
  157. package/vendor/chatgpt2api/source/web/src/components/ui/tabs.tsx +91 -91
  158. package/vendor/chatgpt2api/source/web/src/components/ui/text-3d-flip.tsx +269 -269
  159. package/vendor/chatgpt2api/source/web/src/components/ui/textarea.tsx +18 -18
  160. package/vendor/chatgpt2api/source/web/src/components/version-release-dialog.tsx +127 -127
  161. package/vendor/chatgpt2api/source/web/src/constants/common-env.ts +6 -6
  162. package/vendor/chatgpt2api/source/web/src/hooks/use-version-check.ts +85 -85
  163. package/vendor/chatgpt2api/source/web/src/lib/api.ts +926 -926
  164. package/vendor/chatgpt2api/source/web/src/lib/auth-session.ts +26 -26
  165. package/vendor/chatgpt2api/source/web/src/lib/release.ts +25 -25
  166. package/vendor/chatgpt2api/source/web/src/lib/request.ts +93 -93
  167. package/vendor/chatgpt2api/source/web/src/lib/use-auth-guard.ts +89 -89
  168. package/vendor/chatgpt2api/source/web/src/lib/utils.ts +6 -6
  169. package/vendor/chatgpt2api/source/web/src/store/auth.ts +112 -112
  170. package/vendor/chatgpt2api/source/web/src/store/editable-file-history.ts +33 -33
  171. package/vendor/chatgpt2api/source/web/src/store/image-conversations.ts +318 -318
  172. package/vendor/chatgpt2api/source/web/tsconfig.json +42 -42
@@ -1,129 +1,129 @@
1
- #!/usr/bin/env python3
2
- """
3
- 存储后端测试脚本
4
-
5
- 用法:
6
- python scripts/test_storage.py
7
- """
8
-
9
- import os
10
- import sys
11
- from pathlib import Path
12
-
13
- # 添加项目根目录到 Python 路径
14
- sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
15
-
16
- DATA_DIR = Path(__file__).resolve().parents[1] / "data"
17
-
18
- from services.storage.factory import create_storage_backend
19
-
20
-
21
- def test_storage():
22
- """测试当前配置的存储后端"""
23
- DATA_DIR.mkdir(parents=True, exist_ok=True)
24
- print("=" * 60)
25
- print("ChatGPT2API 存储后端测试")
26
- print("=" * 60)
27
-
28
- # 显示当前配置
29
- backend_type = os.getenv("STORAGE_BACKEND", "json")
30
- print(f"\n当前存储后端: {backend_type}")
31
-
32
- if backend_type in ("sqlite", "postgres", "postgresql", "mysql", "database"):
33
- database_url = os.getenv("DATABASE_URL", "")
34
- if database_url:
35
- # 隐藏密码
36
- if "://" in database_url and "@" in database_url:
37
- protocol, rest = database_url.split("://", 1)
38
- if "@" in rest:
39
- credentials, host = rest.split("@", 1)
40
- if ":" in credentials:
41
- username, _ = credentials.split(":", 1)
42
- database_url = f"{protocol}://{username}:****@{host}"
43
- print(f"数据库连接: {database_url}")
44
- else:
45
- print(f"数据库连接: 本地 SQLite (data/accounts.db)")
46
-
47
- elif backend_type == "git":
48
- repo_url = os.getenv("GIT_REPO_URL", "")
49
- branch = os.getenv("GIT_BRANCH", "main")
50
- file_path = os.getenv("GIT_FILE_PATH", "accounts.json")
51
- print(f"Git 仓库: {repo_url}")
52
- print(f"Git 分支: {branch}")
53
- print(f"文件路径: {file_path}")
54
-
55
- print("\n" + "=" * 60)
56
-
57
- try:
58
- # 创建存储后端
59
- print("\n[1/5] 创建存储后端...")
60
- storage = create_storage_backend(DATA_DIR)
61
- print("✅ 存储后端创建成功")
62
-
63
- # 获取后端信息
64
- print("\n[2/5] 获取后端信息...")
65
- info = storage.get_backend_info()
66
- print(f"✅ 后端类型: {info.get('type')}")
67
- print(f" 描述: {info.get('description')}")
68
- for key, value in info.items():
69
- if key not in ('type', 'description'):
70
- print(f" {key}: {value}")
71
-
72
- # 健康检查
73
- print("\n[3/5] 执行健康检查...")
74
- health = storage.health_check()
75
- status = health.get("status")
76
- if status == "healthy":
77
- print(f"✅ 健康状态: {status}")
78
- else:
79
- print(f"❌ 健康状态: {status}")
80
- print(f" 错误: {health.get('error')}")
81
- return False
82
-
83
- # 读取数据
84
- print("\n[4/5] 读取账号数据...")
85
- accounts = storage.load_accounts()
86
- print(f"✅ 成功读取 {len(accounts)} 个账号")
87
-
88
- # 写入测试(可选)
89
- print("\n[5/5] 测试写入功能...")
90
- test_account = {
91
- "access_token": "test_token_" + str(os.getpid()),
92
- "type": "Free",
93
- "status": "测试",
94
- "quota": 0,
95
- "email": "test@example.com",
96
- }
97
-
98
- # 添加测试账号
99
- test_accounts = accounts + [test_account]
100
- storage.save_accounts(test_accounts)
101
- print("✅ 写入测试账号成功")
102
-
103
- # 验证写入
104
- reloaded = storage.load_accounts()
105
- if len(reloaded) == len(test_accounts):
106
- print("✅ 验证写入成功")
107
- else:
108
- print(f"❌ 验证失败: 期望 {len(test_accounts)} 个账号,实际 {len(reloaded)} 个")
109
- return False
110
-
111
- # 恢复原始数据
112
- storage.save_accounts(accounts)
113
- print("✅ 恢复原始数据")
114
-
115
- print("\n" + "=" * 60)
116
- print("✅ 所有测试通过!")
117
- print("=" * 60)
118
- return True
119
-
120
- except Exception as e:
121
- print(f"\n❌ 测试失败: {e}")
122
- import traceback
123
- traceback.print_exc()
124
- return False
125
-
126
-
127
- if __name__ == "__main__":
128
- success = test_storage()
129
- sys.exit(0 if success else 1)
1
+ #!/usr/bin/env python3
2
+ """
3
+ 存储后端测试脚本
4
+
5
+ 用法:
6
+ python scripts/test_storage.py
7
+ """
8
+
9
+ import os
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ # 添加项目根目录到 Python 路径
14
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
15
+
16
+ DATA_DIR = Path(__file__).resolve().parents[1] / "data"
17
+
18
+ from services.storage.factory import create_storage_backend
19
+
20
+
21
+ def test_storage():
22
+ """测试当前配置的存储后端"""
23
+ DATA_DIR.mkdir(parents=True, exist_ok=True)
24
+ print("=" * 60)
25
+ print("ChatGPT2API 存储后端测试")
26
+ print("=" * 60)
27
+
28
+ # 显示当前配置
29
+ backend_type = os.getenv("STORAGE_BACKEND", "json")
30
+ print(f"\n当前存储后端: {backend_type}")
31
+
32
+ if backend_type in ("sqlite", "postgres", "postgresql", "mysql", "database"):
33
+ database_url = os.getenv("DATABASE_URL", "")
34
+ if database_url:
35
+ # 隐藏密码
36
+ if "://" in database_url and "@" in database_url:
37
+ protocol, rest = database_url.split("://", 1)
38
+ if "@" in rest:
39
+ credentials, host = rest.split("@", 1)
40
+ if ":" in credentials:
41
+ username, _ = credentials.split(":", 1)
42
+ database_url = f"{protocol}://{username}:****@{host}"
43
+ print(f"数据库连接: {database_url}")
44
+ else:
45
+ print(f"数据库连接: 本地 SQLite (data/accounts.db)")
46
+
47
+ elif backend_type == "git":
48
+ repo_url = os.getenv("GIT_REPO_URL", "")
49
+ branch = os.getenv("GIT_BRANCH", "main")
50
+ file_path = os.getenv("GIT_FILE_PATH", "accounts.json")
51
+ print(f"Git 仓库: {repo_url}")
52
+ print(f"Git 分支: {branch}")
53
+ print(f"文件路径: {file_path}")
54
+
55
+ print("\n" + "=" * 60)
56
+
57
+ try:
58
+ # 创建存储后端
59
+ print("\n[1/5] 创建存储后端...")
60
+ storage = create_storage_backend(DATA_DIR)
61
+ print("✅ 存储后端创建成功")
62
+
63
+ # 获取后端信息
64
+ print("\n[2/5] 获取后端信息...")
65
+ info = storage.get_backend_info()
66
+ print(f"✅ 后端类型: {info.get('type')}")
67
+ print(f" 描述: {info.get('description')}")
68
+ for key, value in info.items():
69
+ if key not in ('type', 'description'):
70
+ print(f" {key}: {value}")
71
+
72
+ # 健康检查
73
+ print("\n[3/5] 执行健康检查...")
74
+ health = storage.health_check()
75
+ status = health.get("status")
76
+ if status == "healthy":
77
+ print(f"✅ 健康状态: {status}")
78
+ else:
79
+ print(f"❌ 健康状态: {status}")
80
+ print(f" 错误: {health.get('error')}")
81
+ return False
82
+
83
+ # 读取数据
84
+ print("\n[4/5] 读取账号数据...")
85
+ accounts = storage.load_accounts()
86
+ print(f"✅ 成功读取 {len(accounts)} 个账号")
87
+
88
+ # 写入测试(可选)
89
+ print("\n[5/5] 测试写入功能...")
90
+ test_account = {
91
+ "access_token": "test_token_" + str(os.getpid()),
92
+ "type": "Free",
93
+ "status": "测试",
94
+ "quota": 0,
95
+ "email": "test@example.com",
96
+ }
97
+
98
+ # 添加测试账号
99
+ test_accounts = accounts + [test_account]
100
+ storage.save_accounts(test_accounts)
101
+ print("✅ 写入测试账号成功")
102
+
103
+ # 验证写入
104
+ reloaded = storage.load_accounts()
105
+ if len(reloaded) == len(test_accounts):
106
+ print("✅ 验证写入成功")
107
+ else:
108
+ print(f"❌ 验证失败: 期望 {len(test_accounts)} 个账号,实际 {len(reloaded)} 个")
109
+ return False
110
+
111
+ # 恢复原始数据
112
+ storage.save_accounts(accounts)
113
+ print("✅ 恢复原始数据")
114
+
115
+ print("\n" + "=" * 60)
116
+ print("✅ 所有测试通过!")
117
+ print("=" * 60)
118
+ return True
119
+
120
+ except Exception as e:
121
+ print(f"\n❌ 测试失败: {e}")
122
+ import traceback
123
+ traceback.print_exc()
124
+ return False
125
+
126
+
127
+ if __name__ == "__main__":
128
+ success = test_storage()
129
+ sys.exit(0 if success else 1)
@@ -1,92 +1,92 @@
1
- """验证 OAuth 账号的自动刷新是否生效。
2
-
3
- 用法(在容器内,工作目录 /app):
4
- uv run python scripts/verify_oauth_refresh.py # 只读诊断,安全
5
- uv run python scripts/verify_oauth_refresh.py --force # 真正触发一次刷新
6
-
7
- 只读模式:列出每个账号的 access_token 剩余有效期、是否带 refresh_token、
8
- 上次刷新时间与上次刷新错误,判断"有没有料可刷"。
9
- --force :对每个带 refresh_token 的账号强制走一次 refresh_access_token(force=True),
10
- 直接验证 refresh_token + 本项目 client_id 能否从 OpenAI 换出新 access_token。
11
- 这会真实轮换 access_token(新 token 有效,不损坏账号)。
12
- """
13
- from __future__ import annotations
14
-
15
- import sys
16
-
17
- from services.account_service import account_service
18
-
19
-
20
- def _fmt_remaining(seconds: int | None) -> str:
21
- if seconds is None:
22
- return "无法解析 exp"
23
- if seconds <= 0:
24
- return f"已过期 {(-seconds) // 3600}h"
25
- return f"{seconds // 3600}h{(seconds % 3600) // 60}m 后过期"
26
-
27
-
28
- def diagnose() -> list[str]:
29
- """只读:打印每个账号的刷新就绪状态,返回带 refresh_token 的 token 列表。"""
30
- tokens = account_service.list_tokens()
31
- print(f"账号总数: {len(tokens)}\n")
32
- refreshable: list[str] = []
33
- for token in tokens:
34
- account = account_service.get_account(token) or {}
35
- remaining = account_service._token_expires_in(token)
36
- has_rt = bool(str(account.get("refresh_token") or "").strip())
37
- if has_rt:
38
- refreshable.append(token)
39
- print(f"- email={account.get('email') or '(未知)'}")
40
- print(f" access_token[:20] = {token[:20]}...")
41
- print(f" 距过期 = {_fmt_remaining(remaining)}")
42
- print(f" refresh_token = {'有 ✅' if has_rt else '无 ❌(无法自动刷新)'}")
43
- print(f" last_token_refresh_at = {account.get('last_token_refresh_at')}")
44
- print(f" last_token_refresh_error = {account.get('last_token_refresh_error')}")
45
- print()
46
- return refreshable
47
-
48
-
49
- def force_refresh(tokens: list[str]) -> None:
50
- """对每个账号 force 刷新一次,并对比前后状态判断成败。"""
51
- if not tokens:
52
- print("没有带 refresh_token 的账号,无法验证刷新。")
53
- return
54
- print("=" * 60)
55
- print(f"开始对 {len(tokens)} 个账号 force 刷新(真实调用 OpenAI)...\n")
56
- ok = 0
57
- for token in tokens:
58
- before = account_service.get_account(token) or {}
59
- new_token = account_service.refresh_access_token(token, force=True, event="manual_verify")
60
- after = account_service.get_account(new_token) or {}
61
- err = str(after.get("last_token_refresh_error") or "").strip()
62
- rotated = new_token != token
63
- success = bool(new_token) and not err
64
- if success:
65
- ok += 1
66
- print(f"- email={before.get('email') or '(未知)'}")
67
- print(f" 旧 access_token[:20] = {token[:20]}...")
68
- print(f" 新 access_token[:20] = {new_token[:20]}...")
69
- print(f" token 是否轮换 = {'是' if rotated else '否(exp 未到刷新窗口时可能返回原值)'}")
70
- print(f" last_token_refresh_at = {after.get('last_token_refresh_at')}")
71
- print(f" last_token_refresh_error = {after.get('last_token_refresh_error') or '无'}")
72
- print(f" >>> 刷新结果 = {'成功 ✅' if success else '失败 ❌'}")
73
- print()
74
- print("=" * 60)
75
- print(f"汇总: {ok}/{len(tokens)} 个账号刷新成功")
76
- if ok == len(tokens):
77
- print("✅ 自动刷新机制对这些账号完全可用——refresh_token 与 client_id 匹配。")
78
- else:
79
- print("❌ 有账号刷新失败,看上面的 last_token_refresh_error,或 docker logs 里的 [oauth-login]/refresh 日志。")
80
-
81
-
82
- def main() -> None:
83
- do_force = "--force" in sys.argv[1:]
84
- refreshable = diagnose()
85
- if do_force:
86
- force_refresh(refreshable)
87
- else:
88
- print("提示: 加 --force 参数可真正触发一次刷新以验证能否成功。")
89
-
90
-
91
- if __name__ == "__main__":
92
- main()
1
+ """验证 OAuth 账号的自动刷新是否生效。
2
+
3
+ 用法(在容器内,工作目录 /app):
4
+ uv run python scripts/verify_oauth_refresh.py # 只读诊断,安全
5
+ uv run python scripts/verify_oauth_refresh.py --force # 真正触发一次刷新
6
+
7
+ 只读模式:列出每个账号的 access_token 剩余有效期、是否带 refresh_token、
8
+ 上次刷新时间与上次刷新错误,判断"有没有料可刷"。
9
+ --force :对每个带 refresh_token 的账号强制走一次 refresh_access_token(force=True),
10
+ 直接验证 refresh_token + 本项目 client_id 能否从 OpenAI 换出新 access_token。
11
+ 这会真实轮换 access_token(新 token 有效,不损坏账号)。
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import sys
16
+
17
+ from services.account_service import account_service
18
+
19
+
20
+ def _fmt_remaining(seconds: int | None) -> str:
21
+ if seconds is None:
22
+ return "无法解析 exp"
23
+ if seconds <= 0:
24
+ return f"已过期 {(-seconds) // 3600}h"
25
+ return f"{seconds // 3600}h{(seconds % 3600) // 60}m 后过期"
26
+
27
+
28
+ def diagnose() -> list[str]:
29
+ """只读:打印每个账号的刷新就绪状态,返回带 refresh_token 的 token 列表。"""
30
+ tokens = account_service.list_tokens()
31
+ print(f"账号总数: {len(tokens)}\n")
32
+ refreshable: list[str] = []
33
+ for token in tokens:
34
+ account = account_service.get_account(token) or {}
35
+ remaining = account_service._token_expires_in(token)
36
+ has_rt = bool(str(account.get("refresh_token") or "").strip())
37
+ if has_rt:
38
+ refreshable.append(token)
39
+ print(f"- email={account.get('email') or '(未知)'}")
40
+ print(f" access_token[:20] = {token[:20]}...")
41
+ print(f" 距过期 = {_fmt_remaining(remaining)}")
42
+ print(f" refresh_token = {'有 ✅' if has_rt else '无 ❌(无法自动刷新)'}")
43
+ print(f" last_token_refresh_at = {account.get('last_token_refresh_at')}")
44
+ print(f" last_token_refresh_error = {account.get('last_token_refresh_error')}")
45
+ print()
46
+ return refreshable
47
+
48
+
49
+ def force_refresh(tokens: list[str]) -> None:
50
+ """对每个账号 force 刷新一次,并对比前后状态判断成败。"""
51
+ if not tokens:
52
+ print("没有带 refresh_token 的账号,无法验证刷新。")
53
+ return
54
+ print("=" * 60)
55
+ print(f"开始对 {len(tokens)} 个账号 force 刷新(真实调用 OpenAI)...\n")
56
+ ok = 0
57
+ for token in tokens:
58
+ before = account_service.get_account(token) or {}
59
+ new_token = account_service.refresh_access_token(token, force=True, event="manual_verify")
60
+ after = account_service.get_account(new_token) or {}
61
+ err = str(after.get("last_token_refresh_error") or "").strip()
62
+ rotated = new_token != token
63
+ success = bool(new_token) and not err
64
+ if success:
65
+ ok += 1
66
+ print(f"- email={before.get('email') or '(未知)'}")
67
+ print(f" 旧 access_token[:20] = {token[:20]}...")
68
+ print(f" 新 access_token[:20] = {new_token[:20]}...")
69
+ print(f" token 是否轮换 = {'是' if rotated else '否(exp 未到刷新窗口时可能返回原值)'}")
70
+ print(f" last_token_refresh_at = {after.get('last_token_refresh_at')}")
71
+ print(f" last_token_refresh_error = {after.get('last_token_refresh_error') or '无'}")
72
+ print(f" >>> 刷新结果 = {'成功 ✅' if success else '失败 ❌'}")
73
+ print()
74
+ print("=" * 60)
75
+ print(f"汇总: {ok}/{len(tokens)} 个账号刷新成功")
76
+ if ok == len(tokens):
77
+ print("✅ 自动刷新机制对这些账号完全可用——refresh_token 与 client_id 匹配。")
78
+ else:
79
+ print("❌ 有账号刷新失败,看上面的 last_token_refresh_error,或 docker logs 里的 [oauth-login]/refresh 日志。")
80
+
81
+
82
+ def main() -> None:
83
+ do_force = "--force" in sys.argv[1:]
84
+ refreshable = diagnose()
85
+ if do_force:
86
+ force_refresh(refreshable)
87
+ else:
88
+ print("提示: 加 --force 参数可真正触发一次刷新以验证能否成功。")
89
+
90
+
91
+ if __name__ == "__main__":
92
+ main()