dsh-plugin-image-workbench 1.1.5 → 1.1.7

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 (173) 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 +1371 -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/server/static/index.html +15 -0
  25. package/vendor/chatgpt2api/source/.env.example +82 -82
  26. package/vendor/chatgpt2api/source/.python-version +1 -1
  27. package/vendor/chatgpt2api/source/LICENSE +21 -21
  28. package/vendor/chatgpt2api/source/README.md +391 -391
  29. package/vendor/chatgpt2api/source/VERSION +1 -1
  30. package/vendor/chatgpt2api/source/api/__init__.py +2 -2
  31. package/vendor/chatgpt2api/source/api/accounts.py +537 -537
  32. package/vendor/chatgpt2api/source/api/ai.py +217 -217
  33. package/vendor/chatgpt2api/source/api/app.py +62 -62
  34. package/vendor/chatgpt2api/source/api/errors.py +46 -46
  35. package/vendor/chatgpt2api/source/api/image_inputs.py +309 -309
  36. package/vendor/chatgpt2api/source/api/image_tasks.py +120 -120
  37. package/vendor/chatgpt2api/source/api/support.py +134 -134
  38. package/vendor/chatgpt2api/source/api/system.py +365 -365
  39. package/vendor/chatgpt2api/source/assets/kunrouai.svg +50 -50
  40. package/vendor/chatgpt2api/source/config.default.json +108 -108
  41. package/vendor/chatgpt2api/source/main.py +9 -9
  42. package/vendor/chatgpt2api/source/pyproject.toml +27 -27
  43. package/vendor/chatgpt2api/source/scripts/init_proxy_config.py +186 -186
  44. package/vendor/chatgpt2api/source/scripts/migrate_storage.py +163 -163
  45. package/vendor/chatgpt2api/source/scripts/privoxy-warp.conf +13 -13
  46. package/vendor/chatgpt2api/source/scripts/test_outlook_token_mailbox.py +385 -385
  47. package/vendor/chatgpt2api/source/scripts/test_storage.py +129 -129
  48. package/vendor/chatgpt2api/source/scripts/verify_oauth_refresh.py +92 -92
  49. package/vendor/chatgpt2api/source/services/account_service.py +1757 -1757
  50. package/vendor/chatgpt2api/source/services/auth_service.py +253 -253
  51. package/vendor/chatgpt2api/source/services/backup_service.py +671 -671
  52. package/vendor/chatgpt2api/source/services/config.py +667 -667
  53. package/vendor/chatgpt2api/source/services/content_filter.py +242 -242
  54. package/vendor/chatgpt2api/source/services/cpa_service.py +315 -315
  55. package/vendor/chatgpt2api/source/services/editable_file_task_service.py +260 -260
  56. package/vendor/chatgpt2api/source/services/image_service.py +376 -376
  57. package/vendor/chatgpt2api/source/services/image_storage_service.py +405 -405
  58. package/vendor/chatgpt2api/source/services/image_tags_service.py +76 -76
  59. package/vendor/chatgpt2api/source/services/image_task_service.py +550 -550
  60. package/vendor/chatgpt2api/source/services/log_service.py +337 -337
  61. package/vendor/chatgpt2api/source/services/model_service.py +182 -182
  62. package/vendor/chatgpt2api/source/services/oauth_login_service.py +267 -267
  63. package/vendor/chatgpt2api/source/services/openai_backend_api.py +2763 -2763
  64. package/vendor/chatgpt2api/source/services/openai_oauth.py +32 -32
  65. package/vendor/chatgpt2api/source/services/protocol/__init__.py +2 -2
  66. package/vendor/chatgpt2api/source/services/protocol/anthropic_v1_messages.py +307 -307
  67. package/vendor/chatgpt2api/source/services/protocol/chat_completion_cache.py +238 -238
  68. package/vendor/chatgpt2api/source/services/protocol/conversation.py +1644 -1644
  69. package/vendor/chatgpt2api/source/services/protocol/error_response.py +124 -124
  70. package/vendor/chatgpt2api/source/services/protocol/openai_search.py +19 -19
  71. package/vendor/chatgpt2api/source/services/protocol/openai_v1_chat_complete.py +316 -316
  72. package/vendor/chatgpt2api/source/services/protocol/openai_v1_image_edit.py +86 -86
  73. package/vendor/chatgpt2api/source/services/protocol/openai_v1_image_generations.py +42 -42
  74. package/vendor/chatgpt2api/source/services/protocol/openai_v1_models.py +53 -53
  75. package/vendor/chatgpt2api/source/services/protocol/openai_v1_response.py +453 -453
  76. package/vendor/chatgpt2api/source/services/protocol/web_search_tool.py +160 -160
  77. package/vendor/chatgpt2api/source/services/proxy_service.py +648 -648
  78. package/vendor/chatgpt2api/source/services/storage/__init__.py +5 -5
  79. package/vendor/chatgpt2api/source/services/storage/base.py +38 -38
  80. package/vendor/chatgpt2api/source/services/storage/database_storage.py +194 -194
  81. package/vendor/chatgpt2api/source/services/storage/factory.py +104 -104
  82. package/vendor/chatgpt2api/source/services/storage/git_storage.py +183 -183
  83. package/vendor/chatgpt2api/source/services/storage/json_storage.py +95 -95
  84. package/vendor/chatgpt2api/source/services/sub2api_service.py +531 -531
  85. package/vendor/chatgpt2api/source/utils/helper.py +469 -469
  86. package/vendor/chatgpt2api/source/utils/image_tokens.py +346 -346
  87. package/vendor/chatgpt2api/source/utils/log.py +110 -110
  88. package/vendor/chatgpt2api/source/utils/pkce.py +19 -19
  89. package/vendor/chatgpt2api/source/utils/pow.py +207 -207
  90. package/vendor/chatgpt2api/source/utils/sentinel.py +159 -159
  91. package/vendor/chatgpt2api/source/utils/turnstile.py +184 -184
  92. package/vendor/chatgpt2api/source/uv.lock +904 -904
  93. package/vendor/chatgpt2api/source/web/bun.lock +1501 -1501
  94. package/vendor/chatgpt2api/source/web/components.json +24 -24
  95. package/vendor/chatgpt2api/source/web/eslint.config.mjs +17 -17
  96. package/vendor/chatgpt2api/source/web/next.config.ts +40 -40
  97. package/vendor/chatgpt2api/source/web/package.json +58 -58
  98. package/vendor/chatgpt2api/source/web/postcss.config.mjs +5 -5
  99. package/vendor/chatgpt2api/source/web/src/app/accounts/components/account-import-dialog.tsx +925 -925
  100. package/vendor/chatgpt2api/source/web/src/app/accounts/page.tsx +1307 -1307
  101. package/vendor/chatgpt2api/source/web/src/app/debug/components/chat-panel.tsx +224 -224
  102. package/vendor/chatgpt2api/source/web/src/app/debug/components/editable-file-panel.tsx +444 -444
  103. package/vendor/chatgpt2api/source/web/src/app/debug/components/ppt-panel.tsx +9 -9
  104. package/vendor/chatgpt2api/source/web/src/app/debug/components/psd-panel.tsx +9 -9
  105. package/vendor/chatgpt2api/source/web/src/app/debug/components/search-panel.tsx +172 -172
  106. package/vendor/chatgpt2api/source/web/src/app/debug/components/skill-panel.tsx +169 -169
  107. package/vendor/chatgpt2api/source/web/src/app/debug/components/types.ts +39 -39
  108. package/vendor/chatgpt2api/source/web/src/app/debug/page.tsx +59 -59
  109. package/vendor/chatgpt2api/source/web/src/app/globals.css +230 -230
  110. package/vendor/chatgpt2api/source/web/src/app/image/components/image-composer.tsx +542 -542
  111. package/vendor/chatgpt2api/source/web/src/app/image/components/image-results.tsx +554 -554
  112. package/vendor/chatgpt2api/source/web/src/app/image/components/image-sidebar.tsx +176 -176
  113. package/vendor/chatgpt2api/source/web/src/app/image/page.tsx +1799 -1799
  114. package/vendor/chatgpt2api/source/web/src/app/image-manager/page.tsx +732 -732
  115. package/vendor/chatgpt2api/source/web/src/app/layout.tsx +50 -50
  116. package/vendor/chatgpt2api/source/web/src/app/login/page.tsx +101 -101
  117. package/vendor/chatgpt2api/source/web/src/app/logs/page.tsx +356 -356
  118. package/vendor/chatgpt2api/source/web/src/app/page.tsx +30 -30
  119. package/vendor/chatgpt2api/source/web/src/app/settings/components/api-docs-card.tsx +341 -341
  120. package/vendor/chatgpt2api/source/web/src/app/settings/components/backup-settings-card.tsx +447 -447
  121. package/vendor/chatgpt2api/source/web/src/app/settings/components/base-url-card.tsx +71 -71
  122. package/vendor/chatgpt2api/source/web/src/app/settings/components/config-card.tsx +491 -491
  123. package/vendor/chatgpt2api/source/web/src/app/settings/components/cpa-pool-dialog.tsx +108 -108
  124. package/vendor/chatgpt2api/source/web/src/app/settings/components/cpa-pools-card.tsx +169 -169
  125. package/vendor/chatgpt2api/source/web/src/app/settings/components/import-browser-dialog.tsx +185 -185
  126. package/vendor/chatgpt2api/source/web/src/app/settings/components/proxy-runtime-card.tsx +379 -379
  127. package/vendor/chatgpt2api/source/web/src/app/settings/components/proxy-settings-card.tsx +127 -127
  128. package/vendor/chatgpt2api/source/web/src/app/settings/components/proxy-settings.tsx +200 -200
  129. package/vendor/chatgpt2api/source/web/src/app/settings/components/settings-header.tsx +12 -12
  130. package/vendor/chatgpt2api/source/web/src/app/settings/components/sub2api-connections.tsx +886 -886
  131. package/vendor/chatgpt2api/source/web/src/app/settings/components/third-party-apps-card.tsx +81 -81
  132. package/vendor/chatgpt2api/source/web/src/app/settings/components/user-keys-card.tsx +422 -422
  133. package/vendor/chatgpt2api/source/web/src/app/settings/page.tsx +135 -135
  134. package/vendor/chatgpt2api/source/web/src/app/settings/store.ts +1069 -1069
  135. package/vendor/chatgpt2api/source/web/src/components/date-range-filter.tsx +49 -49
  136. package/vendor/chatgpt2api/source/web/src/components/header-actions.tsx +24 -24
  137. package/vendor/chatgpt2api/source/web/src/components/image-lightbox.tsx +434 -434
  138. package/vendor/chatgpt2api/source/web/src/components/image-thumbnail.tsx +46 -46
  139. package/vendor/chatgpt2api/source/web/src/components/theme-script.tsx +18 -18
  140. package/vendor/chatgpt2api/source/web/src/components/theme-toggle.tsx +14 -14
  141. package/vendor/chatgpt2api/source/web/src/components/top-nav.tsx +263 -263
  142. package/vendor/chatgpt2api/source/web/src/components/ui/animated-theme-toggler.tsx +256 -256
  143. package/vendor/chatgpt2api/source/web/src/components/ui/badge.tsx +41 -41
  144. package/vendor/chatgpt2api/source/web/src/components/ui/button.tsx +59 -59
  145. package/vendor/chatgpt2api/source/web/src/components/ui/calendar.tsx +46 -46
  146. package/vendor/chatgpt2api/source/web/src/components/ui/card.tsx +61 -61
  147. package/vendor/chatgpt2api/source/web/src/components/ui/checkbox.tsx +27 -27
  148. package/vendor/chatgpt2api/source/web/src/components/ui/dia-text-reveal.tsx +267 -267
  149. package/vendor/chatgpt2api/source/web/src/components/ui/dialog.tsx +132 -132
  150. package/vendor/chatgpt2api/source/web/src/components/ui/field.tsx +13 -13
  151. package/vendor/chatgpt2api/source/web/src/components/ui/input.tsx +19 -19
  152. package/vendor/chatgpt2api/source/web/src/components/ui/label.tsx +24 -24
  153. package/vendor/chatgpt2api/source/web/src/components/ui/morphing-text.tsx +149 -149
  154. package/vendor/chatgpt2api/source/web/src/components/ui/popover.tsx +38 -38
  155. package/vendor/chatgpt2api/source/web/src/components/ui/select.tsx +175 -175
  156. package/vendor/chatgpt2api/source/web/src/components/ui/sheet.tsx +143 -143
  157. package/vendor/chatgpt2api/source/web/src/components/ui/table.tsx +29 -29
  158. package/vendor/chatgpt2api/source/web/src/components/ui/tabs.tsx +91 -91
  159. package/vendor/chatgpt2api/source/web/src/components/ui/text-3d-flip.tsx +269 -269
  160. package/vendor/chatgpt2api/source/web/src/components/ui/textarea.tsx +18 -18
  161. package/vendor/chatgpt2api/source/web/src/components/version-release-dialog.tsx +127 -127
  162. package/vendor/chatgpt2api/source/web/src/constants/common-env.ts +6 -6
  163. package/vendor/chatgpt2api/source/web/src/hooks/use-version-check.ts +85 -85
  164. package/vendor/chatgpt2api/source/web/src/lib/api.ts +926 -926
  165. package/vendor/chatgpt2api/source/web/src/lib/auth-session.ts +26 -26
  166. package/vendor/chatgpt2api/source/web/src/lib/release.ts +25 -25
  167. package/vendor/chatgpt2api/source/web/src/lib/request.ts +93 -93
  168. package/vendor/chatgpt2api/source/web/src/lib/use-auth-guard.ts +89 -89
  169. package/vendor/chatgpt2api/source/web/src/lib/utils.ts +6 -6
  170. package/vendor/chatgpt2api/source/web/src/store/auth.ts +112 -112
  171. package/vendor/chatgpt2api/source/web/src/store/editable-file-history.ts +33 -33
  172. package/vendor/chatgpt2api/source/web/src/store/image-conversations.ts +318 -318
  173. 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()