/** * 光湖知识库 · API 服务器 * * Express + Git 引擎,提供知识库全部 REST API。 * Agent 直接调用这些接口,不经过任何第三方服务。 */ import express from 'express'; import cors from 'cors'; import { GitEngine } from './git-engine.js'; import path from 'path'; import { fileURLToPath } from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); // ─── 配置 ─── const PORT = parseInt(process.env.KB_PORT || '3890', 10); const REPO_PATH = process.env.KB_REPO_PATH || path.resolve(__dirname, '../kb-data'); // ─── 初始化 ─── const app = express(); const engine = new GitEngine(REPO_PATH); app.use(cors()); app.use(express.json({ limit: '10mb' })); // Express v5 的 {*param} 返回数组,工具函数统一转字符串 const p = (v: unknown): string => Array.isArray(v) ? v.join('/') : String(v); // ─── 健康检查 ─── app.get('/api/health', (_req, res) => { res.json({ module: 'guanghu-knowledge-base', version: '0.1.0', engine: 'git', repo: REPO_PATH, status: 'online', time: new Date().toISOString(), }); }); // ─── 文档树 ─── app.get('/api/tree', async (_req, res) => { try { const tree = await engine.getTree(); res.json({ ok: true, tree }); } catch (err: any) { res.status(500).json({ ok: false, error: err.message }); } }); // ─── 文档 CRUD ─── // 读取文档 app.get('/api/docs/{*docPath}', async (req, res) => { try { const docPath = p(req.params.docPath); const doc = await engine.getDoc(docPath); res.json({ ok: true, doc }); } catch (err: any) { res.status(404).json({ ok: false, error: `文档不存在: ${p(req.params.docPath)}` }); } }); // 创建文档 app.post('/api/docs/{*docPath}', async (req, res) => { try { const docPath = p(req.params.docPath); const { title, body, author } = req.body; if (!title || !body) { return res.status(400).json({ ok: false, error: 'title 和 body 必填' }); } const doc = await engine.createDoc(docPath, title, body, author); res.status(201).json({ ok: true, doc }); } catch (err: any) { res.status(500).json({ ok: false, error: err.message }); } }); // 更新文档 app.put('/api/docs/{*docPath}', async (req, res) => { try { const docPath = p(req.params.docPath); const { title, body, author } = req.body; if (!title || !body) { return res.status(400).json({ ok: false, error: 'title 和 body 必填' }); } const doc = await engine.updateDoc(docPath, title, body, author); res.json({ ok: true, doc }); } catch (err: any) { res.status(500).json({ ok: false, error: err.message }); } }); // 删除文档 app.delete('/api/docs/{*docPath}', async (req, res) => { try { const docPath = p(req.params.docPath); await engine.deleteDoc(docPath); res.json({ ok: true, message: `已删除: ${docPath}` }); } catch (err: any) { res.status(500).json({ ok: false, error: err.message }); } }); // 重命名/移动文档 app.post('/api/move', async (req, res) => { try { const { oldPath, newPath } = req.body; if (!oldPath || !newPath) { return res.status(400).json({ ok: false, error: 'oldPath 和 newPath 必填' }); } await engine.moveDoc(oldPath, newPath); res.json({ ok: true, message: `${oldPath} → ${newPath}` }); } catch (err: any) { res.status(500).json({ ok: false, error: err.message }); } }); // ─── 版本历史 ─── app.get('/api/history/{*docPath}', async (req, res) => { try { const docPath = p(req.params.docPath); const maxCount = parseInt(req.query.max?.toString() || '50', 10); const history = await engine.getHistory(docPath, maxCount); res.json({ ok: true, history }); } catch (err: any) { res.status(500).json({ ok: false, error: err.message }); } }); // 获取某版本内容 app.get('/api/version/:hash/{*docPath}', async (req, res) => { try { const { hash } = req.params; const docPath = p(req.params.docPath); const content = await engine.getDocAtVersion(docPath, hash); res.json({ ok: true, content }); } catch (err: any) { res.status(500).json({ ok: false, error: err.message }); } }); // 版本对比 app.get('/api/diff/{*docPath}', async (req, res) => { try { const docPath = p(req.params.docPath); const { from, to } = req.query; if (!from || !to) { return res.status(400).json({ ok: false, error: 'from 和 to (commit hash) 必填' }); } const diff = await engine.diffVersions(docPath, from.toString(), to.toString()); res.json({ ok: true, diff }); } catch (err: any) { res.status(500).json({ ok: false, error: err.message }); } }); // ─── 搜索 ─── app.get('/api/search', async (req, res) => { try { const query = req.query.q?.toString(); if (!query) { return res.status(400).json({ ok: false, error: 'q 参数必填' }); } const results = await engine.search(query); res.json({ ok: true, results, count: results.length }); } catch (err: any) { res.status(500).json({ ok: false, error: err.message }); } }); // ─── 启动 ─── async function start() { await engine.init(); app.listen(PORT, () => { console.log(`光湖知识库 API 已启动: http://localhost:${PORT}`); console.log(`仓库路径: ${REPO_PATH}`); }); } start().catch(err => { console.error('启动失败:', err); process.exit(1); }); export default app;