Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
982fbeb
chore: migrate audit DDP methods to REST endpoints
ggazzo May 29, 2026
87e48ef
fix: address TS errors in audit batch
ggazzo May 29, 2026
4fac9a7
chore(api): rename audit.omnichannel.messages to audit.omnichannelMes…
ggazzo Jul 2, 2026
141af29
chore: restore hasPermissionAsync import path after Phase 5 rebase
ggazzo Jul 13, 2026
f91deb4
chore: fix audit functions import order
ggazzo Jul 13, 2026
92fdcf5
test(api): cover audit.auditions/messages/omnichannelMessages REST en…
ggazzo Jul 14, 2026
a4d721d
fix(api): correct audit import paths after develop reorg (statistics,…
ggazzo Jul 16, 2026
ecd381b
chore: fix import order and prettier formatting
ggazzo Jul 17, 2026
aa451b6
fix(api): restore parseDateOrFail helper dropped during rebase
ggazzo Jul 17, 2026
bc8c6c8
docs(audit): document why omnichannel audit skips livechat room restr…
ggazzo Jul 17, 2026
680f2c1
chore(audit): drop stale DDP deprecation warning from shared audit logic
ggazzo Jul 17, 2026
21d508b
test(audit): cover invalid dates + assert audit log entry is persisted
ggazzo Jul 17, 2026
3d3d56f
fix(audit): deserialize audit REST payload instead of force-casting
ggazzo Jul 17, 2026
dbe94cb
fix(audit): keep omnichannel audit rids an array to avoid $in: undefined
ggazzo Jul 17, 2026
ed0c1be
chore(audit): TODO for dropped omnichannel visitor/agent filters
ggazzo Jul 17, 2026
fc2bf62
test(audit): grant can-audit-log to auditor for audit.auditions cases
ggazzo Jul 17, 2026
926531f
chore(api): declare 401/403 response validators on audit endpoints
ggazzo Jul 24, 2026
899bce5
chore(api): tighten audit.auditions response schema (IAuditLog)
ggazzo Jul 24, 2026
dc76e17
fix(api): allow null in audit.auditions fields schema
ggazzo Jul 24, 2026
22b3f3d
fix(audit): stop persisting undefined audit fields as null
ggazzo Jul 24, 2026
29ff63a
fix input schema
sampaiodiego Jul 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/ddp-migrate-batch6-audit-callers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@rocket.chat/meteor': patch
---

Migrated the audit panel (`AuditLogTable`, `useAuditMutation`) from the three `auditGet*` DDP methods to the new `/v1/audit.*` REST endpoints. DDP methods stay registered with deprecation logs pointing at the new routes until 9.0.0.
11 changes: 11 additions & 0 deletions .changeset/rest-audit-endpoints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
'@rocket.chat/meteor': minor
---

Added three new REST endpoints under `/v1/audit.*` (EE-only, requires the `auditing` license) covering the audit flows that previously only existed as DDP methods:

- `GET /v1/audit.auditions?startDate=&endDate=` → `{ auditions: IAuditLog[] }` (replaces `auditGetAuditions`, `can-audit-log`)
- `POST /v1/audit.messages` body `{ rid?, startDate, endDate, users, msg, type, visitor?, agent? }` → `{ messages: IMessage[] }` (replaces `auditGetMessages`, `can-audit`)
- `POST /v1/audit.omnichannelMessages` body `{ startDate, endDate, users, msg, type, visitor?, agent? }` → `{ messages: IMessage[] }` (replaces `auditGetOmnichannelMessages`, `can-audit`)

Each endpoint is rate-limited at 10 requests / 60s (matching the DDP `DDPRateLimiter` rules) and writes the same `AuditLog` entry the DDP methods produced. Dates are serialized as ISO strings on the wire. The DDP methods remain registered with deprecation logs pointing at the new routes until 9.0.0.
26 changes: 22 additions & 4 deletions apps/meteor/client/views/audit/components/AuditLogTable.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { IAuditLog } from '@rocket.chat/core-typings';
import { Field, FieldLabel, FieldRow } from '@rocket.chat/fuselage';
import { GenericTable, GenericTableHeaderCell, GenericTableBody, GenericTableLoadingRow, GenericTableHeader } from '@rocket.chat/ui-client';
import { useTranslation, useMethod } from '@rocket.chat/ui-contexts';
import { useTranslation, useEndpoint } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import { useState } from 'react';

Expand All @@ -18,14 +19,31 @@ const AuditLogTable = () => {
end: createEndOfToday(),
}));

