- 添加账号管理详情页(基本信息、提示词、CapCut、参考图标签页) - 重构资产页面,按项目组分开展示图片/视频 - 聊天界面支持深度思考内容折叠展示、复制、删除消息 - 设置页面支持Agent配置(Anthropic/OpenAI协议)和工具配置 - 后端支持OpenAI兼容协议流式输出和DeepSeek思考模式 - 添加对话置顶/删除功能、数据库迁移、资产清单API - 添加账号参考图上传/删除、技能配置持久化、连接测试API
111 lines
3.8 KiB
TypeScript
111 lines
3.8 KiB
TypeScript
import { Router } from 'express';
|
|
import { randomUUID } from 'crypto';
|
|
import Anthropic from '@anthropic-ai/sdk';
|
|
import OpenAI from 'openai';
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
import { getDb } from '../db';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
const PROJECT_ROOT = path.resolve(__dirname, '..', '..', '..');
|
|
const SKILLS_CONFIG_PATH = path.join(PROJECT_ROOT, '.claude', 'skills', 'config.json');
|
|
|
|
export const configsRouter = Router();
|
|
|
|
configsRouter.get('/', (_req, res) => {
|
|
const rows = getDb().prepare('SELECT * FROM configs ORDER BY key').all();
|
|
res.json(rows);
|
|
});
|
|
|
|
configsRouter.get('/:key', (req, res) => {
|
|
const row = getDb().prepare('SELECT * FROM configs WHERE key = ?').get(req.params.key);
|
|
if (!row) return res.status(404).json({ error: 'Config not found' });
|
|
res.json(row);
|
|
});
|
|
|
|
configsRouter.put('/:key', (req, res) => {
|
|
const { value } = req.body;
|
|
getDb().prepare(`
|
|
INSERT INTO configs (id, key, value, updated_at)
|
|
VALUES (?, ?, ?, datetime('now'))
|
|
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at
|
|
`).run(randomUUID(), req.params.key, JSON.stringify(value));
|
|
res.json({ key: req.params.key, ok: true });
|
|
});
|
|
|
|
// Skills config.json (pipeline tools configuration)
|
|
configsRouter.get('/skills/file', (_req, res) => {
|
|
try {
|
|
const data = JSON.parse(fs.readFileSync(SKILLS_CONFIG_PATH, 'utf-8'));
|
|
res.json(data);
|
|
} catch {
|
|
res.status(404).json({ error: 'Skills config not found' });
|
|
}
|
|
});
|
|
|
|
configsRouter.put('/skills/file', (req, res) => {
|
|
try {
|
|
fs.writeFileSync(SKILLS_CONFIG_PATH, JSON.stringify(req.body, null, 2), 'utf-8');
|
|
res.json({ ok: true });
|
|
} catch (e) {
|
|
res.status(500).json({ error: (e as Error).message });
|
|
}
|
|
});
|
|
|
|
// Test API connection
|
|
configsRouter.post('/test-connection', async (_req, res) => {
|
|
const configRow = getDb().prepare('SELECT value FROM configs WHERE key = ?').get('api_keys') as { value: string } | undefined;
|
|
if (!configRow) return res.json({ ok: false, error: '未配置 API Key' });
|
|
|
|
let apiKey = '';
|
|
let baseURL: string | undefined;
|
|
let protocol: string = 'anthropic';
|
|
let model: string = 'claude-sonnet-4-6';
|
|
|
|
try {
|
|
const cfg = JSON.parse(configRow.value);
|
|
apiKey = cfg.ANTHROPIC_AUTH_TOKEN || '';
|
|
baseURL = cfg.ANTHROPIC_BASE_URL;
|
|
protocol = cfg.PROTOCOL || 'anthropic';
|
|
model = cfg.ANTHROPIC_MODEL || model;
|
|
} catch {
|
|
return res.json({ ok: false, error: '配置解析失败' });
|
|
}
|
|
|
|
if (!apiKey) return res.json({ ok: false, error: 'API Key 为空' });
|
|
|
|
try {
|
|
if (protocol === 'openai') {
|
|
const client = new OpenAI({ apiKey, baseURL: baseURL || 'https://api.openai.com/v1' });
|
|
const models = await client.models.list();
|
|
const modelIds: string[] = [];
|
|
for await (const m of models) { modelIds.push(m.id); if (modelIds.length >= 5) break; }
|
|
res.json({ ok: true, models: modelIds });
|
|
} else {
|
|
const client = new Anthropic({ apiKey, baseURL });
|
|
// Anthropic SDK doesn't have a models.list() in all versions, try a minimal request
|
|
try {
|
|
await client.messages.create({
|
|
model,
|
|
max_tokens: 1,
|
|
messages: [{ role: 'user', content: 'hi' }],
|
|
});
|
|
res.json({ ok: true, models: [model] });
|
|
} catch (e: unknown) {
|
|
const err = e as { status?: number; message?: string };
|
|
if (err.status === 401) {
|
|
res.json({ ok: false, error: `认证失败: ${err.message}` });
|
|
} else if (err.status === 400 || err.status === undefined) {
|
|
res.json({ ok: true, models: [model] });
|
|
} else {
|
|
res.json({ ok: false, error: `错误 (${err.status}): ${err.message}` });
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
res.json({ ok: false, error: (e as Error).message });
|
|
}
|
|
});
|