fix: 邮件懒加载、cloudEnabled 刷新、邮箱写入时机、验证码重发冷却
- server/lib/email.ts: transporter 改为懒加载,避免 ESM import 提升导致 EMAIL_PASS 未加载 - server/routes/auth.ts: send-verify 改为写 pendingEmail,验证成功后才写 email;新增 60s 冷却;catch 块打印错误 - server/db.ts: 新增 pendingEmail 字段及迁移 - src/stores/appStore.ts: 新增 refreshCurrentUser,启动时调用 apiGetMe 拉取真实 cloudEnabled - src/App.tsx: 启动时调用 refreshCurrentUser - ProfileModal/AccountModal: 加入 60s 倒计时和重新发送按钮 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
b864b2903a
commit
689c8b2d86
@@ -22,6 +22,7 @@ export const users = sqliteTable('users', {
|
||||
avatar: text('avatar'),
|
||||
email: text('email'),
|
||||
emailVerified: integer('email_verified', { mode: 'boolean' }).notNull().default(false),
|
||||
pendingEmail: text('pending_email'),
|
||||
emailVerifyCode: text('email_verify_code'),
|
||||
emailVerifyExpiry: integer('email_verify_expiry'),
|
||||
resetCode: text('reset_code'),
|
||||
@@ -134,4 +135,5 @@ export function initDb() {
|
||||
if (!colNames.includes('reset_code_expiry')) sqlite.exec(`ALTER TABLE users ADD COLUMN reset_code_expiry INTEGER`)
|
||||
if (!colNames.includes('nickname')) sqlite.exec(`ALTER TABLE users ADD COLUMN nickname TEXT`)
|
||||
if (!colNames.includes('avatar')) sqlite.exec(`ALTER TABLE users ADD COLUMN avatar TEXT`)
|
||||
if (!colNames.includes('pending_email')) sqlite.exec(`ALTER TABLE users ADD COLUMN pending_email TEXT`)
|
||||
}
|
||||
|
||||
+13
-11
@@ -1,17 +1,19 @@
|
||||
import nodemailer from 'nodemailer'
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: 'smtp.126.com',
|
||||
port: 465,
|
||||
secure: true,
|
||||
auth: {
|
||||
user: 'mikivl@126.com',
|
||||
pass: process.env.EMAIL_PASS!,
|
||||
},
|
||||
})
|
||||
function createTransporter() {
|
||||
return nodemailer.createTransport({
|
||||
host: 'smtp.126.com',
|
||||
port: 465,
|
||||
secure: true,
|
||||
auth: {
|
||||
user: 'mikivl@126.com',
|
||||
pass: process.env.EMAIL_PASS!,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export async function sendVerifyCode(to: string, code: string) {
|
||||
await transporter.sendMail({
|
||||
await createTransporter().sendMail({
|
||||
from: '"MikiVL 笔记" <mikivl@126.com>',
|
||||
to,
|
||||
subject: '邮箱验证码',
|
||||
@@ -21,7 +23,7 @@ export async function sendVerifyCode(to: string, code: string) {
|
||||
}
|
||||
|
||||
export async function sendResetCode(to: string, code: string) {
|
||||
await transporter.sendMail({
|
||||
await createTransporter().sendMail({
|
||||
from: '"MikiVL 笔记" <mikivl@126.com>',
|
||||
to,
|
||||
subject: '重置密码验证码',
|
||||
|
||||
+13
-4
@@ -96,17 +96,25 @@ authRouter.post('/email/send-verify', requireAuth, async (c) => {
|
||||
const { email } = await c.req.json<{ email: string }>()
|
||||
if (!email || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) return c.json({ error: '邮箱格式不正确' }, 400)
|
||||
|
||||
// 检查邮箱是否已被其他用户绑定
|
||||
// 检查邮箱是否已被其他已验证用户绑定
|
||||
const existing = db.select().from(users).where(eq(users.email, email)).all()
|
||||
if (existing.some(u => u.id !== userId)) return c.json({ error: '该邮箱已被其他账号绑定' }, 409)
|
||||
|
||||
// 60 秒冷却:上次发送时间在 60 秒内则拒绝
|
||||
const [user] = db.select().from(users).where(eq(users.id, userId)).all()
|
||||
if (user?.emailVerifyExpiry && user.emailVerifyExpiry - Date.now() > 9 * 60 * 1000) {
|
||||
return c.json({ error: '请等待 60 秒后再重新发送' }, 429)
|
||||
}
|
||||
|
||||
const code = randomCode()
|
||||
const expiry = Date.now() + 10 * 60 * 1000
|
||||
db.update(users).set({ email, emailVerifyCode: code, emailVerifyExpiry: expiry, emailVerified: false }).where(eq(users.id, userId)).run()
|
||||
// 只写入 pendingEmail,不修改 email 字段
|
||||
db.update(users).set({ pendingEmail: email, emailVerifyCode: code, emailVerifyExpiry: expiry }).where(eq(users.id, userId)).run()
|
||||
|
||||
try {
|
||||
await sendVerifyCode(email, code)
|
||||
} catch {
|
||||
} catch (err) {
|
||||
console.error('[email]', err)
|
||||
return c.json({ error: '邮件发送失败,请稍后再试' }, 500)
|
||||
}
|
||||
return c.json({ success: true })
|
||||
@@ -123,7 +131,8 @@ authRouter.post('/email/verify', requireAuth, async (c) => {
|
||||
if (!user.emailVerifyCode || user.emailVerifyCode !== code.trim()) return c.json({ error: '验证码错误' }, 400)
|
||||
if (!user.emailVerifyExpiry || Date.now() > user.emailVerifyExpiry) return c.json({ error: '验证码已过期,请重新发送' }, 400)
|
||||
|
||||
db.update(users).set({ emailVerified: true, emailVerifyCode: null, emailVerifyExpiry: null }).where(eq(users.id, userId)).run()
|
||||
// 验证成功:将 pendingEmail 写入正式 email 字段
|
||||
db.update(users).set({ email: user.pendingEmail, emailVerified: true, pendingEmail: null, emailVerifyCode: null, emailVerifyExpiry: null }).where(eq(users.id, userId)).run()
|
||||
return c.json({ success: true })
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user