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

325 lines
9.4 KiB
JavaScript

/**
* 文档服务模块 - 处理文档管理功能
*/
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
};