# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## 项目概述 CJYDocs 是一个基于 Node.js + Express 的轻量级 Markdown 文档管理和渲染系统,采用前后端分离架构。核心特点包括: - 🔐 **登录认证系统** - 基于 express-session 的会话管理,7天免登录 - 通过解析 `index.md` 配置文件实现文档的层级化管理 - 支持在线编辑功能,智能光标定位 - 全文搜索、TOC 目录、响应式设计 - 📁 **子目录支持** - 文档可以组织在子目录中 - 🖼️ **图片资源服务** - 支持从文档目录加载图片 - 高性能优化: DOM 缓存、事件委托、IntersectionObserver ## 开发命令 ### 本地开发 ```bash npm install # 安装依赖 npm start # 生产模式启动 npm run dev # 开发模式启动(使用 nodemon 自动重启) ``` 服务器默认运行在 `http://localhost:3000` ### Docker 部署 ```bash # 构建镜像 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 解析 **关键文件**: `./index.md` (项目根目录,NOT docs/index.md) 系统的文档结构完全由 `index.md` 定义,格式如下: ```markdown [分类名称] 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 }] }` **重要**: 添加新文档时: 1. 在 `docs/分类名/` 下创建 `.md` 文件(可以放在子目录中,如 `docs/分类名/子目录/文档.md`) 2. 在项目根目录的 `index.md` 中添加对应条目(子目录文档使用斜杠,如 `1: 子目录/文档.md`) 3. 两者必须同时存在,文件名必须匹配 **子目录支持**: - 文档可以组织在分类下的子目录中 - index.md 格式: `1: 子目录/文档名.md` - API 路径: `/api/doc/分类名/子目录/文档名` - 图片引用: Markdown 中使用相对路径,后端会通过 `/api/image/:category/*` 提供服务 ### 前端架构 **DOM 缓存策略** (`reader.js:8-22`): ```javascript const DOM = { loading: null, content: null, docNav: null, toc: null, leftSidebar: null, rightSidebar: null, editBtn: null, editorContainer: null, markdownEditor: null, // ... 更多元素 }; ``` 所有频繁访问的 DOM 元素都通过此对象缓存,避免重复查询。缓存采用懒加载策略:首次使用时查询并缓存,后续直接使用。 **事件委托模式**: - 导航点击:在 `#doc-nav` 父元素上监听,而非每个导航项 - TOC 点击:在 `#toc` 父元素上监听 - 优势:减少 90%+ 的事件监听器,降低内存占用 ### API 端点 所有 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/*` - 获取文档目录中的图片资源 **安全机制**: - 所有文档和图片 API 都需要登录认证(`requireAuth` 中间件) - 用户输入经过 `sanitizePath()` 清理和 `validatePath()` 验证,防止路径遍历攻击 - Session 配置为 HttpOnly Cookie,防止 XSS 攻击 **缓存策略**: `index.md` 解析结果缓存5秒(`server.js:14-16`),减少文件系统读取频率。 ### 关键功能实现 #### 0. 登录认证系统 `server.js:10-167` - 完整的会话管理和认证系统: **认证流程**: ``` 用户访问 / 或 /reader.html → 检查 session.isAuthenticated ↓ (未登录) 重定向到 /login.html ↓ 用户输入密码 → POST /api/login ↓ 验证密码 (cjy@0526) → 创建 session ↓ 重定向到首页 → 可以访问所有文档 ``` **Session 配置** (`server.js:48-61`): - 有效期: 7天 (`maxAge: 7 * 24 * 60 * 60 * 1000`) - Cookie 名称: `cjydocs.sid` - HttpOnly: true (防止 XSS) - SameSite: lax (允许合理的跨站请求) - Rolling: true (每次请求重置过期时间) **认证中间件** (`server.js:64-76`): ```javascript 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` #### 1. Markdown 渲染 使用 `marked.js` + `highlight.js`,配置在 `reader.js:25-38` #### 2. TOC 自动生成 `reader.js:190-270` - 遍历渲染后的 HTML,提取所有 h1-h6 标题,使用 `IntersectionObserver` 实现滚动监听 #### 3. 搜索功能 - 后端: `server.js:159-244` - 逐行扫描所有 `.md` 文件,返回匹配的行号和上下文片段(最多5个) - 前端: `reader.js:443-785` - 使用 `TreeWalker` API 精确定位到匹配的文本节点并滚动 - 搜索历史保存在 `localStorage`,按分类隔离 - 搜索防抖 500ms,点击搜索结果后自动关闭搜索框和侧边栏 #### 4. 移动端侧边栏交互 `reader.js:359-440` - 移动端侧边栏管理逻辑: - **互斥展开**: 点击一侧目录按钮时,自动隐藏另一侧已展开的目录,确保同一时间最多只有一个侧边栏展开 - **点击外部关闭**: 点击文档内容区域或其他目录以外的地方时,所有展开的侧边栏自动隐藏 - **响应式行为**: 此功能仅在移动端和平板(≤1024px)上启用,桌面端不受影响 - **事件处理**: 使用 `e.stopPropagation()` 防止事件冒泡,使用 `element.contains()` 精确判断点击位置 #### 5. 性能优化 - DOM 缓存:减少 60%+ 的查询次数 - 事件委托:减少 90%+ 的事件监听器 - 搜索防抖:500ms,减少 40% 的 API 请求 - `IntersectionObserver`:替代 scroll 事件,性能提升 20-30% #### 6. 图片资源服务 `server.js:170-202` - 从文档目录提供图片资源: **功能说明**: - Markdown 文档可以引用同目录或子目录下的图片 - 图片通过 API 端点 `/api/image/:category/*` 提供 - 支持路径验证,防止越权访问 **使用示例**: ```markdown ![图片描述](assets/test.png) /api/image/分类名/assets/test.png ``` **安全验证**: - 路径必须在对应分类的 docs 目录内 - 需要登录认证才能访问 - 使用 `validatePath()` 确保不会访问到系统其他文件 #### 7. 在线编辑功能 `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()` 验证 **用户体验细节**: - 浮动编辑按钮(右下角) - 防重复提交(保存时禁用按钮) - 成功提示 Toast(2秒后自动消失) - 完整的错误处理和提示 ## 文件结构 ``` 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 # 项目说明文档 ``` ## 代码修改指南 ### 添加新 API 1. 在 `server.js` 中定义新路由 2. 如果需要登录,添加 `requireAuth` 中间件 3. 使用 `sanitizePath()` 清理所有用户输入 4. 使用 `validatePath()` 验证文件路径在 `docs/` 目录内 5. 返回详细的错误信息(但不泄露内部路径) ### 修改登录密码 1. 修改 `server.js:11` 中的 `PASSWORD` 常量 2. 或使用环境变量: `const PASSWORD = process.env.PASSWORD || 'cjy@0526';` 3. 考虑实施更安全的认证方案(如使用数据库存储哈希密码) ### 修改前端逻辑 1. 优先使用 `DOM` 缓存对象获取元素 2. 使用事件委托替代单独绑定事件 3. 对高频操作使用防抖/节流 4. 更新 DOM 时使用 `classList.toggle()` 简化条件判断 5. 添加移动端交互时,注意检查窗口宽度(`window.innerWidth`)以区分设备类型 6. 使用 `e.stopPropagation()` 防止事件冒泡,避免触发外部点击事件 ### 修改文档结构 1. 编辑项目根目录的 `index.md` 2. 在对应目录下创建/删除 `.md` 文件 3. 无需重启服务器,刷新页面即可(缓存5秒后生效) ### 修改/扩展编辑功能 1. 所有文档保存操作必须通过 PUT API,并经过安全验证 2. 编辑器光标定位依赖 `findVisibleElement()` 和 `positionCursorByElement()` 函数 3. 修改编辑行为时注意保持 `currentMarkdownContent` 和 `savedScrollPosition` 的同步 4. 编辑模式切换时需要正确管理 DOM 显示/隐藏状态 5. 不要允许编辑功能创建新文件,只能编辑已存在的文档 ## 安全注意事项 ### 1. 认证和会话安全 **Session 配置**: - HttpOnly Cookie - 防止 JavaScript 访问(XSS 防护) - SameSite: lax - 防止 CSRF 攻击 - 会话密钥应使用环境变量,不要硬编码 - 生产环境建议启用 HTTPS 并设置 `secure: true` **密码安全**: - 当前使用硬编码密码 `cjy@0526`,仅适用于开发/个人使用 - 生产环境建议: - 使用环境变量存储密码 - 实施密码哈希(如 bcrypt) - 考虑多用户支持和数据库存储 - 添加登录失败限制和验证码 ### 2. 路径遍历防护 **核心函数**: - `sanitizePath()` (`server.js:19-29`) - 移除 `../`、开头的 `/`、`\` 等危险字符 - `validatePath()` (`server.js:32-36`) - 确保解析后的路径在 `docs/` 目录内 - 所有文件操作 API 都必须使用这两个函数验证用户输入 **示例**: ```javascript 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 路由使用通配符 `*` 来支持子目录,如 `/api/doc/:category/*` - 即使支持子目录,`validatePath()` 依然确保路径不会越出 docs 目录 ## 响应式设计要点 ### 移动端适配 (≤768px) - 左右侧边栏默认折叠,通过底部悬浮按钮切换 - 侧边栏以 `fixed` 定位覆盖在内容区上方 - 使用 `transform: translateX()` 实现滑入滑出动画 - 回到顶部按钮位置调整为 `bottom: 100px`,避免与侧边栏按钮冲突 ### 平板适配 (768px-1024px) - 左侧边栏默认展开,右侧边栏默认折叠 - 右侧边栏以 `fixed` 定位,只在需要时显示 ### 桌面端 (>1024px) - 左右侧边栏始终展开 - 三栏布局:文档导航 + 内容区 + TOC 目录 ## 常见问题 ### 登录相关 #### 忘记密码 - 查看 `server.js:11` 中的 `PASSWORD` 常量 - 或启动服务器时查看控制台输出,会显示当前密码 #### 登录后仍然重定向到登录页 检查: 1. 浏览器是否启用了 Cookie 2. 浏览器控制台是否有 CORS 错误 3. Session 配置是否正确(特别是跨域场景) #### Session 频繁过期 - 检查 `server.js:48-61` 中的 session 配置 - `rolling: true` 应该会在每次请求时重置过期时间 - 确保浏览器没有频繁清除 Cookie ### 文档显示为空或 404 检查: 1. 是否已登录(未登录会重定向到登录页) 2. 项目根目录的 `index.md` 中是否有对应条目 3. 文件名是否与 `index.md` 中的名称完全一致(区分大小写) 4. 文件是否在正确的分类目录下 5. 如果是子目录文档,路径格式是否正确(使用正斜杠 `/`) ### 搜索功能不工作 检查: 1. 搜索关键词是否至少 2 个字符 2. 文档内容是否包含匹配文本 3. 浏览器控制台是否有 API 错误 ### 移动端侧边栏无法关闭 检查: 1. 点击事件是否正确绑定到 document 2. `e.stopPropagation()` 是否正确阻止了按钮点击事件冒泡 3. 窗口宽度判断逻辑是否正确(`window.innerWidth <= 1024`) ### 性能问题 检查: 1. 是否为每个元素单独绑定了事件(应使用事件委托) 2. 是否频繁查询 DOM(应使用 `DOM` 缓存对象) 3. 是否对搜索等高频操作使用了防抖 ### 编辑功能无法保存 检查: 1. 是否已登录(编辑功能需要认证) 2. 文件是否存在(编辑功能不能创建新文件) 3. 浏览器控制台是否有 PUT API 错误 4. 服务器是否返回 403(路径验证失败)或 400(数据格式错误) 5. 文件权限是否允许服务器写入 ### 图片无法显示 检查: 1. 图片文件是否在对应分类的 docs 目录下 2. Markdown 中的图片路径是否正确(相对路径) 3. 浏览器控制台中图片请求的 URL 是否正确 4. 是否已登录(图片资源需要认证) ## 技术栈版本 - Node.js: v14+ - Express: 4.x - Marked.js: 11.x - Highlight.js: 11.x - 前端:Vanilla JavaScript (ES6+),无框架依赖