claude-code-cchui/src/lib/email.ts
gaoziman 733c93a91c feat(认证): 实现用户认证API和工具库
- 实现 JWT Token 生成和验证
- 实现登录、注册、登出、重置密码 API
- 实现邮箱验证码发送功能(配置从环境变量读取)
- 实现密码加密和验证工具
- 支持获取当前用户信息
2025-12-19 22:36:08 +08:00

137 lines
4.5 KiB
TypeScript

import nodemailer from 'nodemailer';
// 邮箱配置(从环境变量读取)
const SMTP_HOST = process.env.SMTP_HOST || 'smtp.qq.com';
const SMTP_PORT = parseInt(process.env.SMTP_PORT || '465');
const SMTP_USER = process.env.SMTP_USER || '';
const SMTP_PASS = process.env.SMTP_PASS || '';
// SMTP 配置
const transporter = nodemailer.createTransport({
host: SMTP_HOST,
port: SMTP_PORT,
secure: true,
auth: {
user: SMTP_USER,
pass: SMTP_PASS,
},
});
// 邮件模板类型
type EmailType = 'register' | 'login' | 'reset';
// 获取邮件主题
function getSubject(type: EmailType): string {
const subjects: Record<EmailType, string> = {
register: 'LionCode - 注册验证码',
login: 'LionCode - 登录验证码',
reset: 'LionCode - 重置密码验证码',
};
return subjects[type];
}
// 获取邮件内容
function getEmailContent(type: EmailType, code: string): string {
const typeText: Record<EmailType, string> = {
register: '注册账号',
login: '登录账号',
reset: '重置密码',
};
return `
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>验证码</title>
</head>
<body style="margin: 0; padding: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background-color: #f5f5f5;">
<table role="presentation" style="width: 100%; border-collapse: collapse;">
<tr>
<td align="center" style="padding: 40px 0;">
<table role="presentation" style="width: 100%; max-width: 600px; border-collapse: collapse; background-color: #ffffff; border-radius: 12px; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);">
<!-- Header -->
<tr>
<td style="padding: 40px 40px 20px; text-align: center;">
<h1 style="margin: 0; font-size: 28px; font-weight: 700; color: #1a1a1a;">LionCode</h1>
<p style="margin: 8px 0 0; font-size: 14px; color: #666;">智能 AI 对话助手</p>
</td>
</tr>
<!-- Content -->
<tr>
<td style="padding: 20px 40px;">
<p style="margin: 0 0 20px; font-size: 16px; color: #333; line-height: 1.6;">
您好!
</p>
<p style="margin: 0 0 20px; font-size: 16px; color: #333; line-height: 1.6;">
您正在进行<strong>${typeText[type]}</strong>操作,验证码如下:
</p>
<!-- Code Box -->
<div style="background: linear-gradient(135deg, #E06B3E 0%, #D4643E 100%); border-radius: 8px; padding: 24px; text-align: center; margin: 24px 0;">
<span style="font-size: 36px; font-weight: 700; letter-spacing: 8px; color: #ffffff;">${code}</span>
</div>
<p style="margin: 20px 0 0; font-size: 14px; color: #666; line-height: 1.6;">
验证码有效期为 <strong>5 分钟</strong>,请尽快完成验证。
</p>
<p style="margin: 12px 0 0; font-size: 14px; color: #999; line-height: 1.6;">
如果这不是您本人的操作,请忽略此邮件。
</p>
</td>
</tr>
<!-- Footer -->
<tr>
<td style="padding: 20px 40px 40px; border-top: 1px solid #eee;">
<p style="margin: 0; font-size: 12px; color: #999; text-align: center;">
此邮件由系统自动发送,请勿回复。
</p>
<p style="margin: 8px 0 0; font-size: 12px; color: #999; text-align: center;">
© ${new Date().getFullYear()} LionCode. All rights reserved.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
`.trim();
}
// 发送验证码邮件
export async function sendVerificationEmail(
to: string,
code: string,
type: EmailType
): Promise<{ success: boolean; error?: string }> {
try {
await transporter.sendMail({
from: {
name: 'LionCode',
address: SMTP_USER,
},
to,
subject: getSubject(type),
html: getEmailContent(type, code),
});
return { success: true };
} catch (error) {
console.error('发送邮件失败:', error);
return {
success: false,
error: error instanceof Error ? error.message : '发送邮件失败',
};
}
}
// 生成6位数字验证码
export function generateVerificationCode(): string {
return Math.floor(100000 + Math.random() * 900000).toString();
}