|
|
@@ -120,16 +120,18 @@ app.get('/api/category/:category', async (req, res) => {
|
|
|
});
|
|
|
|
|
|
// API: 获取指定文档的内容
|
|
|
-app.get('/api/doc/:category/:docName', async (req, res) => {
|
|
|
+// 修改为支持通配符路径参数,以正确处理子目录
|
|
|
+app.get('/api/doc/:category/*', async (req, res) => {
|
|
|
try {
|
|
|
const category = sanitizePath(req.params.category);
|
|
|
- const docName = sanitizePath(req.params.docName);
|
|
|
+ const docName = req.params[0]; // 获取剩余的路径部分,支持子目录
|
|
|
|
|
|
if (!category || !docName) {
|
|
|
return res.status(400).json({ error: '无效的分类或文档名称' });
|
|
|
}
|
|
|
|
|
|
const docsDir = path.join(__dirname, 'docs');
|
|
|
+ // 直接使用 docName,它可能包含子目录路径
|
|
|
const docPath = path.join(docsDir, category, `${docName}.md`);
|
|
|
|
|
|
// 验证路径是否在 docs 目录内
|
|
|
@@ -145,10 +147,11 @@ app.get('/api/doc/:category/:docName', async (req, res) => {
|
|
|
});
|
|
|
|
|
|
// API: 保存文档内容
|
|
|
-app.put('/api/doc/:category/:docName', async (req, res) => {
|
|
|
+// 修改为支持通配符路径参数,以正确处理子目录
|
|
|
+app.put('/api/doc/:category/*', async (req, res) => {
|
|
|
try {
|
|
|
const category = sanitizePath(req.params.category);
|
|
|
- const docName = sanitizePath(req.params.docName);
|
|
|
+ const docName = req.params[0]; // 获取剩余的路径部分,支持子目录
|
|
|
const { content } = req.body;
|
|
|
|
|
|
// 验证输入
|
|
|
@@ -161,6 +164,7 @@ app.put('/api/doc/:category/:docName', async (req, res) => {
|
|
|
}
|
|
|
|
|
|
const docsDir = path.join(__dirname, 'docs');
|
|
|
+ // 直接使用 docName,它可能包含子目录路径
|
|
|
const docPath = path.join(docsDir, category, `${docName}.md`);
|
|
|
|
|
|
// 验证路径是否在 docs 目录内
|
|
|
@@ -194,7 +198,8 @@ app.put('/api/doc/:category/:docName', async (req, res) => {
|
|
|
app.get('/api/search/:category', async (req, res) => {
|
|
|
try {
|
|
|
const category = sanitizePath(req.params.category);
|
|
|
- const currentDoc = sanitizePath(req.query.currentDoc);
|
|
|
+ // currentDoc 可能包含子目录路径,不要使用 sanitizePath
|
|
|
+ const currentDoc = req.query.currentDoc || '';
|
|
|
const { q } = req.query;
|
|
|
|
|
|
if (!category) {
|
|
|
@@ -214,17 +219,36 @@ app.get('/api/search/:category', async (req, res) => {
|
|
|
return res.status(403).json({ error: '拒绝访问:无效的路径' });
|
|
|
}
|
|
|
|
|
|
- // 读取分类下的所有文档
|
|
|
- const files = await fs.readdir(categoryPath);
|
|
|
- const mdFiles = files.filter(file => file.endsWith('.md'));
|
|
|
+ // 从 index.md 获取当前分类下应该搜索的文档列表
|
|
|
+ const structure = await getIndexStructure();
|
|
|
+ const categoryData = structure.find(cat => cat.name === category);
|
|
|
+
|
|
|
+ if (!categoryData) {
|
|
|
+ return res.status(404).json({ error: '分类不存在' });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 只搜索 index.md 中列出的文档
|
|
|
+ const mdFiles = categoryData.docs.map(doc => {
|
|
|
+ // doc.name 可能包含子目录路径,如 "测试/11"
|
|
|
+ return doc.name + '.md';
|
|
|
+ });
|
|
|
|
|
|
const currentDocResults = [];
|
|
|
const otherDocsResults = [];
|
|
|
|
|
|
// 搜索每个文档
|
|
|
for (const file of mdFiles) {
|
|
|
- const docName = file.replace('.md', '');
|
|
|
- const filePath = path.join(categoryPath, file);
|
|
|
+ const docName = file.replace('.md', '').replace(/\\/g, '/'); // 统一使用正斜杠
|
|
|
+ const filePath = path.join(categoryPath, file.replace(/\//g, path.sep)); // 处理路径分隔符
|
|
|
+
|
|
|
+ // 检查文件是否存在(以防 index.md 中列出的文件实际不存在)
|
|
|
+ try {
|
|
|
+ await fs.access(filePath);
|
|
|
+ } catch (err) {
|
|
|
+ console.warn(`文档不存在,跳过搜索: ${filePath}`);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
|
|
|
|
// 搜索匹配的行
|
|
|
@@ -259,7 +283,9 @@ app.get('/api/search/:category', async (req, res) => {
|
|
|
};
|
|
|
|
|
|
// 区分当前文档和其他文档
|
|
|
- if (docName === currentDoc) {
|
|
|
+ // 处理 URL 解码后的 currentDoc(支持斜杠路径)
|
|
|
+ const decodedCurrentDoc = decodeURIComponent(currentDoc).replace(/\\/g, '/');
|
|
|
+ if (docName === decodedCurrentDoc) {
|
|
|
currentDocResults.push(result);
|
|
|
} else {
|
|
|
otherDocsResults.push(result);
|