Files
Docker-Proxy/hubcmdui/services/documentationService.js

325 lines
9.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 文档服务模块 - 处理文档管理功能
*/
const fs = require('fs').promises;
const path = require('path');
const logger = require('../logger');
const DOCUMENTATION_DIR = path.join(__dirname, '..', 'documentation');
const META_DIR = path.join(DOCUMENTATION_DIR, 'meta');
// 确保文档目录存在
async function ensureDocumentationDir() {
try {
await fs.access(DOCUMENTATION_DIR);
logger.debug('文档目录已存在');
// 确保meta目录存在
try {
await fs.access(META_DIR);
logger.debug('文档meta目录已存在');
} catch (error) {
if (error.code === 'ENOENT') {
await fs.mkdir(META_DIR, { recursive: true });
logger.success('文档meta目录已创建');
} else {
throw error;
}
}
} catch (error) {
if (error.code === 'ENOENT') {
await fs.mkdir(DOCUMENTATION_DIR, { recursive: true });
logger.success('文档目录已创建');
// 创建meta目录
await fs.mkdir(META_DIR, { recursive: true });
logger.success('文档meta目录已创建');
} else {
throw error;
}
}
}
// 获取文档列表
async function getDocumentationList() {
try {
await ensureDocumentationDir();
const files = await fs.readdir(DOCUMENTATION_DIR);
const documents = await Promise.all(files.map(async file => {
// 跳过目录和非文档文件
if (file === 'meta' || file.startsWith('.')) return null;
// 处理JSON文件
if (file.endsWith('.json')) {
try {
const filePath = path.join(DOCUMENTATION_DIR, file);
const content = await fs.readFile(filePath, 'utf8');
const doc = JSON.parse(content);
return {
id: path.parse(file).name,
title: doc.title,
published: doc.published,
createdAt: doc.createdAt || new Date().toISOString(),
updatedAt: doc.updatedAt || new Date().toISOString()
};
} catch (fileError) {
logger.error(`读取JSON文档文件 ${file} 失败:`, fileError);
return null;
}
}
// 处理MD文件
if (file.endsWith('.md')) {
try {
const id = path.parse(file).name;
let metaData = { published: true, title: path.parse(file).name };
// 尝试读取meta数据
try {
const metaPath = path.join(META_DIR, `${id}.json`);
const metaContent = await fs.readFile(metaPath, 'utf8');
metaData = { ...metaData, ...JSON.parse(metaContent) };
} catch (metaError) {
// meta文件不存在或无法解析使用默认值
logger.warn(`无法读取文档 ${id} 的meta数据:`, metaError.message);
}
// 确保有发布状态
if (typeof metaData.published !== 'boolean') {
metaData.published = true;
}
return {
id,
title: metaData.title || id,
path: file, // 不直接加载内容,而是提供路径
published: metaData.published,
createdAt: metaData.createdAt || new Date().toISOString(),
updatedAt: metaData.updatedAt || new Date().toISOString()
};
} catch (mdError) {
logger.error(`处理MD文档 ${file} 失败:`, mdError);
return null;
}
}
return null;
}));
return documents.filter(doc => doc !== null);
} catch (error) {
logger.error('获取文档列表失败:', error);
throw error;
}
}
// 获取已发布文档
async function getPublishedDocuments() {
const documents = await getDocumentationList();
return documents.filter(doc => doc.published);
}
// 获取单个文档
async function getDocument(id) {
try {
await ensureDocumentationDir();
// 首先尝试读取JSON文件
try {
const jsonPath = path.join(DOCUMENTATION_DIR, `${id}.json`);
const jsonContent = await fs.readFile(jsonPath, 'utf8');
return JSON.parse(jsonContent);
} catch (jsonError) {
// JSON文件不存在尝试读取MD文件
if (jsonError.code === 'ENOENT') {
const mdPath = path.join(DOCUMENTATION_DIR, `${id}.md`);
const mdContent = await fs.readFile(mdPath, 'utf8');
// 读取meta数据
let metaData = { published: true, title: id };
try {
const metaPath = path.join(META_DIR, `${id}.json`);
const metaContent = await fs.readFile(metaPath, 'utf8');
metaData = { ...metaData, ...JSON.parse(metaContent) };
} catch (metaError) {
// meta文件不存在或无法解析使用默认值
logger.warn(`无法读取文档 ${id} 的meta数据:`, metaError.message);
}
return {
id,
title: metaData.title || id,
content: mdContent,
published: metaData.published,
createdAt: metaData.createdAt || new Date().toISOString(),
updatedAt: metaData.updatedAt || new Date().toISOString()
};
}
// 其他错误,直接抛出
throw jsonError;
}
} catch (error) {
logger.error(`获取文档 ${id} 失败:`, error);
throw error;
}
}
// 保存文档
async function saveDocument(id, title, content) {
try {
await ensureDocumentationDir();
const docId = id || Date.now().toString();
const docPath = path.join(DOCUMENTATION_DIR, `${docId}.json`);
// 检查是否已存在,保留发布状态
let published = false;
try {
const existingDoc = await fs.readFile(docPath, 'utf8');
published = JSON.parse(existingDoc).published || false;
} catch (error) {
// 文件不存在,使用默认值
}
const now = new Date().toISOString();
const docData = {
title,
content,
published,
createdAt: now,
updatedAt: now
};
await fs.writeFile(
docPath,
JSON.stringify(docData, null, 2),
'utf8'
);
return { id: docId, ...docData };
} catch (error) {
logger.error('保存文档失败:', error);
throw error;
}
}
// 删除文档
async function deleteDocument(id) {
try {
await ensureDocumentationDir();
// 删除JSON文件(如果存在)
try {
const jsonPath = path.join(DOCUMENTATION_DIR, `${id}.json`);
await fs.unlink(jsonPath);
} catch (error) {
if (error.code !== 'ENOENT') {
logger.warn(`删除JSON文档 ${id} 失败:`, error);
}
}
// 删除MD文件(如果存在)
try {
const mdPath = path.join(DOCUMENTATION_DIR, `${id}.md`);
await fs.unlink(mdPath);
} catch (error) {
if (error.code !== 'ENOENT') {
logger.warn(`删除MD文档 ${id} 失败:`, error);
}
}
// 删除meta文件(如果存在)
try {
const metaPath = path.join(META_DIR, `${id}.json`);
await fs.unlink(metaPath);
} catch (error) {
if (error.code !== 'ENOENT') {
logger.warn(`删除文档 ${id} 的meta数据失败:`, error);
}
}
return { success: true };
} catch (error) {
logger.error(`删除文档 ${id} 失败:`, error);
throw error;
}
}
// 切换文档发布状态
async function toggleDocumentPublish(id) {
try {
await ensureDocumentationDir();
// 尝试读取JSON文件
try {
const jsonPath = path.join(DOCUMENTATION_DIR, `${id}.json`);
const content = await fs.readFile(jsonPath, 'utf8');
const doc = JSON.parse(content);
doc.published = !doc.published;
doc.updatedAt = new Date().toISOString();
await fs.writeFile(jsonPath, JSON.stringify(doc, null, 2), 'utf8');
return doc;
} catch (jsonError) {
// 如果JSON文件不存在尝试处理MD文件的meta数据
if (jsonError.code === 'ENOENT') {
const mdPath = path.join(DOCUMENTATION_DIR, `${id}.md`);
// 确认MD文件存在
try {
await fs.access(mdPath);
} catch (mdError) {
throw new Error(`文档 ${id} 不存在`);
}
// 获取或创建meta数据
const metaPath = path.join(META_DIR, `${id}.json`);
let metaData = { published: true, title: id };
try {
const metaContent = await fs.readFile(metaPath, 'utf8');
metaData = { ...metaData, ...JSON.parse(metaContent) };
} catch (metaError) {
// meta文件不存在使用默认值
}
// 切换发布状态
metaData.published = !metaData.published;
metaData.updatedAt = new Date().toISOString();
// 保存meta数据
await fs.writeFile(metaPath, JSON.stringify(metaData, null, 2), 'utf8');
// 获取MD文件内容
const mdContent = await fs.readFile(mdPath, 'utf8');
return {
id,
title: metaData.title,
content: mdContent,
published: metaData.published,
createdAt: metaData.createdAt,
updatedAt: metaData.updatedAt
};
}
// 其他错误,直接抛出
throw jsonError;
}
} catch (error) {
logger.error(`切换文档 ${id} 发布状态失败:`, error);
throw error;
}
}
module.exports = {
ensureDocumentationDir,
getDocumentationList,
getPublishedDocuments,
getDocument,
saveDocument,
deleteDocument,
toggleDocumentPublish
};