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

128 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
import { db } from '@/drizzle/db';
import { users, verificationCodes } from '@/drizzle/schema';
import { eq, and, gt } from 'drizzle-orm';
import { sendVerificationEmail, generateVerificationCode } from '@/lib/email';
import { validateEmail } from '@/lib/password';
// 请求体验证
const sendCodeSchema = z.object({
email: z.string().email('邮箱格式不正确'),
type: z.enum(['register', 'login', 'reset']),
});
export async function POST(request: NextRequest) {
try {
const body = await request.json();
// 验证请求体
const result = sendCodeSchema.safeParse(body);
if (!result.success) {
return NextResponse.json(
{ success: false, error: result.error.issues[0].message },
{ status: 400 }
);
}
const { email, type } = result.data;
// 验证邮箱格式
if (!validateEmail(email)) {
return NextResponse.json(
{ success: false, error: '邮箱格式不正确' },
{ status: 400 }
);
}
// 检查邮箱是否已注册(仅注册时检查)
if (type === 'register') {
const existingUser = await db
.select()
.from(users)
.where(eq(users.email, email))
.limit(1);
if (existingUser.length > 0) {
return NextResponse.json(
{ success: false, error: '该邮箱已被注册' },
{ status: 400 }
);
}
}
// 检查邮箱是否存在(登录和重置密码时检查)
if (type === 'login' || type === 'reset') {
const existingUser = await db
.select()
.from(users)
.where(eq(users.email, email))
.limit(1);
if (existingUser.length === 0) {
return NextResponse.json(
{ success: false, error: '该邮箱未注册' },
{ status: 400 }
);
}
}
// 检查是否频繁发送1分钟内只能发送1次
const oneMinuteAgo = new Date(Date.now() - 60 * 1000);
const recentCode = await db
.select()
.from(verificationCodes)
.where(
and(
eq(verificationCodes.email, email),
eq(verificationCodes.type, type),
gt(verificationCodes.createdAt, oneMinuteAgo)
)
)
.limit(1);
if (recentCode.length > 0) {
return NextResponse.json(
{ success: false, error: '发送太频繁,请稍后再试' },
{ status: 429 }
);
}
// 生成验证码
const code = generateVerificationCode();
// 计算过期时间5分钟后
const expiresAt = new Date(Date.now() + 5 * 60 * 1000);
// 保存验证码到数据库
await db.insert(verificationCodes).values({
email,
code,
type,
expiresAt,
});
// 发送邮件
const emailResult = await sendVerificationEmail(email, code, type);
if (!emailResult.success) {
return NextResponse.json(
{ success: false, error: emailResult.error || '发送邮件失败' },
{ status: 500 }
);
}
return NextResponse.json({
success: true,
message: '验证码已发送,请查收邮件',
expiresIn: 300, // 5分钟
});
} catch (error) {
console.error('发送验证码失败:', error);
return NextResponse.json(
{ success: false, error: '服务器错误' },
{ status: 500 }
);
}
}