Skip to content

历史:EdgeOne CMS 方案

历史材料: 本文记录已退役的 EdgeOne 方案与当时的约束。当前生产部署为 Cloudflare Pages + Workers KV/R2;以 /guide/deploy 为准。

🔴 这份方案已作废(2026-08-22)——但结论要留着

决定:不走纯 EdgeOne 这条路,接入现有 CMS(Halo)。

作废的理由不是设计错了,是账算不过来:免费版与个人版配额相同且 CPU 时间 很紧,而触顶会殃及同域名下其他所有 EdgeOne 项目——爆炸半径超出本项目。 把可用性压在「配额够不够」上不可接受。

EdgeOne 仍然留着,但只当 CDN 与资源面,不再跑 Function。

下面这些实测结论依旧有效,只是不再约束本项目的架构。留着是因为它们是 花代价换来的,而且换任何一个「静态托管 + 边缘函数」平台都可能重演:

  1. 路由冲突时静态优先,函数不触发——静态产物存在时,边缘准入是死代码;
  2. 规则引擎与重建走同一条同步线路,生效速度一样,从来就不是快路;
  3. 真正的判据是「每请求现读强一致存储」而非「下发配置等它传播」;
  4. KV 最终一致、边缘缓存最长 60 秒;Blob 可对单次读切强一致。

铁律 11 那个「做不到」的困境随这次换宿主一起消失: 一台正常的服务器天然有请求期控制,且不按调用计费。 所以之前那三条候选(全量换 / 分级 / 改铁律 11)都不必选了。

目标:整站一个域名、功能有机结合、统一管理(装了哪些插件、什么显示什么不显示、 访问数据、SEO)。形态是 CMS + 插件,但不跑在 VPS 上,跑在 EdgeOne 边缘。


一、先说清楚缺了什么

@aio/kernel浏览器那一半。它管交互:点一下唤起 ADV、点一下显示精灵。

CMS 还需要边缘那一半——路由、服务端渲染、SEO 元信息。原因很硬:

2,400 多个页面(725 篇剧情 + 241 个角色 + 1,404 条卡牌)的内容如果只在 浏览器里由插件渲染,爬虫看到的是空壳。Google 勉强能跑 JS,百度基本不行—— 而中文资料站的主要流量来源恰恰是百度。

所以插件要有两半,由同一个插件 id 绑在一起:

插件 adv-player
 ├─ 边缘半边(@aio/site)  路由 /story/:id、SSR、meta、sitemap 条目
 └─ 浏览器半边(@aio/kernel)能力 adv.play、surface 挂载、进度回流

后台一个开关同时管住两边——这是「统一管理」在代码上的落点:

关掉 adv-player结果
边缘/story/:id 返回 404(reason: 'plugin-disabled'),且不进 sitemap
浏览器剧本行上的播放按钮消失,剧本照常可读

两半各写一套开关,迟早出现「后台显示关着,页面还在」这种查不出来的状态。 本包的 site.test.ts 里有一个测试专门钉这件事。


二、EdgeOne 上的落点

CMS 部件落在哪备注
内容页静态导出(现状)见 §二·五:静态导出读不到 KV
动态行为 / 后台 / 下架兜底Pages FunctionsKV 仅在边缘函数内可用
站点配置(插件开关、导航、SEO)KV后台写、边缘读
下架清单Blob(强一致模式)见下节,不能放 KV
资源字节Blob 或 COS + CDN20 GiB 量级
权威内容(交叉表、策略、契约)git人工核对过的判断,需要 review 与历史
后台鉴权会话KV短 TTL
访问统计Functions 写 KVEdgeOne 官方有 PV 计数模板
缓存失效cacheTags + 主动 purge见第四节

🔴 KV 是最终一致的,最长 60 秒

官方文档写明:KV 采用中心存储 + 边缘缓存,其它边缘节点最长 60 秒才看到新值

对导航、SEO 文案、插件开关,这没问题——晚一分钟生效无所谓。

只有一件事不能等:下架。 版权方来函时那 60 秒是实打实的暴露窗口。 所以 TakedownList 单独一份、单独一条读取路径,走 Blob 的强一致模式, 代价是需要判定下架的请求多一次强一致读。

这不是洁癖:同类项目里已经出现过因为这种压力而退役公开站的先例, 仓库里有一份 TAKEDOWN.md。下架能力是这个站的必备件,不是可选件。

其它已知限额

KV 单值25 MB
KV 键名512 字节
KV 账号总容量1 GB(免费额度)
KV 命名空间10 个/账号
list() 分页默认 256 条
KV 可用范围仅边缘函数内

这些数字取自 EdgeOne Pages 官方文档页。开工前仍需在控制台复核一次—— 见 docs/CONSTRAINTS.md


二·五、🔴 静态导出与 KV 不兼容——这条要先解决

apps/station 现在是 Next.js 静态导出next buildout/,CI 已在看着)。 而官方文档写明 KV「仅支持在边缘函数内使用」

两件事放一起就是一个硬矛盾:

静态导出的页面在构建时就定型了,请求到来时没有任何代码在跑, 因此读不到 KV。后台改一个插件开关,静态页面不会知道。

三条出路,各有代价:

做法开关生效速度SEO代价
A纯静态:配置在构建期烘焙一次重建+部署(分钟级)✅ 真 HTML后台变成「生成一个配置提交并触发构建」,不是即时开关
B全部改 Functions/SSR秒级(KV 60 秒最终一致)放弃静态导出;每请求都要算,成本与冷启动
C混合(建议)见下多几个活动部件

建议走 C,分工如下

内容交付方式配置从哪来
2,400 个内容页(角色/剧情/卡牌)静态导出构建期烘焙。它们的内容本来就来自交叉表与清单,本就该构建期定型
插件开关、导航、SEO 文案静态页 + 构建期烘焙;后台改动触发重建KV 存的是「下次构建要用的值」
浏览器侧的能力门禁客户端启动时向一个 Function 端点拉当前配置KV,秒级
下架🔴 没有可用机制,见下Blob 强一致
后台、鉴权、统计FunctionsKV

为什么下架不能靠重建

静态页已经在边缘缓存里了,重建要几分钟。收到通知时的正确动作是 立刻让那条路径不可达——那只能由请求路径上的东西做。

packages/siteTakedownListisTakenDown() 因此有两个消费者: 构建期(把下架的页排除出 sitemap 与产物)与请求期(兜住漏网的缓存副本)。 两处都要,缺一个都有窗口。

🔴 请求期那一道:判据一直写错了

原方案写「请求期由规则引擎或 Function 兜住」,把规则引擎当成快路。 两条实测事实(2026-08-22 由维护者查证)把它推翻了:

  1. 规则引擎与重建走同一条同步线路,生效速度一样。 它从来就不是快路—— 这条比「Pages 项目有没有规则引擎」根本得多:就算有,也不解决问题。
  2. 路由冲突时静态优先。 Edge Functions 与 Node.js Functions 的路由若与 静态资源冲突,请求优先被路由到静态资源,函数不会被触发。

所以真正的判据不是「规则引擎 vs 重建」,而是:

每次请求现读一个强一致存储 ——还是—— 下发一份配置等它传播

后者不管叫重建、purge 还是规则引擎,延迟下限是同一个。 只有前者能做到「收到通知,下一个请求就不可达」。

这就把可选项收敛到了一种形状:那条路径由 Function 出内容, 且 Function 每次请求现读下架清单(本仓库早已把它设计成走 Blob 强一致读, 正是为此)。而 Function 要能触发,那条路径下就不能有静态产物。

嵌入面已经是这个形状(tools/pack-embed-pages.mjs 把页面搬进函数的包、 删掉 out/ 下的原件)。把它套到 2,400 个内容页上是一个大决定 (每请求一次函数调用的成本与延迟、边缘冷启动、SEO 影响), 不该由实现顺手定。见「待拍板事项」。

这条的落地顺序

先确认 EdgeOne 的缓存 purge 是否支持按 tag(待验证项 3)。 只支持按 URL 的话,rev: 标签方案要换成版本化路径(如 /_v7/character/1001 再由规则重写),那会改变整个 URL 设计——必须在铺开内容页之前定下来


三、内容模型:不要做内容编辑器

这个站的内容 95% 是生成的,不是编辑出来的

交叉表(git)+ 资源清单(Blob)

        │ 边缘插件的 render()

   角色页 / 剧情页 / 卡牌页   ← 2,400+ 个,没有一个是人写的

