聊天API (chat/route.ts): - 添加秘塔API Key解密和传递 - 集成工具使用追踪,记录每次对话使用的工具 - 支持图片搜索结果的流式返回 - 添加 tool_used 和 tool_search_images 事件类型 设置API (settings/route.ts): - 支持秘塔API Key的加密存储和清除 - 更新默认工具列表包含秘塔工具 消息API (messages/route.ts): - 支持搜索图片数据的追加保存
240 lines
7.0 KiB
TypeScript
240 lines
7.0 KiB
TypeScript
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';
|
||
|
||
// 默认设置值
|
||
const DEFAULT_SETTINGS = {
|
||
cchUrl: process.env.CCH_DEFAULT_URL || 'https://claude.leocoder.cn/',
|
||
cchApiKeyConfigured: false,
|
||
metasoApiKeyConfigured: false,
|
||
apiFormat: 'claude' as 'claude' | 'openai', // API 格式:claude(原生)| openai(兼容)
|
||
defaultModel: 'claude-sonnet-4-5-20250929',
|
||
defaultTools: ['web_search', 'web_fetch', 'mita_search', 'mita_reader'],
|
||
systemPrompt: '',
|
||
temperature: '0.7',
|
||
theme: 'light',
|
||
language: 'zh-CN',
|
||
fontSize: 15,
|
||
enableThinking: false,
|
||
saveChatHistory: true,
|
||
};
|
||
|
||
// 格式化设置响应(不返回 API Key 本身)
|
||
function formatSettingsResponse(settings: typeof userSettings.$inferSelect | null) {
|
||
if (!settings) {
|
||
return DEFAULT_SETTINGS;
|
||
}
|
||
|
||
return {
|
||
cchUrl: settings.cchUrl || DEFAULT_SETTINGS.cchUrl,
|
||
cchApiKeyConfigured: settings.cchApiKeyConfigured || false,
|
||
metasoApiKeyConfigured: settings.metasoApiKeyConfigured || false,
|
||
apiFormat: (settings.apiFormat as 'claude' | 'openai') || DEFAULT_SETTINGS.apiFormat,
|
||
defaultModel: settings.defaultModel || DEFAULT_SETTINGS.defaultModel,
|
||
defaultTools: settings.defaultTools || DEFAULT_SETTINGS.defaultTools,
|
||
systemPrompt: settings.systemPrompt || '',
|
||
temperature: settings.temperature || '0.7',
|
||
theme: settings.theme || DEFAULT_SETTINGS.theme,
|
||
language: settings.language || DEFAULT_SETTINGS.language,
|
||
fontSize: settings.fontSize || 15,
|
||
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(
|
||
{ error: 'Failed to get settings' },
|
||
{ status: 500 }
|
||
);
|
||
}
|
||
}
|
||
|
||
// 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,
|
||
cchApiKey,
|
||
metasoApiKey,
|
||
apiFormat,
|
||
defaultModel,
|
||
defaultTools,
|
||
systemPrompt,
|
||
temperature,
|
||
theme,
|
||
language,
|
||
fontSize,
|
||
enableThinking,
|
||
saveChatHistory,
|
||
} = body;
|
||
|
||
// 2. 构建更新对象
|
||
const updateData: Record<string, unknown> = {
|
||
updatedAt: new Date(),
|
||
};
|
||
|
||
if (cchUrl !== undefined) {
|
||
updateData.cchUrl = cchUrl;
|
||
}
|
||
|
||
// 如果提供了 API Key,加密后存储
|
||
if (cchApiKey !== undefined) {
|
||
if (cchApiKey === '') {
|
||
// 清除 API Key
|
||
updateData.cchApiKey = null;
|
||
updateData.cchApiKeyConfigured = false;
|
||
} else {
|
||
// 加密存储 API Key
|
||
updateData.cchApiKey = encryptApiKey(cchApiKey);
|
||
updateData.cchApiKeyConfigured = true;
|
||
}
|
||
}
|
||
|
||
// 如果提供了秘塔 API Key,加密后存储
|
||
if (metasoApiKey !== undefined) {
|
||
if (metasoApiKey === '') {
|
||
// 清除秘塔 API Key
|
||
updateData.metasoApiKey = null;
|
||
updateData.metasoApiKeyConfigured = false;
|
||
} else {
|
||
// 加密存储秘塔 API Key
|
||
updateData.metasoApiKey = encryptApiKey(metasoApiKey);
|
||
updateData.metasoApiKeyConfigured = true;
|
||
}
|
||
}
|
||
|
||
// API 格式类型
|
||
if (apiFormat !== undefined) {
|
||
updateData.apiFormat = apiFormat;
|
||
}
|
||
|
||
if (defaultModel !== undefined) {
|
||
updateData.defaultModel = defaultModel;
|
||
}
|
||
|
||
if (defaultTools !== undefined) {
|
||
updateData.defaultTools = defaultTools;
|
||
}
|
||
|
||
if (systemPrompt !== undefined) {
|
||
updateData.systemPrompt = systemPrompt;
|
||
}
|
||
|
||
if (temperature !== undefined) {
|
||
updateData.temperature = temperature;
|
||
}
|
||
|
||
if (theme !== undefined) {
|
||
updateData.theme = theme;
|
||
}
|
||
|
||
if (language !== undefined) {
|
||
updateData.language = language;
|
||
}
|
||
|
||
if (fontSize !== undefined) {
|
||
// 限制字体大小在 12-20 之间
|
||
updateData.fontSize = Math.min(20, Math.max(12, fontSize));
|
||
}
|
||
|
||
if (enableThinking !== undefined) {
|
||
updateData.enableThinking = enableThinking;
|
||
}
|
||
|
||
if (saveChatHistory !== undefined) {
|
||
updateData.saveChatHistory = saveChatHistory;
|
||
}
|
||
|
||
// 3. 检查是否存在该用户的设置记录
|
||
const existing = await db.query.userSettings.findFirst({
|
||
where: eq(userSettings.userId, user.userId),
|
||
});
|
||
|
||
if (!existing) {
|
||
// 创建新的设置记录
|
||
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,
|
||
...updateData,
|
||
});
|
||
} else {
|
||
// 更新现有记录
|
||
await db
|
||
.update(userSettings)
|
||
.set(updateData)
|
||
.where(eq(userSettings.userId, user.userId));
|
||
}
|
||
|
||
// 4. 返回更新后的设置(不包含 API Key)
|
||
const updatedSettings = await db.query.userSettings.findFirst({
|
||
where: eq(userSettings.userId, user.userId),
|
||
});
|
||
|
||
return NextResponse.json(formatSettingsResponse(updatedSettings ?? null));
|
||
} catch (error) {
|
||
console.error('Failed to update settings:', error);
|
||
return NextResponse.json(
|
||
{ error: 'Failed to update settings' },
|
||
{ status: 500 }
|
||
);
|
||
}
|
||
}
|