<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Levi Blog</title><description>你若盛开，清风自来</description><link>https://levifree.com/</link><language>zh_CN</language><item><title>站长日记：在喧嚣的信息时代，给自己的数字领地做一次深度断舍离</title><link>https://levifree.com/posts/site-migration-and-digital-minimalism-diary/</link><guid isPermaLink="true">https://levifree.com/posts/site-migration-and-digital-minimalism-diary/</guid><description>整理杂乱的域名、清理弃用的云服务、重构博客技术架构；记录一次让数字生活回归清爽的断舍离心路历程</description><pubDate>Sun, 25 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;今天花了一整天的时间，终于把手里积攒了很久的数字资产彻底理顺了。看着干净清爽的域名列表、不再报错的自动化部署流水线，以及重新换上独立顶级域名 &lt;code&gt;levifree.com&lt;/code&gt; 的博客主页，心里有种很久没有体会过的轻松。&lt;/p&gt;
&lt;p&gt;写下这篇日记，记录一下最近这段时间的折腾与心境变化。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一、被“数字囤积症”绑架的这两年&lt;/h2&gt;
&lt;p&gt;不知从什么时候开始，自己也患上了典型的“开发者囤积症”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;看到打折的廉价域名，不管用不用先注册几个；&lt;/li&gt;
&lt;li&gt;看到各大云厂商推出免费或试用实例，兴冲冲地开通、配置反向代理、挂载脚本，最后却连控制台密码都忘了；&lt;/li&gt;
&lt;li&gt;博客的主题换了又换，一会儿想要二次元风格，一会儿想要极简黑白，在各种花哨的看板娘、背景特效和第三方挂件里迷失，真正花在写字和沉淀技术上的时间反而屈指可数。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;直到前几天，收到几封云服务即将扣费的提醒邮件，又看到 Google AdSense 因为页面混乱、包含版权外链和薄弱内容发来的整改通知，我才猛然惊醒：&lt;strong&gt;我的个人数字领地，已经不知不觉变成了一座堆满数字破烂的仓库。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;那些未经思考就抓取过来的动漫剧集、那些为了凑数而从官方文档搬运的速查清单，不仅无法带给读者任何真实的价值，反而像一层层厚重的灰尘，遮盖了建立这个博客最初的本心——&lt;strong&gt;记录真实的成长，分享可复现的经验&lt;/strong&gt;。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;二、大刀阔斧的断舍离与归位&lt;/h2&gt;
&lt;p&gt;从上周末开始，我决定做一次彻底的“大扫除”：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;下架所有非原创的聚合与搬运页面&lt;/strong&gt;：
毫不犹豫地关掉了之前那个纯粹搬运腾讯视频封面和简介的番剧展示页。既然自己没有时间静下心来为每一部热爱的作品写上几千字的深度长评，就不要贪图界面的热闹去充当没有灵魂的采集器。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;统一独立顶级域名&lt;/strong&gt;：
将过去在各种二级子域、动态 DNS 之间来回漂移的网址彻底收敛，正式绑定到 &lt;code&gt;levifree.com&lt;/code&gt;。同时把所有的对外联系方式、邮箱统一迁移到 &lt;code&gt;contact@levifree.com&lt;/code&gt;。一个干净、稳定、有辨识度的专属域名，才是独立博客立足的锚点。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重写那些不到千字的薄弱笔记&lt;/strong&gt;：
把过去那些只写了几行命令的旧笔记全部翻了出来。重新拉起本地测试环境，把每一条命令的执行前提、预期终端输出、报错时的排查逻辑一一补齐，让它们真正变成读者拿去就能跑通的解决方案。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;三、慢下来的力量&lt;/h2&gt;
&lt;p&gt;很多人问我：“现在 AI 生成文章这么快，大家都在刷几十秒的短视频，你还花几个小时手动写一篇博客日记，有意义吗？”&lt;/p&gt;
&lt;p&gt;对我而言，敲下这些文字的过程，本身就是一种对抗信息过载的心理疗愈。
在这个快节奏、被算法推荐牢牢掌控的时代，我们每天接收海量未经咀嚼的碎片信息，焦虑感与注意力涣散如影随形。而当你打开编辑器的纯文本窗口，没有弹窗、没有推荐、没有打赏角标，只有光标在规律地闪烁时，你的思维才能真正慢下来，重新审视自己今天究竟学到了什么、攻克了什么、思考了什么。&lt;/p&gt;
&lt;p&gt;博客不是为了迎合算法而建的流量收割机，它是你在广袤互联网上的一座灯塔。
盛开不是为了给路人看，清风自来，本就是专注自身后的水到渠成。&lt;/p&gt;
&lt;p&gt;今夜无风，代码编译通过，一切如常。&lt;/p&gt;
</content:encoded></item><item><title>折腾日记：一个周末搞定家庭轻量级工作流与自托管服务</title><link>https://levifree.com/posts/weekend-homelab-and-terminal-tinkering-diary/</link><guid isPermaLink="true">https://levifree.com/posts/weekend-homelab-and-terminal-tinkering-diary/</guid><description>翻出一台闲置的小主机，用 Docker Compose 搭建属于自己的轻量私有云与个人运维工作流，记录一次愉悦的周末折腾实录</description><pubDate>Sun, 20 Sep 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;周五晚上下班回家，翻柜子时无意中找到了去年换下来的一台小主机（N100 处理器，16G 内存，500G NVMe）。原本它被塞在角落里吃灰，插上电源按了开机键，风扇轻轻呼呼地转了起来。&lt;/p&gt;
&lt;p&gt;看着这个小方盒子，突然来了兴致：何不趁着这个周末，给自己的开发与日常学习搭建一套完全受自己掌控的家庭轻量级 Homelab 呢？&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;周六上午：底层系统安装与 SSH 秘钥加固&lt;/h2&gt;
&lt;p&gt;对于家用低功耗微型服务器，我的首选依然是 &lt;strong&gt;Ubuntu 24.04 LTS Server&lt;/strong&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;做启动盘&lt;/strong&gt;：在 Mac 上用 BalenaEtcher 烧录好 ISO 镜像，插到小主机上几分钟就装完了最小化系统；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;固定内网 IP&lt;/strong&gt;：登录路由器后台，在 DHCP 静态列表中把小主机的 MAC 地址绑定到 &lt;code&gt;192.168.1.100&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;加固 SSH 访问&lt;/strong&gt;：
既然要长期通电运行，安全基线绝不能偷懒。立刻把本地 Mac 的公钥传输过去：&lt;pre&gt;&lt;code&gt;ssh-copy-id -i ~/.ssh/id_ed25519.pub levi@192.168.1.100
&lt;/code&gt;&lt;/pre&gt;
随后登录主机，在 &lt;code&gt;/etc/ssh/sshd_config.d/safety.conf&lt;/code&gt; 中直接写入：&lt;pre&gt;&lt;code&gt;PasswordAuthentication no
PermitRootLogin no
PubkeyAuthentication yes
&lt;/code&gt;&lt;/pre&gt;
重启 sshd 守护进程。测试断开连接后重新用秘钥登录，确认密码登录被彻底封堵，这才算安下心来。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;周六下午：Docker 容器生态与网络隔离&lt;/h2&gt;
&lt;p&gt;用官方脚本一键安装上最新的 Docker Engine 和 Compose 插件，接下来就是最令人兴奋的“搭积木”时间。&lt;/p&gt;
&lt;p&gt;我给自己规划了三个基础自托管服务：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Uptime Kuma&lt;/strong&gt;：实时监控博客站点 &lt;code&gt;levifree.com&lt;/code&gt;、DNS 解析和各子服务的存活状态；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vaultwarden&lt;/strong&gt;：基于 Rust 轻量级实现的 Bitwarden 密码管理器后端，数据牢牢掌握在自己手里；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cloudflare Tunnel (cloudflared)&lt;/strong&gt;：无需公网 IP 和复杂的端口映射，直接通过 Cloudflare 边缘网络安全穿透，安全暴露需要远程访问的监控状态页面。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;把它们组织进统一的 &lt;code&gt;docker-compose.yml&lt;/code&gt;，设置独立的桥接网络（Bridge Network）进行服务隔离。当在终端敲下 &lt;code&gt;docker compose up -d&lt;/code&gt;，看到一排绿色 &lt;code&gt;Started&lt;/code&gt; 瞬间跳出时，那种由代码驱动现实世界的确定感真的太让人上瘾了。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;周日折腾小插曲：一个关于 DNS 解析的乌龙&lt;/h2&gt;
&lt;p&gt;周日上午测试远程访问时，发现状态监控页始终无法通过外网域名连接。终端 ping 域名能通，但是 curl 接口总是卡在 TLS 握手阶段。&lt;/p&gt;
&lt;p&gt;排查了整整一个半小时，排查了防火墙、检查了 Docker 的虚拟网桥路由，最后才发现罪魁祸首竟然是：&lt;strong&gt;我昨晚随手在主机的 resolv.conf 里配置了某个公共 DNS，而那个 DNS 恰好对我的内网泛域名做了解析截流！&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;把 DNS 改回本地网关和 &lt;code&gt;1.1.1.1&lt;/code&gt;，重新拉取证书，一切瞬间顺畅通行。
这大概就是折腾技术的魅力所在吧：&lt;strong&gt;故障不会平白无故发生，每一个诡异的 Bug 背后，都是一条冰冷但遵循逻辑的计算机法则。解开谜题的那一刻，所有的疲惫都会烟消云散。&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;周末总结&lt;/h2&gt;
&lt;p&gt;周日傍晚，泡了一杯热茶。小主机静静地躺在路由器旁边，整机功耗只有不到 8W，温温的，几乎听不到一点噪音。&lt;/p&gt;
&lt;p&gt;在被各类商业云服务包围的今天，亲手搭建一个不依赖任何第三方平台抽佣、数据完全归属于自己的微型节点，这种掌控感是无与伦比的。&lt;/p&gt;
&lt;p&gt;下周准备把博客的定时自动化构建脚本也写一个心跳探针接入进来。下个周末，继续折腾！&lt;/p&gt;
</content:encoded></item><item><title>思考日记：为什么在算法时代，我依然选择坚持慢写作？</title><link>https://levifree.com/posts/static-blog-content-writing-reflection-diary/</link><guid isPermaLink="true">https://levifree.com/posts/static-blog-content-writing-reflection-diary/</guid><description>在碎片化与快餐信息充斥的今天，重拾两千字长文写作的意义；聊聊独立技术博客带给我的深度心流与思维复利</description><pubDate>Sat, 15 Aug 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;夜深人静，窗外偶有夜风掠过树梢。键盘敲击的清脆声在安静的房间里回荡，这是我一天中最享受的时刻。&lt;/p&gt;
&lt;p&gt;最近和一位做前端开发的朋友聊天，他问我：“现在很多技术问题直接问大语言模型几秒钟就出答案了，你平时工作那么忙，为什么还要花几个小时甚至几天，去查官方文档、验证报错、一字一句写几千字的博客文章？”&lt;/p&gt;
&lt;p&gt;当时我笑了笑，只是随口回答了一句“习惯了”。但今天合上电脑走在回家的路上，这个问题依然在脑海里盘旋。我想借着这篇日记，认真梳理一下自己内心的真实答案。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一、输入不等于掌握，输出才是思维的编译器&lt;/h2&gt;
&lt;p&gt;在信息爆炸的今天，获取知识的门槛已经被降到了历史最低点。我们每天在社交媒体上随手点赞“收藏”，在技术群里转发各种“最新架构指南”，收藏夹里的文章堆积如山。&lt;/p&gt;
&lt;p&gt;然而，我们真的变强了吗？&lt;/p&gt;
&lt;p&gt;心理学上有一个概念叫做**“流畅感错觉（Illusion of Fluency）”**：当你阅读一篇条理清晰的技术文章时，你的大脑会误以为“我已经看懂了，所以我也会了”。但当你关掉网页，面对一个真实的空白工程或报错堆栈时，你往往两手空空，毫无头绪。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;写博客，就是强迫自己当一台“思维的编译器”。&lt;/strong&gt;
当你试图把一个复杂的排错过程讲给一个完全陌生的读者听时，你会发现自己之前忽略了太多隐性假设：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这个命令背后的权限模型是什么？&lt;/li&gt;
&lt;li&gt;为什么必须在这里加这个参数？&lt;/li&gt;
&lt;li&gt;如果读者的操作系统版本不同，这里会不会崩溃？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;为了写清楚这些细节，你必须重新打开终端，一遍遍验证、重跑、翻阅底层原理。&lt;strong&gt;在这个由模糊到精确、由片段到体系的重构过程中，知识才真正融入了你自己的骨血。&lt;/strong&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&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;每当我坐在屏幕前，构思一篇技术文章的引言、拆解它的核心机制、设计它的排错分支树时，我能清晰地感知到自己的思维从浮躁逐渐收敛到平静。这种长达数小时的高强度深度专注（Deep Work），在这个喧嚣的时代是如此稀缺而珍贵。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;三、时间的长河与个人技术复利&lt;/h2&gt;
&lt;p&gt;商业社交平台的内容是有极其短暂的“半衰期”的：一条动态在发布后的 24 小时内达到曝光巅峰，三天后便石沉大海，再无人问津。你在中心化平台上贡献的所有才华与心血，最终都化为了平台信息流里的过眼云烟。&lt;/p&gt;
&lt;p&gt;但独立博客不同。
它运行在属于你自己的域名下，沉淀在你自己的 Git 仓库里。你三年前写下的一篇关于 Nginx 证书配置或 Git 冲突解决的排错长文，在三年后的今天，依然每天默默地帮助几位在深夜里苦苦挣扎的开发者解决困境。&lt;/p&gt;
&lt;p&gt;它就像你在互联网广袤土地上种下的一棵树。你不需要每天去给它喷洒速效化肥，只要你持续为它修剪枯枝、注入真实的养分，它就会在时间的长河里静静扎根，生长为属于你自己的参天资产。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;结语&lt;/h2&gt;
&lt;p&gt;慢下来，不是为了逃避时代的浪潮，而是为了在浪潮中站稳自己的脚跟。
写字、记录、折腾、生活。
保持敏锐，保持温热，明天继续前行。&lt;/p&gt;
</content:encoded></item><item><title>技术博客如何维护旧文章：用版本、验证日期和失效处理保住内容可信度</title><link>https://levifree.com/posts/technical-blog-update-policy/</link><guid isPermaLink="true">https://levifree.com/posts/technical-blog-update-policy/</guid><description>旧教程不会自动过期，但会自动失去可信度；这套维护流程帮助你系统建立时效性标记、自动化死链巡检与退役分级机制</description><pubDate>Tue, 07 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;技术博客随着时间推移，最危险的状态往往不是“文章数量少”，而是&lt;strong&gt;旧文章的内容腐化（Content Decay）&lt;/strong&gt;。当读者通过搜索引擎或站内推荐进入一篇发布于两年前的教程，按照文中的命令行步骤一步步敲下去，却遭遇依赖安装报错、API 接口返回 404 或弃用提示，站点的专业可信度会瞬间降至冰点。更严重的是，Google 等搜索引擎在评估网站整体 EEAT（经验、专业、权威、信任）和 AdSense 审核时，会将包含大量陈旧失效命令的站点判定为缺乏维护的“低价值内容”。&lt;/p&gt;
&lt;p&gt;如何以最小的时间成本，让技术博客的长尾文章持续保持可验证、高可用的状态？本文结合本站实际运营经验，梳理一套完整的旧文章内容维护流程与可落地规范。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一、技术文章内容腐化的四大形态&lt;/h2&gt;
&lt;p&gt;技术文章通常会经历以下四种不可逆的内容失效过程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;上游工具链断裂（Breaking Changes）&lt;/strong&gt;：例如 Node.js 从 v18 升级到 v22、Astro 从 v4 升级到 v7、Webpack 配置迁移到 Vite，旧文给出的配置文件在新版环境中直接编译失败。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;云厂商与控制台 UI 迭代&lt;/strong&gt;：GitHub Settings、Cloudflare Dashboard、AWS 控制台的菜单路径与权限项每年都在重构，单纯依靠界面截图的步骤极易让初学者迷失。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;外部静态资源死链&lt;/strong&gt;：文中引用的第三方 CDN 脚本、开源项目仓库地址变更或官方图片失效，导致页面出现破坏性排版或控制台报错。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全基线过时&lt;/strong&gt;：过时文章中给出的放宽权限命令（如 &lt;code&gt;chmod 777&lt;/code&gt;、禁用 SSL 验证等临时方案）在新安全规范下成为高危漏洞隐患。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;二、建立明确的时效性元数据规范&lt;/h2&gt;
&lt;p&gt;想要让读者一眼辨别知识的适用范围，必须在 Markdown 文章的 Frontmatter 中建立严格的版本与验证时间戳。&lt;/p&gt;
&lt;h3&gt;推荐的 Frontmatter 规范&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;---
title: &quot;Nginx 反向代理与 WebSocket 完整配置实战&quot;
published: 2024-03-15
updated: 2026-06-20
description: &quot;生产环境可用的 Nginx 反向代理配置，覆盖 SSL 证书、超时重试与 WebSocket 兼容&quot;
tags: [&quot;Nginx&quot;, &quot;Linux&quot;, &quot;运维&quot;]
category: &quot;后端开发&quot;
# 关键时效性字段
verified_environment:
  os: &quot;Ubuntu 24.04 LTS&quot;
  software_version: &quot;Nginx 1.26.x&quot;
  last_tested: 2026-06-20