真正需要人编辑的只有四类,全都是配置而不是内容:

  1. 站点设置(站名、导航、SEO 模板)
  2. 插件开关
  3. 个别页面的 SEO 覆盖(overrides['/character/1001'] = { title, description }
  4. 下架清单

所以 SiteConfig 里没有 posts / pages 这类表。内容是管道产物,配置才是内容管理。 这省掉了 CMS 里最重最容易做歪的一块(编辑器、草稿、版本、发布流)。

要发公告或写「关于」页怎么办?——那是一个插件pages 插件, 内容放 git 的 Markdown,构建时进 Blob),不是往核心里加一张表。


四、缓存失效才是真正的难点

边缘 CMS 的坑不在渲染,在改了东西之后旧页面还在

本包的做法:每个渲染结果带 cacheTags

ts
cacheTags: ['a:scenario/310241', 'rev:7']
  • a:scenario/310241 —— 内容标签。这篇剧情更新/下架时按此 purge。
  • rev:7 —— 配置版本号,每次后台保存自增。改了任何设置, 所有旧页面的标签集就整体失效,不必逐页记住谁受影响。

第二条是关键。没有它,「改了标题模板,为什么只有一半页面变了」会成为常驻问题。


五、SEO 的具体做法

要素由谁产出
<title> / descriptionresolveMeta():页面标题套 titleTemplate,覆盖项优先
canonicalcanonicalOrigin + 路径,防止同内容多路径
robots三层叠加,见下
JSON-LD插件在 meta.jsonld 里给,</ 已转义防脚本提前闭合
sitemap.xmlsite.sitemap():枚举开着的插件 × 可索引 × 未下架
robots.txtsite.robots():全站关索引时不再递 sitemap

索引默认是 selective,不是 all

ts
indexing: 'all' | 'none' | 'selective'   // 默认 selective

默认只索引显式登记的路径前缀。反过来(默认全收)意味着任何人加一条路由 都会顺带把它推给搜索引擎,而没人会在加路由时想到这件事

'none' 是总闸:收到通知时一键收缩整站可见面,页面级 index 也盖不过它。

一个必须由你决定的取舍

把 725 篇剧情正文做进索引,SEO 收益很大——但那是版权方的文本, 索引得越全,被发现得越快。这类公开站退役过不止一次。

方案默认的姿态是保守的(selective + 空前缀 = 什么都不索引), 把决定权留给你。要放开哪些前缀,是你的判断,不是我的。


六、后台

后台不是「一个面板」,是三件具体的事:

页面干什么后端
/admin/plugins插件开关、看每个插件提供什么能力、哪些 ref kind 没有提供者(缺口视图)KV
/admin/content下架开关、资源线路健康、清单版本Blob 强一致 + KV
/admin/seo索引模式、允许前缀、单页覆盖、sitemap 预览KV
/admin/analyticsPV/UV、热门页面KV 计数

「缺口视图」几乎免费:内核已有 plugins / providersFor / can, 列出哪些能力没人提供就是几十行。

鉴权:EdgeOne Functions + KV 会话。robots.txtDisallow: /admin/, 且后台路由一律 noindex——两道都要,因为 Disallow 只是请求,不是强制。


七、落地顺序

做什么前提
1一个 Pages Functions 入口 + Site 接上 KV 配置,跑通 /character/:id 的 SSR域名
2sitemap.xml / robots.txt / cacheTags purge步 1
3/admin/plugins + 鉴权,验证「一个开关两边都生效」步 1
4下架链路(Blob 强一致 + purge),在放开索引之前做完步 2
5资源搬迁到 Blob/COS,插件真正接上独立于 1–4
6PV 统计与热门页步 1

步 4 必须在放开索引之前。 先把内容推给搜索引擎、再去建下架能力, 顺序反了就是给自己挖坑。


待验证项

  1. Pages Functions 的 CPU 时间 / 内存 / 请求体上限(决定 SSR 能做多重)。
  2. Blob 强一致读的延迟与配额(下架链路的关键路径)。
  3. EdgeOne 的缓存 purge API 是否支持按 tag,还是只能按 URL / 前缀。 只能按 URL 的话,第四节的 rev: 标签方案要改成版本化路径。
  4. ICP 备案(国内加速必须)。

GPLv3。素材版权归各自的版权方所有,本站与本仓库不含任何素材。