llmapi-v2 2.1.0
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.
- package/.env.example +40 -0
- package/Dockerfile +17 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.js +98 -0
- package/dist/config.js.map +1 -0
- package/dist/converter/request.d.ts +6 -0
- package/dist/converter/request.js +184 -0
- package/dist/converter/request.js.map +1 -0
- package/dist/converter/response.d.ts +6 -0
- package/dist/converter/response.js +76 -0
- package/dist/converter/response.js.map +1 -0
- package/dist/converter/stream.d.ts +54 -0
- package/dist/converter/stream.js +318 -0
- package/dist/converter/stream.js.map +1 -0
- package/dist/converter/types.d.ts +239 -0
- package/dist/converter/types.js +6 -0
- package/dist/converter/types.js.map +1 -0
- package/dist/data/posts.d.ts +19 -0
- package/dist/data/posts.js +462 -0
- package/dist/data/posts.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +233 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/api-key-auth.d.ts +6 -0
- package/dist/middleware/api-key-auth.js +76 -0
- package/dist/middleware/api-key-auth.js.map +1 -0
- package/dist/middleware/quota-guard.d.ts +10 -0
- package/dist/middleware/quota-guard.js +27 -0
- package/dist/middleware/quota-guard.js.map +1 -0
- package/dist/middleware/rate-limiter.d.ts +5 -0
- package/dist/middleware/rate-limiter.js +50 -0
- package/dist/middleware/rate-limiter.js.map +1 -0
- package/dist/middleware/request-logger.d.ts +6 -0
- package/dist/middleware/request-logger.js +37 -0
- package/dist/middleware/request-logger.js.map +1 -0
- package/dist/middleware/session-auth.d.ts +19 -0
- package/dist/middleware/session-auth.js +99 -0
- package/dist/middleware/session-auth.js.map +1 -0
- package/dist/providers/aliyun.d.ts +13 -0
- package/dist/providers/aliyun.js +20 -0
- package/dist/providers/aliyun.js.map +1 -0
- package/dist/providers/base-provider.d.ts +36 -0
- package/dist/providers/base-provider.js +133 -0
- package/dist/providers/base-provider.js.map +1 -0
- package/dist/providers/deepseek.d.ts +11 -0
- package/dist/providers/deepseek.js +18 -0
- package/dist/providers/deepseek.js.map +1 -0
- package/dist/providers/registry.d.ts +18 -0
- package/dist/providers/registry.js +98 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/types.d.ts +17 -0
- package/dist/providers/types.js +3 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/routes/admin.d.ts +1 -0
- package/dist/routes/admin.js +153 -0
- package/dist/routes/admin.js.map +1 -0
- package/dist/routes/auth.d.ts +2 -0
- package/dist/routes/auth.js +318 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/blog.d.ts +1 -0
- package/dist/routes/blog.js +29 -0
- package/dist/routes/blog.js.map +1 -0
- package/dist/routes/dashboard.d.ts +1 -0
- package/dist/routes/dashboard.js +184 -0
- package/dist/routes/dashboard.js.map +1 -0
- package/dist/routes/messages.d.ts +1 -0
- package/dist/routes/messages.js +309 -0
- package/dist/routes/messages.js.map +1 -0
- package/dist/routes/models.d.ts +1 -0
- package/dist/routes/models.js +39 -0
- package/dist/routes/models.js.map +1 -0
- package/dist/routes/payment.d.ts +1 -0
- package/dist/routes/payment.js +150 -0
- package/dist/routes/payment.js.map +1 -0
- package/dist/routes/sitemap.d.ts +1 -0
- package/dist/routes/sitemap.js +38 -0
- package/dist/routes/sitemap.js.map +1 -0
- package/dist/services/alipay.d.ts +27 -0
- package/dist/services/alipay.js +106 -0
- package/dist/services/alipay.js.map +1 -0
- package/dist/services/database.d.ts +4 -0
- package/dist/services/database.js +170 -0
- package/dist/services/database.js.map +1 -0
- package/dist/services/health-checker.d.ts +13 -0
- package/dist/services/health-checker.js +95 -0
- package/dist/services/health-checker.js.map +1 -0
- package/dist/services/mailer.d.ts +3 -0
- package/dist/services/mailer.js +91 -0
- package/dist/services/mailer.js.map +1 -0
- package/dist/services/metrics.d.ts +56 -0
- package/dist/services/metrics.js +94 -0
- package/dist/services/metrics.js.map +1 -0
- package/dist/services/remote-control.d.ts +20 -0
- package/dist/services/remote-control.js +209 -0
- package/dist/services/remote-control.js.map +1 -0
- package/dist/services/remote-ws.d.ts +5 -0
- package/dist/services/remote-ws.js +143 -0
- package/dist/services/remote-ws.js.map +1 -0
- package/dist/services/usage.d.ts +13 -0
- package/dist/services/usage.js +39 -0
- package/dist/services/usage.js.map +1 -0
- package/dist/utils/errors.d.ts +27 -0
- package/dist/utils/errors.js +48 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +14 -0
- package/dist/utils/logger.js.map +1 -0
- package/docker-compose.yml +19 -0
- package/package.json +39 -0
- package/public/robots.txt +8 -0
- package/src/config.ts +140 -0
- package/src/converter/request.ts +207 -0
- package/src/converter/response.ts +85 -0
- package/src/converter/stream.ts +373 -0
- package/src/converter/types.ts +257 -0
- package/src/data/posts.ts +474 -0
- package/src/index.ts +219 -0
- package/src/middleware/api-key-auth.ts +82 -0
- package/src/middleware/quota-guard.ts +28 -0
- package/src/middleware/rate-limiter.ts +61 -0
- package/src/middleware/request-logger.ts +36 -0
- package/src/middleware/session-auth.ts +91 -0
- package/src/providers/aliyun.ts +16 -0
- package/src/providers/base-provider.ts +148 -0
- package/src/providers/deepseek.ts +14 -0
- package/src/providers/registry.ts +111 -0
- package/src/providers/types.ts +26 -0
- package/src/routes/admin.ts +169 -0
- package/src/routes/auth.ts +369 -0
- package/src/routes/blog.ts +28 -0
- package/src/routes/dashboard.ts +208 -0
- package/src/routes/messages.ts +346 -0
- package/src/routes/models.ts +37 -0
- package/src/routes/payment.ts +189 -0
- package/src/routes/sitemap.ts +40 -0
- package/src/services/alipay.ts +116 -0
- package/src/services/database.ts +187 -0
- package/src/services/health-checker.ts +115 -0
- package/src/services/mailer.ts +90 -0
- package/src/services/metrics.ts +104 -0
- package/src/services/remote-control.ts +226 -0
- package/src/services/remote-ws.ts +145 -0
- package/src/services/usage.ts +57 -0
- package/src/types/express.d.ts +46 -0
- package/src/utils/errors.ts +44 -0
- package/src/utils/logger.ts +8 -0
- package/tsconfig.json +17 -0
- package/views/pages/404.ejs +14 -0
- package/views/pages/admin.ejs +307 -0
- package/views/pages/blog-post.ejs +378 -0
- package/views/pages/blog.ejs +148 -0
- package/views/pages/dashboard.ejs +441 -0
- package/views/pages/docs.ejs +807 -0
- package/views/pages/index.ejs +416 -0
- package/views/pages/login.ejs +170 -0
- package/views/pages/orders.ejs +111 -0
- package/views/pages/pricing.ejs +379 -0
- package/views/pages/register.ejs +397 -0
- package/views/pages/remote.ejs +334 -0
- package/views/pages/settings.ejs +373 -0
- package/views/partials/header.ejs +70 -0
- package/views/partials/nav.ejs +140 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
<%- include('../partials/header') %>
|
|
2
|
+
<%- include('../partials/nav') %>
|
|
3
|
+
|
|
4
|
+
<main class="min-h-[calc(100vh-4rem)] flex items-center justify-center px-4 py-12">
|
|
5
|
+
<div class="w-full max-w-md">
|
|
6
|
+
<div class="bg-white rounded-2xl shadow-sm border border-claude-dark/5 p-8">
|
|
7
|
+
<!-- Header -->
|
|
8
|
+
<div class="text-center mb-8">
|
|
9
|
+
<h1 class="text-2xl font-bold text-claude-dark">创建账户</h1>
|
|
10
|
+
<p class="mt-2 text-sm text-claude-dark/60">注册 LLM API,开始使用 Claude Code</p>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<!-- Google Sign-In -->
|
|
14
|
+
<script src="https://accounts.google.com/gsi/client" async></script>
|
|
15
|
+
<div id="g_id_onload"
|
|
16
|
+
data-client_id="128504392054-4rfrtk05umm9fvfpd83l9qn9r4qnaa98.apps.googleusercontent.com"
|
|
17
|
+
data-callback="handleGoogleCallback"
|
|
18
|
+
data-auto_prompt="false">
|
|
19
|
+
</div>
|
|
20
|
+
<div class="flex justify-center mb-6">
|
|
21
|
+
<div class="g_id_signin"
|
|
22
|
+
data-type="standard"
|
|
23
|
+
data-shape="rectangular"
|
|
24
|
+
data-theme="outline"
|
|
25
|
+
data-text="signup_with"
|
|
26
|
+
data-size="large"
|
|
27
|
+
data-width="400">
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div class="relative mb-6">
|
|
32
|
+
<div class="absolute inset-0 flex items-center"><div class="w-full border-t border-claude-dark/10"></div></div>
|
|
33
|
+
<div class="relative flex justify-center text-xs"><span class="bg-white px-3 text-claude-dark/40">or register with email</span></div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<!-- Step Indicator -->
|
|
37
|
+
<div class="flex items-center justify-center mb-8">
|
|
38
|
+
<div class="flex items-center space-x-3">
|
|
39
|
+
<div id="step1Indicator" class="flex items-center justify-center w-8 h-8 rounded-full bg-claude-orange text-white text-sm font-semibold">1</div>
|
|
40
|
+
<div id="stepLine" class="w-12 h-0.5 bg-claude-dark/10"></div>
|
|
41
|
+
<div id="step2Indicator" class="flex items-center justify-center w-8 h-8 rounded-full bg-claude-dark/10 text-claude-dark/40 text-sm font-semibold">2</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<!-- Error / Success Display -->
|
|
46
|
+
<div id="regError" class="hidden mb-4 p-3 rounded-lg bg-red-50 border border-red-200 text-sm text-red-700"></div>
|
|
47
|
+
<div id="regSuccess" class="hidden mb-4 p-3 rounded-lg bg-green-50 border border-green-200 text-sm text-green-700"></div>
|
|
48
|
+
|
|
49
|
+
<!-- Step 1: Registration Form -->
|
|
50
|
+
<form id="step1Form" class="space-y-4" onsubmit="handleStep1(event)">
|
|
51
|
+
<div>
|
|
52
|
+
<label for="name" class="block text-sm font-medium text-claude-dark/80 mb-1.5">用户名</label>
|
|
53
|
+
<input
|
|
54
|
+
type="text"
|
|
55
|
+
id="name"
|
|
56
|
+
name="name"
|
|
57
|
+
required
|
|
58
|
+
minlength="2"
|
|
59
|
+
placeholder="您的名字"
|
|
60
|
+
class="w-full px-4 py-2.5 rounded-lg border border-claude-dark/15 bg-claude-cream/30 text-claude-dark placeholder-claude-dark/30 focus:outline-none focus:ring-2 focus:ring-claude-orange/30 focus:border-claude-orange transition-colors"
|
|
61
|
+
>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<div>
|
|
65
|
+
<label for="email" class="block text-sm font-medium text-claude-dark/80 mb-1.5">邮箱地址</label>
|
|
66
|
+
<input
|
|
67
|
+
type="email"
|
|
68
|
+
id="email"
|
|
69
|
+
name="email"
|
|
70
|
+
required
|
|
71
|
+
autocomplete="email"
|
|
72
|
+
placeholder="you@example.com"
|
|
73
|
+
class="w-full px-4 py-2.5 rounded-lg border border-claude-dark/15 bg-claude-cream/30 text-claude-dark placeholder-claude-dark/30 focus:outline-none focus:ring-2 focus:ring-claude-orange/30 focus:border-claude-orange transition-colors"
|
|
74
|
+
>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<div>
|
|
78
|
+
<label for="password" class="block text-sm font-medium text-claude-dark/80 mb-1.5">密码</label>
|
|
79
|
+
<div class="relative">
|
|
80
|
+
<input
|
|
81
|
+
type="password"
|
|
82
|
+
id="password"
|
|
83
|
+
name="password"
|
|
84
|
+
required
|
|
85
|
+
minlength="8"
|
|
86
|
+
placeholder="至少 8 个字符"
|
|
87
|
+
class="w-full px-4 py-2.5 rounded-lg border border-claude-dark/15 bg-claude-cream/30 text-claude-dark placeholder-claude-dark/30 focus:outline-none focus:ring-2 focus:ring-claude-orange/30 focus:border-claude-orange transition-colors"
|
|
88
|
+
>
|
|
89
|
+
<button type="button" onclick="togglePassword('password')" class="absolute right-3 top-1/2 -translate-y-1/2 text-claude-dark/40 hover:text-claude-dark/70">
|
|
90
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
|
|
91
|
+
</button>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div>
|
|
96
|
+
<label for="confirmPassword" class="block text-sm font-medium text-claude-dark/80 mb-1.5">确认密码</label>
|
|
97
|
+
<input
|
|
98
|
+
type="password"
|
|
99
|
+
id="confirmPassword"
|
|
100
|
+
name="confirmPassword"
|
|
101
|
+
required
|
|
102
|
+
minlength="8"
|
|
103
|
+
placeholder="再次输入密码"
|
|
104
|
+
class="w-full px-4 py-2.5 rounded-lg border border-claude-dark/15 bg-claude-cream/30 text-claude-dark placeholder-claude-dark/30 focus:outline-none focus:ring-2 focus:ring-claude-orange/30 focus:border-claude-orange transition-colors"
|
|
105
|
+
>
|
|
106
|
+
</div>
|
|
107
|
+
|
|
108
|
+
<button
|
|
109
|
+
type="submit"
|
|
110
|
+
id="step1Btn"
|
|
111
|
+
class="w-full py-2.5 px-4 bg-claude-orange text-white font-medium rounded-lg hover:bg-claude-orange/90 transition-colors focus:outline-none focus:ring-2 focus:ring-claude-orange/30 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
112
|
+
>
|
|
113
|
+
发送验证码
|
|
114
|
+
</button>
|
|
115
|
+
</form>
|
|
116
|
+
|
|
117
|
+
<!-- Step 2: Verification Code -->
|
|
118
|
+
<form id="step2Form" class="hidden space-y-5" onsubmit="handleStep2(event)">
|
|
119
|
+
<div class="text-center mb-2">
|
|
120
|
+
<p class="text-sm text-claude-dark/60">验证码已发送至 <span id="sentEmail" class="font-medium text-claude-dark"></span></p>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div>
|
|
124
|
+
<label for="code" class="block text-sm font-medium text-claude-dark/80 mb-1.5 text-center">输入 6 位验证码</label>
|
|
125
|
+
<input
|
|
126
|
+
type="text"
|
|
127
|
+
id="code"
|
|
128
|
+
name="code"
|
|
129
|
+
required
|
|
130
|
+
maxlength="6"
|
|
131
|
+
pattern="[0-9]{6}"
|
|
132
|
+
inputmode="numeric"
|
|
133
|
+
autocomplete="one-time-code"
|
|
134
|
+
placeholder="000000"
|
|
135
|
+
class="w-full px-4 py-4 rounded-lg border border-claude-dark/15 bg-claude-cream/30 text-claude-dark text-center text-2xl font-mono tracking-[0.5em] placeholder-claude-dark/20 focus:outline-none focus:ring-2 focus:ring-claude-orange/30 focus:border-claude-orange transition-colors"
|
|
136
|
+
>
|
|
137
|
+
</div>
|
|
138
|
+
|
|
139
|
+
<button
|
|
140
|
+
type="submit"
|
|
141
|
+
id="step2Btn"
|
|
142
|
+
class="w-full py-2.5 px-4 bg-claude-orange text-white font-medium rounded-lg hover:bg-claude-orange/90 transition-colors focus:outline-none focus:ring-2 focus:ring-claude-orange/30 disabled:opacity-50 disabled:cursor-not-allowed"
|
|
143
|
+
>
|
|
144
|
+
验证并注册
|
|
145
|
+
</button>
|
|
146
|
+
|
|
147
|
+
<div class="text-center">
|
|
148
|
+
<button
|
|
149
|
+
type="button"
|
|
150
|
+
id="resendBtn"
|
|
151
|
+
onclick="handleResend()"
|
|
152
|
+
disabled
|
|
153
|
+
class="text-sm text-claude-orange hover:text-claude-orange/80 font-medium transition-colors disabled:text-claude-dark/30 disabled:cursor-not-allowed"
|
|
154
|
+
>
|
|
155
|
+
重新发送验证码 (<span id="countdown">60</span>s)
|
|
156
|
+
</button>
|
|
157
|
+
</div>
|
|
158
|
+
|
|
159
|
+
<button
|
|
160
|
+
type="button"
|
|
161
|
+
onclick="backToStep1()"
|
|
162
|
+
class="w-full py-2 text-sm text-claude-dark/50 hover:text-claude-dark/70 transition-colors"
|
|
163
|
+
>
|
|
164
|
+
返回上一步
|
|
165
|
+
</button>
|
|
166
|
+
</form>
|
|
167
|
+
|
|
168
|
+
<!-- Footer Links -->
|
|
169
|
+
<div class="mt-6 text-center">
|
|
170
|
+
<p class="text-sm text-claude-dark/60">
|
|
171
|
+
已有账户?
|
|
172
|
+
<a href="/login" class="text-claude-orange hover:text-claude-orange/80 font-medium transition-colors">立即登录</a>
|
|
173
|
+
</p>
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
</main>
|
|
178
|
+
|
|
179
|
+
<script>
|
|
180
|
+
function handleGoogleCallback(response) {
|
|
181
|
+
fetch('/api/auth/google/token', {
|
|
182
|
+
method: 'POST',
|
|
183
|
+
headers: { 'Content-Type': 'application/json' },
|
|
184
|
+
body: JSON.stringify({ idToken: response.credential }),
|
|
185
|
+
})
|
|
186
|
+
.then(function(r) { return r.json(); })
|
|
187
|
+
.then(function(data) {
|
|
188
|
+
if (data.success) window.location.href = '/dashboard';
|
|
189
|
+
else showError(data.error || 'Google sign-up failed');
|
|
190
|
+
})
|
|
191
|
+
.catch(function() { showError('Google sign-up failed'); });
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let regEmail = '';
|
|
195
|
+
let regPassword = '';
|
|
196
|
+
let regName = '';
|
|
197
|
+
let countdownTimer = null;
|
|
198
|
+
|
|
199
|
+
function togglePassword(fieldId) {
|
|
200
|
+
const input = document.getElementById(fieldId);
|
|
201
|
+
input.type = input.type === 'password' ? 'text' : 'password';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function showError(msg) {
|
|
205
|
+
const el = document.getElementById('regError');
|
|
206
|
+
el.textContent = msg;
|
|
207
|
+
el.classList.remove('hidden');
|
|
208
|
+
document.getElementById('regSuccess').classList.add('hidden');
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function showSuccess(msg) {
|
|
212
|
+
const el = document.getElementById('regSuccess');
|
|
213
|
+
el.textContent = msg;
|
|
214
|
+
el.classList.remove('hidden');
|
|
215
|
+
document.getElementById('regError').classList.add('hidden');
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function hideMessages() {
|
|
219
|
+
document.getElementById('regError').classList.add('hidden');
|
|
220
|
+
document.getElementById('regSuccess').classList.add('hidden');
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function goToStep2() {
|
|
224
|
+
document.getElementById('step1Form').classList.add('hidden');
|
|
225
|
+
document.getElementById('step2Form').classList.remove('hidden');
|
|
226
|
+
document.getElementById('step2Indicator').classList.remove('bg-claude-dark/10', 'text-claude-dark/40');
|
|
227
|
+
document.getElementById('step2Indicator').classList.add('bg-claude-orange', 'text-white');
|
|
228
|
+
document.getElementById('stepLine').classList.remove('bg-claude-dark/10');
|
|
229
|
+
document.getElementById('stepLine').classList.add('bg-claude-orange');
|
|
230
|
+
document.getElementById('sentEmail').textContent = regEmail;
|
|
231
|
+
startCountdown();
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function backToStep1() {
|
|
235
|
+
document.getElementById('step2Form').classList.add('hidden');
|
|
236
|
+
document.getElementById('step1Form').classList.remove('hidden');
|
|
237
|
+
document.getElementById('step2Indicator').classList.add('bg-claude-dark/10', 'text-claude-dark/40');
|
|
238
|
+
document.getElementById('step2Indicator').classList.remove('bg-claude-orange', 'text-white');
|
|
239
|
+
document.getElementById('stepLine').classList.add('bg-claude-dark/10');
|
|
240
|
+
document.getElementById('stepLine').classList.remove('bg-claude-orange');
|
|
241
|
+
hideMessages();
|
|
242
|
+
if (countdownTimer) clearInterval(countdownTimer);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function startCountdown() {
|
|
246
|
+
let seconds = 60;
|
|
247
|
+
const btn = document.getElementById('resendBtn');
|
|
248
|
+
const countdownEl = document.getElementById('countdown');
|
|
249
|
+
btn.disabled = true;
|
|
250
|
+
btn.innerHTML = '重新发送验证码 (<span id="countdown">60</span>s)';
|
|
251
|
+
|
|
252
|
+
if (countdownTimer) clearInterval(countdownTimer);
|
|
253
|
+
|
|
254
|
+
countdownTimer = setInterval(function() {
|
|
255
|
+
seconds--;
|
|
256
|
+
document.getElementById('countdown').textContent = seconds;
|
|
257
|
+
if (seconds <= 0) {
|
|
258
|
+
clearInterval(countdownTimer);
|
|
259
|
+
btn.disabled = false;
|
|
260
|
+
btn.textContent = '重新发送验证码';
|
|
261
|
+
}
|
|
262
|
+
}, 1000);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async function handleStep1(e) {
|
|
266
|
+
e.preventDefault();
|
|
267
|
+
hideMessages();
|
|
268
|
+
|
|
269
|
+
var name = document.getElementById('name').value.trim();
|
|
270
|
+
var email = document.getElementById('email').value.trim();
|
|
271
|
+
var password = document.getElementById('password').value;
|
|
272
|
+
var confirmPassword = document.getElementById('confirmPassword').value;
|
|
273
|
+
|
|
274
|
+
if (!name || name.length < 2) {
|
|
275
|
+
showError('用户名至少需要 2 个字符');
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
if (!email) {
|
|
279
|
+
showError('请输入邮箱地址');
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (password.length < 8) {
|
|
283
|
+
showError('密码至少需要 8 个字符');
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (password !== confirmPassword) {
|
|
287
|
+
showError('两次输入的密码不一致');
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
var btn = document.getElementById('step1Btn');
|
|
292
|
+
btn.disabled = true;
|
|
293
|
+
btn.textContent = '发送中...';
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
var res = await fetch('/api/auth/send-code', {
|
|
297
|
+
method: 'POST',
|
|
298
|
+
headers: { 'Content-Type': 'application/json' },
|
|
299
|
+
credentials: 'same-origin',
|
|
300
|
+
body: JSON.stringify({ name: name, email: email, password: password }),
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
var data = await res.json();
|
|
304
|
+
|
|
305
|
+
if (data.success) {
|
|
306
|
+
regEmail = email;
|
|
307
|
+
regPassword = password;
|
|
308
|
+
regName = name;
|
|
309
|
+
showSuccess('验证码已发送至您的邮箱');
|
|
310
|
+
setTimeout(function() {
|
|
311
|
+
hideMessages();
|
|
312
|
+
goToStep2();
|
|
313
|
+
}, 800);
|
|
314
|
+
} else {
|
|
315
|
+
showError(data.error || '发送验证码失败,请重试');
|
|
316
|
+
}
|
|
317
|
+
} catch (err) {
|
|
318
|
+
showError('网络错误,请检查连接后重试');
|
|
319
|
+
} finally {
|
|
320
|
+
btn.disabled = false;
|
|
321
|
+
btn.textContent = '发送验证码';
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
async function handleStep2(e) {
|
|
326
|
+
e.preventDefault();
|
|
327
|
+
hideMessages();
|
|
328
|
+
|
|
329
|
+
var code = document.getElementById('code').value.trim();
|
|
330
|
+
|
|
331
|
+
if (!code || code.length !== 6 || !/^\d{6}$/.test(code)) {
|
|
332
|
+
showError('请输入 6 位数字验证码');
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
var btn = document.getElementById('step2Btn');
|
|
337
|
+
btn.disabled = true;
|
|
338
|
+
btn.textContent = '注册中...';
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
var res = await fetch('/api/auth/register', {
|
|
342
|
+
method: 'POST',
|
|
343
|
+
headers: { 'Content-Type': 'application/json' },
|
|
344
|
+
credentials: 'same-origin',
|
|
345
|
+
body: JSON.stringify({ email: regEmail, code: code }),
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
var data = await res.json();
|
|
349
|
+
|
|
350
|
+
if (data.success) {
|
|
351
|
+
showSuccess('注册成功!正在跳转...');
|
|
352
|
+
setTimeout(function() {
|
|
353
|
+
window.location.href = '/dashboard';
|
|
354
|
+
}, 1000);
|
|
355
|
+
} else {
|
|
356
|
+
showError(data.error || '验证失败,请重试');
|
|
357
|
+
}
|
|
358
|
+
} catch (err) {
|
|
359
|
+
showError('网络错误,请检查连接后重试');
|
|
360
|
+
} finally {
|
|
361
|
+
btn.disabled = false;
|
|
362
|
+
btn.textContent = '验证并注册';
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
async function handleResend() {
|
|
367
|
+
hideMessages();
|
|
368
|
+
|
|
369
|
+
var btn = document.getElementById('resendBtn');
|
|
370
|
+
btn.disabled = true;
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
var res = await fetch('/api/auth/send-code', {
|
|
374
|
+
method: 'POST',
|
|
375
|
+
headers: { 'Content-Type': 'application/json' },
|
|
376
|
+
credentials: 'same-origin',
|
|
377
|
+
body: JSON.stringify({ name: regName, email: regEmail, password: regPassword }),
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
var data = await res.json();
|
|
381
|
+
|
|
382
|
+
if (data.success) {
|
|
383
|
+
showSuccess('验证码已重新发送');
|
|
384
|
+
startCountdown();
|
|
385
|
+
} else {
|
|
386
|
+
showError(data.error || '发送失败,请重试');
|
|
387
|
+
btn.disabled = false;
|
|
388
|
+
}
|
|
389
|
+
} catch (err) {
|
|
390
|
+
showError('网络错误,请重试');
|
|
391
|
+
btn.disabled = false;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
</script>
|
|
395
|
+
|
|
396
|
+
</body>
|
|
397
|
+
</html>
|