strapi-cms-audit-log 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/CHANGELOG.md +212 -0
  2. package/LICENSE +21 -0
  3. package/README.md +1026 -0
  4. package/dist/admin/App-BLt4lqEM.js +1284 -0
  5. package/dist/admin/App-ou5hy99f.mjs +1266 -0
  6. package/dist/admin/en-B0rPiE2W.mjs +87 -0
  7. package/dist/admin/en-BMlJxq3g.js +87 -0
  8. package/dist/admin/index-BYi8OPTw.js +80 -0
  9. package/dist/admin/index-DdmY-p3Q.mjs +81 -0
  10. package/dist/admin/index.js +4 -0
  11. package/dist/admin/index.mjs +4 -0
  12. package/dist/admin/src/components/AuditLogFilters.d.ts +18 -0
  13. package/dist/admin/src/components/AuditLogTable.d.ts +19 -0
  14. package/dist/admin/src/components/ChangeViewer.d.ts +14 -0
  15. package/dist/admin/src/components/JsonViewer.d.ts +20 -0
  16. package/dist/admin/src/components/PluginIcon.d.ts +3 -0
  17. package/dist/admin/src/components/WidgetDiff.d.ts +19 -0
  18. package/dist/admin/src/hooks/useAuditLogs.d.ts +36 -0
  19. package/dist/admin/src/index.d.ts +3 -0
  20. package/dist/admin/src/pages/App.d.ts +11 -0
  21. package/dist/admin/src/pages/AuditLogDetails.d.ts +10 -0
  22. package/dist/admin/src/pages/AuditLogs.d.ts +3 -0
  23. package/dist/admin/src/permissions.d.ts +26 -0
  24. package/dist/admin/src/pluginId.d.ts +2 -0
  25. package/dist/admin/src/types.d.ts +91 -0
  26. package/dist/admin/src/utils/format.d.ts +41 -0
  27. package/dist/admin/src/utils/getTranslation.d.ts +2 -0
  28. package/dist/admin/src/utils/widgets.d.ts +85 -0
  29. package/dist/server/index.js +1856 -0
  30. package/dist/server/index.mjs +1856 -0
  31. package/dist/server/src/bootstrap.d.ts +14 -0
  32. package/dist/server/src/config/index.d.ts +52 -0
  33. package/dist/server/src/constants.d.ts +140 -0
  34. package/dist/server/src/content-types/audit-log/index.d.ts +86 -0
  35. package/dist/server/src/content-types/audit-log/schema.d.ts +141 -0
  36. package/dist/server/src/content-types/index.d.ts +88 -0
  37. package/dist/server/src/controllers/audit-log.d.ts +31 -0
  38. package/dist/server/src/controllers/index.d.ts +42 -0
  39. package/dist/server/src/destroy.d.ts +19 -0
  40. package/dist/server/src/index.d.ts +328 -0
  41. package/dist/server/src/register.d.ts +31 -0
  42. package/dist/server/src/routes/admin.d.ts +26 -0
  43. package/dist/server/src/routes/index.d.ts +19 -0
  44. package/dist/server/src/services/access.d.ts +51 -0
  45. package/dist/server/src/services/audit.d.ts +25 -0
  46. package/dist/server/src/services/config.d.ts +30 -0
  47. package/dist/server/src/services/context.d.ts +33 -0
  48. package/dist/server/src/services/diff.d.ts +48 -0
  49. package/dist/server/src/services/immutability.d.ts +30 -0
  50. package/dist/server/src/services/index.d.ts +137 -0
  51. package/dist/server/src/services/retention.d.ts +22 -0
  52. package/dist/server/src/services/security.d.ts +54 -0
  53. package/dist/server/src/services/snapshot.d.ts +46 -0
  54. package/dist/server/src/services/tracker.d.ts +39 -0
  55. package/dist/server/src/types/index.d.ts +214 -0
  56. package/dist/server/src/utils/json.d.ts +17 -0
  57. package/dist/server/src/utils/paths.d.ts +34 -0
  58. package/dist/server/src/utils/sanitize.d.ts +16 -0
  59. package/package.json +112 -0
