
Activepieces 服务端后端开发指南技术栈、工程模式与版本兼容门控机制【免费下载链接】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 仓库 packages/server/CLAUDE.md 展开系统讲解其服务端后端的核心技术与工程实践Fastify 5 TypeORM BullMQ 的技术选型与目录结构、控制器与模块封装模式、缓存键版本化与 N1 查询防治、邮件模板设计规范、evlog 结构化日志字段模式以及 app 与 worker 之间的发布版本兼容门控机制。读完本文你将掌握在 Activepieces 服务端新增接口、修改缓存结构、编写邮件模板、输出结构化日志时的全部规范并理解版本号读取失败即拒绝派发任务这一防静默损坏设计的底层原理。技术栈概览服务端后端packages/server/api与packages/server/worker建立在以下技术栈之上层面选型用途框架Fastify 5HTTP 服务与插件体系ORMTypeORM PostgreSQL数据持久化与迁移任务队列BullMQ异步任务与作业调度缓存 / Redisioredis分布式缓存、锁与分布式存储可观测性evlog结构化宽事件经AP_OTEL_ENABLED输出 OTLP 日志日志、指标与告警语言TypeScript严格模式全量代码路由层采用fastify-type-provider-zod即控制器中的路由定义与请求/响应校验都通过 Zod schema 完成类型在编译期与运行期保持一致。evlog 的 OTLP 日志输出由环境变量AP_OTEL_ENABLED控制这是将结构化事件接入 OpenTelemetry 采集管道的开关。项目结构服务端代码按功能模块组织核心目录如下packages/server/api/src/app/— 功能模块flows、pieces、tables、authentication、webhooks 等如 app 目录 所示每个功能对应一个目录内含 controller / service / entity 文件packages/server/api/src/app/ee/— 企业版功能SSO、SAML、SCIM、多租户等与 CE 功能物理隔离packages/server/api/src/app/database/— TypeORM 数据库连接与迁移管理packages/server/api/src/app/helper/— 服务端共享工具入口文件为 app.ts应用装配与 main.ts进程启动。整个app.ts通过await app.register(module)的方式逐个注册功能模块企业版模块单独落在src/app/ee/下保证 CE/EE 边界清晰。核心工程模式优先复用既有接口而非新增在新增任何 endpoint 之前先扫描当前 controller 及处理同一资源的兄弟 controller确认是否已有返回所需数据的路由。优先复用或扩展既有接口原因在于新接口会复制一套校验、缓存、安全配置、文档与测试面平行接口不同过滤器、不同缓存策略、不同响应结构容易漂移并引入缺陷。只有当没有任何既有路由满足用例时才允许新增接口。控制器FastifyPluginAsyncZod Zod 校验路由定义统一使用fastify-type-provider-zod导出的FastifyPluginAsyncZod配合 Zod schema 做请求校验。例如 flow.module.ts 中await app.register(flowVersionController, { prefix: /v1/flows }) await app.register(flowController, { prefix: /v1/flows }) await app.register(sampleDataController, { prefix: /v1/sample-data })模块 wrapper 独占路由前缀app.ts中每个功能都以await app.register(somethingModule)注册且不携带内联prefix选项。前缀必须写在模块文件内部如my-feature.module.ts中的await app.register(myController, { prefix: /v1/... })。禁止从app.ts直接以内联前缀注册 controller —— 应创建一个轻量*.module.tswrapper使路由的身份与其 handler 保持内聚。HTTP 方法约定所有创建与更新操作一律使用POST。这是全仓库一致的约定不需要在单个接口上争论 PUT / PATCH。缓存值形状变更必须换缓存键任何通过distributedStore写入的缓存值在滚动部署期间都可能被旧代码写入、被新代码读回新加的字段在旧条目中是undefined。由于Nullable()展开为z.optional(z.nullable(...))缺失字段能通过响应校验——不报错、不记日志只是静默降级直到条目过期。因此只要修改了缓存值的形状就必须在键构建器中提升版本段例如platform_plan:billing-overview:v1:${platformId} → platform_plan:billing-overview:v2:${platformId}旧条目无需清理前提是它们带 TTL。distributed-store-factory.ts 的实现说明了原因put(key, value, ttlInSeconds?)只有在传入ttlInSeconds时才走setex否则走set永久存活于是改键名后旧键会成为孤儿数据。因此写入缓存必须显式传入 TTL变更缓存形状时提升键的版本段而不是复用旧键。distributedStore实例由 redis-connections.ts 通过distributedStoreFactory(redisConnections.useExisting)导出Redis 客户端基于 ioredis。数据库迁移schema 变更必须通过 TypeORM 生成的迁移进行禁止在无迁移的情况下直接修改实体。相关规范可参考 server-module-anatomy.md。数组列的实体写法TypeORM 实体中的数组列统一使用如下模式columnName: { type: String, array: true, nullable: false, }邮件模板规范邮件模板位于packages/server/api/src/assets/emails/实际渲染路径由 smtp-email-sender.ts 中的packages/server/api/src/assets/emails/${templateData.name}.html与footer.html拼接确认创建或修改模板时必须遵守以下规则F 型布局— Logo、标题、正文、注释、回退链接、页脚全部左对齐CTA 按钮自动宽度、左对齐。设计系统一致— 与 Web 应用同字号体系Inter 字体族标题 32px/500正文 16px结尾 14px弱化文字 11px。颜色标题#0a0a0a、正文#2f2e2e、弱化文字#a3a3a3。白标就绪— 使用 Mustache 变量{{fullLogoUrl}}、{{primaryColor}}、{{primaryColorLight}}、{{platformName}}严禁硬编码 Activepieces 或品牌色。这些变量在renderEmailBody中由 platform 配置或默认主题defaultTheme填充。卡片式布局— 白色卡片560px宽、border-radius: 12px落在{{primaryColorLight}}淡色背景上。CTA 按钮— 自动宽度、左对齐、{{primaryColor}}背景、16px/500 白色文字、12px 18px内边距、8px圆角。回退链接— CTA 下方提供 If the button doesnt work, click here.11px#a3a3a3其中click here以{{primaryColor}}下划线强调。正文少用加粗— 仅对用户需要快速识别的动态名称项目名、角色、流程名加粗静态文本一律不加粗。Outlook 兼容— 包含!--[if mso]字体族覆盖块使用 table 布局与内联样式。无外部依赖— 不使用link样式表、跟踪像素或外部字体 CSSstyle中的font-faceCDN URL 仅作为渐进增强可接受。页脚— 使用{{ footer}}Mustache partial仅在 Cloud 版渲染地址代码中根据edition ApEdition.CLOUD注入地址字符串。N1 查询防治永远不要在循环中先取集合、再逐条查询每个条目。用 JOIN、子查询或IN子句把过滤与补全压进单条 SQL。检查跨关联行的条件如是否存在任一成员拥有权限 X时JOIN 关联表并在 SQL 中过滤而不是把行全部加载进 JS 再过滤。列表接口补全关联数据时优先leftJoinAndSelect/innerJoin或使用IN (:...ids)批量查询避免Promise.all/.map()中的逐条查找。结构化日志字段模式evlog所有结构化日志都经由 evloglogger.{info,warn,error,debug}({ fields }, msg)与activepieces/server-utils导出的wideEvent.set/error/timed。字段键而非消息字符串是仪表盘、告警与 OTLP 输出背后的可查询 schema必须保持一个概念 一条路径处处一致。字段规则按实体分组实体自身的 id 在组内用id绝不使用顶层flowRunId、runId或裸id。例如流程运行是flowRun: { id }展平为flowRun.id组名是领域模型中的 camelCase 单数实体。这条规则最关键——代码库此前曾把同一 flow-run id 记成runId、flowRunId、id三种导致所有关联查询失效。实体属性与id同组、合并进同一对象{ jobId, jobType }→job: { id, type }{ pieceName, pieceVersion }→piece: { name, version }flowRun: { id, status, environment }。禁止顶层裸name/version/status/type。错误统一用error而非errap-logger.ts 会规范化obj.err ?? obj.error并输出规范键error。有描述性名称的错误字段migrationError、pageError可保留原名。单位作为叶子键后缀时长以Ms结尾durationMs、timings.{op}Ms字节以Bytes结尾计数用Count/复数。不嵌套、不设置保留/自动填充键service、version、level、msg、timestamp、error、timings、requestId、traceId、method、path由evlog-setup.ts/ap-logger.ts/wide-event.ts及 evlog 请求中间件附加。requestId保持扁平不要折进组内。规范组清单flowRun: { id, status, environment }、flow: { id, version }、flowVersion: { id }、project: { id }、platform: { id }、user: { id }、job: { id, type }、piece: { name, version }、connection: { id }AppConnection、sandbox: { id }、worker: { id }、webhook: { id, requestId, mode, flowFound, responseStatus }、conversation: { id }、run: { id }聊天逐消息运行 id与flowRun区分串联 controller → job → worker → RPC 的聊天轮次、tool: { name, callId, phase, durationMs, input, output }聊天工具调用、gate: { id }聊天审批门、waitpoint: { id }、step: { name }、trigger: { name }、migration: { name }。需要强调的是只有日志调用的 metadata 对象logger.*、log.child、createLogger、wideEvent.set才做分组。数据模型/线上字段保持扁平——JobData.runId、数据库查询参数findOneBy({ id })、服务调用参数resumeFromWaitpoint({ flowRunId })、DTO、返回对象与客户端事件载荷都不是日志保留原始键名。发布版本检测与派发门控apVersionUtilapVersionUtil.getCurrentRelease()位于activepieces/server-utils实现见 ap-version.ts从process.cwd()/package.json读取当前运行版本。关键事实读取是 cwd 相对的读取路径为path.resolve(process.cwd(), package.json)并非模块相对路径——之前尝试过__dirname但在打包产物中不可用不要用那种方式修复。任何失败文件缺失、JSON 非法、version缺失或非字符串都会记录warn并返回哨兵值UNKNOWN_VERSION即0.0.0。读取结果在进程生命周期内缓存模块级cachedCurrentRelease后续调用直接返回缓存值。UNKNOWN_VERSION 的含义0.0.0表示读取失败而不是此进程就是 0.0.0 版本。绝不能把它当成真实发布版本。兼容性判定fail-closedapp 与 worker 两侧都通过apVersionUtil.versionsAreCompatible({ versionA, versionB })做比较该函数默认失败关闭输入情况结果任一侧为undefined旧版、未引入门控的 worker不兼容任一侧为0.0.0读取失败不兼容包括两侧都是0.0.0其余情况仅当两侧真实版本相等时兼容为什么两侧都是0.0.0也必须失败关闭两侧都读失败不等于两侧是同一个发布版本。一个跨越多个版本的打包/cwd 缺陷会让两个不同构建都上报0.0.0若退化为相等比较0.0.0 0.0.0门控会放行一个版本倾斜的运行——这正是门控要阻止的静默损坏。失败关闭的代价是空转worker 停止轮询、任务挂起这是响亮、无损、立即暴露的零 worker 运行严格优于静默跑偏也与 PR #13518 的空转等待、绝不静默出错原则一致。在正确部署中0.0.0永远不会出现npm 会随包带上package.json所以稳态成本为零。当原因为0.0.0时门控日志记录为error级别而非 warn因为该状态在部署完成后不会自愈需要人工介入。错误日志前移到启动阶段两侧进程都把读取失败的日志前置到启动让错误打包的部署立即发出声音app 端appPostBootapp.ts调用assertReleaseReadableworker 端worker.start()worker.ts调用assertReleaseReadable实现见 version-checker.ts。两侧都会将getCurrentRelease()与UNKNOWN_VERSION比较读取失败时记录error。分页on-call方式因进程而异app 在启动时即可从自身环境读取PAGE_ONCALL_WEBHOOKAppSystemProp.PAGE_ONCALL_WEBHOOK因此从appPostBoot分页worker 只能在 socket 连接后通过WorkerSettingsResponse收到 webhook因此由轮询循环中的版本兼容检查pageOnceForUnreadableWorkerVersion一次性守卫在 settings 就绪后分页——它不得在启动时调用workerSettings.getSettings()否则会在 settings 到达前抛错。运行时与健康检查可见性运行时读取是权威的发布的 tag 则由.github/workflows/release-self-hosted.yml中的 Verify tag matches package.json 步骤在构建期校验这是唯一带自由文本 tag 输入的发版路径。当前版本与已连接 worker 的版本倾斜通过GET /v1/health/system的release块暴露在平台健康页GetSystemHealthChecksResponse→ health.service.ts 的buildReleaseHealthrelease.current为0.0.0即读取失败信号workers.versionMismatched与mismatchedVersions列出版本不匹配的 worker。测试钉住这一行为读取回退与versionsAreCompatible的完整用例表含两侧0.0.0由 ap-version.test.ts 钉住它验证了正常读取、文件缺失、JSON 非法、version 缺失/非字符串时回退到0.0.0、首次读取缓存以及兼容性判定的全部组合相同真实版本为真、不同为假、任一侧undefined为假、任一侧0.0.0为假、两侧0.0.0为假。若修改哨兵值、读取策略或兼容规则必须同步更新该测试。对于任何新的当前版本比较都应复用versionsAreCompatible而非手写!/并保留回退警告——它是读取失败的唯一信号读取只发生一次并在进程生命周期内缓存。小结Activepieces 服务端的工程规范可以浓缩为几条核心原则接口先复用再新增、路由前缀由模块 wrapper 内聚管理、缓存键随形状变更提升版本段、数组列与 POST 方法全仓库统一、邮件模板严格白标化且兼容 Outlook、日志字段按实体分组且一个概念一条路径、版本比较一律走 fail-closed 的versionsAreCompatible。其中版本门控是滚动部署安全的基石——宁可让 worker 空转暴露问题也绝不让版本倾斜的运行静默写入错误数据。新增功能或修改既有代码时建议先阅读 packages/server/CLAUDE.md 与对应模块的既有实现保持模式一致。【免费下载链接】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),仅供参考