feat: 添加大纲写作功能与服务器端改进
- 新增 OutlineWriterPanel 和 OutlineResultPanel 组件 - 重构服务器端数据库接口 (server/db.js) - 添加 LLM 服务模块 (server/llm.js) - 更新配置和设置面板 - 优化文档选择器和素材面板 - 更新部署文档和环境变量示例
This commit is contained in:
413
server/index.js
413
server/index.js
@@ -1,93 +1,344 @@
|
||||
import 'dotenv/config';
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import { getAllParadigms, getParadigmById, createParadigm, updateParadigm, deleteParadigm } from './db.js';
|
||||
import {
|
||||
getAllParadigms,
|
||||
getParadigmById,
|
||||
updateParadigm,
|
||||
deleteParadigm,
|
||||
addParadigm,
|
||||
getAllReferences,
|
||||
getReferenceById,
|
||||
getReferencesByType,
|
||||
addReference,
|
||||
updateReference,
|
||||
deleteReference,
|
||||
getConfig,
|
||||
setConfig,
|
||||
getAllDocuments,
|
||||
getDocumentById,
|
||||
getDocumentVersions,
|
||||
createDocument,
|
||||
updateDocument,
|
||||
saveDocumentVersion,
|
||||
deleteDocument,
|
||||
clearDocuments,
|
||||
getDocumentsByStatus,
|
||||
getAllOutlineMaterials,
|
||||
addOutlineMaterial,
|
||||
updateOutlineMaterial,
|
||||
deleteOutlineMaterial,
|
||||
exportDatabase,
|
||||
exportAsJSON,
|
||||
resetDatabase
|
||||
} from './db.js';
|
||||
import { getProviderSummary, streamChat } from './llm.js';
|
||||
|
||||
const app = express();
|
||||
const PORT = process.env.API_PORT || 3001;
|
||||
|
||||
// 中间件
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.use(express.json({ limit: '2mb' }));
|
||||
|
||||
// API 路由
|
||||
// 获取所有范式
|
||||
app.get('/api/paradigms', (req, res) => {
|
||||
try {
|
||||
const paradigms = getAllParadigms();
|
||||
res.json({ success: true, data: paradigms });
|
||||
} catch (error) {
|
||||
console.error('获取范式列表失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 获取单个范式
|
||||
app.get('/api/paradigms/:id', (req, res) => {
|
||||
try {
|
||||
const paradigm = getParadigmById(req.params.id);
|
||||
if (!paradigm) {
|
||||
return res.status(404).json({ success: false, error: '范式不存在' });
|
||||
}
|
||||
res.json({ success: true, data: paradigm });
|
||||
} catch (error) {
|
||||
console.error('获取范式失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 创建范式
|
||||
app.post('/api/paradigms', (req, res) => {
|
||||
try {
|
||||
const paradigm = createParadigm(req.body);
|
||||
console.log('✅ 创建范式:', paradigm.name);
|
||||
res.status(201).json({ success: true, data: paradigm });
|
||||
} catch (error) {
|
||||
console.error('创建范式失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 更新范式
|
||||
app.put('/api/paradigms/:id', (req, res) => {
|
||||
try {
|
||||
const paradigm = updateParadigm(req.params.id, req.body);
|
||||
if (!paradigm) {
|
||||
return res.status(404).json({ success: false, error: '范式不存在' });
|
||||
}
|
||||
console.log('✅ 更新范式:', paradigm.name);
|
||||
res.json({ success: true, data: paradigm });
|
||||
} catch (error) {
|
||||
console.error('更新范式失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 删除范式
|
||||
app.delete('/api/paradigms/:id', (req, res) => {
|
||||
try {
|
||||
const deleted = deleteParadigm(req.params.id);
|
||||
if (!deleted) {
|
||||
return res.status(404).json({ success: false, error: '范式不存在' });
|
||||
}
|
||||
console.log('✅ 删除范式:', req.params.id);
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
console.error('删除范式失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 健康检查
|
||||
app.get('/api/health', (req, res) => {
|
||||
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
||||
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
||||
});
|
||||
|
||||
// 启动服务器
|
||||
app.listen(PORT, () => {
|
||||
console.log(`🚀 API 服务器已启动: http://localhost:${PORT}`);
|
||||
console.log(` - GET /api/paradigms`);
|
||||
console.log(` - GET /api/paradigms/:id`);
|
||||
console.log(` - POST /api/paradigms`);
|
||||
console.log(` - PUT /api/paradigms/:id`);
|
||||
console.log(` - DELETE /api/paradigms/:id`);
|
||||
// LLM 代理
|
||||
app.get('/api/llm/providers', (req, res) => {
|
||||
res.json({ success: true, data: getProviderSummary() });
|
||||
});
|
||||
|
||||
app.post('/api/llm/stream', async (req, res) => {
|
||||
const controller = new AbortController();
|
||||
req.on('close', () => controller.abort());
|
||||
|
||||
try {
|
||||
const { providerId, model, appId, messages, options } = req.body || {};
|
||||
if (!providerId || !Array.isArray(messages)) {
|
||||
res.status(400).json({ success: false, error: '参数不完整' });
|
||||
return;
|
||||
}
|
||||
|
||||
await streamChat({
|
||||
providerId,
|
||||
model,
|
||||
appId,
|
||||
messages,
|
||||
options,
|
||||
res,
|
||||
signal: controller.signal
|
||||
});
|
||||
} catch (error) {
|
||||
if (!res.headersSent) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 素材库
|
||||
app.get('/api/references', (req, res) => {
|
||||
try {
|
||||
const { type } = req.query;
|
||||
const data = type ? getReferencesByType(type) : getAllReferences();
|
||||
res.json({ success: true, data });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/references/:id', (req, res) => {
|
||||
try {
|
||||
const data = getReferenceById(req.params.id);
|
||||
if (!data) return res.status(404).json({ success: false, error: '素材不存在' });
|
||||
res.json({ success: true, data });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/references', (req, res) => {
|
||||
try {
|
||||
const id = addReference(req.body);
|
||||
res.status(201).json({ success: true, data: { id } });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/api/references/:id', (req, res) => {
|
||||
try {
|
||||
const ok = updateReference(req.params.id, req.body || {});
|
||||
res.json({ success: true, data: ok });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/api/references/:id', (req, res) => {
|
||||
try {
|
||||
const ok = deleteReference(req.params.id);
|
||||
res.json({ success: true, data: ok });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 范式
|
||||
app.get('/api/paradigms', (req, res) => {
|
||||
try {
|
||||
const paradigms = getAllParadigms();
|
||||
res.json({ success: true, data: paradigms });
|
||||
} catch (error) {
|
||||
console.error('获取范式列表失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/paradigms/:id', (req, res) => {
|
||||
try {
|
||||
const paradigm = getParadigmById(req.params.id);
|
||||
if (!paradigm) return res.status(404).json({ success: false, error: '范式不存在' });
|
||||
res.json({ success: true, data: paradigm });
|
||||
} catch (error) {
|
||||
console.error('获取范式失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/paradigms', (req, res) => {
|
||||
try {
|
||||
const id = addParadigm(req.body);
|
||||
res.status(201).json({ success: true, data: { id } });
|
||||
} catch (error) {
|
||||
console.error('创建范式失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/api/paradigms/:id', (req, res) => {
|
||||
try {
|
||||
const ok = updateParadigm(req.params.id, req.body || {});
|
||||
res.json({ success: true, data: ok });
|
||||
} catch (error) {
|
||||
console.error('更新范式失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/api/paradigms/:id', (req, res) => {
|
||||
try {
|
||||
const ok = deleteParadigm(req.params.id);
|
||||
res.json({ success: true, data: ok });
|
||||
} catch (error) {
|
||||
console.error('删除范式失败:', error);
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 用户配置
|
||||
app.get('/api/config/:key', (req, res) => {
|
||||
try {
|
||||
const value = getConfig(req.params.key, null);
|
||||
res.json({ success: true, data: value });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/api/config/:key', (req, res) => {
|
||||
try {
|
||||
const ok = setConfig(req.params.key, req.body?.value);
|
||||
res.json({ success: true, data: ok });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 文稿
|
||||
app.get('/api/documents', (req, res) => {
|
||||
try {
|
||||
const { status } = req.query;
|
||||
const docs = status ? getDocumentsByStatus(status) : getAllDocuments();
|
||||
res.json({ success: true, data: docs });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/documents/:id', (req, res) => {
|
||||
try {
|
||||
const doc = getDocumentById(req.params.id);
|
||||
if (!doc) return res.status(404).json({ success: false, error: '文稿不存在' });
|
||||
res.json({ success: true, data: doc });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/documents/:id/versions', (req, res) => {
|
||||
try {
|
||||
const versions = getDocumentVersions(req.params.id);
|
||||
res.json({ success: true, data: versions });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/documents', (req, res) => {
|
||||
try {
|
||||
const id = createDocument(req.body);
|
||||
res.status(201).json({ success: true, data: { id } });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/documents/:id/versions', (req, res) => {
|
||||
try {
|
||||
const { content, changeNote } = req.body || {};
|
||||
const versionNumber = saveDocumentVersion(req.params.id, content || '', changeNote || '');
|
||||
res.json({ success: true, data: { versionNumber } });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/api/documents/:id', (req, res) => {
|
||||
try {
|
||||
const ok = updateDocument(req.params.id, req.body || {});
|
||||
res.json({ success: true, data: ok });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/api/documents/:id', (req, res) => {
|
||||
try {
|
||||
const ok = deleteDocument(req.params.id);
|
||||
res.json({ success: true, data: ok });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/documents/clear', (req, res) => {
|
||||
try {
|
||||
const ok = clearDocuments();
|
||||
res.json({ success: true, data: ok });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 提纲素材
|
||||
app.get('/api/outline-materials', (req, res) => {
|
||||
try {
|
||||
const data = getAllOutlineMaterials();
|
||||
res.json({ success: true, data });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/outline-materials', (req, res) => {
|
||||
try {
|
||||
const id = addOutlineMaterial(req.body);
|
||||
res.status(201).json({ success: true, data: { id } });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/api/outline-materials/:id', (req, res) => {
|
||||
try {
|
||||
const ok = updateOutlineMaterial(req.params.id, req.body || {});
|
||||
res.json({ success: true, data: ok });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.delete('/api/outline-materials/:id', (req, res) => {
|
||||
try {
|
||||
const ok = deleteOutlineMaterial(req.params.id);
|
||||
res.json({ success: true, data: ok });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// 数据导出/重置
|
||||
app.get('/api/db/export', (req, res) => {
|
||||
try {
|
||||
const data = exportDatabase();
|
||||
res.setHeader('Content-Type', 'application/octet-stream');
|
||||
res.send(data);
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/api/db/export-json', (req, res) => {
|
||||
try {
|
||||
const data = exportAsJSON();
|
||||
res.json({ success: true, data });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/api/db/reset', async (req, res) => {
|
||||
try {
|
||||
await resetDatabase();
|
||||
res.json({ success: true });
|
||||
} catch (error) {
|
||||
res.status(500).json({ success: false, error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(PORT, () => {
|
||||
console.log(`🚀 API 服务器已启动: http://localhost:${PORT}`);
|
||||
console.log(' - LLM 代理: POST /api/llm/stream');
|
||||
console.log(' - 数据接口: /api/references /api/paradigms /api/documents');
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user