十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

Refine 审计日志 Provider 实战指南:在 React 管理后台中实现完整的操作审计与合规追踪

Refine 审计日志 Provider 实战指南:在 React 管理后台中实现完整的操作审计与合规追踪 Refine 审计日志 Provider 实战指南在 React 管理后台中实现完整的操作审计与合规追踪【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine导读本指南以 Refine v5 的 Audit Log Provider审计日志 Provider为核心讲解如何在 React 管理后台中自动记录数据的创建、更新与删除事件并追踪谁在什么时间改了什么。你将掌握auditLogProvider的create/get/update三个核心方法的实现细节、useLog与useLogList两个 Hook 的实际用法以及如何通过meta.audit按资源按需开启审计最终结合仓库中的audit-log-provider示例项目落地一套可运行的操作审计方案。Audit Log Provider 是什么为管理后台建立数据操作黑匣子Refine 提供了一种称为Audit Log Provider审计日志 Provider的机制每当一条记录被创建、更新或删除时Refine 都会自动向auditLogProvider发送一条新的日志事件从而实现对哪些数据被改了、是谁改的、改之前是什么样的完整追踪。这一能力直接服务于合规审计compliance、操作溯源traceability与内部工具/管理后台的真实业务需求。在 Refine 的架构中auditLogProvider只是数据流中的一个旁路它不参与数据获取而是观察通过数据 Hook 发起的变更操作。使用它只需要将其作为属性传递给Refine组件import { Refine } from refinedev/core; import auditLogProvider from ./auditLogProvider; const App () ( Refine /* ... */ auditLogProvider{auditLogProvider} / );auditLogProvider的接口定义位于核心包的类型文件中packages/core/src/contexts/auditLog/types.ts源码将其定义为三个必需方法的集合export type AuditLogProvider RequiredIAuditLogContext;其中IAuditLogContext展开后即对应三个方法const auditLogProvider { create: (params: { resource: string; action: string; data?: any; author?: { name?: string; [key: string]: any; }; previousData?: any; meta?: Recordstring, any; }) void; get: (params: { resource: string; action?: string; meta?: Recordstring, any; author?: Recordstring, any; }) Promiseany; update: (params: { id: BaseKey; name: string; }) Promiseany; }三个方法的职责分别是方法职责触发时机create将一条事件写入审计日志每次数据变更create/update/delete 及其 many 变体成功后自动触发也可用useLog的log方法手动触发get返回符合条件的日志事件列表使用useLogList查询历史记录时触发update更新某条审计日志事件使用useLog的log方法更新事件名称时触发手把手实现一个 Audit Log ProviderRefine 对 Provider 的实现方式保持Agnostic无关实现——官方示例使用dataProvider来处理事件但你完全可以用任意后端、任意存储来实现。下面我们按照get→create→update的顺序逐一实现。get按资源与记录 ID 拉取审计历史get方法用于获取审计日志事件列表。当你使用useLogList列出某个资源在某条记录 ID 下的所有活动时Refine 会向get传入如下事件{ resource: posts, meta: { id: 1 } }在 Provider 中可以这样处理export const auditLogProvider: AuditLogProvider { get: async (params) { const { resource, meta, action, author } params; const response await fetch( https://example.com/api/audit-logs/${resource}/${meta.id}, { method: GET, }, ); const data await response.json(); return data; }, };注意get的方法签名中action与author是可选参数这允许你按操作类型如只看update或按操作者进行过滤。create记录一次数据变更事件⚠️安全提示由于客户端数据可能被篡改官方强烈建议将审计日志的创建放在服务端 API侧完成。前端的create实现应主要用于打通流程或原型验证。create在每次数据变更成功后被自动调用入参包含了新记录以及可选的旧记录的值。关于入参有三个重要的自动补充规则previousData来自 react-query 缓存能找到就返回旧值否则为undefined对于create类型的变更如果请求响应中包含id字段该id会被自动加入meta对象如果 auth provider 中定义了getUserIdentity其返回值会被封装为author对象附加到事件上。根据变更类型的不同create收到的参数也各不相同。下面以posts资源为例完整列出六种变更的入参形态Create单条创建{ action: create, resource: posts, data: { title: Hello World, content: Hello World }, meta: { dataProviderName: simple-rest, // 如果请求响应带有 id 字段会追加到 meta 中 id: 1 } }Update单条更新{ action: update, resource: posts, data: { title: New Hello World, content: New Hello World }, previousData: { title: Hello World, content: Hello World }, meta: { dataProviderName: simple-rest, id: 1 } }Delete单条删除{ action: delete, resource: posts, meta: { dataProviderName: simple-rest, id: 1 } }Create Many批量创建useCreateMany{ action: createMany, resource: posts, data: [ { title: Hello World 1 }, { title: Hello World 2 } ], meta: { dataProviderName: simple-rest, // 如果请求响应带有 id 字段会以 ids 数组追加到 meta 中 ids: [1, 2] } }Update Many批量更新useUpdateMany{ action: updateMany, resource: posts, data: { status: published }, previousData: [ { status: draft }, { status: archived } ], meta: { dataProviderName: simple-rest, ids: [1, 2] } }Delete Many批量删除useDeleteMany{ action: deleteMany, resource: posts, meta: { dataProviderName: simple-rest, id: [1, 2] } }在 Provider 中接收并转发这些事件的完整实现如下export const auditLogProvider: AuditLogProvider { create: (params) { const { resource, meta, action, author, data, previousData } params; console.log(resource); // products, posts, etc. console.log(meta); // { id: 1 }, { id: 2 }, etc. console.log(action); // create, update, delete // author 对象即 useGetIdentity 钩子的返回值 console.log(author); // { id: 1, name: John Doe } console.log(data); // { name: Product 1, price: 100 } console.log(previousData); // { name: Product 1, price: 50 } await fetch(https://example.com/api/audit-logs, { method: POST, body: JSON.stringify(params), }); return { success: true }; }, };update修改审计事件的名称update方法用于更新一条审计日志事件。当你使用useLog的log方法为事件命名时Refine 会向update传入如下参数{ id: 1, name: event name }对应的实现export const auditLogProvider: AuditLogProvider { update: async (params) { const { id, name, ...rest } params; console.log(id); // 1 console.log(name); // Created Product 1 console.log(rest); // { foo: bar } await fetch(https://example.com/api/audit-logs/${id}, { method: PATCH, body: JSON.stringify(params), }); return { success: true }; }, };访问审计日志的 HookuseLog 与 useLogListProvider 实现完成后你可以在应用任意位置通过两个内置 Hook 访问它useLog用于手动向审计日志写入调用log方法触发create或更新事件触发updateuseLogList用于读取审计事件列表触发get。useLogList的调用形态如下resource指定资源meta.id指定要追溯的记录 IDimport { useLogList } from refinedev/core; const { isLoading, data } useLogList({ resource: posts, meta: { id: 1 }, });在示例项目audit-log-provider中History 组件 正是使用useLogList读取并渲染某条记录的全部操作历史资源名、动作、变更后数据、变更前数据、时间戳export const History: FCHistoryProps ({ resource, id }) { const { isLoading, data } useLogListILog[]({ resource, meta: { id }, }); if (isLoading) return divLoading.../div; return ( div h2History #{id}/h2 {data?.length 0 divNo history/div} {data?.map((item) ( div key{item.id} divResource: {item.resource}/div divAction: {item.action}/div divData: pre{JSON.stringify(item.data, null, 2)}/pre/div divPrevious Data: pre{JSON.stringify(item.previousData, null, 2)}/pre/div divTimestamp: {item.timestamp}/div /div ))} /div ); };对应的日志数据结构定义在 examples/audit-log-provider/src/interfaces/index.d.ts 中export interface ILog { id: string; action: string; resource: string; data: unknown; previousData: unknown; timestamp: string; }哪些 Hook 会触发审计日志下面这些 Hook 在变更成功后会调用auditLogProvider的create方法包Hooksrefinedev/coreuseFormrefinedev/antduseForm、useModalForm、useDrawerForm、useStepsFormrefinedev/mantineuseForm、useModalForm、useDrawerForm、useStepsFormrefinedev/react-hook-formuseForm、useModalForm、useStepsForm每个数据 Hook 传给create的具体参数如下useCreateconst { mutate } useCreate(); mutate({ resource: posts, values: { title: New Post, status: published, content: New Post Content, }, meta: { foo: bar }, }); // 调用 Audit Log Provider 的 create参数为 { action: create, resource: posts, data: { title: Title, status: published, content: New Post Content }, meta: { id: 1, // 额外的元数据会包含在 meta 中 foo: bar } }useCreateManyconst { mutate } useCreateMany(); mutate({ resource: posts, values: [ { title: Title1, status: published, content: New Post Content1 }, { title: Title2, status: published, content: New Post Content2 }, ], meta: { foo: bar }, }); // 参数为 { action: createMany, resource: posts, data: [ { title: Title1, status: published, content: New Post Content1 }, { title: Title2, status: published, content: New Post Content2 } ], meta: { ids: [1, 2], foo: bar } }useUpdateconst { mutate } useUpdate(); mutate({ id: 1, resource: posts, values: { title: Updated New Title }, }); // 参数为 { action: update, resource: posts, data: { title: Updated New Title, status: published, content: New Post Content }, previousData: { title: Title, status: published, content: New Post Content }, meta: { id: 1 } }useUpdateManyconst { mutate } useUpdateMany(); mutate({ ids: [1, 2], resource: posts, values: { title: Updated New Title }, }); // 参数为 { action: updateMany, resource: posts, data: { title: Updated New Title }, previousData: [ { title: Title1 }, { title: Title2 } ], meta: { ids: [1, 2] } }useDeleteconst { mutate } useDelete(); mutate({ id: 1, resource: posts }); // 参数为 { action: delete, resource: posts, meta: { id: 1 } }useDeleteManyconst { mutate } useDeleteMany(); mutate({ ids: [1, 2], resource: posts }); // 参数为 { action: deleteMany, resource: posts, meta: { ids: [1, 2] } }按资源、按变更类型精细控制审计开关默认情况下一个资源的所有 create / update / delete 操作都会产生审计日志。如果你只想对特定变更类型记录日志可以在资源的meta.audit中声明允许的类型。例如下面的配置只对create操作生成审计事件update与delete则不再记录Refine dataProvider{dataProvider(API_URL)} resources{[ { name: posts, meta: { audit: [create], }, }, ]} /这一设计让你可以针对不同资源定制审计策略——例如订单表全量审计、草稿箱只审计删除操作既满足合规要求又避免日志噪音。完整可运行示例audit-log-provider仓库中的 examples/audit-log-provider 是一个完整的 Headless 示例项目将上述概念串联为一个可运行的应用列表页展示帖子可创建/编辑/删除并为每条记录提供History弹窗展示其操作历史。示例中auditLogProvider的三个方法全部基于refinedev/simple-rest实现将日志写入logs资源见 App.tsxRefine auditLogProvider{{ get: async ({ resource, meta }) { const { data } await dataProvider(API_URL).getList({ resource: logs, filters: [ { field: resource, operator: eq, value: resource }, { field: meta.id, operator: eq, value: meta?.id }, ], }); return data; }, create: (params) { return dataProvider(API_URL).create({ resource: logs, variables: params, }); }, update: async ({ id, name }) { const { data } await dataProvider(API_URL).update({ resource: logs, id, variables: { name }, }); return data; }, }} /* ... */ /可以看到get使用resource与meta.id组合过滤来精确返回某条记录的完整历史create将整个事件参数原样 POST 到logs资源update则按id更新事件名称。列表页list.tsx通过useModal与自定义Modal组件modal.tsx在点击History时展示History组件编辑页edit.tsx同样内置了历史弹窗入口。本地运行该示例Node.js 20npm create refine-applatest -- --example audit-log-provider随后在项目目录内执行npm run dev对应脚本定义见 examples/audit-log-provider/package.json即可启动开发服务器体验创建 → 修改 → 查看历史的完整审计闭环。总结与最佳实践要在生产环境用好 Refine 的 Audit Log Provider以下几点值得牢记服务端落库审计日志具有不可抵赖性需求务必在 API 侧真正落库前端create只做转发或原型验证善用自动附加的元数据previousData、author依赖getUserIdentity、meta.id/ids都是由 Refine 自动补充的合理利用可大幅降低日志建模成本按需开启审计通过meta.audit控制每个资源记录哪些操作平衡合规需求与存储成本前端展示闭环结合useLogList在编辑页/详情页提供历史弹窗让操作者有据可查实现无关性auditLogProvider与数据层解耦无论你使用 REST、GraphQL 还是自建后端接入成本都是一致的。进一步阅读仓库内的相关文档可深入useLog与useLogList的完整 API审计日志 Provider 完整指南、useLogList Hook 文档、useLog Hook 文档类型定义见 packages/core/src/contexts/auditLog/types.ts完整示例见 examples/audit-log-provider。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表