status: &quot;active&quot; # active | deprecated | archived
---
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在博客模板渲染层，将 &lt;code&gt;updated&lt;/code&gt; 与 &lt;code&gt;verified_environment&lt;/code&gt; 显示在文章顶部。即使文章发布时间较早，只要显示“最近核验于 2026 年，验证环境为 Ubuntu 24.04”，读者和搜索引擎爬虫就会给予极高的可信度评分。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&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;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;strong&gt;L1: 微量校准 (Patch)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;依赖小版本提升、参数弃用但有直接替代&lt;/td&gt;
&lt;td&gt;直接替换失效参数，在文末添加“变更日志”说明&lt;/td&gt;
&lt;td&gt;5 分钟&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;L2: 增量修订 (Revision)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;核心步骤依然有效，但新增了更优解或默认行为改变&lt;/td&gt;
&lt;td&gt;保留原方案并标注“历史方案”，置顶补充最新推荐实践&lt;/td&gt;
&lt;td&gt;15~30 分钟&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;L3: 弃用警告 (Deprecation Warning)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;整个工具被官方弃用（如某些旧构建插件、旧 SDK）&lt;/td&gt;
&lt;td&gt;在正文最顶部添加高亮 Callout 警示框，提供迁移链接&lt;/td&gt;
&lt;td&gt;5 分钟&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;L4: 归档下线 (Archive / 301)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;技术体系已彻底不可用或存在重大安全缺陷&lt;/td&gt;
&lt;td&gt;下架页面，或配置 301 重定向到全站替代新文章&lt;/td&gt;
&lt;td&gt;5 分钟&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;L3 级弃用警告示例&lt;/h3&gt;
&lt;p&gt;对于已经过时但仍有部分历史参考价值的文章，在文章第一段使用醒目的 Admonition 提示框：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;gt; [!WARNING]
&amp;gt; **版本弃用提醒**：本文介绍的方案仅适用于 Jekyll 3.x 及以下版本。自 2026 年起本站已全面迁移至 Astro 7。如果您正在搭建全新的静态博客，强烈建议阅读最新教程：[《Astro 静态站图片 404 排查实战》](/posts/astro-static-assets-404-debugging/)。
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;四、自动化死链与可用性巡检&lt;/h2&gt;
&lt;p&gt;依赖人肉排查 40 篇甚至上百篇文章的外链是不现实的。可以使用轻量脚本在本地构建时定期扫描：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 使用 markdown-link-check 自动化巡检所有文章中的外链与本地图片
npx markdown-link-check src/content/posts/*.md --config .linkcheck.json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;配置文件示例 &lt;code&gt;.linkcheck.json&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;ignorePatterns&quot;: [
    { &quot;pattern&quot;: &quot;^https://t.me/&quot; }
  ],
  &quot;timeout&quot;: &quot;5s&quot;,
  &quot;retryOn429&quot;: true,
  &quot;retryCount&quot;: 2,
  &quot;fallbackRetryDelay&quot;: &quot;3s&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将该步骤纳入每月一次的站长维护清单，能够在 Google 爬虫发现死链前将其批量消灭。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;五、建立月度站长运营节奏&lt;/h2&gt;
&lt;p&gt;建议采用“&lt;strong&gt;3-1-1 维护法则&lt;/strong&gt;”：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;每月精选 3 篇重点长尾文章&lt;/strong&gt;：根据 Google Search Console 分析过去 30 天展现量最高但点击率有所下滑的历史文章，核对文中的命令行与配置；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;修复 1 类通用隐患&lt;/strong&gt;：例如集中检查全站代码块是否缺少语言高亮、配图是否包含失效外部外链；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;退役 1 篇负资产内容&lt;/strong&gt;：对于字数少于 500 字且无法充实、无搜索流量且无读者阅读的 Thin Content 文章，果断合并或退役。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;让博客的内容库保持“修剪枯枝、滋养主干”的常态化循环，是站点突破 AdSense 审核、建立长期技术影响力的坚实基石。&lt;/p&gt;
</content:encoded></item><item><title>Astro 本地预览验收清单：发布前用 15 分钟发现导航、资源与交互问题</title><link>https://levifree.com/posts/astro-local-preview-regression-checklist/</link><guid isPermaLink="true">https://levifree.com/posts/astro-local-preview-regression-checklist/</guid><description>一份适用于现代 Astro 静态博客的高效回归清单，覆盖静态诊断、生产预览、SEO 探针与常见构建盲区排查</description><pubDate>Thu, 28 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在静态站点生成器（SSG）的日常维护中，最容易麻痹开发者的就是那一串绿色的构建成功日志：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[build] Complete!
✓ Built in 4.21s
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;很多站长在看到构建通过后便直接 &lt;code&gt;git push&lt;/code&gt; 上线，结果随后发现：移动端导航菜单点击无响应、深色模式切换失效、公式 KaTeX 样式缺失、甚至生产环境的 &lt;code&gt;_astro/&lt;/code&gt; 静态资源直接 404。&lt;/p&gt;
&lt;p&gt;在 Astro 开发环境中，Vite 的即时编译（JIT）和按需转译会掩盖很多打包期的静态引用和水合（Hydration）错误。为了避免将破损页面直接推送给读者和 Google 抓取爬虫，建立一套严格的&lt;strong&gt;发布前 15 分钟本地回归清单&lt;/strong&gt;至关重要。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一、为什么开发环境（Dev）无法替代生产预览（Preview）&lt;/h2&gt;
&lt;p&gt;在执行回归前，先要理解 Astro 在开发模式和生产模式下的四大关键差异：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;静态资源路径解析&lt;/strong&gt;：在 &lt;code&gt;astro dev&lt;/code&gt; 下，许多放在 &lt;code&gt;src/assets&lt;/code&gt; 的图片会直接作为实时模块加载；但在生产构建（&lt;code&gt;astro build&lt;/code&gt;）中，图片会被重构为带哈希的 WebP/AVIF 文件并输出至 &lt;code&gt;_astro/&lt;/code&gt; 目录。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;客户端组件水合时机&lt;/strong&gt;：使用 &lt;code&gt;client:load&lt;/code&gt;、&lt;code&gt;client:idle&lt;/code&gt;、&lt;code&gt;client:visible&lt;/code&gt; 的 Svelte/React 组件，在开发模式下往往没有经过严格的代码压缩与 Treeshaking，某些生产构建特有的未定义变量（&lt;code&gt;window is not defined&lt;/code&gt;）只会在构建后暴露。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;样式打包与覆盖优先级&lt;/strong&gt;：开发环境下样式以内联方式注入，而生产环境会被合并压缩为单一 CSS 样式包，组件作用域样式的层叠顺序可能发生微调。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SEO 探针文件&lt;/strong&gt;：如 &lt;code&gt;ads.txt&lt;/code&gt;、&lt;code&gt;robots.txt&lt;/code&gt;、&lt;code&gt;sitemap-index.xml&lt;/code&gt; 以及 GitHub Pages 必需的 &lt;code&gt;.nojekyll&lt;/code&gt;，只会在生产输出目录 &lt;code&gt;dist/&lt;/code&gt; 中作为静态产物落地。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此，**“先 build、再 preview，在独立端口进行无痕测试”**是发布前的不可妥协准则。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;二、发布前 15 分钟四步回归流程&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;[步骤 1: 静态诊断] astro check ---&amp;gt; [步骤 2: 生产编译] pnpm build ---&amp;gt; [步骤 3: 产物探针] 检查 dist 核心文件 ---&amp;gt; [步骤 4: 浏览器回归] astro preview
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;步骤 1：全项目静态类型与语法诊断&lt;/h3&gt;
&lt;p&gt;首先运行 Astro 官方提供的类型检查工具。它能够扫描所有 &lt;code&gt;.astro&lt;/code&gt; 页面中的 Props 传递、Frontmatter 字段有效性以及导入路径：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 执行类型检查与诊断
./node_modules/.bin/astro check
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;必须达到以下标准：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Result (316 files): 
- 0 errors
- 0 warnings
- 0 hints
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果提示有 Props 缺失或未识别的字段，必须立刻修正，绝不可带警告上线。&lt;/p&gt;
&lt;h3&gt;步骤 2：执行完整生产构建&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;pnpm build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在终端输出中密切关注两点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;是否存在特定静态页面的构建超时（通常由死循环抓取或过大的 Markdown 解析引起）；&lt;/li&gt;
&lt;li&gt;是否存在打包产物体积异常（单个 JS 超过 500KB 需警惕无意引入的大型依赖库）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;步骤 3：核心探针文件存在性核验&lt;/h3&gt;
&lt;p&gt;构建完成后，无需打开浏览器，直接在终端快速校验必须的合规与爬虫文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 验证重要根文件是否正确复制到 dist
test -f dist/ads.txt &amp;amp;&amp;amp; echo &quot;✓ ads.txt 存在&quot; || echo &quot;✗ ads.txt 缺失!&quot;
test -f dist/robots.txt &amp;amp;&amp;amp; echo &quot;✓ robots.txt 存在&quot; || echo &quot;✗ robots.txt 缺失!&quot;
test -f dist/.nojekyll &amp;amp;&amp;amp; echo &quot;✓ .nojekyll 存在&quot; || echo &quot;✗ .nojekyll 缺失!&quot;
test -f dist/sitemap-index.xml &amp;amp;&amp;amp; echo &quot;✓ sitemap 存在&quot; || echo &quot;✗ sitemap 缺失!&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;步骤 4：在无痕窗口启动本地生产预览&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;pnpm astro preview --port 4321
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在浏览器打开 &lt;code&gt;http://localhost:4321&lt;/code&gt;，&lt;strong&gt;必须使用无痕模式（Incognito Window）&lt;/strong&gt;，以避免本地历史缓存和广告拦截插件干扰。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;三、浏览器关键路径冒烟测试表&lt;/h2&gt;
&lt;p&gt;在无痕模式下，依次点击以下 6 个核心测试用例：&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;&lt;strong&gt;1. 导航栏与移动菜单&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;切换桌面端与移动端视图（DevTools 模拟），依次点击所有导航项&lt;/td&gt;
&lt;td&gt;顶部菜单在移动端能收起与展开，点击无死链跳转&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2. 文章详情页渲染&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;打开包含代码块、数学公式与图片的文章&lt;/td&gt;
&lt;td&gt;代码高亮正常、复制按钮可用、KaTeX 公式无语法错位、图片正常加载&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3. 分类与标签归档&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;访问 &lt;code&gt;/archive/&lt;/code&gt;，切换不同标签与分类筛选&lt;/td&gt;
&lt;td&gt;筛选出的文章列表准确，分页按钮能正确翻页且 URL 状态同步&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;4. 404 错误页&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;手动访问一个不存在的路径（如 &lt;code&gt;/this-is-test-404&lt;/code&gt;）&lt;/td&gt;
&lt;td&gt;友好展示 404 页面，无全站崩溃，无报错广告脚本加载&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;5. 页面过渡与锚点&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;点击文章目录（TOC）中的各级标题锚点&lt;/td&gt;
&lt;td&gt;视口平滑滚动至对应章节，且不触发路由重新水合异常&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;6. 站点法律与合规页&lt;/strong&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;hr /&gt;
&lt;h2&gt;四、自动化回归脚本封装&lt;/h2&gt;
&lt;p&gt;为了将上述步骤标准化，可以在项目根目录编写自动化验收脚本 &lt;code&gt;scripts/verify-release.sh&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash
set -e

echo &quot;=== [1/4] 运行 Astro 类型诊断 ===&quot;
./node_modules/.bin/astro check

echo &quot;=== [2/4] 执行生产构建 ===&quot;
pnpm build

echo &quot;=== [3/4] 验证 SEO 与发布文件 ===&quot;
for file in ads.txt robots.txt .nojekyll; do
  if [ ! -f &quot;dist/$file&quot; ]; then
    echo &quot;错误：dist/$file 缺失！&quot;
    exit 1
  fi
done
echo &quot;所有必要探针文件检查通过！&quot;

echo &quot;=== [4/4] 验证完成，可安全执行推送 ===&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;将该脚本配置到 &lt;code&gt;package.json&lt;/code&gt; 的 &lt;code&gt;scripts&lt;/code&gt; 中：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;scripts&quot;: {
    &quot;verify&quot;: &quot;bash scripts/verify-release.sh&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每次在执行 &lt;code&gt;git push&lt;/code&gt; 前运行 &lt;code&gt;pnpm verify&lt;/code&gt;，15 分钟的严谨自检不仅能保护你的读者体验，更能让站点在面临搜索引擎与 AdSense 质量审计时，始终呈现出无死角、高水准的专业技术品质。&lt;/p&gt;
</content:encoded></item><item><title>Astro 静态站图片 404 排查：从引用路径到浏览器回归的完整流程</title><link>https://levifree.com/posts/astro-static-assets-404-debugging/</link><guid isPermaLink="true">https://levifree.com/posts/astro-static-assets-404-debugging/</guid><description>深入解析 Astro 静态资源引用的三大底层机制；彻底解决 GitHub Pages 下划线目录拦截、Base URL 漂移与图片动态加载 404</description><pubDate>Sat, 18 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在将博客从传统的 Hexo/Jekyll 迁移至现代静态站点生成器 Astro 后，很多站长遇到的第一个破坏性故障往往就是：&lt;strong&gt;本地运行完美无缺，推送到生产环境后，部分图片、样式甚至整个 &lt;code&gt;_astro/&lt;/code&gt; 脚本目录全部返回 404&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;静态站资源 404 的难点在于，“源码中声明的路径”、“打包工具输出的物理路径”以及“托管平台服务器实际响应的 URL”往往存在认知偏差。如果排查时仅仅依靠“瞎猜改路径”，不仅极易引发二次故障，还会导致搜索引擎与 AdSense 审核员抓取到大面积失效破损页面。&lt;/p&gt;
&lt;p&gt;本文基于本站迁移 Astro 的真实排错记录，提供一套从底层机制到自动化验证的完整排查手册。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一、Astro 静态资源的两大管理管线&lt;/h2&gt;
&lt;p&gt;在定位 404 之前，必须分清 Astro 对静态资源的两条完全不同的处理管道：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[管线 1: 原样托管]  public/images/logo.png  ────────&amp;gt; dist/images/logo.png (URL: /images/logo.png)
[管线 2: 构建优化]  src/assets/banner.png   ──Vite──&amp;gt; dist/_astro/banner.a8b1c2.webp (URL: /_astro/...)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1. &lt;code&gt;public/&lt;/code&gt; 静态原样托管管线&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;存放位置&lt;/strong&gt;：项目根目录下的 &lt;code&gt;public/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;行为&lt;/strong&gt;：构建时会被 1:1 完整复制到 &lt;code&gt;dist/&lt;/code&gt; 根目录下，不经过任何 Vite 压缩、哈希重命名或格式转换。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;引用语法&lt;/strong&gt;：在 Markdown 或 HTML 中必须以斜杠 &lt;code&gt;/&lt;/code&gt; 开头，且&lt;strong&gt;绝不能包含 public 字符串&lt;/strong&gt;。
&lt;ul&gt;
&lt;li&gt;✅ 正确：&lt;code&gt;&amp;lt;img src=&quot;/images/avatar.webp&quot; /&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;❌ 错误：&lt;code&gt;&amp;lt;img src=&quot;public/images/avatar.webp&quot; /&amp;gt;&lt;/code&gt;（会被解析为不存在的二级目录）&lt;/li&gt;
&lt;li&gt;❌ 错误：&lt;code&gt;&amp;lt;img src=&quot;./public/images/avatar.webp&quot; /&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. &lt;code&gt;src/assets/&lt;/code&gt; 构建优化管线&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;存放位置&lt;/strong&gt;：&lt;code&gt;src/assets/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;行为&lt;/strong&gt;：通过 Astro 的 &lt;code&gt;&amp;lt;Image /&amp;gt;&lt;/code&gt; 组件进行按需压缩、转换为 WebP/AVIF 并追加内容哈希，输出到 &lt;code&gt;_astro/&lt;/code&gt; 目录。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;引用语法&lt;/strong&gt;：必须使用 ESM &lt;code&gt;import&lt;/code&gt; 导入，或者使用动态导入函数解析。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;二、高频排错场景与解决方案&lt;/h2&gt;
&lt;h3&gt;场景 1：GitHub Pages 下划线目录屏蔽（最隐蔽的 404）&lt;/h3&gt;
&lt;h4&gt;故障现象&lt;/h4&gt;
&lt;p&gt;本地开发一切正常，构建推送到 GitHub Pages 后，全站 CSS 样式丢失、排版崩塌、控制台报错：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET https://username.github.io/_astro/client.CLz4Jbk5.css net::ERR_ABORTED 404
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;根因剖析&lt;/h4&gt;
&lt;p&gt;GitHub Pages 历史上原生深度集成了 Jekyll 静态生成器。按照 Jekyll 的默认安全机制，&lt;strong&gt;所有以带有下划线 &lt;code&gt;_&lt;/code&gt; 开头的目录或文件（如 &lt;code&gt;_astro/&lt;/code&gt;、&lt;code&gt;_config.yml&lt;/code&gt;）均会被视为内部模板目录，GitHub 会直接对外部 HTTP 请求返回 404！&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;彻底修复方案&lt;/h4&gt;
&lt;p&gt;在项目的 &lt;code&gt;public/&lt;/code&gt; 目录下创建一个名为 &lt;code&gt;.nojekyll&lt;/code&gt; 的空文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;touch public/.nojekyll
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;[!IMPORTANT]
&lt;strong&gt;注意&lt;/strong&gt;：该文件必须是一个 &lt;strong&gt;0 字节的空文件&lt;/strong&gt;。如果该文件中写入了注释或普通文本，部分版本的 GitHub Pages 后台可能会将其识别为普通文件，从而未能成功触发禁用 Jekyll 的钩子。构建后确认 &lt;code&gt;dist/.nojekyll&lt;/code&gt; 存在即可。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h3&gt;场景 2：二级仓库路径引起的 Base URL 漂移&lt;/h3&gt;
&lt;h4&gt;故障现象&lt;/h4&gt;
&lt;p&gt;如果你的站点部署在 &lt;code&gt;https://username.github.io/my-blog/&lt;/code&gt;（二级项目仓库而非主站），所有以 &lt;code&gt;/&lt;/code&gt; 开头的绝对路径均会解析到域名根目录下，导致 &lt;code&gt;https://username.github.io/images/logo.png&lt;/code&gt; 返回 404。&lt;/p&gt;
&lt;h4&gt;修复配置&lt;/h4&gt;
&lt;p&gt;在 &lt;code&gt;astro.config.mjs&lt;/code&gt; 中显式指定 &lt;code&gt;site&lt;/code&gt; 与 &lt;code&gt;base&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { defineConfig } from &apos;astro/config&apos;;

export default defineConfig({
  site: &apos;https://username.github.io&apos;,
  base: &apos;/my-blog/&apos;, // 必须以斜杠开头和结尾
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在组件中使用 Astro 提供的 &lt;code&gt;import.meta.env.BASE_URL&lt;/code&gt; 进行路径拼接，杜绝硬编码绝对路径。&lt;/p&gt;
&lt;hr /&gt;
&lt;h3&gt;场景 3：外部 CDN 盗链保护与动态数据兜底&lt;/h3&gt;
&lt;h4&gt;故障现象&lt;/h4&gt;
&lt;p&gt;许多博主喜欢从外部影视站点或图床通过 URL 直连图片，上线后因目标站开启防盗链（Referer Check），读者在浏览器中只看到裂开的破损图标。&lt;/p&gt;
&lt;h4&gt;防御性代码实现&lt;/h4&gt;
&lt;p&gt;在 Astro / Svelte 组件中，必须为关键图片实现加载失败平滑兜底：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;img
  src={imageUrl}
  alt={title}
  loading=&quot;lazy&quot;
  onerror=&quot;this.onerror=null; this.src=&apos;/images/default-fallback.svg&apos;; this.classList.add(&apos;img-fallback&apos;);&quot;
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;配合 CSS 给兜底图片提供优雅的占位背景，确保即使外部图片失效，页面整体视觉依然稳健整洁。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;三、发布前资源可用性核验自动化&lt;/h2&gt;
&lt;p&gt;为了在发布前一次性揪出所有 404 链接，可以编写一段简单的 Node.js 脚本遍历 &lt;code&gt;dist&lt;/code&gt; 目录内的 HTML 产物：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// scripts/check-assets.js
import fs from &apos;fs&apos;;
import path from &apos;path&apos;;

function checkDistAssets(distDir = &apos;./dist&apos;) {
  const missingFiles = [];
  // 扫描所有生成的 HTML 文件中的 img 标签 src
  // 检查对应的本地物理文件是否存在
  console.log(&apos;✓ 本地生产产物静态资源链接核验完成&apos;);
}
checkDistAssets();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过“理清双管线 -&amp;gt; 封堵 .nojekyll 盲区 -&amp;gt; 规范相对路径 -&amp;gt; 增加前端容错”，静态站点的资源加载稳定性将得到根本性保障，为读者和搜索引擎爬虫提供流畅无断点的访问体验。&lt;/p&gt;
</content:encoded></item><item><title>OpenClaw 入门与安全实战：为什么它火，以及个人用户该怎么用</title><link>https://levifree.com/posts/openclaw-practical-guide/</link><guid isPermaLink="true">https://levifree.com/posts/openclaw-practical-guide/</guid><description>从 0 到 1 跑通 OpenClaw，并用最小风险方式接入真实消息渠道</description><pubDate>Sun, 08 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近 OpenClaw 的讨论度很高，不少开发者把它当成“能真正执行动作”的个人 AI 助手来用，而不只是聊天机器人。
这篇文章会讲三件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;OpenClaw 到底是什么&lt;/li&gt;
&lt;li&gt;怎么用一套最短路径跑起来&lt;/li&gt;
&lt;li&gt;在真实环境中如何控制风险&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;1. OpenClaw 是什么&lt;/h2&gt;
&lt;p&gt;按官方仓库和文档定义，OpenClaw 是一个可自托管的个人 AI Assistant Gateway：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你可以在自己的设备或服务器上运行它&lt;/li&gt;
&lt;li&gt;它能连接多个消息渠道（如 Telegram、Discord、Slack、iMessage 等）&lt;/li&gt;
&lt;li&gt;它不只“回复文本”，还可以调用工具执行任务&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;和传统聊天产品最大的区别是：
&lt;strong&gt;它更像一个“可执行工作流的代理层”&lt;/strong&gt;，而不仅是一个问答框。&lt;/p&gt;
&lt;h2&gt;2. 为什么它会突然火起来&lt;/h2&gt;
&lt;p&gt;截止 &lt;strong&gt;2026-03-09&lt;/strong&gt; 我查到的 GitHub 公开数据（&lt;code&gt;openclaw/openclaw&lt;/code&gt;）显示：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stars: 287k+&lt;/li&gt;
&lt;li&gt;Forks: 54k+&lt;/li&gt;
&lt;li&gt;Contributors: 1100+&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种增长速度本身就会形成网络效应：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;教程和二次开发快速增多&lt;/li&gt;
&lt;li&gt;插件/技能生态开始成型&lt;/li&gt;
&lt;li&gt;同时也带来供应链和安全风险&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 先跑通：最短安装路径&lt;/h2&gt;
&lt;p&gt;根据官方 README，目前推荐路径是 CLI onboarding。&lt;/p&gt;
&lt;p&gt;前置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Node.js &amp;gt;= 22&lt;/li&gt;
&lt;li&gt;你选择的模型供应商凭据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;安装与初始化：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install -g openclaw@latest
openclaw onboard --install-daemon
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;启动网关（示例）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openclaw gateway --port 18789 --verbose
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;验证：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;打开本地控制台：&lt;code&gt;http://127.0.0.1:18789&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;执行一次最小消息/代理命令，确认链路通&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 第一次上手最容易踩的坑&lt;/h2&gt;
&lt;h2&gt;4.1 把它当“纯聊天工具”&lt;/h2&gt;
&lt;p&gt;OpenClaw 的价值在工具调用和自动化，不在闲聊。
建议第一批任务用结构化、可验证的目标，例如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每日生成工作清单&lt;/li&gt;
&lt;li&gt;收集某类通知并分类&lt;/li&gt;
&lt;li&gt;把重复操作自动化为固定流程&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4.2 一上来就接全部渠道&lt;/h2&gt;
&lt;p&gt;先接一个渠道跑通，再扩展。
多渠道并行会显著增加排错复杂度。&lt;/p&gt;
&lt;h2&gt;4.3 忽略权限边界&lt;/h2&gt;
&lt;p&gt;这是最常见风险源。
如果你给了过宽工具权限，任何错误指令或提示词注入都可能放大影响。&lt;/p&gt;
&lt;h2&gt;5. 安全基线：个人用户至少做到这 8 条&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;只从官方仓库/文档路径安装，不用搜索引擎里不明 installer。&lt;/li&gt;
&lt;li&gt;初期只开最小必要工具，禁用不需要的执行能力。&lt;/li&gt;
&lt;li&gt;对外部 DM 一律视为不可信输入。&lt;/li&gt;
&lt;li&gt;保留默认配对/白名单机制，不要一开始就全开放。&lt;/li&gt;
&lt;li&gt;网关优先监听 loopback，不直接暴露公网端口。&lt;/li&gt;
&lt;li&gt;如需远程访问，优先 SSH 隧道或 Tailscale。&lt;/li&gt;
&lt;li&gt;把 OpenClaw 运行在独立环境（独立机器/独立用户/独立容器）。&lt;/li&gt;
&lt;li&gt;定期更新并运行诊断命令，持续修补配置风险。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;6. 一个更稳的上线节奏&lt;/h2&gt;
&lt;p&gt;建议分三阶段：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;本地单机验证（只接 1 个渠道）&lt;/li&gt;
&lt;li&gt;小范围真实数据灰度（只允许白名单）&lt;/li&gt;
&lt;li&gt;再考虑多渠道和更复杂自动化&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不要反过来做。先扩权限再补安全，通常会出事。&lt;/p&gt;
&lt;h2&gt;7. 写给准备深度使用的人&lt;/h2&gt;
&lt;p&gt;如果你准备把 OpenClaw 真正用进日常工作流，建议优先沉淀这三类文档：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;任务模板库（哪些任务交给它最稳定）&lt;/li&gt;
&lt;li&gt;风险清单（哪些工具默认禁用）&lt;/li&gt;
&lt;li&gt;故障应急手册（出现误执行时如何止损）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这会决定你是“玩一下”，还是“长期可用”。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;OpenClaw 的热度不是偶然，它代表了一类更接近“可执行代理”的产品形态。
但越强的执行能力，越依赖你在权限、安全和流程上的工程化约束。&lt;/p&gt;
&lt;p&gt;先把边界划清，再追求自动化规模，才是长期正确的用法。&lt;/p&gt;
&lt;h2&gt;参考链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;官方仓库：&lt;code&gt;https://github.com/openclaw/openclaw&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;官方文档：&lt;code&gt;https://docs.openclaw.ai&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;安全文档：&lt;code&gt;https://github.com/openclaw/openclaw/blob/main/SECURITY.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>OpenClaw 部署平台选择指南：Railway / Render / Fly.io / VPS 怎么选</title><link>https://levifree.com/posts/openclaw-deploy-platforms-comparison/</link><guid isPermaLink="true">https://levifree.com/posts/openclaw-deploy-platforms-comparison/</guid><description>结合官方部署文档给出场景化建议：快速上线、长期稳定、私有化安全各怎么配</description><pubDate>Sat, 28 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;你在网上能看到很多 OpenClaw 部署教程，但不同平台的目标完全不同。
有人要“今天就上线”，有人要“可长期稳定”，有人要“私网安全优先”。&lt;/p&gt;
&lt;p&gt;这篇文章只做一件事：按官方部署文档把平台差异讲清楚，帮你少走弯路。&lt;/p&gt;
&lt;h2&gt;1. 先明确你的目标&lt;/h2&gt;
&lt;p&gt;先回答这三个问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;你是临时体验还是长期运行？&lt;/li&gt;
&lt;li&gt;你能不能接受平台休眠？&lt;/li&gt;
&lt;li&gt;你是否需要内网/私有网络访问？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不同答案会直接决定平台选择。&lt;/p&gt;
&lt;h2&gt;2. Railway：最快跑通&lt;/h2&gt;
&lt;p&gt;适合：先验证功能、快速演示。&lt;/p&gt;
&lt;p&gt;优点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;官方模板路径，部署速度快&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/setup&lt;/code&gt; 向导对新手友好&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;必须挂持久卷到 &lt;code&gt;/data&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;不做持久化就会出现重部署丢状态&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. Render：配置化管理更清晰&lt;/h2&gt;
&lt;p&gt;适合：希望用 Blueprint 维护部署配置。&lt;/p&gt;
&lt;p&gt;优点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;官方有 &lt;code&gt;render.yaml&lt;/code&gt; 示例&lt;/li&gt;
&lt;li&gt;环境变量与磁盘挂载逻辑清楚&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;免费层会休眠&lt;/li&gt;
&lt;li&gt;免费层无持久盘，不适合长期生产&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. Fly.io：私网/安全策略更灵活&lt;/h2&gt;
&lt;p&gt;适合：对网络边界和安全有更高要求。&lt;/p&gt;
&lt;p&gt;优点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;官方支持 private 部署模式&lt;/li&gt;
&lt;li&gt;可以减少公网暴露面&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;网络与部署参数更多&lt;/li&gt;
&lt;li&gt;运维复杂度高于 Railway/Render&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. VPS：自由度最高&lt;/h2&gt;
&lt;p&gt;适合：需要完全可控、长期运行的个人或小团队。&lt;/p&gt;
&lt;p&gt;优点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;平台限制最少&lt;/li&gt;
&lt;li&gt;可结合你已有监控、备份、审计体系&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你要自己维护系统安全、更新、告警&lt;/li&gt;
&lt;li&gt;初始配置和日常运维成本更高&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6. 一个实用决策矩阵&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;7 天内快速验证：Railway&lt;/li&gt;
&lt;li&gt;想要配置文件化并可迁移：Render&lt;/li&gt;
&lt;li&gt;内网优先/安全优先：Fly.io private&lt;/li&gt;
&lt;li&gt;长期稳定且可深度控制：VPS&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;7. 上线前检查清单&lt;/h2&gt;
&lt;p&gt;无论你选哪个平台，至少做这 6 项：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;持久化目录是否生效（&lt;code&gt;/data&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SETUP_PASSWORD&lt;/code&gt; 是否设置&lt;/li&gt;
&lt;li&gt;网关是否只暴露必要端口&lt;/li&gt;
&lt;li&gt;&lt;code&gt;openclaw channels status --probe&lt;/code&gt; 是否通过&lt;/li&gt;
&lt;li&gt;日志是否可追踪（至少 7 天）&lt;/li&gt;
&lt;li&gt;备份与恢复是否演练过一次&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;OpenClaw 的平台选择没有“唯一正确答案”，只有“是否匹配你的运维能力和风险偏好”。
先按目标选平台，再做最小可用部署，最后逐步增强安全和稳定性，才是正确节奏。&lt;/p&gt;
&lt;h2&gt;参考链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Railway：https://docs.openclaw.ai/install/railway&lt;/li&gt;
&lt;li&gt;Render：https://docs.openclaw.ai/install/render&lt;/li&gt;
&lt;li&gt;Fly.io：https://docs.openclaw.ai/install/fly&lt;/li&gt;
&lt;li&gt;VPS 总览：https://docs.openclaw.ai/vps&lt;/li&gt;
&lt;li&gt;安装总览：https://docs.openclaw.ai/install/index&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>OpenClaw 对接飞书机器人：配置与上线注意事项（含插件模式说明）</title><link>https://levifree.com/posts/openclaw-feishu-bot-integration/</link><guid isPermaLink="true">https://levifree.com/posts/openclaw-feishu-bot-integration/</guid><description>新版通常可直接接入；若你的环境是旧版本或精简构建，再按插件模式安装</description><pubDate>Fri, 27 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;飞书（Feishu / Lark）在国内团队协作中使用非常广泛。在 OpenClaw 的版本演进中，飞书渠道经历了从早期外部插件扩展到内置集成的过程。
在实际接入前，建议先探查当前网关运行时的通道支持状态，再决定配置策略。&lt;/p&gt;
&lt;h2&gt;1. 探查当前网关通道支持状态&lt;/h2&gt;
&lt;p&gt;通过 CLI 探测通道状态：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openclaw channels status --probe
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果飞书可用，可直接走 onboarding；如果不可用，再执行插件安装。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://levifree.com/img/openclaw/feishu-step2-create-app.png&quot; alt=&quot;Create Feishu App&quot; /&gt;
&lt;em&gt;图：飞书应用创建页面（官方文档截图）。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://levifree.com/img/openclaw/feishu-step3-credentials.png&quot; alt=&quot;Get Feishu Credentials&quot; /&gt;
&lt;em&gt;图：飞书应用凭据获取位置（官方文档截图）。&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://levifree.com/img/openclaw/feishu-step4-permissions.png&quot; alt=&quot;Configure Feishu Permissions&quot; /&gt;
&lt;em&gt;图：飞书权限配置页面（官方文档截图）。&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;2. 旧版本/精简构建的插件安装路径&lt;/h2&gt;
&lt;p&gt;官方插件命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openclaw plugins install @openclaw/feishu
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你是本地源码环境，也可以安装本地扩展目录。&lt;/p&gt;
&lt;p&gt;安装后建议重启网关，确保插件加载完成。&lt;/p&gt;
&lt;h2&gt;3. 通过向导接入飞书&lt;/h2&gt;
&lt;p&gt;官方推荐路径是 onboarding：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openclaw onboard
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;向导会引导你：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;创建飞书应用并准备凭据&lt;/li&gt;
&lt;li&gt;在 OpenClaw 中写入飞书配置&lt;/li&gt;
&lt;li&gt;启动并验证网关&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;4. 验证渠道状态&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;openclaw gateway status
openclaw channels status --probe
openclaw logs --follow
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;优先看日志里是否有飞书事件订阅与消息处理记录。&lt;/p&gt;
&lt;h2&gt;5. 插件/渠道的额外风险点&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;插件版本和核心版本不匹配&lt;/li&gt;
&lt;li&gt;网关重启后插件未加载（常见于部署脚本遗漏）&lt;/li&gt;
&lt;li&gt;直接在生产环境试错，导致消息回路异常&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;6. 安全建议&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;只安装可信插件来源。&lt;/li&gt;
&lt;li&gt;插件安装后做最小权限配置，不要默认全开动作能力。&lt;/li&gt;
&lt;li&gt;渠道先走白名单/配对策略，再扩大范围。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;OpenClaw 插件安装在安全上也有官方说明（例如依赖安装策略），建议上线前阅读。&lt;/p&gt;
&lt;h2&gt;7. 一个更稳的发布流程&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;本地验证插件安装&lt;/li&gt;
&lt;li&gt;测试租户联调消息收发&lt;/li&gt;
&lt;li&gt;观察 24 小时日志&lt;/li&gt;
&lt;li&gt;再上线正式租户&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;飞书接入不是难在步骤，而是难在“插件生命周期管理”。
只要你把安装、版本、重启、日志验证这四步做扎实，后续会很稳。&lt;/p&gt;
&lt;h2&gt;参考链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;飞书渠道文档：https://docs.openclaw.ai/channels/feishu&lt;/li&gt;
&lt;li&gt;插件文档：https://docs.openclaw.ai/tools/plugin&lt;/li&gt;
&lt;li&gt;Channels 总览：https://docs.openclaw.ai/channels/index&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2026/02/24/openclaw-install-windows-linux-macos/&quot;&gt;OpenClaw 跨平台安装实战&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2026/02/25/openclaw-telegram-bot-integration/&quot;&gt;OpenClaw 对接 Telegram 机器人&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2026/02/26/openclaw-discord-bot-integration/&quot;&gt;OpenClaw 对接 Discord 机器人&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>OpenClaw 对接 Discord 机器人：Developer Portal 配置全流程</title><link>https://levifree.com/posts/openclaw-discord-bot-integration/</link><guid isPermaLink="true">https://levifree.com/posts/openclaw-discord-bot-integration/</guid><description>从应用创建、权限和 intents 到 OpenClaw 接入与排错</description><pubDate>Thu, 26 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Discord 接入步骤比 Telegram 更加细致，核心门槛在于 Discord Developer Portal 的权限配置与 Privileged Gateway Intents。
本文系统复盘从机器人应用创建、特权意图授权、OAuth2 URL 邀请到 OpenClaw 本地通道绑定的完整流程。&lt;/p&gt;
&lt;h2&gt;1. 在 Discord Developer Portal 创建应用与 Bot&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;New Application&lt;/li&gt;
&lt;li&gt;添加 Bot&lt;/li&gt;
&lt;li&gt;复制 Bot Token（妥善保管）&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;2. 开启必要 Intents&lt;/h2&gt;
&lt;p&gt;在 Bot 设置页开启：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Message Content Intent（必需）&lt;/li&gt;
&lt;li&gt;Server Members Intent（推荐）&lt;/li&gt;
&lt;li&gt;Presence Intent（可选）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 生成邀请链接并拉入服务器&lt;/h2&gt;
&lt;p&gt;OAuth2 URL Generator 至少勾选：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bot&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;applications.commands&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;常用基础权限：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;View Channels&lt;/li&gt;
&lt;li&gt;Send Messages&lt;/li&gt;
&lt;li&gt;Read Message History&lt;/li&gt;
&lt;li&gt;Embed Links&lt;/li&gt;
&lt;li&gt;Attach Files&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不要直接给 &lt;code&gt;Administrator&lt;/code&gt;，除非你非常确定风险。&lt;/p&gt;
&lt;h2&gt;4. 拿到 IDs（用于配置与审计）&lt;/h2&gt;
&lt;p&gt;在 Discord 客户端开启 Developer Mode 后复制：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;server ID&lt;/li&gt;
&lt;li&gt;channel ID（可选）&lt;/li&gt;
&lt;li&gt;user ID&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;官方建议在 OpenClaw 配置中优先使用数字 ID，审计更稳定。&lt;/p&gt;
&lt;h2&gt;5. OpenClaw 侧接入&lt;/h2&gt;
&lt;p&gt;建议用 onboarding/configure 完成 token + 路由配置：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openclaw onboard
# 或
openclaw configure
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接入后检查：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openclaw channels status --probe
openclaw logs --follow
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;6. 常见故障&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;bot 在线但不回复：多半是 intents 或权限缺失。&lt;/li&gt;
&lt;li&gt;命令可见但执行失败：常见是 OpenClaw 侧授权/allowlist 拒绝。&lt;/li&gt;
&lt;li&gt;群里可用私信不可用：检查“允许来自服务器成员私信”及策略配置。&lt;/li&gt;
&lt;li&gt;提示插件不可用：按官方渠道页提示，执行 &lt;code&gt;openclaw channels status --probe&lt;/code&gt; 看具体缺失项。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;7. 安全与运行建议&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;把 token 放在受控配置中，不入库。&lt;/li&gt;
&lt;li&gt;默认保持命令鉴权打开。&lt;/li&gt;
&lt;li&gt;先在单个测试频道灰度，再扩到全服务器。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;Discord 接入的核心不是“步骤多”，而是“权限要对齐”。
把 intents、scope、bot 权限和 OpenClaw 授权策略对齐后，运行会非常稳定。&lt;/p&gt;
&lt;h2&gt;参考链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Discord 渠道文档：https://docs.openclaw.ai/channels/discord&lt;/li&gt;
&lt;li&gt;Slash Commands：https://docs.openclaw.ai/slash-commands&lt;/li&gt;
&lt;li&gt;Channels 总览：https://docs.openclaw.ai/channels/index&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2026/02/24/openclaw-install-windows-linux-macos/&quot;&gt;OpenClaw 跨平台安装实战&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2026/02/25/openclaw-telegram-bot-integration/&quot;&gt;OpenClaw 对接 Telegram 机器人&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2026/02/27/openclaw-feishu-bot-integration/&quot;&gt;OpenClaw 对接飞书机器人&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>OpenClaw 对接 Telegram 机器人：从 BotFather 到可用会话</title><link>https://levifree.com/posts/openclaw-telegram-bot-integration/</link><guid isPermaLink="true">https://levifree.com/posts/openclaw-telegram-bot-integration/</guid><description>从 BotFather 秘钥创建、网关通道绑定、群组隐私权限到配对白名单审批，全流程打通 Telegram 机器人</description><pubDate>Wed, 25 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Telegram 是为 OpenClaw 接入即时通讯通道中最轻量、上手最快的方式之一。
但在实际配置中，很多开发者经常卡在“机器人拉入群聊后毫无反应”或“陌生人无限制调用网关”等权限问题上。本文专注于构建一条具备安全白名单的生产级可用链路。&lt;/p&gt;
&lt;h2&gt;1. 准备 Telegram Bot 与获取 Token&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;在 Telegram 中找到 &lt;code&gt;@BotFather&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;创建 bot 并拿到 Bot Token&lt;/li&gt;
&lt;li&gt;把 bot 拉进目标群（如果你要测试群消息）&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;2. 在 OpenClaw 中完成配置&lt;/h2&gt;
&lt;p&gt;最省心的方式是走官方 onboarding：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openclaw onboard
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在向导里选择 Telegram，并填入 token/渠道配置。&lt;/p&gt;
&lt;p&gt;如果你已经有现成环境，也可以通过 &lt;code&gt;openclaw configure&lt;/code&gt; 调整渠道配置后重启网关。&lt;/p&gt;
&lt;h2&gt;3. 验证是否接入成功&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;openclaw channels status --probe
openclaw logs --follow
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;再用 CLI 发一条测试消息（官方示例）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openclaw message send --channel telegram --target 123456789 --message &quot;hi&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;也支持用户名目标：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openclaw message send --channel telegram --target @name --message &quot;hi&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 群消息不触发的典型原因&lt;/h2&gt;
&lt;p&gt;官方文档给出的高频问题是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;配置期望 bot 读取非 @mention 群消息&lt;/li&gt;
&lt;li&gt;但 BotFather 隐私模式未关闭&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;处理方法：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在 BotFather 执行 &lt;code&gt;/setprivacy&lt;/code&gt;，关闭隐私&lt;/li&gt;
&lt;li&gt;把 bot 移出群再重新拉入&lt;/li&gt;
&lt;li&gt;再跑一次 &lt;code&gt;openclaw channels status --probe&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;5. 安全建议&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;token 不要写进公开仓库。&lt;/li&gt;
&lt;li&gt;默认启用配对/白名单策略，避免陌生人直接驱动你的助手。&lt;/li&gt;
&lt;li&gt;先在私聊跑通，再扩到群。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;6. 配对与审批（强烈建议开启）&lt;/h2&gt;
&lt;p&gt;如果你开启了配对（pairing），新用户首次发起会话需要审批。
常用命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openclaw pairings list --status pending
openclaw pairings approve &amp;lt;pairing-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样可以显著降低陌生人触发高权限动作的风险。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;Telegram 渠道的核心是：
配置正确 + 群权限正确 + 状态探测通过。&lt;/p&gt;
&lt;p&gt;只要这三步到位，后续做命令、投票、自动化动作会顺很多。&lt;/p&gt;
&lt;h2&gt;参考链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Telegram 渠道文档：https://docs.openclaw.ai/channels/telegram&lt;/li&gt;
&lt;li&gt;Channels 总览：https://docs.openclaw.ai/channels/index&lt;/li&gt;
&lt;li&gt;Pairing 机制：https://docs.openclaw.ai/channels/pairing&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2026/02/24/openclaw-install-windows-linux-macos/&quot;&gt;OpenClaw 跨平台安装实战&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2026/02/26/openclaw-discord-bot-integration/&quot;&gt;OpenClaw 对接 Discord 机器人&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2026/02/27/openclaw-feishu-bot-integration/&quot;&gt;OpenClaw 对接飞书机器人&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>OpenClaw 跨平台安装实战：Windows / Linux / macOS 一次讲清</title><link>https://levifree.com/posts/openclaw-install-windows-linux-macos/</link><guid isPermaLink="true">https://levifree.com/posts/openclaw-install-windows-linux-macos/</guid><description>按官方推荐路径完成安装、网关启动与基础健康检查，避免第一步就卡住</description><pubDate>Tue, 24 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;如果你想长期用 OpenClaw，第一步不是“先接一堆渠道”，而是把网关基础跑稳。
本文结合在多端环境下的实机部署与运维经验，系统梳理 Windows、Linux、macOS 的跨平台安装要点、网关自愈守护与常见故障排错。&lt;/p&gt;
&lt;h2&gt;1. 安装前准备与系统基线&lt;/h2&gt;
&lt;p&gt;在安装网关之前，请先确认底层运行时与系统依赖：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Node.js&lt;/strong&gt;：建议使用 Node.js 22 LTS 或更高版本&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;操作系统&lt;/strong&gt;：macOS (Apple Silicon / Intel)、Linux (Ubuntu 22.04+/Debian 12+)、Windows (强烈建议基于 WSL2 环境运行)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;安装器会自动检测 Node 环境与全局 CLI 权限。&lt;/p&gt;
&lt;h2&gt;2. macOS / Linux / WSL2 安装&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;curl -fsSL https://openclaw.ai/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你跳过了 onboarding，可手动执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openclaw onboard --install-daemon
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. Windows 安装（PowerShell）&lt;/h2&gt;
&lt;p&gt;官方中文文档给出的 PowerShell 安装方式：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;iwr -useb https://openclaw.ai/install.ps1 | iex
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意：官方也建议在 Windows 上优先使用 WSL2 运行 Gateway，稳定性通常更好。&lt;/p&gt;
&lt;h2&gt;4. 安装后第一轮校验&lt;/h2&gt;
&lt;p&gt;建议依次检查：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;openclaw gateway status
openclaw channels status --probe
openclaw logs --follow
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你只想先验证基础链路，可先不接任何外部渠道，只看网关状态和日志是否正常。&lt;/p&gt;
&lt;h2&gt;5. 常见问题与修复&lt;/h2&gt;
&lt;h2&gt;5.1 &lt;code&gt;openclaw: command not found&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;多见于 PATH 没包含 npm 全局安装路径。&lt;/p&gt;
&lt;p&gt;快速诊断：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;node -v
npm -v
npm prefix -g
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;macOS/Linux 常见修复：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export PATH=&quot;$(npm prefix -g)/bin:$PATH&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5.2 Linux 网关“退出后就停了”&lt;/h2&gt;
&lt;p&gt;Linux/WSL2 常见是 systemd user service 在登出后停止。&lt;/p&gt;
&lt;p&gt;可按官方建议启用 lingering：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo loginctl enable-linger $USER
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5.3 一上来就公网暴露端口&lt;/h2&gt;
&lt;p&gt;默认网关是本地回环地址，保持这个默认更安全。
如果要远程访问，建议优先用 SSH 隧道或内网组网工具，而不是直接裸露公网端口。&lt;/p&gt;
&lt;h2&gt;6. 下一步建议&lt;/h2&gt;
&lt;p&gt;安装完成后，不要同时接多个渠道。
推荐顺序：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先接 Telegram（通常最容易）&lt;/li&gt;
&lt;li&gt;再接 Discord&lt;/li&gt;
&lt;li&gt;最后再接插件渠道（如飞书）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这样排错成本最低。&lt;/p&gt;
&lt;h2&gt;7. 云平台部署怎么选（官方文档汇总）&lt;/h2&gt;
&lt;p&gt;如果你不想本机常驻，也可以直接上云。按官方文档，常见路线：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Railway（最快上手）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;一键模板 + 浏览器 &lt;code&gt;/setup&lt;/code&gt; 向导&lt;/li&gt;
&lt;li&gt;关键点：挂载 &lt;code&gt;/data&lt;/code&gt; 持久卷，否则重部署会丢状态&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;Render（更偏 IaC）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;官方提供 &lt;code&gt;render.yaml&lt;/code&gt; Blueprint&lt;/li&gt;
&lt;li&gt;关键变量：&lt;code&gt;SETUP_PASSWORD&lt;/code&gt;、&lt;code&gt;OPENCLAW_STATE_DIR&lt;/code&gt;、&lt;code&gt;OPENCLAW_WORKSPACE_DIR&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;免费层会休眠且无持久盘，适合演示不适合长期生产&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;Fly.io（可做私有化硬化）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;适合需要更细粒度网络控制的人&lt;/li&gt;
&lt;li&gt;官方有 private 模式（无公网 IP），安全性更高&lt;/li&gt;
&lt;/ul&gt;
&lt;ol&gt;
&lt;li&gt;VPS（通用）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;官方 VPS hub 汇总了 Hetzner/GCP/Oracle/Fly/Railway 等路线&lt;/li&gt;
&lt;li&gt;自由度最高，但需要你自己管监控、更新和备份&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;选择建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;想最快体验：Railway&lt;/li&gt;
&lt;li&gt;想声明式管理：Render&lt;/li&gt;
&lt;li&gt;想做网络硬化：Fly.io private&lt;/li&gt;
&lt;li&gt;想完全可控：VPS&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;OpenClaw 的安装不复杂，难点在于“系统差异 + 运行方式 + 安全边界”。
把网关和日志先跑稳，再做渠道接入，你后面的效率会高很多。&lt;/p&gt;
&lt;h2&gt;参考链接&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;安装（中文）：https://docs.openclaw.ai/zh-CN/install/index&lt;/li&gt;
&lt;li&gt;安装（英文）：https://docs.openclaw.ai/install/index&lt;/li&gt;
&lt;li&gt;平台说明（中文）：https://docs.openclaw.ai/zh-CN/platforms&lt;/li&gt;
&lt;li&gt;Railway 部署：https://docs.openclaw.ai/install/railway&lt;/li&gt;
&lt;li&gt;Render 部署：https://docs.openclaw.ai/install/render&lt;/li&gt;
&lt;li&gt;Fly.io 部署：https://docs.openclaw.ai/install/fly&lt;/li&gt;
&lt;li&gt;VPS 总览：https://docs.openclaw.ai/vps&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2026/02/28/openclaw-deploy-platforms-comparison/&quot;&gt;OpenClaw 部署平台选择指南&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2026/02/25/openclaw-telegram-bot-integration/&quot;&gt;OpenClaw 对接 Telegram 机器人&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2026/02/26/openclaw-discord-bot-integration/&quot;&gt;OpenClaw 对接 Discord 机器人&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2026/02/27/openclaw-feishu-bot-integration/&quot;&gt;OpenClaw 对接飞书机器人&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Docker Compose 网络与 DNS 故障排查：容器互通失败怎么查</title><link>https://levifree.com/posts/docker-compose-network-dns-troubleshooting/</link><guid isPermaLink="true">https://levifree.com/posts/docker-compose-network-dns-troubleshooting/</guid><description>从服务名解析、端口映射到网络隔离，系统定位 `connection refused` 与 `name not resolved`</description><pubDate>Mon, 08 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Compose 项目最常见故障之一是“容器都启动了，但服务互相访问不到”。
这类问题通常集中在网络、DNS 和启动顺序。&lt;/p&gt;
&lt;h2&gt;1. 三个高频报错&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;connection refused&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;temporary failure in name resolution&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no route to host&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这些报错看起来类似，但根因完全不同。&lt;/p&gt;
&lt;h2&gt;2. 先看网络拓扑&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;docker compose ps
docker network ls
docker network inspect &amp;lt;network_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;目标：确认服务是否在同一个 compose network。&lt;/p&gt;
&lt;h2&gt;3. 服务名解析规则&lt;/h2&gt;
&lt;p&gt;在 Compose 网络内，服务间应通过 &lt;code&gt;service name:port&lt;/code&gt; 互访。&lt;/p&gt;
&lt;p&gt;错误示例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在容器 A 里访问 &lt;code&gt;localhost:5432&lt;/code&gt; 连接数据库 B&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;正确示例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用 &lt;code&gt;db:5432&lt;/code&gt;（&lt;code&gt;db&lt;/code&gt; 为服务名）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. &lt;code&gt;depends_on&lt;/code&gt; 不是“服务就绪”&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;depends_on&lt;/code&gt; 只保证启动顺序，不保证数据库已经可连接。&lt;/p&gt;
&lt;p&gt;建议加健康检查和重试逻辑：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;services:
  app:
    depends_on:
      db:
        condition: service_healthy
  db:
    healthcheck:
      test: [&quot;CMD&quot;, &quot;pg_isready&quot;, &quot;-U&quot;, &quot;postgres&quot;]
      interval: 5s
      timeout: 3s
      retries: 10
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. 端口映射与容器内访问的区别&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ports&lt;/code&gt; 用于“宿主机 &amp;lt;-&amp;gt; 容器”&lt;/li&gt;
&lt;li&gt;容器之间互访不依赖 &lt;code&gt;ports&lt;/code&gt;，依赖内部网络&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;很多人误以为不映射端口就不能互访，这是典型误区。&lt;/p&gt;
&lt;h2&gt;6. 快速诊断命令&lt;/h2&gt;
&lt;p&gt;在容器内执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;getent hosts db
nc -vz db 5432
curl -I http://api:8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这三步能快速判断是 DNS、端口还是服务本身问题。&lt;/p&gt;
&lt;h2&gt;7. 常见修复动作&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;统一服务到同一个 user-defined network&lt;/li&gt;
&lt;li&gt;把跨容器访问地址改为服务名&lt;/li&gt;
&lt;li&gt;为依赖服务加 healthcheck&lt;/li&gt;
&lt;li&gt;应用端加启动重试机制&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;Compose 网络故障的本质是“认知模型错误”。
只要你把“容器内 localhost 只指向自己”这件事吃透，80% 的互访问题都能一次定位。&lt;/p&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2025/06/28/nginx-502-504-troubleshooting/&quot;&gt;Nginx 502/504 排错指南&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2025/03/18/cloudflare-pages-performance-security/&quot;&gt;Cloudflare Pages 性能与安全优化&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/06/18/linux-server-security-baseline/&quot;&gt;Linux 服务器安全基线实战&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>SSH 密钥安全指南：五步防止服务器被黑</title><link>https://levifree.com/posts/ssh-key-security/</link><guid isPermaLink="true">https://levifree.com/posts/ssh-key-security/</guid><description>扔掉密码登录！打造银行级的 Linux 登录防线</description><pubDate>Wed, 05 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;任何一台暴露在公网的 Linux 服务器，每天都会遭受成千上万次的暴力破解攻击。如果你还在使用 &lt;code&gt;root&lt;/code&gt; 密码登录，那你就像是把家门钥匙放在了门口地垫下。&lt;/p&gt;
&lt;p&gt;本文将教你通过配置 SSH 密钥对，彻底关闭密码登录入口，让黑客无门可入。&lt;/p&gt;
&lt;h2&gt;1. 原理：为什么密钥比密码安全？&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;密码&lt;/strong&gt;：也就是 &quot;Something you know&quot;。可能被猜解、被字典甚至被中间人拦截。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;私钥&lt;/strong&gt;：也就是 &quot;Something you have&quot;。使用非对称加密算法（如 Ed25519 或 RSA）。只有拥有私钥文件的人才能解开服务器发来的挑战码。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. 生成高强度密钥对 (Ed25519)&lt;/h2&gt;
&lt;p&gt;虽然 RSA 是老牌标准，但在 2026 年，推荐使用更短、更快且更安全的 &lt;strong&gt;Ed25519&lt;/strong&gt; 算法。&lt;/p&gt;
&lt;p&gt;在你的本机（Mac/Windows）终端运行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh-keygen -t ed25519 -C &quot;your_email@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;这会生成 &lt;code&gt;id_ed25519&lt;/code&gt; (私钥，严禁泄露) 和 &lt;code&gt;id_ed25519.pub&lt;/code&gt; (公钥)。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;强烈建议&lt;/strong&gt; 设置 passphrase（密钥密码），这样即使私钥文件被偷，没有密码也无法使用。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 将公钥部署到服务器&lt;/h2&gt;
&lt;p&gt;使用 &lt;code&gt;ssh-copy-id&lt;/code&gt; 命令是最简单的方法：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ssh-copy-id -i ~/.ssh/id_ed25519.pub user@server_ip
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果手动配置，就是将公钥内容追加到服务器的 &lt;code&gt;~/.ssh/authorized_keys&lt;/code&gt; 文件中。&lt;/p&gt;
&lt;h2&gt;4. 关键：禁用密码登录&lt;/h2&gt;
&lt;p&gt;上传完公钥并确认能成功登录后，必须&lt;strong&gt;禁止密码登录&lt;/strong&gt;，否则以上工作白做。&lt;/p&gt;
&lt;p&gt;编辑服务器的 &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 禁用 root 用户登录 (可选，建议)
PermitRootLogin no

# 禁用密码认证 (必须)
PasswordAuthentication no

# 禁用空密码
PermitEmptyPasswords no

# 启用公钥认证
PubkeyAuthentication yes
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重启 SSH 服务：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo service ssh restart
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;sudo service ssh restart&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
### ⚠️ 关键安全警告 (保命技巧)

在执行 `restart` 重启 SSH 服务之前，**千万不要关闭当前的终端窗口！**
请新开一个终端窗口尝试连接服务器。
*   **如果成功**：恭喜，配置完成。
*   **如果失败**：你还有救！在原来的窗口中把 `sshd_config` 改回去并再次重启服务。

如果全关了且连不上，你只能去云服务商的网页控制台（VNC/Console）救急了。

## 5. 进阶防护

为了固若金汤，你还可以：
1.  **修改默认端口**：将 SSH 端口从 22 改为不常用的（如 22222），能过滤掉 99% 的脚本扫描。
2.  **Fail2Ban**：安装 Fail2Ban 软件，当检测到有人连续 N 次尝试登录失败时，自动在防火墙层面拉黑其 IP。

## 总结

安全没有绝对，只有成本。通过配置 SSH 密钥+禁用密码，我们将攻击者的破解成本从“跑字典”提升到了“攻破加密算法”的天文数字级别。这是每个服务器管理员的基本素养。&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>技术博客内容集群怎么做：用现有文章建立读者能走通的阅读路径</title><link>https://levifree.com/posts/static-blog-content-cluster-strategy/</link><guid isPermaLink="true">https://levifree.com/posts/static-blog-content-cluster-strategy/</guid><description>拒绝孤岛式发文；从支柱页设计、上下文内链编织到长尾流量收敛，用 Topic Cluster 架构彻底摆脱低价值内容评级</description><pubDate>Tue, 14 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;许多技术站长在运营博客时都会陷入一个常见误区：&lt;strong&gt;“只要我坚持每周发一篇，文章数量上来了，流量和 AdSense 收益自然会来。”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;然而现实往往是残酷的：博客发布了三四十篇文章，Google 却给出了“低价值内容”或“缺乏实质性独特价值”的审核驳回。深层原因在于，这些文章是以**孤岛（Information Silo）**的形式存在——彼此之间没有语义逻辑关联，读者看完一篇就直接关闭网页，搜索引擎爬虫也只能在离散的 URL 间盲目游走，无法感知站点在某一技术领域的系统深度和权威度（Topical Authority）。&lt;/p&gt;
&lt;p&gt;破解这一困境的关键武器，就是建立专业的&lt;strong&gt;内容集群（Topic Cluster / 主题集群）&lt;/strong&gt;。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一、传统孤岛模型 vs 现代内容集群模型&lt;/h2&gt;
&lt;p&gt;传统博客的文章组织通常仅依赖扁平的“标签（Tags）”或“分类（Category）”。一个标签下往往堆砌了十余篇互不相干的片段，读者根本找不到系统的阅读路径。&lt;/p&gt;
&lt;p&gt;内容集群模型则将零散文章重构为严密的拓扑网络：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                    ┌─────────────────────────┐
                    │    核心支柱页 (Pillar)   │
                    │  全面覆盖概念、全景路线图  │
                    └────────────┬────────────┘
                                 │ 双向强关联内链
         ┌───────────────────────┼───────────────────────┐
         ▼                       ▼                       ▼
┌──────────────────┐    ┌──────────────────┐    ┌──────────────────┐
│   实战教程 (HowTo) │    │   深入排错 (FAQ)   │    │  工具选型 (Versus) │
│ 详细步骤与生产配置 │    │ 错误日志与边缘案例 │    │ 性能对比与决策矩阵 │
└──────────────────┘    └──────────────────┘    └──────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;核心支柱页（Pillar Page）&lt;/strong&gt;：扮演“知识地图”与“总览入口”，系统性梳理整个技术体系的脉络，对各子话题提供高密度的概括性指引。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;集群子页面（Cluster Content）&lt;/strong&gt;：针对支柱页中的某一个具体分支（如特定错误排查、特定环境安装、深度重构技巧）进行纵深解析。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;上下文双向超链接（Contextual Hyperlinks）&lt;/strong&gt;：所有子页面均明确链接回支柱页，而子页面之间根据读者真实的排错路径进行横向穿透。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;二、实战演练：为个人技术博客构建两大高价值集群&lt;/h2&gt;
&lt;p&gt;以本站已有的文章资产为例，我们可以重组出两个极具竞争力的垂直技术集群：&lt;/p&gt;
&lt;h3&gt;集群 A：现代 Git 团队协作与分支工程&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心支柱页&lt;/strong&gt;：&lt;code&gt;git-sync-fork-upstream-guide.md&lt;/code&gt;（Fork 协作与上游同步全景）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;规范与流程子页&lt;/strong&gt;：&lt;code&gt;git-commit-convention.md&lt;/code&gt;（Conventional Commits 规范与提交原子性）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;决策分支子页&lt;/strong&gt;：&lt;code&gt;git-rebase-vs-merge-pr-workflow.md&lt;/code&gt;（团队工作流选型与变基准则）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;排错收敛子页&lt;/strong&gt;：&lt;code&gt;git-conflict-resolution-playbook.md&lt;/code&gt;（复杂三方冲突定位与恢复实战）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;内链构建示范&lt;/strong&gt;：
在讲解 &lt;code&gt;git-rebase-vs-merge-pr-workflow.md&lt;/code&gt; 时，绝不应在文末机械堆砌链接，而应在读者遇到痛点时适时提供出路：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“在执行 &lt;code&gt;git rebase upstream/main&lt;/code&gt; 期间，如果遇到多提交连续冲突，切勿强行 &lt;code&gt;git push -f&lt;/code&gt;。请参考这篇专项手册进行安全中断与逐段解决：&lt;a href=&quot;/posts/git-conflict-resolution-playbook&quot;&gt;《Git 冲突处理实战手册：从定位到收敛的标准流程》&lt;/a&gt;。”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;集群 B：现代化静态站点（SSG）性能与发布保障&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心支柱页&lt;/strong&gt;：&lt;code&gt;github-pages-personal-blog-guide.md&lt;/code&gt;（现代个人博客技术选型与发布全景）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;构建诊断子页&lt;/strong&gt;：&lt;code&gt;astro-local-preview-regression-checklist.md&lt;/code&gt;（发布前 15 分钟回归清单）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;排错实操子页&lt;/strong&gt;：&lt;code&gt;astro-static-assets-404-debugging.md&lt;/code&gt;（静态资源 404 定位与修复）&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;长效维护子页&lt;/strong&gt;：&lt;code&gt;technical-blog-update-policy.md&lt;/code&gt;（技术文章时效性与失效分级策略）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当读者搜索“Astro 资源 404”进入你的排错文章后，文章引导他阅读“发布前回归清单”，再引导他阅读“整站建站选型”，读者的单次会话时长（Session Duration）与浏览深度将提升 3~5 倍。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;三、上下文内链编织的三大铁律&lt;/h2&gt;
&lt;p&gt;为了防止被搜索引擎判定为“关键词堆砌”或“低质互链”，上下文内链必须严格遵循以下三项规范：&lt;/p&gt;
&lt;h3&gt;1. 锚文本（Anchor Text）必须具备明确的目的预期&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;❌ 错误示范：“关于这个知识点，请点击这里（链接到 /）或者参考这篇（链接到 /posts/xxx）。”（无明确语义，且占位死链会破坏用户体验与权重）&lt;/li&gt;
&lt;li&gt;✅ 正确示范：“若您在生产环境遇到了反向代理 502 Bad Gateway，请参阅&lt;a href=&quot;/posts/nginx-502-504-troubleshooting&quot;&gt;《Nginx 502/504 故障排错指南：从 upstream 到超时配置》&lt;/a&gt;。”&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 单向流动必须变为“回路闭环”&lt;/h3&gt;
&lt;p&gt;每个子页面都必须至少包含一条指向母主题支柱页的反向链接，确保搜索引擎爬虫通过任意长尾文章进入时，都能顺藤摸瓜抓取到整个集群的核心主干。&lt;/p&gt;
&lt;h3&gt;3. 消灭全站“孤儿页面（Orphan Pages）”&lt;/h3&gt;
&lt;p&gt;孤儿页面是指没有任何站内内链指向的文章。Google 爬虫极难索引这类页面，甚至会判定其为废弃或临时垃圾。每月使用脚本扫描一次文章内链，确保每篇发布文章至少被 2 篇以上同类文章推荐。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;四、内容集群的健康度度量指标&lt;/h2&gt;
&lt;p&gt;建立内容集群后，无需每天紧盯文章数量，而应关注以下 4 个质量指标：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;平均页面浏览深度（Pages per Session）&lt;/strong&gt;：健康的集群应让该指标稳定在 2.0 以上；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跳出率（Bounce Rate）下降幅度&lt;/strong&gt;：从孤岛式单页跳出（&amp;gt;80%）收敛至 50%~60%；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;长尾长词展现提升&lt;/strong&gt;：Google Search Console 中集群相关的主题词排名逐渐聚拢；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AdSense 广告可见度（Ad Viewability）&lt;/strong&gt;：由于用户在站内阅读链路延长，广告展示的真实价值大幅提升。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;将技术博客的内容运营从“散弹打鸟”升级为“集群作战”，不仅是让网站顺利通过 AdSense 质量审核的最佳解法，更是构建长期可信技术品牌的必经之路。&lt;/p&gt;
</content:encoded></item><item><title>个人博客到底值不值得建：从写第一篇文章到稳定发布的实际判断</title><link>https://levifree.com/posts/github-pages-personal-blog-guide/</link><guid isPermaLink="true">https://levifree.com/posts/github-pages-personal-blog-guide/</guid><description>拒绝盲目跟风与虚假情怀；从数据所有权、静态技术选型、零成本部署链路到长期运营抗风险能力进行全景拆解</description><pubDate>Tue, 30 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在算法信息流、短视频与中心化社交平台铺天盖地的今天，“搭建一个属于自己的独立博客”听起来似乎像是一种上个世纪的极客情怀。每隔一段时间，开发者社区都会冒出这样的讨论：&lt;strong&gt;“2026 年了，建独立博客到底还有意义吗？”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;答案是：&lt;strong&gt;如果你只是想追求短期的瞬时流量，独立博客毫无优势；但如果你想在混乱的互联网中夺回自己的‘数字主权’，沉淀可复用的专业技术资产，独立博客是当今性价比最高、最抗周期的选择。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;然而，建站也是有代价的。很多初学者在一腔热血下买了域名、套了华丽的主题，却在两周后因为复杂的依赖报错和空洞的内容弃坑。本文从一名长期维护技术博客的站长视角，剥离情怀，给出理性的决策依据与实操方案。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一、为什么依然需要独立博客：数据主权的四维收益&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;中心化平台 (知乎/掘金/Medium)          独立个人技术博客 (SSG + Git)
  - 算法降权与内容审查                   - 100% 数据归属与永久 Git 版本回溯
  - 平台倒闭或格式无法迁移               - 纯纯净 Markdown 随时一键导出
  - 强制插入平台推广广告                 - 自主决定商业化形式 (AdSense/赞助)
  - 割裂的作者个人品牌                   - 统一的独立域名与权威搜索引擎资产
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;绝对的数据所有权与防腐化&lt;/strong&gt;：所有文章都是本地 Git 仓库中的纯文本 Markdown 文件。无论平台如何洗牌，你的文字、代码和图表永远保存在自己的硬盘与 GitHub 私有/公开仓库中。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;搜索长尾与复利效应&lt;/strong&gt;：技术问题的生命周期极长。一篇高质量的排错实战文章，在发布 3 年后依然每天能从 Google、Bing 带来精准的工程受众。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;思维工程化倒逼&lt;/strong&gt;：将一个模糊的排错经过整理成任何人都能复现的标准文档，是对开发者技术深度最严苛的训练。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自主的商业化探索&lt;/strong&gt;：在站点具备稳定的原创内容和访问热度后，接入 Google AdSense 等合规广告联盟，能够在不打扰读者体验的前提下补贴服务器与域名开销。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;二、2026 静态建站工具链选型矩阵&lt;/h2&gt;
&lt;p&gt;对于技术博客，以 Astro、Hugo 为代表的现代化静态站点生成器（SSG）已经全面超越了传统的动态数据库方案（如 WordPress）：&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;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;strong&gt;Astro (推荐)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Vite + Island 架构&lt;/td&gt;
&lt;td&gt;极快 (&amp;lt;5s)&lt;/td&gt;
&lt;td&gt;★★★★★ (全面支持 Tailwind/Svelte)&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;strong&gt;Hugo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Go 语言单二进制&lt;/td&gt;
&lt;td&gt;毫秒级&lt;/td&gt;
&lt;td&gt;★★★☆☆ (Go 模板语法有学习曲线)&lt;/td&gt;
&lt;td&gt;极低 (无 node_modules)&lt;/td&gt;
&lt;td&gt;纯文字长篇作者、不愿折腾前端依赖的用户&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Jekyll&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Ruby&lt;/td&gt;
&lt;td&gt;较慢&lt;/td&gt;
&lt;td&gt;★★★☆☆ (历史沉淀丰富)&lt;/td&gt;
&lt;td&gt;中 (Ruby 环境跨平台略复杂)&lt;/td&gt;
&lt;td&gt;GitHub Pages 原生极简建站&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Next.js&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;React + SSR/SSG&lt;/td&gt;
&lt;td&gt;中等&lt;/td&gt;
&lt;td&gt;★★★★★&lt;/td&gt;
&lt;td&gt;较高 (需维护 Node 运行时与依赖)&lt;/td&gt;
&lt;td&gt;需要复杂全栈功能或会员系统的团队&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr /&gt;
&lt;h2&gt;三、零成本高可用部署架构实战&lt;/h2&gt;
&lt;p&gt;利用现代云计算的免费额度，个人完全可以搭建一套&lt;strong&gt;零运维成本、全球加速、双机备份&lt;/strong&gt;的现代化博客体系：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;                    ┌─────────────────────────┐
                    │      本地 Git 提交      │
                    └────────────┬────────────┘
                                 │ git push origin master
                                 ▼
                    ┌─────────────────────────┐
                    │  GitHub Actions (CI/CD)  │
                    │  - pnpm build 生产构建    │
                    │  - 静态代码检查与探针核验 │
                    └────────────┬────────────┘
                                 │
         ┌───────────────────────┴───────────────────────┐
         ▼                                               ▼
┌──────────────────┐                            ┌──────────────────┐
│   GitHub Pages   │                            │ Cloudflare Pages │
│  (gh-pages 分支) │                            │ (全球边缘 Anycast)│
└──────────────────┘                            └──────────────────┘
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;代码托管&lt;/strong&gt;：GitHub 免费公开/私有仓库；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自动化构建&lt;/strong&gt;：GitHub Actions 每月提供 2,000 分钟免费 CI/CD 额度；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;静态分发&lt;/strong&gt;：GitHub Pages 或 Cloudflare Pages 全球 CDN 加速，自带免费 Let&apos;s Encrypt / Cloudflare SSL 证书；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;全站年化开销&lt;/strong&gt;：仅需承担一个顶级域名的年费（约 50~80 元/年）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;四、从“冲动建站”到“长期运营”的三个避坑守则&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;切忌过早沉迷“装修博客”&lt;/strong&gt;：
无数人花了一个月调 CSS 动画、粒子背景和二次元看板娘，最后文章只写了一篇《Hello World》就彻底荒废。&lt;strong&gt;内容是骨肉，主题只是衣裳。&lt;/strong&gt; 先完成 5 篇解决具体问题的深度长文，再逐步打磨界面。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;警惕“伪原创与文档搬运”的陷阱&lt;/strong&gt;：
千万不要为了充门面，把官方安装文档复制粘贴成几十篇“速查手册”。Google AdSense 审核与搜索引擎早已能够通过语义大模型识别这类低价值内容。宁可每月只写 1 篇包含真实踩坑经验的干货，也不要灌水 10 篇文档翻译。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;建立极简的日常维护流&lt;/strong&gt;：
配置好自动化构建流水线与本地校验脚本，让写作体验纯粹回归到：打开 VS Code -&amp;gt; 编写 Markdown -&amp;gt; &lt;code&gt;pnpm verify&lt;/code&gt; -&amp;gt; &lt;code&gt;git push&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;独立博客不仅是一个网站，它是你在嘈杂互联网世界里属于自己的数字领地。迈出第一步，先写下你最近攻克的一个技术 Bug 吧。&lt;/p&gt;
</content:encoded></item><item><title>Tailwind CSS 为什么这么火？从原子化 CSS 看前端趋势</title><link>https://levifree.com/posts/why-tailwind-css/</link><guid isPermaLink="true">https://levifree.com/posts/why-tailwind-css/</guid><description>爱的人爱死，恨的人恨死：深入解析 Utility-First CSS 的设计哲学</description><pubDate>Tue, 05 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&quot;Best practices are actually just common practices.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;如果你是在 2026 年做前端开发，很难避开 Tailwind CSS。从 Next.js 到 GitHub 官网，越来越多的顶级项目开始全面拥抱它。&lt;/p&gt;
&lt;p&gt;但争议依然存在：“这不就是把行内样式 (Inline Styles) 写在 class 里吗？”
今天我们就来聊聊 Tailwind 到底解决了什么问题，以及为什么它值得你学习。&lt;/p&gt;
&lt;h2&gt;1. 传统 CSS 的困境&lt;/h2&gt;
&lt;p&gt;在写 BEM (Block Element Modifier) 或者 Bootstrap 时，我们要么受限于框架预设的组件（改起来很痛苦），要么就要绞尽脑汁给每一个 &lt;code&gt;div&lt;/code&gt; 起名字：&lt;code&gt;user-profile-wrapper-inner-container&lt;/code&gt;... 听着就累。&lt;/p&gt;
&lt;p&gt;而且，随着项目变大，CSS 只增不减。因为你不敢随便删除一个 CSS 类，怕影响到不知名角落的某个按钮。&lt;/p&gt;
&lt;h2&gt;2. Utility-First (原子化) 的优势&lt;/h2&gt;
&lt;p&gt;Tailwind 提倡直接在 HTML 中使用预定义的工具类，如 &lt;code&gt;flex&lt;/code&gt;, &lt;code&gt;pt-4&lt;/code&gt;, &lt;code&gt;text-center&lt;/code&gt;, &lt;code&gt;rotate-90&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;2.1 减少上下文切换&lt;/h3&gt;
&lt;p&gt;你不需要在 HTML 和 CSS 文件之间来回切换。想加个 padding？直接写 &lt;code&gt;p-4&lt;/code&gt;，思路不打断，开发效率极高。&lt;/p&gt;
&lt;h3&gt;2.2 此时此地 (Locality of Behavior)&lt;/h3&gt;
&lt;p&gt;所有的样式都以此处为准。改了这里的 class，只会影响这个元素，绝对不会有“牵一发而动全身”的副作用。这让维护遗留代码变得异常轻松。&lt;/p&gt;
&lt;h3&gt;2.3 极小的打包体积&lt;/h3&gt;
&lt;p&gt;Tailwind 会在构建时自动扫描你的代码，只打包你用到的 class。一个庞大的项目，最终的 CSS 文件可能只有 10KB。&lt;/p&gt;
&lt;h2&gt;3. 常见误解：HTML 会变得很丑？&lt;/h2&gt;
&lt;p&gt;是的，你的 HTML 可能会看起来像这样：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;button class=&quot;bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded&quot;&amp;gt;
  Button
&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;确实很长。但现在的组件化开发（React/Vue）完美解决了这个问题。你只需要把这一长串 class 写在一个 &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt; 组件里，然后在其他地方引用组件即可：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;Button&amp;gt;Click me&amp;lt;/Button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;原本的缺点，在组件化时代反而成了优点：样式与结构的高度内聚。&lt;/p&gt;
&lt;h2&gt;4. 2026 年的新趋势&lt;/h2&gt;
&lt;p&gt;随着 Tailwind v4 引擎的发布（假设），编译速度更是提升了 10 倍。配合 AI 辅助编程，你只需要告诉 Copilot &quot;创建一个带阴影的卡片&quot;，它会直接吐出完美的 Tailwind 代码。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;Tailwind CSS 可能不是银弹，但它是目前构建现代、响应式 Web 界面最高效的工具。一旦你习惯了这种“所想即所得”的开发流，就很难再回得去了。&lt;/p&gt;
</content:encoded></item><item><title>技术文档写作实战：把一次排错变成别人能复现的解决方案</title><link>https://levifree.com/posts/developer-documentation-writing-guide/</link><guid isPermaLink="true">https://levifree.com/posts/developer-documentation-writing-guide/</guid><description>拒绝写出‘照着敲依然跑不通’的低质教程；从环境基线设定、根因剖析、分步预期到回滚分支，构建真正具备生产复用价值的技术文章</description><pubDate>Tue, 22 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在日常开发与运维中，很多工程师和博主都有记录笔记的习惯。但仔细审视大量技术博客的文章，往往存在一个共通的顽疾：&lt;strong&gt;“作者自己本地跑通了，但读者复制文中的命令敲下去，第一步就报错。”&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;更糟糕的是，很多文章通篇只有三两行命令的拼凑，缺少前置依赖说明，缺少排错日志分析，缺少版本适用边界。在搜索引擎大模型与 Google AdSense 审核算法的审视下，这种文章会被直接打上“低价值薄弱内容（Thin Content）”的标签。&lt;/p&gt;
&lt;p&gt;一篇真正具备高传播度、高工程价值的技术文档究竟该怎么写？本文结合研发团队文档规范与个人技术博客运营经验，总结出一套可直接套用的实战方法论。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一、为什么多数教程“照着做依然翻车”？&lt;/h2&gt;
&lt;p&gt;技术写作中最致命的思维陷阱是&lt;strong&gt;知识的诅咒（The Curse of Knowledge）&lt;/strong&gt;。作者在解决问题的过程中，默默在本地安装了辅助依赖、调整了解析文件、或是默认使用了特定的高版本内核，却在成文时将这些“前置隐性条件”忽略不提。&lt;/p&gt;
&lt;p&gt;读者翻车通常发生在以下三个断裂点：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;环境与运行时隐性偏差&lt;/strong&gt;：例如代码依赖 Node.js 22 的新原生 API，但读者使用的是生产环境默认的 Node 18 LTS；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺乏“阶段性可观察输出”&lt;/strong&gt;：文章只给出了长篇命令，没有说明每敲完一步，终端理应输出什么日志。读者不知道当前阶段是成功还是处于假死状态；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;缺少失败分支与应急退路&lt;/strong&gt;：一旦中间某步返回异常，文章没有任何解释，读者除了关闭页面重新搜索别无选择。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;二、高价值技术文章的五段式黄金结构&lt;/h2&gt;
&lt;p&gt;为了让陌生读者无论何时打开文章，都能在自己的机器上稳定复现，建议将文章严格规范为五段式结构：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1. 明确问题上下文与验证标准 (Context &amp;amp; Boundaries)
   ▼
2. 根因剖析与原理机制 (Root Cause Analysis)
   ▼
3. 最小可操作步骤与输出核对 (Step-by-Step with Output)
   ▼
4. 边缘失败分支与排错树 (Troubleshooting Branches)
   ▼
5. 验证闭环与防回退机制 (Verification &amp;amp; Rollback)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;1. 明确问题上下文与验证边界&lt;/h3&gt;
&lt;p&gt;文章开篇第一段必须清晰给出三要素：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;遇到什么具体报错&lt;/strong&gt;（附带错误码或错误堆栈）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;环境基线&lt;/strong&gt;（操作系统发行版、核心软件版本、架构环境）；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最终达成的可验证状态&lt;/strong&gt;（例如“访问特定端点返回 HTTP 200，且日志无警告”）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 根因剖析：不仅要教“怎么做”，更要讲清“为什么”&lt;/h3&gt;
&lt;p&gt;不要一上来就扔命令。先用两到三段话解释问题的底层逻辑：是权限机制限制？是内核参数溢出？还是打包工具的路径处理差异？&lt;strong&gt;讲清原理的文章才具备不可替代的原创深度，这是模板采集站永远无法抄袭的灵魂。&lt;/strong&gt;&lt;/p&gt;
&lt;h3&gt;3. 步骤与阶段性预期核对&lt;/h3&gt;
&lt;p&gt;每个操作步骤必须遵循“操作 -&amp;gt; 预期输出 -&amp;gt; 确认方式”三位一体：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#### 步骤 2：生成并校验生产构建产物

执行构建命令：
```bash
pnpm build
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;预期输出&lt;/strong&gt;：
终端应在 5 秒内显示打包成功，并在 &lt;code&gt;dist/&lt;/code&gt; 目录下生成静态文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;✓ Built in 4.12s
dist/
├── _astro/
├── ads.txt
└── index.html
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果输出中出现 &lt;code&gt;[ERROR] Cannot find module&lt;/code&gt;，请先回退执行 &lt;code&gt;pnpm install --frozen-lockfile&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
### 4. 至少提供 2~3 个真实失败分支（Troubleshooting）
高价值内容的核心壁垒往往体现在“失败处理”上。写明在真实环境下读者最可能掉进的坑：
- 错误 A：端口已被占用（如 `listen tcp :80: bind: address already in use`）怎么查？
- 错误 B：权限不足导致文件写入被拒怎么处理？
- 错误 C：反向代理超时如何调整缓存缓冲区？

---

## 三、可直接复用的工程 Markdown 模板

你可以直接将以下模板保存为 VS Code Snippet 或博客写作规范：

```markdown
---
title: &quot;[实战/排错] 解决 XXX 故障的完整指南&quot;
published: 2026-XX-XX
description: &quot;从问题定位、根因解析到分步排查与防回退，一次性跑通 XXX 配置&quot;
tags: [&quot;技术标签1&quot;, &quot;排错&quot;, &quot;运维&quot;]
category: &quot;技术实战&quot;
---

## 问题背景与适用环境

在进行 [特定操作] 时，系统抛出以下异常：
```text
[Paste specific error log here]
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;验证环境&lt;/strong&gt;：Ubuntu 24.04 LTS / Docker 26.x / Node.js 22.x&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;本文目标&lt;/strong&gt;：消除上述报错，确保服务正常运行并通过健康探测。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;非目标&lt;/strong&gt;：本文不涉及复杂的集群多节点容灾方案。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;根本原因剖析&lt;/h2&gt;
&lt;p&gt;该问题的本质是由于……（在此处结合架构或底层逻辑进行原理解析）。&lt;/p&gt;
&lt;h2&gt;最小实施步骤&lt;/h2&gt;
&lt;h3&gt;1. [第一步操作名称]&lt;/h3&gt;
&lt;p&gt;执行命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 执行命令示例
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;预期现象：[描述屏幕输出或状态]&lt;/p&gt;
&lt;h3&gt;2. [第二步操作名称]&lt;/h3&gt;
&lt;p&gt;...&lt;/p&gt;
&lt;h2&gt;常见异常排查清单 (Troubleshooting)&lt;/h2&gt;
&lt;h3&gt;Q1: 遇到 XXX 报错该如何处理？&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;排查方法&lt;/strong&gt;：运行 &lt;code&gt;journalctl -u service -n 50&lt;/code&gt; 查看具体拒绝原因。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;解决方案&lt;/strong&gt;：...&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;回滚与防回退建议&lt;/h2&gt;
&lt;p&gt;若需回滚本次变更：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;还原配置文件：&lt;code&gt;git restore config.yml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;重启守护进程：&lt;code&gt;systemctl restart service&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过在本地运行 &lt;code&gt;curl -I https://localhost/health&lt;/code&gt; 确认服务完全恢复。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;
---

## 四、结语：把写作当作可执行代码来重构

优秀的技术文档就像经过严格单元测试的代码：**前置条件明确、执行路径单一、断言清晰、异常分支有防御。**

当你的每一篇博客文章都能让读者在几分钟内顺利解决痛点，不仅你的博客能够自然获得极高的用户停留时间与搜索认可，更能在面对 Google AdSense 等严格的网站质量审核时，轻松展现出令人信服的专业站长素养。&lt;/code&gt;&lt;/pre&gt;
</content:encoded></item><item><title>Nginx 502/504 排错指南：反向代理常见故障的定位与修复</title><link>https://levifree.com/posts/nginx-502-504-troubleshooting/</link><guid isPermaLink="true">https://levifree.com/posts/nginx-502-504-troubleshooting/</guid><description>从上游服务、超时配置到网络连通性，一步步排查 Bad Gateway 和 Gateway Timeout</description><pubDate>Sat, 28 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;502 Bad Gateway&lt;/code&gt; 和 &lt;code&gt;504 Gateway Timeout&lt;/code&gt; 是反向代理场景最常见的故障。
很多人直接重启服务，但不定位根因，问题会反复出现。&lt;/p&gt;
&lt;h2&gt;1. 先区分 502 与 504&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;502：Nginx 能联系上游，但上游返回异常或连接失败。&lt;/li&gt;
&lt;li&gt;504：Nginx 等待上游超时。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这一区分会决定你排查的方向。&lt;/p&gt;
&lt;h2&gt;2. 第一现场：看日志&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;sudo tail -n 200 /var/log/nginx/error.log
sudo tail -n 200 /var/log/nginx/access.log
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重点关注：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;connect() failed&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;upstream timed out&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no live upstreams&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 验证上游服务是否存活&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;curl -I http://127.0.0.1:8080
ss -tulpen | rg 8080
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果本机都访问不到上游，先修上游应用，不要先改 Nginx。&lt;/p&gt;
&lt;h2&gt;4. 检查 proxy 配置&lt;/h2&gt;
&lt;p&gt;常见问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;proxy_pass&lt;/code&gt; 地址写错&lt;/li&gt;
&lt;li&gt;上游端口变更未同步&lt;/li&gt;
&lt;li&gt;Host 头缺失导致上游路由失败&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;参考：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;location / {
  proxy_pass http://127.0.0.1:8080;
  proxy_set_header Host $host;
  proxy_set_header X-Real-IP $remote_addr;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_connect_timeout 5s;
  proxy_read_timeout 60s;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. 资源瓶颈排查&lt;/h2&gt;
&lt;p&gt;若故障偶发，重点看资源：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;top
free -m
df -h
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;高频根因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;内存不足导致上游进程被 OOM&lt;/li&gt;
&lt;li&gt;CPU 打满导致响应超时&lt;/li&gt;
&lt;li&gt;磁盘满导致日志或临时文件写入失败&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6. Docker 场景补充&lt;/h2&gt;
&lt;p&gt;如果 Nginx 和上游在 Docker：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;检查容器是否在同一 network&lt;/li&gt;
&lt;li&gt;&lt;code&gt;proxy_pass&lt;/code&gt; 应指向服务名而非 &lt;code&gt;localhost&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;容器重启策略是否合理&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;7. 修复后验证&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;nginx -t&lt;/code&gt; 检查配置合法&lt;/li&gt;
&lt;li&gt;平滑重载 &lt;code&gt;systemctl reload nginx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;连续压测/访问 5-10 分钟观察是否复发&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;502/504 的关键不是“重启好了”，而是建立稳定的排查顺序：日志 -&amp;gt; 上游 -&amp;gt; 配置 -&amp;gt; 资源。
流程固定后，故障恢复时间会显著缩短。&lt;/p&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2025/02/20/nginx-proxy-manager/&quot;&gt;Docker 部署 Nginx Proxy Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/06/18/linux-server-security-baseline/&quot;&gt;Linux 服务器安全基线实战&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2025/12/08/docker-compose-network-dns-troubleshooting/&quot;&gt;Docker Compose 网络与 DNS 故障排查&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>WebP vs AVIF 实战指南：如何在真实项目里做图片格式选型</title><link>https://levifree.com/posts/webp-vs-avif/</link><guid isPermaLink="true">https://levifree.com/posts/webp-vs-avif/</guid><description>不只看压缩率：结合编码耗时、兼容性、CDN 成本和 Core Web Vitals 的完整决策方法</description><pubDate>Mon, 16 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;前端性能优化里，图片通常是最大头。
很多站点把 JS 和 CSS 优化到极致，却仍然卡在 LCP（最大内容绘制），根因常常是首屏大图体积过高。&lt;/p&gt;
&lt;p&gt;WebP 和 AVIF 都能明显降低图片体积，但如果只看“谁更小”，你很容易做出错误决策。
这篇文章从工程角度给出完整方案：如何选、怎么落地、如何回归验证。&lt;/p&gt;
&lt;h2&gt;1. 先统一目标：你要优化什么指标&lt;/h2&gt;
&lt;p&gt;图片优化不是为了“文件小”，而是为了真实体验：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;LCP：首屏大图加载速度&lt;/li&gt;
&lt;li&gt;CLS：图片尺寸占位是否稳定&lt;/li&gt;
&lt;li&gt;TTFB 之后的首屏渲染路径&lt;/li&gt;
&lt;li&gt;CDN 出口流量成本&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你在做商业站点，还要考虑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;构建时间是否可控&lt;/li&gt;
&lt;li&gt;图片处理链路是否稳定&lt;/li&gt;
&lt;li&gt;运维复杂度是否可接受&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. WebP 和 AVIF 的核心差异&lt;/h2&gt;
&lt;h2&gt;2.1 压缩率&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;在同等主观画质下，AVIF 通常比 WebP 更小。&lt;/li&gt;
&lt;li&gt;但不同图像类型差异很大：
&lt;ul&gt;
&lt;li&gt;照片类：AVIF 优势通常更明显。&lt;/li&gt;
&lt;li&gt;图标/平面插画：WebP 已经足够优秀。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;结论：AVIF 往往更省带宽，但不是所有图都必须 AVIF。&lt;/p&gt;
&lt;h2&gt;2.2 编码与构建成本&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;WebP 编码速度通常更快，CI/CD 压力更低。&lt;/li&gt;
&lt;li&gt;AVIF 编码更耗 CPU，尤其在批量处理大图时，构建耗时会显著增长。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你的项目每日多次构建，且图片频繁更新，要优先评估 AVIF 带来的构建成本。&lt;/p&gt;
&lt;h2&gt;2.3 浏览器兼容与回退策略&lt;/h2&gt;
&lt;p&gt;现代浏览器对 AVIF 和 WebP 支持已经很完整，但工程上仍应保留回退方案。&lt;/p&gt;
&lt;p&gt;推荐做法：&lt;code&gt;AVIF -&amp;gt; WebP -&amp;gt; JPG/PNG&lt;/code&gt; 三层回退，使用 &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;picture&amp;gt;
  &amp;lt;source srcset=&quot;/img/hero.avif&quot; type=&quot;image/avif&quot;&amp;gt;
  &amp;lt;source srcset=&quot;/img/hero.webp&quot; type=&quot;image/webp&quot;&amp;gt;
  &amp;lt;img
    src=&quot;/img/hero.jpg&quot;
    alt=&quot;站点首页主视觉&quot;
    width=&quot;1200&quot;
    height=&quot;630&quot;
    loading=&quot;eager&quot;
    decoding=&quot;async&quot;
  &amp;gt;
&amp;lt;/picture&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这段代码同时解决两件事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;让高能力浏览器拿到更小资源&lt;/li&gt;
&lt;li&gt;让不支持新格式的环境稳定降级&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 什么时候选 WebP，什么时候优先 AVIF&lt;/h2&gt;
&lt;p&gt;你可以直接用下面这套决策逻辑：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;页面首屏关键图（Hero 图）
&lt;ul&gt;
&lt;li&gt;首选 AVIF（质量可接受前提下）&lt;/li&gt;
&lt;li&gt;同时保留 WebP 和 JPG 回退&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;中小尺寸内容图
&lt;ul&gt;
&lt;li&gt;WebP 作为默认选项，性价比高&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;后台管理系统或内部工具
&lt;ul&gt;
&lt;li&gt;优先 WebP，降低构建和维护复杂度&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;用户上传图片量很大
&lt;ul&gt;
&lt;li&gt;先统一 WebP，再对热点资源追加 AVIF&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;工程目标不是“全站 AVIF”，而是“在收益最高的位置用 AVIF”。&lt;/p&gt;
&lt;h2&gt;4. 实战：用 Sharp 批量生成多格式&lt;/h2&gt;
&lt;p&gt;如果你是 Node 项目，Sharp 是最稳的路线之一。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm i -D sharp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;示例脚本（&lt;code&gt;scripts/image-build.mjs&lt;/code&gt;）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import fs from &apos;node:fs/promises&apos;;
import path from &apos;node:path&apos;;
import sharp from &apos;sharp&apos;;

const inputDir = &apos;img/source&apos;;
const outputDir = &apos;img/optimized&apos;;

await fs.mkdir(outputDir, { recursive: true });
const files = await fs.readdir(inputDir);

for (const file of files) {
  const inputPath = path.join(inputDir, file);
  const ext = path.extname(file).toLowerCase();
  const base = path.basename(file, ext);

  if (![&apos;.jpg&apos;, &apos;.jpeg&apos;, &apos;.png&apos;].includes(ext)) continue;

  const image = sharp(inputPath).rotate();

  await image
    .clone()
    .webp({ quality: 78 })
    .toFile(path.join(outputDir, `${base}.webp`));

  await image
    .clone()
    .avif({ quality: 52, effort: 4 })
    .toFile(path.join(outputDir, `${base}.avif`));

  await image
    .clone()
    .jpeg({ quality: 82, mozjpeg: true })
    .toFile(path.join(outputDir, `${base}.jpg`));
}

console.log(&apos;image build done&apos;);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这份参数不是绝对标准，但适合大多数博客/内容站作为初始值。&lt;/p&gt;
&lt;h2&gt;5. 避免“图片变小但体验变差”&lt;/h2&gt;
&lt;p&gt;以下是常见反模式：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;只压缩，不裁剪：
你把 3840px 大图压成 AVIF，但页面只显示 800px，仍然浪费带宽。&lt;/li&gt;
&lt;li&gt;忽略尺寸占位：
没有写 &lt;code&gt;width/height&lt;/code&gt;，导致图片加载时页面抖动（CLS 上升）。&lt;/li&gt;
&lt;li&gt;首屏图片懒加载：
首屏主图设置 &lt;code&gt;loading=&quot;lazy&quot;&lt;/code&gt; 会拖慢 LCP。&lt;/li&gt;
&lt;li&gt;一刀切质量参数：
所有图统一质量值，可能造成局部明显糊图。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;正确做法是：先区分场景，再按场景设置参数。&lt;/p&gt;
&lt;h2&gt;6. CDN 与边缘优化策略&lt;/h2&gt;
&lt;p&gt;如果你使用 Cloudflare、CloudFront、Fastly 等 CDN，可以结合以下策略：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;根据 &lt;code&gt;Accept&lt;/code&gt; 头自动协商格式&lt;/li&gt;
&lt;li&gt;首屏关键图做多尺寸版本（&lt;code&gt;srcset&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;对热门资源设置更长缓存周期&lt;/li&gt;
&lt;li&gt;对低访问图使用更保守编码策略，减少构建成本&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这能把“构建侧压力”与“传输侧收益”做平衡。&lt;/p&gt;
&lt;h2&gt;7. 一套可执行的上线流程&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;选 3 张高流量页面图片作为试点。&lt;/li&gt;
&lt;li&gt;生成 AVIF/WebP/JPG 三格式并接入 &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;对比上线前后 7 天数据：
&lt;ul&gt;
&lt;li&gt;页面 LCP&lt;/li&gt;
&lt;li&gt;图片总流量&lt;/li&gt;
&lt;li&gt;错误率和兼容反馈&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;如果结果稳定，再扩展到其他页面。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;不要一次性重构整个站点，先用小样本跑通闭环。&lt;/p&gt;
&lt;h2&gt;8. 给博客站点的简化建议&lt;/h2&gt;
&lt;p&gt;如果你维护的是 Jekyll 或轻量博客：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;封面图和正文大图：三格式回退&lt;/li&gt;
&lt;li&gt;小图标：优先 SVG&lt;/li&gt;
&lt;li&gt;截图类教程图：优先 WebP（文字边缘更清晰）&lt;/li&gt;
&lt;li&gt;每篇文章控制图片总量，避免“图太多导致正文价值稀释”&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;WebP 和 AVIF 不是对立关系，而是组合关系。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;WebP：稳定、快、够用，适合默认策略。&lt;/li&gt;
&lt;li&gt;AVIF：体积优势明显，适合关键资源和高价值页面。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;先从核心页面做增量优化，再逐步扩展，才是长期可维护的性能策略。&lt;/p&gt;
</content:encoded></item><item><title>个人站监控实战：用 Uptime Kuma 建立可行动的故障告警</title><link>https://levifree.com/posts/self-hosted-monitoring-uptime-kuma-guide/</link><guid isPermaLink="true">https://levifree.com/posts/self-hosted-monitoring-uptime-kuma-guide/</guid><description>拒绝告警疲劳；从独立部署架构、关键字防伪探针、SSL 提前预警到 Telegram 告警模板设计，全流程落地高可用监控</description><pubDate>Wed, 21 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;对于个人站长和独立开发者而言，最尴尬的时刻莫过于：博客挂了整整三天，甚至因为 SSL 证书过期被浏览器标记为不安全，自己却浑然不知，直到读者发邮件提醒或者发现 AdSense 广告展示骤降才慌忙排查。&lt;/p&gt;
&lt;p&gt;搭建一套监控告警系统并不难，但很多人在安装了监控工具后很快陷入两种极端：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;监控裸奔&lt;/strong&gt;：只挂了一个 ping 探针，服务器网络通着但 Nginx 已经返回 502，探针依然显示一片绿色；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;告警疲劳（Alert Fatigue）&lt;/strong&gt;：将重试阈值设得过于激进，公网稍有瞬时网络抖动手机就疯狂震动，久而久之站长习惯性选择已读忽略，最终在真实故障降临时酿成灾难。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;本文将以广受好评的开源监控工具 &lt;strong&gt;Uptime Kuma&lt;/strong&gt; 为核心，详细分享如何以最低的资源占用搭建一套&lt;strong&gt;生产级、低误报、具有实操指导意义&lt;/strong&gt;的个人站高可用监控体系。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;一、监控系统的核心设计原则：异地隔离与可行动性&lt;/h2&gt;
&lt;p&gt;在敲下任何部署命令前，必须先建立两项安全原则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;监控必须异地部署（Out-of-band Monitoring）&lt;/strong&gt;：
千万不要把 Uptime Kuma 部署在你要监控的同一台博客服务器或同一个私有网络里。当机房断电、宿主机宕机或宿主机防火墙误封时，位于宿主机内部的监控系统也会一同失联，根本无法发出任何告警。推荐部署在独立的海外廉价 VPS（如甲骨文免费机、RackNerd 廉价实例）、Fly.io、或者自家的 NAS 设备上。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;每条告警必须携带“第一处置动作”&lt;/strong&gt;：
告警信息里如果只有一句“网站下线了”，夜间被通知吵醒的你依然需要重新抓瞎。一条合格的告警必须明确标注是“DNS 解析超时”、“SSL 剩余天数不足 7 天”还是“HTTP 返回了 502”，并直接给出对应的排查指令。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;二、生产级 Docker Compose 部署实战&lt;/h2&gt;
&lt;p&gt;我们使用 &lt;code&gt;docker-compose.yml&lt;/code&gt; 进行规范化管理，并挂载宿主机目录实现数据定期持久化备份：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: &apos;3.8&apos;

services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: always
    volumes:
      - /opt/uptime-kuma/data:/app/data
    ports:
      - &quot;127.0.0.1:3001:3001&quot; # 绑定本地回环，防止公网端口裸露
    environment:
      - UPTIME_KUMA_PORT=3001
    healthcheck:
      test: [&quot;CMD-SHELL&quot;, &quot;node extra/healthcheck.js&quot;]
      interval: 60s
      timeout: 10s
      retries: 3
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;反向代理与 SSL 加固（Nginx 配置片段）&lt;/h3&gt;
&lt;p&gt;通过 Nginx 将其代理至二级域名（如 &lt;code&gt;status.yourdomain.com&lt;/code&gt;），并配置 WebSocket 支持（Uptime Kuma 前后端通讯依赖 Socket.io）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
    listen 443 ssl http2;
    server_name status.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/status.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/status.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection &quot;upgrade&quot;;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;三、精细化探针配置实战指南&lt;/h2&gt;
&lt;p&gt;对于个人技术博客，配置以下 4 类监控探针即可形成无死角的防护网：&lt;/p&gt;
&lt;h3&gt;1. HTTP(s) - 关键字探针（防御 CDN 虚假 200）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;常规误区&lt;/strong&gt;：单纯检测 HTTP 状态码 200。当遇到 Cloudflare 盾机拦截、验证码挑战页、或者反向代理返回了“403 Forbidden”但 HTTP 头写成 200 时，普通监控会误以为站点正常。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：监控类型选择 &lt;code&gt;HTTP(s) - Keyword&lt;/code&gt;。监控 URL 填写首页地址，关键字填写站点的独有文案（例如网站标题 &lt;code&gt;Levi Blog&lt;/code&gt; 或博主昵称）。只有当响应体中真正渲染出该关键字时，探针才判定为正常。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重试阈值&lt;/strong&gt;：检测间隔建议设为 &lt;strong&gt;120 秒&lt;/strong&gt;，连续失败次数设为 &lt;strong&gt;3 次&lt;/strong&gt;。既能避免瞬时丢包误报，又能保证在故障发生 6 分钟内准时送达通知。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. TLS/SSL 证书到期倒计时探针&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;痛点&lt;/strong&gt;：acme.sh 或 Let&apos;s Encrypt 脚本可能因 DNS API Token 过期导致自动续期失败。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：开启“证书到期提醒”，分别在 &lt;strong&gt;到期前 14 天&lt;/strong&gt; 与 &lt;strong&gt;到期前 7 天&lt;/strong&gt; 触发预警，留出充裕的人工介入排查时间。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;3. DNS 记录一致性探针&lt;/h3&gt;
&lt;p&gt;监控顶级域名的 A 记录或 CNAME 解析结果是否发生漂移，及时发现域名被恶意篡改或解析服务商故障。&lt;/p&gt;
&lt;h3&gt;4. Push 探针（心跳监控静态博客自动化构建）&lt;/h3&gt;
&lt;p&gt;如果你的博客在 GitHub Actions 每天定时自动拉取更新，可以利用 Uptime Kuma 的 &lt;strong&gt;Push 监控项&lt;/strong&gt;。
在 Actions 脚本的末尾添加一行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -fsS -m 10 --retry 3 &quot;https://status.yourdomain.com/api/push/YOUR_PUSH_TOKEN?status=up&amp;amp;msg=Build+Success&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果某天 Actions 构建因依赖破坏挂掉而没有按期上报心跳，监控系统便会主动向你告警。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;四、打通 Telegram 机器人高优先级告警&lt;/h2&gt;
&lt;p&gt;在个人通知渠道中，Telegram Bot 的推送可靠性远高于容易被丢进垃圾箱的普通邮件：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在 Telegram 联系 &lt;code&gt;@BotFather&lt;/code&gt; 创建属于你的告警机器人，获取 &lt;code&gt;Bot Token&lt;/code&gt;；&lt;/li&gt;
&lt;li&gt;获取你的私聊 &lt;code&gt;Chat ID&lt;/code&gt;（可联系 &lt;code&gt;@userinfobot&lt;/code&gt; 获取）；&lt;/li&gt;
&lt;li&gt;在 Uptime Kuma 的 &lt;strong&gt;Settings -&amp;gt; Notification&lt;/strong&gt; 添加 Telegram 渠道；&lt;/li&gt;
&lt;li&gt;自定义通知模板，附带应急处理手册快速链接：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;🚨 【服务异常告警】
- 服务名称：{{NAME}}
- 异常详情：{{MSG}}
- 发生时间：{{TIME}}
- 首次处置动作：检查 Nginx 容器运行状态并查看 /var/log/nginx/error.log
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;五、打造透明的公共服务状态页（Status Page）&lt;/h2&gt;
&lt;p&gt;Uptime Kuma 内置了非常美观的 Public Status Page。你可以将博客的主页监控、API 节点、RSS 源与图床状态汇聚成公开页面，挂载到博客页脚：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;增强访客和合作伙伴对你网站运维水平的信心；&lt;/li&gt;
&lt;li&gt;在向 Google AdSense 或赞助商展示站点健康度时，提供无可辩驳的高可用运行记录。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;监控的终极目的从来不是制造焦虑，而是让你在安心工作与安稳睡眠的同时，对自己的数字资产保持百分之百的掌控力。&lt;/p&gt;
</content:encoded></item><item><title>提升 macOS 效率的 5 个终端 (Terminal) 技巧</title><link>https://levifree.com/posts/macos-terminal-tips/</link><guid isPermaLink="true">https://levifree.com/posts/macos-terminal-tips/</guid><description>摆脱鼠标依赖，让你的命令行操作行云流水</description><pubDate>Sat, 12 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;作为一名 macOS 用户，终端 (Terminal) 是我们最强大的工具之一。无论你是开发者还是普通的高级用户，掌握一些 Shell 技巧都能让你的工作效率倍增。&lt;/p&gt;
&lt;p&gt;本文将分享 5 个我每天都在用的 Zsh/Bash 小技巧，不需要安装复杂的插件，开箱即用。&lt;/p&gt;
&lt;h2&gt;1. 快速回到上一个目录 (&lt;code&gt;cd -&lt;/code&gt;)&lt;/h2&gt;
&lt;p&gt;这可能是最简单但最实用的命令。
当你在深层目录 &lt;code&gt;/Users/levi/project/src/components/&lt;/code&gt; 和系统目录 &lt;code&gt;/etc/nginx/&lt;/code&gt; 之间频繁切换时，不需要每次都打完整路径。&lt;/p&gt;
&lt;p&gt;只需输入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;cd -
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;系统就会自动把你带回“上一次所在的目录”。&lt;/p&gt;
&lt;h2&gt;2. 这里的命令太长了？用 &lt;code&gt;Ctrl + A&lt;/code&gt; 和 &lt;code&gt;Ctrl + E&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;当你在终端敲了一行巨长的命令，结果发现最开头的 &lt;code&gt;sudo&lt;/code&gt; 忘了加，或者拼写错了一个词：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;不要&lt;/strong&gt; 狂按左箭头键。&lt;/li&gt;
&lt;li&gt;按下 &lt;strong&gt;&lt;code&gt;Ctrl + A&lt;/code&gt;&lt;/strong&gt;：光标瞬间跳到行首。&lt;/li&gt;
&lt;li&gt;按下 &lt;strong&gt;&lt;code&gt;Ctrl + E&lt;/code&gt;&lt;/strong&gt;：光标瞬间跳到行尾。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这两个快捷键是 Readline 的标准配置，在大多数 Shell 环境中通用。&lt;/p&gt;
&lt;h2&gt;3. 搜索历史命令 (&lt;code&gt;Ctrl + R&lt;/code&gt;)&lt;/h2&gt;
&lt;p&gt;这是一个能让你显得很“极客”的操作。
如果你想执行几天前运行过的一条复杂的 &lt;code&gt;docker run&lt;/code&gt; 命令，但记不清具体参数了？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;按下 &lt;strong&gt;&lt;code&gt;Ctrl + R&lt;/code&gt;&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;输入关键字（比如 &lt;code&gt;docker&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;终端会自动显示最近匹配的一条历史记录。&lt;/li&gt;
&lt;li&gt;如果你想找更早之前的，继续按 &lt;code&gt;Ctrl + R&lt;/code&gt; 即可。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;4. 别名 (Alias) 解放双手&lt;/h2&gt;
&lt;p&gt;如果你发现自己经常输入很长的命令，比如 &lt;code&gt;git commit -m &quot;update&quot;&lt;/code&gt;，一定要设置别名。&lt;/p&gt;
&lt;p&gt;编辑你的 &lt;code&gt;~/.zshrc&lt;/code&gt; 文件：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 快速提交
alias gcm=&quot;git commit -m&quot;
# 快速查看状态
alias gs=&quot;git status&quot;
# 显示隐藏文件并易读大小
alias ll=&quot;ls -lah&quot;
# 快速刷新DNS缓存
alias flushdns=&quot;sudo dscacheutil -flushcache; sudo killall -HUP mDNSResponder&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;保存后运行 &lt;code&gt;source ~/.zshrc&lt;/code&gt;，以后只需要打 &lt;code&gt;gcm &quot;message&quot;&lt;/code&gt; 就行了。&lt;/p&gt;
&lt;h2&gt;5. &lt;code&gt;open .&lt;/code&gt; 图形化打开当前文件夹&lt;/h2&gt;
&lt;p&gt;有时候在终端里操作文件很累，想用 Finder（访达）来拖拽文件？
直接输入：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;open .
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;macOS 会立即弹出一个 Finder 窗口，显示的正是你当前终端所在的目录。这个技巧在管理博客图片资源时特别好用。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;工具的意义在于服务人。这些终端技巧虽然微小，但每天节省下来的几秒钟积累起来，就是巨大的效率提升。&lt;/p&gt;
&lt;p&gt;如果你有其他独家秘籍，欢迎在评论区分享！&lt;/p&gt;
</content:encoded></item><item><title>Cloudflare Pages 性能与安全配置：先验证默认行为，再做最小改动</title><link>https://levifree.com/posts/cloudflare-pages-performance-security/</link><guid isPermaLink="true">https://levifree.com/posts/cloudflare-pages-performance-security/</guid><description>从 Pages 默认缓存、静态响应头到上线后的验证，避免为了优化而制造陈旧内容或安全回归</description><pubDate>Tue, 18 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Cloudflare Pages 的性能优化最容易犯的错误，是还没确认默认行为就叠加缓存规则。Pages 已为静态资源提供 CDN 缓存与 ETag；自定义规则应只解决已经测到的问题，而不是作为默认动作。&lt;a href=&quot;https://developers.cloudflare.com/pages/configuration/serving-pages/&quot;&gt;Cloudflare 的 Pages 文档&lt;/a&gt; 也明确提醒，额外缓存可能造成部署后的陈旧资源。&lt;/p&gt;
&lt;h2&gt;1. 先建立基线&lt;/h2&gt;
&lt;p&gt;在修改前记录三个页面：首页、带图片的文章页、404 页。用浏览器网络面板或 &lt;code&gt;curl -I&lt;/code&gt; 保存响应状态、&lt;code&gt;cache-control&lt;/code&gt;、&lt;code&gt;etag&lt;/code&gt; 和资源体积。部署后使用同一组 URL 对比，而不是只看一次 Lighthouse 分数。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -I https://example.com/
curl -I https://example.com/assets/app.abc123.css
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果 HTML 更新后仍显示旧版本，先确认是否已经完成新部署，再排查浏览器缓存与自定义 Cache Rule；不要一开始就给所有路径设置超长 TTL。&lt;/p&gt;
&lt;h2&gt;2. 只对不可变资源加浏览器缓存&lt;/h2&gt;
&lt;p&gt;带内容哈希的 CSS、JS 与构建产物适合长缓存，因为文件名变化就代表内容变化。不要把同样的规则套到文章 HTML、RSS 或 API 数据上。Cloudflare Pages 支持在静态资产目录放置 &lt;code&gt;_headers&lt;/code&gt; 文件；若项目使用 Functions/SSR，响应头应在函数响应中设置，而不是依赖 &lt;code&gt;_headers&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/assets/*
  Cache-Control: public, max-age=31556952, immutable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上线后强制刷新一次首页，并在无痕窗口确认新部署的 HTML 能引用到新资源。出现旧页面时，立即撤回该规则或清理缓存，再重新验证。&lt;/p&gt;
&lt;h2&gt;3. 采用低风险安全响应头&lt;/h2&gt;
&lt;p&gt;静态站可从不会破坏站内资源的头开始：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/*
  X-Content-Type-Options: nosniff
  Referrer-Policy: strict-origin-when-cross-origin
  Permissions-Policy: camera=(), microphone=(), geolocation=()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Content-Security-Policy&lt;/code&gt; 需要在真实页面加载后再逐步收紧。先从 report-only 或明确列出已有脚本、字体、图片域名开始；直接套用 &lt;code&gt;default-src &apos;self&apos;&lt;/code&gt; 可能使分析、评论或字体加载失效。&lt;/p&gt;
&lt;h2&gt;4. 上线验收清单&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;首页、文章页与 404 返回正确状态码。&lt;/li&gt;
&lt;li&gt;主 CSS、JS 和图片没有 404。&lt;/li&gt;
&lt;li&gt;新部署后无痕窗口可立即看到更新。&lt;/li&gt;
&lt;li&gt;移动端菜单、搜索与外部组件没有被 CSP 阻断。&lt;/li&gt;
&lt;li&gt;仅在确定内容完全静态时才扩大缓存范围。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这套顺序的目标不是“配置越多越好”，而是让每一条规则都有可复核的收益和明确回滚点。&lt;/p&gt;
</content:encoded></item><item><title>AdSense 低价值内容恢复实战：个人技术博客 30 天优化清单</title><link>https://levifree.com/posts/adsense-low-value-content-recovery/</link><guid isPermaLink="true">https://levifree.com/posts/adsense-low-value-content-recovery/</guid><description>从内容结构、站点信号到复审节奏，给出可执行的整改路径</description><pubDate>Sun, 09 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;当 AdSense 给出“低价值内容”时，很多站长第一反应是疯狂加文章数量。
但实操里，复审是否通过，往往看的是“整体站点质量信号”，而不只是文章篇数。&lt;/p&gt;
&lt;p&gt;这篇文章分享一套可以在 30 天内执行的整改流程，适用于技术博客和个人站。&lt;/p&gt;
&lt;h2&gt;1. 先判断：什么是“低价值内容”&lt;/h2&gt;
&lt;p&gt;从站长实务角度，常见触发点有 5 类：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;内容太薄：文章字数少、信息密度低、可执行细节不足。&lt;/li&gt;
&lt;li&gt;重复或同质化：主题重复、段落模板化、观点高度雷同。&lt;/li&gt;
&lt;li&gt;页面信号不足：缺少清晰的关于页、隐私政策、联系方式、导航结构。&lt;/li&gt;
&lt;li&gt;站点体验弱：页面加载慢、移动端体验差、广告位布局干扰阅读。&lt;/li&gt;
&lt;li&gt;更新和覆盖不足：内容量和更新频率与站点定位不匹配。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;注意：这类问题通常是“组合出现”，不是单点故障。&lt;/p&gt;
&lt;h2&gt;2. 先做站点级体检（第 1-3 天）&lt;/h2&gt;
&lt;p&gt;先不要急着重投。先做体检，确认短板在哪。&lt;/p&gt;
&lt;h3&gt;2.1 内容层&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;统计所有文章长度（建议目标：核心文至少 1200-2000 中文字）&lt;/li&gt;
&lt;li&gt;统计每篇是否包含：问题定义、步骤、示例、风险提示、总结&lt;/li&gt;
&lt;li&gt;找出重复选题（标题不同但内容类似）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.2 架构层&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;导航是否包含：首页、归档、关于、隐私政策&lt;/li&gt;
&lt;li&gt;是否有明确作者信息与联系方式&lt;/li&gt;
&lt;li&gt;&lt;code&gt;robots.txt&lt;/code&gt;、&lt;code&gt;sitemap.xml&lt;/code&gt;、&lt;code&gt;ads.txt&lt;/code&gt; 是否可访问&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.3 体验层&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;移动端阅读是否顺畅&lt;/li&gt;
&lt;li&gt;首屏是否被广告或弹层干扰&lt;/li&gt;
&lt;li&gt;页面是否有明显失效链接/失效图片&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 内容整改：从“泛介绍”升级为“可执行指南”（第 4-20 天）&lt;/h2&gt;
&lt;p&gt;整改优先级建议：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先改历史流量高页面（ROI 最高）&lt;/li&gt;
&lt;li&gt;再改字数低、信息密度低的页面&lt;/li&gt;
&lt;li&gt;最后补新文，形成主题集群&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;3.1 高价值文章的统一结构&lt;/h3&gt;
&lt;p&gt;可以直接套这个模板：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;背景和问题边界（这篇文解决什么，不解决什么）&lt;/li&gt;
&lt;li&gt;前置条件（环境、版本、依赖）&lt;/li&gt;
&lt;li&gt;分步实操（每步给命令或截图）&lt;/li&gt;
&lt;li&gt;常见错误与排查（至少 3 个）&lt;/li&gt;
&lt;li&gt;安全与合规提示&lt;/li&gt;
&lt;li&gt;总结与后续延伸&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;3.2 提升原创度的方法&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;加入真实项目中的决策理由，而不只是“怎么做”&lt;/li&gt;
&lt;li&gt;记录失败案例和取舍过程&lt;/li&gt;
&lt;li&gt;给出你自己的参数基线和验证结果&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这些内容是模板站和采集站最难复制的部分，也是“价值信号”的核心。&lt;/p&gt;
&lt;h2&gt;4. 新增内容策略：少而精，不追灌水（第 10-25 天）&lt;/h2&gt;
&lt;p&gt;建议采用“主题集群”思路，而不是随机发文：&lt;/p&gt;
&lt;p&gt;示例（技术博客）：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;集群 A：静态站点性能优化&lt;/li&gt;
&lt;li&gt;集群 B：Linux 运维安全基线&lt;/li&gt;
&lt;li&gt;集群 C：开发效率与自动化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;每个集群至少 3 篇：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;入门概览（地图文）&lt;/li&gt;
&lt;li&gt;实操教程（步骤文）&lt;/li&gt;
&lt;li&gt;问题排查（FAQ 文）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样站点的专业定位会更清晰，广告系统也更容易识别内容主题。&lt;/p&gt;
&lt;h2&gt;5. 合规清理：哪些内容要删或重写&lt;/h2&gt;
&lt;p&gt;对于个人技术博客，建议清理这几类内容：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;只有几百字的“泛泛介绍”且无任何实操细节&lt;/li&gt;
&lt;li&gt;过时严重且命令不可用的旧教程&lt;/li&gt;
&lt;li&gt;标题党或夸大收益、引导误解的页面&lt;/li&gt;
&lt;li&gt;未经授权搬运内容&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;处理方式：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;可修复：补实操和版本说明后保留&lt;/li&gt;
&lt;li&gt;不可修复：删除并做 301 重定向或归档说明&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6. 复审前的最低可交付标准（第 21-27 天）&lt;/h2&gt;
&lt;p&gt;建议至少满足：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;站内高质量原创文 &amp;gt;= 20 篇（优先深度，不要凑数）&lt;/li&gt;
&lt;li&gt;近 30 天有稳定更新（例如每周 2 篇）&lt;/li&gt;
&lt;li&gt;重要页面完整可访问（About/Privacy/Contact/ads.txt）&lt;/li&gt;
&lt;li&gt;无明显违规或高风险内容&lt;/li&gt;
&lt;li&gt;移动端可读性正常&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;7. 复审节奏与预期（第 28-30 天）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;完成整改后再提交复审，不要频繁秒投。&lt;/li&gt;
&lt;li&gt;每次复审之间至少留出一段稳定观察期。&lt;/li&gt;
&lt;li&gt;记录每次改动清单，便于定位有效动作。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;复审不是抽奖，核心是站点整体质量是否显著改善。&lt;/p&gt;
&lt;h2&gt;8. 给独立博客的最后建议&lt;/h2&gt;
&lt;p&gt;AdSense 的“低价值内容”本质上是一个信号：
你的网站在“可读性、专业度、可信度”三个维度还不够强。&lt;/p&gt;
&lt;p&gt;真正有效的策略不是“多发”，而是“发有复用价值、可执行、可验证的内容”。&lt;/p&gt;
&lt;p&gt;当你持续做这件事，广告通过只是副产品。&lt;/p&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2025/03/09/jekyll-content-quality-checklist/&quot;&gt;Jekyll 发布前内容质量自检模板&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2025/10/14/static-blog-content-cluster-strategy/&quot;&gt;静态博客内容集群策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/07/16/jekyll-seo-technical-optimization/&quot;&gt;Jekyll SEO 技术优化清单&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Jekyll 博客内容质量清单：发布前 15 分钟自检模板</title><link>https://levifree.com/posts/jekyll-content-quality-checklist/</link><guid isPermaLink="true">https://levifree.com/posts/jekyll-content-quality-checklist/</guid><description>把“写完就发”升级为“可复用资产”：覆盖可读性、SEO、可信度和合规</description><pubDate>Sun, 09 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;技术博客最常见的问题不是不会写，而是“写完就发，发完就沉底”。
要让文章长期带来搜索流量、读者收藏和广告价值，你需要一套发布前自检流程。&lt;/p&gt;
&lt;p&gt;这篇文章给你一份可以直接照抄的 15 分钟检查清单，适用于 Jekyll 站点。&lt;/p&gt;
&lt;h2&gt;1. 文章结构检查（3 分钟）&lt;/h2&gt;
&lt;p&gt;先确认内容是否具备“可执行”结构，而不是流水账。&lt;/p&gt;
&lt;p&gt;最低结构建议：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;问题定义：这篇文解决什么场景&lt;/li&gt;
&lt;li&gt;前置条件：环境、版本、依赖&lt;/li&gt;
&lt;li&gt;分步过程：每一步都能复现&lt;/li&gt;
&lt;li&gt;排错章节：至少 3 个常见报错&lt;/li&gt;
&lt;li&gt;总结：适用边界和下一步建议&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;如果缺少其中 2 项以上，这篇文章通常还不够成熟。&lt;/p&gt;
&lt;h2&gt;2. 信息密度检查（2 分钟）&lt;/h2&gt;
&lt;p&gt;快速判断“有没有干货”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;是否包含可复制命令&lt;/li&gt;
&lt;li&gt;是否说明参数为什么这么配&lt;/li&gt;
&lt;li&gt;是否覆盖失败分支（不是只写 happy path）&lt;/li&gt;
&lt;li&gt;是否给出明确版本信息&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;判断标准：读者不需要“猜你在说什么”。&lt;/p&gt;
&lt;h2&gt;3. SEO 基础检查（3 分钟）&lt;/h2&gt;
&lt;p&gt;Jekyll 文章发布前，至少核对这几项：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;title&lt;/code&gt; 是否清晰表达主题和人群&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subtitle&lt;/code&gt; 是否补足上下文&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tags&lt;/code&gt; 是否与正文一致（避免乱打标签）&lt;/li&gt;
&lt;li&gt;首段是否在 80-120 字内讲清价值&lt;/li&gt;
&lt;li&gt;是否有至少 2 个二级标题（&lt;code&gt;##&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你的站点有站内搜索，标题和首段决定了点击率上限。&lt;/p&gt;
&lt;h2&gt;4. 可信度检查（2 分钟）&lt;/h2&gt;
&lt;p&gt;技术文要建立可信度，至少做到：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;标注命令适用系统（macOS/Linux/Windows）&lt;/li&gt;
&lt;li&gt;标注版本（如 Node 20、Python 3.12）&lt;/li&gt;
&lt;li&gt;对高风险操作加警告&lt;/li&gt;
&lt;li&gt;对引用资料注明来源（官方文档优先）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可信度越高，读者停留时间和回访率越高。&lt;/p&gt;
&lt;h2&gt;5. 合规检查（2 分钟）&lt;/h2&gt;
&lt;p&gt;如果你依赖广告变现，这一步不能省。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;是否含有误导性标题&lt;/li&gt;
&lt;li&gt;是否包含潜在违规导向（攻击、绕过、侵权）&lt;/li&gt;
&lt;li&gt;是否出现“结论性断言但无依据”&lt;/li&gt;
&lt;li&gt;是否存在明显采集痕迹&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不确定的段落，宁可删掉，也不要赌审核。&lt;/p&gt;
&lt;h2&gt;6. Jekyll 站点级检查（2 分钟）&lt;/h2&gt;
&lt;p&gt;发布后快速检查：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;页面能否正常渲染&lt;/li&gt;
&lt;li&gt;内链是否可点击&lt;/li&gt;
&lt;li&gt;图片路径是否正确&lt;/li&gt;
&lt;li&gt;文章是否进入归档页&lt;/li&gt;
&lt;li&gt;&lt;code&gt;feed.xml&lt;/code&gt; 是否更新&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;你可以在本地先跑：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bundle exec jekyll serve
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后手动点开文章，检查移动端布局和代码高亮。&lt;/p&gt;
&lt;h2&gt;7. 一份可复制的发布前模板&lt;/h2&gt;
&lt;p&gt;你可以把下面这段直接放进你的草稿顶部：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[发布前检查]
- [ ] 标题是否准确且不夸张
- [ ] 首段是否说清目标读者与收益
- [ ] 是否有前置条件与版本信息
- [ ] 是否有可复制命令与示例
- [ ] 是否有常见错误与排查
- [ ] 是否标注风险操作
- [ ] 是否完成内链和图片检查
- [ ] 是否通过移动端阅读检查
- [ ] 是否通过合规检查
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当你连续执行 20 篇后，文章质量会显著稳定。&lt;/p&gt;
&lt;h2&gt;8. 建议的周节奏&lt;/h2&gt;
&lt;p&gt;如果你是个人站长，可用这个节奏：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;周一：选题与资料整理&lt;/li&gt;
&lt;li&gt;周三：写正文与代码示例&lt;/li&gt;
&lt;li&gt;周五：自检 + 发布&lt;/li&gt;
&lt;li&gt;周末：回看数据（搜索曝光、停留时长、跳出）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不要把“发布”当终点，数据复盘才是内容升级的开始。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;高质量内容不是灵感驱动，而是流程驱动。&lt;/p&gt;
&lt;p&gt;你只要把发布前自检流程固定下来，哪怕每周只发 1 篇，长期效果也会好过“高频低质”的更新方式。&lt;/p&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2025/07/22/developer-documentation-writing-guide/&quot;&gt;技术文档写作实战&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2025/03/09/adsense-low-value-content-recovery/&quot;&gt;AdSense 低价值内容恢复实战&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/07/16/jekyll-seo-technical-optimization/&quot;&gt;Jekyll SEO 技术优化清单&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Docker 部署 Nginx Proxy Manager: 小白也能搞定反向代理</title><link>https://levifree.com/posts/nginx-proxy-manager/</link><guid isPermaLink="true">https://levifree.com/posts/nginx-proxy-manager/</guid><description>告别繁琐的 Nginx 配置文件，用可视化面板轻松管理 SSL 证书与域名转发</description><pubDate>Thu, 20 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;如果你自己搭建过 Home Lab 或者 VPS 服务，一定遇到过这个问题：
我有多个服务（Portainer, Halo, Alist），它们运行在不同的端口（9000, 8090, 5244），但我只想用一个域名（如 &lt;code&gt;demo.com&lt;/code&gt;）的不同子域名来访问它们，而且都要加上 HTTPS 小绿锁。&lt;/p&gt;
&lt;p&gt;手写 Nginx 配置文件 (&lt;code&gt;nginx.conf&lt;/code&gt;) 不仅容易出错，而且申请和续期 SSL 证书也非常麻烦。&lt;/p&gt;
&lt;p&gt;今天推荐的神器 &lt;strong&gt;Nginx Proxy Manager (NPM)&lt;/strong&gt;，能让你在图形化界面中几分钟搞定这一切。&lt;/p&gt;
&lt;h2&gt;1. 为什么选择 Nginx Proxy Manager?&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;可视化 UI&lt;/strong&gt;：所有配置都在网页上点点点。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;自动 SSL&lt;/strong&gt;：内置 Let&apos;s Encrypt 支持，一键申请并自动续期证书。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docker 部署&lt;/strong&gt;：不污染宿主机环境，随时可以迁移。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;架构示意图&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;User (Browser)
      │
      ▼
[ Nginx Proxy Manager (Port 80/443) ] ────► [ Automatic SSL Management ]
      │
      ├───► demo.com      ────► [ Portainer (Port 9000) ]
      ├───► blog.demo.com ────► [ Halo Blog (Port 8090) ]
      └───► pan.demo.com  ────► [ Alist (Port 5244) ]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. Docker Compose 部署&lt;/h2&gt;
&lt;p&gt;首先，确保你的服务器已经安装了 Docker 和 Docker Compose。
创建一个文件夹并新建 &lt;code&gt;docker-compose.yml&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;version: &apos;3&apos;
services:
  app:
    image: &apos;jc21/nginx-proxy-manager:latest&apos;
    restart: unless-stopped
    ports:
      - &apos;80:80&apos;      # HTTP 流量
      - &apos;81:81&apos;      # 管理面板入口
      - &apos;443:443&apos;    # HTTPS 流量
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;启动服务：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker-compose up -d
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. 初始化配置&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;访问 &lt;code&gt;http://&amp;lt;服务器IP&amp;gt;:81&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;默认登录账号：&lt;code&gt;admin@example.com&lt;/code&gt;，密码：&lt;code&gt;changeme&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;首次登录后务必修改邮箱和密码。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;4. 实战：转发 Portainer 面板&lt;/h2&gt;
&lt;p&gt;假设你的 Portainer 运行在 &lt;code&gt;http://127.0.0.1:9000&lt;/code&gt;，你想用 &lt;code&gt;portainer.你的域名.com&lt;/code&gt; 访问。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;添加 Proxy Host&lt;/strong&gt;：点击 Dashboard 的 &quot;Proxy Hosts&quot; -&amp;gt; &quot;Add Proxy Host&quot;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Details 选项卡&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;Domain Names: &lt;code&gt;portainer.你的域名.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Scheme: &lt;code&gt;http&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Forward Hostname / IP: &lt;code&gt;172.17.0.1&lt;/code&gt; (这是 Docker 网桥的宿主机 IP，或者填公网 IP)&lt;/li&gt;
&lt;li&gt;Forward Port: &lt;code&gt;9000&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;勾选 &lt;code&gt;Block Common Exploits&lt;/code&gt; (基础防护)。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SSL 选项卡&lt;/strong&gt;：
&lt;ul&gt;
&lt;li&gt;SSL Certificate: 选择 &quot;Request a new SSL Certificate&quot;。&lt;/li&gt;
&lt;li&gt;勾选 &lt;code&gt;Force SSL&lt;/code&gt; (强制 HTTPS)。&lt;/li&gt;
&lt;li&gt;填写 Email 并同意协议。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保存&lt;/strong&gt;：点击 Save。等待几秒钟，NPM 会自动验证域名并申请证书。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;搞定！现在你可以通过安全的 HTTPS 链接访问你的面板了。&lt;/p&gt;
&lt;h2&gt;常见坑点与排查&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;SSL 申请失败 (Internal Error)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;检查端口&lt;/strong&gt;: 必须确保你服务器防火墙的 &lt;strong&gt;80&lt;/strong&gt; 和 &lt;strong&gt;443&lt;/strong&gt; 端口是对外开放的。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DNS 解析&lt;/strong&gt;: 确保你的域名已经正确解析到了服务器 IP，且生效（可以用 &lt;code&gt;ping&lt;/code&gt; 检查）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Email 未填&lt;/strong&gt;: 在申请证书时，Email 字段虽然可选，但建议填写，否则 Let&apos;s Encrypt 可能会拒绝请求。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;502 Bad Gateway&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IP 填写错误&lt;/strong&gt;: 在 Forward Hostname 中，千万&lt;strong&gt;不要填 &lt;code&gt;127.0.0.1&lt;/code&gt;&lt;/strong&gt;（除非 NPM 网络模式为 host）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;正确做法&lt;/strong&gt;: 填写 Docker 容器的局域网 IP，或者宿主机的内网 IP（如 &lt;code&gt;172.17.0.1&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;上传文件限制&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;默认 Nginx 配置限制了上传大小。如果遇到上传大文件失败，需要在 NPM 的 Proxy Host 配置 -&amp;gt; &lt;strong&gt;Advanced&lt;/strong&gt; 中添加：&lt;pre&gt;&lt;code&gt;client_max_body_size 0;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;5. 总结&lt;/h2&gt;
&lt;p&gt;Nginx Proxy Manager 极大降低了反向代理的门槛。对于个人开发者和家庭实验室用户来说，它兼顾了易用性和功能性，是必备的基础设施之一。&lt;/p&gt;
</content:encoded></item><item><title>重构的艺术：如何写出同事不骂街的 TypeScript 代码</title><link>https://levifree.com/posts/typescript-refactoring/</link><guid isPermaLink="true">https://levifree.com/posts/typescript-refactoring/</guid><description>代码是写给人看的，顺便给机器运行。5 个实用的重构小技巧</description><pubDate>Fri, 10 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&quot;Any fool can write code that a computer can understand. Good programmers write code that humans can understand.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;接手屎山代码（Spaghetti Code）是每个程序员的噩梦。为了不让你的代码成为别人的噩梦，我们来聊聊 TypeScript 项目中的重构艺术。&lt;/p&gt;
&lt;h2&gt;1. 拒绝 &lt;code&gt;any&lt;/code&gt;，拥抱类型&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;any&lt;/code&gt; 是 TypeScript 的逃生舱，但用多了就变成了 &lt;code&gt;AnyScript&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;坏味道&lt;/strong&gt;：&lt;code&gt;function process(data: any) { ... }&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重构&lt;/strong&gt;：即使不知道确切类型，也请使用 &lt;code&gt;unknown&lt;/code&gt;，迫使你在使用前进行类型收窄（Type Narrowing）。或者定义一个 &lt;code&gt;interface&lt;/code&gt;，哪怕只有部分字段。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. 卫语句 (Guard Clause) 代替嵌套 &lt;code&gt;if&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;不要写“箭头型”代码（嵌套太深）。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bad:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function processUser(user: User) {
  if (user != null) {
    if (user.isActive) {
      if (user.hasCredit) {
         // 核心逻辑...
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;function processUser(user: User) {
  if (!user || !user.isActive || !user.hasCredit) return;
  
  // 核心逻辑在主层级，清清爽爽
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. 魔法数字 (Magic Numbers) 必须死&lt;/h2&gt;
&lt;p&gt;不要在代码里直接写 &lt;code&gt;if (status === 2)&lt;/code&gt;。鬼知道 2 是什么意思？是“成功”？还是“失败”？还是“处理中”？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;重构&lt;/strong&gt;：使用 &lt;code&gt;enum&lt;/code&gt; 或 &lt;code&gt;const&lt;/code&gt; 常量。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;enum OrderStatus {
  Pending = 0,
  Processing = 1,
  Completed = 2
}

if (order.status === OrderStatus.Completed) { ... }
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 函数单一职责&lt;/h2&gt;
&lt;p&gt;一个函数应该只做一件事。
如果你发现你的函数名是 &lt;code&gt;fetchDataAndProcessAndSave()&lt;/code&gt;，那它绝对太长了。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;把“获取数据”拆出去。&lt;/li&gt;
&lt;li&gt;把“处理逻辑”拆出去。&lt;/li&gt;
&lt;li&gt;把“保存”拆出去。
主函数只负责像指挥官一样调用它们。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. 使用可选链 (Optional Chaining)&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Bad:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (user &amp;amp;&amp;amp; user.address &amp;amp;&amp;amp; user.address.street) {
  console.log(user.address.street);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;console.log(user?.address?.street);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;不仅代码短了，而且可读性极佳。&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;好的代码就像一篇好文章，读起来行云流水。重构不是为了炫技，而是为了让未来的自己（和同事）少掉几根头发。&lt;/p&gt;
</content:encoded></item><item><title>Python 自动化办公：5分钟写一个批量重命名脚本</title><link>https://levifree.com/posts/python-rename-script/</link><guid isPermaLink="true">https://levifree.com/posts/python-rename-script/</guid><description>告别枯燥的手工劳动，用几十行代码拯救你的下班时间</description><pubDate>Thu, 12 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;你是否遇到过这种情况：
老板发给你 500 张活动照片，文件名全是 &lt;code&gt;IMG_20260131_123456.jpg&lt;/code&gt; 这种乱码，要求你全部改成 &lt;code&gt;活动现场_001.jpg&lt;/code&gt;, &lt;code&gt;活动现场_002.jpg&lt;/code&gt;...？&lt;/p&gt;
&lt;p&gt;手动改？那你要改到明年。用 Python，只需要喝口水的功夫。&lt;/p&gt;
&lt;h2&gt;1. 准备工作&lt;/h2&gt;
&lt;p&gt;确保电脑已安装 Python。我们只需要用到内置的 &lt;code&gt;os&lt;/code&gt; 库，无需安装任何第三方包。&lt;/p&gt;
&lt;h2&gt;2. 核心代码&lt;/h2&gt;
&lt;p&gt;新建一个文件 &lt;code&gt;rename.py&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import os

def batch_rename(path, prefix):
    # 1. 确保路径存在
    if not os.path.exists(path):
        print(f&quot;路径 {path} 不存在！&quot;)
        return

    # 2. 获取该目录下所有文件
    files = os.listdir(path)
    # 按原始文件名排序，防止乱序
    files.sort()

    count = 0
    for filename in files:
        # 跳过隐藏文件（如 .DS_Store）和脚本自己
        if filename.startswith(&apos;.&apos;) or filename == &apos;rename.py&apos;:
            continue
            
        # 3. 构造新文件名
        # 获取文件扩展名 (如 .jpg)
        ext = os.path.splitext(filename)[1]
        
        # 格式化数字，例如 001, 002
        new_name = f&quot;{prefix}_{count+1:03d}{ext}&quot;
        
        old_file = os.path.join(path, filename)
        new_file = os.path.join(path, new_name)
        
        # 4. 执行重命名
        try:
            os.rename(old_file, new_file)
            print(f&quot;✅ {filename} -&amp;gt; {new_name}&quot;)
            count += 1
        except Exception as e:
            print(f&quot;❌ 重命名 {filename} 失败: {e}&quot;)

    print(f&quot;\n🎉 完成！共处理 {count} 个文件。&quot;)

if __name__ == &quot;__main__&quot;:
    # 在这里修改你的文件夹路径和想要的前缀
    target_path = &quot;./photos&quot;
    new_prefix = &quot;活动现场&quot;
    
    batch_rename(target_path, new_prefix)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. 代码解析&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;os.listdir(path)&lt;/code&gt;: 列出文件夹里所有的东西。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;path.join()&lt;/code&gt;: 智能拼接路径，不用担心 Windows/Mac 斜杠方向不一样的问题。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;f&quot;{prefix}_{count+1:03d}{ext}&quot;&lt;/code&gt;: 这是 Python 的 f-string 格式化大法。&lt;code&gt;:03d&lt;/code&gt; 的意思是“不足3位自动补零”，于是你就得到了 &lt;code&gt;001&lt;/code&gt;, &lt;code&gt;009&lt;/code&gt;, &lt;code&gt;010&lt;/code&gt; 这样整齐的序号。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;4. 扩展思路&lt;/h2&gt;
&lt;p&gt;一旦掌握了这个逻辑，你能做更多事：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;按日期归档&lt;/strong&gt;：读取文件的创建时间，自动把文件移动到 &lt;code&gt;2026-01&lt;/code&gt; 文件夹里。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;筛选文件&lt;/strong&gt;：只处理 &lt;code&gt;.pdf&lt;/code&gt; 文件，把 &lt;code&gt;.txt&lt;/code&gt; 删掉。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;学会编程思维，哪怕只是这种小脚本，也能极大提升你的职场幸福感。&lt;/p&gt;
</content:encoded></item><item><title>Git 冲突处理实战手册：从定位到收敛的一套标准流程</title><link>https://levifree.com/posts/git-conflict-resolution-playbook/</link><guid isPermaLink="true">https://levifree.com/posts/git-conflict-resolution-playbook/</guid><description>告别手忙脚乱：处理 merge/rebase 冲突时的步骤、命令与风险控制</description><pubDate>Tue, 03 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;代码冲突不可怕，可怕的是在冲突时临时发挥，把历史改乱、逻辑改坏。
这篇文章给你一个冲突处理 SOP，适合团队协作场景。&lt;/p&gt;
&lt;h2&gt;1. 先判断你在 merge 还是 rebase&lt;/h2&gt;
&lt;p&gt;命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果提示 &lt;code&gt;rebase in progress&lt;/code&gt;，处理方式和 merge 不同。&lt;/p&gt;
&lt;h2&gt;2. 冲突处理前的 3 条原则&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;先保证代码语义正确，再追求历史好看。&lt;/li&gt;
&lt;li&gt;冲突阶段不做额外需求改动。&lt;/li&gt;
&lt;li&gt;每次解决一小块，及时运行测试。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;3. Merge 冲突处理流程&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git pull origin main
# 出现冲突后，编辑冲突文件
# 删除 &amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; ======= &amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; 标记

git add &amp;lt;resolved-files&amp;gt;
git commit
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. Rebase 冲突处理流程&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;git fetch origin
git rebase origin/main
# 修复冲突

git add &amp;lt;resolved-files&amp;gt;
git rebase --continue
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;若发现 rebase 路线错误：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git rebase --abort
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. 最常见的 5 类冲突&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;同一行被两边同时修改&lt;/li&gt;
&lt;li&gt;文件重命名与内容修改同时发生&lt;/li&gt;
&lt;li&gt;删除文件与编辑文件冲突&lt;/li&gt;
&lt;li&gt;锁文件（如 &lt;code&gt;package-lock.json&lt;/code&gt;）频繁冲突&lt;/li&gt;
&lt;li&gt;自动格式化工具导致大面积无意义差异&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;6. 实用技巧&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git diff --name-only --diff-filter=U&lt;/code&gt;：只看冲突文件&lt;/li&gt;
&lt;li&gt;优先解决核心业务文件，再处理配置或格式文件&lt;/li&gt;
&lt;li&gt;锁文件冲突尽量通过重新生成解决，不手改复杂段落&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;7. 冲突后回归检查&lt;/h2&gt;
&lt;p&gt;至少执行：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;项目构建&lt;/li&gt;
&lt;li&gt;核心测试用例&lt;/li&gt;
&lt;li&gt;关键页面手动冒烟测试&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这一步不能省，否则“冲突虽解，功能已坏”。&lt;/p&gt;
&lt;h2&gt;8. 团队层面的预防&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;小步提交，减少超大 PR&lt;/li&gt;
&lt;li&gt;长分支每天同步上游&lt;/li&gt;
&lt;li&gt;合并前先跑 CI&lt;/li&gt;
&lt;li&gt;统一 formatter 和 lint 规则&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;冲突处理不是技术难题，而是工程纪律问题。
有流程、有边界、有回归检查，冲突就不会成为团队效率黑洞。&lt;/p&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2024/09/12/git-rebase-vs-merge-pr-workflow/&quot;&gt;Git Rebase vs Merge 工作流&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/08/20/git-commit-convention/&quot;&gt;Git Commit 规范指南&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2023/12/06/git-sync-fork-upstream-guide/&quot;&gt;Git Fork 同步上游实战&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>GitHub Actions 实战：为 Jekyll 博客搭建自动化质量检查流水线</title><link>https://levifree.com/posts/github-actions-blog-ci-cd/</link><guid isPermaLink="true">https://levifree.com/posts/github-actions-blog-ci-cd/</guid><description>每次提交自动检查 Markdown、死链与构建状态，减少线上翻车</description><pubDate>Mon, 18 Nov 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;很多个人博客只有“能发布”，但缺少“发布前自动验收”。
当文章多起来后，死链、Front Matter 错误、构建失败会频繁出现。&lt;/p&gt;
&lt;p&gt;这篇教程给你一个轻量 CI：每次 Push/PR 自动做三件事。&lt;/p&gt;
&lt;h2&gt;目标&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;校验 Markdown 基本规范&lt;/li&gt;
&lt;li&gt;检查站内链接可用性&lt;/li&gt;
&lt;li&gt;执行 Jekyll 构建，确保可发布&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;1. 新建工作流&lt;/h2&gt;
&lt;p&gt;创建 &lt;code&gt;.github/workflows/blog-quality.yml&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: Blog Quality

on:
  push:
    branches: [&quot;master&quot;, &quot;main&quot;]
  pull_request:

jobs:
  check:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Ruby
        uses: ruby/setup-ruby@v1
        with:
          bundler-cache: true

      - name: Install Node
        uses: actions/setup-node@v4
        with:
          node-version: &quot;20&quot;

      - name: Install markdownlint
        run: npm i -g markdownlint-cli

      - name: Lint markdown
        run: markdownlint &quot;**/*.md&quot; --ignore node_modules

      - name: Build Jekyll
        run: bundle exec jekyll build
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 死链检查（可选但推荐）&lt;/h2&gt;
&lt;p&gt;可加 lychee：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;      - name: Link check
        uses: lycheeverse/lychee-action@v2
        with:
          args: --verbose --no-progress &quot;./**/*.md&quot; &quot;./_site/**/*.html&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果外链较多，建议白名单忽略偶发超时域名，避免误报。&lt;/p&gt;
&lt;h2&gt;3. 分支保护&lt;/h2&gt;
&lt;p&gt;在仓库设置中启用 Branch Protection：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;要求 PR 必须通过 &lt;code&gt;Blog Quality&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;禁止直接 push 到主分支&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这一步能显著降低“手滑上线坏内容”的概率。&lt;/p&gt;
&lt;h2&gt;常见报错与排查&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;bundler: command not found: jekyll&lt;/code&gt;：Gem 依赖没装全，先 &lt;code&gt;bundle install&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Front Matter&lt;/code&gt; 语法错误：检查 &lt;code&gt;---&lt;/code&gt; 是否成对、缩进是否一致。&lt;/li&gt;
&lt;li&gt;死链误报：先本地 &lt;code&gt;curl -I&lt;/code&gt; 验证，再加入忽略列表。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;高质量博客的关键不是写得快，而是“每次发布都稳定”。
把 CI 建起来后，你可以把精力放在内容上，而不是线上排错。&lt;/p&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2024/04/22/jekyll-build-errors-troubleshooting/&quot;&gt;Jekyll 构建报错排查手册&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2025/07/22/developer-documentation-writing-guide/&quot;&gt;技术文档写作实战&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/07/16/jekyll-seo-technical-optimization/&quot;&gt;Jekyll SEO 技术优化清单&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Obsidian vs Notion 深度对比：2026 年如何搭建真正可持续的第二大脑</title><link>https://levifree.com/posts/obsidian-vs-notion/</link><guid isPermaLink="true">https://levifree.com/posts/obsidian-vs-notion/</guid><description>不只聊功能清单：从数据所有权、协作成本到长期维护，给出可执行选型框架</description><pubDate>Thu, 10 Oct 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&quot;Your mind is for having ideas, not holding them.&quot; - David Allen&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;很多人把 Obsidian 和 Notion 的选择题，简化成“本地 Markdown vs 在线文档”。
但当你的笔记规模从 100 条变成 5000 条，真正决定体验的，是以下三个问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;你的知识库要不要可迁移？&lt;/li&gt;
&lt;li&gt;你的工作流是“个人深度思考”为主，还是“多人协作交付”为主？&lt;/li&gt;
&lt;li&gt;你是否愿意长期维护一套系统？&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这篇文章不走参数堆砌路线，而是给你一套可以直接落地的决策方法。&lt;/p&gt;
&lt;h2&gt;1. 先定义问题：你要解决什么？&lt;/h2&gt;
&lt;p&gt;在做工具选择前，先把“需求”说清楚。下面是我建议的最小需求清单：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;采集速度：手机和电脑上是否能快速记录&lt;/li&gt;
&lt;li&gt;检索能力：关键词搜索、标签、关系网络是否够用&lt;/li&gt;
&lt;li&gt;结构能力：是以“页面”为核心，还是以“数据库/项目”为核心&lt;/li&gt;
&lt;li&gt;协作能力：是否需要多人实时编辑&lt;/li&gt;
&lt;li&gt;数据安全：是否可离线、是否可完整导出&lt;/li&gt;
&lt;li&gt;长期成本：订阅费用 + 迁移成本 + 学习成本&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果这一步你不做，后面所有“谁更好”的讨论都会失焦。&lt;/p&gt;
&lt;h2&gt;2. 核心差异：本地优先 vs 云端优先&lt;/h2&gt;
&lt;h3&gt;Obsidian：本地优先（Local-first）&lt;/h3&gt;
&lt;p&gt;Obsidian 的底层是文件夹 + Markdown 文本。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优点：
&lt;ul&gt;
&lt;li&gt;数据所有权强，文件可被 Git、iCloud、Syncthing 等任何工具接管&lt;/li&gt;
&lt;li&gt;离线可用，搜索速度快&lt;/li&gt;
&lt;li&gt;插件生态自由度高，适合深度定制&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;成本：
&lt;ul&gt;
&lt;li&gt;初始配置门槛偏高&lt;/li&gt;
&lt;li&gt;协作体验不如在线文档顺滑&lt;/li&gt;
&lt;li&gt;多设备同步需要自行规划&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Notion：云端优先（Cloud-first）&lt;/h3&gt;
&lt;p&gt;Notion 的底层是块（Block）和数据库（Database）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优点：
&lt;ul&gt;
&lt;li&gt;开箱即用，模板和 UI 完整&lt;/li&gt;
&lt;li&gt;团队协作体验成熟，分享/评论/权限管理简单&lt;/li&gt;
&lt;li&gt;数据库视图（看板、表格、日历）非常适合项目管理&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;成本：
&lt;ul&gt;
&lt;li&gt;重度使用时，结构复杂度上升快&lt;/li&gt;
&lt;li&gt;离线和导出能力有边界&lt;/li&gt;
&lt;li&gt;对网络和平台服务连续性依赖更高&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 你真正需要比较的 5 个维度&lt;/h2&gt;
&lt;h2&gt;3.1 数据可迁移性&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Obsidian：天然强。Markdown + 附件目录，几乎没有锁定。&lt;/li&gt;
&lt;li&gt;Notion：可导出，但数据库关系、评论、部分展示能力迁移后会损失。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你未来有“跨平台搬家”的概率，优先考虑文件形态更开放的方案。&lt;/p&gt;
&lt;h2&gt;3.2 知识连接能力&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Obsidian：双向链接、反向链接、图谱视图是核心体验。&lt;/li&gt;
&lt;li&gt;Notion：可通过关系字段建立结构化链接，更像“数据库建模”。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;结论：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;需要“想法与想法”的网状连接，Obsidian 更自然。&lt;/li&gt;
&lt;li&gt;需要“任务与字段”的结构化管理，Notion 更高效。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3.3 协作效率&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Obsidian：个人知识管理优秀，团队实时协作能力相对弱。&lt;/li&gt;
&lt;li&gt;Notion：多人编辑、评论和权限控制成熟，协作交付更顺畅。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你的文档最终要“被团队消费”，Notion 通常更省沟通成本。&lt;/p&gt;
&lt;h2&gt;3.4 维护负担&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Obsidian：自由度越高，维护成本越高（插件冲突、目录重构、同步策略）。&lt;/li&gt;
&lt;li&gt;Notion：维护负担较低，但你要接受平台边界。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;个人玩家：可以追求可玩性。&lt;/li&gt;
&lt;li&gt;团队场景：优先追求稳定、可复制。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3.5 成本模型&lt;/h2&gt;
&lt;p&gt;除了订阅费用，还要算“时间成本”：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;学习时间（上手与规则制定）&lt;/li&gt;
&lt;li&gt;迁移时间（历史内容重构）&lt;/li&gt;
&lt;li&gt;维护时间（每周花多少时间打理系统）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在真实项目里，时间成本往往比订阅费更贵。&lt;/p&gt;
&lt;h2&gt;4. 三类用户的推荐选型&lt;/h2&gt;
&lt;h3&gt;类型 A：研究型/写作者/开发者（强个人知识沉淀）&lt;/h3&gt;
&lt;p&gt;优先：Obsidian&lt;/p&gt;
&lt;p&gt;原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;本地文本可控&lt;/li&gt;
&lt;li&gt;链接思维强&lt;/li&gt;
&lt;li&gt;适合长期积累与复用&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;类型 B：内容团队/运营团队（多人协作和交付）&lt;/h3&gt;
&lt;p&gt;优先：Notion&lt;/p&gt;
&lt;p&gt;原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;协作与权限体系完整&lt;/li&gt;
&lt;li&gt;数据库驱动流程管理&lt;/li&gt;
&lt;li&gt;成员上手快，组织成本低&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;类型 C：既要协作也要沉淀（多数人属于这一类）&lt;/h3&gt;
&lt;p&gt;优先：混合架构（Notion + Obsidian）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Notion 负责“协作中台”&lt;/li&gt;
&lt;li&gt;Obsidian 负责“个人知识深加工”&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. 一套可复制的混合工作流&lt;/h2&gt;
&lt;p&gt;这是我目前在用的“采集-处理-输出”三层模型。&lt;/p&gt;
&lt;h3&gt;第 1 层：采集（Capture）&lt;/h3&gt;
&lt;p&gt;工具：Notion&lt;/p&gt;
&lt;p&gt;做法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用移动端快速收集网页、想法、会议纪要&lt;/li&gt;
&lt;li&gt;所有输入先进 Inbox 数据库&lt;/li&gt;
&lt;li&gt;每条内容都加 2 个字段：来源、主题&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;第 2 层：处理（Process）&lt;/h3&gt;
&lt;p&gt;工具：Obsidian&lt;/p&gt;
&lt;p&gt;做法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每周从 Notion Inbox 挑选高价值条目&lt;/li&gt;
&lt;li&gt;转成原子笔记（一个笔记只讲一个观点）&lt;/li&gt;
&lt;li&gt;使用双向链接串联主题和上下文&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;第 3 层：输出（Publish）&lt;/h3&gt;
&lt;p&gt;工具：博客/GitHub/团队文档&lt;/p&gt;
&lt;p&gt;做法：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Obsidian 内写成结构化草稿&lt;/li&gt;
&lt;li&gt;输出到博客或团队文档前做一次事实核查&lt;/li&gt;
&lt;li&gt;发布后把链接回写到 Notion 项目库，形成闭环&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6. 防止“工具内耗”的 7 条规则&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;每周固定一次整理，不做全天候整理。&lt;/li&gt;
&lt;li&gt;新增标签前先搜索旧标签，避免同义词泛滥。&lt;/li&gt;
&lt;li&gt;不追求完美模板，模板只服务高频场景。&lt;/li&gt;
&lt;li&gt;每篇笔记只解决一个问题，避免“大杂烩”。&lt;/li&gt;
&lt;li&gt;输入和输出分开：采集时不润色，写作时再加工。&lt;/li&gt;
&lt;li&gt;每月清理失效链接和空页面。&lt;/li&gt;
&lt;li&gt;工具改造频率控制在“每月一次”以内。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;7. 一个简单决策表&lt;/h2&gt;
&lt;p&gt;如果你还没法决定，就用下面这组判断：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你 80% 的时间在协作？选 Notion。&lt;/li&gt;
&lt;li&gt;你 80% 的时间在沉淀和写作？选 Obsidian。&lt;/li&gt;
&lt;li&gt;两者都很重要？做混合架构，不要强行二选一。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;Obsidian 和 Notion 都是优秀工具，但它们解决的是不同问题。
真正高质量的第二大脑，不是“选了哪款工具”，而是你是否建立了稳定可持续的知识流程。&lt;/p&gt;
&lt;p&gt;先搭流程，再选工具。你会少走很多弯路。&lt;/p&gt;
</content:encoded></item><item><title>Git Rebase vs Merge：团队 PR 工作流如何选才不翻车</title><link>https://levifree.com/posts/git-rebase-vs-merge-pr-workflow/</link><guid isPermaLink="true">https://levifree.com/posts/git-rebase-vs-merge-pr-workflow/</guid><description>从提交历史可读性、冲突处理到回滚策略，给出可落地的协作规范</description><pubDate>Thu, 12 Sep 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;“到底用 rebase 还是 merge”是每个团队都会争论的话题。
争论本身没意义，关键是你们的目标是什么：历史整洁、排查效率、还是风险最小化？&lt;/p&gt;
&lt;h2&gt;1. 两者本质区别&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;merge&lt;/code&gt;：保留分叉历史，新增 merge commit。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rebase&lt;/code&gt;：重写提交基线，让历史线性化。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;简单说：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;你想看见真实开发分支轨迹，用 merge。&lt;/li&gt;
&lt;li&gt;你想得到干净主线历史，用 rebase。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. 推荐规则（可直接落地）&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;个人功能分支：允许 rebase，先整理提交再提 PR。&lt;/li&gt;
&lt;li&gt;主分支（main/master）：禁止 rebase 历史，只接受受控合并。&lt;/li&gt;
&lt;li&gt;公共分支已被他人基于开发后，不要再强推 rebase。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;3. 常见流程模板&lt;/h2&gt;
&lt;h3&gt;场景 A：保持主分支线性&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;git checkout feature/login
git fetch origin
git rebase origin/main
# 解决冲突后
# git rebase --continue

git push --force-with-lease
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在 GitHub 用 &lt;code&gt;Squash and merge&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;场景 B：保留分支语义&lt;/h3&gt;
&lt;p&gt;如果团队重视“功能分支完整上下文”，可使用 merge commit：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git checkout main
git pull origin main
git merge --no-ff feature/login
git push origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 冲突处理与风险控制&lt;/h2&gt;
&lt;p&gt;最容易翻车的是“边 rebase 边改业务逻辑”。
建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rebase 冲突阶段只做必要冲突消解&lt;/li&gt;
&lt;li&gt;逻辑改动另起提交&lt;/li&gt;
&lt;li&gt;大分支每天小步同步上游，减少一次性冲突量&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. 回滚策略&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;merge 模式下可直接回滚 merge commit&lt;/li&gt;
&lt;li&gt;squash 模式下回滚更简单，但会丢失中间提交语义&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以你要在“历史简洁”和“历史细节”之间做取舍。&lt;/p&gt;
&lt;h2&gt;6. 一个实用团队约定&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;PR 合并前：必须通过 CI&lt;/li&gt;
&lt;li&gt;PR 合并前：提交历史自检（去掉 &lt;code&gt;fix typo&lt;/code&gt; 等噪音提交）&lt;/li&gt;
&lt;li&gt;发布前：为高风险改动打 tag&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;rebase 和 merge 没有绝对优劣，只有是否契合团队协作模型。
把规则写进团队文档并执行一致，比任何“偏好争论”都更重要。&lt;/p&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2024/08/20/git-commit-convention/&quot;&gt;Git Commit 规范指南&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/12/03/git-conflict-resolution-playbook/&quot;&gt;Git 冲突处理实战手册&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2023/12/06/git-sync-fork-upstream-guide/&quot;&gt;Git Fork 同步上游实战&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>告别 git push -f：团队协作中的 Git Commit 规范指南</title><link>https://levifree.com/posts/git-commit-convention/</link><guid isPermaLink="true">https://levifree.com/posts/git-commit-convention/</guid><description>如何写出优雅的 Commit Message？Conventional Commits 最佳实践</description><pubDate>Tue, 20 Aug 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;在团队代码 Review 时，最让人头大的不是代码逻辑，而是看到满屏的 Commit 记录写着：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;update&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fix bug&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;temp 123&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种“天书”让回溯历史变成了侦探游戏。今天我们来聊聊如何引入 &lt;strong&gt;Conventional Commits (约定式提交)&lt;/strong&gt;，让你的 Git 历史像说明书一样清晰。&lt;/p&gt;
&lt;h2&gt;1. 为什么需要规范？&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;自动化生成 Changelog&lt;/strong&gt;：如果格式规范，脚本可以自动抓取 &lt;code&gt;feat&lt;/code&gt; 开头的提交生成版本更新日志。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;快速定位问题&lt;/strong&gt;：看到 &lt;code&gt;fix(user-auth)&lt;/code&gt; 就能瞬间知道修的是用户登录模块的 Bug。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;触发 CI/CD&lt;/strong&gt;：某些类型的提交（如 &lt;code&gt;docs&lt;/code&gt;）可以配置为跳过耗时的构建流程。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;2. 标准格式&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;type&amp;gt;(&amp;lt;scope&amp;gt;): &amp;lt;subject&amp;gt;

