张路.

ARCHITECTURE

这个站怎么运作

内容是 git 仓库里的纯文本文件,一条命令编译成一整套静态产物,push 即部署。 没有 CMS、没有数据库、没有服务端运行时 —— 线上只有静态文件和 CDN。

这条约束是所有其它设计的根源:因为没有服务端,搜索必须在客户端做、端点必须是构建时落盘的文件、 不能有鉴权、也不可能实时依赖外部 API。

全景:从一个文件到线上

四层:内容源 → 契约 → 构建 → 产物,然后 push 到 CDN。 唯一需要人手写的是第一层。

① 内容源 · 唯一需要人手写的东西

src/content/<coll>/

中文内容

src/content/<coll>En/

英文平行版

src/data/*.json

简介 / 社交

src/i18n/ui.ts

UI 文案

② 契约层 · Zod schema

src/content/config.ts

缺字段 / 类型不对 → build 失败(不是线上出空白页)


pnpm build

③ 构建 · Astro

字段形状:src/lib/api.ts(zh / en 共用一份)

④ 产物 · 全部是静态文件

5 类内容 × 2 语言

共 60 篇 × 2

页面 HTML

每页都有中英两版 + 404

24 个端点类型

12 类 × 2 语言 → 120 个 JSON 文件

2 份 RSS

/rss.xml · /en/rss.xml

2 份 llms.txt

agent 自发现入口

sitemap · robots · _headers

含 hreflang / Content-Signal / CORS


git push origin main

Cloudflare Pages

1–2 分钟上线

浏览器

zhanglu.net · /en/

agent

JSON 端点

/api · /en/api

CLI

npm 包

npx zhanglu-net

爬虫

sitemap + robots

Content-Signal

5 类内容

每一类都有中英两份平行内容,数量 1:1。 一个集合 = 一个目录,一篇内容 = 一个 markdown 文件。

集合 篇数 页面 端点 备注
projects 项目 8 × 2 列表 + 详情 列表 + 详情 带 loc / persona / cover
articles 文章 5 × 2 仅列表 仅列表 写作索引,链接指向原始出处
presentations 展示 4 × 2 仅列表 仅列表 卡片直接跳外链
skills Skills 42 × 2 列表 + 详情 列表 + 详情 16 个自动同步 + 14 个手写
weekly 周报 1 × 2 列表 + 详情 列表 + 详情 脱敏公开版

写一次,出现在 7 个地方

这是整个结构最实在的收益。以 src/content/projects/boss.md 为例 —— 写一个文件,自动出现在下面每一处,没有任何一处需要手动同步

projects/boss.md

│ pnpm build

1 /projects/boss
2 /projects 列表卡片
3 首页「精选项目」
4 /api/projects.json agent
5 /api/projects/boss.json(含全文) agent
6 /api/search.json 语料 agent / CLI
7 RSS + sitemap 条目 订阅者 / 搜索引擎

英文版 projectsEn/boss.md 同理再出 7 份

双语是怎么做的

路由

中文在根 /,英文在 /en/。资源、/api、外链、锚点都不加前缀 —— 只有页面路由加。

语言判定

组件里 getLangFromUrl(Astro.url) 自检,不靠 props 一层层传下去。

内容

平行 *En 集合,不是同集合加语言字段 —— 后者要在十几处消费端加过滤,漏一处就串语言。代价是内容写两份。

UI 文案

src/i18n/ui.ts 字典(zh / en 两套)。页面独有的长散文写在各自语言的页面文件里,不进字典。

首访自适应

navigator.language 跳转 —— 英文浏览器打开 zhanglu.net 会自动到 /en/。

手动切换

页头「中 / EN」把选择写进 localStorage,之后以你的选择为准,不再自动跳

SEO

<html lang>hreflang(zh-CN / en / x-default)、og:locale、分语言 RSS、sitemap i18n。

给机器读的那一层

站点内容同时以 JSON 形式发布,agent 不需要解析 HTML。详细用法在 /agents

自发现入口

/llms.txt · /en/llms.txt

manifest

/api/index.json —— counts + 全部端点 + 语言交叉链接

列表类 · 8 个

projects · articles · presentations · skills · weekly · about · social · search

详情类 · 3 个(含 body_md 全文)

projects/{slug} · skills/{slug} · weekly/{slug}

几条约束,以及为什么

这些不是抽象原则,每条背后都有一次真实的返工。

没有服务端

线上只有静态文件。永远可用、零运维、构建即快照。所有其它设计都是这条的推论 —— 所以搜索在客户端做、端点是构建时落盘的文件、不做鉴权。

schema 强校验

Zod 定义在 src/content/config.ts。内容缺字段或类型不对,build 直接失败 —— 而不是线上出一个空白页。

字段只定义一次

所有端点的字段形状在 src/lib/api.ts,zh / en 共用同一份 builder。早期各端点内联字段,漂过一次:列表有 loc/persona/cover、详情没有。

会漂的数字 build 时算

本页所有计数、/agents 上的 CLI 行数与版本号,都是构建时读出来的。曾经写死「270 行」,实际早已 500+。

状态码可信

不存在的路径返回真 404,不是 200 + 首页。agent 可以直接用状态码判断端点存在与否。

表驱动

CLI 的 KINDS 表:加一种内容类型只改一处,list / get / 帮助文本自动跟上。

从改一个字到上线

改 src/content/... 或 src/data/...
  │
  ├── pnpm dev      本地热更新看效果
  ├── pnpm build    必过(schema 校验 + 产物生成)
  ├── docs/dev-log/ 留一条过程记录
  └── git push main → Cloudflare Pages → 1–2 分钟上线

完整操作规范在仓库的 AGENTS.md —— 那是给 AI agent 和未来的我看的权威指南,含 schema、踩过的坑、发版流程。 本页的架构文档版本:docs/architecture.md