Activepieces 审计日志(Audit Logs)全解:事件模型、捕获架构与生产级查询索引实践
Activepieces 审计日志Audit Logs全解事件模型、捕获架构与生产级查询索引实践【免费下载链接】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 开源仓库中的审计日志模块为核心完整解析其事件模型、基于事件总线的捕获架构、平台管理员查询 API以及从生产事故中沉淀出的索引设计与事件发射最佳实践。读完本文你将掌握audit_event表的字段与索引设计、ApplicationEvent判别联合类型、GET /v1/audit-events的过滤与游标分页用法以及如何避免事件漏记与大表查询超时这两类高发问题。一、审计日志的定位与启用条件Activepieces 的审计日志Audit Logs用于记录安全相关的用户与系统操作为合规审计compliance与取证forensics提供依据。所有事件被持久化到audit_event表并可由平台管理员platform admin查询。该功能仅对Enterprise / Cloud版本开放由平台套餐字段platform.plan.auditLogEnabled控制。在服务端这一门控是在路由注册时通过 Fastify 的preHandler钩子强制执行的。参见 audit-event-module.tsexport const auditEventModule: FastifyPluginAsyncZod async (app) { auditLogService(app.log).setup() app.addHook(preHandler, platformMustHaveFeatureEnabled((platform) platform.plan.auditLogEnabled)) await app.register(auditEventController, { prefix: /v1/audit-events }) }这意味着当套餐未开启auditLogEnabled时/v1/audit-events相关请求会被直接拦截同时前端平台管理页面的审计日志入口也会基于同一字段禁用见下文 React Query hooks 中的enabled: platform.plan.auditLogEnabled。二、数据模型ApplicationEvent 联合类型与 audit_event 表2.1 ApplicationEvent可审计事件的判别联合所有可审计的事件类型被建模为一个discriminated union判别联合定义在 packages/core/shared/src/lib/ee/audit-events/index.ts。该联合由 20 个 Zod schema 组合而成AgentAuditEvent、ConnectionEvent、VariableEvent、FlowCreatedEvent、FlowDeletedEvent、FlowUpdatedEvent、FlowPiecesUpgradedEvent、FlowPiecesRevertedEvent、FlowPublishedEvent、FlowActivatedEvent、FlowDeactivatedEvent、FlowRunEvent、AuthenticationEvent、FolderEvent、SignUpEvent、SigningKeyEvent、ProjectRoleEvent、ProjectReleaseEvent、ProjectReplacedEvent、FlowApprovalEvent以action字段作为判别键每个事件类型都复用一组公共信封字段const BaseAuditEventProps { ...BaseModelSchema, // id / created / updated platformId: z.string(), projectId: z.string().optional(), projectDisplayName: z.string().optional(), userId: z.string().optional(), userEmail: z.string().optional(), ip: z.string().optional(), }公共信封 类型化的data载荷既保证了入库数据的结构一致也让消费端摘要生成、前端渲染、测试可以按事件类型安全地访问载荷。2.2 ApplicationEventName39 个事件名枚举ApplicationEventName是事件的字符串枚举。当前源码中共有39 个值早期内部笔记记录为 27 个仓库已持续扩充按领域可归纳为下表领域事件名流程生命周期flow.created、flow.deleted、flow.updated、flow.published、flow.activated、flow.deactivated流程运行flow.run.started、flow.run.finished、flow.run.resumed、flow.run.retried流程维护flow.pieces.upgraded、flow.pieces.reverted、flow.approval.requested、flow.approval.granted、flow.approval.rejected、flow.approval.withdrawn文件夹folder.created、folder.updated、folder.deleted连接connection.upserted、connection.deleted变量variable.upserted、variable.deleted、variable.value.revealedAgentagent.created、agent.updated、agent.deleted、agent.published、agent.unpublished认证与用户user.signed.up、user.signed.in、user.password.reset、user.email.verified平台治理signing.key.created、project.role.created、project.role.updated、project.role.deleted、project.release.created、project.replaced值得注意的细节variable.value.revealed变量值被查看与signing.key.created签名密钥创建属于安全敏感事件说明审计范围不止覆盖业务流程还覆盖凭证类操作。而project.replaced事件的数据载荷中包含了 flows / tables / folders / connections 各自的 created / updated / deleted / unchanged 计数与outcome、durationMs等字段为项目替换类批量操作提供了完整的事后统计见 index.ts。2.3 audit_event 表结构与索引audit_event表以 TypeORMEntitySchema定义见 audit-event-entity.ts。字段如下字段类型可空说明id/created/updated—否BaseColumnSchemaPart提供的公共列platformIdString否所属平台外键关联platform级联删除projectIdString是所属项目后台/系统级事件可无项目actionString否事件名对应ApplicationEventNameuserEmailString是操作者邮箱系统事件可空projectDisplayNameString是项目显示名快照datajsonb否类型化事件载荷JSONB 存储ipString是客户端真实 IP请求来源时填充userIdString是操作者用户 ID表上定义了 4 个复合索引audit-event-entity.tsaudit_event_platform_id_project_id_user_id_action_idx(platformId, projectId, userId, action)最窄组合覆盖最常见的过滤场景audit_event_platform_id_user_id_action_idx(platformId, userId, action)audit_event_platform_id_action_idx(platformId, action)audit_event_platform_id_created_id_desc_idx(platformId, created, id)——这个索引与分页排序强相关详见第五节。data采用 jsonb 而非拆列使事件载荷可以随业务演进自由扩展无需为每个新事件类型做 DDL 迁移。三、事件捕获架构基于事件总线的解耦监听审计日志的核心设计思想是通过事件总线applicationEvents解耦捕获业务代码只负责发事件审计模块只负责听事件二者互不感知。auditLogService.setup()在模块初始化时调用audit-event-service.tsexport const auditLogService (log: FastifyBaseLogger) ({ setup(): void { applicationEvents(log).registerListeners(log, { userEvent: (log) async (params) { rejectedPromiseHandler(auditLogRepo().save(params), log) }, workerEvent: (log) async (_projectId, params) { rejectedPromiseHandler(auditLogRepo().save(params), log) }, }) }, // ... })两个监听器分别对应两类事件来源均为 fire-and-forget异步落库、异常不抛出userEvent由 HTTP 请求触发的用户操作如登录、编辑流程事件参数中已携带完整的userEmail、projectDisplayName、ip等上下文workerEvent由后台 Worker 触发的操作如流程运行、项目替换以(projectId, params)形式传入事件总线会补上id、created、updated等信封字段。总线实现在 application-events.tssendUserEvent()会对请求来源做上下文丰富enrichAuditEventParam包括解析出真实用户 ID、项目、项目显示名、用户邮箱与真实客户端 IP再把事件分发给所有userEventListenerssendWorkerEvent()则直接构造事件并广播给workerEventListeners。rejectedPromiseHandler保证落库失败不会反噬主业务流程。3.1 上下文如何被丰富对于请求来源的事件extractMetaInformation()会从 Fastify 请求中提取platformId取principal.platform.idprojectId取request.projectId ?? principal.projectIduserId经authenticationUtils.extractUserIdFromRequest()解析ip经networkUtils.extractClientRealIp()从CLIENT_REAL_IP_HEADER指定的请求头提取。随后事件总线再依据userId反查用户邮箱、依据projectId反查项目显示名把裸事件丰富成可读、可审计的完整记录application-events.ts。这也是为什么审计日志能直接展示谁、在哪个项目、什么 IP、做了什么——这些信息在事件源头发送时可能并不完整。四、查询 API 与前端集成4.1 GET /v1/audit-events路由挂载于packages/server/api/src/app/ee/audit-logs/audit-event-module.ts前缀/v1/audit-events仅平台管理员可访问securityAccess.platformAdminOnly([PrincipalType.SERVICE, PrincipalType.USER])。控制器将查询参数透传给auditLogService.list()返回SeekPageApplicationEvent按created倒序排列。请求参数由 Zod schemaListAuditEventsRequest定义index.ts参数类型说明limitnumber可选每页条数未传时服务端默认 20cursorstring可选游标分页令牌actionstring[]可选数组按事件名过滤支持多个projectIdstring[]可选数组按项目过滤支持多个userIdstring可选按用户 ID 过滤createdBeforestring可选只返回该时间点之前的事件createdAfterstring可选只返回该时间点之后的事件服务端的过滤实现audit-event-service.ts以platformId为强制过滤条件平台数据隔离再按userId等值、action/projectId的IN列表、created的区间逐层叠加andWhere最后交给buildPaginator完成游标分页。集成测试 audit-event.test.ts 验证了两点管理员能列出本平台全部事件非 ownerPlatformRole.MEMBER请求返回403 FORBIDDEN。4.2 前端API client、React Query hooks 与管理页面前端数据链路清晰分层audit-events-api.ts封装GET /v1/audit-events请求audit-log-hooks.tsReact Query 的useAuditLogs()hook把 URL 查询参数cursor、limit、action[]、projectId[]、userId、createdBefore/After映射为 API 参数并设置enabled: platform.plan.auditLogEnabled—— 套餐未开启时直接不发起请求UI 页面位于 packages/web/src/app/routes/platform/security/audit-logs/供平台管理员在安全菜单下按事件类型筛选、按时间区间查询。五、从生产事故中沉淀的实战陷阱Gotchas该模块的知识库笔记记录了几条极具工程价值的生产教训以下逐条展开。5.1 事件必须由执行操作的服务发出而非调用方事故 #14591 的根因flow 类事件当初在flow.controller.ts中发射导致所有绕过 controller 直接调用 flow 服务代码的路径——包括 15 个 MCP flow 工具、app-connection.handler.ts、worker-rpc-service.ts、project-state-helper.ts、platform-teardown-jobs.ts——在修改流程时完全没有留下审计记录。而 flowruns从未出现该问题因为运行事件经由flow-run-service.ts内的flowRunSideEffects发出。由此形成两条硬性规范把*-side-effects.ts钩子放进执行操作的服务内部默认开启事件发射而不是放在 controller 层或让每个调用方各自发射批量/系统路径若确实需要静默如 project release apply、platform teardown必须显式传emitEvents: false退出让这次不记审计成为一个可审查的显式决策而非意外遗漏。此外schema 中ip是可选的——正确做法是把它作为 controller 传入的一个可选参数向下传递而不是为了让事件拿到 IP 就把发射逻辑留在 controller 层。仓库中新增的 mcp-flow-audit-trail.test.ts 正是针对 MCP 工具链审计追踪的回归测试。5.2 排序与索引分页必须覆盖排序列列表接口的排序规则是created DESC, id DESC——Paginator会自动追加id作为平局决胜键withIdTiebreaker因此索引必须同时覆盖这两列。只有(platformId, created DESC)查询计划会在索引之上叠加 Incremental Sort 节点只有(platformId, created DESC, id DESC)是纯索引扫描plain index scan即当前实体中定义的audit_event_platform_id_created_id_desc_idx若两者都没有Postgres 只能借助platformId开头的action索引读完整平台的所有行再全量排序后只返回一页如 11 条导致语句超时、页面 500对应事故 GIT-1705。文档记录的生产规模数据可佐证问题的严重性Cloud 生产环境2026 年 8 月audit_event已达约 3.62 亿行 / 475 GB单个平台约 650 万行错误查询计划代价高达 740 万且永不结束。同时该表从不清理GIT-1574因此任何新增查询形态都需要设计覆盖排序的索引而不只是覆盖过滤条件。5.3 在生产大表上建索引是运维操作不是迁移步骤在 475 GB 的表上CREATE INDEX CONCURRENTLY需要运行数小时。而项目迁移在main.ts中、服务开始监听之前执行——启动期构建索引永远无法及时通过健康检查部署会被回滚到一个只建了一半的索引上。因此规范做法是在部署前手工构建索引让迁移里的IF NOT EXISTS直接跳过no-opCREATE INDEX CONCURRENTLY同样受statement_timeout约束构建会话中需先SET statement_timeout 0若角色级超时已设置启动期路径会直接失败被中断的 CONCURRENTLY 构建会留下indisvalid false的索引普通IF NOT EXISTS重试会跳过它并报告成功但查询规划器永远不会使用这个索引——这也是 1820 号迁移要检查pg_index.indisvalid并在重建前删除无效残留的原因。5.4 游标分页的演进同一秒内的事件不再被跳过如果读到过该分页器用DATE_TRUNC(second, created)生成游标的旧资料那是过时信息。当前实现改为选择created::text并生成复合游标(created c) OR (created c AND id i)彻底修复了同一秒内的事件跨页被跳过的旧缺陷。也就是说分页在created与id两层上都是稳定、唯一的。六、摘要生成与测试基建summarizeApplicationEvent()index.ts将每个事件渲染成一句话的人类可读摘要。其中flow.updated最复杂它依据FlowOperationTypeADD_ACTION、UPDATE_ACTION、DELETE_ACTION、CHANGE_NAME、LOCK_AND_PUBLISH、MOVE_ACTION、ADD/DUPLICATE/DELETE/MOVE_BRANCH、ADD/UPDATE/DELETE_NOTE 等 20 余种操作生成精确的细节描述例如Added action Send Email to Order Flow Flow.或Deleted actions step_1, step_2 from Order Flow Flow.。运行类事件则输出Flow run id is started / finished / retried from a failed step审批类事件附带rejectionReason。配套的buildMockEvent()mock-event-builder.ts为每个事件名生成一个带完整合法载荷的 typed mock统一使用ip: 127.0.0.1等固定测试数据既用于事件目的地event-destination测试投递也是后续新增事件类型时一键产出样例数据的基座。七、关键文件索引服务端模块packages/server/api/src/app/ee/audit-logs/含 audit-event-module.ts、audit-event-service.ts、audit-event-entity.ts事件总线packages/server/api/src/app/helper/application-events.ts事件类型与工具packages/core/shared/src/lib/ee/audit-events/ApplicationEvent联合、ApplicationEventName枚举、summarizeApplicationEvent()、buildMockEvent()前端audit-events-api.ts、audit-log-hooks.ts、审计日志管理页面集成测试packages/server/api/test/integration/cloud/audit-event/含 audit-event.test.ts 与 mcp-flow-audit-trail.test.ts面向用户的文档docs/admin-guide/security/audit-logs/每种事件类型一个文档页结语Activepieces 的审计日志模块是一套事件模型 事件总线 门控查询三位一体的实现判别联合的事件类型保证了数据结构的可扩展性applicationEvents总线实现了业务与审计的解耦而auditLogEnabled门控与platformAdminOnly权限控制保证了只有合规范围内的人员能访问。真正值得借鉴的是那些踩坑记录——把事件发射放在服务内部、为created id排序设计复合索引、在超大规模表上以运维流程而非启动迁移的方式建索引——这些实践同样适用于任何需要长期累积、按时间倒序查询的海量审计表。【免费下载链接】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),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →