diff --git a/backend/database.js b/backend/database.js index 295ff7b..e275f97 100644 --- a/backend/database.js +++ b/backend/database.js @@ -510,17 +510,24 @@ const SettingsDB = { // 邮箱验证管理 const VerificationDB = { - setVerification(userId, token, expiresAt) { + setVerification(userId, token, expiresAtMs) { db.prepare(` UPDATE users SET verification_token = ?, verification_expires_at = ?, is_verified = 0, updated_at = CURRENT_TIMESTAMP WHERE id = ? - `).run(token, expiresAt, userId); + `).run(token, expiresAtMs, userId); }, consumeVerificationToken(token) { const row = db.prepare(` SELECT * FROM users - WHERE verification_token = ? AND verification_expires_at > CURRENT_TIMESTAMP AND is_verified = 0 + WHERE verification_token = ? + AND ( + verification_expires_at IS NULL + OR verification_expires_at = '' + OR verification_expires_at > strftime('%s','now')*1000 -- 数值时间戳(ms) + OR verification_expires_at > CURRENT_TIMESTAMP -- 兼容旧的字符串时间 + ) + AND is_verified = 0 `).get(token); if (!row) return null; @@ -535,16 +542,19 @@ const VerificationDB = { // 密码重置 Token 管理 const PasswordResetTokenDB = { - create(userId, token, expiresAt) { + create(userId, token, expiresAtMs) { db.prepare(` INSERT INTO password_reset_tokens (user_id, token, expires_at, used) VALUES (?, ?, ?, 0) - `).run(userId, token, expiresAt); + `).run(userId, token, expiresAtMs); }, use(token) { const row = db.prepare(` SELECT * FROM password_reset_tokens - WHERE token = ? AND used = 0 AND expires_at > CURRENT_TIMESTAMP + WHERE token = ? AND used = 0 AND ( + expires_at > strftime('%s','now')*1000 -- 数值时间戳 + OR expires_at > CURRENT_TIMESTAMP -- 兼容旧的字符串时间 + ) `).get(token); if (!row) return null; db.prepare(`UPDATE password_reset_tokens SET used = 1 WHERE id = ?`).run(row.id); diff --git a/backend/server.js b/backend/server.js index 7f6b98d..673d673 100644 --- a/backend/server.js +++ b/backend/server.js @@ -815,7 +815,7 @@ app.post('/api/register', } const verifyToken = generateRandomToken(24); - const expiresAt = new Date(Date.now() + 30 * 60 * 1000).toISOString(); // 30分钟 + const expiresAtMs = Date.now() + 30 * 60 * 1000; // 30分钟 // 创建用户(不需要FTP配置),标记未验证 const userId = UserDB.create({ @@ -824,7 +824,7 @@ app.post('/api/register', password, is_verified: 0, verification_token: verifyToken, - verification_expires_at: expiresAt + verification_expires_at: expiresAtMs }); const verifyLink = `${getProtocol(req)}://${req.get('host')}/?verifyToken=${verifyToken}`; @@ -891,8 +891,8 @@ app.post('/api/resend-verification', [ } const verifyToken = generateRandomToken(24); - const expiresAt = new Date(Date.now() + 30 * 60 * 1000).toISOString(); - VerificationDB.setVerification(user.id, verifyToken, expiresAt); + const expiresAtMs = Date.now() + 30 * 60 * 1000; + VerificationDB.setVerification(user.id, verifyToken, expiresAtMs); const verifyLink = `${getProtocol(req)}://${req.get('host')}/?verifyToken=${verifyToken}`; await sendMail( @@ -956,8 +956,8 @@ app.post('/api/password/forgot', [ } const token = generateRandomToken(24); - const expiresAt = new Date(Date.now() + 30 * 60 * 1000).toISOString(); - PasswordResetTokenDB.create(user.id, token, expiresAt); + const expiresAtMs = Date.now() + 30 * 60 * 1000; + PasswordResetTokenDB.create(user.id, token, expiresAtMs); const resetLink = `${getProtocol(req)}://${req.get('host')}/?resetToken=${token}`; await sendMail( diff --git a/frontend/app.html b/frontend/app.html index 7ab7d77..ea534cd 100644 --- a/frontend/app.html +++ b/frontend/app.html @@ -663,6 +663,7 @@
{{ errorMessage }}
{{ successMessage }}
+
{{ verifyMessage }}
diff --git a/frontend/app.js b/frontend/app.js index 2b21b7a..a45f316 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -158,6 +158,7 @@ createApp({ // 提示信息 errorMessage: '', successMessage: '', + verifyMessage: '', // 存储相关 storageType: 'sftp', // 当前使用的存储类型 @@ -453,7 +454,8 @@ handleDragLeave(e) { try { const response = await axios.get(`${this.apiBase}/api/verify-email`, { params: { token } }); if (response.data.success) { - this.showToast('success', '成功', '邮箱验证成功,请登录'); + this.verifyMessage = '邮箱验证成功,请登录'; + this.isLogin = true; // 清理URL const url = new URL(window.location.href); url.searchParams.delete('verifyToken'); @@ -461,7 +463,7 @@ handleDragLeave(e) { } } catch (error) { console.error('邮箱验证失败:', error); - this.showToast('error', '错误', error.response?.data?.message || '验证失败'); + this.verifyMessage = error.response?.data?.message || '验证失败'; } }, @@ -1675,7 +1677,8 @@ handleDragLeave(e) { try { const response = await axios.post(`${this.apiBase}/api/password/reset`, this.resetPasswordForm); if (response.data.success) { - this.showToast('success', '成功', '密码已重置,请登录'); + this.verifyMessage = '密码已重置,请登录'; + this.isLogin = true; this.showResetPasswordModal = false; this.resetPasswordForm = { token: '', new_password: '' }; // 清理URL中的token