管理后台的「接口」页把三件原本只能改代码的事拆开,按风险分级:
| 能力 | 谁用 | 改什么 | 怎么生效 | 风险 |
|---|---|---|---|---|
| 文案编辑 | 运营 / 管理员 | 标题、描述、参数说明、示例、返回字段说明 | 保存即生效,不重启 | 低:结构字段只读 |
| 新增声明式接口 | 运营 / 管理员 | 取值 → 请求上游 → 字段映射的 JSON 配置 | 草稿 → 发布,立即注册路由 | 中:只能调上游和映射,不执行代码 |
| 核心接口实现 | 研发 | src/apis/** 里的 JS |
提交 + 更新/重启 | 高:仍走代码审查 |
- 代码是结构的唯一权威。 文案覆盖层(
api_meta)没有修改参数名、类型、必填、枚举的入口,合并函数只写title / description / summary / desc / example。覆盖前后的shapeHash必须完全相等,test/api-meta.test.js锁住这一点。 - 线上改动要能回滚。 声明式接口每次发布都写一条
custom_api_versions,回滚只切换线上指针,不依赖 git;删除会同时删除发布记录,但会留下操作审计。
- 一行覆盖 = 一个补丁;删掉补丁即恢复代码默认值,不会出现"字段没文档"。
- 每条补丁记录自己的结构指纹(
targetRev)。参数约束或代码结构变了、内容无效时,覆盖立即停止生效,目录恢复代码默认文案;体检标为「待确认」,管理员确认保存后才重新生效。 - 合并时同时检查目标、结构指纹和内容合法性决定是否生效:代码里已经没有的字段,补丁自动失效并进入「孤儿」清单,可一键清理。
- 并发:详情和列表返回持久的
editRev,每次写入必须提交expectedRev;旧页面、其他标签或进程已修改内容时返回409并保留当前输入,不静默覆盖。所有写入与审计在同一事务,文案按目标记录前后值;版本跨重启保留。
体检分类(GET /admin/apis/doctor):
| kind | 含义 | 处理 |
|---|---|---|
orphan |
代码里已没有这个字段 | 还原或清理补丁 |
stale |
保存后代码结构变过 | 确认文案是否仍然准确 |
invalid |
超长、必填为空、含控制字符 | 改短或还原 |
module-missing |
整个接口已被删除 | 清理补丁 |
invalid-target |
目标格式不合法(旧版本遗留) | 清理补丁 |
配置存在 custom_apis(草稿 draft 与线上 config 分开),启动和发布时由 registry 合并进路由表,不写入代码目录,因此在线更新不会冲掉。
最小配置:
{
"name": "custom-example",
"category": "tools",
"title": "示例接口",
"description": "从后台新增的接口",
"sourceInfo": { "kind": "third-party-service", "network": "required", "summary": "示例数据源" },
"routes": [{
"method": "GET",
"path": "/api/custom/example",
"summary": "按关键词查询",
"params": [{ "name": "q", "required": true, "desc": "关键词", "example": "hello", "maxLength": 40 }],
"url": "https://api.example.com/search?q={q}",
"extract": "data",
"map": { "title": "name", "link": "url" },
"fields": [
{ "name": "title", "type": "string", "desc": "标题" },
{ "name": "link", "type": "string", "desc": "链接" }
],
"cacheTtlSec": 300,
"timeoutMs": 10000
}]
}规则(发布前全部校验):
name3~40 位小写字母/数字/-,不能和核心接口重名;path以/api/开头,不能带:。url里用{参数名}取值(自动 URL 编码),{env:CUSTOM_API_变量名}仅引用显式声明的专用环境变量;不能读取 SMTP、会话或其他系统密钥;必填参数必须给example(自动检测会用它)。- 上游域名和端口必须固定;仅直连公网,DNS 在连接时校验所有解析地址,不跟随任何跳转,不经过系统出站代理。使用专用环境变量的路由必须是 HTTPS。变量被上游反射回传时会隐藏,但仍需信任所配置的上游。
- 支持
GET/POST;POST可以给body(对象或字符串,值同样支持占位符)。 extract从上游响应里取路径(data.items、data.items[0]);取不到时返回 502「上游返回结构可能已变化」,不会静默返回 null。map把上游字段映射成返回字段;每个输出字段都必须在fields里写说明。cacheTtlSec086400 秒,0 表示实时获取;POST 必须为0。20000,覆盖连接和正文;正文最多1 MiB,最多10000个取值节点。timeoutMs500- 参数支持 string/int、oneOf、长度或数值边界;POST 的
in: "body"参数从传入 JSON 正文读取。暂不支持自定义正则;禁止__proto__、constructor、prototype字段和继承路径。 - 发布时检查与核心接口(含动态路由)、其他自定义接口的路径冲突;冲突返回 409,不会出现两条路由抢同一个地址。
- 在「新建接口」或接口详情的「接口定义」里改 JSON。
- 「校验」只做结构与冲突检查,不落库。
- 「保存草稿」写
draft,线上不变。 - 「发布」要求填写原因(写入操作日志),采用当前编辑器内容;事务内完成保存、验证和 registry 预构建,任一步失败均保留原线上版本,成功后:
- 版本号 +1,写入
custom_api_versions; - registry 重建,新接口立刻出现在
/api目录、文档页、sitemap、OpenAPI、Key 权限下拉和统计里; - 清掉该模块的缓存。
- 版本号 +1,写入
- 「回滚到此版」把线上指针切到指定版本;「删除」同时删除发布记录(审计日志保留)。
GET /openapi.json:OpenAPI 3.1,按运行时目录生成,含参数、枚举、示例、x-miao.fields字段说明。GET /llms.txt:纯文本接口清单,供 LLM/Agent 直接阅读。GET /api:原有目录 JSON,新增模块带origin: "custom"和custom: true。
以上都是运行时生成,后台改文案或新增接口后不需要另写文档;契约响应使用 no-store,避免发布后浏览器继续显示旧目录。
本地纯计算可以直接试跑;外部上游、POST 和可能写入本地状态的处理须填写原因并确认,操作留审计。试跑不计调用额度,但可能消耗真实上游配额或产生副作用;不会自动重试。自定义上游始终标记为联网,不参加自动巡检或缓存预热,避免 GET/POST 被后台任务重复调用。已关闭、合规停用或暂停的模块不能绕过状态试跑。
存在已保存草稿时试跑草稿,尚未保存的编辑内容须先保存。试跑绕过线上缓存;缓存按配置、发布代次及专用变量区分,旧请求可以结束但不会污染新版本。
数据库保存、审计和 registry 预构建在同一同步事务;提交后切换快照。其他服务进程在下一次 HTTP 请求前发现版本变化并重建,在途请求继续用旧 route。SQLite 数据必须位于该部署保留的 data/ 目录;不支持跨机器共享内存快照。
npm run api:check # 路径/命名唯一、来源登记、README 数量、接口清单快照
npm run api:sync # 同步 README 数量和 docs/api-surface.json
npm testtest/api-contract.test.js 和 api:check 共用同一份规则(src/lib/api-contract.js),所以 CI 现有的 node --test 就会拦住过期快照与非法路径,不依赖额外的 workflow 步骤。
新增核心接口时:
- 写
src/apis/<分类>/<name>.js(约定见src/apis/README.md); - 在分类
index.js里导出; src/lib/api-sources.js登记数据来源;npm run api:sync更新数量与快照;- 补测试与 fixture。
docs/api-surface.json 是接口清单快照:任何改名、删除或新增都会在 diff 里显示,避免"数量悄悄变了"。
- 本版本不提供在线写任意 JS。声明式配置覆盖"调上游 + 映射字段"这一类需求;需要写解析逻辑的接口仍走代码,理由是可审查、可测试、可回滚。
- 角色分层(
editor只能改文案、admin才能发布代码)是下一步,目前工作台只对管理员开放。 - README 的逐行接口表格仍手写;下一步可以由
docs/api-surface.json生成,减少一次手改。
| 文件 | 作用 |
|---|---|
src/lib/api-meta.js |
文案补丁:合并、校验、体检、结构指纹 |
src/lib/custom-api.js |
声明式接口:配置校验、运行、草稿/发布/回滚 |
src/registry.js |
可重建的运行时快照(核心 + 文案 + 自定义) |
src/routes/api-admin.js |
工作台后台接口 |
src/lib/openapi.js |
OpenAPI / llms.txt 生成 |
public/admin-apis.js |
工作台页面 |
scripts/api-check.mjs |
契约自检与快照同步 |
从管理概览 /admin 打开「接口开关与展示」,每张卡片的「公开显示」开关可以立即隐藏或恢复该接口模块(同一模块的多个地址一并处理);上方可按显示/隐藏筛选。接口工作台 /admin/apis 同样保留所有项,详情里可隐藏和恢复,自定义草稿也可预先设置。
隐藏和禁用相互独立:隐藏只移除公开目录、搜索、分类数量、单接口文档、站点地图、OpenAPI/llms、今日推荐和公开状态列表,已有调用地址仍按原来的身份、Key 范围和配额工作。隐藏不能用来保密或阻止调用;停止服务应关闭接口。恢复显示也不会开启已关闭的接口。代码和历史内容中的地址不会被删除。
状态保存在 SQLite module_settings.hidden,旧数据默认为0(显示),刷新及重启保留。保存沿用 expectedRev 和管理员同源 JSON 写入保护,写入与审计原子提交;409或网络失败会保留原开关并提示重新读取后再操作。不重建调用路由或清空当前进程已有调用缓存。自定义接口重新发布/回滚保留隐藏状态;删除后重新创建默认显示。
公开页面导航和快速搜索会重新获取目录;已打开页面仍可能保留之前加载的内容,刷新或再次导航即可更新。隐藏不删除历史统计,平台历史总调用量仍保留。仅编辑已有 API Key 的名称或来源时不重写接口范围,不会因隐藏自动删除或扩大权限;主动调整范围仍按新的勾选结果保存。