@@ -0,0 +1,87 @@
1
+ const en = {
2
+ "plugin.name": "Audit Logs",
3
+ "plugin.description": "Track every change to your content and every security event.",
4
+ "list.subtitle": "{total, plural, =0 {No records} one {# record} other {# records}} of content and security activity",
5
+ "list.search": "Search by document id, user, content type or request id",
6
+ "list.empty": "No audit logs match these filters. Records appear here as soon as content is created, updated, deleted, published or unpublished, and whenever someone signs in, signs out, is refused access, or changes an admin user, role, permission or media file.",
7
+ "list.view": "View details",
8
+ "list.delete": "Delete this record",
9
+ "list.column.createdAt": "Date",
10
+ "list.column.userName": "User",
11
+ "list.column.action": "Action",
12
+ "list.column.contentType": "Content type",
13
+ "list.column.contentDocumentId": "Document ID",
14
+ "list.column.locale": "Locale",
15
+ "list.column.source": "Source",
16
+ "list.column.actions": "Actions",
17
+ "filters.all": "All",
18
+ "filters.action": "Action",
19
+ "filters.contentType": "Content type",
20
+ "filters.user": "User",
21
+ "filters.locale": "Locale",
22
+ "filters.source": "Source",
23
+ "filters.documentId": "Document ID",
24
+ "filters.documentIdPlaceholder": "Exact document id",
25
+ "filters.dateFrom": "From",
26
+ "filters.dateTo": "To",
27
+ "filters.clearDate": "Clear date",
28
+ "filters.clear": "Clear all filters",
29
+ "detail.title": "Audit Log",
30
+ "detail.overview": "Overview",
31
+ "detail.action": "Action",
32
+ "detail.user": "User",
33
+ "detail.userEmail": "User email",
34
+ "detail.date": "Date",
35
+ "detail.contentType": "Content type",
36
+ "detail.documentId": "Document ID",
37
+ "detail.entryId": "Entry ID",
38
+ "detail.locale": "Locale",
39
+ "detail.source": "Source",
40
+ "detail.ipAddress": "IP address",
41
+ "detail.requestId": "Request ID",
42
+ "detail.userAgent": "User agent",
43
+ "detail.snapshots": "Raw snapshots",
44
+ "detail.snapshotsHint": "The document state either side of this operation, with configured sensitive fields removed. A missing snapshot means the state did not exist (a create has no before, a delete has no after) or storage for it is disabled in the plugin configuration.",
45
+ "detail.before": "Before",
46
+ "detail.after": "After",
47
+ "changes.title": "Changes",
48
+ "changes.before": "Before",
49
+ "changes.after": "After",
50
+ "changes.raw": "Raw value for {path}",
51
+ "changes.empty": "No field-level changes were recorded for this operation. This is expected for creates and deletes, and for updates that re-saved a document without modifying it.",
52
+ "json.show": "Show ({count} lines)",
53
+ "json.hide": "Hide",
54
+ "delete.title": "Delete audit record",
55
+ "delete.confirm": "Audit records are evidence of what happened to your content. Deleting one cannot be undone. Are you sure?",
56
+ "delete.action": "Delete",
57
+ "delete.cancel": "Cancel",
58
+ "delete.success": "Audit record deleted.",
59
+ "delete.error": "Could not delete this audit record.",
60
+ "filters.outcome": "Outcome",
61
+ "detail.outcome": "Outcome",
62
+ "detail.metadata": "Event detail",
63
+ "widgets.title": "Widgets",
64
+ "widgets.hint": "Each changed widget is shown twice — once with the data it held before this operation, and once with the data it holds after. Fields that differ are highlighted in both copies.",
65
+ "widgets.before": "Before",
66
+ "widgets.after": "After",
67
+ "widgets.show": "Show",
68
+ "widgets.hide": "Hide",
69
+ "widgets.showUnchanged": "Show unchanged widgets",
70
+ "widgets.hideUnchanged": "Hide unchanged widgets",
71
+ "widgets.showAllFields": "Show {count} more fields",
72
+ "widgets.fieldCount": "{count} fields",
73
+ "widgets.zoneSummary": "{changed} changed of {total}",
74
+ "widgets.allUnchanged": "No widget in this zone changed. {count} unchanged widgets are hidden.",
75
+ "widgets.replaced": "This slot previously held a different widget ({component}).",
76
+ "widgets.noBefore": "This widget did not exist before the change.",
77
+ "widgets.noAfter": "This widget was removed by the change.",
78
+ "widgets.raw": "Raw JSON for slot {index}",
79
+ "widgets.status.changed": "Changed",
80
+ "widgets.status.added": "Added",
81
+ "widgets.status.removed": "Removed",
82
+ "widgets.status.unchanged": "Unchanged",
83
+ "list.column.outcome": "Outcome"
84
+ };
85
+ export {
86
+ en as default
87
+ };
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const en = {
4
+ "plugin.name": "Audit Logs",
5
+ "plugin.description": "Track every change to your content and every security event.",
6
+ "list.subtitle": "{total, plural, =0 {No records} one {# record} other {# records}} of content and security activity",
7
+ "list.search": "Search by document id, user, content type or request id",
8
+ "list.empty": "No audit logs match these filters. Records appear here as soon as content is created, updated, deleted, published or unpublished, and whenever someone signs in, signs out, is refused access, or changes an admin user, role, permission or media file.",
9
+ "list.view": "View details",
10
+ "list.delete": "Delete this record",
11
+ "list.column.createdAt": "Date",
12
+ "list.column.userName": "User",
13
+ "list.column.action": "Action",
14
+ "list.column.contentType": "Content type",
15
+ "list.column.contentDocumentId": "Document ID",
16
+ "list.column.locale": "Locale",
17
+ "list.column.source": "Source",
18
+ "list.column.actions": "Actions",
19
+ "filters.all": "All",
20
+ "filters.action": "Action",
21
+ "filters.contentType": "Content type",
22
+ "filters.user": "User",
23
+ "filters.locale": "Locale",
24
+ "filters.source": "Source",
25
+ "filters.documentId": "Document ID",
26
+ "filters.documentIdPlaceholder": "Exact document id",
27
+ "filters.dateFrom": "From",
28
+ "filters.dateTo": "To",
29
+ "filters.clearDate": "Clear date",
30
+ "filters.clear": "Clear all filters",
31
+ "detail.title": "Audit Log",
32
+ "detail.overview": "Overview",
33
+ "detail.action": "Action",
34
+ "detail.user": "User",
35
+ "detail.userEmail": "User email",
36
+ "detail.date": "Date",
37
+ "detail.contentType": "Content type",
38
+ "detail.documentId": "Document ID",
39
+ "detail.entryId": "Entry ID",
40
+ "detail.locale": "Locale",
41
+ "detail.source": "Source",
42
+ "detail.ipAddress": "IP address",
43
+ "detail.requestId": "Request ID",
44
+ "detail.userAgent": "User agent",
45
+ "detail.snapshots": "Raw snapshots",
46
+ "detail.snapshotsHint": "The document state either side of this operation, with configured sensitive fields removed. A missing snapshot means the state did not exist (a create has no before, a delete has no after) or storage for it is disabled in the plugin configuration.",
47
+ "detail.before": "Before",
48
+ "detail.after": "After",
49
+ "changes.title": "Changes",
50
+ "changes.before": "Before",
51
+ "changes.after": "After",
52
+ "changes.raw": "Raw value for {path}",
53
+ "changes.empty": "No field-level changes were recorded for this operation. This is expected for creates and deletes, and for updates that re-saved a document without modifying it.",
54
+ "json.show": "Show ({count} lines)",
55
+ "json.hide": "Hide",
56
+ "delete.title": "Delete audit record",
57
+ "delete.confirm": "Audit records are evidence of what happened to your content. Deleting one cannot be undone. Are you sure?",
58
+ "delete.action": "Delete",
59
+ "delete.cancel": "Cancel",
60
+ "delete.success": "Audit record deleted.",
61
+ "delete.error": "Could not delete this audit record.",
62
+ "filters.outcome": "Outcome",
63
+ "detail.outcome": "Outcome",
64
+ "detail.metadata": "Event detail",
65
+ "widgets.title": "Widgets",
66
+ "widgets.hint": "Each changed widget is shown twice — once with the data it held before this operation, and once with the data it holds after. Fields that differ are highlighted in both copies.",
67
+ "widgets.before": "Before",
68
+ "widgets.after": "After",
69
+ "widgets.show": "Show",
70
+ "widgets.hide": "Hide",
71
+ "widgets.showUnchanged": "Show unchanged widgets",
72
+ "widgets.hideUnchanged": "Hide unchanged widgets",
73
+ "widgets.showAllFields": "Show {count} more fields",
74
+ "widgets.fieldCount": "{count} fields",
75
+ "widgets.zoneSummary": "{changed} changed of {total}",
76
+ "widgets.allUnchanged": "No widget in this zone changed. {count} unchanged widgets are hidden.",
77
+ "widgets.replaced": "This slot previously held a different widget ({component}).",
78
+ "widgets.noBefore": "This widget did not exist before the change.",
79
+ "widgets.noAfter": "This widget was removed by the change.",
80
+ "widgets.raw": "Raw JSON for slot {index}",
81
+ "widgets.status.changed": "Changed",
82
+ "widgets.status.added": "Added",
83
+ "widgets.status.removed": "Removed",
84
+ "widgets.status.unchanged": "Unchanged",
85
+ "list.column.outcome": "Outcome"
86
+ };
87
+ exports.default = en;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ const jsxRuntime = require("react/jsx-runtime");
3
+ const icons = require("@strapi/icons");
4
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
5
+ const v = glob[path];
6
+ if (v) {
7
+ return typeof v === "function" ? v() : Promise.resolve(v);
8
+ }
9
+ return new Promise((_, reject) => {
10
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
11
+ reject.bind(
12
+ null,
13
+ new Error(
14
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
15
+ )
16
+ )
17
+ );
18
+ });
19
+ };
20
+ const PluginIcon = () => /* @__PURE__ */ jsxRuntime.jsx(icons.ClockCounterClockwise, {});
21
+ const PLUGIN_ID = "audit-log";
22
+ const entity = (action) => ({
23
+ action,
24
+ subject: null,
25
+ id: "",
26
+ actionParameters: {},
27
+ properties: {},
28
+ conditions: []
29
+ });
30
+ const PERMISSIONS = {
31
+ read: [entity(`plugin::${PLUGIN_ID}.read`)],
32
+ delete: [entity(`plugin::${PLUGIN_ID}.delete`)],
33
+ settings: [entity(`plugin::${PLUGIN_ID}.settings`)]
34
+ };
35
+ const getTranslation = (id) => `${PLUGIN_ID}.${id}`;
36
+ const plugin = {
37
+ register(app) {
38
+ app.addMenuLink({
39
+ to: "audit-logs",
40
+ icon: PluginIcon,
41
+ intlLabel: {
42
+ id: `${PLUGIN_ID}.plugin.name`,
43
+ defaultMessage: "Audit Logs"
44
+ },
45
+ permissions: PERMISSIONS.read,
46
+ Component: () => Promise.resolve().then(() => require("./App-BLt4lqEM.js")).then((mod) => ({ default: mod.App }))
47
+ });
48
+ app.registerPlugin({
49
+ id: PLUGIN_ID,
50
+ name: PLUGIN_ID,
51
+ isReady: true
52
+ });
53
+ },
54
+ /**
55
+ * Loads a translation bundle per admin locale, namespacing every key with the
56
+ * plugin id so it cannot collide with the admin's own messages. A locale with
57
+ * no bundle resolves to an empty object, and React Intl falls back to each
58
+ * message's `defaultMessage`.
59
+ */
60
+ registerTrads({ locales }) {
61
+ return Promise.all(
62
+ locales.map(async (locale) => {
63
+ try {
64
+ const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => Promise.resolve().then(() => require("./en-BMlJxq3g.js")) }), `./translations/${locale}.json`, 3);
65
+ const namespaced = {};
66
+ for (const key of Object.keys(data)) {
67
+ namespaced[getTranslation(key)] = data[key];
68
+ }
69
+ return { data: namespaced, locale };
70
+ } catch {
71
+ return { data: {}, locale };
72
+ }
73
+ })
74
+ );
75
+ }
76
+ };
77
+ exports.PERMISSIONS = PERMISSIONS;
78
+ exports.PLUGIN_ID = PLUGIN_ID;
79
+ exports.getTranslation = getTranslation;
80
+ exports.plugin = plugin;
@@ -0,0 +1,81 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { ClockCounterClockwise } from "@strapi/icons";
3
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
4
+ const v = glob[path];
5
+ if (v) {
6
+ return typeof v === "function" ? v() : Promise.resolve(v);
7
+ }
8
+ return new Promise((_, reject) => {
9
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
10
+ reject.bind(
11
+ null,
12
+ new Error(
13
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
14
+ )
15
+ )
16
+ );
17
+ });
18
+ };
19
+ const PluginIcon = () => /* @__PURE__ */ jsx(ClockCounterClockwise, {});
20
+ const PLUGIN_ID = "audit-log";
21
+ const entity = (action) => ({
22
+ action,
23
+ subject: null,
24
+ id: "",
25
+ actionParameters: {},
26
+ properties: {},
27
+ conditions: []
28
+ });
29
+ const PERMISSIONS = {
30
+ read: [entity(`plugin::${PLUGIN_ID}.read`)],
31
+ delete: [entity(`plugin::${PLUGIN_ID}.delete`)],
32
+ settings: [entity(`plugin::${PLUGIN_ID}.settings`)]
33
+ };
34
+ const getTranslation = (id) => `${PLUGIN_ID}.${id}`;
35
+ const plugin = {
36
+ register(app) {
37
+ app.addMenuLink({
38
+ to: "audit-logs",
39
+ icon: PluginIcon,
40
+ intlLabel: {
41
+ id: `${PLUGIN_ID}.plugin.name`,
42
+ defaultMessage: "Audit Logs"
43
+ },
44
+ permissions: PERMISSIONS.read,
45
+ Component: () => import("./App-ou5hy99f.mjs").then((mod) => ({ default: mod.App }))
46
+ });
47
+ app.registerPlugin({
48
+ id: PLUGIN_ID,
49
+ name: PLUGIN_ID,
50
+ isReady: true
51
+ });
52
+ },
53
+ /**
54
+ * Loads a translation bundle per admin locale, namespacing every key with the
55
+ * plugin id so it cannot collide with the admin's own messages. A locale with
56
+ * no bundle resolves to an empty object, and React Intl falls back to each
57
+ * message's `defaultMessage`.
58
+ */
59
+ registerTrads({ locales }) {
60
+ return Promise.all(
61
+ locales.map(async (locale) => {
62
+ try {
63
+ const { default: data } = await __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/en.json": () => import("./en-B0rPiE2W.mjs") }), `./translations/${locale}.json`, 3);
64
+ const namespaced = {};
65
+ for (const key of Object.keys(data)) {
66
+ namespaced[getTranslation(key)] = data[key];
67
+ }
68
+ return { data: namespaced, locale };
69
+ } catch {
70
+ return { data: {}, locale };
71
+ }
72
+ })
73
+ );
74
+ }
75
+ };
76
+ export {
77
+ PLUGIN_ID as P,
78
+ PERMISSIONS as a,
79
+ getTranslation as g,
80
+ plugin as p
81
+ };
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
3
+ const index = require("./index-BYi8OPTw.js");
4
+ exports.default = index.plugin;
@@ -0,0 +1,4 @@
1
+ import { p } from "./index-DdmY-p3Q.mjs";
2
+ export {
3
+ p as default
4
+ };
@@ -0,0 +1,18 @@
1
+ import { AuditFilterOptions, AuditListQuery } from '../types';
2
+ import * as React from 'react';
3
+ interface AuditLogFiltersProps {
4
+ options: AuditFilterOptions;
5
+ query: AuditListQuery;
6
+ onChange: (patch: Partial<AuditListQuery>) => void;
7
+ onClear: () => void;
8
+ }
9
+ /**
10
+ * Filter bar for the audit list.
11
+ *
12
+ * Every control writes to the URL rather than to local state, which is what
13
+ * makes a filtered view shareable — pasting the link into a ticket reproduces
14
+ * exactly what the reporter was looking at. The parent turns that query string
15
+ * straight into the server request; nothing is filtered in the browser.
16
+ */
17
+ declare const AuditLogFilters: ({ options, query, onChange, onClear }: AuditLogFiltersProps) => React.JSX.Element;
18
+ export { AuditLogFilters };
@@ -0,0 +1,19 @@
1
+ import { AuditLog } from '../types';
2
+ import * as React from 'react';
3
+ interface AuditLogTableProps {
4
+ logs: AuditLog[];
5
+ sort: string;
6
+ onSortChange: (sort: string) => void;
7
+ onView: (log: AuditLog) => void;
8
+ onDelete: ((log: AuditLog) => void) | null;
9
+ isLoading: boolean;
10
+ }
11
+ /**
12
+ * The audit list.
13
+ *
14
+ * Sorting is a link back to the parent's query state rather than an in-memory
15
+ * `Array.sort`: the table only ever holds one page, so sorting it locally would
16
+ * reorder twenty rows and quietly lie about the other ten thousand.
17
+ */
18
+ declare const AuditLogTable: ({ logs, sort, onSortChange, onView, onDelete, isLoading, }: AuditLogTableProps) => React.JSX.Element;
19
+ export { AuditLogTable };
@@ -0,0 +1,14 @@
1
+ import { AuditChangeSet } from '../types';
2
+ import * as React from 'react';
3
+ interface ChangeViewerProps {
4
+ changes: AuditChangeSet | null;
5
+ }
6
+ /**
7
+ * Renders the field-level diff of an audit record.
8
+ *
9
+ * Paths arrive already flattened by the server's diff engine
10
+ * (`seo.metaTitle`, `blocks[2].heading`), so nesting is conveyed by the path
11
+ * itself and the list stays flat and scannable however deep the change was.
12
+ */
13
+ declare const ChangeViewer: ({ changes }: ChangeViewerProps) => React.JSX.Element;
14
+ export { ChangeViewer };
@@ -0,0 +1,20 @@
1
+ import * as React from 'react';
2
+ interface JsonViewerProps {
3
+ value: unknown;
4
+ /** Rendered above the block; also the label of the expand/collapse control. */
5
+ label: string;
6
+ /** Start expanded. Small payloads read better open. */
7
+ defaultOpen?: boolean;
8
+ /** Lines shown before the block scrolls internally. */
9
+ maxHeight?: string;
10
+ }
11
+ /**
12
+ * Collapsible, monospaced JSON block.
13
+ *
14
+ * Collapsed by default because a `before`/`after` snapshot of a page with a
15
+ * dynamic zone is thousands of lines, and a detail page that opens with a wall
16
+ * of JSON buries the thing the reader came for — the diff. The raw payload is
17
+ * still one click away, which matters when the diff summary is not enough.
18
+ */
19
+ declare const JsonViewer: ({ value, label, defaultOpen, maxHeight }: JsonViewerProps) => React.JSX.Element;
20
+ export { JsonViewer };
@@ -0,0 +1,3 @@
1
+ /** Sidebar icon. A clock running backwards — history, which is what a log is. */
2
+ declare const PluginIcon: () => import("react").JSX.Element;
3
+ export { PluginIcon };
@@ -0,0 +1,19 @@
1
+ import * as React from 'react';
2
+ interface WidgetDiffProps {
3
+ before: Record<string, unknown> | null;
4
+ after: Record<string, unknown> | null;
5
+ }
6
+ /**
7
+ * Per-widget before/after view of a dynamic zone.
8
+ *
9
+ * The flat `changes` list one section up answers "which fields moved". This
10
+ * answers the question an editor actually arrives with — "what did that widget
11
+ * look like before, and what does it look like now" — by rendering each changed
12
+ * widget twice, once per side, in full.
13
+ *
14
+ * Zones are found structurally rather than by name (see `utils/widgets.ts`), so
15
+ * this works on `widgets`, on `blocks`, and on any zone added to any content
16
+ * type later, with nothing to register.
17
+ */
18
+ declare const WidgetDiff: ({ before, after }: WidgetDiffProps) => React.JSX.Element | null;
19
+ export { WidgetDiff };
@@ -0,0 +1,36 @@
1
+ import { AuditFilterOptions, AuditLog, AuditPagination } from '../types';
2
+ /**
3
+ * Fetches one page of audit logs.
4
+ *
5
+ * The whole query — filters, search, sort, pagination — is forwarded to the
6
+ * server and applied in SQL. Nothing is filtered in the browser, because the
7
+ * audit table is the one table in a Strapi project that grows without bound;
8
+ * fetching it to filter it client-side would work fine in development and fall
9
+ * over the first time it matters.
10
+ */
11
+ export declare const useAuditLogs: (search: string) => {
12
+ data: AuditLog[];
13
+ pagination: AuditPagination;
14
+ isLoading: boolean;
15
+ error: string | null;
16
+ refresh: () => void;
17
+ };
18
+ /**
19
+ * Loads the distinct values behind the filter dropdowns.
20
+ *
21
+ * Fetched once per mount rather than derived from the current page: a filter
22
+ * list built from twenty visible rows would offer only the options the user can
23
+ * already see.
24
+ */
25
+ export declare const useAuditFilterOptions: () => {
26
+ options: AuditFilterOptions;
27
+ isLoading: boolean;
28
+ };
29
+ /** Loads one audit record for the detail page. */
30
+ export declare const useAuditLog: (id: string | undefined) => {
31
+ data: AuditLog | null;
32
+ isLoading: boolean;
33
+ error: string | null;
34
+ };
35
+ /** Deletes one record. Requires `plugin::audit-log.delete` on the server. */
36
+ export declare const useDeleteAuditLog: () => (id: number) => Promise<void>;
@@ -0,0 +1,3 @@
1
+ import { StrapiApp } from '@strapi/strapi/admin';
2
+ declare const plugin: StrapiApp['appPlugins'][string];
3
+ export default plugin;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Routes behind the `Audit Logs` sidebar entry, mounted at `/admin/audit-logs`.
3
+ *
4
+ * `Page.Protect` re-checks `plugin::audit-log.read` here rather than relying on
5
+ * the hidden sidebar link. Hiding a menu item stops it being *offered*; it does
6
+ * nothing about a bookmarked or shared URL. The server enforces the same
7
+ * permission on every route, so this is the third of three checks and the only
8
+ * one whose job is to show a friendly "no access" page instead of a raw 403.
9
+ */
10
+ declare const App: () => import("react").JSX.Element;
11
+ export { App };
@@ -0,0 +1,10 @@
1
+ /**
2
+ * A single audit record in full.
3
+ *
4
+ * Laid out as metadata first, then the diff, then the raw snapshots. That order
5
+ * matches how the page is actually read: "who and when" answers most questions
6
+ * outright, the diff answers most of the rest, and the raw JSON is there for the
7
+ * cases where neither does.
8
+ */
9
+ declare const AuditLogDetails: () => import("react").JSX.Element;
10
+ export { AuditLogDetails };
@@ -0,0 +1,3 @@
1
+ import * as React from 'react';
2
+ declare const AuditLogs: () => React.JSX.Element;
3
+ export { AuditLogs };
@@ -0,0 +1,26 @@
1
+ export declare const PERMISSIONS: {
2
+ read: {
3
+ action: string;
4
+ subject: null;
5
+ id: string;
6
+ actionParameters: {};
7
+ properties: {};
8
+ conditions: never[];
9
+ }[];
10
+ delete: {
11
+ action: string;
12
+ subject: null;
13
+ id: string;
14
+ actionParameters: {};
15
+ properties: {};
16
+ conditions: never[];
17
+ }[];
18
+ settings: {
19
+ action: string;
20
+ subject: null;
21
+ id: string;
22
+ actionParameters: {};
23
+ properties: {};
24
+ conditions: never[];
25
+ }[];
26
+ };
@@ -0,0 +1,2 @@
1
+ /** Must match `strapi.name` in package.json and `PLUGIN_ID` on the server. */
2
+ export declare const PLUGIN_ID = "audit-log";
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Wire types for the plugin's admin API.
3
+ *
4
+ * These describe JSON as it arrives over HTTP, which is not quite the server's
5
+ * own model: `Date` columns are ISO strings here, and the compiled ignore-list
6
+ * matchers on `AuditConfig` never cross the wire. The canonical definitions live
7
+ * in `server/src/types` and are re-exported from
8
+ * `strapi-plugin-audit-log/strapi-server`; see the note there on why the two
9
+ * halves cannot share one module.
10
+ */
11
+ export type AuditAction = 'create' | 'update' | 'delete' | 'publish' | 'unpublish';
12
+ /**
13
+ * Operations recorded outside the Document Service: authentication, denied
14
+ * requests, and changes to the admin users, roles, permissions and media that
15
+ * govern everything else.
16
+ */
17
+ export type AuditSecurityAction = 'login.success' | 'login.failed' | 'logout' | 'access.denied' | 'admin.user.create' | 'admin.user.update' | 'admin.user.delete' | 'admin.role.create' | 'admin.role.update' | 'admin.role.delete' | 'admin.permission.create' | 'admin.permission.update' | 'admin.permission.delete' | 'media.create' | 'media.update' | 'media.delete' | 'media-folder.create' | 'media-folder.update' | 'media-folder.delete';
18
+ export type AuditAnyAction = AuditAction | AuditSecurityAction;
19
+ /** Whether the recorded attempt succeeded. Only security actions are ever `failure`. */
20
+ export type AuditOutcome = 'success' | 'failure';
21
+ export type AuditSource = 'admin' | 'api' | 'system' | 'cron' | 'migration' | 'unknown';
22
+ export interface AuditChange {
23
+ from: unknown;
24
+ to: unknown;
25
+ }
26
+ export type AuditChangeSet = Record<string, AuditChange>;
27
+ export interface AuditLog {
28
+ id: number;
29
+ documentId: string;
30
+ action: AuditAnyAction | string;
31
+ contentType: string;
32
+ contentTypeDisplayName: string | null;
33
+ contentDocumentId: string | null;
34
+ contentId: string | null;
35
+ locale: string | null;
36
+ userId: string | null;
37
+ userEmail: string | null;
38
+ userName: string | null;
39
+ source: AuditSource | string;
40
+ ipAddress: string | null;
41
+ userAgent: string | null;
42
+ requestId: string | null;
43
+ changes: AuditChangeSet | null;
44
+ before: Record<string, unknown> | null;
45
+ after: Record<string, unknown> | null;
46
+ outcome: AuditOutcome | null;
47
+ /** Action-specific detail: the reason a login failed, the path of a denied request, an upload's filename. */
48
+ metadata: Record<string, unknown> | null;
49
+ createdAt: string;
50
+ updatedAt: string;
51
+ }
52
+ export interface AuditPagination {
53
+ page: number;
54
+ pageSize: number;
55
+ pageCount: number;
56
+ total: number;
57
+ }
58
+ export interface AuditLogListResponse {
59
+ results: AuditLog[];
60
+ pagination: AuditPagination;
61
+ }
62
+ export interface AuditFilterOptions {
63
+ contentTypes: Array<{
64
+ uid: string;
65
+ displayName: string;
66
+ }>;
67
+ users: Array<{
68
+ userId: string;
69
+ label: string;
70
+ }>;
71
+ locales: string[];
72
+ actions: string[];
73
+ sources: string[];
74
+ outcomes: string[];
75
+ }
76
+ /** Query-string state owned by the list page. Mirrors what the server accepts. */
77
+ export interface AuditListQuery {
78
+ page?: string | number;
79
+ pageSize?: string | number;
80
+ sort?: string;
81
+ action?: string;
82
+ contentType?: string;
83
+ userId?: string;
84
+ locale?: string;
85
+ source?: string;
86
+ outcome?: string;
87
+ contentDocumentId?: string;
88
+ dateFrom?: string;
89
+ dateTo?: string;
90
+ _q?: string;
91
+ }