123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- import { NextRequest, NextResponse } from 'next/server';
- import { findUserByEmail, createVerificationToken } from '@/lib/user-service';
- import { sendEmail, generateVerificationEmailHtml } from '@/lib/email';
- import { isValidEmail } from '@/lib/auth-utils';
- import { db } from '@/lib/db';
- import { verificationTokens } from '@/lib/schema';
- import { and, eq, gt } from 'drizzle-orm';
- // 限制重发频率(5分钟内只能发送一次)
- const RESEND_COOLDOWN_MINUTES = 5;
- export async function POST(request: NextRequest) {
- let locale = 'zh'; // 默认语言
-
- try {
- const { email, locale: requestLocale = 'zh' } = await request.json();
- locale = requestLocale;
- // 验证输入
- if (!email) {
- return NextResponse.json(
- { error: locale === 'zh' ? '邮箱地址是必填项' : 'Email address is required' },
- { status: 400 }
- );
- }
- if (!isValidEmail(email)) {
- return NextResponse.json(
- { error: locale === 'zh' ? '邮箱格式不正确' : 'Invalid email format' },
- { status: 400 }
- );
- }
- // 查找用户
- const user = await findUserByEmail(email.toLowerCase().trim());
- if (!user) {
- return NextResponse.json(
- { error: locale === 'zh' ? '用户不存在' : 'User not found' },
- { status: 404 }
- );
- }
- // 检查邮箱是否已验证
- if (user.isEmailVerified) {
- return NextResponse.json(
- { error: locale === 'zh' ? '邮箱已经验证过了' : 'Email is already verified' },
- { status: 400 }
- );
- }
- // 检查是否在冷却期内(防止重复发送)
- const cooldownTime = new Date(Date.now() - RESEND_COOLDOWN_MINUTES * 60 * 1000);
- const recentToken = await db
- .select()
- .from(verificationTokens)
- .where(
- and(
- eq(verificationTokens.email, user.email),
- eq(verificationTokens.type, 'email_verification'),
- gt(verificationTokens.createdAt, cooldownTime)
- )
- )
- .limit(1);
- if (recentToken.length > 0) {
- return NextResponse.json(
- {
- error: locale === 'zh'
- ? `请等待${RESEND_COOLDOWN_MINUTES}分钟后再重新发送验证邮件`
- : `Please wait ${RESEND_COOLDOWN_MINUTES} minutes before resending verification email`
- },
- { status: 429 }
- );
- }
- // 生成新的验证令牌
- const verificationToken = await createVerificationToken(
- user.email,
- 'email_verification',
- 24
- );
- if (!verificationToken) {
- return NextResponse.json(
- { error: locale === 'zh' ? '生成验证令牌失败' : 'Failed to generate verification token' },
- { status: 500 }
- );
- }
- // 发送验证邮件
- const verificationUrl = `${process.env.NEXTAUTH_URL}/${locale}/auth/verify-email?token=${verificationToken}`;
- const emailHtml = generateVerificationEmailHtml(verificationUrl, locale);
-
- const emailResult = await sendEmail({
- to: user.email,
- subject: locale === 'zh' ? 'Aiartools - 邮箱验证' : 'Aiartools - Email Verification',
- html: emailHtml,
- });
- if (!emailResult.success) {
- console.error('验证邮件发送失败:', emailResult.error);
- return NextResponse.json(
- { error: locale === 'zh' ? '邮件发送失败,请稍后重试' : 'Failed to send email, please try again later' },
- { status: 500 }
- );
- }
- return NextResponse.json({
- message: locale === 'zh'
- ? '验证邮件已重新发送,请查收邮箱'
- : 'Verification email has been resent, please check your inbox',
- cooldownMinutes: RESEND_COOLDOWN_MINUTES
- });
- } catch (error) {
- console.error('重发验证邮件错误:', error);
- return NextResponse.json(
- { error: locale === 'zh' ? '服务器内部错误' : 'Internal server error' },
- { status: 500 }
- );
- }
- }
- export async function GET() {
- return NextResponse.json({
- message: '重发邮箱验证API',
- version: '1.0.0',
- cooldown_minutes: RESEND_COOLDOWN_MINUTES,
- usage: 'POST to /api/auth/resend-verification with email and locale'
- });
- }
|