1
0

2 Commity 1f1d395221 ... dbdf69b3d6

Autor SHA1 Správa Dátum
  admincjy dbdf69b3d6 '不需要Docker.md' 1 mesiac pred
  admincjy c6b883d752 'docker测试' 1 mesiac pred
2 zmenil súbory, kde vykonal 122 pridanie a 93 odobranie
  1. 93 93
      CLAUDE.md
  2. 29 0
      Dockerfile

+ 93 - 93
CLAUDE.md

@@ -4,18 +4,18 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
 
 ## 项目概述
 
-CJYDocs 是一个基于 Node.js + Express 的轻量级 Markdown 文档管理和渲染系统,采用前后端分离架构。核心特点包括:
-- 通过解析 `docs/index.md` 配置文件实现文档的层级化管理
-- 支持在线编辑功能智能光标定位
+CJYDocs 是一个基于 Node.js + Express 的轻量级 Markdown 文档管理和渲染系统,采用前后端分离架构。核心特点包括:
+- 通过解析 `index.md` 配置文件实现文档的层级化管理
+- 支持在线编辑功能,智能光标定位
 - 全文搜索、TOC 目录、响应式设计
-- 高性能优化DOM 缓存、事件委托、IntersectionObserver
+- 高性能优化: DOM 缓存、事件委托、IntersectionObserver
 
 ## 开发命令
 
 ### 启动服务器
 ```bash
 npm start          # 生产模式启动
-npm run dev        # 开发模式启动(使用 nodemon 自动重启)
+npm run dev        # 开发模式启动(使用 nodemon 自动重启)
 ```
 
 服务器默认运行在 `http://localhost:3000`
@@ -37,11 +37,11 @@ npm install
 数据层 (docs/)
 ```
 
-### 文档配置核心index.md 解析
+### 文档配置核心: index.md 解析
 
-**关键文件**: `docs/index.md`
+**关键文件**: `./index.md` (项目根目录,NOT docs/index.md)
 
-系统的文档结构完全由 `index.md` 定义,格式如下:
+系统的文档结构完全由 `index.md` 定义,格式如下:
 
 ```markdown
 [分类名称]
