Files
video-create/web/server/routes/configs.ts
sion123 088bdb9a8e feat(web): 重构前端UI并支持OpenAI协议
- 添加账号管理详情页(基本信息、提示词、CapCut、参考图标签页)
- 重构资产页面,按项目组分开展示图片/视频
- 聊天界面支持深度思考内容折叠展示、复制、删除消息
- 设置页面支持Agent配置(Anthropic/OpenAI协议)和工具配置
- 后端支持OpenAI兼容协议流式输出和DeepSeek思考模式
- 添加对话置顶/删除功能、数据库迁移、资产清单API
- 添加账号参考图上传/删除、技能配置持久化、连接测试API
2026-05-07 23:48:26 +08:00

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 });
}
});