<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>二次元宅男录</title><description>记录技术与生活</description><link>https://li8.me/</link><templateTheme>Firefly</templateTheme><templateThemeVersion>1.0.0</templateThemeVersion><templateThemeUrl>https://github.com/CuteLeaf/Firefly</templateThemeUrl><lastBuildDate>2026年10月31日 22:53:05</lastBuildDate><item><title>斗破127集夯爆了</title><link>https://li8.me/posts/10/</link><guid isPermaLink="true">https://li8.me/posts/10/</guid><pubDate>Sat, 31 Oct 2026 14:25:00 GMT</pubDate><content:encoded>&lt;p&gt;无论是感情戏还是打斗群像都是夯爆了。经费炸裂，斗破官方你的钱花不完了是吧？给我爽到了。NB这样就让我很期待双帝之战了。这集已经没有什么可挑的，爽爆了。&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;/images/u/20261031-224501-e8dae7.jpg.jpg&quot; alt=&quot;1275&quot; /&gt;&lt;figcaption&gt;1275&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;/images/u/20261031-224505-2ebc97.png&quot; alt=&quot;1268&quot; /&gt;&lt;figcaption&gt;1268&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;/images/u/20261031-224511-8d59e0.png&quot; alt=&quot;1267&quot; /&gt;&lt;figcaption&gt;1267&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;</content:encoded></item><item><title>文章目录滑不动？锅在 flex，账在我头上</title><link>https://li8.me/posts/9/</link><guid isPermaLink="true">https://li8.me/posts/9/</guid><description>从被 flex 默认值压扁的侧栏卡片，到抢方向盘的自动跟随，再到藏在每个读接口里的网络往返——一次目录修复加后台提速的联合记录</description><pubDate>Sun, 11 Oct 2026 12:30:00 GMT</pubDate><content:encoded>&lt;p&gt;这周又攒了两个问题一起修：一个是 PC 端文章页的目录滑不动，一个是后台的内容列表慢半拍。两件事八竿子打不着，但排查完发现它们的剧本出奇地像——表面症状和真正的病灶，隔了好几层。老规矩，把过程记下来，给下次的自己抄作业。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;一、目录滑不动：先给滚动条洗清冤屈&lt;a href=&quot;#一目录滑不动先给滚动条洗清冤屈&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;现象很朴素：文章长了以后，PC 端右侧的目录显示不全，底部直接被裁掉，滚轮放上去毫无反应。移动端没事，就 PC 有。&lt;/p&gt;&lt;p&gt;我的第一反应是滚动条样式的问题——毕竟这个站的滚动条定制过，透明度调得很低，第一眼确实看不见。改了半天透明度，滚动条是看得见了，但目录还是滚不动。看得见摸不着，那就不是样式的问题。&lt;/p&gt;&lt;p&gt;回头把侧栏的布局翻出来看，才找到真凶。侧栏是个纵向 flex 容器，外面套着 &lt;code&gt;max-height&lt;/code&gt; 和 &lt;code&gt;overflow-y: auto&lt;/code&gt;，意图很直白：最多这么高，装不下就内部滚动。但 flex 有个不太直觉的默认行为——&lt;strong&gt;子元素默认 &lt;code&gt;flex-shrink: 1&lt;/code&gt;，容器装不下的时候，所有子元素会按比例被压扁&lt;/strong&gt;。&lt;/p&gt;&lt;p&gt;也就是说：目录卡片、作者卡片这些加起来比视口高的时候，flex 先做的不是触发外层滚动，而是把每个卡片均匀压扁。目录内容被裁掉一截之后，卡片内部的高度刚好没超出——没超出，自然就没得滚，滚动条委屈地认为「我没有活干」。&lt;/p&gt;&lt;p&gt;修复反而简单得可笑：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;/* 子卡片禁止收缩，装不下就让侧栏整体滚 */&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#left-sidebar-sticky&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#right-sidebar-sticky&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;flex-shrink: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;一行 CSS，目录恢复滑动。写这段布局的时候，我大概默认了「装不下就会滚动」，但 flex 的收缩优先级排在溢出前面。这个默认值，坑过的大概不止我一个。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;二、修完压缩，又撞上「弹回去」&lt;a href=&quot;#二修完压缩又撞上弹回去&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;以为完事了，实测时冒出了更迷惑的现象：我把目录往下滚到想看的小节，松手不到一秒，它自己弹回去了。&lt;/p&gt;&lt;p&gt;这个就不是 CSS 了，是交互逻辑打架。目录有个「自动跟随」功能——页面滚到哪个小节，目录就把对应条目滚到可视区中间，之前写的，一直工作得挺好。问题在于它太尽职：我手动滚动目录这个动作，在它眼里和「用户翻了页面」没区别，下一帧就坚持把活动项拉回中间。我的手和它的逻辑，只能活一个。&lt;/p&gt;&lt;p&gt;解法是给「用户的手」加发言权：在目录上监听 &lt;code&gt;wheel&lt;/code&gt; 和 &lt;code&gt;touchmove&lt;/code&gt;，一旦检测到手动滚动就打个时间戳，四秒之内自动跟随闭嘴：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;userScrollingUntil&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;private&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;readonly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;markUserScrolling&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;userScrollingUntil&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;4000&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// scrollToActiveItem 开头：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;userScrollingUntil&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;四秒是试出来的。太短的话页面稍微一滚它又开始抢方向盘，太长的话点到新小节时目录跟不上趟。&lt;/p&gt;&lt;p&gt;顺手还对了另外两笔账：侧栏的 &lt;code&gt;top&lt;/code&gt; 和 &lt;code&gt;max-height&lt;/code&gt; 之前是各写各的，底部会超出视口几十像素，这次对齐了；滚动条透明度也从「隐身模式」调回到肉眼可见——一个用户看不见的滚动条等于没有滚动条，这是我第一节白忙活半小时换来的觉悟。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;三、后台慢半拍：接口在替 git 排队&lt;a href=&quot;#三后台慢半拍接口在替-git-排队&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;第二个问题是后台的内容列表越用越慢，刷新一次要等好几秒，网络不好的时候直接超时。&lt;/p&gt;&lt;p&gt;排查时先看接口本身——查询逻辑没什么毛病，本地数据读取快得很。但每个读接口在干活之前都先干了一件事：&lt;strong&gt;同步一次内容仓库&lt;/strong&gt;。当初的思路是「读之前先拉最新，保证内容新鲜」，听起来很负责，代价是每次请求都背一次网络往返。列表页一打开，好几个接口排着队跑网络。同步快的时候几百毫秒，慢的时候——就是你看到的转圈圈。&lt;/p&gt;&lt;p&gt;修复思路一句话：&lt;strong&gt;把慢的东西从关键路径上挪走&lt;/strong&gt;。仓库同步不再跟着请求走，改成后台线程自己定期做，同步加硬超时，防止哪次网络抽风把线程挂死在一个请求上：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;_repo_sync_loop&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;interval&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;45&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gitops.&lt;/span&gt;&lt;span&gt;pull&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;except&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Exception&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;pass&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# 这轮失败了下轮再来，别影响主流程&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;time.&lt;/span&gt;&lt;span&gt;sleep&lt;/span&gt;&lt;span&gt;(interval)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;同步周期 45 秒。对个人后台来说，「内容最多延迟一分钟」和「每次打开都等几秒」之间，根本不用犹豫。改完之后列表页的响应从秒级等待回到毫秒级，终于像个本地工具该有的样子。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;写在最后&lt;a href=&quot;#写在最后&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;两件事修完，发现教训居然是同一句话：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;症状出现在哪里，病灶不一定在哪里。看得见的问题（滚不动）可能藏在看不见的布局默认值里；等得起的耗时（内容同步）不该出现在等不起的地方（接口路径上）。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;再补一条给写 CSS 的自己：flex 的默认值不会「大概按我想的来」。&lt;code&gt;flex-shrink&lt;/code&gt; 这个属性我看了很多年，第一次真正理解它，是在自己博客的目录滚不动的那天。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>我给博客换了链接，然后它挂了一早上</title><link>https://li8.me/posts/8/</link><guid isPermaLink="true">https://li8.me/posts/8/</guid><description>从乱码查询参数到数字编号和拼音 slug——一次链接结构翻新的完整记录，以及凌晨自动更新把 nginx 弄宕机九小时的复盘与三层加固</description><pubDate>Thu, 01 Oct 2026 08:30:00 GMT</pubDate><content:encoded>&lt;p&gt;这周折腾了两件事，本来不想写成文章，但第二件事实在太有戏剧性——我把博客的链接全部翻新了一遍，第二天凌晨它就挂了九个小时。两件事还挺有因果关系的味道，一起记下来。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;一、链接翻新：从「能访问」到「有规律」&lt;a href=&quot;#一链接翻新从能访问到有规律&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;原来的链接有多随意&lt;a href=&quot;#原来的链接有多随意&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;先自曝家丑。这个站原来的链接长这样：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;文章：/posts/how-i-built-this-blog/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;标签：/archive/?tag=%E9%9A%8F%E7%AC%94&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;分类：/archive/?category=%E6%8A%80%E6%9C%AF&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;文章 slug 是标题转的英文，长短随缘；标签和分类点开更离谱——&lt;code&gt;%E9%9A%8F%E7%AC%94&lt;/code&gt; 这是「随笔」的 URL 编码，一串百分号乱码，别说访客看不懂，搜索引擎对这种查询参数式链接也不太待见：它本质上是归档页的过滤参数，不是独立页面，没有自己的权重。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;文章改数字编号&lt;a href=&quot;#文章改数字编号&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;第一个决定：文章 slug 全部改成纯数字，&lt;code&gt;/posts/1/&lt;/code&gt;、&lt;code&gt;/posts/2/&lt;/code&gt;，按发布顺序排。理由很简单——短、整齐、永远不重名。缺点是链接没了语义，但对个人博客来说，标题在列表和搜索结果里都有展示，URL 里那点语义没那么值钱。&lt;/p&gt;&lt;p&gt;改的时候给自己挖了个坑：旧链接全部 301 重定向到新地址，这个做了；但&lt;strong&gt;文章正文里的互相引用&lt;/strong&gt;忘了改——三篇技术文互相链接用的还是旧 slug，全站扫描死链的时候才暴露出来。顺手把关于页里一个指向不存在的 &lt;code&gt;/posts/&lt;/code&gt; 的入口也修成了归档页。&lt;/p&gt;&lt;p&gt;教训：改 URL 结构，扫描器跑一遍全站比肉眼靠谱得多。全站 1928 个站内链接，人工根本看不过来。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;分类标签建独立页面&lt;a href=&quot;#分类标签建独立页面&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;这是更大的一块。原主题根本没有分类详情页，所谓「点标签」就是跳归档页带个过滤参数。这次直接新建了两类静态页面：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;分类：/categories/ji-shu/    （技术）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;标签：/tags/sui-bi/          （随笔）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;slug 的规则：&lt;strong&gt;中文转全拼小写，英文原样，短横线分隔&lt;/strong&gt;。看名字就能拼出 URL，以后打新标签也自动按这个规则生成页面，规律是死的，不会因为手滑打出新格式。&lt;/p&gt;&lt;p&gt;实现上用的是 pinyin-pro（主题本来就带了）在构建期转换，页面静态生成、自动进 sitemap，每个分类和标签从此都有了自己可收录的落地页。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;顺手把图压了&lt;a href=&quot;#顺手把图压了&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;审计图片的时候发现相册里有四张原图是原样发布的：一张 1.5MB 的 PNG、两张快 1MB 的 JPG。全部转成 WebP（质量 85，肉眼无感），整个相册从 4.2MB 降到 1.76MB，减了 58%。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;二、宕机九小时：一次教科书级的连锁反应&lt;a href=&quot;#二宕机九小时一次教科书级的连锁反应&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;链接翻新的第二天凌晨 06&amp;lt;44&amp;gt;，博客挂了。而且不是慢，是完全打不开，一直挂到下午。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;时间线还原&lt;a href=&quot;#时间线还原&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;翻了系统日志，链条其实很短：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;06:44:05  系统自动安全更新（unattended-upgrade）启动，升级 openssl&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;06:44:18  openssl 是 nginx 的依赖库 → 触发 nginx 自动重启&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;06:44:18  重启瞬间 DNS 抖动：配置里的封面代理域名 t.alcy.cc 解析失败&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;06:44:18  nginx -t 报错：host not found in upstream → 启动失败&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;（然后呢？然后就没有然后了）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;两个问题叠加成了九小时的宕机：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第一，nginx 启动时要解析配置里所有的 upstream 域名。&lt;/strong&gt; 平时这不是问题，但「启动的那一刻 DNS 抖了一下」就够了——DNS 恢复后配置完全正常，可 nginx 已经躺在那里不会自己爬起来。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第二，nginx 的 systemd 重启策略是 &lt;code&gt;Restart=no&lt;/code&gt;。&lt;/strong&gt; Ubuntu 的默认值。失败不重试，一次失败就是永久失败。&lt;/p&gt;&lt;p&gt;最讽刺的是：我明明有每分钟跑的监控脚本、有每 5 分钟的部署兜底任务，但没有任何一个东西负责「发现 nginx 挂了就把它拉起来」。监控和数据都在，就差最后一步。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;三层修复&lt;a href=&quot;#三层修复&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;修的时候就没打算只修「这一次」：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第一层，根治启动依赖。&lt;/strong&gt; 封面代理的 upstream 改成「变量 + resolver」：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;resolver &lt;/span&gt;&lt;span&gt;223.5.5.5&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;119.29.29.29&lt;/span&gt;&lt;span&gt; valid=300s ipv6=off;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;set &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;alcy_upstream&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;t.alcy.cc&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;rewrite&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;^/cover-proxy/alcy/(.*)$&lt;/span&gt;&lt;span&gt; /$&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;break&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;proxy_pass &lt;/span&gt;&lt;span&gt;https://$&lt;/span&gt;&lt;span&gt;alcy_upstream&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;用变量做 upstream 时，域名解析推迟到运行时——启动时不再碰 DNS。以后 DNS 再抖，最坏也就是那一次封面图请求 502，nginx 本体稳如泰山。有个小坑：&lt;code&gt;proxy_pass&lt;/code&gt; 带变量时不会自动剥离 location 前缀，必须手动 &lt;code&gt;rewrite&lt;/code&gt; 补上，不然转发路径就是错的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第二层，自动拉起。&lt;/strong&gt; 给 nginx 的 systemd 加了 &lt;code&gt;Restart=on-failure&lt;/code&gt;，挂掉 5 秒后自动重试。就算再来一次同样的故障，DNS 恢复的几秒内它就自己站起来了。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第三层，看门狗。&lt;/strong&gt; 监控脚本旁边加了一个每分钟跑的看门狗：nginx 或后台服务不是 active 就直接拉起，写日志。它不聪明，但足够愣——专治各种「以为它会自己好」。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;零损失的安慰&lt;a href=&quot;#零损失的安慰&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;复盘时最庆幸的一点：宕机的九个小时里没有发布任何内容，最后一次部署停在宕机前一晚，所有数据完好。而且部署兜底任务走的是 cron 直连，根本不经过 nginx——就算那九小时里我发了文章，内容也会照常上线，只是没人看得到而已。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;写在最后&lt;a href=&quot;#写在最后&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这次折腾完，两件原本不相干的事最后落在了同一个认知上：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;「它能跑」和「它挂了我能立刻知道、立刻站起来」是两码事。前者叫功能，后者才叫运维。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;链接是给人和搜索引擎看的，规律比个性重要；服务是给凌晨四点的自动更新折腾的，兜底比侥幸重要。文章里如果有你也用得上的东西，欢迎翻翻这个系列的其他几篇——现在它们都在 &lt;code&gt;/posts/1/&lt;/code&gt; 到 &lt;code&gt;/posts/6/&lt;/code&gt; 里，链接干净得很。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>博客正式上线了</title><link>https://li8.me/posts/7/</link><guid isPermaLink="true">https://li8.me/posts/7/</guid><pubDate>Mon, 28 Sep 2026 04:30:00 GMT</pubDate><content:encoded>&lt;p&gt;🎉域名ICP备案总算是顺利通过啦!我的小站终于可以正式和大家见面咯～ 已经把备案等待期间写的所有文章都迁移过来啦，其实从提交备案申请那天开始，我就已经动笔攒内容了，熬到今天备案落地、完成全站迁移，说起来今天是正式开站的日子，但其实站子早在备案第一天就悄悄搭好骨架咯😂😂😂&lt;/p&gt;</content:encoded></item><item><title>这或许是最有性价比，最便捷的4k掌上副屏方案—索尼xzp</title><link>https://li8.me/posts/6/</link><guid isPermaLink="true">https://li8.me/posts/6/</guid><pubDate>Thu, 24 Sep 2026 16:14:00 GMT</pubDate><content:encoded>&lt;section&gt;&lt;h1&gt;效果图&lt;a href=&quot;#效果图&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;p&gt;国际惯例先来效果图&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;/images/u/20260925-002358-e93af0.jpg.jpg&quot; alt=&quot;xzp&quot; /&gt;&lt;figcaption&gt;xzp&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h1&gt;把我的4K老手机折腾成电脑副屏&lt;a href=&quot;#把我的4k老手机折腾成电脑副屏&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h1&gt;&lt;section&gt;&lt;h2&gt;折腾起因：吃灰多年的日版神机&lt;a href=&quot;#折腾起因吃灰多年的日版神机&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我的日版手机自从&lt;strong&gt;百来块&lt;/strong&gt;淘来之后，就一直在吃灰。4+64的存储配上骁龙835，&lt;strong&gt;根本带不动日常应用&lt;/strong&gt;，更带不动这块4K屏幕。而且因为日版&lt;strong&gt;不能解锁&lt;/strong&gt;，还无法全局开启4K。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;转折：论坛发现可解锁&lt;a href=&quot;#转折论坛发现可解锁&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;但是逛了逛论坛，发现&lt;strong&gt;居然可以解锁了&lt;/strong&gt;！解锁之后就刷EVO系统开全局4K。不过日版刷了会&lt;strong&gt;直接掉基带&lt;/strong&gt;，只能刷回原版，没办法的事。&lt;/p&gt;&lt;p&gt;这配置应该也没人当主力机用了，我干脆把包里的&lt;strong&gt;谷歌全家桶、打电话拨号&lt;/strong&gt;这些占用的东西全部删除，系统分区只剩&lt;strong&gt;1个多G&lt;/strong&gt;，但还是很卡。&lt;/p&gt;&lt;p&gt;我也尝试过第三方开源开发的应用，虽然有全局4K，但&lt;strong&gt;手机性能还是带不动&lt;/strong&gt;——看一会儿B站视频就卡、发热非常严重。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;灵机一动：让电脑性能来带它&lt;a href=&quot;#灵机一动让电脑性能来带它&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;既然手机性能带不动，那就&lt;strong&gt;拿电脑性能带它&lt;/strong&gt;，当电脑副屏！毕竟这货可是&lt;strong&gt;实打实的4K&lt;/strong&gt;呀。&lt;/p&gt;&lt;p&gt;我自己做了个模块配合 Spacedesk 使用，模块的作用大致是：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;走 USB 连接&lt;/li&gt;
&lt;li&gt;开机插线自动启动、自动连接显示&lt;/li&gt;
&lt;li&gt;保证连接使用过程中屏幕不息屏&lt;/li&gt;
&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;其实做模块的话，理论上可以实现不依靠第三方软件，直接当正常的显示屏连接。但是有点麻烦，所以&lt;strong&gt;怎么方便怎么来&lt;/strong&gt;。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;使用感受：很爽，但有两个缺陷&lt;a href=&quot;#使用感受很爽但有两个缺陷&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;电池容量太小&lt;/strong&gt;，只能插线（不过电池容量大的话也只能插线，详情看第2点）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;屏幕最高只有60Hz&lt;/strong&gt;，还不能跑满，平均只能跑到50多&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;那这有什么用？&lt;a href=&quot;#那这有什么用&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;肯定会有人问：&lt;strong&gt;花这么大劲折腾，到底有什么用？&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;我电脑是&lt;strong&gt;七彩虹橘宝 R16 Pro 2.5K屏&lt;/strong&gt;，正好缺个4K屏看动漫追剧。但是当初选笔记本就是图&lt;strong&gt;便捷携带&lt;/strong&gt;，不可能花大价钱去买一个4K显示屏——那样太不性价比，带起来也吃力。&lt;/p&gt;&lt;p&gt;不过&lt;strong&gt;索尼XZP&lt;/strong&gt;这个屏幕尺寸真的&lt;strong&gt;非常适合看日漫&lt;/strong&gt;，还是可以接受的。主要还是&lt;strong&gt;方便、便宜&lt;/strong&gt;。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;那发不发热呢？&lt;a href=&quot;#那发不发热呢&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;多多少少都会有点小热，可以接受。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h2&gt;使用模式&lt;a href=&quot;#使用模式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;使用场景&lt;/th&gt;&lt;th&gt;设备&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;游戏模式&lt;/td&gt;&lt;td&gt;自带屏&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;看番追剧&lt;/td&gt;&lt;td&gt;XZP&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;blockquote&gt;&lt;p&gt;温馨提示：本文为设备折腾经验的分享，刷机、解锁、ROOT 等操作可能带来数据丢失、设备变砖、保修失效等风险，请务必在充分了解并备份后再操作。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;/section&gt;</content:encoded></item><item><title>论现代的奇葩脑回路</title><link>https://li8.me/posts/5/</link><guid isPermaLink="true">https://li8.me/posts/5/</guid><pubDate>Thu, 24 Sep 2026 10:34:00 GMT</pubDate><content:encoded>&lt;p&gt;前几天和一位打游戏认识的高中年龄段女网友聊天，得知她和男朋友分手了——谈了七个月，散了。&lt;/p&gt;
&lt;p&gt;这本不算什么稀奇事，现在这个年代，分分合合跟换头像一样频繁。真正让我愣住的是后面那段对话。&lt;/p&gt;
&lt;p&gt;我问她怎么分的。她倒也不避讳，说早就在和自己的男闺蜜走得很近，聊天聊到半夜、周末单独出去吃饭看电影，男友介意，提了出来，吵了几次，最后就分了。我说，那你男闺蜜知道你们这个关系吗？她理直气壮：“我们只是好朋友啊，他是我男闺蜜，清清白白的。”&lt;/p&gt;
&lt;p&gt;我不知道现在的社会到底怎么了。我不知道我接下来的话算不算苛责，但我真的想问：这个世界上，真的有男人能坦然接受自己的另一半，和另一个男生保持这种”比朋友近一点、比恋人远一点”的关系吗？&lt;/p&gt;
&lt;p&gt;她整个人浑身上下都写着”我没做错什么”。我不明白我这一句话是不是不该说，但她反过来给我讲起了道理：时代变了，男女之间有纯友谊，是我思想老古董，是把人想得太龌龊。&lt;/p&gt;
&lt;p&gt;那一刻我忽然意识到，我们俩讲的可能根本不是一回事。她讲的是”我又没出轨”，我讲的是”边界感”。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;触网太早，心智没跟上&lt;a href=&quot;#触网太早心智没跟上&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;放在几年前，我大概只会把这事归结为”人各有志”。但她是高中年龄段的人，这让我多想了一层：现在的未成年人，触网实在太早了。&lt;/p&gt;&lt;p&gt;我们这一代人，小时候顶多玩玩单机游戏，情感观是摔了跟头才慢慢长出来的。而现在的小孩，小学就有了自己的手机，短视频、社交平台无缝切换，成年世界的情感模式——暧昧、养鱼、快餐式恋爱——隔着屏幕直接灌进还没长好的心智里。&lt;/p&gt;&lt;p&gt;社会发展太快，环境一天一个样，但人的心智是要靠时间和经历一寸一寸长出来的。接受的信息量是成年的，处理信息的心智是未成年的，中间这道落差，就是各种”奇葩脑回路”的生长土壤。&lt;/p&gt;&lt;p&gt;她们不是不聪明，是太早学会了太多概念，却没机会真正理解那些概念背后的分量。“边界感&quot;&quot;情绪价值&quot;&quot;独立自我”，张口就来，可这些东西要踩过坑才懂。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;不是奔着爱去的，是为新鲜感买单&lt;a href=&quot;#不是奔着爱去的是为新鲜感买单&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;再回到她这件事本身。琢磨了几天，我的评价其实就一句话：她这段感情，从头到尾都不是奔着爱去的，而是在为自己的新鲜感买单。&lt;/p&gt;&lt;p&gt;恋爱七个月，男闺蜜全程在线；男友介意了，先沟通的明明是男友，她却把自己摘得干干净净。在她的逻辑里，恋爱是体验项目，男朋友是体验的一部分，男闺蜜也是体验的一部分，谁让她觉得新鲜、谁让她情绪舒服，她就往谁那边靠。&lt;/p&gt;&lt;p&gt;至于爱？爱是要负责任的，是要为对方收敛自由的，是要在”新鲜感消退”之后依然选择留下来的。而这些恰恰是网络短视频从来不教的部分——短视频只教”心动”，不教”守约”；只教”做自己”，不教”顾别人”。&lt;/p&gt;&lt;p&gt;新鲜感驱动的感情有个共同结局：热度一过，就要找下一个刺激源。这次是男闺蜜，下次还可以是别人。男朋友换了一个又一个，其实换的从来不是人，是那口新鲜气。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;边界感不是清白，是分寸&lt;a href=&quot;#边界感不是清白是分寸&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;她一直在强调”清清白白”——没牵手、没接吻、没越界，所以没问题。但感情里的边界感，从来不是用法律标准来衡量的。&lt;/p&gt;&lt;p&gt;深夜的专属聊天、只对彼此说的悄悄话、电视剧里男女主角才有的相处模式，这些东西一样都没”越界”，但每一样都在把本该属于伴侣的情绪价值，外包给另一个人。恋爱里的安全感是有限的，你往外面漏一点，留给自己伴侣的就少一点。&lt;/p&gt;&lt;p&gt;说白了：出轨是结果，越界是过程。等到有”结果”的时候，早就不知道越了多少次界了。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;讲道理讲不通的根源&lt;a href=&quot;#讲道理讲不通的根源&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;最让我无语的其实不是她的行为，而是那个逻辑闭环：&lt;/p&gt;&lt;p&gt;你提出疑问 → 她说你不信任她 → 你解释你介意的是相处模式 → 她说那是你的问题，她问心无愧 → 最后你反而成了那个”思想有问题”的人。&lt;/p&gt;&lt;p&gt;绕了一圈，话题从”这事合不合适”变成了”你心态好不好”。只要她一口咬定自己清白，任何讨论都无法进行——因为她根本不觉得这件事有讨论的必要。&lt;/p&gt;&lt;p&gt;后来我想通了：讲道理的前提，是双方承认同一套标准。她那套标准里，“没实质越界”就是满分答案；而我这套标准里，“让伴侣感到不安还毫不在意”本身就是问题。标准都不同，道理是讲不通的。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;写在最后&lt;a href=&quot;#写在最后&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我不太担心她一个人，我担心的是这种状态不是个例。触网太早的一代正在批量长大，网络教给他们一整套成熟的恋爱话术，却没人在意他们有没有成熟的心智去撑起这些话术。&lt;/p&gt;&lt;p&gt;如果你也遇到了这样的人，别急着讲道理，先问自己一个问题：你要的是这个人，还是这段关系里随时会漏风的安全感？&lt;/p&gt;&lt;p&gt;想清楚这个，答案其实就不难选了。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>《败犬女主》第 8、8.5、9 卷品鉴：全员神人，只有温八正常</title><link>https://li8.me/posts/4/</link><guid isPermaLink="true">https://li8.me/posts/4/</guid><description>台版实体书到手，又连夜补完网络汉化，把 8、8.5、9 三卷连起来品了一遍。老马的告白炸场、8.5 的全员悬置、第九卷的神人狂欢——三卷连读才是完整的过山车。</description><pubDate>Tue, 22 Sep 2026 12:10:00 GMT</pubDate><content:encoded>&lt;p&gt;这三卷我是连着读完的：台版实体书到手，先捧着把第八卷翻完，又连夜补完 8.5 和第九卷的汉化。&lt;/p&gt;
&lt;p&gt;合上书的感受一句话就能说完：&lt;strong&gt;这三卷里，除了温八，没一个正常的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;第八卷，老马把话挑明；8.5 卷，全员假装无事发生；第九卷，樱井三人组把”神人”两个字焊死在书页上。而温水和八奈见，从头到尾都像误闯片场的一对正常少男少女——正常吃饭，正常拌嘴，正常地谁也不敢把那层纸捅破。&lt;/p&gt;
&lt;p&gt;本篇全程剧透，三卷一起聊。没读到的朋友先去补课，回来我们接着品。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;第八卷：老马的告白，全书最正面的一击&lt;a href=&quot;#第八卷老马的告白全书最正面的一击&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;第八卷主线是生徒会选举，马剃天爱星的政治题材。说实话选举本身我看得中规中矩，直到最后一击。&lt;/p&gt;&lt;p&gt;卷末，天爱星把话挑明了。我是捧着台版实体书读到这一段的——第 290 页，插画里老马双手交握在胸前，脸红得不行：&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;figure&gt;&lt;img src=&quot;/assets/images/laoma.webp&quot; alt=&quot;台版《敗北女角太多了！》第八卷第 290 页：老马告白的名场面&quot; /&gt;&lt;figcaption&gt;台版《敗北女角太多了！》第八卷第 290 页：老马告白的名场面&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;然后是这两句：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;“温水同学，我喜欢你。”&lt;/p&gt;&lt;p&gt;“当然我指的是恋爱的喜欢。你愿意和我交往吗？”&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;不绕弯子，不打哑谜，是全系列到目前为止最正面、最不留退路的一次进攻。温水的内心是：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;“我——被告白了。”&lt;/p&gt;&lt;p&gt;“只会在小说中出现过的事件，我现在成为当事人，在我眼前发生。”&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;注意，他没有拒绝。到第八卷收尾为止，这个答案就那么空着——这一空，就空出了整个 8.5 卷。&lt;/p&gt;&lt;p&gt;老马这一击的含金量，要放在”败犬”的坐标系里看：这个系列里其他女生表达喜欢的方式，是自动翻译、是天台让路、是”我不碍事”；只有天爱星，是把话放到台面上等你接。僵尸系学姐，全书唯一的直球选手。就冲这一段，第八卷封神不亏。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;8.5 卷：全员悬置，温水的不体面现场&lt;a href=&quot;#85-卷全员悬置温水的不体面现场&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;8.5 卷是短篇集，讲告白之后的日常——或者说，讲所有人怎么假装那场告白没发生过。&lt;/p&gt;&lt;p&gt;最损的一场戏：老八主动开口试探温水——“温水君——怎么看马剃同学？“温水打个哈哈不接。然后八奈见顺着台阶自己翻译：”……如果那么想的话，我们也不能妨碍你啊。”&lt;/p&gt;&lt;p&gt;翻译翻译：你敢喜欢她，我就退出。&lt;/p&gt;&lt;p&gt;看，这就是败犬。试探完了，先替你把退路铺好，顺便把自己的心再埋深一寸。而温水两头都不敢接：不敢对老马的告白给答案，也不敢接老八递过来的那个”我不碍事”。他心里的自动翻译倒是转得飞快，把每一句危险发言都翻译成安全的日常对话，翻完糊弄过去，装作无事发生。&lt;/p&gt;&lt;p&gt;短篇集的好处在这里体现得淋漓尽致：告白之后的每一段日常，读者都知道底下压着什么，但角色们个个若无其事。这种全桌人都在装傻的悬置感，比任何误会冲突都折磨人——折磨的同时，糖也是真没停过。温水和老马的关系在微妙地升温，温水和老八的相处也在微妙地变味，所有人都在”少しずつ”（一点点地）动。&lt;/p&gt;&lt;p&gt;而全卷糖分最高的一场戏，我必须单拎出来讲：8.5 卷加笔短篇《把秘密塞进口袋》，老八和烧盐在社办边啃鱿鱼干边复习的日常回。八奈见一边嘎吱嘎吱嚼着鱿鱼干，一边状似无意地开口试探：”……那个呀，那天晚上你和温水君一起出去了对吧？“——名古屋那一晚，她记到现在。结果试探没进行三句，烧盐头都没抬，反手就是一枪：“测验结束后不是有花火大会吗……我要不要约阿温呢？”&lt;/p&gt;&lt;p&gt;老八当场僵住，连”咦”都是破音的。烧盐还嫌不够，收尾补刀：“事后才慌张的话，我可不管哦。“我读到这段直接拍腿：平时看烧盐是文艺部吉祥物，真坐上感情牌桌，她才是全场牌最顺的人——句句装傻，句句带刺，把老八那点”若无其事式试探”的小心思一次性击穿。老八最后只能借口买奶茶落荒而逃。&lt;/p&gt;&lt;p&gt;但这篇真正的杀招在结尾。温水把老八叫住，支支吾吾、眼神乱飘，手还下意识往她手边碰——八奈见内心都开始 “某种事件正在发生”了，紧张到整理头发。结果递过来的，是一块虎鲸形状的名古屋特产最中饼：“八奈见同学从名古屋回来时想买，但是没钱买不了吧？我买来当给家里人的土产，所以想分你一个。”&lt;/p&gt;&lt;p&gt;她以为要被告白，结果是点心。气得当场炸毛：“你的问题就出在这里！温水君！”&lt;/p&gt;&lt;p&gt;可下一秒，她把最中饼塞进口袋，丢下一句”不是说要对大家保密吗”，脚步轻盈地走了。标题的”秘密”就藏在这——品品这个细节：温水的温柔从来不走嘴，他记得的是老八在名古屋盯着最中饼却没钱买的那一眼。对一个从小让球让到大、连贪吃都要顾着面子的姑娘来说，有人默默接住她这点小心思，比一百句情话都致命。而且你把 8.5 的这块最中饼和 9 卷旅馆那句”可是我的啊”连起来看，根本是同一套连招：先让她尝到糖，再让她喊出心声。雨森老贼，你小子是懂饲养败犬的。&lt;/p&gt;&lt;p&gt;然后第九卷告诉你：装傻是要还的。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;第九卷·温八篇：全员神人中唯一的正常糖&lt;a href=&quot;#第九卷温八篇全员神人中唯一的正常糖&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;第九卷主线看起来很扯：樱井的女友小春想跟樱井”制造既成事实”，拉五个人搞飞驒高山旅行，计划让小两口错过末班车共处一室。结果樱井看穿一切淡定上车，真正错过末班车的是温水和八奈见——房订好了，就剩他俩。&lt;/p&gt;&lt;p&gt;同宿这一夜，雨森写得非常干净。没有越界，没有俗套的意外碰撞，就是两个高中生在同一家旅馆的同一间房里，尴尬、拌嘴、谁都不肯先睡、又谁都不舍得先睡。第二天早上那副鸡飞狗跳的样子——老八的表情管理当场碎掉，温水假装镇定其实耳朵通红——是我这半年来在轻小说里看到过的最好笑也最好嗑的一章。&lt;/p&gt;&lt;p&gt;然后是名场面。小春家出跑到温水家蹭住，当面质问八奈见：“我确认一下，八奈见同学没有和温水君交往吧？那么，我和温水君进旅馆也没问题吧？”&lt;/p&gt;&lt;p&gt;八大王当场破防：&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;“温水君可是我的啊？！”&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;喊完她自己僵在原地，双手乱摇：“不对不对不对，不是那个意思，你明白我的意思吧？”&lt;/p&gt;&lt;p&gt;分析一下这四秒钟。九卷以来，老八的人设是什么？是每一次心动都能自动翻译成”朋友”的嘴硬王者，是把”我们只是青梅竹马”当护身符的标准败犬。而这一句失态，是九年伪装的第一次系统报错——报错之后秒怂，怂完之后躲着温水好几天不敢见面。&lt;/p&gt;&lt;p&gt;这一连串反应比任何直球告白都真实。真心的流露从来不是设计好的告白，是防不住的那一句。而且你注意，她躲的不是丢脸，是”被他知道”——躲了好几天之后两个人又若无其事地聊回去了，谁都没提那一晚。这种默契的不提，比提了更甜。&lt;/p&gt;&lt;p&gt;还有一场隐藏高光：有人问温水”如果有人叫你不要见我，你会怎么做”，他的回答绕开了所有人——连亲妹妹都可以摆到局外人位置去。这个嘴巴很严的男人，宁可把妹妹划出去，也没把八奈见划出去。答案早就在选择题里了，只是他不承认。&lt;/p&gt;&lt;p&gt;对照着看更有意思：老马在第八卷选择了最直球的告白，老八在第九卷只有靠破防才漏出半句真心。两个路线，两种败法。所以我说温八是这三卷里唯一的正常：正常的吃醋，正常的别扭，正常的装傻，正常的谁也不捅破——放在这群神人中间，这份正常甜得发光。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;第九卷·神人组：樱井、小春、放虎原&lt;a href=&quot;#第九卷神人组樱井小春放虎原&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;现在讲让我血压拉满的主线——这三个人，一个比一个神人。&lt;/p&gt;&lt;p&gt;先看他们的操作有多离谱。小春，樱井的女友，想跟男朋友更进一步，手段是委托男朋友的哥们儿帮忙设局制造既成事实；设局失败后直接家出，搬进这个哥们儿家里住了五天，用意是刺激男朋友。这五天里她还顺手把”我和温水进旅馆也没问题吧”甩在老八脸上。见了两面加一天旅行就敢上门住，“朋友妻”这个身份也太好用了——我第一次读到这儿，合上手机缓了半天。&lt;/p&gt;&lt;p&gt;但小春的离谱，是有账本的。回忆杀揭开的那一刻，我血压直接转向心酸——&lt;/p&gt;&lt;p&gt;初一那年的秋天，放虎原家里出事，父亲把哥哥逐出家门，撂下”别再跨进这个家门”。她一个人躲进田边小屋，在雨里哭。冒雨追来的是小学六年级的樱井，把自己的衣服脱下来借给淋透的她。然后——放虎原从背后环住他的手臂，樱井把手覆了上去。那一晚，两个人待到了天亮。&lt;/p&gt;&lt;p&gt;对，你们没看错，一个初一，一个小学六年级。这配置放在任何一部作品里都是重量级。&lt;/p&gt;&lt;p&gt;要我复述那一晚具体干了什么？我都不想说。主要是雨森也没让我有说的余地——写到”把手覆上去”就断章了！就这四个字，信息量直接拉满，剩下的全靠读者自己脑补。我盯着这段看了得有一分钟，脑子里只有一个声音：写清楚啊！……不对，你别写了，你还是留白吧，写清楚了我以后没法直视樱井了。&lt;/p&gt;&lt;p&gt;更绝的还在后面。两家父亲的反应速度，比这两人的感情进展快多了——第二天就为遗产撕破脸，一个喊”云雀诱拐了小学生弘人”，一个喊”我女儿被玷污了”。好家伙，恋爱还没个说法呢，大人已经把它换算成官司的筹码了。没有一个人问那两个孩子昨晚经历了什么，一个都没有。&lt;/p&gt;&lt;p&gt;在孩子们抱团取暖的地方，大人们只看得见把柄。吐槽归吐槽，这一段是真的狠——我见过很多写”大人丑恶”的，写这么直白的，这是头一份。&lt;/p&gt;&lt;p&gt;替孩子们收场的，又是另一个孩子。小春冲进骂战，一句”当时我也在场&quot;&quot;我和弘人在交往”，把”诱拐”和”玷污”两个帽子同时摘了。她用一句编造的恋爱，给放虎原和樱井挡了刀——然后把自己钉死在这个谎上八年。&lt;/p&gt;&lt;p&gt;八年。她跟樱井的交往始于救场，不是始于喜欢。所以她才执着于”既成事实”，执着于”更进一步”——她要的不是那种事，是一个八年都没等到的确认：弘人心里到底有没有我。&lt;/p&gt;&lt;p&gt;樱井的回答？“等云雀姐毕业。”&lt;/p&gt;&lt;p&gt;让女朋友等另一个女人毕业。看到这儿我人都麻了——你逃避就逃避吧，你别两头抓啊。烟花大会上小春一头槌撞散了”再给我一次机会”，喊出分手，那一下我居然松了口气：八年了，这个女孩终于替自己喊了一次。&lt;/p&gt;&lt;p&gt;顺带一提，支线也神人浓度超标：姬宫听说温八同宿，专程跑来给温水”传授经验”，确认啥也没发生才闭嘴。这本书就没有正常人在谈恋爱。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;三卷连评：一场安排好的过山车&lt;a href=&quot;#三卷连评一场安排好的过山车&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;单独看，第八卷是政治+告白，8.5 是发糖短篇，第九卷是神人修罗场。但连起来看，你才发现这三卷是一个完整的结构：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;第八卷把雷埋下（老马告白），8.5 卷把雷按住（全员悬置），第九卷在雷上跳舞（神人狂欢+温八突破）。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;雨森的算计在于：老马的直球把温水的”迟钝”逼到失效，8.5 的悬置把温水”装糊涂”的成本越垒越高，到第九卷，樱井三人组直接给他看了一场”装糊涂二十年”的现场版——放虎原就是一个把喜欢烂在心里、计划毕业就离开丰桥的青梅竹马，这个配置跟老八像得我后背发凉。温水看着樱井线的结局，等于看着自己和小八的”如果放任不管”版本。&lt;/p&gt;&lt;p&gt;所以樱井线根本不是水剧情：那是杀鸡给猴看，鸡是樱井，猴是温水和屏幕前的我们。&lt;/p&gt;&lt;p&gt;打分的话：第八卷五星（告白一击封神），8.5 卷四星（糖管够但体量小），第九卷四星半（樱井线收得偏急，扣半星）。三卷连读的体验，五星。&lt;/p&gt;&lt;p&gt;最终评价还是那句话：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;这三卷，除了温八能让我感到正常，其他人真的都太神人了。&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;老马的神人在于敢把所有败犬不敢说的话一口气说完；小春的神人、樱井的神人、放虎原的神人，在于把八年的错位谈成了一场谁也退不出困局；连姬宫都在支线里神人。全员疯批狂欢里，只有温泉旅馆那一夜的两个人，在认真地尴尬、认真地别扭、认真地不敢说。&lt;/p&gt;&lt;p&gt;第十卷，老马的答案不能再拖了，佳树要登场了，温八只差一个命名。台版实体书我已经开始收了——这三卷，值得摆在书架上。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>零依赖服务器监控面板：不用现成工具，自己写一个</title><link>https://li8.me/posts/3/</link><guid isPermaLink="true">https://li8.me/posts/3/</guid><description>不装任何监控软件，用一段 bash 脚本 + Nginx + 博客页面，给小内存服务器做一个零依赖、可配置、防扫描的服务器状态面板</description><pubDate>Mon, 21 Sep 2026 09:20:24 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;/posts/1/&quot;&gt;《从零搭建个人博客》&lt;/a&gt;里提过，我的服务器是 2 核 2G 的小机型。想在博客里放一个服务器状态面板，现成方案不少——beszel、netdata、dashdot 都很漂亮——但动辄吃掉几百 MB 内存，还得单独开端口、配登录。2G 内存的机器经不起这么造。&lt;/p&gt;
&lt;p&gt;最后的方案：&lt;strong&gt;一段 bash 脚本 + 一段 Nginx 配置 + 一个博客页面&lt;/strong&gt;，零依赖、零端口、零额外内存占用，效果见本站的「状态」页。这篇把完整实现过程和踩过的坑记录下来。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;一、整体架构&lt;a href=&quot;#一整体架构&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;先想清楚数据从哪来、到哪去：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;┌─ 服务器 ────────────────────────────┐&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│  monitor.sh（cron 每 30 秒）          │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│    ↓ 读 /proc 内核数据，算出指标       │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│    ↓ 写成 JSON 文件（原子替换）        │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│  Nginx 把这个 JSON 以固定路径暴露      │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└──────────────┬──────────────────────┘&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;               &lt;/span&gt;&lt;/span&gt;&lt;span&gt;↓ HTTP（浏览器 fetch）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;┌─ 博客页面 ──────────────────────────┐&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│  JS 每 30 秒拉取 JSON → 渲染卡片       │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└────────────────────────────────────┘&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;关键认识：&lt;strong&gt;数据采集根本不需要 SSH，也不需要在浏览器端连服务器&lt;/strong&gt;。脚本跑在服务器本地，读 &lt;code&gt;/proc&lt;/code&gt; 虚拟文件——那是内核自己维护的运行时账本，数据最权威。浏览器端只是一个普通 HTTP 请求拉 JSON。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;二、第一步：采样脚本&lt;a href=&quot;#二第一步采样脚本&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;完整脚本如下，可以直接抄（保存为 &lt;code&gt;/opt/server-mon/monitor.sh&lt;/code&gt;，记得 &lt;code&gt;chmod +x&lt;/code&gt;）：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#!/usr/bin/env bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 读取 /proc 生成服务器状态 JSON，由 cron 定时执行&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-u&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;OUT&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;/var/www/mon/status.json&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;STATE&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;/var/lib/server-mon/prev.txt&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 自动识别默认网卡&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;IFACE&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;ip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;route&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;show&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt; 2&amp;gt;&lt;/span&gt;&lt;span&gt;/dev/null&lt;/span&gt;&lt;span&gt; | &lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;{print $5; exit}&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;[ &lt;/span&gt;&lt;span&gt;-z&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;${&lt;/span&gt;&lt;span&gt;IFACE&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:-&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ] &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;IFACE&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;eth0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# --- CPU 计数器 ---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;read&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nice&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;system&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;idle&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;iowait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;irq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;softirq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;steal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;_&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/proc/stat&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;CPU_TOTAL&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$((&lt;/span&gt;&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nice&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;system&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;iowait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;irq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;softirq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;steal&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;idle&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;CPU_BUSY&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$((&lt;/span&gt;&lt;/span&gt;&lt;span&gt;user&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nice&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;system&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;iowait&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;irq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;softirq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;steal&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# --- 内存 ---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;memtotal&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;/^MemTotal:/{print $2}&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/proc/meminfo&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;memavail&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;/^MemAvailable:/{print $2}&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/proc/meminfo&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# --- 磁盘（根分区）---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;dtotal&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;df&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-kP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; | &lt;/span&gt;&lt;span&gt;tail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;span&gt; | &lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;{print $2}&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;dused&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;df&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-kP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; | &lt;/span&gt;&lt;span&gt;tail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;span&gt; | &lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;{print $3}&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;davail&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;df&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-kP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; | &lt;/span&gt;&lt;span&gt;tail&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;span&gt; | &lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;{print $4}&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# --- 网络计数器 ---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;RX&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;grep&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;^[[:space:]]*&lt;/span&gt;&lt;span&gt;$IFACE&lt;/span&gt;&lt;span&gt;:&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/proc/net/dev&lt;/span&gt;&lt;span&gt; | &lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;{print $2}&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;TX&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;grep&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;^[[:space:]]*&lt;/span&gt;&lt;span&gt;$IFACE&lt;/span&gt;&lt;span&gt;:&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/proc/net/dev&lt;/span&gt;&lt;span&gt; | &lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;{print $10}&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# --- 负载 / 运行时长 ---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;load1&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;cut&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt;&apos; &apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/proc/loadavg&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;UPTIME&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;cut&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/proc/uptime&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;TS&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+%s&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;33&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;34&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# --- 读上次采样，算差值 ---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;35&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;prev_ct&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;prev_cb&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;prev_rx&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;prev_tx&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;prev_ts&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;36&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;[ &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$STATE&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ] &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-r&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prev_ct&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prev_cb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prev_rx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prev_tx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prev_ts&lt;/span&gt;&lt;span&gt; &amp;lt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$STATE&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;37&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;38&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;dct&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$((&lt;/span&gt;&lt;/span&gt;&lt;span&gt;CPU_TOTAL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prev_ct&lt;/span&gt;&lt;span&gt;)); &lt;/span&gt;&lt;span&gt;dcb&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$((&lt;/span&gt;&lt;/span&gt;&lt;span&gt;CPU_BUSY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prev_cb&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;39&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;drx&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$((&lt;/span&gt;&lt;/span&gt;&lt;span&gt;RX&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prev_rx&lt;/span&gt;&lt;span&gt;));        &lt;/span&gt;&lt;span&gt;dtx&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$((&lt;/span&gt;&lt;/span&gt;&lt;span&gt;TX&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prev_tx&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;40&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;dt&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$((&lt;/span&gt;&lt;/span&gt;&lt;span&gt;TS&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;prev_ts&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;41&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;[ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$dt&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;-lt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; ] &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;dt&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;42&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;[ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$dtx&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-lt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; ] &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;dtx&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;43&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;[ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$drx&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-lt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; ] &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;drx&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;44&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;45&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$prev_ts&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-eq&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; ] || [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$dct&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-le&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;46&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cpu_pct&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;-1&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;rx_s&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;tx_s&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;47&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;48&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cpu_pct&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;b=&quot;&lt;/span&gt;&lt;span&gt;$dcb&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;t=&quot;&lt;/span&gt;&lt;span&gt;$dct&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;BEGIN{p=b/t*100; if(p&amp;lt;0)p=0; if(p&amp;gt;100)p=100; printf &quot;%.1f&quot;, p}&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;49&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;rx_s&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r=&quot;&lt;/span&gt;&lt;span&gt;$drx&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;t=&quot;&lt;/span&gt;&lt;span&gt;$dt&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;BEGIN{printf &quot;%.1f&quot;, r/t/1024}&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;50&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;tx_s&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r=&quot;&lt;/span&gt;&lt;span&gt;$dtx&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;t=&quot;&lt;/span&gt;&lt;span&gt;$dt&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;BEGIN{printf &quot;%.1f&quot;, r/t/1024}&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;51&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;52&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;53&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;printf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;%s %s %s %s %s\n&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CPU_TOTAL&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CPU_BUSY&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$RX&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TX&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TS&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &amp;gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$STATE&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;54&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;55&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;MEM_PCT&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;t=&quot;&lt;/span&gt;&lt;span&gt;$memtotal&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a=&quot;&lt;/span&gt;&lt;span&gt;$memavail&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;BEGIN{printf &quot;%.1f&quot;, (t&amp;gt;0)? (t-a)/t*100 : 0}&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;56&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;DISK_PCT&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;u=&quot;&lt;/span&gt;&lt;span&gt;$dused&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a=&quot;&lt;/span&gt;&lt;span&gt;$davail&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;BEGIN{t=u+a; printf &quot;%.1f&quot;, (t&amp;gt;0)? u/t*100 : 0}&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;57&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;TOTRX&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;r=&quot;&lt;/span&gt;&lt;span&gt;$RX&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;BEGIN{printf &quot;%.2f&quot;, r/1073741824}&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;58&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;TOTTX&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;awk&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;t2=&quot;&lt;/span&gt;&lt;span&gt;$TX&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;BEGIN{printf &quot;%.2f&quot;, t2/1073741824}&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;59&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;60&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;mkdir&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/var/www/mon&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;61&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 注意：JSON 里刻意不写主机名、IP、网卡名，避免泄露服务器信息&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;62&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;printf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;{&quot;ts&quot;:%s,&quot;uptime&quot;:%s,&quot;load1&quot;:&quot;%s&quot;,&quot;cpu&quot;:{&quot;usage&quot;:%s},&quot;mem&quot;:{&quot;total_kb&quot;:%s,&quot;avail_kb&quot;:%s,&quot;used_pct&quot;:%s},&quot;disk&quot;:{&quot;total_kb&quot;:%s,&quot;used_kb&quot;:%s,&quot;used_pct&quot;:%s},&quot;net&quot;:{&quot;rx_kbps&quot;:%s,&quot;tx_kbps&quot;:%s,&quot;total_rx_gb&quot;:%s,&quot;total_tx_gb&quot;:%s}}\n&apos;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;63&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TS&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$UPTIME&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$load1&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$cpu_pct&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$memtotal&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$memavail&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$MEM_PCT&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;64&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$dtotal&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$dused&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$DISK_PCT&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$rx_s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$tx_s&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TOTRX&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$TOTTX&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &amp;gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$OUT&lt;/span&gt;&lt;span&gt;.tmp&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;65&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;mv&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$OUT&lt;/span&gt;&lt;span&gt;.tmp&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$OUT&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;几个准确性设计，都是查过资料才定的：&lt;/p&gt;&lt;p&gt;&lt;strong&gt;CPU 用两次计数差值，不是瞬时值。&lt;/strong&gt; &lt;code&gt;/proc/stat&lt;/code&gt; 的 CPU 时间是开机以来的累计值，单次读没有任何意义。脚本把上次计数存在 &lt;code&gt;prev.txt&lt;/code&gt;，这次减上次，差值才是这段时间的真实占用率。这也是为什么页面刚上线时 CPU 会显示几秒的 ”…”——第一次运行还没有差值，用 &lt;code&gt;-1&lt;/code&gt; 标记，前端识别后显示省略号而不是错误数据。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;内存用 &lt;code&gt;MemAvailable&lt;/code&gt; 口径。&lt;/strong&gt; Linux 会把空闲内存拿去做文件缓存（buff/cache），&lt;code&gt;free&lt;/code&gt; 命令里的”可用”就是 &lt;code&gt;MemAvailable&lt;/code&gt;：等于空闲 + 可回收缓存。用它算占用率才贴近真实体验，直接拿 &lt;code&gt;MemTotal - MemFree&lt;/code&gt; 会把缓存算成”已用”，2G 机器看起来永远 90%+。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;磁盘百分比和 &lt;code&gt;df&lt;/code&gt; 命令同口径。&lt;/strong&gt; 有两种算法：&lt;code&gt;used/total&lt;/code&gt; 和 &lt;code&gt;used/(used+avail)&lt;/code&gt;。后者才是 &lt;code&gt;df&lt;/code&gt; 命令显示的那个百分比——因为 &lt;code&gt;total&lt;/code&gt; 里含一部分 root 保留块（普通用户写不进去），分母用 &lt;code&gt;used+avail&lt;/code&gt; 才不会把面板显示得比实际乐观。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;JSON 用「写临时文件 + mv」原子替换。&lt;/strong&gt; 如果前端 fetch 的瞬间脚本正写到一半，会拿到半截 JSON 直接解析报错。先写 &lt;code&gt;.tmp&lt;/code&gt; 再 &lt;code&gt;mv&lt;/code&gt;，&lt;code&gt;mv&lt;/code&gt; 在同一分区是原子操作，任何时刻文件都是完整的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;计数器回绕保护。&lt;/strong&gt; 服务器重启后 &lt;code&gt;/proc/net/dev&lt;/code&gt; 的流量计数会归零，差值算出负数，&lt;code&gt;[ &quot;$dtx&quot; -lt 0 ] &amp;amp;&amp;amp; dtx=0&lt;/code&gt; 把它钳到 0，面板不会出现负速率。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;三、第二步：定时采样&lt;a href=&quot;#三第二步定时采样&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;cron 最小粒度是一分钟，想要 30 秒采样有个经典技巧——一条规则里执行两次，第二次延后半分钟：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;* * * * * /opt/server-mon/monitor.sh &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; &lt;/span&gt;&lt;span&gt;sleep&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;/opt/server-mon/monitor.sh&lt;/span&gt;&lt;span&gt; &amp;gt;&lt;/span&gt;&lt;span&gt;/dev/null&lt;/span&gt;&lt;span&gt; 2&amp;gt;&amp;amp;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;用 &lt;code&gt;crontab -e&lt;/code&gt; 添加后，&lt;code&gt;crontab -l&lt;/code&gt; 确认。采样间隔缩短一半有两个好处：网速/CPU 的统计窗口更短，短时突发流量更能捕捉到；页面每 30 秒刷新时每次都能拿到新数据，而不是同样的数字看一分钟。&lt;/p&gt;&lt;p&gt;脚本的差值逻辑天然支持任意间隔（用实际时间差 &lt;code&gt;dt&lt;/code&gt; 做除数），所以就算 cron 延迟几分钟跑，速率计算依然准确。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;四、第三步：Nginx 暴露数据&lt;a href=&quot;#四第三步nginx-暴露数据&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;JSON 文件在 &lt;code&gt;/var/www/mon/status.json&lt;/code&gt;，在站点 Nginx 配置里加一个 location：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 路径里那串随机字符是「路径令牌」，防止端点被扫描，需与博客配置一致&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; ^~ &lt;/span&gt;&lt;span&gt;/server-status/&amp;lt;随机令牌&amp;gt;/ &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;alias &lt;/span&gt;&lt;span&gt;/var/www/mon/;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;Cache-Control &lt;/span&gt;&lt;span&gt;&quot;no-store&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;X-Robots-Tag &lt;/span&gt;&lt;span&gt;&quot;noindex, nofollow&quot;&lt;/span&gt;&lt;span&gt; always;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;三个细节：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;no-store&lt;/code&gt;&lt;/strong&gt;：状态数据永远不能被缓存，否则访客看到的是几分钟前的旧数据；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;noindex, nofollow&lt;/code&gt;&lt;/strong&gt;：告诉搜索引擎不要收录这个数据端点；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;路径令牌&lt;/strong&gt;：JSON 的真实地址里拼一段随机字符串。改完 &lt;code&gt;nginx -t&lt;/code&gt; 测试语法，再 &lt;code&gt;systemctl reload nginx&lt;/code&gt; 平滑生效，站点不中断。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;五、第四步：博客展示页&lt;a href=&quot;#五第四步博客展示页&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;页面是 Astro 写的（其他框架同理，核心就是 fetch + 渲染）。骨架：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DATA_URL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/server-status/&amp;lt;随机令牌&amp;gt;/status.json&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;REFRESH_SEC&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;STALE_SEC&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;180&lt;/span&gt;&lt;span&gt;;      &lt;/span&gt;&lt;span&gt;// 数据超过 3 分钟没更新视为异常&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;WARN_THRESHOLD&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;;  &lt;/span&gt;&lt;span&gt;// 占用 60% 进度条转黄&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HIGH_THRESHOLD&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;85&lt;/span&gt;&lt;span&gt;;  &lt;/span&gt;&lt;span&gt;// 占用 85% 转红&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;refresh&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;DATA_URL&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;?t=&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;(), { &lt;/span&gt;&lt;span&gt;cache&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;no-store&quot;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;ok&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;throw&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Error&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;render&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;);  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;// 按 CPU/内存/磁盘/网络分块渲染&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;stale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Date&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;now&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;d&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;ts&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;STALE_SEC&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;setStatus&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt;&lt;span&gt;stale&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;stale&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;数据延迟（采样服务可能异常）&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;运行正常&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;setStatus&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;无法获取状态数据&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;refresh&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;setInterval&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;refresh&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;REFRESH_SEC&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1000&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;?t=时间戳&lt;/code&gt; 是防止浏览器或中间层自作聪明缓存请求。渲染上有两个容错设计：每个字段赋值前都判空，某个字段缺失只跳过那一处，不会让整个面板白屏；进度条按占用分三档变色（正常主题色 → 黄 → 红），一眼就能看出哪项吃紧。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;六、第五步：配置化&lt;a href=&quot;#六第五步配置化&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;把所有参数收进博客的统一配置文件（&lt;code&gt;src/config/index.ts&lt;/code&gt; 的「服务器状态」模块），页面和导航都从这里读：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;serverStatusConfig&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;enable&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,        &lt;/span&gt;&lt;span&gt;// 总开关：关闭后导航隐藏入口，页面跳转 404&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;服务器状态&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;dataUrl&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;...&quot;&lt;/span&gt;&lt;span&gt;,      &lt;/span&gt;&lt;span&gt;// 由路径令牌拼出&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;refreshSec&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;staleSec&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;180&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;warnThreshold&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;60&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;highThreshold&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;85&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;cards&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;cpu&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;mem&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;disk&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;net&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt; },  &lt;/span&gt;&lt;span&gt;// 卡片开关&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;导航栏入口也写成条件渲染——&lt;code&gt;enable&lt;/code&gt; 一关，导航上「状态」直接消失，不需要再改第二个文件。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;七、关于防泄露&lt;a href=&quot;#七关于防泄露&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;面板数据是公开的（所有访客都能看），所以做了四层处理：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;措施&lt;/th&gt;&lt;th&gt;防什么&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;JSON 路径拼随机令牌&lt;/td&gt;&lt;td&gt;扫描器批量猜 &lt;code&gt;/server-status/status.json&lt;/code&gt; 这类常见路径，直接 404&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;noindex&lt;/code&gt; 响应头&lt;/td&gt;&lt;td&gt;搜索引擎不收录数据端点&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;JSON 字段脱敏&lt;/td&gt;&lt;td&gt;不输出主机名、IP、网卡名、系统版本，只有资源占用指标&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;总开关联动&lt;/td&gt;&lt;td&gt;关闭后整个站不再出现数据地址&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;要诚实说明边界：令牌会出现在网页源码里，所以它防的是扫描器和顺手试探，&lt;strong&gt;不防决心翻你源码的人&lt;/strong&gt;。这是「隐藏入口」不是「加密认证」，对个人博客是成本收益比最高的一档。真要更严格，可以上 Nginx 的 &lt;code&gt;secure_link&lt;/code&gt; 模块做签名校验，或者限制访问 IP——对个人博客来说通常没必要。&lt;/p&gt;&lt;p&gt;另外，想换令牌随时可以：改博客配置里的一处 + Nginx 配置里的一处，reload 即可，旧地址立刻作废，相当于换门牌号。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;八、踩过的坑&lt;a href=&quot;#八踩过的坑&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;记录几个调试时真实撞上的问题：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Windows 编辑的脚本在 Linux 跑不了&lt;/strong&gt;：报 &lt;code&gt;integer expected&lt;/code&gt;。Windows 换行是 CRLF，bash 把 &lt;code&gt;\r&lt;/code&gt; 当成了内容的一部分。修复：&lt;code&gt;sed -i &apos;s/\r$//&apos; monitor.sh&lt;/code&gt;。写服务器脚本最好直接在服务器上用 nano/vim 写，或传完必跑一次这条命令。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网速永远是 0&lt;/strong&gt;：&lt;code&gt;/proc/net/dev&lt;/code&gt; 每行行首有空格，直接 &lt;code&gt;awk -F&apos;[: ]+&apos; &apos;{print $2}&apos;&lt;/code&gt; 会把空字段算进去取错列。修复：先用 &lt;code&gt;grep &quot;^[[:space:]]*eth0:&quot;&lt;/code&gt; 锁定网卡那一行，再用默认分隔符取列。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;磁盘百分比和 &lt;code&gt;df&lt;/code&gt; 对不上&lt;/strong&gt;：算法口径问题，见前文，改用 &lt;code&gt;used/(used+avail)&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;内存显示 1.69 TB&lt;/strong&gt;：前端单位换算错位——&lt;code&gt;1048576 KB&lt;/code&gt; 是 1 GB 不是 1 TB，把 2G 内存显示成了 1.69 TB。修完的换算表：KB → 1024 → MB → 1024 → GB → 1024 → TB，写的时候直接把 1073741824 这类常数标注在代码注释里。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文章时间偏移 8 小时&lt;/strong&gt;：CI 在 UTC 时区构建，frontmatter 里的时间不写时区就会被按 UTC 解析。修复：所有 &lt;code&gt;published&lt;/code&gt; 明确写 &lt;code&gt;+08:00&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;九、小结&lt;a href=&quot;#九小结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这套面板的全部成本：约 70 行 bash + 一段 Nginx 配置 + 一个页面组件，运行时内存占用约等于零（脚本每 30 秒跑一次，跑完就退）。准确度上因为直接读内核数据、用差值法和标准口径，数字和 &lt;code&gt;free&lt;/code&gt;、&lt;code&gt;df&lt;/code&gt; 完全对得上。&lt;/p&gt;&lt;p&gt;适合的场景很明确：小内存服务器、想要和博客风格统一的轻量面板、不想多开端口和服务。如果你需要历史曲线、告警通知，那还是上 beszel 这类专业工具——但我 2G 的小机器，这个刚好。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;系列文章：&lt;a href=&quot;/posts/1/&quot;&gt;《从零搭建个人博客》&lt;/a&gt; · &lt;a href=&quot;/posts/2/&quot;&gt;《blog 日常使用指南》&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;</content:encoded></item><item><title>博客日常使用指南：写文章、发动态、加书签（自用记录）</title><link>https://li8.me/posts/2/</link><guid isPermaLink="true">https://li8.me/posts/2/</guid><description>手把手教程：从把网站跑起来，到写第一篇文章、用正文语法、发布上线，再到发动态、加书签、改配置，每一步都给出可直接复制的命令和模板</description><pubDate>Mon, 21 Sep 2026 04:07:42 GMT</pubDate><content:encoded>&lt;p&gt;上一篇&lt;a href=&quot;/posts/1/&quot;&gt;《从零搭建个人博客》&lt;/a&gt;记录了怎么把站搭起来。这篇是&lt;strong&gt;手把手使用手册&lt;/strong&gt;：每一步都给出可以直接复制的命令和模板，照着做就行，不用动脑子（划掉）不用查资料。&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;记住一个总原则：&lt;strong&gt;所有站点配置都集中在 &lt;code&gt;src/config/index.ts&lt;/code&gt; 一个文件里&lt;/strong&gt;（按模块分区、带编号注释），&lt;strong&gt;所有内容都在 &lt;code&gt;src/content/&lt;/code&gt; 目录下&lt;/strong&gt;。改完保存，本地开发页面即时热更新。&lt;/p&gt;&lt;/blockquote&gt;
&lt;section&gt;&lt;h2&gt;第 0 步：把网站跑起来&lt;a href=&quot;#第-0-步把网站跑起来&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;写文章之前，先让网站在本地转起来。只需要做一次。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;1. 确认环境&lt;a href=&quot;#1-确认环境&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;打开终端（Windows 用 PowerShell），分别输入这两条命令检查版本：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;node&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# 需要 v22.23 以上&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# 需要 11.22 以上&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;如果提示「不是内部或外部命令」，说明还没安装：去 &lt;a href=&quot;https://nodejs.org/&quot; target=&quot;_blank&quot;&gt;nodejs.org&lt;/a&gt; 装 Node.js（会自带核心），再输入 &lt;code&gt;npm install -g pnpm&lt;/code&gt; 装 pnpm。装完重开一个终端再检查一次。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2. 启动网站&lt;a href=&quot;#2-启动网站&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;D:&lt;/span&gt;&lt;span&gt;\b&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;\F&lt;/span&gt;&lt;span&gt;irefly&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# 换成你的项目文件夹路径&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;# 第一次运行才需要，安装依赖&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;              &lt;/span&gt;&lt;span&gt;# 启动本地开发服务器&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;看到终端里出现 &lt;code&gt;Local http://localhost:4321/&lt;/code&gt; 就说明成功了。用浏览器打开这个地址，你看到的就是自己的网站。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;热更新&lt;/strong&gt;：只要 &lt;code&gt;pnpm dev&lt;/code&gt; 开着，你改任何文件保存，浏览器页面会自动刷新。写文章时建议左边开编辑器、右边开浏览器，改一段看一眼。&lt;/p&gt;&lt;p&gt;按 &lt;code&gt;Ctrl + C&lt;/code&gt; 停止开发服务器。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;第 1 步：写第一篇文章&lt;a href=&quot;#第-1-步写第一篇文章&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;文章放在哪里&lt;a href=&quot;#文章放在哪里&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;所有文章都是 &lt;code&gt;src/content/posts/&lt;/code&gt; 目录下的 Markdown 文件，&lt;strong&gt;一个 &lt;code&gt;.md&lt;/code&gt; 文件就是一篇文章&lt;/strong&gt;。不需要注册或登记，文件放进去就自动出现在首页、归档和 RSS 里。&lt;/p&gt;&lt;p&gt;目录按分类建了文件夹：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;src/content/posts/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;├── tech/     # 技术类：建站、教程、运维&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;├── daily/    # 日常随笔&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└── anime/    # 追番相关&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;以后想加新分类，在 &lt;code&gt;posts/&lt;/code&gt; 下新建一个文件夹就行，不需要改任何配置。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;方法一：用脚手架创建（推荐）&lt;a href=&quot;#方法一用脚手架创建推荐&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;在项目根目录的终端里执行（把 &lt;code&gt;tech/my-first-post&lt;/code&gt; 换成你的文件名）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new-post&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tech/my-first-post&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这条命令会自动做三件事：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;在 &lt;code&gt;src/content/posts/tech/&lt;/code&gt; 下创建 &lt;code&gt;my-first-post.md&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;写好 frontmatter 模板（标题、日期、slug 都是现成的）；&lt;/li&gt;
&lt;li&gt;如果文件名里写了中文，会自动转成拼音作为 URL，比如 &lt;code&gt;我的笔记&lt;/code&gt; → &lt;code&gt;wo-de-bi-ji&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;文件名可以带子文件夹前缀（如 &lt;code&gt;tech/xxx&lt;/code&gt;），也可以不带（直接生成在 &lt;code&gt;posts/&lt;/code&gt; 根目录，再手动挪进分类文件夹）。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;方法二：手动创建&lt;a href=&quot;#方法二手动创建&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;在 &lt;code&gt;src/content/posts/tech/&lt;/code&gt; 里直接新建一个 &lt;code&gt;我的文章.md&lt;/code&gt;，把下面的模板整个粘进去，然后按注释逐行改：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;我的第一篇文章&lt;/span&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;# 必填，显示在列表和浏览器标签页&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;published&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2026-09-22&lt;/span&gt;&lt;span&gt;           &lt;/span&gt;&lt;span&gt;# 必填，发布日期&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;一两句话的摘要&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;# 建议填，列表页和分享卡会显示&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;建站&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;随笔&lt;/span&gt;&lt;span&gt;]               &lt;/span&gt;&lt;span&gt;# 标签，方括号里逗号分隔，可以多个&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;category&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;技术&lt;/span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;span&gt;# 分类，和所在文件夹保持一致&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;draft&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;                    &lt;/span&gt;&lt;span&gt;# false = 正式发布；true = 草稿&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;slug&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;my-first-post&lt;/span&gt;&lt;span&gt;             &lt;/span&gt;&lt;span&gt;# 可选，自定义 URL；不填就用文件名&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;正文从这里开始，标准 Markdown 语法……&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;脚手架用户注意&lt;/strong&gt;：&lt;code&gt;pnpm new-post&lt;/code&gt; 生成的模板里有一行 &lt;code&gt;image: &apos;&apos;&lt;/code&gt;（空值 = 不显示封面）。&lt;strong&gt;想要随机二次元封面，就把这一行删掉&lt;/strong&gt;；想指定封面再填图片直链。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;frontmatter 字段速查&lt;a href=&quot;#frontmatter-字段速查&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;frontmatter 就是文件开头两行 &lt;code&gt;---&lt;/code&gt; 之间的那段 YAML，是文章的”身份证”。常用字段：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;字段&lt;/th&gt;&lt;th&gt;必填&lt;/th&gt;&lt;th&gt;作用&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;title&lt;/code&gt;&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;文章标题&lt;/td&gt;&lt;td&gt;列表、页面标题都用它&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;published&lt;/code&gt;&lt;/td&gt;&lt;td&gt;✅&lt;/td&gt;&lt;td&gt;发布日期&lt;/td&gt;&lt;td&gt;决定排序和归档年份&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;description&lt;/code&gt;&lt;/td&gt;&lt;td&gt;建议&lt;/td&gt;&lt;td&gt;摘要&lt;/td&gt;&lt;td&gt;列表页显示；不填会截取正文开头&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;tags&lt;/code&gt;&lt;/td&gt;&lt;td&gt;可选&lt;/td&gt;&lt;td&gt;标签&lt;/td&gt;&lt;td&gt;&lt;code&gt;[标签A, 标签B]&lt;/code&gt; 格式&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;category&lt;/code&gt;&lt;/td&gt;&lt;td&gt;建议&lt;/td&gt;&lt;td&gt;分类&lt;/td&gt;&lt;td&gt;一篇只归一类，建议与文件夹同名&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;draft&lt;/code&gt;&lt;/td&gt;&lt;td&gt;可选&lt;/td&gt;&lt;td&gt;草稿开关&lt;/td&gt;&lt;td&gt;&lt;code&gt;true&lt;/code&gt; 只在本地预览可见，构建时排除&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;slug&lt;/code&gt;&lt;/td&gt;&lt;td&gt;建议&lt;/td&gt;&lt;td&gt;URL 地址&lt;/td&gt;&lt;td&gt;见下方「URL 规则」&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;image&lt;/code&gt;&lt;/td&gt;&lt;td&gt;可选&lt;/td&gt;&lt;td&gt;封面图&lt;/td&gt;&lt;td&gt;删掉这行 = 随机封面；填直链 = 指定封面；&lt;code&gt;&apos;&apos;&lt;/code&gt; = 无封面&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;pinned&lt;/code&gt;&lt;/td&gt;&lt;td&gt;可选&lt;/td&gt;&lt;td&gt;置顶&lt;/td&gt;&lt;td&gt;&lt;code&gt;true&lt;/code&gt; 固定在文章列表最前面&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;updated&lt;/code&gt;&lt;/td&gt;&lt;td&gt;可选&lt;/td&gt;&lt;td&gt;最后编辑时间&lt;/td&gt;&lt;td&gt;改过文章后填新日期&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;series&lt;/code&gt;&lt;/td&gt;&lt;td&gt;可选&lt;/td&gt;&lt;td&gt;系列名&lt;/td&gt;&lt;td&gt;同名系列自动组成系列页，配合 &lt;code&gt;seriesOrder: 1&lt;/code&gt; 排序&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;password&lt;/code&gt;&lt;/td&gt;&lt;td&gt;可选&lt;/td&gt;&lt;td&gt;访问密码&lt;/td&gt;&lt;td&gt;填了则文章加密，读者需输密码；&lt;code&gt;passwordHint&lt;/code&gt; 可给提示&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;几个要点：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;URL 规则&lt;/strong&gt;：&lt;code&gt;tech/docker-basics.md&lt;/code&gt; 不写 &lt;code&gt;slug&lt;/code&gt; 时地址是 &lt;code&gt;/posts/tech/docker-basics/&lt;/code&gt;；写了 &lt;code&gt;slug: docker-basics&lt;/code&gt; 则是干净的 &lt;code&gt;/posts/docker-basics/&lt;/code&gt;。&lt;strong&gt;建议每篇都写 &lt;code&gt;slug&lt;/code&gt;&lt;/strong&gt;，以后调整文件夹不影响链接；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;中文文件名可以用&lt;/strong&gt;（脚手架会自动转拼音），但手动建文件时建议文件名用英文短横线风格，URL 更好看；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;时区坑点&lt;/strong&gt;：&lt;code&gt;published&lt;/code&gt; 只写日期没问题；如果精确到几点几分，必须写成 &lt;code&gt;2026-09-21T09:00:00+08:00&lt;/code&gt; 带时区的格式。因为构建机在 GitHub 的 UTC 时区，不带时区会被当成 UTC，时间全部偏移 8 小时。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;保存，看效果&lt;a href=&quot;#保存看效果&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;保存文件，浏览器（&lt;code&gt;http://localhost:4321&lt;/code&gt;）会自动刷新——首页列表里出现你的新文章了。点进去检查排版，改到满意为止。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;第 2 步：正文常用写法&lt;a href=&quot;#第-2-步正文常用写法&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;正文就是标准 Markdown，下面每种给一个最小可复制示例。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;基础排版&lt;a href=&quot;#基础排版&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;## 二级标题（一级标题留给文章大标题，正文从二级开始）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;普通段落，&lt;/span&gt;&lt;span&gt;**加粗**&lt;/span&gt;&lt;span&gt;、&lt;/span&gt;&lt;span&gt;*斜体*&lt;/span&gt;&lt;span&gt;、~~删除线~~、&lt;/span&gt;&lt;span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;行内代码&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span&gt;。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 无序列表项&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 另一项&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;1.&lt;/span&gt;&lt;span&gt; 有序列表项&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;2.&lt;/span&gt;&lt;span&gt; 另一项&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; 引用块，适合放注意事项或台词&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;链接文字&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;https://example.com&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;图片&lt;a href=&quot;#图片&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;![&lt;/span&gt;&lt;span&gt;图片说明&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;https://example.com/photo.jpg&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;   # 网络图片，直接贴直链&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;![&lt;/span&gt;&lt;span&gt;本地图片&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;/assets/images/photo.jpg&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;        # 本地图片放到 public/assets/images/ 下&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;代码块&lt;a href=&quot;#代码块&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;三个反引号开头，后面跟语言名：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;```js&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;console&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;Hello, blog!&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;```&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;支持语法高亮和复制按钮。&lt;strong&gt;超过 15 行的长代码会自动折叠&lt;/strong&gt;（显示前 8 行 + 展开按钮），也可以在语言名后面写 &lt;code&gt;collapse&lt;/code&gt; 手动控制。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;提示框（callout）&lt;a href=&quot;#提示框callout&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;&lt;code&gt;&amp;gt; [!类型]&lt;/code&gt; 开头的引用块会渲染成彩色提示框，支持五种类型：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;!NOTE&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; 补充说明，蓝色。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;!TIP&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; 小技巧，绿色。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;!IMPORTANT&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; 重要信息，紫色。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;!WARNING&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; 警告内容，黄色。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; &lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;!CAUTION&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&amp;gt; 危险操作，红色。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;表格&lt;a href=&quot;#表格&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| 姓名 | 作用 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| --- | --- |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| Astro | 静态站点框架 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;| Svelte | 前端交互框架 |&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;数学公式&lt;a href=&quot;#数学公式&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;行内公式：$E = mc^2$&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;独立公式块：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;$$&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;\int_{-\infty}^{\infty} e^{-x^2} \, dx = \sqrt{\pi}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;$$&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;流程图（Mermaid）&lt;a href=&quot;#流程图mermaid&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;```mermaid&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;graph LR&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;A[写文章] --&amp;gt; B[git push]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;B --&amp;gt; C[自动构建]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;C --&amp;gt; D[自动上线]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;```&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;时序图（PlantUML）&lt;a href=&quot;#时序图plantuml&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;```plantuml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@startuml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;游客 -&amp;gt; 网站 : 打开文章&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;网站 --&amp;gt; 游客 : 返回内容&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;@enduml&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;```&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;每篇文章自带的「AI 智能总结」&lt;a href=&quot;#每篇文章自带的ai-智能总结&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;文章页顶部有个「AI 智能总结」卡片，&lt;strong&gt;不需要你做任何事&lt;/strong&gt;：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;摘要在网站构建时用免费 AI 预先生成好，直接烘进页面，访客秒开；&lt;/li&gt;
&lt;li&gt;只在你改了文章内容后，下次构建才会重新生成那篇的摘要；&lt;/li&gt;
&lt;li&gt;开关、接口、字数、逐字特效等参数都在 &lt;code&gt;src/config/index.ts&lt;/code&gt; 的「AI 总结」模块；&lt;/li&gt;
&lt;li&gt;只在构建时把正文发给 AI 一次，不收集任何访客信息。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;第 3 步：发布上线&lt;a href=&quot;#第-3-步发布上线&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;本地满意后，三连发布：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-A&lt;/span&gt;&lt;span&gt;                                        &lt;/span&gt;&lt;span&gt;# 1. 把所有改动放进暂存区&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;post: 新文章「我的第一篇文章」&quot;&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;# 2. 打包成一个存档点&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;                                          &lt;/span&gt;&lt;span&gt;# 3. 推送到 GitHub&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;推送后 GitHub 会&lt;strong&gt;自动构建&lt;/strong&gt;，构建完成后服务器&lt;strong&gt;自动拉取上线&lt;/strong&gt;，约两三分钟后刷新你的线上网站就能看到新文章——不需要登录服务器做任何操作。&lt;/p&gt;&lt;p&gt;修改已发布的文章也一样：改文件 → 重新三连即可，链接不变（这就是建议写 &lt;code&gt;slug&lt;/code&gt; 的原因）。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;常见问题排查&lt;a href=&quot;#常见问题排查&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;改了内容但页面没变？&lt;/strong&gt;&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;先确认 &lt;code&gt;pnpm dev&lt;/code&gt; 还开着、浏览器开着的是 &lt;code&gt;localhost:4321&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;还不行就 &lt;code&gt;Ctrl + C&lt;/code&gt; 停掉，重新 &lt;code&gt;pnpm dev&lt;/code&gt;（改 frontmatter 后偶尔需要）；&lt;/li&gt;
&lt;li&gt;再不行删掉项目根目录的 &lt;code&gt;.astro&lt;/code&gt; 文件夹后重启——这是构建缓存，删了没副作用。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;线上要等才生效？&lt;/strong&gt; 记住分界线：&lt;code&gt;pnpm dev&lt;/code&gt; 里的改动只存在于你电脑；只有 &lt;code&gt;git push&lt;/code&gt; 之后才会上线。另外改 &lt;code&gt;src/config/index.ts&lt;/code&gt; 里的构建类配置（如 AI 总结），本地也要重新 &lt;code&gt;pnpm build&lt;/code&gt; 才能验证。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;发布日期不对？&lt;/strong&gt; 见上方时区坑点，时间精确到分时必须带 &lt;code&gt;+08:00&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;文章不想让人看到？&lt;/strong&gt; &lt;code&gt;draft: true&lt;/code&gt; 转草稿，只在本地可见。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;发动态&lt;a href=&quot;#发动态&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;动态是短内容随手记，和文章分开管理，存放在 &lt;code&gt;src/content/dynamic/&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;一行命令发一条：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new-dynamic&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;今天给博客加了个状态页，折腾了一下午，值！&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;会在动态目录生成一个按时间命名的 &lt;code&gt;.md&lt;/code&gt; 文件，内容就是你刚才输入的文字，&lt;code&gt;published&lt;/code&gt; 时间自动填好。也可以手动建文件，frontmatter 只有两三个字段：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;published&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2026-09-21T20:30:00+08:00&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# 必填，精确到分记得带时区&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pinned&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;                          &lt;/span&gt;&lt;span&gt;# 可选，true 置顶在动态流最前&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;上海&lt;/span&gt;&lt;span&gt;                         &lt;/span&gt;&lt;span&gt;# 可选，显示在动态卡片上&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;这里写动态内容，支持 Markdown。图片地址写在正文里会自动渲染成图片墙。&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;小坑&lt;/strong&gt;：删除或修改动态后页面没变化，是构建缓存问题——删掉 &lt;code&gt;.astro&lt;/code&gt; 目录重启 &lt;code&gt;pnpm dev&lt;/code&gt; 就好。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;添加书签导航&lt;a href=&quot;#添加书签导航&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;书签导航页（&lt;code&gt;/booknav/&lt;/code&gt;）的数据在 &lt;code&gt;src/config/index.ts&lt;/code&gt; 的「书签导航」模块，按「分类组 → 书签项」两级组织。把下面示例粘进去改成自己的书签即可：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;booknavConfig&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;BooknavGroup&lt;/span&gt;&lt;span&gt;[] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// —— 分类组 ——&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;dev&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 唯一标识&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;开发&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 分类名称&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;icon&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;material-symbols:code-rounded&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 分类图标（Iconify 格式）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;写代码时离不开的站点&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 分类描述，可省略&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;weight&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;100&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 排序权重，越大越靠前&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;items&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;// —— 书签项 ——&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;MDN Web Docs&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://developer.mozilla.org&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;desc&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;最权威的 Web 技术文档&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 可省略&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;weight&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;9&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// 组内排序&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Astro 文档&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://docs.astro.build&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;图标不用手动找图&lt;/strong&gt;：书签项没写 &lt;code&gt;icon&lt;/code&gt; 时会自动抓取目标站点的 favicon。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;其他常用配置速查&lt;a href=&quot;#其他常用配置速查&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;想做什么&lt;/th&gt;&lt;th&gt;改哪里&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;站名 / 副标题 / 主题色 / 域名&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/config/index.ts&lt;/code&gt; ① 站点基础（站名改 &lt;code&gt;SITE_TITLE&lt;/code&gt; 一处即可）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;头像 / 昵称 / 社交链接&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/config/index.ts&lt;/code&gt; ② 用户资料（首页横幅链接同步生效）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;开关某个页面（如相册、友链）&lt;/td&gt;&lt;td&gt;① 站点基础里的 &lt;code&gt;pages&lt;/code&gt; 字段&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;导航栏菜单与顺序&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/config/index.ts&lt;/code&gt; ④ 导航栏&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;侧边栏组件与顺序&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/config/index.ts&lt;/code&gt; ⑥ 侧边栏布局（桌面与移动端共用的组件在 &lt;code&gt;WIDGETS&lt;/code&gt; 里改一处）&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;首页壁纸与横幅文案&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/config/index.ts&lt;/code&gt; ⑦ 背景壁纸&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;相册&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/config/index.ts&lt;/code&gt; ⑮ 相册 + 图片放 &lt;code&gt;public/gallery/&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;页脚文字&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/config/FooterConfig.html&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;关于页 / 友链页&lt;/td&gt;&lt;td&gt;&lt;code&gt;src/content/spec/about.md&lt;/code&gt; / &lt;code&gt;friends.mdx&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;两个通用规律：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;pages&lt;/code&gt; 开关是总闸&lt;/strong&gt;：把某个页面设为 &lt;code&gt;false&lt;/code&gt;，导航、sitemap 里都会自动消失，不用到处删链接；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;图标统一用 &lt;a href=&quot;https://icon-visualizer.ntl-sync.dev/&quot; target=&quot;_blank&quot;&gt;Iconify&lt;/a&gt; 语法&lt;/strong&gt;：&lt;code&gt;图标集:图标名&lt;/code&gt;，如 &lt;code&gt;material-symbols:home&lt;/code&gt;、&lt;code&gt;fa7-brands:bilibili&lt;/code&gt;，全站通用。&lt;/li&gt;
&lt;/ol&gt;&lt;hr /&gt;&lt;p&gt;把这篇当手册用：&lt;strong&gt;写文章走第 1～3 步，忘语法查第 2 步，出问题查 FAQ，改配置查速查表&lt;/strong&gt;。后续有新的配置心得，我会持续更新这篇文章。&lt;/p&gt;&lt;/section&gt;</content:encoded></item><item><title>从零搭建个人博客：手把手完整教程与踩坑记录</title><link>https://li8.me/posts/1/</link><guid isPermaLink="true">https://li8.me/posts/1/</guid><description>手把手教程：环境准备、项目初始化、站点配置、服务器选购、Nginx 部署到 GitHub Actions 全自动发布，每一步给出可直接复制的命令，并解释背后的原理</description><pubDate>Mon, 21 Sep 2026 01:24:17 GMT</pubDate><content:encoded>&lt;p&gt;这篇文章把我从零搭建这个博客的&lt;strong&gt;全过程&lt;/strong&gt;记录下来：每一步怎么操作、用什么命令、为什么这么做、踩过哪些坑。照着做就能复现出一模一样的系统，也当作我自己的运维手册。&lt;/p&gt;
&lt;p&gt;日常使用（写文章、发动态）在&lt;a href=&quot;/posts/2/&quot;&gt;《博客日常使用指南》&lt;/a&gt;，这篇只讲「怎么把站搭起来、怎么自动化」。&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;全文涉及敏感信息的地方（服务器 IP、密码等）都用 &lt;code&gt;&amp;lt;占位符&amp;gt;&lt;/code&gt; 代替，实际操作时替换成你自己的。&lt;/p&gt;&lt;/blockquote&gt;
&lt;section&gt;&lt;h2&gt;一、整体规划&lt;a href=&quot;#一整体规划&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;先想清楚要什么，再动手：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;静态博客&lt;/strong&gt;：文章是 Markdown，构建后是纯 HTML。没有数据库、没有后端、没有登录入口，黑不掉也慢不了，维护成本约等于零；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;国内云服务器托管&lt;/strong&gt;：文章推到 GitHub 仓库，GitHub Actions 构建完成后，服务器自动拉取构建产物上线，服务器只负责 Nginx 托管静态文件；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全自动发布&lt;/strong&gt;：&lt;code&gt;git push&lt;/code&gt; 之后构建、分发、上线全部自动，我只负责写文章。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;技术栈一句话：&lt;strong&gt;Astro + Firefly 主题 + GitHub Actions + Git + Nginx&lt;/strong&gt;。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;二、环境准备&lt;a href=&quot;#二环境准备&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;我的本地环境是 Windows + PowerShell。需要装三样东西，每样都有验证方法。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;1. Node.js（≥ 22）&lt;a href=&quot;#1-nodejs-22&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;去 &lt;a href=&quot;https://nodejs.org/&quot; target=&quot;_blank&quot;&gt;Node.js 官网&lt;/a&gt; 下载 LTS 安装包，一路下一步。装完&lt;strong&gt;重开一个终端&lt;/strong&gt;验证：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;node &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;v   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;# 输出 v22.x.x 即可&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;npm &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;老版本不够就先卸载再装新版，Node 多版本共存工具（nvm-windows）是进阶玩法，入门没必要。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2. pnpm（≥ 11）&lt;a href=&quot;#2-pnpm-11&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Node 自带 npm，用它装 pnpm：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;npm install &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;g pnpm&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;pnpm &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;v&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;坑点：如果项目用 &lt;code&gt;.npmrc&lt;/code&gt; 锁定了 pnpm 版本，直接运行 &lt;code&gt;pnpm&lt;/code&gt; 时它会自动切换到项目要求的版本，不用手动降级。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3. Git&lt;a href=&quot;#3-git&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;去 &lt;a href=&quot;https://git-scm.com/&quot; target=&quot;_blank&quot;&gt;git-scm.com&lt;/a&gt; 下载安装，安装选项保持默认即可。第一次用 Git 需要配置身份（每条提交记录的署名）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user.name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;你的用户名&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--global&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user.email&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;你的邮箱&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;坑点：Windows 下有些终端装完 Git 后找不到命令，重开一个终端窗口就好；还不行就把 Git 的 &lt;code&gt;cmd&lt;/code&gt; 目录加进 &lt;code&gt;PATH&lt;/code&gt; 环境变量。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;三、初始化项目&lt;a href=&quot;#三初始化项目&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;1. 拿到主题代码&lt;a href=&quot;#1-拿到主题代码&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;在 GitHub 上把 &lt;a href=&quot;https://github.com/CuteLeaf/Firefly&quot; target=&quot;_blank&quot;&gt;Firefly 主题仓库&lt;/a&gt; Fork（或右上角 Use this template）到自己账号下，然后克隆到本地：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clone&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;https://github.com/&amp;lt;你的用户名&amp;gt;/&amp;lt;仓库名&amp;gt;.git&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;仓库名&amp;gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;建议直接在 GitHub 上把仓库设为 &lt;strong&gt;Private&lt;/strong&gt;——文章草稿、服务器配置这些不需要公开。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2. 安装依赖并启动&lt;a href=&quot;#2-安装依赖并启动&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# 第一次运行，下载所有依赖，可能要几分钟&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dev&lt;/span&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# 启动本地开发服务器&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;浏览器打开 &lt;code&gt;http://localhost:4321&lt;/code&gt;，看到主题默认首页就成功了。dev server 支持热更新：改代码保存，页面自动刷新。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3. 认识目录结构&lt;a href=&quot;#3-认识目录结构&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;几千个文件里，你日常只需要碰这几处：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;├── astro.config.mjs        # Astro 全局配置（构建、插件、trailingSlash 等）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;├── src/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│   ├── config/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│   │   ├── index.ts        # ★ 站点所有配置都在这一个文件（按模块分区编号）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│   │   └── FooterConfig.html  # 页脚文字（唯一的例外）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│   ├── content/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│   │   ├── posts/          # ★ 文章（Markdown，按分类建文件夹）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│   │   ├── dynamic/        # 动态（短内容随手记）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│   │   └── spec/           # 关于页/友链页等固定页面&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;│   └── styles/             # 样式微调&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;└── public/                 # 纯静态资源（favicon、壁纸、相册图片）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;记住一条：&lt;strong&gt;想改任何功能和文案，先打开 &lt;code&gt;src/config/index.ts&lt;/code&gt; 找编号注释，九成能找到&lt;/strong&gt;。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;四、站点配置&lt;a href=&quot;#四站点配置&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;配置都是改 TypeScript 文件，保存即时生效。所有配置集中在 &lt;code&gt;src/config/index.ts&lt;/code&gt;，按模块编号分区。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;1. 站点基本信息（① 站点基础模块）&lt;a href=&quot;#1-站点基本信息-站点基础模块&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;// 站点名称：站点信息、导航栏标题、首页横幅共用这一处&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SITE_TITLE&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;你的博客名&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;siteConfig&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SiteConfig&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;SITE_TITLE&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;subtitle&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;一句话简介&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;site_url&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://你的域名.com&quot;&lt;/span&gt;&lt;span&gt;,   &lt;/span&gt;&lt;span&gt;// 有域名填域名，暂时没有可以先填 IP 方案&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;themeColor&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;hue&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;220&lt;/span&gt;&lt;span&gt;,          &lt;/span&gt;&lt;span&gt;// 0-360 色相，一个数字全站变色（220 是蓝色）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;defaultMode&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;light&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;},&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;siteStartDate&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;2026-09-20&quot;&lt;/span&gt;&lt;span&gt;,       &lt;/span&gt;&lt;span&gt;// 建站日期，页脚的运行天数从这里算&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;pages&lt;/code&gt; 字段是各功能页的总闸，用不到的设为 &lt;code&gt;false&lt;/code&gt;——导航、sitemap 里会自动消失，不用到处删链接：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pages&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;resolvePageToggles&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;friends&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,     &lt;/span&gt;&lt;span&gt;// 友链&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;dynamic&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,     &lt;/span&gt;&lt;span&gt;// 动态&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;gallery&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,     &lt;/span&gt;&lt;span&gt;// 相册&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;booknav&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,     &lt;/span&gt;&lt;span&gt;// 书签导航&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;bilibili&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,    &lt;/span&gt;&lt;span&gt;// 追番&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2. 作者信息（② 用户资料模块）&lt;a href=&quot;#2-作者信息-用户资料模块&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;头像图片放进 &lt;code&gt;src/assets/images/&lt;/code&gt;，然后在配置里引用：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;profileConfig&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ProfileConfig&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;avatar&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;assets/images/avatar.png&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;你的昵称&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;bio&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;一句话介绍自己&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;links&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// 图标用 Iconify 格式，showName: false 只显示图标&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;QQ群&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;icon&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;fa7-brands:qq&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;https://qm.qq.com/xxx&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;showName&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;{ &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;Email&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;icon&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;fa7-solid:envelope&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;mailto:你的邮箱&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;};&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;这份 &lt;code&gt;links&lt;/code&gt; 会同时用在侧边栏资料卡和首页横幅上，只改这一处。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3. 其他常用模块&lt;a href=&quot;#3-其他常用模块&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;配置文件里每个模块都有中文注释，改之前读一遍注释就够了。我启用了几个值得一说的：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AI 总结（⑩）&lt;/strong&gt;：构建时调用免费 AI 接口给每篇文章生成摘要，无需密钥，参数都在注释里；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;随机封面（⑪）&lt;/strong&gt;：文章不写 &lt;code&gt;image&lt;/code&gt; 字段就自动配随机二次元封面，图源是按顺序备用的公共 API 列表；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;追番页（Bilibili UID）&lt;/strong&gt;：&lt;code&gt;siteConfig&lt;/code&gt; 的 &lt;code&gt;bilibili.uid&lt;/code&gt; 填自己的 B 站 UID，追番进度自动同步。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;4. 写一篇文章试手&lt;a href=&quot;#4-写一篇文章试手&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;文章就是 &lt;code&gt;src/content/posts/&lt;/code&gt; 下的 Markdown 文件，头部 frontmatter 是元信息：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;我的第一篇测试文章&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;published&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2026-09-21&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;摘要，会显示在列表页&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;tags&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;标签1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;标签2&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;category&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;技术&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;draft&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;slug&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;my-first-post&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# 不填则用「文件夹+文件名」当 URL&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;---&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;正文用 Markdown 写……&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;保存后回浏览器看效果。完整的逐字段说明、正文语法、发布流程都在&lt;a href=&quot;/posts/2/&quot;&gt;使用指南&lt;/a&gt;里，这里不重复。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;五、踩坑记录&lt;a href=&quot;#五踩坑记录&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;搭的过程中遇到的三个印象最深的坑，写下来防止后人（和未来的我）重踩。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;坑 1：不带斜杠的 URL 404&lt;a href=&quot;#坑-1不带斜杠的-url-404&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Astro 配置了 &lt;code&gt;trailingSlash: &quot;always&quot;&lt;/code&gt; 后，&lt;code&gt;/posts/xxx&lt;/code&gt;（无斜杠）直接 404。而生产环境静态托管对无斜杠请求一般会自动 301 补斜杠，本地却不行，行为不一致。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;：把配置改成 &lt;code&gt;&quot;ignore&quot;&lt;/code&gt;，再加一个中间件统一重定向：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;src/middleware.ts&lt;/span&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;defineMiddleware&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;astro:middleware&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;onRequest&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;defineMiddleware&lt;/span&gt;&lt;span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;parsed&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;URL&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;parsed&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;last&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;split&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;pop&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;// 跳过：根路径 / 已带斜杠 / 带扩展名的文件 / Astro 与 Vite 内部端点&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;endsWith&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;last&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;includes&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;.&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;startsWith&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/@&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;startsWith&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/_&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;next&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;context&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;redirect&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;pathname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;parsed&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;/span&gt;&lt;span&gt;search&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;meta&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;DEV&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;302&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;301&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;});&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;坑中坑：重定向状态码在开发环境要用 302，别用 301——301 会被浏览器永久缓存，万一重定向规则写错了，之后怎么改都跳旧地址，只能清缓存。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;坑 2：主题色改了但浏览器没变&lt;a href=&quot;#坑-2主题色改了但浏览器没变&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;改了色相值刷新还是旧颜色，其实是浏览器 &lt;code&gt;localStorage&lt;/code&gt; 缓存了旧值（主题把用户的色相选择存在里面）。清站点数据或 &lt;code&gt;Ctrl+F5&lt;/code&gt; 强刷即可。不是代码的问题。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;坑 3：dev server 莫名崩溃&lt;a href=&quot;#坑-3dev-server-莫名崩溃&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Windows 上有个系统临时文件在盘符根目录，被 Vite 的文件监听扫到时会抛 &lt;code&gt;EINVAL&lt;/code&gt; 直接把 dev server 搞崩。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;解决&lt;/strong&gt;两步走：在 &lt;code&gt;astro.config.mjs&lt;/code&gt; 里把该文件加入 &lt;code&gt;server.watch.ignored&lt;/code&gt; 忽略列表（治本），再写个循环脚本崩溃后自动重启（兜底）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;while&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;$true&lt;/span&gt;&lt;span&gt;) { pnpm dev; &lt;/span&gt;&lt;span&gt;Start-Sleep&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt;Seconds &lt;/span&gt;&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;教训：玄学崩溃先看完整报错栈，定位到具体文件路径，别急着怀疑自己的代码。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;六、服务器选购与初始化&lt;a href=&quot;#六服务器选购与初始化&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;1. 配置选择&lt;a href=&quot;#1-配置选择&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;静态博客对配置要求极低，我选的是国内云厂商的入门款：&lt;/p&gt;

