feat(设置): 实现用户级别设置隔离

- 设置 API 改为基于当前登录用户
- 注册时自动创建默认用户设置
- API Key 加密后存储到数据库
- 添加默认设置常量和格式化函数
This commit is contained in:
gaoziman 2025-12-21 14:03:55 +08:00
parent 2e8033a8ae
commit 058ea85daa
2 changed files with 109 additions and 57 deletions

View File

@ -122,9 +122,19 @@ export async function POST(request: NextRequest) {
})
.returning();
// 创建默认用户设置
// 创建默认用户设置API Key 为空,需要用户自行配置)
await db.insert(userSettings).values({
userId,
cchUrl: process.env.CCH_DEFAULT_URL || 'https://claude.leocoder.cn/',
cchApiKey: null,
cchApiKeyConfigured: false,
defaultModel: 'claude-sonnet-4-20250514',
defaultTools: ['web_search', 'code_execution', 'web_fetch'],
theme: 'light',
language: 'zh-CN',
fontSize: 15,
enableThinking: false,
saveChatHistory: true,
});
// 生成 JWT Token

View File

@ -2,18 +2,12 @@ import { NextResponse } from 'next/server';
import { db } from '@/drizzle/db';
import { userSettings } from '@/drizzle/schema';
import { eq } from 'drizzle-orm';
import { getCurrentUser } from '@/lib/auth';
import { encryptApiKey } from '@/lib/crypto';
// GET /api/settings - 获取用户设置
export async function GET() {
try {
const settings = await db.query.userSettings.findFirst({
where: eq(userSettings.id, 1),
});
if (!settings) {
// 如果没有设置,返回默认值
return NextResponse.json({
cchUrl: process.env.CCH_DEFAULT_URL || 'http://localhost:13500',
// 默认设置值
const DEFAULT_SETTINGS = {
cchUrl: process.env.CCH_DEFAULT_URL || 'https://claude.leocoder.cn/',
cchApiKeyConfigured: false,
defaultModel: 'claude-sonnet-4-20250514',
defaultTools: ['web_search', 'code_execution', 'web_fetch'],
@ -24,23 +18,65 @@ export async function GET() {
fontSize: 15,
enableThinking: false,
saveChatHistory: true,
});
};
// 格式化设置响应(不返回 API Key 本身)
function formatSettingsResponse(settings: typeof userSettings.$inferSelect | null) {
if (!settings) {
return DEFAULT_SETTINGS;
}
// 不返回 API Key 本身,只返回是否已配置
return NextResponse.json({
cchUrl: settings.cchUrl,
cchApiKeyConfigured: settings.cchApiKeyConfigured,
defaultModel: settings.defaultModel,
defaultTools: settings.defaultTools,
return {
cchUrl: settings.cchUrl || DEFAULT_SETTINGS.cchUrl,
cchApiKeyConfigured: settings.cchApiKeyConfigured || false,
defaultModel: settings.defaultModel || DEFAULT_SETTINGS.defaultModel,
defaultTools: settings.defaultTools || DEFAULT_SETTINGS.defaultTools,
systemPrompt: settings.systemPrompt || '',
temperature: settings.temperature || '0.7',
theme: settings.theme,
language: settings.language,
theme: settings.theme || DEFAULT_SETTINGS.theme,
language: settings.language || DEFAULT_SETTINGS.language,
fontSize: settings.fontSize || 15,
enableThinking: settings.enableThinking,
saveChatHistory: settings.saveChatHistory,
enableThinking: settings.enableThinking || false,
saveChatHistory: settings.saveChatHistory ?? true,
};
}
// GET /api/settings - 获取当前用户的设置
export async function GET() {
try {
// 1. 获取当前登录用户
const user = await getCurrentUser();
if (!user) {
// 未登录用户返回默认设置(允许查看但无法保存)
return NextResponse.json(DEFAULT_SETTINGS);
}
// 2. 查询该用户的设置
const settings = await db.query.userSettings.findFirst({
where: eq(userSettings.userId, user.userId),
});
// 3. 如果没有设置记录,创建默认设置
if (!settings) {
const newSettings = await db
.insert(userSettings)
.values({
userId: user.userId,
cchUrl: DEFAULT_SETTINGS.cchUrl,
defaultModel: DEFAULT_SETTINGS.defaultModel,
defaultTools: DEFAULT_SETTINGS.defaultTools,
theme: DEFAULT_SETTINGS.theme,
language: DEFAULT_SETTINGS.language,
fontSize: DEFAULT_SETTINGS.fontSize,
enableThinking: DEFAULT_SETTINGS.enableThinking,
saveChatHistory: DEFAULT_SETTINGS.saveChatHistory,
})
.returning();
return NextResponse.json(formatSettingsResponse(newSettings[0]));
}
return NextResponse.json(formatSettingsResponse(settings));
} catch (error) {
console.error('Failed to get settings:', error);
return NextResponse.json(
@ -50,9 +86,18 @@ export async function GET() {
}
}
// PUT /api/settings - 更新用户设置
// PUT /api/settings - 更新当前用户设置
export async function PUT(request: Request) {
try {
// 1. 获取当前登录用户
const user = await getCurrentUser();
if (!user) {
return NextResponse.json(
{ error: '请先登录后再修改设置' },
{ status: 401 }
);
}
const body = await request.json();
const {
cchUrl,
@ -68,7 +113,7 @@ export async function PUT(request: Request) {
saveChatHistory,
} = body;
// 构建更新对象
// 2. 构建更新对象
const updateData: Record<string, unknown> = {
updatedAt: new Date(),
};
@ -77,14 +122,15 @@ export async function PUT(request: Request) {
updateData.cchUrl = cchUrl;
}
// 如果提供了 API Key更新它
// 如果提供了 API Key加密后存储
if (cchApiKey !== undefined) {
if (cchApiKey === '') {
// 清除 API Key
updateData.cchApiKey = null;
updateData.cchApiKeyConfigured = false;
} else {
updateData.cchApiKey = cchApiKey;
// 加密存储 API Key
updateData.cchApiKey = encryptApiKey(cchApiKey);
updateData.cchApiKeyConfigured = true;
}
}
@ -126,15 +172,23 @@ export async function PUT(request: Request) {
updateData.saveChatHistory = saveChatHistory;
}
// 检查是否存在设置记录
// 3. 检查是否存在该用户的设置记录
const existing = await db.query.userSettings.findFirst({
where: eq(userSettings.id, 1),
where: eq(userSettings.userId, user.userId),
});
if (!existing) {
// 创建新的设置记录
await db.insert(userSettings).values({
id: 1,
userId: user.userId,
cchUrl: DEFAULT_SETTINGS.cchUrl,
defaultModel: DEFAULT_SETTINGS.defaultModel,
defaultTools: DEFAULT_SETTINGS.defaultTools,
theme: DEFAULT_SETTINGS.theme,
language: DEFAULT_SETTINGS.language,
fontSize: DEFAULT_SETTINGS.fontSize,
enableThinking: DEFAULT_SETTINGS.enableThinking,
saveChatHistory: DEFAULT_SETTINGS.saveChatHistory,
...updateData,
});
} else {
@ -142,27 +196,15 @@ export async function PUT(request: Request) {
await db
.update(userSettings)
.set(updateData)
.where(eq(userSettings.id, 1));
.where(eq(userSettings.userId, user.userId));
}
// 返回更新后的设置(不包含 API Key
// 4. 返回更新后的设置(不包含 API Key
const updatedSettings = await db.query.userSettings.findFirst({
where: eq(userSettings.id, 1),
where: eq(userSettings.userId, user.userId),
});
return NextResponse.json({
cchUrl: updatedSettings?.cchUrl,
cchApiKeyConfigured: updatedSettings?.cchApiKeyConfigured,
defaultModel: updatedSettings?.defaultModel,
defaultTools: updatedSettings?.defaultTools,
systemPrompt: updatedSettings?.systemPrompt || '',
temperature: updatedSettings?.temperature || '0.7',
theme: updatedSettings?.theme,
language: updatedSettings?.language,
fontSize: updatedSettings?.fontSize || 15,
enableThinking: updatedSettings?.enableThinking,
saveChatHistory: updatedSettings?.saveChatHistory,
});
return NextResponse.json(formatSettingsResponse(updatedSettings ?? null));
} catch (error) {
console.error('Failed to update settings:', error);
return NextResponse.json(