Activepieces Platform 配置深度解析:顶层租户模型、品牌与认证管理,以及平台删除的级联清理机制
【免费下载链接】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
Platform(平台)是 Activepieces 中最顶层的租户命名空间:每一个安装实例至少拥有一个 Platform,它统管品牌形象(Logo、颜色、favicon)、认证设置(邮箱登录开关、允许登录域名、联邦 SSO),以及通过PlatformPlan驱动的功能开关与配额限制。本文基于仓库内brain/knowledge/platform-editions-ee/platform-configuration.md文档,结合服务端、共享模型与前端源码,完整梳理 Platform 的实体模型、服务层方法、REST 端点、前端交互细节,以及平台删除时"先切断、后清理"的两拍式 teardown 机制,帮助你彻底掌握在 Activepieces 中配置与运维平台租户的能力。
一、Platform 是什么:所有版本通用的顶层租户
在 Activepieces 的租户模型中,Platform 是最高一级的命名空间。每个安装至少有一个平台,它拥有三类核心资产:
- 品牌(Branding):Logo、主题颜色、favicon;
- 认证(Auth):邮箱登录开关(
emailAuthEnabled)、允许登录的域名列表(allowedAuthDomains)、联邦 SSO(OAuth2 + SAML); - 套餐(
PlatformPlan):驱动功能开关(feature flags)与配额限制(limits)。
从版本分布看,Cloud 上一个用户可以拥有多个平台(多租户);CE/EE 自托管部署通常只有一个平台。该功能在所有版本中均可用(Available in all editions),只是不同版本开放的能力不同——例如 CE 使用固定的OPEN_SOURCE_PLAN,而usage用量数据只在非 Community 版本返回。
实体与字段
platform实体承载的核心字段如下(完整 zod 模型见 platform.model.ts,TypeORM 实体见 platform.entity.ts):
| 字段 | 类型 | 说明 |
|---|---|---|
ownerId | ApId | 平台所有者用户 ID,指向user表 |
name | string | 平台名称 |
primaryColor | string | 主色(十六进制) |
themeColors | jsonb, nullable | 完整主题色;为null时从前端品牌派生默认主题 |
logoIconUrl/fullLogoUrl/favIconUrl | string | 品牌图片 URL |
cloudAuthEnabled/googleAuthEnabled | boolean | 云认证 / Google 认证开关(默认true) |
emailAuthEnabled | boolean | 邮箱登录开关 |
allowedAuthDomains | string[] | 允许登录的域名白名单 |
enforceAllowedAuthDomains | boolean | 是否强制校验允许域名 |
allowedEmbedOrigins | string[] | 允许嵌入的来源(embed 白名单) |
ssoDomain/ssoDomainVerification | string / jsonb | SSO 域名及其校验状态 |
federatedAuthProviders | jsonb | 联邦认证配置(OAuth2 + SAML),列定义中select: false,默认不随查询返回 |
autoCreatePersonalProjects | boolean | 登录后是否自动创建个人项目(默认true) |
pinnedPieces | string[] | 置顶的 piece 列表 |
pieceSelectorConfig | jsonb, nullable | Piece 选择器配置;为null时使用默认标签页 |
需要特别注意的是federatedAuthProviders在 platform.entity.ts 中带有select: false,即普通查询默认不会读取它,只有显式addSelect才能拿到(对应服务层的getOneWithFederatedAuthOrThrow)。此外实体上还有两个约束值得留意:
ssoDomain上有唯一索引idx_platform_sso_domain(仅在非 NULL 时生效);ownerId → user的外键是ON DELETE RESTRICT,这条约束直接决定了平台删除时的顺序(详见后文)。
默认值与创建流程
创建平台时,服务层会写入一组确定的默认值(见 platform.service.ts):
primaryColor缺省为defaultTheme.colors.primary.default,Logo/favicon 缺省为defaultTheme.logos.*;emailAuthEnabled: true、autoCreatePersonalProjects: true、enforceAllowedAuthDomains: false、allowedAuthDomains: [];federatedAuthProviders: { saml: null }、pinnedPieces: []、pieceSelectorConfig: null、allowedEmbedOrigins: []。
在 Cloud 的 onboarding 场景下,createPlatformWithProject会用一把create-platform-${identityId}的 Redis 分布式锁串行执行"创建 owner 用户 → 创建平台 → 创建个人项目(ProjectType.PERSONAL)→ 轮换 token 版本 → 上报注册事件",并且对"创建到一半中断"的状态做了幂等恢复:已存在 owner 则复用并链接,已创建未链接 owner 的平台则补链接(linkOwnerToPlatform)。个人项目的命名规则是platformName去掉结尾的Platform后缀加上Project,或以's Project结尾。
二、PlatformPlan:功能开关、配额与计费相关字段
PlatformPlan是平台的"能力清单",由 platform.model.ts 的 zod 模型定义,大致可分成四类:
- 功能开关(FeatureFlag):
tablesEnabled、eventStreamingEnabled、environmentsEnabled、analyticsEnabled、auditLogEnabled、embeddingEnabled、aiProvidersEnabled、chatEnabled、agentsEnabled、workerGroupsEnabled、managePiecesEnabled、manageTemplatesEnabled、customAppearanceEnabled、projectRolesEnabled、globalConnectionsEnabled、customRolesEnabled、apiKeysEnabled、ssoEnabled、secretManagersEnabled、scimEnabled、showPoweredBy等。这些开关的枚举定义在FeatureFlagId中,是前端渲染与后端鉴权的共同依据。 - 不可消耗配额(Unconsumable):
teamProjectsLimit(团队项目数)、usersLimit(座位数)、activeFlowsLimit(活跃流程数)。 - 可消耗配额(Consumable):
apCredits(AP 积分)、appSumoAiCredits(AppSumo AI 积分)。 - 计费/许可字段:
includedCredits、licenseKey、licenseExpiresAt、projectsLimit、billedTeamProjectsLimit、scheduledUsersLimit、workerGroupId。
此外,模型保留了几个已废弃字段(带@deprecated注释):dedicatedWorkers、canary以及customDomainsEnabled(自定义域名功能已移除,仅保留列以兼容旧库)——现代实现统一使用workerGroupId指向 worker 组。
版本差异也很关键:在 Community 版本中,getPlan直接返回固定的OPEN_SOURCE_PLAN,usage返回undefined(见 platform.service.ts),因此 CE 自托管不存在额度计费概念。
主题色与 Piece 选择器配置
PlatformThemeColors定义了完整的可自定义主题色结构:avatar、blue-link、danger、selection,以及primary(dark/light/medium)、warn(default/light/dark)、success(default/light)三组色板,每个颜色都必须是HEX_COLOR_PATTERN(/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/)匹配的十六进制值。PieceSelectorConfig由一组tabs组成,每个 tab 的kind为BUILTIN或CUSTOM。内置 tab 有五种:EXPLORE、APPS、UTILITY、AI_AND_AGENTS、APPROVALS;自定义 tab 必须有非空title(zod refine 校验:Custom tabs must have a name),可配置icon、hidden、pieceNames与sections(分组展示)。pieceSelectorConfig为null时前端回退到默认 tab。
三、platformService:服务层方法与职责划分
服务层(platform.service.ts)围绕platformRepo提供以下核心方法:
| 方法 | 用途 |
|---|---|
create | 以默认值创建平台,并把 owner 用户关联到平台、触发onPlatformCreated初始化套餐 |
createPlatformWithProject | 注册/onboarding 专用:分布式锁保护下创建平台 + 个人项目,幂等恢复中断状态 |
update | 更新品牌、认证、piece 配置,可选地联动更新plan |
getOneWithPlanAndUsageOrThrow | 返回平台 + 套餐 + 用量(Cloud/EE 的完整视图) |
getOneWithPlanOrThrow | 只返回平台 + 套餐(不含 usage),用于鉴权守卫 |
listPlatformsForIdentityWithAtleastProject | 列出某身份拥有(且有项目)的平台,供平台切换器使用 |
getOldestPlatform | 按created ASC取最老平台,CE 单平台解析的入口 |
hasSamlConfigured | 判断平台是否配置过 SAML |
getOneWithFederatedAuthOrThrow | 显式读取敏感的federatedAuthProviders |
update中有两个值得注意的细节:
- SAML 配置前置校验:写入
federatedAuthProviders.saml前,先检查套餐是否开启ssoEnabled(否则抛FEATURE_DISABLED);配置 SAML 还要求ssoDomainVerification.status === VERIFIED,即"SSO 域名必须先完成校验"。 - SAML 缓存失效:更新 SAML 后调用
invalidateSamlClientCache(params.id)清掉缓存的 SAML 客户端,保证新配置立即生效。
返回给调用方之前,所有方法都会经过stripFederatedAuth,把federatedAuthProviders从对象中剔除——敏感的 SSO 数据默认不出服务层。
四、REST 端点:查询、更新、资产下载与删除
平台相关端点集中在 platform.controller.ts,由platformModule注册到 Fastify 应用(入口见 platform.module.ts 与 app.ts)。
GET /v1/platforms/:id —— 获取平台(plan + usage)
- 鉴权:
securityAccess.publicPlatform([PrincipalType.USER, PrincipalType.SERVICE]),同时兼容 API key(SERVICE principal)调用; - 返回
PlatformWithoutSensitiveData:SAML 敏感数据被剥离,只保留saml: {}占位或null; - USER principal 的响应会被重写(见 platform.controller.ts):
plan.chatEnabled被替换为按用户计算的 chat 可见性(chatVisibilityHelper.resolveChatEnabledForUser),嵌入式用户(embedded)的licenseKey会被置为null。
POST /v1/platforms/:id —— 更新平台
- 鉴权:
platformAdminOnly([PrincipalType.USER]),且req.principal.platform.id必须等于路径中的:id,否则抛AUTHORIZATION; - 请求体是multipart(因为要上传 Logo),通过
attachMultipartFieldsToBody预处理器把 JSON 字符串字段解析回对象(见 platform.request.ts 的jsonFromMultipart); - 三个图片字段
logoIcon、fullLogo、favIcon会先经fileService.uploadPublicAsset(FileType.PLATFORM_ASSET)上传,得到 URL 后再随其他字段一起落库——即品牌文件更新必须先走公共资产上传; - 可更新字段与校验规则(
UpdatePlatformRequestBody):name(仅校验SAFE_STRING_PATTERN,不允许.和/)、primaryColor、themeColors、federatedAuthProviders、cloudAuthEnabled、googleAuthEnabled、emailAuthEnabled、autoCreatePersonalProjects、allowedAuthDomains、enforceAllowedAuthDomains、pinnedPieces、pieceSelectorConfig、allowedEmbedOrigins。
allowedEmbedOrigins的校验值得展开:单个 origin 最长 300 字符,协议仅限http:/https:,且支持https://*.example.com形式的通配符子域(校验逻辑见 platform.request.ts)。
DELETE /v1/platforms/:id —— 删除平台(仅 Cloud)
- 仅 Cloud 版本注册该路由(
if (edition === ApEdition.CLOUD)),且必须是平台 owner(platformToEditMustBeOwnedByCurrentUser); - 存在有效订阅时拒绝删除:
hasActiveSubscription(platformPlan.plan)为真则抛DOES_NOT_MEET_BUSINESS_REQUIREMENTS,提示"先取消订阅再删除"; - 删除不是即时的:先调度一个延迟约 7 天(
PLATFORM_PURGE_DELAY_DAYS)的一次性系统任务HARD_DELETE_PLATFORM(jobId: hard-delete-platform-${platformId},最多重试 25 次、固定 60 秒退避),然后立即执行"切断访问"(beginPlatformTeardown),最后向 owner 发送确认邮件(发送失败只记日志,不阻塞删除)。
GET /v1/platforms/assets/:id —— 公共资产下载
公开路由(securityAccess.public()),按文件 ID 读取PLATFORM_ASSET/USER_PROFILE_PICTURE类型的文件,以Content-Disposition: attachment返回,mimetype 从元数据读取。品牌 Logo、favicon 正是通过该端点对外提供。
五、前端交互:useCurrentPlatform 与 Appearance 表单的"逐字段门控"
前端通过 React Query 消费平台数据,核心 hook 在 platform-hooks.ts:
useCurrentPlatform():以当前登录会话的platformId为 key(['platform', currentPlatformId]),staleTime10 秒,返回{ platform, refetch, setCurrentPlatform };useDeletePlatform():调用删除 API 后跳转/sign-in;useUpdateLisenceKey():激活 license key,成功后失效平台、flags 与订阅相关缓存。
关键 Gotcha:平台名称在 CE/未授权版本也可修改
平台配置界面位于Settings > Platform > Setup > General,其 Appearance 表单实现在 appearance-section.tsx。从源码看,该表单并非整体锁定,而是逐字段门控:
- 第 80 行计算
brandingLocked = !platform.plan.customAppearanceEnabled; disabled={brandingLocked}只作用于 Logo、图标、favicon 与主题色输入框;Platform Name输入框不受此门控,且提交时formdata.append('name', name)位于if (!brandingLocked)块之外。
因此,即使是一个 Community 或未授权平台,也能在 Appearance 界面修改平台名称——整个表单从上往下读像是全锁,实际只有品牌相关字段被锁。对应地,API 层的UpdatePlatformRequestBody.name同样不加版本门控,仅校验SAFE_STRING_PATTERN(不允许.//)。这是一个容易被误解的"特性",在排查"为什么 CE 也能改名字"时务必知道它是逐字段设计。
六、平台删除的两拍式机制:先切断,后清理
平台删除是本仓库中设计最精细、坑最多的流程,完整决策记录见 000026-delete-platform-is-a-cloud-owner-action-purged-by-one-cascading-job.md,实现位于 platform-teardown-jobs.ts。
第一拍:请求返回即切断访问(beginPlatformTeardown)
user.status只被交互式登录读取,触发器调度器、BullMQ 队列、API key 产生的 SERVICE principal 全都无视它。所以"切断"必须做四件事(见 platform-teardown-jobs.ts):
- 把所有成员的
user.status置为INACTIVE(阻止交互登录); - 删除平台全部
api_key(否则 SERVICE principal 能在窗口期内重新启用流程); - 遍历所有流程,通过正规的
CHANGE_STATUS操作禁用(FlowOperationType.CHANGE_STATUS→FlowStatus.DISABLED),从而让triggerSourceService.disable注销 webhook、从 BullMQ 中移除 cron 与轮询调度——直接写status列不会解除触发器武装; - 调用
drainFlows清理队列中的遗留工作(batchDeleteByFlowId)。
其中第 3 步对单个流程禁用失败时,会降级为强制调用triggerSourceService.disable并直接更新状态列,确保"没有新 webhook 再放行新运行"。关键顺序:purge 任务在切断之前就已完成调度,因此即使切断过程抛异常,最坏结果也是"如期清理但切断得不干净",而不是"全员失联且无人能重试"。
第二拍:约 7 天后由单个 HARD_DELETE_PLATFORM 任务清理
清理任务刻意不是单事务——每张表一条语句、每条语句可安全重复执行,25 次重试可以从列表中间恢复,而不是跨整租户持锁导致首次超时丢全部进度。删除顺序由 schema 强制,顺序为:
- 再次执行禁用与排空(对已切断的租户是 no-op);
- 删
piece_metadata、app_connection; - 逐个删除项目(先批量删项目关联的 cell/record/field/table/flow_run/trigger_event/file,再删项目行);
- 删
signing_key; - 删那些"没有任何外键可达"的 12 张表:
file(平台级)、project_role、user_invitation、mcp_oauth_token、mcp_oauth_authorization_code、variable、concurrency_pool、tool_search_index等(piece_metadata、app_connection在前已删); - 批量删除审计事件(
audit_event,按 5000 条分块); - 删
platform行——让 14 条CASCADE外键清掉其余数据; - 最后删
user行,并清理不再被任何存活用户引用的user_identity(防止把另一个平台的用户登出)。
约束决定了顺序,而不是偏好
project与signing_key对platform(id)是RESTRICT,是仅存的两个阻塞者(tag、piece_tag曾是NO ACTION,因功能废弃已被删表移除);platform.ownerId → user是RESTRICT,所以平台行必须先于 owner 的user行删除;project.ownerId → user是NO ACTION,所以项目必须先于任何用户删除;- 还有一个连环陷阱:
piece_metadata.archiveId → file是RESTRICT,而file.projectId → project是CASCADE,因此自定义 piece 存档必须在删文件之前删除——这就是piece_metadata排在前面的原因。
"平台删不掉"的诊断口诀:某租户删除失败而另一个成功,几乎总是新增了一张带RESTRICT外键的表(阻塞者),而不是成员数量问题。因为user、file、app_connection、piece_metadata、project_member、project_role、user_invitation、mcp_oauth_token、mcp_oauth_authorization_code、variable、concurrency_pool、tool_search_index这 12 张表都带platformId却没有任何外键——它们既不阻塞也不级联,只会悄悄产生孤儿行,任何 teardown 都必须显式删除它们,绝不能因为"列存在"就推断有级联行为。
对开发者的约定
任何携带platformId的新实体,应当把外键声明为ON DELETE CASCADE;否则就必须把表名手工加进 platform-teardown-jobs.ts 的删除清单(该清单由人工维护,CI 不会检查)。另外,"把成员置为INACTIVE就完事"是一个危险误解:INACTIVE 只挡登录,调度器、队列和 SERVICE principal 都不读user.status,切断平台必须按上述四件事逐项执行。
七、完整 Gotchas 清单
- 平台名称在所有版本都可改:
appearance-section.tsx按字段门控品牌输入,唯独Platform Name与formdata.append('name', ...)在if (!brandingLocked)之外;API 侧name同样无版本门控,仅校验SAFE_STRING_PATTERN。 - 逐项目(project)的 piece/action/trigger 可见性由 piece sets 管理,不是平台——
pinnedPieces/pieceSelectorConfig只管选择器呈现,不做运行期可见性控制。 - GET 响应按调用者重写:USER principal 拿到的是按用户生效的
plan.chatEnabled;嵌入式用户的licenseKey被置空。 - 更新 SAML 会失效缓存:
invalidateSamlClientCache保证新 SAML 配置立即生效。 usage只在非 Community 版本填充:CE 使用OPEN_SOURCE_PLAN,不返回用量。- 品牌文件先上传后落库:Logo/favicon 更新走
fileService.uploadPublicAsset得到 URL 再保存。 platformId列 ≠ 外键:12 张表带列无 FK,删除平台时必须显式清理,不能依赖级联。- 阻塞删除的约束只有
project与signing_key(RESTRICT):删除失败先查pg_constraint,看是否新增了阻塞外键。 - 删除顺序被
ownerId → user的 RESTRICT 强制:平台行先删,owner 的 user 行后删;项目必须先于任何用户删除。 - 新实体规范:带
platformId的新实体声明ON DELETE CASCADE,否则按名加入 teardown 清单。 - INACTIVE 只停登录:必须通过
CHANGE_STATUS禁用全部流程(注销 webhook、移除调度)、batchDeleteByFlowId排空队列、删除api_key,否则流程仍在运行。
八、关键文件速查
- 服务端整个平台切片:packages/server/api/src/app/platform/——module、controller、service、TypeORM 实体、
getPlatformIdForRequest工具(platform.utils.ts); - 删除/清理实现:platform-teardown-jobs.ts——
HARD_DELETE_PLATFORM处理器与控制器调用的stopPlatformExecution切断逻辑; - 共享 zod 模型与请求体:packages/core/shared/src/lib/management/platform/——
Platform、PlatformWithoutSensitiveData、PlatformPlan、PieceSelectorConfig、UpdatePlatformRequestBody(platform.model.ts、platform.request.ts); - 前端 React Query hook:platform-hooks.ts——
useCurrentPlatform(); - 品牌表单:appearance-section.tsx;
- 决策记录:000026-delete-platform-is-a-cloud-owner-action-purged-by-one-cascading-job.md(删除机制的完整论证,含 schema 级证据)。
文中涉及的文件路径均以仓库根目录为基准,与文档标注的校验日期(2026-07-17)一致。若你的部署遇到平台删除失败、CE 改品牌被锁但改名可用、或新增租户表后清理不干净等问题,对照本文的 Gotchas 与 teardown 清单逐项排查即可。
【免费下载链接】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),仅供参考