🐛 修复邮箱验证和密码重置的时间戳问题
后端修复:
- 修改时间戳存储格式:从 ISO 字符串改为数值时间戳(毫秒)
- 优化时间戳比较逻辑:兼容新旧格式的时间戳
- 修复 VerificationDB.consumeVerificationToken() 的时间比较
- 修复 PasswordResetTokenDB.use() 的时间比较
- 统一使用 Date.now() 生成时间戳
前端改进:
- 新增 verifyMessage 独立显示验证相关提示
- 优化邮箱验证成功后的用户体验(自动切换到登录表单)
- 优化密码重置成功后的用户体验(自动切换到登录表单)
- 改进提示信息显示方式
技术细节:
- SQLite 时间比较:strftime('%s','now')*1000 获取当前毫秒时间戳
- 兼容旧数据:同时支持字符串和数值时间戳比较
这个修复解决了邮箱验证令牌一直提示过期的问题。
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user