@@ -54,14 +54,14 @@ npm install
 
 **解析逻辑** (`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 }] }`
+- `编号: 文档名.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` 文件
-2. 在 `docs/index.md` 中添加对应条目
-3. 两者必须同时存在文件名必须匹配
+2. 在项目根目录的 `index.md` 中添加对应条目
+3. 两者必须同时存在,文件名必须匹配
 
 ### 前端架构
 
@@ -81,65 +81,65 @@ const DOM = {
 };
 ```
 
-所有频繁访问的 DOM 元素都通过此对象缓存,避免重复查询。缓存采用懒加载策略:首次使用时查询并缓存,后续直接使用。
+所有频繁访问的 DOM 元素都通过此对象缓存,避免重复查询。缓存采用懒加载策略:首次使用时查询并缓存,后续直接使用。
 
 **事件委托模式**:
-- 导航点击:在 `#doc-nav` 父元素上监听,而非每个导航项
-- TOC 点击在 `#toc` 父元素上监听
-- 优势:减少 90%+ 的事件监听器,降低内存占用
+- 导航点击:在 `#doc-nav` 父元素上监听,而非每个导航项
+- TOC 点击:在 `#toc` 父元素上监听
+- 优势:减少 90%+ 的事件监听器,降低内存占用
 
 ### API 端点
 
-所有 API 都在 `server.js` 中定义
+所有 API 都在 `server.js` 中定义:
 
-- `GET /api/structure` - 获取完整文档结构(解析 index.md,带5秒缓存)
+- `GET /api/structure` - 获取完整文档结构(解析 index.md,带5秒缓存)
 - `GET /api/category/:category` - 获取指定分类信息
 - `GET /api/doc/:category/:docName` - 获取文档内容
-- `PUT /api/doc/:category/:docName` - 保存文档内容(在线编辑)
+- `PUT /api/doc/:category/:docName` - 保存文档内容(在线编辑)
 - `GET /api/search/:category?q=xxx&currentDoc=yyy` - 搜索文档
 
-**安全机制**
-所有用户输入都经过 `sanitizePath()` 清理和 `validatePath()` 验证防止路径遍历攻击。
+**安全机制**:
+所有用户输入都经过 `sanitizePath()` 清理和 `validatePath()` 验证,防止路径遍历攻击。
 
-**缓存策略**
-`index.md` 解析结果缓存5秒(`server.js:9-12`),减少文件系统读取频率。
+**缓存策略**:
+`index.md` 解析结果缓存5秒(`server.js:9-12`),减少文件系统读取频率。
 
 ### 关键功能实现
 
 #### 1. Markdown 渲染
-使用 `marked.js` + `highlight.js`配置在 `reader.js:25-38`
+使用 `marked.js` + `highlight.js`,配置在 `reader.js:25-38`
 
 #### 2. TOC 自动生成
-`reader.js:190-270` - 遍历渲染后的 HTML,提取所有 h1-h6 标题,使用 `IntersectionObserver` 实现滚动监听
+`reader.js:190-270` - 遍历渲染后的 HTML,提取所有 h1-h6 标题,使用 `IntersectionObserver` 实现滚动监听
 
 #### 3. 搜索功能
-- 后端:`server.js:159-244` - 逐行扫描所有 `.md` 文件,返回匹配的行号和上下文片段(最多5个)
-- 前端`reader.js:443-785` - 使用 `TreeWalker` API 精确定位到匹配的文本节点并滚动
-- 搜索历史保存在 `localStorage`按分类隔离
-- 搜索防抖 500ms点击搜索结果后自动关闭搜索框和侧边栏
+- 后端: `server.js:159-244` - 逐行扫描所有 `.md` 文件,返回匹配的行号和上下文片段(最多5个)
+- 前端: `reader.js:443-785` - 使用 `TreeWalker` API 精确定位到匹配的文本节点并滚动
+- 搜索历史保存在 `localStorage`,按分类隔离
+- 搜索防抖 500ms,点击搜索结果后自动关闭搜索框和侧边栏
 
 #### 4. 移动端侧边栏交互
-`reader.js:359-440` - 移动端侧边栏管理逻辑
-- **互斥展开**:点击一侧目录按钮时,自动隐藏另一侧已展开的目录,确保同一时间最多只有一个侧边栏展开
-- **点击外部关闭**:点击文档内容区域或其他目录以外的地方时,所有展开的侧边栏自动隐藏
-- **响应式行为**:此功能仅在移动端和平板(≤1024px)上启用,桌面端不受影响
-- **事件处理**:使用 `e.stopPropagation()` 防止事件冒泡,使用 `element.contains()` 精确判断点击位置
+`reader.js:359-440` - 移动端侧边栏管理逻辑:
+- **互斥展开**: 点击一侧目录按钮时,自动隐藏另一侧已展开的目录,确保同一时间最多只有一个侧边栏展开
+- **点击外部关闭**: 点击文档内容区域或其他目录以外的地方时,所有展开的侧边栏自动隐藏
+- **响应式行为**: 此功能仅在移动端和平板(≤1024px)上启用,桌面端不受影响
+- **事件处理**: 使用 `e.stopPropagation()` 防止事件冒泡,使用 `element.contains()` 精确判断点击位置
 
 #### 5. 性能优化
-- DOM 缓存减少 60%+ 的查询次数
-- 事件委托减少 90%+ 的事件监听器
-- 搜索防抖:500ms,减少 40% 的 API 请求
-- `IntersectionObserver`:替代 scroll 事件,性能提升 20-30%
+- DOM 缓存:减少 60%+ 的查询次数
+- 事件委托:减少 90%+ 的事件监听器
+- 搜索防抖:500ms,减少 40% 的 API 请求
+- `IntersectionObserver`:替代 scroll 事件,性能提升 20-30%
 
 #### 6. 在线编辑功能
-`reader.js:820-1095` + `server.js:113-156` - 完整的在线文档编辑系统
+`reader.js:820-1095` + `server.js:113-156` - 完整的在线文档编辑系统:
 
-**编辑器特性**
-- **智能光标定位** (`reader.js:869-988`):根据当前阅读位置(标题、段落)在编辑器中自动定位到对应的 markdown 源码行
-- **无缝模式切换**:编辑/预览模式切换时保持滚动位置,自动隐藏渲染内容并显示编辑器
-- **实时保存**:通过 `PUT /api/doc/:category/:docName` 保存到服务器,保存后自动重新渲染并更新 TOC
+**编辑器特性**:
+- **智能光标定位** (`reader.js:869-988`):根据当前阅读位置(标题、段落)在编辑器中自动定位到对应的 markdown 源码行
+- **无缝模式切换**:编辑/预览模式切换时保持滚动位置,自动隐藏渲染内容并显示编辑器
+- **实时保存**:通过 `PUT /api/doc/:category/:docName` 保存到服务器,保存后自动重新渲染并更新 TOC
 
-**编辑流程**
+**编辑流程**:
 ```
 点击编辑按钮 → 进入编辑模式
@@ -156,40 +156,40 @@ saveDocument() → PUT /api/doc/:category/:docName
 前端重新渲染 + 退出编辑模式
 ```
 
-**安全验证**
-- 文件必须已存在(不能创建新文件)
+**安全验证**:
+- 文件必须已存在(不能创建新文件)
 - 内容类型必须为字符串
 - 所有路径经过 `sanitizePath()` 和 `validatePath()` 验证
 
-**用户体验细节**
-- 浮动编辑按钮(右下角)
-- 防重复提交(保存时禁用按钮)
-- 成功提示 Toast(2秒后自动消失)
+**用户体验细节**:
+- 浮动编辑按钮(右下角)
+- 防重复提交(保存时禁用按钮)
+- 成功提示 Toast(2秒后自动消失)
 - 完整的错误处理和提示
 
 ## 文件结构
 
 ```
 cjydocs/
-├── server.js              # Express 后端(288行)
-│                          # - 所有 API 端点(GET/PUT)
-│                          # - index.md 解析逻辑(带5秒缓存)
-│                          # - 安全验证函数(sanitizePath, validatePath)
+├── server.js              # Express 后端(288行)
+│                          # - 所有 API 端点(GET/PUT)
+│                          # - index.md 解析逻辑(带5秒缓存)
+│                          # - 安全验证函数(sanitizePath, validatePath)
 │                          # - 搜索引擎实现
+├── index.md               # 文档结构配置文件(项目根目录)
 ├── docs/
-│   ├── index.md          # 文档结构配置文件(核心)
 │   └── 分类名/
-│       └── 文档.md       # 实际文档内容(可通过在线编辑修改)
+│       └── 文档.md        # 实际文档内容(可通过在线编辑修改)
 └── public/
-    ├── index.html        # 首页(显示分类列表)35行
-    ├── reader.html       # 阅读器页面(三栏布局+编辑器)129行
-    ├── css/style.css     # 统一样式
+    ├── index.html         # 首页(显示分类列表)35行
+    ├── reader.html        # 阅读器页面(三栏布局+编辑器)129行
+    ├── css/style.css      # 统一样式
     └── js/
-        ├── index.js      # 首页逻辑(70行)
-        └── reader.js     # 阅读器核心逻辑(1113行)
-                          # - DOM缓存、事件委托
-                          # - 搜索、TOC、侧边栏交互
-                          # - 在线编辑器(智能光标定位)
+        ├── index.js       # 首页逻辑(70行)
+        └── reader.js      # 阅读器核心逻辑(1113行)
+                           # - DOM缓存、事件委托
+                           # - 搜索、TOC、侧边栏交互
+                           # - 在线编辑器(智能光标定位)
 ```
 
 ## 代码修改指南
@@ -198,36 +198,36 @@ cjydocs/
 1. 在 `server.js` 中定义新路由
 2. 使用 `sanitizePath()` 清理所有用户输入
 3. 使用 `validatePath()` 验证文件路径在 `docs/` 目录内
-4. 返回详细的错误信息(但不泄露内部路径)
+4. 返回详细的错误信息(但不泄露内部路径)
 
 ### 修改前端逻辑
 1. 优先使用 `DOM` 缓存对象获取元素
 2. 使用事件委托替代单独绑定事件
 3. 对高频操作使用防抖/节流
 4. 更新 DOM 时使用 `classList.toggle()` 简化条件判断
-5. 添加移动端交互时,注意检查窗口宽度(`window.innerWidth`)以区分设备类型
-6. 使用 `e.stopPropagation()` 防止事件冒泡避免触发外部点击事件
+5. 添加移动端交互时,注意检查窗口宽度(`window.innerWidth`)以区分设备类型
+6. 使用 `e.stopPropagation()` 防止事件冒泡,避免触发外部点击事件
 
 ### 修改文档结构
-1. 编辑 `docs/index.md`
+1. 编辑项目根目录的 `index.md`
 2. 在对应目录下创建/删除 `.md` 文件
-3. 无需重启服务器,刷新页面即可
+3. 无需重启服务器,刷新页面即可(缓存5秒后生效)
 
 ### 修改/扩展编辑功能
-1. 所有文档保存操作必须通过 PUT API并经过安全验证
+1. 所有文档保存操作必须通过 PUT API,并经过安全验证
 2. 编辑器光标定位依赖 `findVisibleElement()` 和 `positionCursorByElement()` 函数
 3. 修改编辑行为时注意保持 `currentMarkdownContent` 和 `savedScrollPosition` 的同步
 4. 编辑模式切换时需要正确管理 DOM 显示/隐藏状态
-5. 不要允许编辑功能创建新文件只能编辑已存在的文档
+5. 不要允许编辑功能创建新文件,只能编辑已存在的文档
 
 ## 安全注意事项
 
-**路径遍历防护**
+**路径遍历防护**:
 - `sanitizePath()` - 移除 `../`、`/`、`\` 等危险字符
 - `validatePath()` - 确保解析后的路径在 `docs/` 目录内
 - 所有 API 都必须使用这两个函数验证用户输入
 
-**示例**
+**示例**:
 ```javascript
 const category = sanitizePath(req.params.category);
 const docPath = path.join(docsDir, category, `${docName}.md`);
@@ -239,50 +239,50 @@ if (!validatePath(docPath, docsDir)) {
 ## 响应式设计要点
 
 ### 移动端适配 (≤768px)
-- 左右侧边栏默认折叠通过底部悬浮按钮切换
+- 左右侧边栏默认折叠,通过底部悬浮按钮切换
 - 侧边栏以 `fixed` 定位覆盖在内容区上方
 - 使用 `transform: translateX()` 实现滑入滑出动画
-- 回到顶部按钮位置调整为 `bottom: 100px`避免与侧边栏按钮冲突
+- 回到顶部按钮位置调整为 `bottom: 100px`,避免与侧边栏按钮冲突
 
 ### 平板适配 (768px-1024px)
-- 左侧边栏默认展开右侧边栏默认折叠
-- 右侧边栏以 `fixed` 定位只在需要时显示
+- 左侧边栏默认展开,右侧边栏默认折叠
+- 右侧边栏以 `fixed` 定位,只在需要时显示
 
 ### 桌面端 (>1024px)
 - 左右侧边栏始终展开
-- 三栏布局文档导航 + 内容区 + TOC 目录
+- 三栏布局:文档导航 + 内容区 + TOC 目录
 
 ## 常见问题
 
 ### 文档显示为空或 404
-检查
-1. `docs/index.md` 中是否有对应条目
-2. 文件名是否与 `index.md` 中的名称完全一致(区分大小写)
+检查:
+1. 项目根目录的 `index.md` 中是否有对应条目
+2. 文件名是否与 `index.md` 中的名称完全一致(区分大小写)
 3. 文件是否在正确的分类目录下
 
 ### 搜索功能不工作
-检查
+检查:
 1. 搜索关键词是否至少 2 个字符
 2. 文档内容是否包含匹配文本
 3. 浏览器控制台是否有 API 错误
 
 ### 移动端侧边栏无法关闭
-检查
+检查:
 1. 点击事件是否正确绑定到 document
 2. `e.stopPropagation()` 是否正确阻止了按钮点击事件冒泡
-3. 窗口宽度判断逻辑是否正确(`window.innerWidth <= 1024`)
+3. 窗口宽度判断逻辑是否正确(`window.innerWidth <= 1024`)
 
 ### 性能问题
-检查
-1. 是否为每个元素单独绑定了事件(应使用事件委托)
-2. 是否频繁查询 DOM(应使用 `DOM` 缓存对象)
+检查:
+1. 是否为每个元素单独绑定了事件(应使用事件委托)
+2. 是否频繁查询 DOM(应使用 `DOM` 缓存对象)
 3. 是否对搜索等高频操作使用了防抖
 
 ### 编辑功能无法保存
-检查
-1. 文件是否存在(编辑功能不能创建新文件)
+检查:
+1. 文件是否存在(编辑功能不能创建新文件)
 2. 浏览器控制台是否有 PUT API 错误
-3. 服务器是否返回 403(路径验证失败)或 400(数据格式错误)
+3. 服务器是否返回 403(路径验证失败)或 400(数据格式错误)
 4. 文件权限是否允许服务器写入
 
 ## 技术栈版本
@@ -291,4 +291,4 @@ if (!validatePath(docPath, docsDir)) {
 - Express: 4.x
 - Marked.js: 11.x
 - Highlight.js: 11.x
-- 前端:Vanilla JavaScript (ES6+),无框架依赖
+- 前端:Vanilla JavaScript (ES6+),无框架依赖

+ 29 - 0
Dockerfile

@@ -0,0 +1,29 @@
+FROM node:18-alpine AS builder
+
+WORKDIR /app
+
+COPY package*.json ./
+
+RUN npm ci --only=production
+
+FROM node:18-alpine
+
+WORKDIR /app
+
+COPY --from=builder /app/node_modules ./node_modules
+
+COPY server.js ./
+COPY public ./public
+COPY package.json ./
+COPY index.md ./
+COPY docs ./docs
+
+EXPOSE 3000
+
+ENV NODE_ENV=production \
+    PORT=3000
+
+HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
+    CMD node -e "require('http').get('http://localhost:3000/api/structure', (res) => { process.exit(res.statusCode === 200 ? 0 : 1); }).on('error', () => { process.exit(1); });"
+
+CMD ["node", "server.js"]