claude-code-cchui/src/app/api/auth/register/route.ts
gaoziman 8392677cea refactor(配置): 移除默认CCH服务地址配置
- 注册接口不再设置默认CCH URL,需用户自行配置
- 设置API默认值改为空字符串
- 更新设置页面placeholder为更通用的提示文本
- 增强用户配置的灵活性
2025-12-23 21:11:49 +08:00

169 lines
4.6 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 { 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();
// 创建默认用户设置API Key 和服务地址为空,需要用户自行配置)
await db.insert(userSettings).values({
userId,
cchUrl: '', // 不设置默认值,让用户自己填写
cchApiKey: null,
cchApiKeyConfigured: false,
defaultModel: 'claude-sonnet-4-5-20250929',
defaultTools: ['web_search', 'web_fetch', 'youdao_translate'],
theme: 'light',
language: 'zh-CN',
fontSize: 15,
enableThinking: false,
saveChatHistory: true,
});
// 生成 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 }
);
}
}