- 实现 JWT Token 生成和验证 - 实现登录、注册、登出、重置密码 API - 实现邮箱验证码发送功能(配置从环境变量读取) - 实现密码加密和验证工具 - 支持获取当前用户信息
171 lines
4.3 KiB
TypeScript
171 lines
4.3 KiB
TypeScript
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 { verifyPassword, validateEmail } from '@/lib/password';
|
|
import { generateToken, setAuthCookie } from '@/lib/auth';
|
|
|
|
// 请求体验证
|
|
const loginSchema = z.object({
|
|
email: z.string().email('邮箱格式不正确'),
|
|
loginType: z.enum(['password', 'code']),
|
|
password: z.string().optional(),
|
|
code: z.string().optional(),
|
|
}).refine((data) => {
|
|
if (data.loginType === 'password') {
|
|
return !!data.password;
|
|
}
|
|
if (data.loginType === 'code') {
|
|
return !!data.code && data.code.length === 6;
|
|
}
|
|
return false;
|
|
}, {
|
|
message: '请提供密码或验证码',
|
|
});
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const body = await request.json();
|
|
|
|
// 验证请求体
|
|
const result = loginSchema.safeParse(body);
|
|
if (!result.success) {
|
|
return NextResponse.json(
|
|
{ success: false, error: result.error.issues[0].message },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const { email, loginType, password, code } = result.data;
|
|
|
|
// 验证邮箱格式
|
|
if (!validateEmail(email)) {
|
|
return NextResponse.json(
|
|
{ success: false, error: '邮箱格式不正确' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// 查找用户
|
|
const [user] = await db
|
|
.select()
|
|
.from(users)
|
|
.where(eq(users.email, email))
|
|
.limit(1);
|
|
|
|
if (!user) {
|
|
return NextResponse.json(
|
|
{ success: false, error: '该邮箱未注册' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// 检查用户状态
|
|
if (user.status === 'banned') {
|
|
return NextResponse.json(
|
|
{ success: false, error: '账号已被禁用' },
|
|
{ status: 403 }
|
|
);
|
|
}
|
|
|
|
if (user.status === 'inactive') {
|
|
return NextResponse.json(
|
|
{ success: false, error: '账号未激活' },
|
|
{ status: 403 }
|
|
);
|
|
}
|
|
|
|
// 密码登录
|
|
if (loginType === 'password') {
|
|
if (!password) {
|
|
return NextResponse.json(
|
|
{ success: false, error: '请输入密码' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const isValidPassword = await verifyPassword(password, user.password);
|
|
if (!isValidPassword) {
|
|
return NextResponse.json(
|
|
{ success: false, error: '密码错误' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// 验证码登录
|
|
if (loginType === 'code') {
|
|
if (!code) {
|
|
return NextResponse.json(
|
|
{ success: false, error: '请输入验证码' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
const now = new Date();
|
|
const validCode = await db
|
|
.select()
|
|
.from(verificationCodes)
|
|
.where(
|
|
and(
|
|
eq(verificationCodes.email, email),
|
|
eq(verificationCodes.code, code),
|
|
eq(verificationCodes.type, 'login'),
|
|
eq(verificationCodes.used, false),
|
|
gt(verificationCodes.expiresAt, now)
|
|
)
|
|
)
|
|
.limit(1);
|
|
|
|
if (validCode.length === 0) {
|
|
return NextResponse.json(
|
|
{ success: false, error: '验证码无效或已过期' },
|
|
{ status: 400 }
|
|
);
|
|
}
|
|
|
|
// 标记验证码为已使用
|
|
await db
|
|
.update(verificationCodes)
|
|
.set({ used: true })
|
|
.where(eq(verificationCodes.id, validCode[0].id));
|
|
}
|
|
|
|
// 更新最后登录时间
|
|
await db
|
|
.update(users)
|
|
.set({ lastLoginAt: new Date(), updatedAt: new Date() })
|
|
.where(eq(users.userId, user.userId));
|
|
|
|
// 生成 JWT Token
|
|
const token = await generateToken({
|
|
userId: user.userId,
|
|
email: user.email,
|
|
nickname: user.nickname,
|
|
plan: user.plan || 'free',
|
|
});
|
|
|
|
// 设置认证 Cookie
|
|
await setAuthCookie(token);
|
|
|
|
return NextResponse.json({
|
|
success: true,
|
|
user: {
|
|
id: user.userId,
|
|
email: user.email,
|
|
nickname: user.nickname,
|
|
plan: user.plan,
|
|
avatar: user.avatar,
|
|
},
|
|
message: '登录成功',
|
|
});
|
|
} catch (error) {
|
|
console.error('登录失败:', error);
|
|
return NextResponse.json(
|
|
{ success: false, error: '服务器错误' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|