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,202 +1,202 @@
1
- <!DOCTYPE html>
2
- <html lang="zh-CN">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>申请访问 · 手绘工坊号池工作台</title>
7
- <style>
8
- :root{
9
- --bg:#f5f3fb; --panel:#ffffff; --ink:#1f1b2e; --ink-2:#5b5573; --ink-3:#8b86a3;
10
- --line:#e8e4f5; --primary:#6a5ae0; --primary-2:#8b7cf6; --pink:#ff6b9a;
11
- --ok:#12b886; --err:#e03131; --chip:#f1eefe;
12
- }
13
- *{box-sizing:border-box}
14
- [hidden]{display:none!important}
15
- body{margin:0;min-height:100vh;color:var(--ink);
16
- font:14px/1.7 -apple-system,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
17
- background:radial-gradient(1100px 620px at 12% -12%,#e6dfff 0%,transparent 62%),
18
- radial-gradient(900px 560px at 100% 0%,#ffe7f0 0%,transparent 58%),
19
- var(--bg);
20
- display:flex;align-items:center;justify-content:center;padding:24px}
21
- .card{width:100%;max-width:420px;background:var(--panel);border:1px solid var(--line);
22
- border-radius:20px;box-shadow:0 18px 60px rgba(80,60,160,.13);padding:30px 28px 24px}
23
- .brand{display:inline-flex;align-items:center;gap:8px;font-size:11px;letter-spacing:.6px;
24
- color:var(--primary);background:var(--chip);border:1px solid #ddd6fb;border-radius:20px;
25
- padding:5px 12px;margin-bottom:18px}
26
- .brand i{width:7px;height:7px;border-radius:50%;background:var(--primary);display:block}
27
- h1{font-size:22px;margin:0 0 6px;letter-spacing:.5px}
28
- .lead{font-size:12.5px;color:var(--ink-2);margin:0 0 22px;line-height:1.8}
29
- .lead b{color:var(--ink);font-weight:600}
30
- .steps{display:flex;gap:6px;margin:0 0 20px}
31
- .steps span{flex:1;height:4px;border-radius:4px;background:var(--line)}
32
- .steps span.on{background:linear-gradient(90deg,var(--primary),var(--pink))}
33
- label{display:block;font-size:11.5px;color:var(--ink-2);margin:0 0 7px;font-weight:600}
34
- input{width:100%;border:1px solid var(--line);border-radius:11px;background:#fbfaff;
35
- padding:12px 13px;font:inherit;color:var(--ink);outline:none;transition:.15s}
36
- input:focus{border-color:var(--primary-2);background:#fff;box-shadow:0 0 0 3px rgba(139,124,246,.14)}
37
- input.code{font-size:22px;letter-spacing:9px;text-align:center;font-weight:600;
38
- font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
39
- .btn{width:100%;border:0;border-radius:11px;padding:12px 14px;font:inherit;font-weight:600;
40
- cursor:pointer;margin-top:14px;transition:.15s;background:#f1eefe;color:var(--primary)}
41
- .btn:hover{filter:brightness(.97)}
42
- .btn.primary{background:linear-gradient(135deg,var(--primary),var(--pink));color:#fff;
43
- box-shadow:0 8px 20px rgba(106,90,224,.26)}
44
- .btn.ghost{background:#fff;border:1px solid var(--line);color:var(--ink-2);font-weight:500}
45
- .btn:disabled{opacity:.55;cursor:not-allowed}
46
- .msg{margin-top:14px;font-size:12px;line-height:1.7;border-radius:10px;padding:0;min-height:0}
47
- .msg:not(:empty){padding:10px 12px;border:1px solid var(--line);background:#faf9ff;color:var(--ink-2)}
48
- .msg.err{border-color:#ffd6d6;background:#fff5f5;color:#c92a2a}
49
- .msg.ok{border-color:#c3f0dd;background:#f2fcf8;color:#087f5b}
50
- .notice{background:#f2fcf8;border:1px solid #c3f0dd;color:#087f5b;border-radius:11px;
51
- padding:11px 13px;font-size:12px;line-height:1.75;margin:0 0 18px}
52
- .foot{margin-top:22px;padding-top:16px;border-top:1px solid var(--line);
53
- display:flex;justify-content:space-between;align-items:center;font-size:11px;color:var(--ink-3)}
54
- .foot a{color:var(--ink-3);text-decoration:none;cursor:pointer;border-bottom:1px dashed var(--line)}
55
- .foot a:hover{color:var(--primary)}
56
- .admin{margin-top:16px;padding:14px;border:1px solid var(--line);border-radius:12px;background:#fbfaff}
57
- .admin p{margin:0 0 10px;font-size:11.5px;color:var(--ink-2);line-height:1.7}
58
- .hint{font-size:11px;color:var(--ink-3);margin:12px 0 0;line-height:1.7}
59
- </style>
60
- </head>
61
- <body>
62
- <div class="card">
63
- <div class="brand"><i></i>手绘工坊 · 号池工作台</div>
64
- <h1>申请访问</h1>
65
- <p class="lead">工作台仅供受邀使用。填写你的邮箱点<b>申请访问</b>,管理员会把验证码回复到这个邮箱。收到后回到本页填入邮箱和验证码即可进入。</p>
66
- <div class="steps"><span id="s1" class="on"></span><span id="s2"></span></div>
67
-
68
- <div id="step1">
69
- <label for="email">申请邮箱</label>
70
- <input id="email" type="email" maxlength="254" autocomplete="email" spellcheck="false" placeholder="name@example.com">
71
- <button id="reqBtn" class="btn primary" type="button">申请访问</button>
72
- <p class="hint">请填能收信的邮箱,管理员会把验证码回复到这里。</p>
73
- </div>
74
-
75
- <div id="step2" hidden>
76
- <div class="notice" id="notice">申请已提交,验证码会回复到你填的邮箱。</div>
77
- <label for="email2">申请时用的邮箱</label>
78
- <input id="email2" type="email" maxlength="254" autocomplete="email" spellcheck="false">
79
- <label for="code" style="margin-top:14px">验证码</label>
80
- <input id="code" class="code" type="text" inputmode="numeric" autocomplete="one-time-code" placeholder="000000">
81
- <button id="verBtn" class="btn primary" type="button">进入工作台</button>
82
- <button id="backBtn" class="btn ghost" type="button">换一个邮箱重新申请</button>
83
- </div>
84
-
85
- <div id="msg" class="msg" role="status"></div>
86
-
87
- <div class="foot">
88
- <span>验证码 24 小时有效 · 一次性 · 与邮箱绑定</span>
89
- <a id="adminToggle" type="button">管理员入口</a>
90
- </div>
91
- <div id="adminBox" class="admin" hidden>
92
- <p>管理员可凭主口令直接进入,不需要申请验证码。主口令只保存在服务器上,请勿写进共享文档。</p>
93
- <label for="master">主口令</label>
94
- <input id="master" type="password" autocomplete="current-password" placeholder="输入主口令">
95
- <button id="masterBtn" class="btn" type="button">进入工作台</button>
96
- </div>
97
- </div>
98
-
99
- <script>
100
- (function(){
101
- var $ = function(id){ return document.getElementById(id); };
102
- var msg = $('msg');
103
- function say(t, cls){
104
- msg.textContent = t || '';
105
- msg.className = 'msg' + (cls ? ' ' + cls : '');
106
- }
107
- function busy(btn, on){
108
- if(on){
109
- if(!btn.dataset.label) btn.dataset.label = btn.textContent;
110
- btn.disabled = true;
111
- btn.textContent = '处理中…';
112
- }else{
113
- btn.disabled = false;
114
- btn.textContent = btn.dataset.label || btn.textContent;
115
- }
116
- }
117
- function post(url, data){
118
- return fetch(url, {method:'POST', headers:{'Content-Type':'application/json'},
119
- body:JSON.stringify(data)}).then(function(r){
120
- return r.json().catch(function(){ return {}; }).then(function(d){
121
- return {status:r.status, data:d};
122
- });
123
- });
124
- }
125
-
126
- function showStep(n){
127
- $('step1').hidden = (n !== 1);
128
- $('step2').hidden = (n !== 2);
129
- $('s2').className = (n === 2 ? 'on' : '');
130
- say('');
131
- }
132
-
133
- $('reqBtn').addEventListener('click', function(){
134
- var u = $('email').value.trim().toLowerCase();
135
- if(!u){ say('请先填申请邮箱', 'err'); return; }
136
- var badMail = !/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(u) || u.indexOf('..') >= 0 || u.charAt(0) === '.';
137
- if(badMail){ say('请填有效的邮箱地址', 'err'); return; }
138
- var btn = this; busy(btn, true);
139
- post('/api/access/request', {email:u}).then(function(res){
140
- busy(btn, false);
141
- if(!res.data.ok){ say(res.data.msg || '提交失败,请稍后重试', 'err'); return; }
142
- sessionStorage.setItem('wb_access_email', u);
143
- $('email2').value = u;
144
- showStep(2);
145
- $('notice').textContent = res.data.reused
146
- ? '该邮箱已有一份待审核的申请,请联系管理员索取之前发出的验证码。'
147
- : '申请已提交,管理员会把验证码回复到这个邮箱,填入下方即可进入。';
148
- $('code').focus();
149
- }).catch(function(){ busy(btn, false); say('网络异常,请稍后重试', 'err'); });
150
- });
151
-
152
- $('verBtn').addEventListener('click', function(){
153
- var u = $('email2').value.trim().toLowerCase();
154
- var c = $('code').value.trim();
155
- if(!u){ say('请填申请时用的邮箱', 'err'); return; }
156
- if(!/^\d{6}$/.test(c)){ say('验证码是 6 位数字', 'err'); return; }
157
- var btn = this; busy(btn, true);
158
- post('/api/access/verify', {email:u, code:c}).then(function(res){
159
- busy(btn, false);
160
- if(!res.data.ok){ say(res.data.msg || '验证失败', 'err'); return; }
161
- sessionStorage.removeItem('wb_access_email');
162
- location.href = '/';
163
- }).catch(function(){ busy(btn, false); say('网络异常,请稍后重试', 'err'); });
164
- });
165
-
166
- $('backBtn').addEventListener('click', function(){
167
- sessionStorage.removeItem('wb_access_email');
168
- $('code').value = '';
169
- showStep(1);
170
- $('email').focus();
171
- });
172
-
173
- $('adminToggle').addEventListener('click', function(){
174
- $('adminBox').hidden = !$('adminBox').hidden;
175
- if(!$('adminBox').hidden) $('master').focus();
176
- });
177
-
178
- $('masterBtn').addEventListener('click', function(){
179
- var p = $('master').value;
180
- if(!p){ say('请输入主口令', 'err'); return; }
181
- var btn = this; busy(btn, true);
182
- post('/api/access/master', {password:p}).then(function(res){
183
- busy(btn, false);
184
- if(!res.data.ok){ say(res.data.msg || '验证失败', 'err'); return; }
185
- location.href = '/';
186
- }).catch(function(){ busy(btn, false); say('网络异常,请稍后重试', 'err'); });
187
- });
188
-
189
- $('email').addEventListener('keydown', function(e){ if(e.key === 'Enter') $('reqBtn').click(); });
190
- $('code').addEventListener('keydown', function(e){ if(e.key === 'Enter') $('verBtn').click(); });
191
- $('master').addEventListener('keydown', function(e){ if(e.key === 'Enter') $('masterBtn').click(); });
192
- $('code').addEventListener('input', function(){ this.value = this.value.replace(/\D/g,'').slice(0,6); });
193
-
194
- var saved = sessionStorage.getItem('wb_access_email');
195
- if(saved){ $('email').value = saved; $('email2').value = saved; showStep(2); $('code').focus(); }
196
- if(new URLSearchParams(location.search).get('expired') === '1'){
197
- say('登录状态已失效,请重新申请验证码', 'err');
198
- }
199
- })();
200
- </script>
201
- </body>
202
- </html>
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>申请访问 · 手绘工坊号池工作台</title>
7
+ <style>
8
+ :root{
9
+ --bg:#f5f3fb; --panel:#ffffff; --ink:#1f1b2e; --ink-2:#5b5573; --ink-3:#8b86a3;
10
+ --line:#e8e4f5; --primary:#6a5ae0; --primary-2:#8b7cf6; --pink:#ff6b9a;
11
+ --ok:#12b886; --err:#e03131; --chip:#f1eefe;
12
+ }
13
+ *{box-sizing:border-box}
14
+ [hidden]{display:none!important}
15
+ body{margin:0;min-height:100vh;color:var(--ink);
16
+ font:14px/1.7 -apple-system,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;
17
+ background:radial-gradient(1100px 620px at 12% -12%,#e6dfff 0%,transparent 62%),
18
+ radial-gradient(900px 560px at 100% 0%,#ffe7f0 0%,transparent 58%),
19
+ var(--bg);
20
+ display:flex;align-items:center;justify-content:center;padding:24px}
21
+ .card{width:100%;max-width:420px;background:var(--panel);border:1px solid var(--line);
22
+ border-radius:20px;box-shadow:0 18px 60px rgba(80,60,160,.13);padding:30px 28px 24px}
23
+ .brand{display:inline-flex;align-items:center;gap:8px;font-size:11px;letter-spacing:.6px;
24
+ color:var(--primary);background:var(--chip);border:1px solid #ddd6fb;border-radius:20px;
25
+ padding:5px 12px;margin-bottom:18px}
26
+ .brand i{width:7px;height:7px;border-radius:50%;background:var(--primary);display:block}
27
+ h1{font-size:22px;margin:0 0 6px;letter-spacing:.5px}
28
+ .lead{font-size:12.5px;color:var(--ink-2);margin:0 0 22px;line-height:1.8}
29
+ .lead b{color:var(--ink);font-weight:600}
30
+ .steps{display:flex;gap:6px;margin:0 0 20px}
31
+ .steps span{flex:1;height:4px;border-radius:4px;background:var(--line)}
32
+ .steps span.on{background:linear-gradient(90deg,var(--primary),var(--pink))}
33
+ label{display:block;font-size:11.5px;color:var(--ink-2);margin:0 0 7px;font-weight:600}
34
+ input{width:100%;border:1px solid var(--line);border-radius:11px;background:#fbfaff;
35
+ padding:12px 13px;font:inherit;color:var(--ink);outline:none;transition:.15s}
36
+ input:focus{border-color:var(--primary-2);background:#fff;box-shadow:0 0 0 3px rgba(139,124,246,.14)}
37
+ input.code{font-size:22px;letter-spacing:9px;text-align:center;font-weight:600;
38
+ font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
39
+ .btn{width:100%;border:0;border-radius:11px;padding:12px 14px;font:inherit;font-weight:600;
40
+ cursor:pointer;margin-top:14px;transition:.15s;background:#f1eefe;color:var(--primary)}
41
+ .btn:hover{filter:brightness(.97)}
42
+ .btn.primary{background:linear-gradient(135deg,var(--primary),var(--pink));color:#fff;
43
+ box-shadow:0 8px 20px rgba(106,90,224,.26)}
44
+ .btn.ghost{background:#fff;border:1px solid var(--line);color:var(--ink-2);font-weight:500}
45
+ .btn:disabled{opacity:.55;cursor:not-allowed}
46
+ .msg{margin-top:14px;font-size:12px;line-height:1.7;border-radius:10px;padding:0;min-height:0}
47
+ .msg:not(:empty){padding:10px 12px;border:1px solid var(--line);background:#faf9ff;color:var(--ink-2)}
48
+ .msg.err{border-color:#ffd6d6;background:#fff5f5;color:#c92a2a}
49
+ .msg.ok{border-color:#c3f0dd;background:#f2fcf8;color:#087f5b}
50
+ .notice{background:#f2fcf8;border:1px solid #c3f0dd;color:#087f5b;border-radius:11px;
51
+ padding:11px 13px;font-size:12px;line-height:1.75;margin:0 0 18px}
52
+ .foot{margin-top:22px;padding-top:16px;border-top:1px solid var(--line);
53
+ display:flex;justify-content:space-between;align-items:center;font-size:11px;color:var(--ink-3)}
54
+ .foot a{color:var(--ink-3);text-decoration:none;cursor:pointer;border-bottom:1px dashed var(--line)}
55
+ .foot a:hover{color:var(--primary)}
56
+ .admin{margin-top:16px;padding:14px;border:1px solid var(--line);border-radius:12px;background:#fbfaff}
57
+ .admin p{margin:0 0 10px;font-size:11.5px;color:var(--ink-2);line-height:1.7}
58
+ .hint{font-size:11px;color:var(--ink-3);margin:12px 0 0;line-height:1.7}
59
+ </style>
60
+ </head>
61
+ <body>
62
+ <div class="card">
63
+ <div class="brand"><i></i>手绘工坊 · 号池工作台</div>
64
+ <h1>申请访问</h1>
65
+ <p class="lead">工作台仅供受邀使用。填写你的邮箱点<b>申请访问</b>,管理员会把验证码回复到这个邮箱。收到后回到本页填入邮箱和验证码即可进入。</p>
66
+ <div class="steps"><span id="s1" class="on"></span><span id="s2"></span></div>
67
+
68
+ <div id="step1">
69
+ <label for="email">申请邮箱</label>
70
+ <input id="email" type="email" maxlength="254" autocomplete="email" spellcheck="false" placeholder="name@example.com">
71
+ <button id="reqBtn" class="btn primary" type="button">申请访问</button>
72
+ <p class="hint">请填能收信的邮箱,管理员会把验证码回复到这里。</p>
73
+ </div>
74
+
75
+ <div id="step2" hidden>
76
+ <div class="notice" id="notice">申请已提交,验证码会回复到你填的邮箱。</div>
77
+ <label for="email2">申请时用的邮箱</label>
78
+ <input id="email2" type="email" maxlength="254" autocomplete="email" spellcheck="false">
79
+ <label for="code" style="margin-top:14px">验证码</label>
80
+ <input id="code" class="code" type="text" inputmode="numeric" autocomplete="one-time-code" placeholder="000000">
81
+ <button id="verBtn" class="btn primary" type="button">进入工作台</button>
82
+ <button id="backBtn" class="btn ghost" type="button">换一个邮箱重新申请</button>
83
+ </div>
84
+
85
+ <div id="msg" class="msg" role="status"></div>
86
+
87
+ <div class="foot">
88
+ <span>验证码 24 小时有效 · 一次性 · 与邮箱绑定</span>
89
+ <a id="adminToggle" type="button">管理员入口</a>
90
+ </div>
91
+ <div id="adminBox" class="admin" hidden>
92
+ <p>管理员可凭主口令直接进入,不需要申请验证码。主口令只保存在服务器上,请勿写进共享文档。</p>
93
+ <label for="master">主口令</label>
94
+ <input id="master" type="password" autocomplete="current-password" placeholder="输入主口令">
95
+ <button id="masterBtn" class="btn" type="button">进入工作台</button>
96
+ </div>
97
+ </div>
98
+
99
+ <script>
100
+ (function(){
101
+ var $ = function(id){ return document.getElementById(id); };
102
+ var msg = $('msg');
103
+ function say(t, cls){
104
+ msg.textContent = t || '';
105
+ msg.className = 'msg' + (cls ? ' ' + cls : '');
106
+ }
107
+ function busy(btn, on){
108
+ if(on){
109
+ if(!btn.dataset.label) btn.dataset.label = btn.textContent;
110
+ btn.disabled = true;
111
+ btn.textContent = '处理中…';
112
+ }else{
113
+ btn.disabled = false;
114
+ btn.textContent = btn.dataset.label || btn.textContent;
115
+ }
116
+ }
117
+ function post(url, data){
118
+ return fetch(url, {method:'POST', headers:{'Content-Type':'application/json'},
119
+ body:JSON.stringify(data)}).then(function(r){
120
+ return r.json().catch(function(){ return {}; }).then(function(d){
121
+ return {status:r.status, data:d};
122
+ });
123
+ });
124
+ }
125
+
126
+ function showStep(n){
127
+ $('step1').hidden = (n !== 1);
128
+ $('step2').hidden = (n !== 2);
129
+ $('s2').className = (n === 2 ? 'on' : '');
130
+ say('');
131
+ }
132
+
133
+ $('reqBtn').addEventListener('click', function(){
134
+ var u = $('email').value.trim().toLowerCase();
135
+ if(!u){ say('请先填申请邮箱', 'err'); return; }
136
+ var badMail = !/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(u) || u.indexOf('..') >= 0 || u.charAt(0) === '.';
137
+ if(badMail){ say('请填有效的邮箱地址', 'err'); return; }
138
+ var btn = this; busy(btn, true);
139
+ post('/api/access/request', {email:u}).then(function(res){
140
+ busy(btn, false);
141
+ if(!res.data.ok){ say(res.data.msg || '提交失败,请稍后重试', 'err'); return; }
142
+ sessionStorage.setItem('wb_access_email', u);
143
+ $('email2').value = u;
144
+ showStep(2);
145
+ $('notice').textContent = res.data.reused
146
+ ? '该邮箱已有一份待审核的申请,请联系管理员索取之前发出的验证码。'
147
+ : '申请已提交,管理员会把验证码回复到这个邮箱,填入下方即可进入。';
148
+ $('code').focus();
149
+ }).catch(function(){ busy(btn, false); say('网络异常,请稍后重试', 'err'); });
150
+ });
151
+
152
+ $('verBtn').addEventListener('click', function(){
153
+ var u = $('email2').value.trim().toLowerCase();
154
+ var c = $('code').value.trim();
155
+ if(!u){ say('请填申请时用的邮箱', 'err'); return; }
156
+ if(!/^\d{6}$/.test(c)){ say('验证码是 6 位数字', 'err'); return; }
157
+ var btn = this; busy(btn, true);
158
+ post('/api/access/verify', {email:u, code:c}).then(function(res){
159
+ busy(btn, false);
160
+ if(!res.data.ok){ say(res.data.msg || '验证失败', 'err'); return; }
161
+ sessionStorage.removeItem('wb_access_email');
162
+ location.href = '/';
163
+ }).catch(function(){ busy(btn, false); say('网络异常,请稍后重试', 'err'); });
164
+ });
165
+
166
+ $('backBtn').addEventListener('click', function(){
167
+ sessionStorage.removeItem('wb_access_email');
168
+ $('code').value = '';
169
+ showStep(1);
170
+ $('email').focus();
171
+ });
172
+
173
+ $('adminToggle').addEventListener('click', function(){
174
+ $('adminBox').hidden = !$('adminBox').hidden;
175
+ if(!$('adminBox').hidden) $('master').focus();
176
+ });
177
+
178
+ $('masterBtn').addEventListener('click', function(){
179
+ var p = $('master').value;
180
+ if(!p){ say('请输入主口令', 'err'); return; }
181
+ var btn = this; busy(btn, true);
182
+ post('/api/access/master', {password:p}).then(function(res){
183
+ busy(btn, false);
184
+ if(!res.data.ok){ say(res.data.msg || '验证失败', 'err'); return; }
185
+ location.href = '/';
186
+ }).catch(function(){ busy(btn, false); say('网络异常,请稍后重试', 'err'); });
187
+ });
188
+
189
+ $('email').addEventListener('keydown', function(e){ if(e.key === 'Enter') $('reqBtn').click(); });
190
+ $('code').addEventListener('keydown', function(e){ if(e.key === 'Enter') $('verBtn').click(); });
191
+ $('master').addEventListener('keydown', function(e){ if(e.key === 'Enter') $('masterBtn').click(); });
192
+ $('code').addEventListener('input', function(){ this.value = this.value.replace(/\D/g,'').slice(0,6); });
193
+
194
+ var saved = sessionStorage.getItem('wb_access_email');
195
+ if(saved){ $('email').value = saved; $('email2').value = saved; showStep(2); $('code').focus(); }
196
+ if(new URLSearchParams(location.search).get('expired') === '1'){
197
+ say('登录状态已失效,请重新申请验证码', 'err');
198
+ }
199
+ })();
200
+ </script>
201
+ </body>
202
+ </html>
@@ -1,82 +1,82 @@
1
- # ============================================
2
- # ChatGPT2API 环境变量配置示例
3
- # ============================================
4
-
5
- # 认证密钥(必填)
6
- CHATGPT2API_AUTH_KEY=your_secret_key_here
7
-
8
- # 基础 URL(可选,用于生成图片 URL)
9
- # CHATGPT2API_BASE_URL=https://your-domain.com
10
-
11
- # ============================================
12
- # 存储后端配置
13
- # ============================================
14
-
15
- # 存储后端类型(可选值: json, sqlite, postgres, git)
16
- # 默认: json
17
- STORAGE_BACKEND=json
18
-
19
- # ============================================
20
- # 数据库配置(当 STORAGE_BACKEND=sqlite/postgres 时使用)
21
- # ============================================
22
-
23
- # PostgreSQL 示例
24
- # DATABASE_URL=postgresql://user:password@localhost:5432/chatgpt2api
25
-
26
- # Supabase 示例
27
- # DATABASE_URL=postgresql://postgres.xxx:password@aws-1-us-west-1.pooler.supabase.com:5432/postgres
28
-
29
- # SQLite 示例(不指定时自动使用 data/accounts.db)
30
- # DATABASE_URL=sqlite:///app/data/accounts.db
31
-
32
- # ============================================
33
- # Git 仓库配置(当 STORAGE_BACKEND=git 时使用)
34
- # ============================================
35
-
36
- # Git 仓库地址(必填)
37
- # GIT_REPO_URL=https://github.com/your-username/your-private-repo.git
38
-
39
- # Git 访问令牌(必填)
40
- # GitHub: ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
41
- # GitLab: glpat-xxxxxxxxxxxxxxxxxxxx
42
- # GIT_TOKEN=your_git_token_here
43
-
44
- # Git 分支(可选,默认: main)
45
- # GIT_BRANCH=main
46
-
47
- # Git 仓库中的文件路径(可选,默认: accounts.json)
48
- # GIT_FILE_PATH=accounts.json
49
-
50
- # ============================================
51
- # WARP / FlareSolverr 稳定代理配置(可选)
52
- # ============================================
53
-
54
- # docker-compose.warp.yml 暴露到宿主机的端口
55
- # WARP_SOCKS_PORT=40000
56
- # PRIVOXY_PORT=40080
57
- # FLARESOLVERR_PORT=8191
58
-
59
- # FlareSolverr 日志与时区
60
- # FLARESOLVERR_LOG_LEVEL=info
61
- # TZ=Asia/Shanghai
62
-
63
- # Cloudflare WARP+ License Key(可选)
64
- # WARP_LICENSE_KEY=
65
-
66
- # 初始化 proxy_runtime 默认值
67
- # CHATGPT2API_PROXY_RUNTIME_ENABLED=true
68
- # CHATGPT2API_PROXY_RUNTIME_EGRESS_MODE=single_proxy
69
- # CHATGPT2API_PROXY_RUNTIME_PROXY_URL=http://privoxy:8118
70
- # CHATGPT2API_PROXY_RUNTIME_RESOURCE_PROXY_URL=
71
- # CHATGPT2API_PROXY_RUNTIME_SKIP_SSL_VERIFY=false
72
- # CHATGPT2API_PROXY_RUNTIME_RESET_STATUS_CODES=403
73
-
74
- # Cloudflare clearance 刷新配置
75
- # CHATGPT2API_PROXY_RUNTIME_CLEARANCE_ENABLED=true
76
- # CHATGPT2API_PROXY_RUNTIME_CLEARANCE_MODE=flaresolverr
77
- # CHATGPT2API_FLARESOLVERR_URL=http://flaresolverr:8191
78
- # CHATGPT2API_PROXY_RUNTIME_CLEARANCE_TIMEOUT_SEC=60
79
- # CHATGPT2API_PROXY_RUNTIME_CLEARANCE_REFRESH_INTERVAL=3600
80
- # CHATGPT2API_PROXY_RUNTIME_WARM_UP_ON_START=false
81
- # CHATGPT2API_PROXY_RUNTIME_BROWSER=chrome
82
- # CHATGPT2API_PROXY_RUNTIME_USER_AGENT=
1
+ # ============================================
2
+ # ChatGPT2API 环境变量配置示例
3
+ # ============================================
4
+
5
+ # 认证密钥(必填)
6
+ CHATGPT2API_AUTH_KEY=your_secret_key_here
7
+
8
+ # 基础 URL(可选,用于生成图片 URL)
9
+ # CHATGPT2API_BASE_URL=https://your-domain.com
10
+
11
+ # ============================================
12
+ # 存储后端配置
13
+ # ============================================
14
+
15
+ # 存储后端类型(可选值: json, sqlite, postgres, git)
16
+ # 默认: json
17
+ STORAGE_BACKEND=json
18
+
19
+ # ============================================
20
+ # 数据库配置(当 STORAGE_BACKEND=sqlite/postgres 时使用)
21
+ # ============================================
22
+
23
+ # PostgreSQL 示例
24
+ # DATABASE_URL=postgresql://user:password@localhost:5432/chatgpt2api
25
+
26
+ # Supabase 示例
27
+ # DATABASE_URL=postgresql://postgres.xxx:password@aws-1-us-west-1.pooler.supabase.com:5432/postgres
28
+
29
+ # SQLite 示例(不指定时自动使用 data/accounts.db)
30
+ # DATABASE_URL=sqlite:///app/data/accounts.db
31
+
32
+ # ============================================
33
+ # Git 仓库配置(当 STORAGE_BACKEND=git 时使用)
34
+ # ============================================
35
+
36
+ # Git 仓库地址(必填)
37
+ # GIT_REPO_URL=https://github.com/your-username/your-private-repo.git
38
+
39
+ # Git 访问令牌(必填)
40
+ # GitHub: ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
41
+ # GitLab: glpat-xxxxxxxxxxxxxxxxxxxx
42
+ # GIT_TOKEN=your_git_token_here
43
+
44
+ # Git 分支(可选,默认: main)
45
+ # GIT_BRANCH=main
46
+
47
+ # Git 仓库中的文件路径(可选,默认: accounts.json)
48
+ # GIT_FILE_PATH=accounts.json
49
+
50
+ # ============================================
51
+ # WARP / FlareSolverr 稳定代理配置(可选)
52
+ # ============================================
53
+
54
+ # docker-compose.warp.yml 暴露到宿主机的端口
55
+ # WARP_SOCKS_PORT=40000
56
+ # PRIVOXY_PORT=40080
57
+ # FLARESOLVERR_PORT=8191
58
+
59
+ # FlareSolverr 日志与时区
60
+ # FLARESOLVERR_LOG_LEVEL=info
61
+ # TZ=Asia/Shanghai
62
+
63
+ # Cloudflare WARP+ License Key(可选)
64
+ # WARP_LICENSE_KEY=
65
+
66
+ # 初始化 proxy_runtime 默认值
67
+ # CHATGPT2API_PROXY_RUNTIME_ENABLED=true
68
+ # CHATGPT2API_PROXY_RUNTIME_EGRESS_MODE=single_proxy
69
+ # CHATGPT2API_PROXY_RUNTIME_PROXY_URL=http://privoxy:8118
70
+ # CHATGPT2API_PROXY_RUNTIME_RESOURCE_PROXY_URL=
71
+ # CHATGPT2API_PROXY_RUNTIME_SKIP_SSL_VERIFY=false
72
+ # CHATGPT2API_PROXY_RUNTIME_RESET_STATUS_CODES=403
73
+
74
+ # Cloudflare clearance 刷新配置
75
+ # CHATGPT2API_PROXY_RUNTIME_CLEARANCE_ENABLED=true
76
+ # CHATGPT2API_PROXY_RUNTIME_CLEARANCE_MODE=flaresolverr
77
+ # CHATGPT2API_FLARESOLVERR_URL=http://flaresolverr:8191
78
+ # CHATGPT2API_PROXY_RUNTIME_CLEARANCE_TIMEOUT_SEC=60
79
+ # CHATGPT2API_PROXY_RUNTIME_CLEARANCE_REFRESH_INTERVAL=3600
80
+ # CHATGPT2API_PROXY_RUNTIME_WARM_UP_ON_START=false
81
+ # CHATGPT2API_PROXY_RUNTIME_BROWSER=chrome
82
+ # CHATGPT2API_PROXY_RUNTIME_USER_AGENT=
@@ -1 +1 @@
1
- 3.13
1
+ 3.13
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 kunkun
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kunkun
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.