# @12-apps/audit 中文文档

规范地址：https://npmcn.com/package/%4012-apps/audit/

版本：5\.11\.0
npm 元数据更新时间：2026-10-30T15:06:03.358Z
中文译文更新时间：2026-11-05T19:43:43.876Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%4012-apps/audit
原文对照：https://npmcn.com/package/%4012-apps/audit/?lang=original
项目主页：https://github.com/12-apps/shared-packages#readme
源码仓库：https://github.com/12-apps/shared-packages

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：284 kB，48 个文件
- 模块格式：ES Module
- TypeScript 类型：未声明
- Node.js 要求：\>=22\.0\.0
- 运行依赖：2 个
- peer 依赖：hono \>=4\.0\.0，react \>=19\.0\.0，react-dom \>=19\.0\.0，@12-apps/wiring \>=1\.3\.0
- 首次发布：2026-08-13T04:00:54.875Z
- 最新版本发布：2026-10-30T15:06:02.955Z
- 近 30 天下载量：33353（2026-10-04 至 2026-11-02，npm 官方下载统计）
- 已发布版本数：20（近 12 个月 20 个）

## 中文简介

普通、便携式行动审计。 无框架核心(动作/资源词汇和默认 diff 编辑),主机挂载后端表面(./ server: 交易写入器, 每请求的演员上下文, 创建/ 更新后印出)

## 安装

```sh
npm install @12-apps/audit
```

也可以使用：`pnpm add @12-apps/audit`、`yarn add @12-apps/audit`、`bun add @12-apps/audit`

## README

# `@12-apps/audit`

动作审计作为套件: 仅是附件的"谁做了什么"线索, 它的交易 编剧,背后的每个请求的演员背景,两个Prisma扩展 执行它, 保留扫描, 租户范围上市 终点和 观者读之.

