This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
CJYDocs 是一个基于 Node.js + Express 的轻量级 Markdown 文档管理和渲染系统,采用前后端分离架构。核心特点包括:
index.md 配置文件实现文档的层级化管理npm install # 安装依赖
npm start # 生产模式启动
npm run dev # 开发模式启动(使用 nodemon 自动重启)
服务器默认运行在 http://localhost:3000
# 构建镜像
docker build -t cjydocs:latest .
# 运行容器(必须挂载 docs 和 index.md)
docker run -d \
--name cjydocs \
-p 3000:3000 \
-v $(pwd)/docs:/app/docs \
-v $(pwd)/index.md:/app/index.md \
--restart unless-stopped \
cjydocs:latest
重要: Docker 镜像不包含文档数据,必须挂载 docs/ 和 index.md,否则容器无法正常工作。
前端 (public/)
↓ REST API
后端 (server.js)
↓ File I/O
数据层 (docs/)
关键文件: ./index.md (项目根目录,NOT docs/index.md)
系统的文档结构完全由 index.md 定义,格式如下:
[分类名称]
1: 文档名.md
2: 文档名.md
2.1: 子文档名.md
2.2: 子文档名.md
解析逻辑 (server.js:247-282):
[分类名] → 对应 docs/ 下的目录名编号: 文档名.md → 文档项,支持多级编号(如 1.1.1)level 通过点号数量计算: 1 = level 0, 1.1 = level 1, 1.1.1 = level 2{ name, docs: [{ number, name, level, fullName }] }重要: 添加新文档时:
docs/分类名/ 下创建 .md 文件(可以放在子目录中,如 docs/分类名/子目录/文档.md)index.md 中添加对应条目(子目录文档使用斜杠,如 1: 子目录/文档.md)子目录支持:
1: 子目录/文档名.md/api/doc/分类名/子目录/文档名/api/image/:category/* 提供服务DOM 缓存策略 (reader.js:8-22):
const DOM = {
loading: null,
content: null,
docNav: null,
toc: null,
leftSidebar: null,
rightSidebar: null,
editBtn: null,
editorContainer: null,
markdownEditor: null,
// ... 更多元素
};
所有频繁访问的 DOM 元素都通过此对象缓存,避免重复查询。缓存采用懒加载策略:首次使用时查询并缓存,后续直接使用。
事件委托模式:
#doc-nav 父元素上监听,而非每个导航项#toc 父元素上监听所有 API 都在 server.js 中定义:
认证相关:
POST /api/login - 用户登录(密码: cjy@0526)POST /api/logout - 用户登出GET /api/check-auth - 检查登录状态文档操作 (需要登录):
GET /api/structure - 获取完整文档结构(解析 index.md,带5秒缓存)GET /api/category/:category - 获取指定分类信息GET /api/doc/:category/* - 获取文档内容(支持子目录路径)PUT /api/doc/:category/* - 保存文档内容(在线编辑,支持子目录)GET /api/search/:category?q=xxx¤tDoc=yyy - 搜索文档资源服务 (需要登录):
GET /api/image/:category/* - 获取文档目录中的图片资源安全机制:
requireAuth 中间件)sanitizePath() 清理和 validatePath() 验证,防止路径遍历攻击缓存策略:
index.md 解析结果缓存5秒(server.js:14-16),减少文件系统读取频率。
server.js:10-167 - 完整的会话管理和认证系统:
认证流程:
用户访问 / 或 /reader.html → 检查 session.isAuthenticated
↓ (未登录)
重定向到 /login.html
↓
用户输入密码 → POST /api/login
↓
验证密码 (cjy@0526) → 创建 session
↓
重定向到首页 → 可以访问所有文档
Session 配置 (server.js:48-61):
maxAge: 7 * 24 * 60 * 60 * 1000)cjydocs.sid认证中间件 (server.js:64-76):
function requireAuth(req, res, next) {
if (req.session && req.session.isAuthenticated) {
return next();
}
// API 请求返回 401,页面请求重定向
if (req.path.startsWith('/api')) {
return res.status(401).json({ error: '未登录,请先登录' });
}
res.redirect('/login.html');
}
密码配置:
server.js:11 (PASSWORD = 'cjy@0526')process.env.PASSWORD使用 marked.js + highlight.js,配置在 reader.js:25-38
reader.js:190-270 - 遍历渲染后的 HTML,提取所有 h1-h6 标题,使用 IntersectionObserver 实现滚动监听
server.js:159-244 - 逐行扫描所有 .md 文件,返回匹配的行号和上下文片段(最多5个)reader.js:443-785 - 使用 TreeWalker API 精确定位到匹配的文本节点并滚动localStorage,按分类隔离reader.js:359-440 - 移动端侧边栏管理逻辑:
e.stopPropagation() 防止事件冒泡,使用 element.contains() 精确判断点击位置IntersectionObserver:替代 scroll 事件,性能提升 20-30%server.js:170-202 - 从文档目录提供图片资源:
功能说明:
/api/image/:category/* 提供使用示例:
<!-- Markdown 文档中 -->

<!-- 前端会自动转换为 -->
/api/image/分类名/assets/test.png
安全验证:
validatePath() 确保不会访问到系统其他文件reader.js:820-1095 + server.js:278-324 - 完整的在线文档编辑系统:
编辑器特性:
reader.js:869-988):根据当前阅读位置(标题、段落)在编辑器中自动定位到对应的 markdown 源码行PUT /api/doc/:category/:docName 保存到服务器,保存后自动重新渲染并更新 TOC编辑流程:
点击编辑按钮 → 进入编辑模式
↓
findVisibleElement() - 找到视口中可见的元素
↓
positionCursorByElement() - 在编辑器中定位到对应行
↓
用户编辑内容
↓
saveDocument() → PUT /api/doc/:category/:docName
↓
服务器验证路径 + 保存文件
↓
前端重新渲染 + 退出编辑模式
安全验证:
sanitizePath() 和 validatePath() 验证用户体验细节:
cjydocs/
├── server.js # Express 后端(499行)
│ # - 登录认证和会话管理
│ # - 所有 API 端点(GET/PUT/POST)
│ # - index.md 解析逻辑(带5秒缓存)
│ # - 安全验证函数(sanitizePath, validatePath)
│ # - 搜索引擎实现
│ # - 图片资源服务
├── index.md # 文档结构配置文件(项目根目录)
├── docs/
│ └── 分类名/
│ ├── 文档.md # 实际文档内容(可通过在线编辑修改)
│ ├── 子目录/
│ │ └── 文档.md # 支持子目录组织
│ └── assets/
│ └── 图片.png # 图片资源
├── public/
│ ├── login.html # 登录页面
│ ├── index.html # 首页(显示分类列表)
│ ├── reader.html # 阅读器页面(三栏布局+编辑器)
│ ├── css/style.css # 统一样式
│ └── js/
│ ├── index.js # 首页逻辑
│ └── reader.js # 阅读器核心逻辑
│ # - DOM缓存、事件委托
│ # - 搜索、TOC、侧边栏交互
│ # - 在线编辑器(智能光标定位)
├── Dockerfile # Docker 镜像构建文件
├── DOCKER.md # Docker 部署详细文档
├── CLAUDE.md # Claude Code 项目指南
└── README.md # 项目说明文档
server.js 中定义新路由requireAuth 中间件sanitizePath() 清理所有用户输入validatePath() 验证文件路径在 docs/ 目录内server.js:11 中的 PASSWORD 常量const PASSWORD = process.env.PASSWORD || 'cjy@0526';DOM 缓存对象获取元素classList.toggle() 简化条件判断window.innerWidth)以区分设备类型e.stopPropagation() 防止事件冒泡,避免触发外部点击事件index.md.md 文件findVisibleElement() 和 positionCursorByElement() 函数currentMarkdownContent 和 savedScrollPosition 的同步Session 配置:
secure: true密码安全:
cjy@0526,仅适用于开发/个人使用核心函数:
sanitizePath() (server.js:19-29) - 移除 ../、开头的 /、\ 等危险字符validatePath() (server.js:32-36) - 确保解析后的路径在 docs/ 目录内示例:
const category = sanitizePath(req.params.category);
const docPath = path.join(docsDir, category, `${docName}.md`);
if (!validatePath(docPath, docsDir)) {
return res.status(403).json({ error: '拒绝访问' });
}
注意事项:
sanitizePath() 会移除所有斜杠,因此某些参数(如 currentDoc)不能使用此函数* 来支持子目录,如 /api/doc/:category/*validatePath() 依然确保路径不会越出 docs 目录fixed 定位覆盖在内容区上方transform: translateX() 实现滑入滑出动画bottom: 100px,避免与侧边栏按钮冲突fixed 定位,只在需要时显示server.js:11 中的 PASSWORD 常量检查:
server.js:48-61 中的 session 配置rolling: true 应该会在每次请求时重置过期时间检查:
index.md 中是否有对应条目index.md 中的名称完全一致(区分大小写)/)检查:
检查:
e.stopPropagation() 是否正确阻止了按钮点击事件冒泡window.innerWidth <= 1024)检查:
DOM 缓存对象)检查:
检查: