建站记录

建立个人网站记录

从一页自述到实时状态与认证后台,记录 Lindeer 个人网站的设计、实现、故障和引用来源。

这是一份截至 2026 年 7 月 22 日的工程记录。文中“使用”指项目实际安装或参与构建的组件,“参考”只表示研究过其功能或视觉表达;两者不会混写。没有得到确认的个人经历和内容也不会被补写。

2026 年 7 月 20 日,这个网站还只是一页自述。最初的目标很简单:让从社交平台点进来的人,能快速看到愿意公开的信息。两天后,它已经有了文章、动态、归档、搜索、关于页、实时状态,以及一个带身份验证的内容后台。

这篇文章记录它是怎样一步步长出来的,也把真正使用或参考过的开源项目集中列在文末。

先确定内容边界

个人网站很容易变成一份“看起来完整”的模板:技能、经历、爱好和口号都有,但未必属于站主。我从一开始就给内容设了一条边界:只写已经提供、并且愿意公开的信息;没有内容的地方宁可暂时留空。

因此,首页负责成为入口,文章负责展开问题,动态记录短更新,归档和搜索帮助回看。B站、小红书、X 和 Steam 都使用直达链接,不让访问者再搜索一次。

用 Hugo 建立自己的结构

网站使用 Hugo 生成静态页面,正文保存在 Markdown 中。我没有直接套用现成的 Hugo 或 Hexo 主题,而是从布局、样式到交互逐步实现自己的版本。

这样做的代价是要自己处理导航、文章列表、分类标签、搜索索引、数学公式和响应式细节;好处是内容仍然是可读的文件,页面结构也不会被主题的默认设计牵着走。

站内公式由 KaTeX 在需要的文章中按需加载。Bricolage Grotesque 与 Newsreader 字体文件通过 Fontsource 打包到站内,页面不依赖第三方字体 CDN。

首页不是一次完成的

首页先有全屏开场,再逐步补齐视觉和交互细节。为了提高开屏图片清晰度,我准备了 1060 与 2120 像素两档 WebP,由浏览器按设备选择;开场和正文之间使用双层波形过渡,而“往下看”的位置单独依据波形高度与安全区计算,避免它在不同屏幕上错位。

导航、开场信息板、搜索和移动菜单采用有限范围的玻璃材质,而不是给所有卡片统一加模糊。网站同时支持明暗主题、三档正文字号、键盘焦点和移动端布局。

这些细节大多来自反复查看真实页面后修正:图片不够清晰就补高分辨率资源,波形没有出现就检查层级,提示文字错位就把它从普通内容流中拆出来。最终效果不是一次生成的设计稿,而是一连串可以被验证的小修改。

把“实时状态”做成诚实的状态

实时状态的数据链路是:

Windows 上报器 → CloudBase live-status 服务 → EdgeOne /api/live-state 同源代理 → 网站状态栏

页面只展示当前应用、媒体信息和更新时间。它不会公开窗口标题、浏览器网址或文件名;写入 CloudBase 服务需要密钥,访问者只能读取公开状态。EdgeOne 同源代理失败时,浏览器会回退到 CloudBase 读取端。

桌面端把光标移到状态栏或用键盘聚焦时,详细状态会在下方展开;手机和其他粗指针设备则通过点击展开,再次点击或点到外部即可收起。若上报停止、电脑离线或数据超过有效时间,页面显示离线,而不是把旧数据伪装成实时信息。

状态上报器提供隐藏启动器;后台发布器与动态同步器通过隐藏的 Windows 计划任务运行,避免定时任务执行时反复弹出控制台窗口。

从静态文件走到内容后台

最初编辑内容只能修改 Markdown。为了能直接在网站里更新内容,我又增加了 /admin 后台,支持首页、关于页、文章、动态、社交链接和图片。

登录密码不会明文保存。配置阶段使用 PBKDF2-SHA256 生成哈希;登录后使用 Secure、HttpOnly、SameSite=Strict Cookie,并对写操作校验 CSRF 令牌。内容按不可变 revision 写入 EdgeOne Blob:保存时必须带上当前版本,两个页面同时修改时会报告冲突,而不是让后保存的一方静默覆盖前一方。

图片上传会校验真实文件类型与大小;Markdown 中允许的原始 HTML 会在发布前由 sanitize-html 清洗。结构化内容的读取和写回使用 YAML,函数与脚本打包使用 esbuild

后台的 Blob 接入使用 @edgeone/pages-blob

发布不是只点一下按钮

后台的“保存草稿”和“发布网站”是两步。保存只产生新的内容 revision;发布会把该 revision 放入队列。随后,本机隐藏发布器拉取这份不可变快照,校验允许修改的文件,清洗内容,运行 28 项回归测试,构建 Hugo,再部署到 EdgeOne。

部署命令成功并不等于文章已经可见。发布器还会读取公网的 /admin-publish.json,只有 revision 与目标一致时,后台才显示“公网 revision 已确认”。断网或域名切换延迟时,任务会保留并重试;构建内容有错误时,该 revision 会被阻止,修正后需要重新明确发布。

这条链路显得比直接上传 public 目录更复杂,但它解决了三个实际问题:后台内容不会覆盖未合并的本地修改,发布失败不会丢失目标版本,后台也不会把“部署请求已提交”误报成“公网已经更新”。

故障与约束怎样改变方案

第一次发生在 EdgeOne CLI 1.6.14。项目本地检查发现,环境变量子命令启动了异步请求,但顶层流程没有等待它完成便退出,因此退出码为 0 也不能证明变量已经写入。最后增加了版本限定的安全包装器:密钥通过标准输入传递,命令在没有 .env 的隔离临时目录运行,并在内存中核对远端结果;只要 CLI 版本或内部结构变化,包装器就拒绝继续。

登录验证还有一项明确的计算约束。当前方案保留 310,000 次 PBKDF2-SHA256,并把计算密集的后台 API 打包到 Node.js Cloud Function;Edge Middleware 只负责精确保护私有 seed 路径等轻量工作。

这些故障与约束都提醒我:成功退出、HTTP 已响应、部署命令已结束,都不是最终结果。真正值得验证的是远端变量是否一致、登录是否可用、目标 revision 是否已经出现在公网。

参考与来源说明

以下项目是网站实际安装或参与构建的开源组件:

下面这些是功能或视觉研究来源,没有直接套用其主题、文字、图片或源码:

建立个人网站的过程并没有在“上线”时结束。真正的分界点,是它开始有明确的内容边界、可解释的数据流、能恢复的发布流程,以及一份不含糊的来源清单。