const getAudits = useMethod('auditGetAuditions');
const getAudits = useEndpoint('GET', '/v1/audit.auditions');

const { data, isLoading, isSuccess } = useQuery({
queryKey: ['audits', dateRange],

queryFn: async () => {
queryFn: async (): Promise<IAuditLog[]> => {
const { start, end } = dateRange;
return getAudits({ startDate: start ?? new Date(0), endDate: end ?? new Date() });
const { auditions } = await getAudits({
startDate: (start ?? new Date(0)).toISOString(),
endDate: (end ?? new Date()).toISOString(),
});
// the REST payload serializes Date fields to strings; deserialize back to Date so the entry
// components can keep their IAuditLog (Date) typings instead of force-casting per row
return auditions.map(
(audition): IAuditLog => ({
...audition,
ts: new Date(audition.ts),
_updatedAt: new Date(audition._updatedAt),
fields: {
...audition.fields,
startDate: audition.fields.startDate ? new Date(audition.fields.startDate) : undefined,
endDate: audition.fields.endDate ? new Date(audition.fields.endDate) : undefined,
},
}),
);
},
meta: {
apiErrorToastMessage: true,
Expand Down
31 changes: 20 additions & 11 deletions apps/meteor/client/views/audit/hooks/useAuditMutation.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,48 @@
import type { IAuditLog } from '@rocket.chat/core-typings';
import { useMethod } from '@rocket.chat/ui-contexts';
import type { IAuditLog, IMessage } from '@rocket.chat/core-typings';
import { useEndpoint } from '@rocket.chat/ui-contexts';
import { useMutation } from '@tanstack/react-query';

import type { AuditFields } from './useAuditForm';
import { mapMessageFromApi } from '../../../lib/utils/mapMessageFromApi';

export const useAuditMutation = (type: IAuditLog['fields']['type']) => {
const getAuditMessages = useMethod('auditGetMessages');
const getOmnichannelAuditMessages = useMethod('auditGetOmnichannelMessages');
const getAuditMessages = useEndpoint('POST', '/v1/audit.messages');
const getOmnichannelAuditMessages = useEndpoint('POST', '/v1/audit.omnichannelMessages');

return useMutation({
mutationKey: ['audit'] as const,

mutationFn: async ({ msg, dateRange, rid, users, visitor, agent }: AuditFields) => {
mutationFn: async ({ msg, dateRange, rid, users, visitor, agent }: AuditFields): Promise<IMessage[]> => {
const startDate = (dateRange.start ?? new Date(0)).toISOString();
const endDate = (dateRange.end ?? new Date()).toISOString();

if (type === 'l') {
return getOmnichannelAuditMessages({
const { messages } = await getOmnichannelAuditMessages({
type,
msg,
startDate: dateRange.start ?? new Date(0),
endDate: dateRange.end ?? new Date(),
startDate,
endDate,
users,
// TODO: the Omnichannel audit form collects visitor/agent (required fields) but they are
// dropped here — carried over from the original DDP flow. Forwarding them would let the
// omnichannel audit filter by the selected visitor/agent. Left for investigation.
visitor: '',
agent: '',
Comment thread
coderabbitai[bot] marked this conversation as resolved.
});
return messages.map((message) => mapMessageFromApi(message));
}

return getAuditMessages({
const { messages } = await getAuditMessages({
type,
msg,
startDate: dateRange.start ?? new Date(0),
endDate: dateRange.end ?? new Date(),
startDate,
endDate,
rid,
users,
visitor,
agent,
});
return messages.map((message) => mapMessageFromApi(message));
},
});
};
Loading
Loading