文档写作规范(用本仓库模板)
来源:
halo-kb/知识库架构设计.md→## D) Markdown 文档模板(原文 7388 字符)
D) Markdown 文档模板
D.1 先确定「本实例到底支持什么写作格式」(已核实)
| 事实 | 证据 |
|---|---|
| Halo 原生文章的正文存储是 HTML | 实测本实例唯一文章 Hello Halo 的 rawType 为 HTML,raw 字段内容是 <h2>…</h2><p>…</p>(通过 Halo API 读取) |
| Halo 内置编辑器是富文本,可通过插件替换 | 官方文档 文章:「编辑器切换:如果安装了其他的编辑器插件,那么就可以在这个位置选择所需的编辑器」,并给出应用市场 ?tag=editor 入口 |
| MiniDocs 的文档存储是 Markdown | KnowledgeBaseDoc.spec.raw 存原文,spec.content 存编辑器渲染出的 HTML;插件自带 MarkdownEditor.vue |
结论与模板策略:
- Halo 文章 → 用 HTML 版模板(可直接粘贴进内置编辑器的「源码」视图;也可正常用富文本工具栏编辑)。
- MiniDocs 文档 → 用 Markdown 版模板(插件自带 Markdown 编辑器)。
- 仓库里另存的
.md文件(如 intranet-tunnel 的docs/)用 Markdown 版,与 MiniDocs 保持一致。
⚠️ 关于 front-matter:Halo 原生文章不使用 front-matter——标题、分类、标签、摘要、封面、可见性等全部在「文章设置」对话框里填写,正文只存内容本身(见官方文档「文章设置」小节)。front-matter 只在通过插件从外部文件导入时才有意义(例如社区插件 plugin-content-tools 的 Markdown 导入)。该插件在本实例未安装,其 front-matter 字段名我未核实 → 标为「未确认」。 本方案的替代做法:在正文开头放一个「元信息块」(普通表格/列表),承载"适用版本 / 最后验证时间 / 环境",它不依赖任何插件,在 Halo 与 MiniDocs 两边都能正常渲染。这比依赖未确认的 front-matter 更稳。 MiniDocs 侧也不需要 front-matter:
title/slug/summary/tags/parentName都在导入参数与实体字段里。
D.2 模板 ①-a:技术笔记 / 排错记录(HTML 版,用于 Halo 文章)
适用:"现象 → 根因 → 修复 → 判据"型文章,这是知识库中价值最高的一类。
<blockquote><p><strong>TL;DR</strong>:一句话说清根因与修法,让读者 5 秒判断是否与他相关。</p></blockquote>
<h2>元信息</h2>
<ul>
<li><strong>适用版本</strong>:intranet-tunnel v1.2.1 / Halo 2.26.1 / PostgreSQL 15.4</li>
<li><strong>最后验证时间</strong>:2026-09-13</li>
<li><strong>环境</strong>:Ubuntu 24.04.4 LTS,Docker 27.x,部署目录 <code>/home/docker/<项目名>/</code></li>
</ul>
<h2>现象</h2>
<p>用户视角看到了什么。要写<strong>可观察的事实</strong>(报错原文、界面表现、返回值),不要写推测。</p>
<pre><code class="language-text">把原始报错整段贴进来,不要截断、不要"美化"。</code></pre>
<h2>排查过程</h2>
<ol>
<li><strong>先确认 X</strong>:执行 <code>docker inspect <容器> --format '{{.State.ExitCode}}'</code>,结果 <code>0</code> → 排除崩溃。</li>
<li><strong>再确认 Y</strong>:……(写明"当时以为是什么、为什么排除")</li>
</ol>
<blockquote><p>⚠️ 踩坑提示:这里写"看似像 A 问题其实不是"的分叉点——这是本文最有价值的部分。</p></blockquote>
<h2>根因</h2>
<p>机制层面的解释:<strong>哪一行代码 / 哪一个配置项 / 哪一条链路</strong>导致了现象。附上关键代码或配置片段。</p>
<pre><code class="language-go">// 只贴关键几行,标注文件路径
// server/internal/proxy/tunnel_stats.go
func (c *Collector) AddIn(name string, n int64) { /* ... */ }</code></pre>
<h2>修复</h2>
<pre><code class="language-bash"># 可直接复制的命令,按顺序执行
docker compose build tunnel-server
docker compose up -d tunnel-server</code></pre>
<p>说明为什么这样改,以及<strong>为什么不是另一种改法</strong>。</p>
<h2>验证(判据)</h2>
<ul>
<li>✅ 正向断言:访问隧道域名 3 次,<code>bytes_in/bytes_out</code> 增量非 0。</li>
<li>✅ <strong>反向断言</strong>:未被访问的隧道统计<strong>仍为 0</strong>(只看总量涨了会把"统计挂错了隧道"放过去)。</li>
<li>✅ 操作本身的成功信号:接口返回 200,且读回值符合预期。</li>
</ul>
<h2>通用教训</h2>
<p>能迁移到其他场景的那一条原则(可被未来的自己在别的项目里复用)。</p>
<h2>相关</h2>
<ul>
<li><a href="/archives/xxx">相关文章</a></li>
<li><a href="/docs/view/tech-docs/tunnel-troubleshoot">文档库 · 隧道排错手册</a></li>
</ul>D.3 模板 ①-b:同一篇内容(Markdown 版,用于 MiniDocs / 仓库)
> **TL;DR**:一句话说清根因与修法。
## 元信息
| 项 | 值 |
|---|---|
| 适用版本 | intranet-tunnel v1.2.1 / Halo 2.26.1 |
| 最后验证时间 | 2026-09-13 |
| 环境 | Ubuntu 24.04.4 / Docker 27.x |
## 现象
可观察的事实 + 原始报错:
```text
Error: command timed out after 30 seconds排查过程
- 先确认进程状态:
pgrep -af acme.sh→ 进程仍在运行,排除"没启动"。 - 再确认超时层级:外层
sh被杀,子进程存活 → 超时来自 CLI 而非任务本身。
⚠️ 踩坑提示:把"看似像 A 其实不是 A"的分叉点写在这里。
根因
机制层面的解释(哪一行代码 / 哪一个配置项)。
修复
nohup ./long-task.sh > /tmp/task.log 2>&1 &验证
- ✅ 正向断言:……
- ✅ 反向断言:……
> **注意**:Markdown 代码块的围栏嵌套在本文档里展示时会冲突,实际写入时按正常 ```` ``` ```` 使用。
### D.4 模板 ②:项目文档 / README 风格(Markdown,用于 MiniDocs 与代码仓库)
````markdown
# intranet-tunnel 服务端部署手册
| 项 | 值 |
|---|---|
| 文档状态 | 现行有效 |
| 适用版本 | v1.2.1 及以上 |
| 最后验证 | 2026-09-13(真实环境验证通过) |
| 维护人 | sushike |
## 这是什么
一段话说清:这个项目解决什么问题、给谁用、不做什么。
## 架构
```text
浏览器 ──TLS──> 宝塔 nginx ──> 127.0.0.1:48080 内置反代 ──> 隧道 ──> 内网服务
│
└── 控制面 :47800 / 面板 :47801快速开始
mkdir -p /home/docker/intranet-tunnel && cd /home/docker/intranet-tunnel
cp .env.example .env # 再填入真实值,切勿提交
docker compose up -d tunnel-server postgres db-backup⚠️ 务必显式指定服务名:默认
up -d会连带启动tunnel-nginx,而该机的 80/443 由宝塔接管。
配置项
| 变量 | 必填 | 默认 | 说明 |
|---|---|---|---|
DB_PASSWORD | 是 | — | 数据库口令,不要写进版本库 |
TUNNEL_DOMAIN | 是 | — | 隧道基础域名,前缀写法由它补全 |
DDNS_IP_APIS | 否 | 内置列表 | 留空需整行注释,显式置空会导致启动失败 |
部署步骤
- 准备目录与权限
- 生成
.env - 启动并确认日志
- 接入 nginx 与证书
验证:docker inspect <容器> --format '{{.State.Status}}' 返回 running。
常见问题
| 症状 | 原因 | 处理 |
|---|---|---|
bind source path does not exist | 旧容器记录了旧目录 | docker rm -f 后重新 up |
| 面板显示裸前缀域名 | 展示路径未做域名补全 | 升级到 v1.0.3+ |
变更记录
| 日期 | 版本 | 变更 |
|---|---|---|
| 2026-09-13 | 1.2.1 | 修复登录后无反应 |
### D.5 模板 ③:教程 / 长文(HTML 版,用于 Halo 文章)
Ethereal 的文章页**自动生成目录**,条件是标题层级正确 + 主题设置开启(本实例 `post.toc.enable_toc = true`、`toc_depth = 2`,即**目录抓取到 H2 深度**)。因此长文请**统一用 H2 作为主章节**。
```html
<h2>这篇教程适合谁</h2>
<p>前置知识、需要准备的东西、预计耗时、最终能达成什么效果。</p>
<ul>
<li><strong>前置</strong>:一台能跑 Docker 的机器,一个已备案的域名。</li>
<li><strong>产出</strong>:可在公网访问的自建服务。</li>
</ul>
<h2>第一步:准备工作</h2>
<p>说清"为什么需要这一步",再给命令:</p>
<pre><code class="language-bash">ssh root@<服务器IP>
mkdir -p /home/docker/<项目名></code></pre>
<p><strong>验证</strong>:<code>ls -ld /home/docker/<项目名></code> 输出目录存在且属主正确。</p>
<h2>第二步:…(依次推进,每步都必须有验证判据)</h2>
<h2>原理补充(可跳过)</h2>
<blockquote><p>💡 这一段解释"为什么这样做有效",不影响操作,供后续排错时回看。</p></blockquote>
<h2>常见坑</h2>
<ol>
<li><strong>坑一</strong>:现象 → 原因 → 处理。</li>
<li><strong>坑二</strong>:……</li>
</ol>
<h2>小结</h2>
<p>三句话总结,并给出下一步可做的事(附内链)。</p>D.6 各要素的写法规范
自动目录导航
| 项 | 做法 |
|---|---|
| 触发条件 | 本实例 Ethereal 的「文章设置 → 目录」已开启(enable_toc = true),无需在正文里写目录 |
| 标题层级 | H2 为主章节(toc_depth = 2)。需要更细的层级时,到「主题设置 → 文章 → 目录 → 目录最大深度」调大 |
| 标题 ID | Halo 自动为标题生成 id(实测 Hello Halo 渲染出 <h2 id="hello-halo">),不要手写锚点,跨文档引用用完整 URL |
| 移动端 | 主题在无可见目录时显示右下角目录悬浮按钮(layout.floatingButtons.enable_toc = true) |
代码高亮
- HTML 版:
<pre><code class="language-go">…</code></pre>——语言 class 必须写对,高亮器靠它识别语言。 - Markdown 版:
`go围栏,信息串即语言名。 - 本实例相关事实:Ethereal 官方 README 把「Shiki 代码高亮」插件(应用市场
app-kzloktzn)列为推荐搭配插件,用于"在内容页高亮显示代码块";据既有调研,本实例前台已加载 shiki。因此只需保证语言 class 正确,不需要在正文里引入任何高亮脚本或额外 CSS。 - 行号、高亮特定行等增强能力取决于该插件的实际实现,未逐一核实 → 未确认;不要写进模板。
提示框 / 警告框
- HTML 版(推荐,确定可用):用引用块 + 符号前缀,主题的
prose排版会渲染为引用样式:
<blockquote><p>⚠️ 注意:覆盖正在运行的 exe 会失败,先确认程序没在跑。</p></blockquote>
<blockquote><p>💡 提示:本机未安装 make,直接用 git 命令。</p></blockquote>- Markdown 版(用于 MiniDocs):同样用
> ⚠️ …引用块——这是确定可行的写法。 :::info容器语法:MiniDocs 的 Markdown 渲染器是否支持该语法、Halo 文章渲染管线是否支持,均未确认(Halo 官方文档站自己使用该语法,但那不能证明站点的文章渲染也支持)。建议不要依赖它,需要强视觉提示时优先用引用块。
图片与附件引用
| 场景 | 做法 |
|---|---|
| 文章内插图 | 始终用编辑器的「插入图片」上传,不要手写 URL。Halo 会自动生成附件记录与访问地址 |
| MiniDocs 文档内插图 | 同上,用插件的图片上传能力;跨知识库引用同一张图时建议用 Halo 附件的稳定地址(具体地址形式未在本实例核实到——当前实例附件数为 0,无样本 → 未确认) |
| 封面图 | 在「文章设置 → 封面图」上传,不要写进正文。主题 post.contentDisplay.showCover = true 会在正文上方显示封面 |
| 附件文件(zip/tar 等) | 用编辑器插入为链接;不要用第三方网盘直链(会失效且不可控) |
| 图片优化 | 本实例「主题设置 → 速度优化 → 图片处理服务」当前为 none(不处理)。若日后接入 CDN/OSS,正文图片会按 article_image_width(默认 1200px)压缩——接入前先确认原图不被过度压缩 |
来源:
halo-kb/README.md→## 附录 A:本次产出物与验证方式(原文 2704 字符)
附录 A:本次产出物与验证方式
A.1 本次任务改动了什么
本任务是纯文档整合,对 Halo 实例与 NAS 零改动。
| 类别 | 内容 |
|---|---|
| 本次新建 | H:\Works\halo-kb\README.md(本文件,总纲交付文档) |
| 本次未改动 | Halo / PostgreSQL 容器、NAS /vol1/1000/docker/halo/ 下任何文件、Gitea 任何数据、D:\DSH Desktop\resources\ 下任何文件 |
| 本次只读取 | 同目录 10 份报告 + scripts/ 下全部脚本与日志 |
本文件与既有报告的关系:本文件是总纲与索引,把 10 份报告与脚本整合成一条可复现路径; 细节与原始证据仍在各专项报告中,两者冲突时以专项报告为准(本文件若与专项报告不一致,属本文件的转写错误)。
| 专项报告 | 本文件对应章节 |
|---|---|
实例现状基线.md | 第二章、第四章 |
插件选型调研.md | 第四章、第六章、第十三章 A/B 组 |
知识库架构设计.md | 第五章、第六章、第七章 |
分类标签落地记录.md | 第五章 |
minidocs-落地记录.md | 第五章 5.6、第十章 |
页面与展示配置记录.md | 第七章 |
AI能力验证.md | 第九章 |
gitea-集成方案.md + gitea-同步落地记录.md | 第八章 |
备份运维与安全.md | 第十一章、第十二章 |
A.2 如何验证本文档
# ① 文档结构完整(应输出 15 个章节标题 + 附录)
Select-String -Path H:\Works\halo-kb\README.md -Pattern '^## ' | Measure-Object
# ② 【硬约束】密钥形态扫描 —— 必须 0 命中
# 注意:Select-String -Path <目录>\* 会把子目录当成文件而报“访问被拒绝”,那是噪声不是命中;
# 要覆盖全部文件(含隐藏文件)请用下面这条 -File -Force 的写法:
Get-ChildItem -Path H:\Works\halo-kb -Recurse -File -Force |
Select-String -Pattern 'pat_[A-Za-z0-9_\-\.]{80,}|hmcp_[A-Za-z0-9_-]{60,}|sk-[A-Za-z0-9]{20,}' -AllMatches
# ③ 本文档不含真实凭据(应只在说明文字里出现占位符)
Select-String -Path H:\Works\halo-kb\README.md -Pattern '<TOKEN>|<REDACTED>'
# ④ 章节顺序检查(应依次出现 一 ~ 十五)
Select-String -Path H:\Works\halo-kb\README.md -Pattern '^## (一|二|三|四|五|六|七|八|九|十|十一|十二|十三|十四|十五)、'判据:
- ② 必须 0 命中;若有命中立即清除并复验;
- ④ 应输出 15 行,顺序为 一 二 三 四 五 六 七 八 九 十 十一 十二 十三 十四 十五。
A.3 复现整套知识库的验收测试(按章节顺序)
| 步骤 | 命令 / 动作 | 期望结果 | |
|---|---|---|---|
| 1 | halo plugin list(CLI) | 能列出插件 ⇒ CLI 认证与地址正确 | |
| 2 | python create_taxonomy.py --verify-only | 分类 29(8 一级 + 20 二级)/ 标签 45,73 条断言全通过 | |
| 3 | python create_taxonomy.py(重跑) | 新建 0、跳过 73、失败 0(幂等) | |
| 4 | .\minidocs_setup.ps1 -VerifyOnly | 知识库 1 个、文档 38 个、exit=0 | |
| 5 | python configure_site_display.py verify | /、/docs、/portfolio、/about、/skills、/timeline、/rss.xml 全部 [OK] 200,结论行「全部通过」 | |
| 6 | python gitea_to_halo.py --self-test | 「自测全部通过」(48 项) | |
| 7 | python verify-sync.py | 「回读校验全部通过」(44 项) | |
| 8 | `curl.exe -s --noproxy "*" http://<内网IP>:28090/portfolio \ | Select-String '共 . 个项目'` | 共 2 个项目 |
| 9 | ./scripts/halo-restore-verify.sh | 「✅ 恢复验证通过」,表数 34=34、extensions 391=391 | |
| 10 | ./scripts/halo-healthcheck.sh | 「✅ 全部通过」(10 项) | |
| 11 | curl.exe -s -o NUL -w "%{http_code}" --noproxy "*" http://<内网IP>:28090/apis/api.console.halo.run/v1alpha1/users | 必须 302(出现 200 = 严重问题) |
第 4、5、6、7、9、10 步都是幂等/只读的,可以随时重复执行用于回归。 唯一的写操作是第 3 步(且它对已存在的数据只跳过、不修改)。
文档结束。 本文档所有结论均可回溯到同目录的专项报告; 标注【未确认】的项请勿直接用于决策,标注【推断】的项请先验证再依赖。
