@flun/html-template 5.0.0 → 5.0.1
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/f-CHANGELOG.md +9 -4
- package/package.json +1 -1
- package/templates/account/2fa.html +17 -23
package/f-CHANGELOG.md
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
# 变更日志
|
|
2
|
+
## [5.0.1] - 2026-06-10 11:59
|
|
3
|
+
### 紧急修复
|
|
4
|
+
- 修复二次验证报错问题:
|
|
5
|
+
- 修复前端脚本语法错误(可选链非法赋值、逗号表达式歧义)。
|
|
6
|
+
- 修正二次验证接口请求字段名(`input` → `token`),使其与后端匹配。
|
|
7
|
+
- 优化验证逻辑,使用统一输入框/按钮引用,避免重复代码。
|
|
8
|
+
- 二次验证(TOTP、备份码、WebAuthn)功能恢复正常。
|
|
9
|
+
> **请快更新和替换该文件(`2fa.html`)**
|
|
2
10
|
## [5.0.0] - 2026-06-10 10:05
|
|
3
11
|
### 重大更新
|
|
4
12
|
- 对templates/account目录中的页面结构进行了大面积优化,如果你是老用户请注意对比迁移!!!;
|
|
@@ -10,7 +18,4 @@
|
|
|
10
18
|
- customize\account.js文件为配合上述功能实现也做了相应适配;
|
|
11
19
|
## [4.4.3] - 2026-06-05 22:16
|
|
12
20
|
### 优化
|
|
13
|
-
- 模板中用户页面的移动端样式适配;
|
|
14
|
-
## [4.4.2] - 2026-05-31 15:08
|
|
15
|
-
### 修复
|
|
16
|
-
- 修复全局中间件中 `getCurrentUser(req)` 返回 `null` 时解构报错导致服务崩溃的问题,增加空值判断并自动清理无效登录态;
|
|
21
|
+
- 模板中用户页面的移动端样式适配;
|
package/package.json
CHANGED
|
@@ -141,10 +141,10 @@
|
|
|
141
141
|
<h2>双因素验证</h2>
|
|
142
142
|
|
|
143
143
|
<!-- TOTP 模式区域 -->
|
|
144
|
-
<div id="totpSection">
|
|
144
|
+
<div id="totpSection" hidden>
|
|
145
145
|
<p id="promptText">请输入身份验证器中的6位数字验证码</p>
|
|
146
146
|
<div class="form-group">
|
|
147
|
-
<input type="text" id="
|
|
147
|
+
<input type="text" id="totpInput" placeholder="6位验证码" maxlength="6" autofocus
|
|
148
148
|
autocomplete="one-time-code" class="totp-input">
|
|
149
149
|
</div>
|
|
150
150
|
<button type="button" class="btn" id="verifyTotpBtn">验证</button>
|
|
@@ -176,8 +176,8 @@
|
|
|
176
176
|
<script src="/static/topImg.js" defer></script>
|
|
177
177
|
<script src="/static/utils/browser.js"></script>
|
|
178
178
|
<script>
|
|
179
|
-
const [totpSection, webauthnSection, backupPanel, messageDiv,
|
|
180
|
-
retryHardwareBtn] = ['totpSection', 'webauthnSection', 'backupPanel', 'message', '
|
|
179
|
+
const [totpSection, webauthnSection, backupPanel, messageDiv, totpInput, verifyTotpBtn, backupCodeInput, submitBackupBtn,
|
|
180
|
+
retryHardwareBtn] = ['totpSection', 'webauthnSection', 'backupPanel', 'message', 'totpInput',
|
|
181
181
|
'verifyTotpBtn', 'backupCodeInput', 'submitBackupBtn', 'retryHardwareBtn']
|
|
182
182
|
.map(id => document.getElementById(id)),
|
|
183
183
|
urlParams = new URLSearchParams(window.location.search), method = urlParams.get('method'),
|
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
const setMessage = msg => messageDiv.textContent = msg, clearMessage = () => messageDiv.textContent = '',
|
|
188
188
|
// 禁用/启用表单控件
|
|
189
189
|
setFormDisabled = disabled => {
|
|
190
|
-
const inputs = [
|
|
190
|
+
const inputs = [totpInput, backupCodeInput, verifyTotpBtn, submitBackupBtn, retryHardwareBtn];
|
|
191
191
|
inputs.forEach(el => { if (el) el.disabled = disabled; });
|
|
192
192
|
},
|
|
193
193
|
// 显示备份码面板
|
|
@@ -197,25 +197,25 @@
|
|
|
197
197
|
backupCodeInput.value = '', backupCodeInput.focus();
|
|
198
198
|
},
|
|
199
199
|
// 统一处理验证响应(TOTP 和备份码共用)
|
|
200
|
-
handleVerifyResponse = async
|
|
201
|
-
if (!
|
|
200
|
+
handleVerifyResponse = async token => {
|
|
201
|
+
if (!token) {
|
|
202
202
|
setMessage('请输入验证码/备用码');
|
|
203
203
|
return false;
|
|
204
204
|
}
|
|
205
|
-
const
|
|
206
|
-
if (!
|
|
205
|
+
const totp = token.length === 6, isBackup = token.length === 10;
|
|
206
|
+
if (!totp && !isBackup) {
|
|
207
207
|
setMessage('验证码必须为6位数字,备用码为10位');
|
|
208
208
|
return false;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
// 临时禁用提交按钮防止重复请求
|
|
212
|
-
const activeBtn =
|
|
212
|
+
const [activeInput, activeBtn] = totp ? [totpInput, verifyTotpBtn] : [backupCodeInput, submitBackupBtn];
|
|
213
213
|
activeBtn.disabled = true, clearMessage();
|
|
214
214
|
|
|
215
215
|
try {
|
|
216
216
|
const response = await fetch('/api/verify-2fa', {
|
|
217
217
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
218
|
-
body: JSON.stringify({
|
|
218
|
+
body: JSON.stringify({ token })
|
|
219
219
|
}), data = await response.json();
|
|
220
220
|
|
|
221
221
|
if (response.ok) {
|
|
@@ -233,16 +233,10 @@
|
|
|
233
233
|
if (data.remainingAttempts !== undefined) {
|
|
234
234
|
const failuresSoFar = 9 - data.remainingAttempts;
|
|
235
235
|
setMessage(`${data.message} (已失败 ${failuresSoFar} 次)`);
|
|
236
|
-
if (failuresSoFar >= maxNum &&
|
|
236
|
+
if (failuresSoFar >= maxNum && totp) showBackupPanel(`连续失败 ${failuresSoFar} 次,请使用备用码登录`);
|
|
237
237
|
}
|
|
238
238
|
else setMessage(data.message);
|
|
239
|
-
|
|
240
|
-
// 清空输入框,准备下次尝试
|
|
241
|
-
tokenInput?.value = '', backupCodeInput?.value = '', activeBtn.disabled = false;
|
|
242
|
-
|
|
243
|
-
// 判断焦点归属
|
|
244
|
-
if (isBackup) backupCodeInput.focus();
|
|
245
|
-
else if (isToken) tokenInput.focus();
|
|
239
|
+
activeInput.value = '', activeInput.focus(), activeBtn.disabled = false;
|
|
246
240
|
return false;
|
|
247
241
|
} catch (err) {
|
|
248
242
|
setMessage(err.message), activeBtn.disabled = false;
|
|
@@ -250,7 +244,7 @@
|
|
|
250
244
|
}
|
|
251
245
|
},
|
|
252
246
|
// TOTP验证和备份码提交入口
|
|
253
|
-
performTotpVerification = () => handleVerifyResponse(
|
|
247
|
+
performTotpVerification = () => handleVerifyResponse(totpInput.value.trim()),
|
|
254
248
|
submitBackupCode = () => handleVerifyResponse(backupCodeInput.value.trim()),
|
|
255
249
|
// 硬件验证核心
|
|
256
250
|
performHardwareVerification = async () => {
|
|
@@ -305,7 +299,7 @@
|
|
|
305
299
|
backupPanel.hidden = true;
|
|
306
300
|
if (method === 'webauthn')
|
|
307
301
|
totpSection.hidden = true, webauthnSection.hidden = false, performHardwareVerification().catch(e => console.warn(e));
|
|
308
|
-
else totpSection.hidden = false, webauthnSection.hidden = true,
|
|
302
|
+
else totpSection.hidden = false, webauthnSection.hidden = true, totpInput.focus();
|
|
309
303
|
|
|
310
304
|
// 事件监听
|
|
311
305
|
submitBackupBtn.addEventListener('click', submitBackupCode);
|
|
@@ -313,10 +307,10 @@
|
|
|
313
307
|
backupCodeInput.addEventListener('keypress', e => {
|
|
314
308
|
if (e.key === 'Enter') submitBackupCode();
|
|
315
309
|
});
|
|
316
|
-
|
|
310
|
+
totpInput.addEventListener('keypress', e => {
|
|
317
311
|
if (e.key === 'Enter') performTotpVerification();
|
|
318
312
|
});
|
|
319
|
-
[backupCodeInput,
|
|
313
|
+
[backupCodeInput, totpInput].forEach(el => el.addEventListener('input', clearMessage));
|
|
320
314
|
};
|
|
321
315
|
|
|
322
316
|
initPage();
|