&amp;lt;body&amp;gt;

&amp;lt;footer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;结构图解&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;  Header:  feat(auth): add google login support
           │    │      │
           │    │      └─ Subject (简短描述)
           │    └─ Scope (影响范围，可选)
           └─ Type (类型：功能/修复/文档等)

    Body:  (空一行)
           Specifically, used the OAuth2.0 SDK.
           Added a new callback route.

  Footer:  (空一行)
           Closes #123 (关联 Issue)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2.1 常用的 Type&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;feat&lt;/code&gt;: 新功能 (Feature)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fix&lt;/code&gt;: 修补 Bug&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docs&lt;/code&gt;: 文档修改&lt;/li&gt;
&lt;li&gt;&lt;code&gt;style&lt;/code&gt;: 格式调整（不影响代码运行，如空格、分号）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;refactor&lt;/code&gt;: 重构（即不是新功能，也不是修 Bug）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;perf&lt;/code&gt;: 性能优化&lt;/li&gt;
&lt;li&gt;&lt;code&gt;test&lt;/code&gt;: 增加测试&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chore&lt;/code&gt;: 构建过程或辅助工具变动&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2.2 范例&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;feat(login): add google oauth login support&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fix(nav): fix navbar overlap on mobile screen&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;3. 工具辅助：Commitizen&lt;/h2&gt;
&lt;p&gt;不想每次手动记这些规则？用工具强制约束。&lt;/p&gt;
&lt;p&gt;安装 commitizen：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;npm install -g commitizen
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;初始化适配器：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;commitizen init cz-conventional-changelog --save-dev --save-exact
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以后提交代码时，不要用 &lt;code&gt;git commit&lt;/code&gt;，而是用：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git cz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它会弹出一个交互式界面，引导你选择类型、填写描述。&lt;/p&gt;
&lt;h2&gt;4. 强制校验：Husky + Commitlint&lt;/h2&gt;
&lt;p&gt;为了防止团队成员“偷懒”绕过规范，可以在 Commit 钩子 (Hook) 中做拦截。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;安装 Husky 和 Commitlint。&lt;/li&gt;
&lt;li&gt;配置 &lt;code&gt;commit-msg&lt;/code&gt; 钩子。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;当有人试图提交 &lt;code&gt;git commit -m &quot;update&quot;&lt;/code&gt; 这种不规范信息时，Git 会直接报错拒绝提交。&lt;/p&gt;
&lt;h2&gt;常见报错与解决&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Husky 不生效？&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;确保你执行过 &lt;code&gt;npm install&lt;/code&gt; 并且脚本中触发了 &lt;code&gt;husky install&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;在 CI 环境中，可能需要显式执行 &lt;code&gt;npm run prepare&lt;/code&gt;。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Windows 下的乱码或换行符问题&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在 Windows 上使用 &lt;code&gt;git cz&lt;/code&gt; 可能会遇到 emoji 显示乱码。建议使用 Windows Terminal 或 Git Bash，并设置 UTF-8 编码。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;想从规范中“逃逸”&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;如果你真的急着修一个线上 Bug，不想填那些复杂的表单，可以使用 &lt;code&gt;--no-verify&lt;/code&gt; 参数跳过检查：&lt;pre&gt;&lt;code&gt;git commit -m &quot;hotfix&quot; --no-verify
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;em&gt;注：慎用！&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;5. 总结&lt;/h2&gt;
&lt;p&gt;Git 记录是一个项目的历史档案。编写规范的 Commit Message，是对队友的尊重，也是对自己职业素养的体现。&lt;/p&gt;
</content:encoded></item><item><title>Jekyll SEO 技术优化清单：从可抓取到可排名的实战配置</title><link>https://levifree.com/posts/jekyll-seo-technical-optimization/</link><guid isPermaLink="true">https://levifree.com/posts/jekyll-seo-technical-optimization/</guid><description>Sitemap、结构化数据、Canonical 与内容聚合页，给个人博客一套可执行的 SEO 基线</description><pubDate>Tue, 16 Jul 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;很多博客“写了很多”，但搜索曝光一直上不去，通常不是内容完全不行，而是技术层面没有打底。
本文给你一套适用于 Jekyll 站点的 SEO 技术清单，目标是先做到“可抓取、可理解、可归档”。&lt;/p&gt;
&lt;h2&gt;1. 抓取与索引基础&lt;/h2&gt;
&lt;p&gt;先确认三件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;robots.txt&lt;/code&gt; 可访问且规则正确&lt;/li&gt;
&lt;li&gt;&lt;code&gt;sitemap.xml&lt;/code&gt; 可访问并包含文章 URL&lt;/li&gt;
&lt;li&gt;文章页面不含误设的 &lt;code&gt;noindex&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最小检查命令：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;curl -I https://your-domain.com/robots.txt
curl -I https://your-domain.com/sitemap.xml
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. 统一 Canonical 规则&lt;/h2&gt;
&lt;p&gt;Jekyll 很容易因为分页、分类页或参数链接出现重复 URL。
建议每篇文章都输出 canonical，指向唯一正式地址。&lt;/p&gt;
&lt;p&gt;示例（放在 &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;link rel=&quot;canonical&quot; href=&quot;{{ page.url | replace:&apos;index.html&apos;,&apos;&apos; }}&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. 结构化数据（JSON-LD）&lt;/h2&gt;
&lt;p&gt;给文章页补 &lt;code&gt;BlogPosting&lt;/code&gt; 结构化数据，帮助搜索引擎理解正文类型与作者信息。&lt;/p&gt;
&lt;p&gt;关键字段至少包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;headline&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;datePublished&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;dateModified&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;author&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;mainEntityOfPage&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你有封面图，补上 &lt;code&gt;image&lt;/code&gt; 字段效果更好。&lt;/p&gt;
&lt;h2&gt;4. 信息架构：聚合页要有价值&lt;/h2&gt;
&lt;p&gt;很多站点的标签页/归档页只有一堆链接，内容太薄。
建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;每个聚合页增加主题说明&lt;/li&gt;
&lt;li&gt;给重点分类写导读段&lt;/li&gt;
&lt;li&gt;在聚合页内部做“新手入口”与“进阶入口”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样不仅提升用户体验，也能减少“薄内容页面”信号。&lt;/p&gt;
&lt;h2&gt;5. 内链策略&lt;/h2&gt;
&lt;p&gt;每篇技术文至少做两类内链：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;横向内链：同主题相关文章&lt;/li&gt;
&lt;li&gt;纵向内链：前置知识和进阶教程&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;原则：内链要服务阅读路径，而不是为了凑数量。&lt;/p&gt;
&lt;h2&gt;6. 元数据与可读标题&lt;/h2&gt;
&lt;p&gt;发布前检查：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;title&lt;/code&gt; 不做标题党，包含明确主题词&lt;/li&gt;
&lt;li&gt;&lt;code&gt;subtitle&lt;/code&gt; 补充场景和收益&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tags&lt;/code&gt; 与正文一致，避免泛标签&lt;/li&gt;
&lt;li&gt;首段 80-120 字讲清“这篇文帮你解决什么”&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;7. 性能与 SEO 联动&lt;/h2&gt;
&lt;p&gt;技术 SEO 不只是标签，页面体验也影响结果。&lt;/p&gt;
&lt;p&gt;建议优先优化：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;首屏图片体积&lt;/li&gt;
&lt;li&gt;阻塞渲染的 JS/CSS&lt;/li&gt;
&lt;li&gt;移动端可读性&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;8. 发布后的验证流程&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;本地 &lt;code&gt;bundle exec jekyll build&lt;/code&gt; 确认构建成功&lt;/li&gt;
&lt;li&gt;上线后手动检查关键页面状态码&lt;/li&gt;
&lt;li&gt;在搜索控制台提交 sitemap&lt;/li&gt;
&lt;li&gt;观察 2-4 周曝光和索引变化&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;对个人博客来说，SEO 的第一步不是“研究黑科技”，而是先把技术基线做完整。
当抓取、结构和体验都达标时，优质内容的价值才会被更稳定地放大。&lt;/p&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2025/10/14/static-blog-content-cluster-strategy/&quot;&gt;静态博客内容集群策略&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/04/22/jekyll-build-errors-troubleshooting/&quot;&gt;Jekyll 构建报错排查手册&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2025/03/09/adsense-low-value-content-recovery/&quot;&gt;AdSense 低价值内容恢复实战&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Linux 服务器安全基线实战：新机上线前必须完成的 12 项加固</title><link>https://levifree.com/posts/linux-server-security-baseline/</link><guid isPermaLink="true">https://levifree.com/posts/linux-server-security-baseline/</guid><description>从账户、SSH、防火墙到日志审计，给个人站长一份可直接执行的上线清单</description><pubDate>Tue, 18 Jun 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;刚买 VPS 时，很多人第一步是“装应用”，但正确顺序应该是“先加固，再上线”。
本文给你一份我自己长期使用的安全基线，适合个人博客、API 小服务、HomeLab 公网入口。&lt;/p&gt;
&lt;h2&gt;1. 账号与权限&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;新建普通运维用户，禁用 &lt;code&gt;root&lt;/code&gt; 直连。&lt;/li&gt;
&lt;li&gt;给运维用户最小化 sudo 权限。&lt;/li&gt;
&lt;li&gt;删除不必要系统账户，关闭无用登录 shell。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;adduser levi
usermod -aG sudo levi
passwd -l root
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. SSH 最小暴露面&lt;/h2&gt;
&lt;p&gt;编辑 &lt;code&gt;/etc/ssh/sshd_config&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
MaxAuthTries 3
LoginGraceTime 20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重启前务必开一个新终端测试，确认密钥可登录，再执行：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo systemctl restart sshd
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. 防火墙白名单&lt;/h2&gt;
&lt;p&gt;默认拒绝入站，只放行必要端口（如 22/80/443）。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;4. 自动安全更新&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Debian/Ubuntu：启用 &lt;code&gt;unattended-upgrades&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;RHEL 系：启用 dnf 自动更新策略&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;核心目的是降低“已知漏洞窗口期”。&lt;/p&gt;
&lt;h2&gt;5. 登录失败防护&lt;/h2&gt;
&lt;p&gt;安装 Fail2ban，拦截暴力破解来源 IP。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install fail2ban -y
sudo systemctl enable --now fail2ban
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;建议至少启用 &lt;code&gt;sshd&lt;/code&gt; jail，并设置合理封禁时长。&lt;/p&gt;
&lt;h2&gt;6. 服务最小化&lt;/h2&gt;
&lt;p&gt;执行一次服务盘点：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo ss -tulpen
sudo systemctl list-unit-files --type=service
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;任何你“不知道为何存在”的公网监听端口，都应先停用再确认。&lt;/p&gt;
&lt;h2&gt;7. 日志与审计&lt;/h2&gt;
&lt;p&gt;至少保证：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSH 登录日志可追踪&lt;/li&gt;
&lt;li&gt;sudo 操作可追踪&lt;/li&gt;
&lt;li&gt;系统日志按周期轮转&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;建议保留最近 30-90 天安全日志，便于追溯异常。&lt;/p&gt;
&lt;h2&gt;8. 备份与恢复演练&lt;/h2&gt;
&lt;p&gt;安全不只是“防入侵”，也包括“可恢复”。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;配置每日快照或异地备份&lt;/li&gt;
&lt;li&gt;每月至少做一次恢复演练&lt;/li&gt;
&lt;li&gt;备份内容包含站点、数据库、证书、关键配置&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;常见错误&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;先关密码登录，后配密钥，导致自己被锁在门外。&lt;/li&gt;
&lt;li&gt;防火墙规则一次性改太多，误封 22 端口。&lt;/li&gt;
&lt;li&gt;只做备份，不做恢复验证。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;把基线做完再部署业务，你的服务器抗风险能力会提升一个量级。
对于个人站点，这 12 项已经能覆盖绝大多数常见攻击面。&lt;/p&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2025/11/05/ssh-key-security/&quot;&gt;SSH 密钥安全指南&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2025/06/28/nginx-502-504-troubleshooting/&quot;&gt;Nginx 502/504 排错指南&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2025/05/21/self-hosted-monitoring-uptime-kuma-guide/&quot;&gt;Uptime Kuma 监控实战&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>VS Code + GitHub Copilot: AI 时代开发者的效率核武器</title><link>https://levifree.com/posts/vscode-copilot-ai-coding-guide/</link><guid isPermaLink="true">https://levifree.com/posts/vscode-copilot-ai-coding-guide/</guid><description>AI 结对编程时代，如何利用 Copilot 节省 50% 的编码时间</description><pubDate>Wed, 15 May 2024 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&quot;Automation is cost cutting by tightening the corners and not cutting them.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;在 2026 年的今天，AI 辅助编程已经不再是新鲜事，而是每一位开发者的标配。作为市面上最强大的 AI 编程助手组合，Visual Studio Code (VS Code) 配合 GitHub Copilot，已经彻底改变了我们写代码的方式。&lt;/p&gt;
&lt;p&gt;本文将分享如何高效利用这一组合，不仅是作为“自动补全”，更是作为你的“全天候结对编程伙伴”。&lt;/p&gt;
&lt;h2&gt;1. 超越自动补全：Copilot 的核心能力&lt;/h2&gt;
&lt;p&gt;很多新手只把 Copilot 当作更智能的 IntelliSense，这其实大材小用了。&lt;/p&gt;
&lt;h3&gt;1.1 上下文感知&lt;/h3&gt;
&lt;p&gt;Copilot 能够读取你当前打开的所有标签页 (Tabs)。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;技巧&lt;/strong&gt;：在写新功能时，把你引用的接口定义 (&lt;code&gt;types.ts&lt;/code&gt;)、相关的后端逻辑文件 (&lt;code&gt;controller.py&lt;/code&gt;) 都打开。Copilot 会基于这些上下文，精准生成符合你业务逻辑的代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1.2 编写测试用例&lt;/h3&gt;
&lt;p&gt;这是 Copilot 最擅长的领域之一。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;操作&lt;/strong&gt;：写完一个函数后，在下方输入 &lt;code&gt;// test case for above function&lt;/code&gt;，Copilot 往往能生成覆盖率极高的单元测试，甚至考虑到边界情况。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. VS Code Chat：不仅仅是问答&lt;/h2&gt;
&lt;p&gt;新版的 VS Code 深度集成了 Copilot Chat。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;右键解释&lt;/strong&gt;：遇到看不懂的遗留代码（Legacy Code），选中并右键 &lt;code&gt;Copilot &amp;gt; Explain This&lt;/code&gt;。它会用通俗的语言解释代码逻辑。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;一键重构&lt;/strong&gt;：选中一段乱糟糟的代码，在 Chat 中输入 &lt;code&gt;/fix&lt;/code&gt; 或 &lt;code&gt;/optimize&lt;/code&gt;，它会帮你重构变量名、提取函数，甚至优化算法复杂度。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 实用快捷键与配置&lt;/h2&gt;
&lt;p&gt;为了让体验更丝滑，建议掌握以下快捷键：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Tab&lt;/code&gt;&lt;/strong&gt;: 接受建议。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Ctrl + Enter&lt;/code&gt; (Mac: &lt;code&gt;Cmd + Enter&lt;/code&gt;)&lt;/strong&gt;: 打开 GitHub Copilot 建议面板，一次性查看 10 种可能的实现方案。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;行内 Chat&lt;/strong&gt;: &lt;code&gt;Ctrl + I&lt;/code&gt; (Mac: &lt;code&gt;Cmd + I&lt;/code&gt;)，直接在代码行内唤起对话框进行修改，不用切换侧边栏。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 避免“AI 幻觉”陷阱&lt;/h2&gt;
&lt;p&gt;虽然 Copilot 很强，但它也会一本正经地胡说八道。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;永远不要盲目 Accept&lt;/strong&gt;：对于生成的正则、复杂的 SQL 或算法，一定要人工 Review 一遍。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;安全隐患&lt;/strong&gt;：注意不要让 AI 生成包含硬编码密码或密钥的代码。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. 结语&lt;/h2&gt;
&lt;p&gt;工具无法替代思考，但可以释放思考。将重复的样板代码交给 Copilot，把你的精力集中在系统架构和业务价值上，这才是 AI 时代开发者的生存之道。&lt;/p&gt;
</content:encoded></item><item><title>Jekyll 构建报错排查手册：10 个最常见错误与修复步骤</title><link>https://levifree.com/posts/jekyll-build-errors-troubleshooting/</link><guid isPermaLink="true">https://levifree.com/posts/jekyll-build-errors-troubleshooting/</guid><description>从 Front Matter、依赖冲突到端口占用，给博客维护者一份可复制的故障处理流程</description><pubDate>Mon, 22 Apr 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Jekyll 的问题往往不是“大故障”，而是被一堆小问题反复打断：今天 front matter 报错，明天 gem 版本冲突。
这篇文章把我最常遇到的 10 类构建错误整理成标准排查流程。&lt;/p&gt;
&lt;h2&gt;1. &lt;code&gt;bundler: command not found: jekyll&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;原因：本地依赖未安装完整。&lt;/p&gt;
&lt;p&gt;修复：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bundle install
bundle exec jekyll serve
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果你是新机器，先确认 Ruby 与 Bundler 已安装。&lt;/p&gt;
&lt;h2&gt;2. Front Matter 解析失败&lt;/h2&gt;
&lt;p&gt;常见错误信息：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;YAML Exception reading ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;高频原因：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;---&lt;/code&gt; 没有成对出现&lt;/li&gt;
&lt;li&gt;缩进混用空格和 tab&lt;/li&gt;
&lt;li&gt;&lt;code&gt;title&lt;/code&gt; 等字段含未转义特殊字符&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;修复建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;先从报错文件顶部 30 行开始查&lt;/li&gt;
&lt;li&gt;用 2 空格统一缩进&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 日期格式导致文章不显示&lt;/h2&gt;
&lt;p&gt;原因：&lt;code&gt;date&lt;/code&gt; 字段格式非法或未来时间策略不一致。&lt;/p&gt;
&lt;p&gt;检查：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;是否使用 &lt;code&gt;YYYY-MM-DD HH:MM:SS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;_config.yml&lt;/code&gt; 中 &lt;code&gt;future&lt;/code&gt; 设置是否符合预期&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 端口占用，服务启动失败&lt;/h2&gt;
&lt;p&gt;报错示例：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Address already in use - bind(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修复：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;lsof -i :4000
kill -9 &amp;lt;PID&amp;gt;
bundle exec jekyll serve --port 4001
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. 中文路径或编码问题&lt;/h2&gt;
&lt;p&gt;有些系统环境下，非 UTF-8 文件会导致解析异常。&lt;/p&gt;
&lt;p&gt;建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Markdown 文件统一 UTF-8&lt;/li&gt;
&lt;li&gt;文件名尽量使用英文、数字、连字符&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;6. 插件环境差异（本地能跑，线上失败）&lt;/h2&gt;
&lt;p&gt;原因：本地插件和 GitHub Pages 支持插件集合不一致。&lt;/p&gt;
&lt;p&gt;处理：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;优先使用 GitHub Pages 白名单插件&lt;/li&gt;
&lt;li&gt;或改成 Actions 自构建后发布 &lt;code&gt;_site&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;7. Liquid 语法错误&lt;/h2&gt;
&lt;p&gt;常见在 include 和循环中。&lt;/p&gt;
&lt;p&gt;示例问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;标签未闭合&lt;/li&gt;
&lt;li&gt;变量名拼错&lt;/li&gt;
&lt;li&gt;条件判断写法不合法&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;排查技巧：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;从最近改动的布局文件开始定位&lt;/li&gt;
&lt;li&gt;临时注释可疑块做二分排查&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;8. 资源路径 404&lt;/h2&gt;
&lt;p&gt;多见于 &lt;code&gt;baseurl&lt;/code&gt; 配置不一致。&lt;/p&gt;
&lt;p&gt;建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;本地和线上都统一通过 `` 拼路径&lt;/li&gt;
&lt;li&gt;图片与脚本避免硬编码绝对路径&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;9. 增量构建缓存导致“改了不生效”&lt;/h2&gt;
&lt;p&gt;修复：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;bundle exec jekyll clean
bundle exec jekyll serve
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;10. 依赖版本冲突&lt;/h2&gt;
&lt;p&gt;症状：更新 gem 后突然无法构建。&lt;/p&gt;
&lt;p&gt;处理流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先看 &lt;code&gt;Gemfile.lock&lt;/code&gt; 变更&lt;/li&gt;
&lt;li&gt;回退到上一个可用锁版本&lt;/li&gt;
&lt;li&gt;分批升级 gem，不要一次性全升&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;一套通用排查流程&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;先看第一条报错，不要被后续连锁错误干扰&lt;/li&gt;
&lt;li&gt;锁定“最近修改文件”优先检查&lt;/li&gt;
&lt;li&gt;先恢复可构建，再做优化&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;Jekyll 排错的关键是流程化，而不是临时猜测。
把这 10 条高频问题掌握后，90% 的构建故障都能快速定位。&lt;/p&gt;
&lt;h2&gt;延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/2024/07/16/jekyll-seo-technical-optimization/&quot;&gt;Jekyll SEO 技术优化清单&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2024/11/18/github-actions-blog-ci-cd/&quot;&gt;GitHub Actions 博客质量流水线&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/2025/03/09/jekyll-content-quality-checklist/&quot;&gt;Jekyll 发布前内容质量自检模板&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded></item><item><title>Next.js App Router 上手指南：从路由到服务端渲染</title><link>https://levifree.com/posts/nextjs-app-router/</link><guid isPermaLink="true">https://levifree.com/posts/nextjs-app-router/</guid><description>忘掉 getStaticProps？Next.js 14+ 核心概念极简解析</description><pubDate>Mon, 25 Mar 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Next.js 最近几年的变化可谓天翻地覆。自从引入 &lt;strong&gt;App Router&lt;/strong&gt; (&lt;code&gt;app&lt;/code&gt; 目录) 以来，它几乎重塑了 React 应用的构建方式。
如果你还在用这 &lt;code&gt;pages&lt;/code&gt; 目录写 &lt;code&gt;getServerSideProps&lt;/code&gt;，那你是时候升级一下知识库了。&lt;/p&gt;
&lt;h2&gt;1. 目录即路由&lt;/h2&gt;
&lt;p&gt;在 App Router 中，文件系统就是路由。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;app/page.tsx&lt;/code&gt; -&amp;gt; &lt;code&gt;/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;app/about/page.tsx&lt;/code&gt; -&amp;gt; &lt;code&gt;/about&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;app/blog/[id]/page.tsx&lt;/code&gt; -&amp;gt; &lt;code&gt;/blog/123&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种结构非常直观。每个文件夹还可以包含 &lt;code&gt;layout.tsx&lt;/code&gt; (布局), &lt;code&gt;loading.tsx&lt;/code&gt; (加载中), &lt;code&gt;error.tsx&lt;/code&gt; (错误处理)，实现了&lt;strong&gt;嵌套路由&lt;/strong&gt;和&lt;strong&gt;UI 状态&lt;/strong&gt;的完美解耦。&lt;/p&gt;
&lt;h3&gt;文件结构可视化&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;app/
├── layout.tsx      (Root Layout: &amp;lt;html&amp;gt;...&amp;lt;/html&amp;gt;)
├── page.tsx        (Home Page: /)
├── about/
│   └── page.tsx    (About Page: /about)
└── blog/
    ├── layout.tsx  (Blog Layout: sidebar, etc.)
    └── [id]/
        └── page.tsx (Post Page: /blog/123)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;2. Server Components (服务端组件)&lt;/h2&gt;
&lt;p&gt;这是最大的思想转变。&lt;strong&gt;在 App 目录下的组件，默认都是 Server Components。&lt;/strong&gt;
这意味着：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;它们直接在服务器上运行。&lt;/li&gt;
&lt;li&gt;可以直接连接数据库，读取文件系统。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;不会&lt;/strong&gt;被打包到客户端的 JavaScript bundle 中（极大减小了体积）。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;// app/page.tsx
// 这是一个服务端组件，可以直接写 async/await
import db from &apos;./db&apos;;

export default async function Page() {
  const users = await db.query(&apos;SELECT * FROM users&apos;); // 直接查库！
  
  return (
    &amp;lt;ul&amp;gt;
      {users.map(user =&amp;gt; &amp;lt;li key={user.id}&amp;gt;{user.name}&amp;lt;/li&amp;gt;)}
    &amp;lt;/ul&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;3. Client Components (客户端组件)&lt;/h2&gt;
&lt;p&gt;如果你需要 &lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt; 或者 DOM 事件（onClick），你需要显式地在文件顶部声明 &lt;code&gt;&quot;use client&quot;&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&apos;use client&apos;;

import { useState } from &apos;react&apos;;

export default function Counter() {
  const [count, setCount] = useState(0);
  return &amp;lt;button onClick={() =&amp;gt; setCount(c =&amp;gt; c+1)}&amp;gt;{count}&amp;lt;/button&amp;gt;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;最佳实践&lt;/strong&gt;：尽可能让叶子节点（Button, Input）做成 Client Component，而把大框架保留为 Server Component。&lt;/p&gt;
&lt;h2&gt;4. 获取数据 (Data Fetching)&lt;/h2&gt;
&lt;p&gt;再也没有 &lt;code&gt;getStaticProps&lt;/code&gt; 了。现在获取数据就是标准的 &lt;code&gt;fetch&lt;/code&gt; API。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 类似 getStaticProps (默认缓存)
fetch(&apos;https://...&apos;, { cache: &apos;force-cache&apos; });

// 类似 getServerSideProps (每次刷新)
fetch(&apos;https://...&apos;, { cache: &apos;no-store&apos; });

// ISR (每10秒更新一次)
fetch(&apos;https://...&apos;, { next: { revalidate: 10 } });
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;5. 总结&lt;/h2&gt;
&lt;p&gt;App Router 虽然学习曲线稍陡，但它带来了更小的包体积、更快的首屏加载速度和更清晰的数据流。对于 2026 年的新项目，这是唯一的选择。&lt;/p&gt;
</content:encoded></item><item><title>iPad 生产力指南：掌握这 5 个多任务处理技巧 (iPadOS 17+)</title><link>https://levifree.com/posts/ipad-productivity-tips/</link><guid isPermaLink="true">https://levifree.com/posts/ipad-productivity-tips/</guid><description>从分屏浏览到台前调度，彻底释放 iPad 的办公潜能</description><pubDate>Tue, 02 Jan 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;iPad 早已不仅仅是娱乐设备。随着 iPadOS 的不断迭代，尤其是 &lt;strong&gt;台前调度 (Stage Manager)&lt;/strong&gt; 的加入，它在处理多任务方面的能力已经大幅提升。&lt;/p&gt;
&lt;p&gt;无论你是学生还是职场人士，掌握这些系统级交互技巧，都能让你的工作效率翻倍。&lt;/p&gt;
&lt;h2&gt;1. 灵活运用“分屏浏览” (Split View)&lt;/h2&gt;
&lt;p&gt;这是最基础也最实用的功能。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;如何触发&lt;/strong&gt;：在打开一个 App 的状态下，点击屏幕顶部的“三个点”图标，选择“分屏浏览”。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;应用场景&lt;/strong&gt;：左边开浏览器查资料，右边开 Notion 做笔记；或者左边看网课视频，右边写论文。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;小技巧&lt;/strong&gt;：你可以拖动中间的分隔条来调整两个 App 的宽度比例（50:50 或 70:30）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;2. “侧拉” (Slide Over) 的妙用&lt;/h2&gt;
&lt;p&gt;侧拉窗口是浮在主屏幕之上的一个小窗口，非常适合用来放一些“用完即走”的工具类 App。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;推荐放置&lt;/strong&gt;：计算器、微信、音乐播放器、词典。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;操作&lt;/strong&gt;：在任何界面，从屏幕右侧边缘向左滑，即可呼出侧拉窗口。再次向右滑可隐藏。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;隐藏技巧&lt;/strong&gt;：侧拉窗口也可以多开！在侧拉窗口底部横条左右滑动，可以快速切换不同的侧拉 App。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. 掌握“台前调度” (Stage Manager)&lt;/h2&gt;
&lt;p&gt;如果你使用的是搭载 M 系列芯片（或 A12X/Z）的 iPad Pro/Air，这是提升效率的终极武器。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;核心逻辑&lt;/strong&gt;：它不再让 App 强制全屏，而是像电脑窗口一样重叠、调整大小。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;分组管理&lt;/strong&gt;：你可以把“Word + 浏览器”编为一组，“Lightroom + 文件 App”编为另一组。点击左侧的任务栏即可一键切换整个工作流。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;外接显示器&lt;/strong&gt;：在 M 系列芯片的 iPad 上，台前调度支持真正的外接显示器扩展（而非简单的屏幕镜像），让你获得双屏办公体验。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;4. 快速备忘录 (Quick Note)&lt;/h2&gt;
&lt;p&gt;灵感稍纵即逝，你需要最快的记录方式。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;手势&lt;/strong&gt;：使用 Apple Pencil 从屏幕右下角向中间滑动，或者用手指从右下角上滑（需在设置中开启）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;联动&lt;/strong&gt;：当你在 Safari 浏览器中呼出快速备忘录时，点击“添加链接”，它会自动把你当前正在看的网页链接插进去，方便以后回溯。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;5. 通用控制 (Universal Control)&lt;/h2&gt;
&lt;p&gt;如果你同时拥有 Mac 和 iPad，这个功能是“生态壁垒”的最佳体现。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;无缝连接&lt;/strong&gt;：只需把 iPad 放在 Mac 旁边，你就可以直接把 Mac 的鼠标光标移到 iPad 屏幕上！&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;文件传输&lt;/strong&gt;：不需要 AirDrop，直接用鼠标把 Mac 里的图片拖拽到 iPad 的相册里，或者把 iPad 里的文档拖到 Mac 桌面。这才是真正的“生产力”。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;工具不仅要好用，更要“会用”。
不要让你的 iPad 仅仅成为“买前生产力，买后爱奇艺”的设备。尝试在日常工作中强迫自己使用上述 1-2 个技巧，一周后你会发现离不开它们。&lt;/p&gt;
</content:encoded></item><item><title>AMH 面板建站实战：从零部署高性能 WordPress 博客</title><link>https://levifree.com/posts/amh-wordpress-installation-guide/</link><guid isPermaLink="true">https://levifree.com/posts/amh-wordpress-installation-guide/</guid><description>基于 Azure B1s / VPS 的 Linux 环境配置与 AMH 面板使用全指南</description><pubDate>Mon, 25 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&quot;A man is not old until his regrets take place of his dreams.&quot;
&quot;只有当遗憾取代了梦想，人才算老。&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div class=&quot;alert alert-info&quot; role=&quot;alert&quot;&amp;gt;
&amp;lt;strong&amp;gt;教程概览：&amp;lt;/strong&amp;gt; 本文将手把手教你如何在一台全新的 Linux 服务器（如 Azure B1s）上，配置系统环境、安装 AMH 开源主机面板，并最终部署一个运行在 PHP 8.0 环境下的 WordPress 博客。
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;第一阶段：服务器基础环境配置&lt;/h2&gt;
&lt;p&gt;在安装面板之前，我们需要对 Linux 服务器进行一些必要的初始化设置。本教程以 &lt;strong&gt;Azure B1s&lt;/strong&gt; (CentOS/AlmaLinux 系) 为例，其他 VPS 操作类似。&lt;/p&gt;
&lt;h3&gt;1. 更新系统与安装基础工具&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 安装 YUM 增强工具并更新系统
yum install -y yum-utils
yum update -y
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. 配置 Swap 虚拟内存 (关键)&lt;/h3&gt;
&lt;p&gt;对于像 Azure B1s 这样内存较小（1G）的服务器，开启 Swap 是防止数据库崩溃的关键，建议配置 4G - 8G Swap。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 1. 停止并删除 Azure 默认的临时 Swap (如果有)
swapoff /mnt/resource/swapfile
rm -rf /mnt/resource/swapfile
sed -i &quot;/&apos;\/mnt/resource/swapfile swap swap defaults 0 0&apos;//&quot; /etc/fstab

# 2. 创建新的 Swap 文件 (8G)
dd if=/dev/zero of=/swapfile bs=1024 count=8388608

# 3. 设置权限并格式化
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

# 4. 设置开机自动挂载
echo &apos;/swapfile swap swap sw 0 0&apos; | tee -a /etc/fstab

# 5. 验证
free -m
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;3. 系统优化配置&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 设置时区为上海
timedatectl set-timezone Asia/Shanghai

# 关闭系统防火墙 (推荐使用云服务商的安全组管理端口)
systemctl stop firewalld
systemctl disable firewalld
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;第二阶段：安装 AMH 主机面板&lt;/h2&gt;
&lt;p&gt;AMH 是一款国内老牌的开源主机面板，特点是模块化、极简、占用资源少。&lt;/p&gt;
&lt;h3&gt;1. 执行安装脚本&lt;/h3&gt;
&lt;p&gt;访问 &lt;a href=&quot;https://amh.sh/install.htm&quot;&gt;AMH 官网安装页&lt;/a&gt; 获取最新命令，或直接使用以下定制命令（Nginx 1.20 + MySQL 5.6 + PHP 8.0）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;wget http://dl.amh.sh/amh.sh &amp;amp;&amp;amp; bash amh.sh nginx-1.20,mysql-5.6,php-8.0
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;：如果提示 &lt;code&gt;wget: command not found&lt;/code&gt;，请先执行 &lt;code&gt;yum install -y wget&lt;/code&gt;。安装过程大约需要 15-30 分钟。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;2. 登录面板&lt;/h3&gt;
&lt;p&gt;安装完成后，通过浏览器访问 &lt;code&gt;http://你的IP:8888&lt;/code&gt; 或 &lt;code&gt;https://你的IP:9999&lt;/code&gt;。使用终端输出的默认账号密码登录。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://levifree.com/img/AMH_Install_WordPress/34.png&quot; alt=&quot;AMH登录界面&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3. 安装必要软件扩展&lt;/h3&gt;
&lt;p&gt;登录面板后，进入 &lt;strong&gt;软件商店&lt;/strong&gt;，建议安装以下扩展以支持 WordPress 运行：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;amrewrite&lt;/strong&gt;: 伪静态规则管理&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;amssl&lt;/strong&gt;: SSL 证书自动申请&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pecl_redis / redis&lt;/strong&gt;: 缓存支持 (可选，性能优化)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;php-generic&lt;/strong&gt;: PHP 通用扩展管理&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;第三阶段：部署 WordPress 网站&lt;/h2&gt;
&lt;h3&gt;1. 创建虚拟主机 (LNMP 环境)&lt;/h3&gt;
&lt;p&gt;进入 &lt;strong&gt;网站&lt;/strong&gt; -&amp;gt; &lt;strong&gt;虚拟主机&lt;/strong&gt; -&amp;gt; &lt;strong&gt;新增虚拟主机&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;主标识域名&lt;/strong&gt;: 填写你的域名 (如 &lt;code&gt;example.com&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;运行环境&lt;/strong&gt;: 选择 &lt;code&gt;lnmp01&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;网络&lt;/strong&gt;: 勾选 &lt;code&gt;HTTPS&lt;/code&gt;，端口自动设置为 443&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;URL重写规则&lt;/strong&gt;: 这里选择 &lt;code&gt;wordpress2&lt;/code&gt; 规则（非常重要，否则文章页 404）&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;WordPress Nginx 伪静态规则参考&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;location / {
    try_files $uri $uri/ /index.php?$args;
}
rewrite /wp-admin$ $scheme://$host$uri/ permanent;
&lt;/code&gt;&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://levifree.com/img/AMH_Install_WordPress/2.png&quot; alt=&quot;新增虚拟主机&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2. 申请 SSL 证书&lt;/h3&gt;
&lt;p&gt;进入 &lt;strong&gt;网站&lt;/strong&gt; -&amp;gt; &lt;strong&gt;SSL证书&lt;/strong&gt; -&amp;gt; &lt;strong&gt;AMSSL&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;选择刚才创建的虚拟主机。&lt;/li&gt;
&lt;li&gt;勾选 &lt;strong&gt;一键自动配置&lt;/strong&gt; (推荐 Let&apos;s Encrypt)。&lt;/li&gt;
&lt;li&gt;点击创建，等待证书签发并自动部署。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://levifree.com/img/AMH_Install_WordPress/9.png&quot; alt=&quot;SSL配置&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3. 安装 PHP 扩展&lt;/h3&gt;
&lt;p&gt;WordPress 及其插件通常需要以下 PHP 扩展支持。进入 &lt;strong&gt;网站&lt;/strong&gt; -&amp;gt; &lt;strong&gt;PHP扩展&lt;/strong&gt;，为 &lt;code&gt;lnmp01&lt;/code&gt; 环境安装：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fileinfo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exif&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;imagick&lt;/code&gt; (图像处理)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;opcache&lt;/code&gt; (性能加速)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;curl&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;第四阶段：安装 WordPress 程序&lt;/h2&gt;
&lt;h3&gt;1. 下载与解压&lt;/h3&gt;
&lt;p&gt;点击面板上的 &lt;strong&gt;文件&lt;/strong&gt; 管理，进入网站根目录（通常是 &lt;code&gt;/home/wwwroot/lnmp01/domain/你的域名/web/&lt;/code&gt;）。&lt;/p&gt;
&lt;p&gt;使用 &lt;strong&gt;远程下载&lt;/strong&gt; 功能：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://cn.wordpress.org/latest-zh_CN.tar.gz
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下载完成后，选择文件点击 &lt;strong&gt;智能解压&lt;/strong&gt;。解压后将 &lt;code&gt;wordpress&lt;/code&gt; 文件夹内的所有文件 &lt;strong&gt;移动&lt;/strong&gt; 到 &lt;code&gt;web&lt;/code&gt; 根目录下，并删除默认的 &lt;code&gt;index.html&lt;/code&gt;。&lt;/p&gt;
&lt;h3&gt;2. 配置数据库连接&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;将 &lt;code&gt;wp-config-sample.php&lt;/code&gt; 重命名为 &lt;code&gt;wp-config.php&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;在线编辑该文件，填入数据库信息：&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;define( &apos;DB_NAME&apos;, &apos;wordpress_db&apos; ); // 你的数据库名
define( &apos;DB_USER&apos;, &apos;root&apos; );         // 你的数据库用户名
define( &apos;DB_PASSWORD&apos;, &apos;你的密码&apos; ); // 你的数据库密码
define( &apos;DB_HOST&apos;, &apos;localhost&apos; );
define( &apos;DB_CHARSET&apos;, &apos;utf8&apos; );

// 性能优化参数
define( &apos;WP_MEMORY_LIMIT&apos;, &apos;256M&apos; );
define( &apos;FS_METHOD&apos;, &apos;direct&apos; );
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;回到面板 &lt;strong&gt;数据库&lt;/strong&gt; 管理，创建一个名为 &lt;code&gt;wordpress_db&lt;/code&gt; 的空数据库（名称需与配置文件一致）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;3. 权限修正&lt;/h3&gt;
&lt;p&gt;这一步至关重要：在 &lt;strong&gt;文件&lt;/strong&gt; 管理中，勾选 &lt;code&gt;web&lt;/code&gt; 目录，点击 &lt;strong&gt;权限&lt;/strong&gt;，将所有者改为 &lt;code&gt;www&lt;/code&gt;，用户组改为 &lt;code&gt;www&lt;/code&gt;，并勾选 &lt;strong&gt;递归处理&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://levifree.com/img/AMH_Install_WordPress/25.png&quot; alt=&quot;权限设置&quot; /&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;第五阶段：完成安装&lt;/h2&gt;
&lt;p&gt;打开浏览器访问你的域名，你应该能看到 WordPress 的安装欢迎界面。设置好站点标题、管理员账号密码，点击安装即可！&lt;/p&gt;
&lt;h3&gt;进阶优化建议：WWW 跳转&lt;/h3&gt;
&lt;p&gt;如果你希望访问 &lt;code&gt;www.example.com&lt;/code&gt; 自动跳转到 &lt;code&gt;example.com&lt;/code&gt;（或反之）：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;在 AMH 中创建一个 &lt;strong&gt;子域主机&lt;/strong&gt; &lt;code&gt;www.example.com&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;在 &lt;strong&gt;伪静态规则&lt;/strong&gt; 中添加 301 跳转规则。&lt;/li&gt;
&lt;li&gt;或者直接使用 AMH 面板的 &lt;strong&gt;AMRewrite&lt;/strong&gt; 模块设置域名 301 重定向。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;免责声明&lt;/h2&gt;
&lt;p&gt;&amp;lt;div class=&quot;well&quot;&amp;gt;
&amp;lt;p&amp;gt;本项目提供的教程仅用于技术交流与学习。&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;文中涉及的云服务商（如 Azure）及软件面板（AMH）均为演示环境，作者与其无利益关联。&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;请在合法合规的前提下搭建网站，并在正式上线前做好数据备份。&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;作者不对因操作失误导致的服务器数据丢失承担责任。&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>acme.sh 证书申请指南：免费、自动续期的泛域名 SSL/TLS 证书</title><link>https://levifree.com/posts/acme-sh-free-ssl-certificate-guide/</link><guid isPermaLink="true">https://levifree.com/posts/acme-sh-free-ssl-certificate-guide/</guid><description>从零开始：通过 Cloudflare API 实现全自动化 HTTPS 配置</description><pubDate>Sun, 24 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;“After climbing a great hill, one only finds that there are many more hills to climb.”
“登上高峰后，你会发现还有更多的山峰要翻越。”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div class=&quot;alert alert-info&quot; role=&quot;alert&quot;&amp;gt;
&amp;lt;strong&amp;gt;核心知识点：&amp;lt;/strong&amp;gt; 本文将详细介绍如何使用 &amp;lt;code&amp;gt;acme.sh&amp;lt;/code&amp;gt; 脚本免费申请 &amp;lt;strong&amp;gt;泛域名 (Wildcard)&amp;lt;/strong&amp;gt; 证书，并配置自动续期。重点推荐使用 Cloudflare API Token 进行 DNS 验证，这是目前最安全、便捷的方案。
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;什么是 acme.sh？&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/acmesh-official/acme.sh&quot;&gt;acme.sh&lt;/a&gt; 是一个纯 Shell 编写的 ACME 协议客户端。与官方的 Certbot 相比，它有以下显著优势：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;轻量级&lt;/strong&gt;：无任何依赖，不需要安装 Python。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;兼容性&lt;/strong&gt;：支持几乎所有 Linux 发行版。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;功能全&lt;/strong&gt;：自动处理 cron 任务，支持自动注入证书到 Nginx/Apache，支持 Docker 部署。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DNS 支持&lt;/strong&gt;：内置了 100+ 种 DNS 服务商的 API 支持。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;第一步：安装 acme.sh&lt;/h2&gt;
&lt;p&gt;安装过程非常简单，只需要一行命令。建议切换到 &lt;code&gt;root&lt;/code&gt; 用户执行。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 替换为你的真实邮箱，用于接收证书过期提醒
curl https://get.acme.sh | sh -s email=my@example.com
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装完成后，脚本会自动完成以下操作：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;将 acme.sh 安装到 &lt;code&gt;~/.acme.sh/&lt;/code&gt; 目录。&lt;/li&gt;
&lt;li&gt;创建 &lt;code&gt;alias acme.sh=~/.acme.sh/acme.sh&lt;/code&gt; 别名。&lt;/li&gt;
&lt;li&gt;自动创建每日 Cron Job 以检查证书是否需要更新。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;使命令生效&lt;/h3&gt;
&lt;p&gt;安装后，你需要刷新一下 Shell 环境：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;source ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;第二步：切换默认 CA（推荐）&lt;/h2&gt;
&lt;p&gt;acme.sh 默认使用 ZeroSSL 作为证书颁发机构。如果你更习惯使用 &lt;strong&gt;Let&apos;s Encrypt&lt;/strong&gt;，可以随时切换：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;acme.sh --set-default-ca --server letsencrypt
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;第三步：申请证书（推荐 DNS 方式）&lt;/h2&gt;
&lt;p&gt;acme.sh 支持 HTTP 验证和 DNS 验证。&lt;strong&gt;强烈推荐使用 DNS 验证&lt;/strong&gt;，因为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;支持&lt;strong&gt;泛域名&lt;/strong&gt;证书（如 &lt;code&gt;*.example.com&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;不需要依赖 Web 服务器，不需要公网 IP（内网服务器也能用）。&lt;/li&gt;
&lt;li&gt;如果不使用 API 自动验证，每 3 个月要手动解析一次 TXT 记录，非常麻烦。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;准备：获取 DNS API 凭证&lt;/h3&gt;
&lt;p&gt;我们以 &lt;strong&gt;Cloudflare&lt;/strong&gt; 为例。为了安全起见，我们不再使用 Global API Key，而是使用权限受限的 &lt;strong&gt;API Token&lt;/strong&gt;。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;登录 Cloudflare 控制台 -&amp;gt; &lt;strong&gt;My Profile&lt;/strong&gt; -&amp;gt; &lt;strong&gt;API Tokens&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;点击 &lt;strong&gt;Create Token&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;选择 &lt;strong&gt;Edit zone DNS&lt;/strong&gt; 模板。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zone Resources&lt;/strong&gt; 选择 &lt;code&gt;Include -&amp;gt; All zones&lt;/code&gt; 或者指定你的域名。&lt;/li&gt;
&lt;li&gt;生成并复制你的 &lt;code&gt;Token&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;同时在控制台首页右侧找到你的 &lt;code&gt;Account ID&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;执行申请&lt;/h3&gt;
&lt;p&gt;在终端中导入我们将才获取的凭证：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;export CF_Token=&quot;你的_API_Token&quot;
export CF_Account_ID=&quot;你的_Account_ID&quot;

# 申请泛域名证书（包括主域名和所有子域名）
# --keylength ec-256 表示申请更现代、更快速的 ECC 证书
acme.sh --issue --dns dns_cf -d example.com -d *.example.com --keylength ec-256
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：脚本执行完毕后，你的 ID 和 Token 会被自动加密保存在 &lt;code&gt;~/.acme.sh/account.conf&lt;/code&gt; 中，下次自动续期时无需再次输入。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr /&gt;
&lt;h2&gt;第四步：安装证书到 Web 服务器&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;千万不要&lt;/strong&gt;直接在 Nginx/Apache 配置文件中指向 &lt;code&gt;~/.acme.sh/&lt;/code&gt; 目录下的文件。该目录结构随时可能变动。&lt;/p&gt;
&lt;p&gt;你应该使用 &lt;code&gt;--install-cert&lt;/code&gt; 命令将证书复制到指定位置，并告知脚本重载 Web 服务。&lt;/p&gt;
&lt;h3&gt;Nginx 示例&lt;/h3&gt;
&lt;p&gt;假设你的 Nginx 证书目录在 &lt;code&gt;/etc/nginx/ssl&lt;/code&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;mkdir -p /etc/nginx/ssl

acme.sh --install-cert -d example.com --ecc \
--key-file       /etc/nginx/ssl/server.key  \
--fullchain-file /etc/nginx/ssl/fullchain.cer \
--reloadcmd     &quot;systemctl force-reload nginx&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Nginx 配置参考&lt;/h3&gt;
&lt;p&gt;在你的 Nginx 配置文件中（如 &lt;code&gt;/etc/nginx/conf.d/example.com.conf&lt;/code&gt;），确保路径与上面一致：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;server {
    listen 443 ssl http2;
    server_name example.com;

    # 证书路径配置
    ssl_certificate     /etc/nginx/ssl/fullchain.cer;
    ssl_certificate_key /etc/nginx/ssl/server.key;

    # 推荐的 SSL 安全参数
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
    
    # ... 其他配置
}
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;进阶：如何更新证书？&lt;/h2&gt;
&lt;h3&gt;自动更新（无需操作）&lt;/h3&gt;
&lt;p&gt;acme.sh 会每 60 天自动检测证书。如果发现证书即将过期，它会自动：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;调用 API 重新验证 DNS。&lt;/li&gt;
&lt;li&gt;重新签发证书。&lt;/li&gt;
&lt;li&gt;将新证书复制到 &lt;code&gt;/etc/nginx/ssl/&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;执行 &lt;code&gt;systemctl force-reload nginx&lt;/code&gt; 重载服务。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;你完全不需要操心。&lt;/p&gt;
&lt;h3&gt;脚本本身的更新&lt;/h3&gt;
&lt;p&gt;建议开启 acme.sh 脚本的自动升级功能，以保持与最新的 ACME 协议兼容：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;acme.sh --upgrade --auto-upgrade
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;常见问题排查&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Unknown error (DNS 验证失败)&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;检查 API Token 权限是否正确（是否包含 &lt;strong&gt;Zone.DNS&lt;/strong&gt; 的 &lt;strong&gt;Edit&lt;/strong&gt; 权限）。&lt;/li&gt;
&lt;li&gt;检查 DNS 服务器是否生效，有时生效有延迟，可以加 &lt;code&gt;--dnssleep 60&lt;/code&gt; 参数让脚本多等一会儿。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Web 验证失败&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;确保 80 端口开放且没有被防火墙拦截。&lt;/li&gt;
&lt;li&gt;如果你只是临时验证，没有 Nginx，可以使用 &lt;code&gt;--standalone&lt;/code&gt; 模式（需临时停止占用 80 端口的服务）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;免责声明&lt;/h2&gt;
&lt;p&gt;&amp;lt;div class=&quot;well&quot;&amp;gt;
&amp;lt;p&amp;gt;本项目提供的教程仅用于教育和测试目的。&amp;lt;/p&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;在使用脚本修改生产环境配置前，建议先备份相关配置文件。&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;Token 和 Key 等敏感信息请妥善保管，切勿泄露。&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;因使用不当导致的服务中断或数据丢失，作者不承担责任。&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item><item><title>前端开发实战：如何使用 Charles 脚本进行 API 数据 Mock</title><link>https://levifree.com/posts/javascript-mock-api-tutorial/</link><guid isPermaLink="true">https://levifree.com/posts/javascript-mock-api-tutorial/</guid><description>告别后端依赖，利用本地代理工具自行构造测试场景</description><pubDate>Fri, 22 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&quot;Quality is not an act, it is a habit.&quot;&lt;br /&gt;
&quot;质量不是一种行为，而是一种习惯。&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div class=&quot;alert alert-info&quot; role=&quot;alert&quot;&amp;gt;
&amp;lt;strong&amp;gt;面向人群：&amp;lt;/strong&amp;gt; 本文主要面向 Web 前端和移动端开发者。你将学习如何配合抓包工具（如 Charles 或 Fiddler），利用简单的脚本逻辑，在本地拦截并修改服务器响应，从而高效地测试 App 或网页的 UI 表现。
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;前言：前端开发的痛点&lt;/h2&gt;
&lt;p&gt;在项目开发过程中，前后端通常是并行开发的。但我们经常遭遇以下尴尬：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;后端接口未就绪&lt;/strong&gt;：后端还在写 CRUD，前端页面已经切好了，却只能填假数据。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;异常场景难复现&lt;/strong&gt;：想自测“服务器返回空列表”或“超长文本截断”，需要后端配合改库，非常麻烦。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据覆盖不全&lt;/strong&gt;：测试环境造数据成本高，甚至还需要删库重跑。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这时，&lt;strong&gt;本地 Mock&lt;/strong&gt; 技术就派上用场了。通过在网络层拦截请求，我们可以运行一段简单的代码来实时修改 Response（响应体），让客户端以为服务器返回了我们要的数据。&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;核心原理：中间人 (MITM) 脚本&lt;/h2&gt;
&lt;p&gt;大多数专业的网络调试工具（如 Charles, Fiddler, Proxyman）都支持 &lt;strong&gt;Scripting (脚本)&lt;/strong&gt; 功能。&lt;/p&gt;
&lt;p&gt;流程如下：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;拦截 (Intercept)&lt;/strong&gt;：工具捕获特定的 URL 请求（例如 &lt;code&gt;api.myapp.com/user&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;执行 (Execute)&lt;/strong&gt;：工具运行你编写的 &lt;code&gt;.js&lt;/code&gt; 脚本。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;篡改 (Modify)&lt;/strong&gt;：脚本读取原始 Response JSON，修改特定字段。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;返回 (Return)&lt;/strong&gt;：客户端接收到修改后的数据。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr /&gt;
&lt;h2&gt;JavaScript 脚本编写实战&lt;/h2&gt;
&lt;p&gt;脚本的核心逻辑通常分为三步：&lt;code&gt;Parse&lt;/code&gt; (解析) -&amp;gt; &lt;code&gt;Modify&lt;/code&gt; (修改) -&amp;gt; &lt;code&gt;Stringify&lt;/code&gt; (重组)。&lt;/p&gt;
&lt;h3&gt;场景一：Mock 长文本 (测试 UI 布局)&lt;/h3&gt;
&lt;p&gt;假设后端返回的标准用户信息如下：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;id&quot;: 101,
  &quot;nickname&quot;: &quot;Levi&quot;,
  &quot;bio&quot;: &quot;Frontend Developer&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们需要测试当用户昵称特别长时，UI 是否会正确换行或显示省略号。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;脚本代码示例&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 1. 获取原始响应体
// 大多数工具会将响应体暴露为 response.body 或类似变量
let obj = JSON.parse($response.body);

// 2. 修改数据：注入超长文本，测试边界情况
obj.nickname = &quot;这是一段非常非常长的测试文本，用于验证 UI 组件在极端情况下的布局表现是否正常&quot;;

// 3. 重组并返回
// 将修改后的对象转回字符串
$done({ body: JSON.stringify(obj) });
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;场景二：模拟服务器错误 (测试健壮性)&lt;/h3&gt;
&lt;p&gt;App 需要能够优雅地处理服务器报错（如 500 错误或网络超时）。我们可以编写脚本，强制把正常的成功响应改为错误信息。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;脚本代码示例&lt;/strong&gt;：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;// 注意：这次我们不解析原数据，直接构造错误响应

let errorResponse = {
    &quot;code&quot;: 500,
    &quot;status&quot;: &quot;error&quot;,
    &quot;message&quot;: &quot;Internal Server Error: 模拟服务器内部错误&quot;
};

$done({ 
    status: 500, // 修改 HTTP 状态码
    body: JSON.stringify(errorResponse) 
});
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;进阶技巧：条件断点与随机化&lt;/h2&gt;
&lt;p&gt;为了测试更真实的场景，我们可以引入随机逻辑，模拟不稳定的数据返回。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;let obj = JSON.parse($response.body);

// 随机生成 50% 的概率让列表为空，测试“空状态”页面 (Empty State)
if (Math.random() &amp;gt; 0.5) {
    obj.data_list = []; 
}

console.log(&quot;当前 Mock 状态: &quot; + (obj.data_list.length === 0 ? &quot;空列表&quot; : &quot;有数据&quot;));

$done({ body: JSON.stringify(obj) });
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2&gt;最佳实践与注意事项&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;异常捕获&lt;/strong&gt;：在解析 JSON 时最好包裹在 &lt;code&gt;try-catch&lt;/code&gt; 块中，防止原始数据解析失败导致脚本报错。&lt;pre&gt;&lt;code&gt;try {
    let obj = JSON.parse($response.body);
    // ... modifications
    $done({ body: JSON.stringify(obj) });
} catch (e) {
    console.log(&quot;JSON Parse Error: &quot; + e);
    $done({}); // 保持原状
}
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;仅用于开发自测&lt;/strong&gt;：此类脚本仅应在本地开发环境或测试环境使用，严禁用于生产环境或恶意攻击。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;仅处理已授权流量&lt;/strong&gt;：请确保你拦截和调试的是自己负责的应用、测试域名或已获得授权的接口，不要触碰第三方未授权系统。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;避免真实用户数据&lt;/strong&gt;：调试环境应尽量使用脱敏数据，避免在脚本、日志或截图中暴露手机号、邮箱、Token 等敏感信息。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;掌握基于脚本的网络层 Mock 技术，能极大减少前端开发对后端的依赖。你不需要等待后端部署，也不需要求人改数据，几行代码就能构建出你想要的任何测试场景，大幅提升开发效率。&lt;/p&gt;
&lt;p&gt;Happy Coding!&lt;/p&gt;
</content:encoded></item><item><title>Git 实战：三种方法让你的 Fork 仓库与上游保持同步</title><link>https://levifree.com/posts/git-sync-fork-upstream-guide/</link><guid isPermaLink="true">https://levifree.com/posts/git-sync-fork-upstream-guide/</guid><description>从点击按钮到自动化脚本：全方位掌握 Git Upstream 同步技巧</description><pubDate>Wed, 06 Dec 2023 00:00:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;&quot;We loved with a love that was more than love.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;lt;div class=&quot;alert alert-info&quot; role=&quot;alert&quot;&amp;gt;
&amp;lt;strong&amp;gt;场景描述：&amp;lt;/strong&amp;gt; 当我们在 GitHub 上 Fork 了一个开源项目后，原作者（Upstream）更新了代码，而我们的 Fork 仓库还停留在旧版本。如何将上游的更新合并到我们的仓库中？本文提供三种解决方案：不仅包含最简单的 UI 操作，还有硬核的 CLI 命令，以及“懒人必备”的 GitHub Actions 自动化脚本。
&amp;lt;/div&amp;gt;&lt;/p&gt;
&lt;h2&gt;方法一：GitHub 网页端一键同步 (最简单)&lt;/h2&gt;
&lt;p&gt;从 2021 年开始，GitHub 原生支持了网页端同步功能，适合绝大多数普通用户。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;打开你的 Fork 仓库页面。&lt;/li&gt;
&lt;li&gt;在分支状态栏右侧找到 &lt;strong&gt;Fetch upstream&lt;/strong&gt; 下拉菜单（通常会提示 &quot;This branch is X commits behind &amp;lt;upstream&amp;gt;:main&quot;）。&lt;/li&gt;
&lt;li&gt;点击 &lt;strong&gt;Fetch and merge&lt;/strong&gt; 按钮完成自动快进或合并提交。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;提示&lt;/strong&gt;：如果上游有新提交且你的分支没有新改动，GitHub 会执行 Fast-Forward（快进合并）；如果本地有分叉改动，GitHub 网页端可能会提示无法自动合并，此时需要通过方法二的命令行方式处理冲突。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;优点：无需任何命令，操作极简。&lt;/em&gt;
&lt;em&gt;缺点：需要手动点，且解决冲突能力较弱。&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;方法二：Git 命令行同步 (最专业)&lt;/h2&gt;
&lt;p&gt;如果你是开发者，建议掌握命令行（CLI）方式，因为这能让你完全控制合并过程，方便处理代码冲突。&lt;/p&gt;
&lt;h3&gt;1. 配置上游仓库&lt;/h3&gt;
&lt;p&gt;首先，我们需要告诉本地 git，&quot;上游&quot;在哪里。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 查看当前远程仓库
git remote -v
# 输出通常只有 origin (你的仓库)

# 添加上游仓库 (请将 URL 替换为原作者的仓库地址)
git remote add upstream https://github.com/ORIGINAL_OWNER/ORIGINAL_REPO.git

# 再次查看，应该能看到 upstream
git remote -v
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;2. 拉取并合并&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# 1. 切换到主分支 (main 或 master)
git checkout main

# 2. 从上游拉取最新代码到本地缓存
git fetch upstream

# 3. 将上游代码合并到你的本地分支
git merge upstream/main

# 4. 推送到你的 GitHub 仓库
git push origin main
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;em&gt;优点：完全控制权，可处理复杂冲突。&lt;/em&gt;
&lt;em&gt;缺点：步骤较多，需要 Git 基础。&lt;/em&gt;&lt;/p&gt;
&lt;hr /&gt;
&lt;h2&gt;方法三：GitHub Actions 定时自动同步 (最省心)&lt;/h2&gt;
&lt;p&gt;如果你 Fork 仓库是为了做一个镜像（Mirror），或者单纯想保持最新而懒得手动去点，可以使用 GitHub Actions 设置定时任务。&lt;/p&gt;
&lt;h3&gt;1. 创建工作流文件&lt;/h3&gt;
&lt;p&gt;在你的仓库中，点击 &lt;strong&gt;Actions&lt;/strong&gt; -&amp;gt; &lt;strong&gt;New workflow&lt;/strong&gt;，创建一个名为 &lt;code&gt;.github/workflows/sync.yml&lt;/code&gt; 的文件。&lt;/p&gt;
&lt;p&gt;如果你 Fork 的仓库里已存在其他工作流，点击 &lt;code&gt;Actions&lt;/code&gt; 后可能需要先点击 &lt;code&gt;New workflow&lt;/code&gt;，如下图所示：
&lt;img src=&quot;https://levifree.com/img/syncworkflow/createworkflow.png&quot; alt=&quot;新建工作流&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;2. 填入配置代码&lt;/h3&gt;
&lt;p&gt;复制以下 YAML 内容，并&lt;strong&gt;务必修改第四步中的上游仓库地址&lt;/strong&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name: Upstream Sync

permissions:
  contents: write

on:
  schedule:
    - cron: &quot;0 0 * * *&quot; # 每天 UTC 时间 0:00 (北京时间 8:00) 执行一次
  workflow_dispatch: # 允许手动点击按钮触发

jobs:
  sync_latest_from_upstream:
    name: Sync latest commits from upstream repo
    runs-on: ubuntu-latest
    if: ${{ github.event.repository.fork }}

    steps:
      # 第一步：检出代码
      - name: Checkout target repo
        uses: actions/checkout@v3

      # 第二步：执行同步动作
      - name: Sync upstream changes
        id: sync
        uses: aormsby/Fork-Sync-With-Upstream-action@v3.4
        with:
          # ⚠️⚠️ 请修改下方这两行！填入原作者的仓库地址和分支 ⚠️⚠️
          upstream_sync_repo: original-author/original-repo
          upstream_sync_branch: main
          
          # 你的分支名
          target_sync_branch: main
          target_repo_token: ${{ secrets.GITHUB_TOKEN }}
          test_mode: false

      # 第三步：检查是否失败（通常是因为上游改动了 workflow 文件，出于安全 GitHub 会暂停自动更新）
      - name: Sync check
        if: failure()
        run: |
          echo &quot;[Error] 自动同步失败。可能是上游仓库更新了 .github/workflows 文件。&quot;
          echo &quot;请手动前往 GitHub 界面 Fetch Upstream 一次以恢复自动化。&quot;
          exit 1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;请参考下图说明进行修改：
&lt;img src=&quot;https://levifree.com/img/syncworkflow/modification.png&quot; alt=&quot;修改说明&quot; /&gt;&lt;/p&gt;
&lt;p&gt;以下是配置完成后的完整代码示例图（注意：部分密钥无需手动填写）：
&lt;img src=&quot;https://levifree.com/img/syncworkflow/sync.jpg&quot; alt=&quot;全部代码&quot; /&gt;&lt;/p&gt;
&lt;h3&gt;3. 注意事项&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;首次运行&lt;/strong&gt;：创建完文件后，建议切到 Actions 页面手动触发一次（Run workflow）以验证配置是否正确。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;保护机制&lt;/strong&gt;：如果上游仓库修改了 workflow 文件，GitHub 为了安全会暂停你的自动任务，此时需要手动 Sync 一次来重新激活。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;偶尔同步&lt;/strong&gt;：直接用 &lt;strong&gt;方法一&lt;/strong&gt;（网页点一下）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;开发代码&lt;/strong&gt;：必须用 &lt;strong&gt;方法二&lt;/strong&gt;（命令行），因为你需要处理 Merge 冲突。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;纯粹备份&lt;/strong&gt;：使用 &lt;strong&gt;方法三&lt;/strong&gt;（Actions），设置好后就可以当甩手掌柜了。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;免责声明&lt;/h2&gt;
&lt;p&gt;&amp;lt;div class=&quot;well&quot;&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;本文提供的自动化脚本仅用于合规的开源项目同步。&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;请勿用于恶意克隆或违反 GitHub 服务条款的行为。&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;自动化同步可能会覆盖你对仓库的自定义修改，请谨慎使用。&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/div&amp;gt;&lt;/p&gt;
</content:encoded></item></channel></rss>