**词汇表是你的.** 这个包船没有动作,没有资源类型, 没有字段许可列表,也没有产品语言——你宣布并传入. 见 [采用.md](https://npmcn.com/package/%4012-apps/audit/ADOPTING.md) 用于迁移表,如果您来自 1.x (中文(简体) ).

两半,各一厂.

```ts
// backend
import { createApiAudit } from '@12-apps/audit/server';
import { auditRouter } from '@12-apps/audit/hono';

// frontend
import { createWebAudit } from '@12-apps/audit/react';
```

## 它是为了什么

审计线索只有在事后无法编辑才值得,而且只值得 如果写得对的话,是站不住脚的 这个包是这两个属性, 机械制造:

-   **不可思议.** 该条目被写入 INSID 呼叫者的交易, 所以"它 "它被记录下来" 承诺或滚回一起——从来没有 火相与忘却. 后来,模型拒绝了 `update` 编号 : `upsert` 编号 : `delete` 在客户端.
-   **正确归属.** 入口带有一个身份的AIR:真正的人类 其全权证书授权写作,而且——当某届会议正在冒充时—— 谁是屏幕声称的。 一个领域无法回答两个问题,而且 拆解它们不能在附件表格中恢复.

## 词汇

一种价值描述的是可能审计的内容,一行可能说的话,以及人类 读取。 两者都取同物.

```ts
import { defineAuditVocabulary } from '@12-apps/audit';

export const AUDIT = defineAuditVocabulary({
  actions: {
    'post.publish': { label: 'Post published' },
    'comment.hide': { label: 'Comment hidden' },
  },
  resources: {
    post: { label: 'Post', fields: ['title', 'state', 'publishedAt'] },
    comment: { label: 'Comment', fields: ['state', 'reason'] },
  },
});
```

`fields` 是一个 **逐项拒绝授权列表**: diff 携带但 词汇表不删除名称, 因此调用者会通过整个数据库行 无法将 PII 泄露到仅包含附件的表格中。 反面是,一个疏漏是 无法在写入站点中隐藏并显示为空洞的条目——列出每个字段 共享资源类型中的每个写入器都会发出.

工厂拒绝,在集会上,一切 使词汇 不安全的后端: 一个空轴, 一个空轴 `fields`,空白标签,空白或 白空加法 id 或 字段名, 重复字段, 整数式 id 。 每个 其中一个是故障打开,解释在 [采用.md](https://npmcn.com/package/%4012-apps/audit/ADOPTING.md) 第5节.

## 后端的一半

```ts
const audit = auditRouter({
  db: () => getPrismaClient(),               // the seam: one owned model
  vocabulary: AUDIT,                         // what may be audited
  trackedModels: ['Post', 'Comment'],        // created_by / updated_by
  retention: { floorDays: 365 },
  directory: { getUsers, listActors },       // who a user id is (optional)
  messages: { forbidden: 'No access.' },     // your product's copy
  resolveActor: async (request) => {         // WHO is calling
    const session = await auth(request.raw);
    if (!session) return null;
    const { tenantId, permissions } = await resolveTenant(request.params.tenantSlug, session);
    // Return null — the 401 — rather than an actor with no tenant. An `undefined`
    // tenantId is a cross-tenant read waiting to happen, so the package refuses it
    // with a 500 (ADOPTING.md rule 1).
    if (!tenantId) return null;
    return {
      tenantId,
      userId: session.user.id,
      permissions,
      role: session.role,
      scope: tenantId,
      onBehalfOfUserId: session.impersonating?.subjectUserId ?? null,
    };
  },
});

app.use('*', audit.actorContext);              // stamp every request
app.route('/api/admin/:tenantSlug', audit.router);
```

你得到什么回来:

|  |  |
| --- | --- |
| `routes` | 框架中性描述符,按挂起顺序排列(`./hono` 适应它们) |
| `write(tx, entry)` | 交易写入器 —— 在您的交易中调用它 |
| `extendPrismaClient(client)` | 两次扩展,一次在客户端构建时应用 |
| `withActorContext(req, run)` | 演员文本中间软件( name`actorContext` 内 `./hono`(中文(简体) ) |
| `retention` | `purgeExpired()` 财务报告和审定财务报表 `purgeTenantWindow(tenant, since, cutoff)` |
| `store` | 租户范围改为: 显示相同行的宿主表面 |
| `vocabulary` | 有效的词汇表——标签和验证 |

### 终点

| 方法 | 路径 |  |
| --- | --- | --- |
| `GET` | `/audit-logs` | 房客的踪迹, 最新的第一—— `{ data, pagination }` |
| `GET` | `/audit-logs/actors` | 观众的演员-过滤选项—— `{ data }` |

过滤器 : `q` (资源id包含, \\\\ 200个字符), `action_in`, (中文(简体) ), `resourceType_in`, (中文(简体) ), `actorUserId`, (中文(简体) ), `resourceId`, (中文(简体) ), `from`编号 :`to` (含 `YYYY-MM-DD`, (中文(简体) ), `sort`, (中文(简体) ), `page`, (中文(简体) ), `pageSize`。呼号是配置(`pagination`),默认为 20 / 100 / 100 / 100 000, 并且它们作为限定整数而不是作为 光线 未知的过滤器值, 不可保存 `sort` 且日期不正确 `400` 从未接受和忽视;否认就是 `{ error }` 在顶层 `401` 编号 : `403`。 。 。 .

名单的顺序是 **共计**数字 : `created_at DESC, id DESC` 默认,或 `ASC, ASC` 页:1 `?sort=createdAt:asc` —— 说 `created_at` 是一条线索的一轴, 向前看 事情是如何重建的 领带断了 遵循方向,否则两种观点不会相互颠倒。 审计线索是: 连续写作 `created_at` 实值 `timestamp(3)`,所以联系是普通的——和 SQL 保证某类无法区分的行顺序 与 `skip`编号 :`take` 意思是读者看到一个条目两次,从不看到另一个. 接合断是接合的一部分`AUDIT_LOG_ORDER_BY`因此,一个非Prisma 而不是重新塑造它.

有一个 **无写入结束点**条目是由变异体自己写的, 所以这里的POST只是一种 伪造历史的方法.

### 允许它打开

`audit:read` 导出为 `AUDIT_READ_PERMISSION`,因为这个软件包拥有 端点 它守护。 把它写入目录,或者用不同的拼写 与 `gatePermissions.read` ——常数是默认数,而不是要求.

## 前端的半边

```ts
const { page: AuditLog } = createWebAudit({
  apiBase: '/api/admin/my-store',
  vocabulary: AUDIT,
  labels: { title: 'History' },   // your product's words
});
```

整个SCREEN,关于共享 `DataViews` 网格:仪表板头 `[i]` 及其导出、 搜索框、 动作/ 资源/ 演员过滤器 药丸、 周期范围、 可排序的日期列、 列可见度、 已保存 视图,带有 diff 摘要和服务器-模式寻呼器的线索——以及 作为一行命名的假配对 **两者** 人类 词汇 与后端部分共享,所以存在的动作是动作 取景器可以标签.

作为 SAME 网格, 主机的其他列表使用是关键: 操作员移动 在Pedidos,Produtos和小道之间学习一个过滤棒. 两个东道方 要求随即提出 `DataViewsCopyProvider` 在您的根部( 网格本身) 语言是属于你的,它拒绝发明它们 `table` 包装,如果你 想要保存视图, 因为坚持是一个后端 。 见 通过.md§4b.

复制默认为英语, 日期为运行时的自有地段 。 两者都是 背弃而不是建议——一揽子方案无法了解你的市场,以及 1.x 默认为一个应用程序.

一个读者会说葡萄牙语的东道主 通过了运来的包而不是 重写它: `PT_BR_AUDIT_LABELS` (单位:千美元)`./react`改为: `labels`, (中文(简体) ), `PT_BR_AUDIT_MESSAGES` (单位:千美元)`./server`改为: `messages`。每个都有一个 `EN_US_*` 双向和标签键地图 (单位:千美元)`AUDIT_LABELS`, (中文(简体) ), `AUDIT_MESSAGES`)——通过.md§4.

## 数据库

软件包拥有一个模型(`prisma/audit.prisma`及其移徙。 一个主机 复制部分与软件包自己的脚本:

```bash
node node_modules/@12-apps/audit/scripts/sync-audit-schema.mjs [<host-schema-dir>]
```

迁移是 **重放安全** (单位:千美元)`CREATE TABLE IF NOT EXISTS`, (中文(简体) ), `ADD COLUMN IF NOT EXISTS`, (中文(简体) ), `CREATE INDEX IF NOT EXISTS`),所以一个宿主已经 有一个 `audit_logs` 页:1 `prisma migrate resolve --applied`。 。 。 .

## 这不能防止

很明显,因为一个没有证件的警卫 会被信任过 他们:

-   **Raw SQL 绕过仅附加的守护.** `$executeRaw*` 编号 : `$queryRaw*` 永远 给一个模型代表,所以任何人 持有客户可以 `DELETE FROM audit_logs`这是故意的——这是保留的唯一办法 扫描可以运行 - 这意味着警卫 停止AccIDENT,不是决心 操作员。 该级别的保证是数据库权限或触发,以及 两者都归东道主所有.
-   **另一个连接没有受到影响.** psql, 一个迁移, 第二个客户端已建立 没有扩展.
-   **主机仍然拥有授权.** 许可标识上的包口 宿主决定了,它不计算它们,它不读任何物 演员没有带.

见 [采用.md](https://npmcn.com/package/%4012-apps/audit/ADOPTING.md) 完全合同,假冒对夫妇 设计、装配拒绝和核对表.

## 常见问题

### @12-apps/audit 是什么？

普通、便携式行动审计。 无框架核心\(动作/资源词汇和默认 diff 编辑\),主机挂载后端表面\(\./ server: 交易写入器, 每请求的演员上下文, 创建/ 更新后印出\)

### 如何安装 @12-apps/audit？

在项目目录运行 npm install @12-apps/audit，或使用 pnpm add / yarn add / bun add。

### @12-apps/audit 支持 TypeScript 吗？

该包的 package\.json 没有声明类型入口。需要类型提示时，可以在 npm 上查找社区维护的 @types/12-apps\_\_audit 是否存在。

### @12-apps/audit 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @12-apps/audit 需要什么版本的 Node\.js？

package\.json 的 engines 字段要求 Node\.js \>=22\.0\.0。版本不符时 npm 默认只给出警告，开启 engine-strict 后会拒绝安装。

### @12-apps/audit 可以商用吗？

@12-apps/audit 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @12-apps/audit 的下载量有多少？

根据 npm 官方下载统计，@12-apps/audit 在 2026/10/04 – 2026/11/02 这 30 天内被下载 33,353 次，日均约 1,112 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @12-apps/audit 还在维护吗？

最新版本 5\.11\.0 发布于 2026/10/30（8 天前）；至今共发布 20 个版本，其中过去 12 个月发布了 20 个；首次发布于 2026/08/13。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

