mirror of
https://github.com/dqzboy/Docker-Proxy.git
synced 2026-07-05 23:36:30 +08:00
325 lines
9.4 KiB
JavaScript
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
|
|
};
|