ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

EmDash 插件内容 API 全指南:schema、翻译、发布策略与恢复操作的权限边界

EmDash 插件内容 API 全指南:schema、翻译、发布策略与恢复操作的权限边界 CMS后端前端插件系统【免费下载链接】emdashEmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress项目地址https://gitcode.com/gh_mirrors/emdas/emdash点击查看免费下载EmDash基于 Astro 的全栈 TypeScript CMS为插件提供了一整套按能力capability门控的内容 API覆盖 schema 读取、内容读写、翻译管理、发布策略钩子与回收站恢复。本文以 creating-plugins 技能参考文档 为主线结合 核心实现、发布策略实现 与 能力词表定义 展开帮助你理解每个能力边界、调用形态、稳定错误语义以及如何在真实项目中安全地读写与发布内容。能力门控总览同一套 API三种执行形态插件内容 API 是能力门控capability-gated的并且由原生插件、Cloudflare Worker Loader 与 Node/workerd 三种执行形态共享。这意味着无论你的插件以何种方式被宿主加载面对的都是同一套ctx.schema、ctx.content语义差异只体现在沙箱边界与传输层而不是 API 形状上。在 SandboxedPlugin 类型 中插件需要在emdash-plugin.jsonc里声明所需能力。与内容直接相关的能力及其授予范围如下完整能力表见 SKILL.md能力授予范围schema:read公开的集合collection与字段field定义content:read内容身份、翻译与已发布公开 URLcontent:revisions:read保留的修订数据隐含内容读取content:write创建、更新、删除与翻译创建隐含读取content:publish修订围栏revision-fenced的发布、取消发布、调度与取消调度隐含读取content:restore回收站内容的修订围栏读取与恢复hooks.content-policy:register发布前、调度前、取消发布前策略钩子值得强调的设计原则能力写在emdash-plugin.jsonc而不是src/plugin.ts。宿主管道会跳过缺少所需能力的钩子与 API 调用参见 hooks.md因此声明与实现必须保持一致这也是 插件发布校验 中declared capabilities and allowed hosts that match the plugin implementation的来源。发现与读取schema:read 与 content:read集合与字段定义schema:read暴露两个批量方法ctx.schema.listCollections()用于获取全部公开集合ctx.schema.getCollection()用于获取单个集合的字段定义。它们在宿主侧由SchemaRegistry提供支持参见 content-access.ts 中对new SchemaRegistry(db).getCollection(collection)的使用因此插件读取到的定义与站点后台管理界面看到的 schema 是同一份数据。内容项的数据形状schema:read之外读取内容本体需要content:read它暴露ctx.content.get(collection, id)按 id 获取单条内容ctx.content.list(collection, options?)分页列表支持limit默认 50、cursor游标、where过滤与orderBy排序ctx.content.getTranslations(collection, id)获取翻译组信息ctx.content.getPublicUrl(collection, id)解析公开可路由 URL。返回的内容项ContentItem结构可以从 createContentAccess 的实现中完整看到包含以下字段字段含义id内容项唯一 idtype/slug内容类型与 slugstatus状态如 draft / publisheddata结构化字段数据createdAt/updatedAt时间戳locale语言区域publishedAt/scheduledAt发布时间 / 计划时间authorId作者 idtranslationGroup翻译组标识liveRevisionId/draftRevisionId线上版与草稿版修订指针version行版本号乐观并发用如果集合启用了 SEO 模块结果中还会附带seo字段。公开 URL 只解析已发布内容getPublicUrl是一个需要特别注意安全语义的方法。查看 实现它要求item.status published、slug存在、且集合是可路由routable的然后按站点的urlPattern、trailingSlash与 locale 规则解析出完整 URL。也就是说公开 URL 解析永远只返回已发布的、可路由的 URL绝不返回预览。插件在生成外链、sitemap 或社交分享链接时应依赖这个方法而不是自己拼 URL。修订读取content:revisions:readcontent:revisions:read额外暴露listRevisions()与getRevision()用于读取修订历史。从 实现 可以看到一个隐私细节修订快照可以保留后来被删除的字段值但返回时通过解构去掉了authorId——修订数据不携带修订者身份。如果你的插件需要审计谁改的需要另行借助users:read或其他来源而不能依赖修订记录。写入与翻译content:writecontent:write在读取能力之上增加创建、更新与删除。创建一条翻译是最典型的用法参考文档给出的调用形态await ctx.content!.create(posts, data, { locale: fr, translationOf: sourceId });这条调用的语义约束非常明确源必须是同一集合中的活动条目active entry不存在的源会触发NOT_FOUND新行会加入源所在的翻译组并继承不可翻译字段、署名byline与分类taxonomy指派校验与保存钩子validation 与 save hooks都会运行且创建者插件会收到可重入围栏re-entrancy fencing保护避免同一插件在钩子内再次进入产生死循环一个翻译组每个 locale 只允许一条活动行重复 locale 会被拒绝。从 hooks.md 可知保存钩子content:beforeSave/content:afterSave分别需要content:write与content:read能力beforeSave可以返回修改后的内容或在沙箱中返回{ __emdashSandboxHookResult: true, version: 1, error: { code: SAVE_REJECTED, reason } }拒绝保存reason 必须为 1–500 字符的纯文本宿主进程则抛出ContentSaveRejectedError见 save-rejection.ts 相关实现。稳定的失败语义写入路径上插件可以依赖以下稳定错误码CONFLICT——并发冲突通常与行版本version不匹配相关NOT_FOUND——目标内容或源不存在VALIDATION_ERROR——字段校验失败SAVE_REJECTED——被钩子显式拒绝。插件应按这些错误码编写重试与用户提示逻辑而不是依赖不稳定的错误消息文本。发布策略钩子不授予任何读写权也能拦截发布hooks.content-policy:register是一个零读写权限的拦截能力它使插件能够注册content:beforePublish、content:beforeSchedule与content:beforeUnpublish但本身不授予任何内容读取、写入或发布操作。这实现了关注点分离——审查/审批类插件可以只做策略判断拿不到内容数据。决策形态与校验钩子返回void表示放行返回{ cancel: true, reason }表示拒绝。拒绝语义由 content-policy.ts 中的inspectContentPolicyDecision严格校验reason必须是非空字符串长度不超过500 个字符按码点计数不允许包含控制字符tab、换行、回车除外决策对象必须恰好包含cancel与reason两个键非法决策或意外中止错误会让整个动作以通用失败告终显式取消分别返回PUBLISH_REJECTED、SCHEDULE_REJECTED或UNPUBLISH_REJECTED。一个基于字段审批状态的示例源自 hooks.mdcontent:beforePublish: async (event) { const data event.content.data; const approvalStatus typeof data object data ! null approval_status in data ? data.approval_status : undefined; if (approvalStatus ! approved) { return { cancel: true, reason: Approve this entry before publishing. }; } },事件来源与调度拒绝策略钩子的事件携带{ content, collection, origin, actor? }其中content:beforeSchedule还包含scheduledAt。origin标识动作来源api、mcp、visual-editor人类操作同时携带相同的actor.sourcevisual-editor 要求来自已认证工具栏渲染的签名短期令牌、plugin附pluginId、scheduler与system。这意味着策略插件可以针对不同来源差异化放行——例如只允许管理员在管理界面发布拒绝 API 令牌直接发布。调度行为有一个容易忽略的细节到点执行的调度发布会再次运行发布策略。若调度器执行content:beforePublish时被拒绝条目会被取消调度unschedule拒绝原因被记录在案管理后台会展示该原因直到条目被重新调度、发布、删除或记录被消除。从源码看这个记录使用前缀emdash:scheduled-policy-rejection:的存储键见 content-policy.ts并携带collection、id、pluginId、reason、rejectedAt等字段。同时因为没有content:beforeUnschedule钩子管理员永远可以取消一次未来发布不会被策略插件锁死。发布与恢复动作content:publish 与 content:restore修订围栏revision fencecontent:publish在读取能力之上增加getVersioned()读取带版本号的内容publish()/unpublish()发布 / 取消发布schedule()/unschedule()调度 / 取消调度。这里最关键的模式是先读后写 携带_rev每次变更前先用getVersioned()读取并把不透明的_rev传给每一次变更动作。_rev是乐观并发锁防止两个进程同时基于同一版本做发布决策导致状态错乱。发布成功的动作会返回下一个修订next revision并正常运行策略、同步synchronization、媒体使用media-usage、缓存失效cache-invalidation以及 after 钩子等完整行为链。恢复不隐含删除权content:restore是一个刻意最小化的能力它只增加getTrashedVersioned()与restore()用于读取并恢复回收站中的内容不授予普通内容读取也不授予永久删除。这保证了能恢复的插件不会因此拥有能读全部内容或能清空回收站的权限是权限最小化设计的典型例子。插件动作的标识与重入防护所有插件动作都会报告{ source: plugin, pluginId }。宿主对同一插件针对同一规范条目canonical entry的相同动作实施重入拒绝——即插件发起发布后若该插件在钩子中再次对同一条目发起发布会被拒绝。这配合前面提到的保存钩子可重入围栏构成对插件递归行为的双层防护发布侧的重入语义见 hooks.ts 中钩子系统的运行机制。运行时测试用 fixture 与 action 覆盖关键行为参考文档给出的测试方法论非常实用用运行时 fixture 建立初始状态用运行时 action 调用生产边界用 inspector 读取持久化状态。fixture 用于初始数据初始条目entries、翻译、署名、分类指派都不应通过触发钩子的方式创建而应直接建立状态避免污染被测行为action 用于发布路径发布、调度、取消发布等必须走真实的生产边界runtime actions因为它们涉及修订围栏、策略运行与缓存失效inspector 用于验证持久化状态读取最终落库状态断言行为副作用是否符合预期。当以下行为对你的插件有意义时务必编写对应测试过期修订stale revisions即_rev已失效的并发场景、策略拒绝policy rejection、重复 locale、重入re-entrancy、重启restart验证调度拒绝记录与状态在进程重启后仍正确以及缓存失效cache invalidation。测试宿主的选择可以参考 SKILL.mdcreatePluginTestHost()适合快速验证钩子、路由、清单、能力、KV、设置与存储传输而涉及真实内容动作、插件激活、媒体、评论、重定向、调度、重启、授权、CSRF、缓存、Block Kit 校验或已保存条目扩展时应使用createPluginRuntimeTestHost()。每次使用后都要 dispose 宿主。能力边界速查表你想做的事需要的能力关键 API / 钩子读集合与字段定义schema:readctx.schema.listCollections()/getCollection()读内容、翻译、公开 URLcontent:readctx.content.get()/list()/getTranslations()/getPublicUrl()读修订历史content:revisions:readlistRevisions()/getRevision()无修订者身份创建 / 更新 / 删除 / 建翻译content:writectx.content.create()/update()/delete()发布 / 取消发布 / 调度content:publishgetVersioned()publish()/unpublish()/schedule()/unschedule()必须传_rev恢复回收站内容content:restoregetTrashedVersioned()/restore()拦截发布 / 调度 / 取消发布hooks.content-policy:registercontent:beforePublish/beforeSchedule/beforeUnpublish返回{ cancel: true, reason }小结EmDash 插件内容 API 的核心设计可以总结为三句话能力即权限每个读、写、发布、恢复动作都有独立能力开关且能力声明在清单而非代码中修订围栏保一致_rev贯穿所有发布与恢复动作先读后写是唯一正确姿势策略与执行分离发布策略钩子不授予任何读写权却能在发布、调度、取消发布三条路径上执行审批并通过调度拒绝记录让管理员始终保有最终控制权。理解这三点你就能写出既安全又实用的内容类插件。赞分享CMS后端前端插件系统【免费下载链接】emdashEmDash is a full-stack TypeScript CMS based on Astro; the spiritual successor to WordPress项目地址https://gitcode.com/gh_mirrors/emdas/emdash点击查看免费下载相关推荐EmDash 插件内容 API 指南schema 读取、翻译创建、发布策略与版本化发布操作EmDash 插件内容 API 指南schema 读取、翻译创建、发布策略与版本化发布操作 EmDash 是一套基于 Astro 的全栈 TypeScriptCMS后端前端插件系统EmDash 插件内容 API 指南Schema、翻译、发布与恢复的完整实现EmDash 插件内容 API 指南Schema、翻译、发布与恢复的完整实现 EmDash 是一款基于 Astro 的全栈 TypeScript CMSWoCMS后端前端插件系统EmDash 插件内容 API 实战Schema 读取、多语言翻译、发布策略与版本化恢复EmDash 插件内容 API 实战Schema 读取、多语言翻译、发布策略与版本化恢复 EmDash全栈 TypeScript CMSAstro 生态CMS后端前端插件系统创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表