&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;项&lt;/th&gt;&lt;th&gt;配置&lt;/th&gt;&lt;th&gt;说明&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;CPU/内存&lt;/td&gt;&lt;td&gt;2核 2G&lt;/td&gt;&lt;td&gt;纯静态托管绰绰有余&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;带宽&lt;/td&gt;&lt;td&gt;固定 3 Mbps&lt;/td&gt;&lt;td&gt;个人博客流量足够，以后可套 CDN&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;系统盘&lt;/td&gt;&lt;td&gt;40G SSD&lt;/td&gt;&lt;td&gt;用不满&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;系统&lt;/td&gt;&lt;td&gt;Ubuntu 26.04 LTS&lt;/td&gt;&lt;td&gt;生态最省心&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;价格&lt;/td&gt;&lt;td&gt;百元级/年&lt;/td&gt;&lt;td&gt;新人活动价&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：国内服务器绑定域名需要 ICP 备案，备案期间可以先用 IP 访问调试（备案只管域名，IP 直连不受影响）。&lt;/p&gt;&lt;p&gt;买完第一件事：在控制台&lt;strong&gt;重置服务器密码&lt;/strong&gt;，记下来，下面 SSH 要用。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2. 放行端口&lt;a href=&quot;#2-放行端口&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;云服务器默认只开 SSH(22)。去控制台 &lt;strong&gt;安全组 → 入方向 → 添加规则&lt;/strong&gt;，放行：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;80/80&lt;/code&gt;（HTTP）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;443/443&lt;/code&gt;（HTTPS，以后用）&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;协议 TCP，源 &lt;code&gt;0.0.0.0/0&lt;/code&gt;。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;坑点：服务器内装好一切却访问不了，八成是安全组没放行——在服务器里 curl 是通的，外面就是连不上。系统自带的防火墙（ufw）一般是关闭的，问题基本都出在云平台的安全组这一层。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3. SSH 免密登录&lt;a href=&quot;#3-ssh-免密登录&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;每次输密码太麻烦，配一下密钥登录：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 本地生成密钥对（一路回车）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ssh-keygen&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ed25519&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 把公钥装到服务器上（这一步需要输一次密码）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;用户名&amp;gt;@&amp;lt;服务器IP&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;mkdir -p ~/.ssh &amp;amp;&amp;amp; cat &amp;gt;&amp;gt; ~/.ssh/authorized_keys &amp;amp;&amp;amp; chmod 600 ~/.ssh/authorized_keys&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.ssh/id_ed25519.pub&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 测试：不再要密码即成功&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;用户名&amp;gt;@&amp;lt;服务器IP&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;echo ok&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;原理：登录时服务器用你的公钥验证客户端持有的私钥，密码不再参与。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;七、首次部署&lt;a href=&quot;#七首次部署&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;section&gt;&lt;h3&gt;1. 本地构建&lt;a href=&quot;#1-本地构建&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;pnpm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;build&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;产物在 &lt;code&gt;dist/&lt;/code&gt; 目录：每个页面是 &lt;code&gt;目录名/index.html&lt;/code&gt;，带哈希的 CSS/JS 在 &lt;code&gt;_astro/&lt;/code&gt; 下。可以本地先看看产物长什么样。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2. 服务器装 Nginx 并上传&lt;a href=&quot;#2-服务器装-nginx-并上传&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 服务器上安装 Nginx 并建站点目录&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;用户名&amp;gt;@&amp;lt;服务器IP&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;apt update &amp;amp;&amp;amp; apt install -y nginx &amp;amp;&amp;amp; mkdir -p /var/www/blog&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 本地打包上传（比逐个文件 scp 快得多）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;tar&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-czf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dist.tar.gz&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dist&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;scp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dist.tar.gz&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;用户名&amp;gt;@&amp;lt;服务器IP&amp;gt;:/tmp/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 服务器上解压&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;用户名&amp;gt;@&amp;lt;服务器IP&amp;gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;tar -xzf /tmp/dist.tar.gz -C /var/www/blog --strip-components=1&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3. Nginx 站点配置&lt;a href=&quot;#3-nginx-站点配置&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;在服务器上创建 &lt;code&gt;/etc/nginx/sites-available/blog&lt;/code&gt;：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;listen &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default_server&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server_name &lt;/span&gt;&lt;span&gt;_;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;/var/www/blog;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;index &lt;/span&gt;&lt;span&gt;index.html;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;charset &lt;/span&gt;&lt;span&gt;utf-8;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;error_page &lt;/span&gt;&lt;span&gt;404&lt;/span&gt;&lt;span&gt; /404.html;      &lt;/span&gt;&lt;span&gt;# Astro 会构建出 404.html&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gzip &lt;/span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;;                        &lt;/span&gt;&lt;span&gt;# 文本资源压缩，HTML/CSS/JS 体积减 70% 左右&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;gzip_types &lt;/span&gt;&lt;span&gt;text/css application/javascript application/json image/svg+xml;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# 带哈希的静态资源：内容变了文件名就变，可以放心长缓存&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; ^~ &lt;/span&gt;&lt;span&gt;/_astro/ &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;expires &lt;/span&gt;&lt;span&gt;30d&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;Cache-Control &lt;/span&gt;&lt;span&gt;&quot;public, immutable&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;启用并重载：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ln&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-sf&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/nginx/sites-available/blog&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/nginx/sites-enabled/blog&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;rm&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/nginx/sites-enabled/default&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# 移除默认站点，避免抢占 80 端口&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;nginx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt;                                  &lt;/span&gt;&lt;span&gt;# 语法检查，必须显示 ok&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reload&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nginx&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;浏览器打开 &lt;code&gt;http://&amp;lt;服务器IP&amp;gt;&lt;/code&gt;，应该就能看到博客了。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Nginx 的工作方式&lt;/strong&gt;：收到请求后按 &lt;code&gt;root&lt;/code&gt; 找文件——&lt;code&gt;/posts/xxx/&lt;/code&gt; 对应 &lt;code&gt;/var/www/blog/posts/xxx/index.html&lt;/code&gt;；请求目录但没带斜杠时自动 301 补斜杠；找不到文件就走 &lt;code&gt;error_page&lt;/code&gt; 的 404 页。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;八、全自动发布：原理详解&lt;a href=&quot;#八全自动发布原理详解&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;这是整套系统最核心的部分，值得讲透。&lt;/p&gt;&lt;section&gt;&lt;h3&gt;1. Git 基础：一次 push 背后发生了什么&lt;a href=&quot;#1-git-基础一次-push-背后发生了什么&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;先建立心智模型。Git 把文件的管理划分成几个「区域」，一次 push 的旅程是这样的：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;工作区                本地仓库                     远程仓库&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;(你编辑的文件)   git add→  暂存区  git commit→  (历史记录)  git push→  GitHub&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;src/content/            (准备打包)            (.git 目录)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;工作区&lt;/strong&gt;：就是硬盘上看得见的文件，你用编辑器改 Markdown 就是在改工作区；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;暂存区（stage）&lt;/strong&gt;：下次要打包进提交的文件清单。改动必须先经过它；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;本地仓库&lt;/strong&gt;：&lt;code&gt;git commit&lt;/code&gt; 会在本机生成一个不可变的快照（提交），包含改动了什么、谁改的、什么时候；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;远程仓库&lt;/strong&gt;：GitHub 上的仓库。&lt;code&gt;git push&lt;/code&gt; 把本地的新提交上传过去。&lt;/li&gt;
&lt;/ul&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;2. 日常发文完整命令流&lt;a href=&quot;#2-日常发文完整命令流&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;以新写一篇文章为例，完整走一遍：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# ① 先看看当前状态（哪些文件改了、哪些是新增的、哪些还没跟踪）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# ② 把改动加入暂存区&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;src/content/posts/my-new-post.md&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# 精确添加单个文件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 或 git add -A                            # 添加全部改动（含删除、改名）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# ③ 再看一眼：改动已进入暂存区（绿色），确认没有误加的东西&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# ④ 提交：把暂存区内容固化为一个本地快照，-m 后面是提交说明&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;post: 新文章标题&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# ⑤ 推送到 GitHub&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;master&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;逐个解释 &lt;code&gt;git push origin master&lt;/code&gt; 里的两个词：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;origin&lt;/code&gt; 是&lt;strong&gt;远程仓库的别名&lt;/strong&gt;，&lt;code&gt;git clone&lt;/code&gt; 时自动指向你克隆来的那个 GitHub 地址（用 &lt;code&gt;git remote -v&lt;/code&gt; 查看）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;master&lt;/code&gt; 是&lt;strong&gt;分支名&lt;/strong&gt;，可以理解成仓库的主时间线。这条命令的意思是：把本地 master 分支上的新提交，同步到 GitHub 的 master 分支。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;第一次 push 时 Git 会要求登录 GitHub。HTTPS 方式下 Windows 的凭证管理器会弹出授权窗口，登录一次之后凭证会被记住，以后不用再输。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;3. 提交前后常用的辅助命令&lt;a href=&quot;#3-提交前后常用的辅助命令&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;diff&lt;/span&gt;&lt;span&gt;                 &lt;/span&gt;&lt;span&gt;# 看工作区改了什么（还没 add 的）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;diff&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--staged&lt;/span&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;# 看暂存区里有什么（已 add 未 commit 的）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--oneline&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-5&lt;/span&gt;&lt;span&gt;     &lt;/span&gt;&lt;span&gt;# 最近 5 条提交历史，一行一条&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;show&lt;/span&gt;&lt;span&gt;                 &lt;/span&gt;&lt;span&gt;# 看最新一次提交的完整改动内容&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;提交说明（&lt;code&gt;-m&lt;/code&gt; 里的内容）建议写清楚这次改了什么，比如 &lt;code&gt;post: 新文章标题&lt;/code&gt;、&lt;code&gt;fix: 修复导航样式&lt;/code&gt;。以后翻历史时，这些说明就是你的操作日志。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;4. 出错了怎么办&lt;a href=&quot;#4-出错了怎么办&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 情况一：忘了 add 就 commit，或者提交说明写错了&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--amend&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;正确的说明&quot;&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# 修补最近一次提交（仅限还没 push）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 情况二：改动不想要了，想恢复到上次提交的样子&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;checkout&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;文件名&lt;/span&gt;&lt;span&gt;                &lt;/span&gt;&lt;span&gt;# 丢弃某文件未提交的改动（慎用）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 情况三：push 被拒绝（rejected），通常是 GitHub 上有你本地没有的提交&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pull&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--rebase&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;master&lt;/span&gt;&lt;span&gt;      &lt;/span&gt;&lt;span&gt;# 先把远端的新提交拉下来合并，再重新 push&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 情况四：已经 push 的提交想撤销&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;revert&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;提交ID&amp;gt;&lt;/span&gt;&lt;span&gt;                  &lt;/span&gt;&lt;span&gt;# 生成一个&quot;反向提交&quot;抵消它，安全且可追溯&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;原则：&lt;strong&gt;只用安全的撤销手段&lt;/strong&gt;（&lt;code&gt;revert&lt;/code&gt;、新建提交覆盖）。&lt;code&gt;git push --force&lt;/code&gt; 之类会重写历史的强操作，个人博客偶尔可救急，但一定要清楚自己在干什么。&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;到这里，代码已经在 GitHub 上了。但 &lt;code&gt;git push&lt;/code&gt; 本身&lt;strong&gt;只负责把代码传到仓库&lt;/strong&gt;，它不知道任何部署的事。真正触发自动部署的，是下面这个机制。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;5. GitHub Actions：构建并发布到 dist 分支&lt;a href=&quot;#5-github-actions构建并发布到-dist-分支&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;仓库里有三个 workflow 文件，各司其职：&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.github/workflows/deploy.yml&lt;/code&gt; — &lt;strong&gt;构建站点并把产物发布到仓库的 &lt;code&gt;dist&lt;/code&gt; 分支&lt;/strong&gt;（核心）；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.github/workflows/build.yml&lt;/code&gt; — 质量检查：Node 22/23 双版本跑 &lt;code&gt;astro check&lt;/code&gt;，防手残；&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.github/workflows/biome.yml&lt;/code&gt; — 代码风格检查（biome）。&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;deploy.yml 的开头声明了触发条件：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;branches&lt;/span&gt;&lt;span&gt;: [ &lt;/span&gt;&lt;span&gt;master&lt;/span&gt;&lt;span&gt; ]   &lt;/span&gt;&lt;span&gt;# master 分支有新 push 就触发&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;git push&lt;/code&gt; 后 GitHub 收到新提交，匹配到 workflow 就起一个全新的临时虚拟机（runner）按 yaml 逐步执行：安装依赖 → &lt;code&gt;pnpm build&lt;/code&gt; → 把 &lt;code&gt;dist/&lt;/code&gt; 目录整体提交到仓库的 &lt;code&gt;dist&lt;/code&gt; 分支。&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;为什么要经过 &lt;code&gt;dist&lt;/code&gt; 分支中转？服务器只有 2G 内存，每次都在服务器上构建又慢又吃力；而且如果让 Actions 直接 SSH 登录服务器部署，每次构建机的境外 IP 登录都会触发阿里云的「异地登录」告警。改成「GitHub 构建 → dist 分支 → 服务器拉取」，构建交给强劲的 CI 机器，服务器只做拉取和同步，全程没有任何境外登录。&lt;/p&gt;&lt;p&gt;附带的好处：&lt;code&gt;dist&lt;/code&gt; 分支的提交历史就是部署历史，哪次构建改了什么一目了然；出问题把服务器回退到旧的 dist 提交即可回滚。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;6. 服务器端拉取部署&lt;a href=&quot;#6-服务器端拉取部署&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;deploy.yml 里「发布 dist 分支」这一步的写法：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Publish dist/ to dist branch&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;git config user.name &quot;github-actions[bot]&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;git config user.email &quot;41898282+github-actions[bot]@users.noreply.github.com&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;git fetch origin dist --depth 1 || echo &quot;首次构建：dist 分支还不存在&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if git rev-parse -q --verify origin/dist &amp;gt;/dev/null; then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;git worktree add /tmp/publish origin/dist&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;git worktree add --orphan -b dist /tmp/publish&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;rsync -a --delete --exclude .git dist/ /tmp/publish/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;cd /tmp/publish&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;git add -A&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;if git diff --cached --quiet; then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;echo &quot;构建产物无变化，跳过推送&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;else&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;git commit -m &quot;build: ${GITHUB_SHA::7}&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;      &lt;/span&gt;&lt;/span&gt;&lt;span&gt;git push origin HEAD:dist&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;原理：&lt;code&gt;git worktree&lt;/code&gt; 在不碰主工作区的前提下开一个临时工作目录指向 &lt;code&gt;dist&lt;/code&gt; 分支，用本次构建产物整体替换后提交推送。推送用的 &lt;code&gt;GITHUB_TOKEN&lt;/code&gt; 由 GitHub 自动注入，&lt;strong&gt;只对本仓库有效且永不过期&lt;/strong&gt;，不需要在 Secrets 里配置任何密钥。&lt;/p&gt;&lt;p&gt;服务器这边，核心是拉取脚本 &lt;code&gt;scripts/deploy.sh&lt;/code&gt;（已放在仓库里）：发现 &lt;code&gt;dist&lt;/code&gt; 分支有新提交，就同步到 Nginx 站点目录——注意它&lt;strong&gt;只做拉取和复制，不构建&lt;/strong&gt;，几秒内完成：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#!/usr/bin/env bash&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;set&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-euo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipefail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;SITE_DIR&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/opt/site&quot;&lt;/span&gt;&lt;span&gt;          &lt;/span&gt;&lt;span&gt;# dist 分支的克隆（构建产物）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;DEPLOY_DIR&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;/var/www/blog&quot;&lt;/span&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Nginx 站点目录&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;BRANCH&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;dist&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;() { &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;[$(&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt; &apos;+%F %T&apos;)] &lt;/span&gt;&lt;span&gt;$*&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;; }&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 文件锁：上一轮还没跑完就跳过本轮&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt; 9&amp;gt;&lt;/span&gt;&lt;span&gt;/tmp/blog-deploy.lock&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;flock&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-n&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;9&lt;/span&gt;&lt;span&gt; || &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$SITE_DIR&lt;/span&gt;&lt;span&gt;/.git&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;站点产物仓库不存在，请先执行：git clone -b dist --single-branch git@github.com:Carlinhosjoga/blog.git &lt;/span&gt;&lt;span&gt;$SITE_DIR&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;cd&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$SITE_DIR&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;origin&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$BRANCH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;LOCAL&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rev-parse&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;HEAD&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;REMOTE&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rev-parse&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;origin/&lt;/span&gt;&lt;span&gt;$BRANCH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$LOCAL&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$REMOTE&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;没有新构建产物，跳过&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;发现新构建 &lt;/span&gt;&lt;span&gt;$LOCAL&lt;/span&gt;&lt;span&gt; -&amp;gt; &lt;/span&gt;&lt;span&gt;$REMOTE&lt;/span&gt;&lt;span&gt;，同步上线&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;reset&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--hard&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;origin/&lt;/span&gt;&lt;span&gt;$BRANCH&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;rsync&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-az&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--delete&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--exclude&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$SITE_DIR&lt;/span&gt;&lt;span&gt;&quot;/&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$DEPLOY_DIR&lt;/span&gt;&lt;span&gt;/&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;部署完成&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;安全性三件事：&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;只读拉取&lt;/strong&gt;：服务器用一把「只读 Deploy Key」访问私有仓库，即使密钥泄露，别人也只能读，推不上去、删不掉仓库；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;构建在 GitHub 完成&lt;/strong&gt;：服务器上不需要 Node 和 pnpm，也不怕构建失败——dist 分支没更新，服务器什么都不做，线上永远是最近一次&lt;strong&gt;成功&lt;/strong&gt;构建的版本；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;rsync —delete&lt;/strong&gt;：站点目录与构建产物严格一致，不会残留已删除页面的旧文件。&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;服务器初始化手把手&lt;/strong&gt;（只需做一次，因为不构建，比在服务器上装 Node 的方案轻得多）：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# ① 装 git 和 rsync（Ubuntu 一般已自带）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-y&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;rsync&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# ② 生成只读拉取密钥&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ssh-keygen&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ed25519&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.ssh/blog_pull_key&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-N&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-C&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;blog-server-pull&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;cat&lt;/span&gt;&lt;span&gt; &amp;gt;&amp;gt; &lt;/span&gt;&lt;span&gt;~/.ssh/config&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;&apos;EOF&apos;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Host&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;IdentityFile&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.ssh/blog_pull_key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;IdentitiesOnly&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yes&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;EOF&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;cat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.ssh/blog_pull_key.pub&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 把输出的公钥添加到 GitHub 仓库：Settings → Deploy keys → Add deploy key&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# （不要勾选 Allow write access）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# ③ 克隆两个仓库：/opt/blog 放源码和脚本，/opt/site 放构建产物&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clone&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git@github.com:Carlinhosjoga/blog.git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/blog&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;clone&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-b&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;dist&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--single-branch&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;git@github.com:Carlinhosjoga/blog.git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/site&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# ④ 手动跑一次，确认能同步上线&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;bash&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/opt/blog/scripts/deploy.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# ⑤ 加入定时任务：每 5 分钟兜底检查一次&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;crontab&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-l&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;2&amp;gt;/dev/null&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;*/5 * * * * bash /opt/blog/scripts/deploy.sh &amp;gt;&amp;gt; /var/log/blog-deploy.log 2&amp;gt;&amp;amp;1&quot;&lt;/span&gt;&lt;span&gt;) | &lt;/span&gt;&lt;span&gt;crontab&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;坑点：如果 &lt;code&gt;git clone&lt;/code&gt; 或 &lt;code&gt;fetch&lt;/code&gt; 报 &lt;code&gt;Permission denied (publickey)&lt;/code&gt;，检查三处——公钥是否已添加为 Deploy key、&lt;code&gt;~/.ssh/config&lt;/code&gt; 是否写对了 IdentityFile、&lt;code&gt;blog_pull_key&lt;/code&gt; 权限是否是 600。另外国内服务器访问 GitHub 偶尔网络抖动，多试两次或换个时间段即可。&lt;/p&gt;&lt;/blockquote&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;7. Webhook：让部署从「分钟级」变「秒级」&lt;a href=&quot;#7-webhook让部署从分钟级变秒级&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;cron 兜底意味着最多等 5 分钟才上线。想秒级触发，给服务器加一个 webhook 接收器：GitHub 在 &lt;code&gt;dist&lt;/code&gt; 分支更新时主动 POST 一个 HTTP 请求给服务器——这只是访问一个网址，&lt;strong&gt;不是登录&lt;/strong&gt;，不会触发任何异地登录告警。服务器收到请求立刻执行部署脚本。&lt;/p&gt;&lt;p&gt;接收器是几十行 Python（放在服务器 &lt;code&gt;/opt/blog-webhook/hook.py&lt;/code&gt;），只监听本机回环地址，由 Nginx 反代对外：&lt;/p&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;#!/usr/bin/env python3&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; hmac, hashlib, json, subprocess&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; http.server &lt;/span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; BaseHTTPRequestHandler, ThreadingHTTPServer&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;TOKEN&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&amp;lt;URL令牌&amp;gt;&quot;&lt;/span&gt;&lt;span&gt;       &lt;/span&gt;&lt;span&gt;# 与 Nginx 反代路径一致&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;SECRET&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;&amp;lt;webhook密钥&amp;gt;&quot;&lt;/span&gt;&lt;span&gt;   &lt;/span&gt;&lt;span&gt;# 与 GitHub webhook 的 Secret 一致&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;DEPLOY&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;/opt/blog/scripts/deploy.sh&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;LOG&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;/var/log/blog-deploy.log&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;Handler&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;BaseHTTPRequestHandler&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;do_POST&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;path &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;.path&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; path.&lt;/span&gt;&lt;span&gt;startswith&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;/webhook/&quot;&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;14&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;            &lt;/span&gt;&lt;/span&gt;&lt;span&gt;path &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; path[&lt;/span&gt;&lt;span&gt;len&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;/webhook&quot;&lt;/span&gt;&lt;span&gt;):]  &lt;/span&gt;&lt;span&gt;# Nginx 反代会带上 /webhook 前缀&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;15&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; path &lt;/span&gt;&lt;span&gt;!=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;/&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;TOKEN&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;16&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;send_response&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;404&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;end_headers&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;17&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;body &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;&lt;span&gt;.rfile.&lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;int&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;&lt;span&gt;.headers.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;Content-Length&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;)))&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;18&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;expect &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;sha256=&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;+&lt;/span&gt;&lt;span&gt; hmac.&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;SECRET&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;encode&lt;/span&gt;&lt;span&gt;(), body, hashlib.sha256).&lt;/span&gt;&lt;span&gt;hexdigest&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;19&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;not&lt;/span&gt;&lt;span&gt;&lt;span&gt; hmac.&lt;/span&gt;&lt;span&gt;compare_digest&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;&lt;span&gt;.headers.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;X-Hub-Signature-256&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;), expect):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;20&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;send_response&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;403&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;end_headers&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;21&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;event &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;&lt;span&gt;.headers.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;X-GitHub-Event&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;22&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; event &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;ping&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;23&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;send_response&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;end_headers&lt;/span&gt;&lt;span&gt;(); &lt;/span&gt;&lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;24&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt;&lt;span&gt; event &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;push&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;and&lt;/span&gt;&lt;span&gt;&lt;span&gt; json.&lt;/span&gt;&lt;span&gt;loads&lt;/span&gt;&lt;span&gt;(body).&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;ref&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;==&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;refs/heads/dist&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;25&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;            &lt;/span&gt;&lt;span&gt;with&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;open&lt;/span&gt;&lt;span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;LOG&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;ab&quot;&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; lf:&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;26&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;                &lt;/span&gt;&lt;/span&gt;&lt;span&gt;subprocess.&lt;/span&gt;&lt;span&gt;Popen&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;span&gt;&quot;bash&quot;&lt;/span&gt;&lt;span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;DEPLOY&lt;/span&gt;&lt;span&gt;], &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stdout&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;lf, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;stderr&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;lf, &lt;/span&gt;&lt;/span&gt;&lt;span&gt;start_new_session&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;True&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;27&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;send_response&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;&lt;span&gt;204&lt;/span&gt;&lt;span&gt;); &lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;end_headers&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;28&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;29&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;def&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;log_message&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;self&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt;args&lt;/span&gt;&lt;span&gt;):&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;30&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;pass&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;&lt;span&gt;# 静默访问日志&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;31&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;32&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;ThreadingHTTPServer&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;/span&gt;&lt;span&gt;&quot;127.0.0.1&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;8787&lt;/span&gt;&lt;span&gt;&lt;span&gt;), Handler).&lt;/span&gt;&lt;span&gt;serve_forever&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span&gt;展开&lt;/span&gt;&lt;span&gt;收起&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;安全校验两层：URL 路径里有随机令牌（外人猜不到入口），请求体用 GitHub webhook Secret 做 HMAC-SHA256 签名验证（伪造请求直接 403）。而且只有 &lt;code&gt;dist&lt;/code&gt; 分支的 push 事件才触发部署——你平时推 master 触发的 webhook 会被它自动忽略。&lt;/p&gt;&lt;p&gt;用 systemd 常驻（&lt;code&gt;/etc/systemd/system/blog-webhook.service&lt;/code&gt;）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;[Unit]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Description&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;Blog deploy webhook receiver&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;After&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;network.target&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;[Service]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;ExecStart&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;/usr/bin/python3 /opt/blog-webhook/hook.py&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;Restart&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;always&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;RestartSec&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;[Install]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;WantedBy&lt;/span&gt;&lt;span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;multi-user.target&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;启用：&lt;code&gt;systemctl daemon-reload &amp;amp;&amp;amp; systemctl enable --now blog-webhook&lt;/code&gt;。&lt;/p&gt;&lt;p&gt;Nginx 加一段反代（&lt;code&gt;/etc/nginx/sites-available/blog&lt;/code&gt;）：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# GitHub webhook 部署通知（路径含随机令牌，仅 dist 分支事件触发部署）&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; = &lt;/span&gt;&lt;span&gt;/webhook/&amp;lt;URL令牌&amp;gt; &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;proxy_pass &lt;/span&gt;&lt;span&gt;http://127.0.0.1:8787;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;add_header &lt;/span&gt;&lt;span&gt;X-Robots-Tag &lt;/span&gt;&lt;span&gt;&quot;noindex, nofollow&quot;&lt;/span&gt;&lt;span&gt; always;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;nginx -t &amp;amp;&amp;amp; systemctl reload nginx&lt;/code&gt; 生效。最后到 GitHub 仓库：&lt;strong&gt;Settings → Webhooks → Add webhook&lt;/strong&gt;，Payload URL 填 &lt;code&gt;http://&amp;lt;服务器IP&amp;gt;/webhook/&amp;lt;URL令牌&amp;gt;&lt;/code&gt;，Content type 选 &lt;code&gt;application/json&lt;/code&gt;，Secret 填与脚本一致的密钥，触发事件保持默认（Just the push event）。&lt;/p&gt;&lt;p&gt;之后每次发布的时序：&lt;code&gt;git push&lt;/code&gt; → GitHub 构建（约 2 分钟）→ dist 分支更新 → webhook 秒达 → 服务器同步上线。全程无登录、无手工操作。&lt;/p&gt;&lt;/section&gt;&lt;section&gt;&lt;h3&gt;8. 完整数据流&lt;a href=&quot;#8-完整数据流&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;本地写文章 (src/content/posts/xxx.md)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│  git add / commit / push (master)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;GitHub 仓库 ←── build.yml / biome.yml 质量检查&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;6&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;7&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│  deploy.yml：pnpm build → 产物提交到 dist 分支&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;8&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;9&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;dist 分支更新 ──webhook 秒级通知──▶ 服务器执行 deploy.sh&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;10&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│                              │&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;11&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;│                              │ cron 每 5 分钟兜底&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;12&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;▼                              ▼&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;13&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;        &lt;/span&gt;&lt;/span&gt;&lt;span&gt;└──────▶ git 拉取 dist → rsync → /var/www/blog → Nginx → 访客&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;构建永远在 GitHub 完成&lt;/strong&gt;：服务器只做几秒钟的拉取同步，2G 内存毫无压力；产物和源码永远一一对应，出问题回滚就是把服务器 reset 到旧的 dist 提交。&lt;/p&gt;&lt;/section&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;九、日常更新流程&lt;a href=&quot;#九日常更新流程&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;配置完之后，写文章只剩三步：&lt;/p&gt;&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;Terminal window&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;1&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 1. 写文章：在 src/content/posts/ 新建 .md 文件&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;2&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 2. 提交：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;3&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-A&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;commit&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-m&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;post: 文章标题&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;4&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;# 3. 推送：&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;div&gt;5&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;p&gt;约两三分钟内自动上线：GitHub 构建（约 2 分钟）→ dist 分支更新 → webhook 秒级触发服务器同步。看进度两个渠道：GitHub 仓库 &lt;strong&gt;Actions&lt;/strong&gt; 标签页看构建日志（绿勾 = 构建成功）；服务器上 &lt;code&gt;tail -f /var/log/blog-deploy.log&lt;/code&gt; 看部署日志。&lt;/p&gt;&lt;/section&gt;
&lt;section&gt;&lt;h2&gt;十、后续计划&lt;a href=&quot;#十后续计划&quot;&gt;&lt;span&gt;#&lt;/span&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;域名备案通过后：DNS 解析到服务器，Nginx 配置 &lt;code&gt;server_name&lt;/code&gt;，用 certbot 免费签发 HTTPS 证书（自动续期）；&lt;/li&gt;
&lt;li&gt;追番页数据是构建时拉取的，计划加一个每日定时构建（即使没有新文章也刷新一次），追番进度自动更新；&lt;/li&gt;
&lt;li&gt;流量起来后在服务器前套一层国内 CDN，源站带宽压力趋近于零。&lt;/li&gt;
&lt;/ul&gt;&lt;hr /&gt;&lt;p&gt;整个搭下来最大的感受：&lt;strong&gt;静态博客的复杂度不在写页面，而在把「写完之后的事」自动化&lt;/strong&gt;。一旦发布管道跑通，写文章这件事就变得毫无心理负担——这也正是坚持输出的前提。&lt;/p&gt;&lt;/section&gt;</content:encoded></item></channel></rss>