feat(设置): 实现用户级别设置隔离
- 设置 API 改为基于当前登录用户 - 注册时自动创建默认用户设置 - API Key 加密后存储到数据库 - 添加默认设置常量和格式化函数
This commit is contained in:
parent
2e8033a8ae
commit
058ea85daa
@ -122,9 +122,19 @@ export async function POST(request: NextRequest) {
|
|||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
|
|
||||||
// 创建默认用户设置
|
// 创建默认用户设置(API Key 为空,需要用户自行配置)
|
||||||
await db.insert(userSettings).values({
|
await db.insert(userSettings).values({
|
||||||
userId,
|
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
|
// 生成 JWT Token
|
||||||
|
|||||||
@ -2,18 +2,12 @@ import { NextResponse } from 'next/server';
|
|||||||
import { db } from '@/drizzle/db';
|
import { db } from '@/drizzle/db';
|
||||||
import { userSettings } from '@/drizzle/schema';
|
import { userSettings } from '@/drizzle/schema';
|
||||||
import { eq } from 'drizzle-orm';
|
import { eq } from 'drizzle-orm';
|
||||||
|
import { getCurrentUser } from '@/lib/auth';
|
||||||
|
import { encryptApiKey } from '@/lib/crypto';
|
||||||
|
|
||||||
// GET /api/settings - 获取用户设置
|
// 默认设置值
|
||||||
export async function GET() {
|
const DEFAULT_SETTINGS = {
|
||||||
try {
|
cchUrl: process.env.CCH_DEFAULT_URL || 'https://claude.leocoder.cn/',
|
||||||
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',
|
|
||||||
cchApiKeyConfigured: false,
|
cchApiKeyConfigured: false,
|
||||||
defaultModel: 'claude-sonnet-4-20250514',
|
defaultModel: 'claude-sonnet-4-20250514',
|
||||||
defaultTools: ['web_search', 'code_execution', 'web_fetch'],
|
defaultTools: ['web_search', 'code_execution', 'web_fetch'],
|
||||||
@ -24,23 +18,65 @@ export async function GET() {
|
|||||||
fontSize: 15,
|
fontSize: 15,
|
||||||
enableThinking: false,
|
enableThinking: false,
|
||||||
saveChatHistory: true,
|
saveChatHistory: true,
|
||||||
});
|
};
|
||||||
|
|
||||||
|
// 格式化设置响应(不返回 API Key 本身)
|
||||||
|
function formatSettingsResponse(settings: typeof userSettings.$inferSelect | null) {
|
||||||
|
if (!settings) {
|
||||||
|
return DEFAULT_SETTINGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 不返回 API Key 本身,只返回是否已配置
|
return {
|
||||||
return NextResponse.json({
|
cchUrl: settings.cchUrl || DEFAULT_SETTINGS.cchUrl,
|
||||||
cchUrl: settings.cchUrl,
|
cchApiKeyConfigured: settings.cchApiKeyConfigured || false,
|
||||||
cchApiKeyConfigured: settings.cchApiKeyConfigured,
|
defaultModel: settings.defaultModel || DEFAULT_SETTINGS.defaultModel,
|
||||||
defaultModel: settings.defaultModel,
|
defaultTools: settings.defaultTools || DEFAULT_SETTINGS.defaultTools,
|
||||||
defaultTools: settings.defaultTools,
|
|
||||||
systemPrompt: settings.systemPrompt || '',
|
systemPrompt: settings.systemPrompt || '',
|
||||||
temperature: settings.temperature || '0.7',
|
temperature: settings.temperature || '0.7',
|
||||||
theme: settings.theme,
|
theme: settings.theme || DEFAULT_SETTINGS.theme,
|
||||||
language: settings.language,
|
language: settings.language || DEFAULT_SETTINGS.language,
|
||||||
fontSize: settings.fontSize || 15,
|
fontSize: settings.fontSize || 15,
|
||||||
enableThinking: settings.enableThinking,
|
enableThinking: settings.enableThinking || false,
|
||||||
saveChatHistory: settings.saveChatHistory,
|
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) {
|
} catch (error) {
|
||||||
console.error('Failed to get settings:', error);
|
console.error('Failed to get settings:', error);
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
@ -50,9 +86,18 @@ export async function GET() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PUT /api/settings - 更新用户设置
|
// PUT /api/settings - 更新当前用户的设置
|
||||||
export async function PUT(request: Request) {
|
export async function PUT(request: Request) {
|
||||||
try {
|
try {
|
||||||
|
// 1. 获取当前登录用户
|
||||||
|
const user = await getCurrentUser();
|
||||||
|
if (!user) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{ error: '请先登录后再修改设置' },
|
||||||
|
{ status: 401 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const {
|
const {
|
||||||
cchUrl,
|
cchUrl,
|
||||||
@ -68,7 +113,7 @@ export async function PUT(request: Request) {
|
|||||||
saveChatHistory,
|
saveChatHistory,
|
||||||
} = body;
|
} = body;
|
||||||
|
|
||||||
// 构建更新对象
|
// 2. 构建更新对象
|
||||||
const updateData: Record<string, unknown> = {
|
const updateData: Record<string, unknown> = {
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
};
|
};
|
||||||
@ -77,14 +122,15 @@ export async function PUT(request: Request) {
|
|||||||
updateData.cchUrl = cchUrl;
|
updateData.cchUrl = cchUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果提供了 API Key,更新它
|
// 如果提供了 API Key,加密后存储
|
||||||
if (cchApiKey !== undefined) {
|
if (cchApiKey !== undefined) {
|
||||||
if (cchApiKey === '') {
|
if (cchApiKey === '') {
|
||||||
// 清除 API Key
|
// 清除 API Key
|
||||||
updateData.cchApiKey = null;
|
updateData.cchApiKey = null;
|
||||||
updateData.cchApiKeyConfigured = false;
|
updateData.cchApiKeyConfigured = false;
|
||||||
} else {
|
} else {
|
||||||
updateData.cchApiKey = cchApiKey;
|
// 加密存储 API Key
|
||||||
|
updateData.cchApiKey = encryptApiKey(cchApiKey);
|
||||||
updateData.cchApiKeyConfigured = true;
|
updateData.cchApiKeyConfigured = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,15 +172,23 @@ export async function PUT(request: Request) {
|
|||||||
updateData.saveChatHistory = saveChatHistory;
|
updateData.saveChatHistory = saveChatHistory;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否存在设置记录
|
// 3. 检查是否存在该用户的设置记录
|
||||||
const existing = await db.query.userSettings.findFirst({
|
const existing = await db.query.userSettings.findFirst({
|
||||||
where: eq(userSettings.id, 1),
|
where: eq(userSettings.userId, user.userId),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!existing) {
|
if (!existing) {
|
||||||
// 创建新的设置记录
|
// 创建新的设置记录
|
||||||
await db.insert(userSettings).values({
|
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,
|
...updateData,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -142,27 +196,15 @@ export async function PUT(request: Request) {
|
|||||||
await db
|
await db
|
||||||
.update(userSettings)
|
.update(userSettings)
|
||||||
.set(updateData)
|
.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({
|
const updatedSettings = await db.query.userSettings.findFirst({
|
||||||
where: eq(userSettings.id, 1),
|
where: eq(userSettings.userId, user.userId),
|
||||||
});
|
});
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json(formatSettingsResponse(updatedSettings ?? null));
|
||||||
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,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to update settings:', error);
|
console.error('Failed to update settings:', error);
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user