把 1998 年搬进 2026:一个 SQLite、一个 DOS 提示符、六个依赖
开源地址:
github.com/xiwen-haochi/oldie-blog前置声明:它有 bug,而且我清楚地知道是哪些。
一、起因
我逛了三个月的老式个人主页,发现一件事:它们比现在的博客快。
不是加载快几十毫秒的那种快。是你按回车,页面就在那里了。没有轮询,没有水合,没有一个 3 MB 的框架在后台偷偷跑。没有"加载中"。
于是我想做一个:界面停在 1998 年,行为停在今天。
现在它长这样:
在这里插入图片描述

二、它长什么样
文章列表


置顶的在最上面,新的在最下面。排序规则只有一条,写在一个地方,全站共用 ------ 我曾经把这条规则在三处各写了一遍,然后三处互相不同意。
文章页

Markdown 进,HTML 出。description 缺省取正文开头,中英文混排、全角标点、行内代码都正常,阅读时间按中文习惯算(一个字约等于两个拉丁字符宽)。
1998 模式

一键把整站换回藏青桌面 + 居中排版 + Times New Roman。
需要说明的是:它只是个 CSS 主题。它不会让浏览器变慢,不会把 HTTP 响应改回去,也不会停止计算它刚刚花 8ms 渲染出来的 HTML。别指望更多,也别担心更多。
后台

后台地址可以改,默认 /admin,从不出现在任何公开页面里,也不会被搜索引擎收录。
三、几个我自己比较喜欢的功能
① 每个页面都有一个 DOS 终端
按 Ctrl + K。
它不是录屏,也不是装饰。它执行的是真命令,打的是真数据:
dos
C:\OLDIE>dir
C:\OLDIE>search 关键词
C:\OLDIE>stats
C:\OLDIE>snake
C:\OLDIE>neofetch
neofetch 那一栏的运行时依赖数,是从 package.json 里读的。你改一个依赖,输出就变 ------ 不会像很多"关于本站"页面那样,写死一行字然后过期三年。
还有 ping 是从你浏览器真实发一次请求 测出来的往返时间,不是 Math.random()。
② 终端里能打游戏
dos
C:\OLDIE>snake
SNAKE (c) 1991, one small computer
arrow keys or WASD to steer · q quits
方向键或 WASD 控制,q 退出,最高分存 localStorage。整个游戏跑在你浏览器里,服务器全程不知道它存在。
③ 芯片音乐,0 字节
按 ♪ 按钮。那个 BGM 是 Web Audio 现场合成的方波。
音频资源大小:0 字节。 下载量 0。你按下去才开始有声音。
④ 留言簿
带审核队列,有蜜罐反垃圾。留言可以选择不公开地址 ------ 签名的人永远看不到自己的邮箱被谁拿走。

⑤ 全文搜索,懂中文
不是把中文塞进英文分词器里祈祷。
- 中文按 unigram + bigram 建倒排索引,所以搜"后训练"能命中"大模型的后训练"
- 支持
tag:前端限定标签 - 支持
"一个短语"精确匹配 - 支持
in:title只搜标题
⑥ 其它
webring(带邻居)、访问计数器(总数/今日/独立访客/在线)、走马灯、闪烁文字、每篇文章都能下载 .TXT、中英切换。
朗读功能:正文里点「📻」,用系统自带的语音合成念给你听。一篇长文通勤路上听正好。
四、技术上我做了什么选择
全部数据在一个文件里
data/oldie.sqlite:
设置、文章、页面、留言、访问计数、订阅者、会话 ------ 全在里面。
用 Node 自带的 node:sqlite,不装任何数据库。
这里有个我踩过的坑,值得单独说:我最早是"每个 store 一个 JSON 文件"的架构,后来改成单一数据库。改完之后,示例站点里那句"存储:Markdown 文件 + 几个 JSON,没有数据库就没有迁移"就变成了假话,而且它就印在每个新克隆的人会看到的关于页面上。
教训:文档漂移比代码过时更难发现。代码过时会有测试报警,文档里的假话只会安静地骗人。
六个运行时依赖
express ejs markdown-it highlight.js gray-matter multer
其余全在 src/lib 里自己写:
| 需要的 | 换成 |
|---|---|
| 搜索库 | 支持中文的倒排索引 |
| session 库 | HMAC 签名 cookie |
bcrypt |
node:crypto 的 scrypt |
| 压缩库 | 手写 gzip 中间件 |
| S3 SDK | 基于 fetch 的 SigV4 签名 |
| ZIP 库 | 自己写的 deflate/store 打包与解包 |
| HTML 消毒库 | 白名单消毒器 |
没有编译器,没有 node-gyp,没有原生模块。 新克隆 pnpm install 几秒就完。
没有构建步骤
前端就一个 public/js/site.js,服务端渲染的 HTML 直接用。
NODE_ENV=production 下静态资源开长缓存,HTML 走 no-cache。就这样。
备份是一个 .zip
后台一键导出,包含数据库、上传的图片和配置。
恢复是先把旧文件挪到一边,不是删掉。 所以手滑了可以反悔 ------ 这是我唯一一个坚持不做"干净利落"的地方。
五、跑起来
bash
git clone git@github.com:xiwen-haochi/oldie-blog.git
cd oldie-blog
nvm use # 推荐 Node 24(.nvmrc 已经提交)
pnpm install
pnpm start # → http://localhost:4173
第一次启动会在终端打印一个临时后台密码。想先看看长什么样:
bash
pnpm seed # 五篇示例文章、一个留言簿、一个计数器
六、部署
bash
cp .env.example .env # 填 ADMIN_PASSWORD 和 SITE_URL
docker compose up -d
推到 GitHub 会自动跑测试并构建镜像到 GitHub Container Registry。
Railway 上只需要三件事:
- 挂一个卷,路径填
/app/data。 Railway 的文件系统是临时的,不挂卷下次重新部署整站清空 - 设
ADMIN_PASSWORD、SESSION_SECRET、SITE_URL三个环境变量 - 端口不用管,Railway 会注入
PORT
八、一句话总结
它不追求快过 LightCMS,也不追求功能全过 WordPress。
它只做一件事:让你按回车,页面就在那里了。
1998 年就是这么上网的。我觉得没什么丢人的。
如果你也想让 2026 年的自己用回 1998 年的方式写博客:
bash
git clone git@github.com:xiwen-haochi/oldie-blog.git
MIT 许可,随便改,改坏了是你自己的博客。