Rocket.Chat 审计 REST API 实践指南:/v1/audit.* 三个端点如何替代 DDP 审计方法
本篇基于 Rocket.Chat 仓库中的变更说明 rest-audit-endpoints.md 展开,讲解三个新增的 /v1/audit.* REST 端点的请求参数、权限模型、限流规则与底层实现。读完后你将能够直接调用这些端点完成消息审计、全渠道(Omnichannel)审计与审计日志查询,并理解它们与旧版 DDP 方法的兼容性边界(预计 9.0.0 移除)。
背景:审计功能从 DDP 方法迁移到 REST API
变更说明(changeset)的核心内容如下:在 @rocket.chat/meteor 的 minor 版本中,新增了三个位于 /v1/audit.* 下的 REST 端点,全部为 EE(Enterprise Edition)功能,要求实例持有 auditing 许可证,覆盖此前仅以 DDP 方法形式存在的审计流程:
| REST 端点 | 替换的 DDP 方法 | 所需权限 |
|---|---|---|
GET /v1/audit.auditions?startDate=&endDate= → { auditions: IAuditLog[] } |
auditGetAuditions |
can-audit-log |
POST /v1/audit.messages(body:{ rid?, startDate, endDate, users, msg, type, visitor?, agent? })→ { messages: IMessage[] } |
auditGetMessages |
can-audit |
POST /v1/audit.omnichannelMessages(body:{ startDate, endDate, users, msg, type, visitor?, agent? })→ { messages: IMessage[] } |
auditGetOmnichannelMessages |
can-audit |
变更说明同时明确了两条兼容性约定:
- 每个端点均限流为 10 次请求 / 60 秒,与 DDP 侧的
DDPRateLimiter规则保持一致; - 每个端点会写入与 DDP 方法完全相同的
AuditLog条目;日期在报文线上序列化为 ISO 字符串;旧 DDP 方法在 9.0.0 之前继续注册,并输出指向新路由的弃用(deprecation)日志。
这符合仓库内 API 端点迁移指南 中描述的演进方向:从旧式 API.v1.addRoute() 迁移到 API.v1.get()/.post() 新写法,配套 AJV 编译的请求/响应校验、统一错误格式与自动生成的 OpenAPI 文档。
三个端点的完整实现集中在 audit.ts(注意其位于 ee/ 目录下,即企业版代码),下文逐端点解析。
端点一:GET /v1/audit.auditions 查询审计日志
该端点返回时间窗口内所有审计操作记录,对应旧 DDP 方法 auditGetAuditions。
请求与响应
- 认证:必须登录(
authRequired: true); - 权限:
can-audit-log(注意与消息审计的can-audit是两个独立权限); - 查询参数(AJV 校验,
additionalProperties: false,即不允许携带额外字段):
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
startDate |
string | 是 | 起始时间,ISO 日期字符串(minLength: 1) |
endDate |
string | 是 | 结束时间,ISO 日期字符串 |
- 响应:
{ success: true, auditions: IAuditLog[] }。IAuditLog的结构定义在 IAuditLog.ts:
export interface IAuditLog extends IRocketChatRecord {
ts: Date;
results: number;
u: Pick<IUser, '_id' | 'username' | 'name' | 'avatarETag'>;
fields: {
type: string;
msg: IMessage['msg'];
startDate?: Date;
endDate?: Date;
rids?: IRoom['_id'][];
room: IRoom['name'];
users?: IUser['username'][];
visitor?: ILivechatVisitor['_id'];
agent?: ILivechatAgent['_id'];
filters?: string;
};
}
请求示例:
curl -X GET "https://<your-instance>/api/v1/audit.auditions?startDate=2026-01-01T00:00:00.000Z&endDate=2026-09-01T00:00:00.000Z" \
-H "Authorization: Bearer <token>"
实现要点
端点注册见 audit.ts:
API.v1.get(
'audit.auditions',
{
authRequired: true,
permissionsRequired: ['can-audit-log'],
query: isAuditAuditionsProps,
license: ['auditing'],
rateLimiterOptions: { numRequestsAllowed: 10, intervalTimeInMS: 60000 },
response: {
200: auditAuditionsResponseSchema,
400: auditErrorResponseSchema,
401: validateUnauthorizedErrorResponse,
403: validateForbiddenErrorResponse,
},
},
async function action() {
const startDate = parseDateOrFail(this.queryParams.startDate, 'startDate');
const endDate = parseDateOrFail(this.queryParams.endDate, 'endDate');
const auditions = await auditGetAuditionsMethod(this.userId, startDate, endDate);
return API.v1.success({ auditions });
},
);
关键行为(源自 functions.ts 中的 auditGetAuditionsMethod):
- 二次权限校验:REST 层用
can-audit-log拦截后,共享函数内还会执行hasPermissionAsync(userId, 'can-audit-log'),不通过则抛Not allowed; - 时间窗口为开区间:查询条件为
ts: { $gt: startDate, $lt: endDate }; - 敏感字段裁剪:返回前通过 Mongo projection 剔除
u.services、u.roles、u.lastLogin、u.statusConnection、u.emails,避免把用户凭据/服务信息暴露给审计日志消费方; - 日期解析:
parseDateOrFail(audit.ts)使用Date.parse,非法日期抛错并被转换为 HTTP 400,错误体为{ success: false, error, errorType }。
端点二:POST /v1/audit.messages 消息审计
该端点按房间、用户、消息内容等条件检索消息,替代 DDP 方法 auditGetMessages,需要 can-audit 权限。
请求体参数
由 AJV schema(auditMessagesSchema,audit.ts)严格校验:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
rid |
string | 否 | 房间 ID;type 非 u 时用于定位目标房间 |
startDate |
string | 是 | ISO 日期字符串 |
endDate |
string | 是 | ISO 日期字符串 |
users |
string[] | 是 | 用户名数组(可为空数组) |
msg |
string | null | 是 | 消息内容关键字;非空时做转义后的忽略大小写正则匹配 |
type |
string | 是 | 搜索类型,语义见下文 u/d/l |
visitor |
string | null | 否 | 全渠道访客 ID |
agent |
string | null | 否 | 客服坐席 ID |
请求示例:
curl -X POST "https://<your-instance>/api/v1/audit.messages" \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"type": "d",
"users": ["alice"],
"startDate": "2026-01-01T00:00:00.000Z",
"endDate": "2026-06-01T00:00:00.000Z",
"msg": "invoice"
}'
实现要点与 type 语义
端点注册见 audit.ts,实际检索逻辑在共享函数 auditGetMessagesMethod。从源码结构看,type 字段驱动三条不同的检索路径:
type === 'u'(按用户全局检索):把users用户名解析为u._id集合后查询u._id $in usersId;同时通过Rooms.findAllPrivateRoomsWithAbacAttributes查出带 ABAC 属性的私密房间并以rid $nin abacRooms排除,保证 ABAC 管控房间的消息不会从这条路径泄露。type === 'd'(私信检索):调用Rooms.findDirectRoomContainingAllUsernames(usernames)定位包含全部指定用户的 DM 房间。- 其他(如
l,全渠道):走getRoomInfoByAuditParams,按visitor/agent在LivechatRooms中查找房间,并执行livechat.applyRoomRestrictions回调附加房间可见性限制;找不到房间时抛Room doesn't exist错误。
通用约束:
- 时间条件为
ts: { $gt: startDate, $lt: endDate }(开区间,含端点日期本身的消息按毫秒精度不含边界时刻); msg过滤先用escapeRegExp转义再做new RegExp(escaped, 'i'),即字面量、忽略大小写匹配,不会把用户输入当正则解释;- 每次成功查询都会向
AuditLog插入一条记录,字段包含msg、users、rids、room、startDate、endDate、type、visitor、agent,并累加Message_Auditing_Panel_Load_Count统计计数器——这正是 changeset 中“写入与 DDP 方法相同的 AuditLog 条目”的落点; - 响应 schema 对
messages数组元素刻意放宽(audit.ts 的注释说明:IMessage的 attachment 联合体是含重叠分支的oneOf,严格$ref会导致带附件的真实消息在响应校验阶段误判 400,故在 attachment schema 增加判别字段前保持宽松)。
端点三:POST /v1/audit.omnichannelMessages 全渠道消息审计
替代 DDP 方法 auditGetOmnichannelMessages,同样是 can-audit 权限。请求体与 audit.messages 几乎一致,唯一区别是没有 rid 字段(见 audit.ts 的 auditOmnichannelMessagesSchema):
{
"startDate": "2026-01-01T00:00:00.000Z",
"endDate": "2026-06-01T00:00:00.000Z",
"users": [],
"msg": "",
"type": "l",
"visitor": "<visitorId>",
"agent": "<agentId>"
}
实现位于 auditGetOmnichannelMessagesMethod,与 audit.messages 的两点差异值得注意:
- 它不走
livechat.applyRoomRestrictions回调(源码注释明确说明:这与原始 DDPauditGetOmnichannelMessages保持一致,访问控制由can-audit权限把关,审计的本意就是覆盖所有全渠道房间,因此不施加单元/可见性限制); - 当
visitor/agent未匹配到任何房间时,rids保持为空数组而非undefined,避免 Mongo 抛出$in needs an array错误。
成功检索后同样写入 AuditLog 条目(room 字段记录为国际化字符串 "Omnichannel")。
限流:REST 与 DDP 双轨一致的 10 次 / 60 秒
三个 REST 端点都显式声明了相同的限流配置:
rateLimiterOptions: { numRequestsAllowed: 10, intervalTimeInMS: 60000 },
路由级限流的注册与执行由 ApiClass.ts 负责(registerRateLimiterForRoute / enforceRateLimitForRoute);从源码结构看,TEST_MODE 环境变量开启时会自动跳过限流注册,方便端到端测试反复调用。
对应的 DDP 侧规则在 methods.ts 中:
DDPRateLimiter.addRule(
{ type: 'method', name: 'auditGetAuditions', userId() { return true; } },
10,
60000,
);
DDP 侧同样以 10 次 / 60000ms 对 auditGetAuditions、auditGetMessages 登记了 DDPRateLimiter 规则,与 REST 侧配额对齐,保证无论走哪条通道,审计查询的访问速率上限一致。
兼容性:DDP 方法保留至 9.0.0 并输出弃用日志
旧的三个 DDP 方法并未删除,而是保留并挂上了弃用标记(methods.ts):
Meteor.methods<ServerMethods>({
async auditGetOmnichannelMessages(params) {
methodDeprecationLogger.method('auditGetOmnichannelMessages', '9.0.0', '/v1/audit.omnichannelMessages');
check(params.startDate, Date);
check(params.endDate, Date);
return auditGetOmnichannelMessagesMethod(Meteor.userId(), params);
},
async auditGetMessages(params) {
methodDeprecationLogger.method('auditGetMessages', '9.0.0', '/v1/audit.messages');
// ...
},
async auditGetAuditions({ startDate, endDate }) {
methodDeprecationLogger.method('auditGetAuditions', '9.0.0', '/v1/audit.auditions');
// ...
},
});
每次调用旧方法都会经过 deprecationWarningLogger:
- 通过
Logger('DeprecationWarning')输出 warn 级别日志,消息形如The method "auditGetAuditions" is deprecated and will be removed on version 9.0.0 (Use the "/v1/audit.auditions" endpoint instead); - 递增
deprecations/deprecationsTotal指标,便于运维侧统计遗留调用量; - 计划移除版本以类型
DeprecationLoggerNextPlannedVersion = '9.0.0'(deprecationWarningLogger.ts)收敛在单处;TEST_MODE=true时该日志会直接抛错,让测试运行尽早暴露对弃用接口的误用。
由于 REST 端点与 DDP 方法最终都调用同一组 auditGet*Method 共享函数,行为(权限校验、检索条件、审计留痕)完全同源。Rocket.Chat 自带前端也已经切到新端点:useAuditMutation.ts 通过 useEndpoint('POST', '/v1/audit.messages') 与 /v1/audit.omnichannelMessages 发起请求,日期在客户端以 toISOString() 序列化为 ISO 字符串后再提交,与 changeset 中“日期在线上为 ISO 字符串”的约定一致。
端到端测试如何验证这些行为
端到端用例 tests/end-to-end/api/audit.ts(仅 EE 构建执行,由 IS_EE 控制)为上述行为提供了可复制的验收清单:
- 401:未携带凭据访问
audit.auditions/audit.messages/audit.omnichannelMessages; - 400:缺少
startDate/endDate或必填 body 字段(users、msg、type),以及日期非法(如'not-a-date'); - 403:
can-audit-log(针对 auditions)或can-audit(针对两个 messages 端点)被收回后请求被拒; - 200 + 结构断言:成功时响应体包含
success: true与auditions/messages数组; - 审计留痕闭环:先查询
audit.auditions记录条数,调用一次audit.messages或audit.omnichannelMessages后再次查询,断言条数增加——直接验证了“每个端点写入 AuditLog 条目”的承诺。
测试中还体现了权限细节:auditor 角色默认只有 can-audit 而无 can-audit-log,测试套件通过 updatePermission('can-audit-log', ['admin', 'auditor']) 临时授权后再验证 auditions 端点。
关键结论
- 三个端点均为 EE 功能(
license: ['auditing']),部署在apps/meteor/ee/下的企业版目录中,社区版实例不可用; - 权限分工明确:查审计日志用
can-audit-log,查审计消息用can-audit,两者在 REST 层与共享函数层各校验一次; - 限流统一为 10 次 / 60 秒,REST(
rateLimiterOptions)与 DDP(DDPRateLimiter.addRule)双通道配额一致; - 时间参数在报文中为 ISO 字符串,实际查询为开区间
$gt/$lt;消息关键字检索是转义后的字面量匹配,忽略大小写; - 旧 DDP 方法在 9.0.0 前继续可用,调用时会在服务端日志与指标中留下弃用记录,集成方应尽快切换到
/v1/audit.*。
主要参考文件:变更说明、REST 端点实现、DDP 方法与限流规则、共享审计函数、IAuditLog 类型、弃用日志器、路由限流、端到端测试、端点迁移指南。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00