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

159 lines
4.2 KiB
TypeScript

import { NextRequest, NextResponse } from 'next/server';
import { z } from 'zod';
import { nanoid } from 'nanoid';
import { db } from '@/drizzle/db';
import { users, verificationCodes, userSettings } from '@/drizzle/schema';
import { eq, and, gt } from 'drizzle-orm';
import { hashPassword, validatePassword, validateEmail, validateNickname } from '@/lib/password';
import { generateToken, setAuthCookie } from '@/lib/auth';
// 请求体验证
const registerSchema = z.object({
email: z.string().email('邮箱格式不正确'),
password: z.string().min(8, '密码长度不能少于8位'),
confirmPassword: z.string(),
nickname: z.string().min(2, '昵称长度不能少于2位').max(20, '昵称长度不能超过20位'),
code: z.string().length(6, '验证码必须是6位'),
}).refine((data) => data.password === data.confirmPassword, {
message: '两次输入的密码不一致',
path: ['confirmPassword'],
});
export async function POST(request: NextRequest) {
try {
const body = await request.json();
// 验证请求体
const result = registerSchema.safeParse(body);
if (!result.success) {
return NextResponse.json(
{ success: false, error: result.error.issues[0].message },
{ status: 400 }
);
}
const { email, password, nickname, code } = result.data;
// 验证邮箱格式
if (!validateEmail(email)) {
return NextResponse.json(
{ success: false, error: '邮箱格式不正确' },
{ status: 400 }
);
}
// 验证密码格式
const passwordValidation = validatePassword(password);
if (!passwordValidation.valid) {
return NextResponse.json(
{ success: false, error: passwordValidation.errors[0] },
{ status: 400 }
);
}
// 验证昵称格式
const nicknameValidation = validateNickname(nickname);
if (!nicknameValidation.valid) {
return NextResponse.json(
{ success: false, error: nicknameValidation.error },
{ status: 400 }
);
}
// 检查邮箱是否已注册
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 }
);
}
// 验证验证码
const now = new Date();
const validCode = await db
.select()
.from(verificationCodes)
.where(
and(
eq(verificationCodes.email, email),
eq(verificationCodes.code, code),
eq(verificationCodes.type, 'register'),
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));
// 加密密码
const hashedPassword = await hashPassword(password);
// 生成用户ID
const userId = nanoid();
// 创建用户
const [newUser] = await db
.insert(users)
.values({
userId,
email,
password: hashedPassword,
nickname,
emailVerified: true, // 已通过邮箱验证
lastLoginAt: now,
})
.returning();
// 创建默认用户设置
await db.insert(userSettings).values({
userId,
});
// 生成 JWT Token
const token = await generateToken({
userId: newUser.userId,
email: newUser.email,
nickname: newUser.nickname,
plan: newUser.plan || 'free',
});
// 设置认证 Cookie
await setAuthCookie(token);
return NextResponse.json({
success: true,
user: {
id: newUser.userId,
email: newUser.email,
nickname: newUser.nickname,
plan: newUser.plan,
},
message: '注册成功',
});
} catch (error) {
console.error('注册失败:', error);
return NextResponse.json(
{ success: false, error: '服务器错误' },
{ status: 500 }
);
}
}