cdn-console · 多 CDN 统一控制台插件
本插件为安知鱼(Anheyu)内容管理平台提供统一的 CDN 运维能力:将多家 CDN 的缓存清除、缓存预热、指标查询封装为一套 HTTP API,并在站点内容变更时自动清除缓存。
维护者:白夜
插件类型:
eventhook(订阅站点事件)+ 自带 HTTP 服务(供前端消费)
1. 概述
站点若同时使用多家 CDN,日常运维(清缓存、预热、看流量/命中率)需要在各厂商控制台之间来回切换,且平台自带的清除能力覆盖有限。cdn-console 把这些操作收敛到一个控制台,并支持:
多实例分开配置:站点、图片等不同域名可分别归属不同 CDN 账号,各自保存凭证。
统一操作:一次提交可同时下发到多个已启用实例。
事件自动清除:文章创建/发布/更新/删除时,自动向启用自动清除的实例提交 URL/标签清除。
两种使用形态:配合前台主题的
/cdn-console页面使用;或开启serve_ui由插件自带一个独立控制台,无需任何前端。
2. 功能特性
分类 | 能力 |
|---|---|
缓存清除 | URL / 目录 / 首页(整站) / 标签,按厂商支持度暴露 |
缓存预热 | 提交 URL 预热,可附带请求头(套餐不支持则不提供) |
指标查询 | 流量 / 带宽 / 请求数 / 命中率 / 状态码分布 / 平均响应,含时序曲线与区间汇总 |
事件自动清除 | 订阅 |
实例管理 | 多厂商、多实例增删改查,凭证脱敏存储、连通性测试 |
安全防护 | 管理员鉴权、写/读/登录三类限流、单次目标数量上限 |
性能 | 指标结果内存缓存 + 并发合并(single-flight),可选 Redis 共享二级缓存 |
3. 支持的 CDN 厂商与能力矩阵
当前支持五家厂商,能力存在差异,插件以"能力矩阵"驱动前端控件显隐。
3.1 能力矩阵
能力 | 腾讯云 EdgeOne | 腾讯云 CDN | 阿里云 CDN | 阿里云 ESA | Cloudflare |
|---|---|---|---|---|---|
URL 清除 | ✅ | ✅ | ✅ | ✅ | ✅ |
目录清除 | ✅ | ✅ | ✅ | ✅ | — |
首页/整站清除 | ✅ | — | — | ✅ | ✅ |
标签(Cache-Tag)清除 | ✅ | — | — | ✅ | — |
通配符清除 | — | — | — | — | — |
预热 | ✅ | ✅ | ✅ | ✅ | —(标准套餐无预热 API) |
流量 | ✅ | ✅ | ✅ | ✅ | ✅ |
带宽 | ✅ | ✅ | ✅ | ✅ | ✅ |
请求数 | ✅ | ✅ | ✅ | ✅ | ✅ |
命中率 | ✅ | ✅ | ✅ | — | — |
状态码分布 | ✅ | ✅ | ✅ | — | ✅ |
平均响应 | ✅ | — | — | — | — |
3.2 厂商标识与所需凭证
厂商 |
| 凭证字段 | 其它必填 | 是否需要 | 是否需要 |
|---|---|---|---|---|---|
腾讯云 EdgeOne |
|
|
| 是(ZoneId) | 否 |
腾讯云 CDN |
|
|
| 否 | 是 |
阿里云 CDN |
|
| — | 否 | 是(取首个域名) |
阿里云 ESA |
|
| — | 是(SiteId) | 否 |
Cloudflare |
|
| — | 是(Zone ID) | 否 |
凭证字段属敏感数据:落盘文件权限为
0600,接口回显时脱敏为********,再次提交占位值将保留原密钥。
4. 工作原理
4.1 进程模型
插件是 Anheyu 主程序以 go-plugin 拉起的独立子进程,与主程序拥有同等系统权限。main() 先启动自带 HTTP 服务(默认监听 :9800),再通过 SDK 注册 eventhook 能力。
4.2 鉴权链路
所有业务 API 默认要求"管理员"身份,插件本身不持有 JWT 密钥:
PLAINTEXT
浏览器 ──(携带 Authorization: Bearer <JWT>)──► Next /cdn-api/* ──(透传)──► 插件 /api/*
│
插件把该 Bearer 转发到后端 admin_check_path
│
HTTP 200 → 管理员,放行;401 → 提示重新登录;其余 → 403
4.3 事件自动清除
订阅上述四类文章事件;命中后向所有 auto_purge=true 的实例提交清除(有 site_url 则拼绝对 URL,否则提交相对路径)。OnEvent 返回的错误仅被主程序记录日志,不影响文章发布等业务流程。
4.4 两种前端形态
主题页:由本仓库前台
/cdn-console页面经 Next 代理消费 API(需具备管理员登录态)。内置控制台:开启
serve_ui后,插件在其 HTTP 服务根路径托管一个自带登录的单文件控制台,可脱离主题独立使用。
内置控制台
5. 环境要求
Anheyu(社区版或 PRO)开启插件系统;建议 App 版本 ≥
1.0.0(详见plugin.json的min_app_version)。插件与后端在同一容器 / Docker 内网可达(用于回源管理员校验接口)。
可选:一个插件进程网络可达的 Redis 服务(启用共享缓存时所需)。
6. 安装
后台进入「插件管理」。
上传安装包
cdn-console.zip(内含plugin.json与各平台二进制)。安装成功后状态变为「运行中」。升级同 ID 插件会保留既有配置与禁用状态。
说明:插件走本地 zip 上传;这与 SSR 主题的
downloadUrl直装是两条不同的通道,请勿混淆。安装包内plugin.json必须位于压缩包根目录。
7. 快速开始
配置引导参数:至少填写
site_url(用于自动清除拼绝对 URL);backend_url/admin_check_path在同容器部署时可保持默认。新建 CDN 实例:选择厂商,填入凭证与
zone_id/domains,点击「测试连接」验证。选择前端入口:使用主题则访问
/cdn-console;或开启serve_ui直接访问插件服务。在控制台执行清除/预热,或依赖事件自动清除。
8. 配置参数参考
以下参数在 plugin.json 的 config_schema 中声明,由后台保存后以环境变量注入插件进程。
参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| number |
| 插件 HTTP 服务端口。仅内网可达,禁止公网暴露。 |
| string | 空 | 监听地址。空=所有网卡;同机部署可填 |
| string |
| 用于管理员校验的后端基地址。 |
| string |
| 一个"仅管理员可访问"的后端接口路径,返回 200 视为放行。 |
| string | 空 | 站点访问地址,自动清除时拼接绝对 URL。 |
| string | 空( | 实例配置与任务历史落盘目录。 |
| boolean |
| 是否启用事件自动清除。 |
| boolean |
| 是否由插件自带 HTTP 服务托管内置控制台页面。 |
| number |
| 每调用者每分钟清除/预热写操作上限(0=不限)。 |
| number |
| 每调用者每分钟指标读取上限(0=不限)。 |
| number |
| 内置控制台每来源 IP 每分钟登录尝试上限(0=不限)。 |
| number |
| 指标结果内存缓存秒数(0=不缓存,仍合并并发)。 |
| number |
| 管理员"通过"结果缓存秒数(0=不缓存)。 |
| boolean |
| 是否启用 Redis 共享二级缓存。 |
| string | 空 | Redis 地址 |
| string | 空 | Redis AUTH 密码(表单为密码框,不回显)。 |
| number |
| Redis 库编号,建议使用独立库。 |
| number |
| Redis 中指标缓存秒数(0=沿用 |
9. 使用指南
9.1 管理 CDN 实例
实例是操作的基本单位。字段含义:name(显示名)、provider(厂商)、enabled(是否参与"全部启用实例"的批量操作与自动清除)、zone_id、domains、auto_purge(是否参与事件自动清除)、purge_tag_prefix(Cache-Tag 前缀,默认 article-)及对应厂商的 credentials。
建议:为不同用途(如站点 HTML、图片资源)分别建立实例,便于按需选择清除目标。
9.2 清除缓存
选择清除类型并提交目标列表。清除类型取值:
| 含义 | 目标写法 |
|---|---|---|
| 按 URL 清除 | 每行一个资源 URL |
| 按目录清除 | 目录 URL(部分厂商为前缀匹配) |
| 首页/整站清除 | 无需目标或使用站点首页 |
| 按 Cache-Tag 清除 | 标签值(可配合 |
| 通配符清除 | 当前各家均不支持,控件不显示 |
未指定实例时,默认下发到全部已启用实例;单次目标数量上限为 500。
9.3 缓存预热
提交需要提前回源的 URL 列表,可为 Cloudflare 外的厂商附带请求头 headers。单次上限同为 500。
9.4 查看指标
选择实例、指标与时间范围查询。指标取值:traffic、bandwidth、requests、hit_rate、status_code、avg_response(不支持的组合不提供)。粒度 granularity 取 minute/hour/day。结果含时序曲线 series 与区间汇总 summary。
9.5 事件自动清除
开启 auto_purge_enabled 且实例 auto_purge=true 时,相应文章事件会触发自动清除。若需关闭某实例的自动清除,取消该实例的 auto_purge。
10. 前端接入
10.1 主题页 /cdn-console
前台通过环境变量 CDN_PLUGIN_URL 指向插件,next.config.ts 中的 rewrite 将 /cdn-api/:path* 反代到插件 /api/:path* 并透传 Authorization:
本地:
CDN_PLUGIN_URL=http://localhost:9800容器:
CDN_PLUGIN_URL=http://<后端容器名>:9800(与后端同容器时也可http://localhost:9800)
页面入口仅对管理员可见。
10.2 内置控制台(serve_ui)
将 serve_ui 置为 true 后,访问插件服务的根路径即可打开内置控制台。登录支持两种方式:填写站点账号密码(经 /api/login 代理到后端),或直接粘贴已有 Token。开启验证码的站点可能无法用密码登录,请改用粘贴 Token。
11. HTTP API 参考
经主题代理访问时前缀为
/cdn-api/...;直连插件时为/api/...。除GET /api/health与POST /api/login外,所有接口都需携带管理员Authorization: Bearer <JWT>。
统一响应封装(业务成功以 code=200 判定):
JSON
{ "code": 200, "message": "ok", "data": { } }
接口一览:
方法 | 路径 | 鉴权 | 说明 |
|---|---|---|---|
GET |
| 否 | 健康检查 |
POST |
| 否 | 内置控制台登录代理,透传后端 |
GET |
| 是 | 各厂商能力矩阵与表单字段元信息 |
GET |
| 是 | 实例列表(凭证脱敏) |
POST |
| 是 | 新增或更新实例(body 为 |
DELETE |
| 是 | 删除实例 |
POST |
| 是 | 测试该实例连通性 |
POST |
| 是 | 提交清除 |
POST |
| 是 | 提交预热 |
GET |
| 是 | 查询指标 |
GET |
| 是 | 清除/预热历史( |
提交清除 POST /api/purge:
JSON
{
"instance_ids": ["inst-1", "inst-2"],
"type": "url",
"targets": ["https://anheyu.com/post/hello"]
}
instance_ids 为空时下发到全部已启用实例。响应 data.results 为逐实例回执 {provider, instance_id, task_id, accepted, message}。
查询指标 GET /api/metrics:
查询参数 | 必填 | 默认 | 说明 |
|---|---|---|---|
| 是 | — | 目标实例 |
| 否 |
| 指标类型,见 §9.4 |
| 否 |
|
|
| 否 | 24 小时前 | Unix 毫秒时间戳 |
| 否 | 当前 | Unix 毫秒时间戳 |
响应 data 结构:
JSON
{
"supported": true,
"summary": { "traffic_bytes": 123, "requests": 456, "hit_rate": 0.92 },
"series": [ { "timestamp": 1700000000, "value": 123 } ],
"status_codes": [ { "label": "2xx", "value": 100 } ],
"message": ""
}
summary 中缺失的字段表示该厂商不支持对应指标;supported=false 时 message 说明原因。
12. 安全说明
谁能触发清除/预热:默认仅管理员。插件将调用者 Bearer 透传给
admin_check_path,返回 200 才放行,否则 401/403;匿名访客经/cdn-api也无法获得权限。不要把插件端口(默认 9800)发布到公网。它应只在 Docker 内网/本机可达;外部访问统一走 Next 的
/cdn-api代理。同机部署可将listen_host设为127.0.0.1进一步收敛。限流:写操作(
rate_limit_per_min)、指标读取(read_limit_per_min)、内置控制台登录(login_limit_per_min)分别按调用者或来源 IP 限流,取X-Forwarded-For→X-Real-Ip→RemoteAddr判定来源。目标上限:单次清除/预热目标数不超过 500,避免放大对厂商接口的调用。
13. 指标缓存与 Redis
指标查询按 (实例/指标/量化到 minute|hour|day 时间窗) 组织两级缓存:
L1 内存(默认启用):
metrics_cache_ttl秒内复用结果;并发相同请求合并为一次上游调用(防击穿);失败/不支持结果用更短 TTL(≤15s)负缓存。指标变化缓慢,通常 L1 已足以挡住重复读取。L2 Redis(
redis_enabled=true时启用):作为跨进程/多副本的共享缓存。链路为「内存未命中 → 查 Redis → 命中则回填 → 否则回源并写回 Redis」,且仅写"成功"结果。
Redis 连接要点:插件为独立进程,无法复用主程序内存中的 Redis 客户端对象,而是按
redis_addr自行拨号连接同一 Redis 服务,需确保插件进程网络可达该地址。Redis 客户端基于 Go 标准库实现(极简 RESP2:GET/SET ... EX/AUTH/SELECT/PING),不引入第三方依赖;Redis 不可达或出错时自动降级为纯内存缓存,不影响可用性。
14. 故障排查
现象 | 可能原因 | 处理 |
|---|---|---|
后台/前台看不到入口 | 非管理员登录态 | 以管理员账号登录后再访问 |
测试连接报鉴权错误 | 密钥错误 / 缺 | 核对凭证;EdgeOne/ESA/Cloudflare 需 Zone/Site ID,腾讯/阿里 CDN 需已接入域名 |
指标一片空白 | 厂商不支持该指标(见能力矩阵)或字段口径/套餐差异 | 换受支持指标;确认为空时按"无数据/需核对"理解 |
启用了 Redis 却未共享 | 地址不可达 / 密码或库号错误 | 检查插件进程到 Redis 的网络;查看插件日志(输出于 stderr) |
清除提示单次目标过多 | 超过 500 | 分批提交 |
插件端口对外可达导致风险 | 误将 9800 公网映射 | 关闭公网发布,仅经 Next |
15. 常见问题
Q:不用主题也能管理吗? A:能。开启 serve_ui=true,直接访问插件服务根路径的内置控制台并登录即可。
Q:插件能连你们后端已在用的 Redis 吗? A:能连同一个 Redis 服务,但需填 redis_addr(如 redis:6379)且保证插件进程可达;不能复用后端进程内的客户端对象。
Q:自动清除会影响发布流程吗? A:不会。事件钩子内的错误只记录日志,不阻断文章创建/发布。
Q:某厂商指标口径与文档不符怎么办? A:各厂商 API 字段以官方文档为准,解析失败会降级为"该区间暂无可用指标数据",不影响清除/预热主功能。
16. 限制与已知事项
平均响应仅 EdgeOne 提供;Cloudflare 不支持命中率与预热;阿里云 ESA 暂不提供命中率/状态码/平均响应。
通配符清除当前五家均未开放,故不提供该控件。
指标数值以厂商返回为准,个别厂商为近似聚合值。
17. 附录:取值枚举
provider:edgeone/tencent_cdn/aliyun/aliyun_esa/cloudflare清除
type:url/directory/homepage/tag(wildcard暂不支持)metric:traffic/bandwidth/requests/hit_rate/status_code/avg_responsegranularity:minute/hour/day能力矩阵 JSON 键:
purge_url、purge_directory、purge_homepage、purge_wildcard、purge_tag、preheat、metrics_traffic、metrics_bandwidth、metrics_requests、metrics_hit_rate、metrics_status_code、metrics_avg_response