feat: 邮箱验证码注册+登录协议勾选
- 后端: VerificationCode模型+send-code API+register增加verifyCode校验 - 前端: 注册表单增加验证码输入框+60s倒计时获取按钮 - 前端: 登录表单增加用户协议/隐私政策勾选 - 前端: 登录按钮增加disabled状态(未勾选时不可点击) - 后端: nodemailer邮件发送工具(可配置SMTP,未配置时返回devCode)
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
const nodemailer = require('nodemailer')
|
||||
const logger = require('./logger')
|
||||
|
||||
// SMTP 配置(优先从环境变量读取,方便部署时配置)
|
||||
const SMTP_HOST = process.env.SMTP_HOST || ''
|
||||
const SMTP_PORT = parseInt(process.env.SMTP_PORT || '465')
|
||||
const SMTP_USER = process.env.SMTP_USER || ''
|
||||
const SMTP_PASS = process.env.SMTP_PASS || ''
|
||||
const SMTP_FROM = process.env.SMTP_FROM || SMTP_USER
|
||||
|
||||
let transporter = null
|
||||
|
||||
function getTransporter() {
|
||||
if (transporter) return transporter
|
||||
if (!SMTP_HOST || !SMTP_USER || !SMTP_PASS) {
|
||||
logger.warn('[mail] SMTP 未配置,邮件发送功能不可用')
|
||||
return null
|
||||
}
|
||||
transporter = nodemailer.createTransport({
|
||||
host: SMTP_HOST,
|
||||
port: SMTP_PORT,
|
||||
secure: SMTP_PORT === 465,
|
||||
auth: { user: SMTP_USER, pass: SMTP_PASS }
|
||||
})
|
||||
return transporter
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送验证码邮件
|
||||
* @param {string} to - 收件人邮箱
|
||||
* @param {string} code - 6位验证码
|
||||
*/
|
||||
async function sendVerificationCode(to, code) {
|
||||
const transport = getTransporter()
|
||||
if (!transport) {
|
||||
// 开发环境:直接打印验证码
|
||||
logger.info(`[mail-dev] 验证码 ${code} -> ${to} (SMTP 未配置,请设置 SMTP_HOST/SMTP_USER/SMTP_PASS)`)
|
||||
return { success: true, devCode: code }
|
||||
}
|
||||
try {
|
||||
await transport.sendMail({
|
||||
from: `"AI维度" <${SMTP_FROM}>`,
|
||||
to,
|
||||
subject: '【AI维度】邮箱验证码',
|
||||
html: `
|
||||
<div style="max-width:480px;margin:0 auto;padding:24px;font-family:sans-serif;">
|
||||
<h2 style="color:#6366f1;">AI 维度 - 邮箱验证</h2>
|
||||
<p>您好!</p>
|
||||
<p>您的验证码为:</p>
|
||||
<div style="font-size:32px;font-weight:700;color:#6366f1;letter-spacing:8px;text-align:center;padding:20px;background:#f5f5f7;border-radius:12px;margin:16px 0;">
|
||||
${code}
|
||||
</div>
|
||||
<p style="color:#666;">验证码有效期为 10 分钟,请勿泄露给他人。</p>
|
||||
<p style="color:#999;font-size:12px;">如果这不是您的操作,请忽略此邮件。</p>
|
||||
<hr style="border:none;border-top:1px solid #eee;margin:20px 0;">
|
||||
<p style="color:#999;font-size:12px;text-align:center;">宇之然 AI 维度 · 北京宇之然科技中心</p>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
logger.info(`[mail] 验证码邮件发送成功 -> ${to}`)
|
||||
return { success: true }
|
||||
} catch (error) {
|
||||
logger.error(`[mail] 发送验证码失败 -> ${to}:`, error.message)
|
||||
return { success: false, error: error.message }
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { sendVerificationCode }
|
||||
Reference in New Issue
Block a user