ARTICLE DETAIL

资讯详情

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

Activepieces 架构决策 000007:Piece Set 可见性在读取时推导(Read-Time Visibility Derivation)

Activepieces 架构决策 000007:Piece Set 可见性在读取时推导(Read-Time Visibility Derivation) Activepieces 架构决策 000007Piece Set 可见性在读取时推导Read-Time Visibility Derivation【免费下载链接】activepiecesAI Agents MCPs AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces导读本文讲解 Activepieces 中一个关键的平台级架构决策Piece Set组件集合的可见性不再在安装时写入而是在列出组件时按需推导。该设计用一份显式允许清单allow-list替代了旧的禁用清单deny-list从根上消除了onPieceCreated安装时扇出写放大问题并使新增组件默认隐藏成为零成本语义。读完本文你将掌握isPieceVisible/isComponentVisible两个纯函数的判定逻辑、mode exceptions的配置模型、服务端过滤层与 Web 前端共享同一套判定逻辑的实现方式以及整套 Piece Set REST API 的用法。该决策文档位于 brain/knowledge/decisions/000007-piece-set-visibility-is-derived-at-read-time.md正文中引用的所有源码路径均可在当前仓库中直接查阅。一、决策概述可见性在读取时推导1.1 核心决策决策一个 Piece Set 只存储包含/排除选择一份可见的允许清单可见性在组件被列出list时计算得出——绝不在组件安装时写入。两个纯函数解析器isPieceVisible/isComponentVisible由服务端过滤层与 Web 前端 UI 共享。这意味着服务端与前端使用同一份判定代码不会出现两端口径不一致数据库里没有任何安装时刷进去的可见性快照可见性是PieceSet.config的派生结果derived不是独立状态。1.2 两个纯函数的定义两个解析器定义在 packages/core/shared/src/lib/ee/piece-set/index.tsexport function isPieceVisible({ pieces, name }: { pieces: PieceSelection, name: string }): boolean { const listed pieces.exceptions.includes(name) return pieces.mode PieceSelectionMode.INCLUDE_ALL ? !listed : listed } export function isComponentVisible({ selected, name }: { selected: string[] | undefined, name: string }): boolean { if (isNil(selected)) { return true } return selected.includes(name) }判定规则非常朴素解析器输入规则语义isPieceVisiblePieceSelectionmode exceptions 组件名INCLUDE_ALL模式下不在 exceptions 中即可见EXCLUDE_ALL模式下在 exceptions 中才可见决定整个组件是否对项目可见isComponentVisibleselected: string[] \| undefined 操作/触发器名selected为undefined未配置时全部可见否则只允许名单内成员决定组件内部具体动作/触发器是否可见之所以拆成两个函数而不是一个统一签名正是决策文档 Consequences 中强调的两种表示是有意为之组件piece携带mode真正的集合级自动包含策略而组件内部的动作/触发器component是纯粹的允许清单——key 存在即表示经过人工筛选curated。二、背景旧模型 deny-list 的三个痛点2.1 旧模型是什么旧模型是一份禁用清单disabledPieces 策略标志policy flags。平台管理员显式列出不允许使用的组件未列入的组件默认可见。2.2 致命缺陷无法表达隐藏尚未存在的东西deny-list 是声明式的但它是反方向的声明——它只能回答哪些东西不允许无法回答哪些东西允许。于是产生一个结构性矛盾一个新组件在元数据层面被创建出来时它天然不在 deny-list 里因此默认对所有人可见除非有人主动把它写进禁用清单。2.3 安装时扇出write-on-install fan-out为了弥补上述矛盾旧模型挂了一个onPieceCreated钩子每当任何组件元数据被创建就遍历该平台下所有 Piece Set把新组件物化materialize进每份 deny-list。这个钩子的触发面非常广每次元数据创建都会触发包括每小时执行的跨平台PIECES_SYNC定时任务该任务定义见 packages/server/api/src/app/helper/system-jobs/common.ts任务名为pieces-sync每份 deny-list 都会无界增长——被同步进来的组件越来越多即使管理员从不使用它们每次同步都是一次 O(平台内 Piece Set 总数 × 新增组件数) 的写放大。用决策文档的原话概括deny-list 无法声明式地表达隐藏还不存在的东西于是只能靠每次创建都去写一遍来兜底而这个兜底路径本身就是问题来源。三、为什么选择 allow-list 读取时推导3.1 核心洞察新 不在列表 隐藏 自动成立存储可见允许清单后语义完全反转INCLUDE_ALL模式默认exceptions 是排除名单与旧 deny-list 行为等价但默认值翻转新组件默认可见需显式排除EXCLUDE_ALL模式exceptions 是包含名单新组件天然不在名单里因此天然隐藏——这正是 deny-list 无法表达的场景组件内部动作/触发器使用selectedActions/selectedTriggers允许清单key 存在表示人工筛选过未配置表示全部放行。新组件出现时不需要写任何一行数据隐藏就自动生效。整个 install-time 写扇出被删除。3.2 被否决的两个替代方案决策文档明确记录了被否决的选项理解它们有助于把握边界重构 deny-list即使把disabledPieces改成更规整的结构仍保留安装时扇出与无界增长两个根本问题只是换了个形式在所有作用域统一{mode, exceptions}模型组件内部的动作/触发器是严格二元的全选 vs 选定引入 mode 字段对它们而言是死重dead weight——所以最终只在 piece 层级保留mode组件层级用裸 allow-list。这个两种表示各取所需的取舍正是本文标题中derived at read time设计能保持精简的原因。3.3 配置模型源码佐证配置结构的 Zod schema 同样定义在 packages/core/shared/src/lib/ee/piece-set/index.tsexport enum PieceSelectionMode { INCLUDE_ALL include_all, EXCLUDE_ALL exclude_all, } export const PieceSelection z.object({ mode: z.enum([PieceSelectionMode.INCLUDE_ALL, PieceSelectionMode.EXCLUDE_ALL]).default(PieceSelectionMode.INCLUDE_ALL), exceptions: z.array(z.string()).default([]), }) export const PieceSetConfig z.object({ pieces: PieceSelection.default({ mode: PieceSelectionMode.INCLUDE_ALL, exceptions: [] }), selectedActions: z.record(z.string(), z.array(z.string())).default({}), selectedTriggers: z.record(z.string(), z.array(z.string())).default({}), })默认配置emptyConfig为INCLUDE_ALL 空 exceptions即全部组件可见、无任何内部筛选见 piece-set-config.ts。四、服务端如何在读取时应用可见性4.1 读取路径的入口resolveVisibility可见性策略在组件被列出的热路径上解析。核心实现在 packages/server/api/src/app/ee/pieces/filters/piece-filtering-utils.tsexport async function resolveVisibility({ platformId, projectId, log }): PromiseVisibilityPolicy | null { const edition system.getEdition() if (![ApEdition.ENTERPRISE, ApEdition.CLOUD].includes(edition)) { return null } if (isNil(platformId) || isNil(projectId)) { return null } const pieceSet await resolvePieceSetForProject({ log, projectId, platformId }) return buildPolicy(pieceSet.config) }几个关键点版本门槛仅在 Enterprise 与 Cloud 版本启用CE 或缺少 platform/project 上下文时返回null不过滤集合解析先查项目绑定的pieceSetId没有则回落到平台默认 Piece SetgetOrCreateDefaultPieceSet见 piece-set.service.ts通过 Redis 分布式锁保证并发下只创建一个默认集只读整个过程只读PieceSet.config不写任何数据。4.2 过滤策略的三种形态buildPolicypiece-filtering-utils.ts基于同一个isPieceVisible闭包构造出VisibilityPolicy提供三类过滤能力方法作用对象说明filterPieces完整组件元数据列表按组件名过滤只保留可见组件filterComponents组件摘要含 suggestedActions / suggestedTriggers在组件摘要层级过滤建议动作/触发器filterPieceComponents单个组件的详细模型在组件详情层级过滤actions/triggers对象其中组件内部过滤通过isComponentVisible({ selected: config.selectedActions[piece.name], name })完成——selectedActions/selectedTriggers以组件名为 key值为该组件允许暴露的动作/触发器名单。4.3 调用位置组件元数据服务resolveVisibility被组件元数据服务在列出/获取组件时调用见 packages/server/api/src/app/pieces/metadata/piece-metadata-service.ts。这印证了可见性只在读路径生效元数据查询的结果在返回给调用方之前先经过VisibilityPolicy过滤而元数据的写入与同步路径完全不感知可见性。五、Piece Set 的存储与更新语义5.1 实体结构Piece Set 实体包含platformId、name、key、isDefault、generatedForProjectId与configschema 见 packages/core/shared/src/lib/ee/piece-set/index.ts。isDefault标记平台默认集合——删除非默认集合时其下绑定的项目会被事务性地改绑回默认集合见 piece-set.service.ts默认集合本身不可删除。5.2 声明式更新 API决策文档强调更新 API 是声明式的全量替换 按组件的意图。UpdatePieceSetRequestBody定义在 packages/core/shared/src/lib/ee/piece-set/index.tsexport const ComponentIntent z.discriminatedUnion(mode, [ z.object({ mode: z.literal(all) }), z.object({ mode: z.literal(selected), selected: z.array(z.string()) }), ]) export const UpdatePieceSetRequestBody z.object({ name: z.string().min(1).optional(), key: z.string().optional(), pieces: PieceSelection.optional(), // 全量替换 piece 选择 actions: z.record(z.string(), ComponentIntent).optional(), // 按组件表达动作意图 triggers: z.record(z.string(), ComponentIntent).optional(), // 按组件表达触发器意图 })ComponentIntent是按组件粒度的意图表达{ mode: all }该组件的动作/触发器全部放开——应用时从允许名单中删除该组件 key等价于放弃人工筛选{ mode: selected, selected: [...] }该组件只暴露名单内的成员——应用时写入去重后的名单。applyUpdate与applyComponentIntents的具体合并逻辑见 piece-set-config.tsintents为undefined的字段保持原值部分更新mode: all会移除该组件的 key退化为全部可见mode: selected会写入去重名单。5.3 并发与重命名语义决策文档 Consequences 明确了三点边界并发管理员编辑为 last-writer-wins后台写入者已被删除剩下的写者只有两个管理员同时编辑同一个集合这种极端情况因此不再需要复杂的合并冲突处理重命名视为新组件组件更名后不在允许清单中自动隐藏直到管理员重新选择更新是声明式全量替换pieces字段整体替换PieceSelection调用方必须提交完整意图而不是增量 patch。六、完整 REST API 与操作示例Piece Set 的控制器定义在 packages/server/api/src/app/ee/pieces/piece-set/piece-set.controller.ts全部接口仅限平台管理员platformAdminOnly支持 USER 与 SERVICE 类型主体即也可用 Service Account / API Key 调用方法路径说明GET/piece-sets分页列出平台的 Piece Setlimit 默认 10上限 100POST/piece-sets创建 Piece Set请求体CreatePieceSetRequestBodyname 可选 keyGET/piece-sets/:id获取单个 Piece SetPOST/piece-sets/:id更新 Piece Set声明式更新见上文DELETE/piece-sets/:id删除默认集合不可删项目改绑默认集POST/piece-sets/:id/duplicate复制集合携带原 configPOST/piece-sets/:id/projects批量把项目绑定到该集合DELETE/piece-sets/:id/projects/:projectId解除项目绑定回落到默认集以隐藏所有新组件仅放行两个组件为例管理员可将集合的pieces更新为{ pieces: { mode: exclude_all, exceptions: [activepieces/piece-github, activepieces/piece-slack] } }此后任何新增组件都不会出现在该集合覆盖的项目中无需任何同步任务。若要进一步只暴露 Slack 组件的部分动作{ actions: { activepieces/piece-slack: { mode: selected, selected: [send_message, create_channel] } } }创建、绑定项目的示例请求体CreatePieceSetRequestBody / AssignProjectsRequestBodyPOST /piece-sets { name: production-pieces } POST /piece-sets/{id}/projects { projectIds: [project-id-1, project-id-2] }七、设计收益与适用边界7.1 收益总结消除写放大onPieceCreated钩子与安装时扇出全部移除PIECES_SYNC等元数据同步任务不再触碰任何 Piece Set存储有界允许清单只记录被显式管理的项不再随组件目录增长而无限膨胀语义自动正确EXCLUDE_ALL模式下新组件 不在列表 隐藏零成本成立天然满足先审后用的安全诉求单一事实来源isPieceVisible/isComponentVisible作为共享纯函数同时被服务端过滤层与 Web UI 使用前后端可见性口径一致服务端实现见 piece-filtering-utils.ts更新即所见声明式全量替换 按组件意图管理员的一次提交完整表达期望状态无增量补丁的中间态。7.2 边界与限制该机制仅在 Enterprise / Cloud 版本的服务端生效resolveVisibility的版本门槛并发管理员同时编辑同一集合时采用 last-writer-wins不做字段级合并组件重命名会导致其从允许清单中消失需管理员重新选择这是重命名视为新组件的刻意取舍。八、延伸阅读决策文档原文000007-piece-set-visibility-is-derived-at-read-time.md共享纯函数与全部请求/响应 schemapackages/core/shared/src/lib/ee/piece-set/index.ts服务端读取时过滤策略packages/server/api/src/app/ee/pieces/filters/piece-filtering-utils.tsPiece Set 业务服务默认集、更新、删除改绑packages/server/api/src/app/ee/pieces/piece-set/piece-set.service.ts配置合并逻辑applyUpdate / applyComponentIntentspackages/server/api/src/app/ee/pieces/piece-set/piece-set-config.tsREST 控制器packages/server/api/src/app/ee/pieces/piece-set/piece-set.controller.tsPIECES_SYNC定时任务定义packages/server/api/src/app/helper/system-jobs/common.ts【免费下载链接】activepiecesAI Agents MCPs AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows AI Agents • MCPs for AI Agents项目地址: https://gitcode.com/GitHub_Trending/ac/activepieces创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表