CLAUDE.md 16 KB

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

开发命令

本地开发

npm install        # 安装依赖
npm start          # 生产模式启动
npm run dev        # 开发模式启动(使用 nodemon 自动重启)

服务器默认运行在 http://localhost:3000

Docker 部署

# 构建镜像
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 定义,格式如下:

[分类名称]

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):

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&currentDoc=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):

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. 修改编辑行为时注意保持 currentMarkdownContentsavedScrollPosition 的同步
  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 都必须使用这两个函数验证用户输入

示例:

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+),无框架依赖