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.
- package/CHANGELOG.md +212 -0
- package/LICENSE +21 -0
- package/README.md +1026 -0
- package/dist/admin/App-BLt4lqEM.js +1284 -0
- package/dist/admin/App-ou5hy99f.mjs +1266 -0
- package/dist/admin/en-B0rPiE2W.mjs +87 -0
- package/dist/admin/en-BMlJxq3g.js +87 -0
- package/dist/admin/index-BYi8OPTw.js +80 -0
- package/dist/admin/index-DdmY-p3Q.mjs +81 -0
- package/dist/admin/index.js +4 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/admin/src/components/AuditLogFilters.d.ts +18 -0
- package/dist/admin/src/components/AuditLogTable.d.ts +19 -0
- package/dist/admin/src/components/ChangeViewer.d.ts +14 -0
- package/dist/admin/src/components/JsonViewer.d.ts +20 -0
- package/dist/admin/src/components/PluginIcon.d.ts +3 -0
- package/dist/admin/src/components/WidgetDiff.d.ts +19 -0
- package/dist/admin/src/hooks/useAuditLogs.d.ts +36 -0
- package/dist/admin/src/index.d.ts +3 -0
- package/dist/admin/src/pages/App.d.ts +11 -0
- package/dist/admin/src/pages/AuditLogDetails.d.ts +10 -0
- package/dist/admin/src/pages/AuditLogs.d.ts +3 -0
- package/dist/admin/src/permissions.d.ts +26 -0
- package/dist/admin/src/pluginId.d.ts +2 -0
- package/dist/admin/src/types.d.ts +91 -0
- package/dist/admin/src/utils/format.d.ts +41 -0
- package/dist/admin/src/utils/getTranslation.d.ts +2 -0
- package/dist/admin/src/utils/widgets.d.ts +85 -0
- package/dist/server/index.js +1856 -0
- package/dist/server/index.mjs +1856 -0
- package/dist/server/src/bootstrap.d.ts +14 -0
- package/dist/server/src/config/index.d.ts +52 -0
- package/dist/server/src/constants.d.ts +140 -0
- package/dist/server/src/content-types/audit-log/index.d.ts +86 -0
- package/dist/server/src/content-types/audit-log/schema.d.ts +141 -0
- package/dist/server/src/content-types/index.d.ts +88 -0
- package/dist/server/src/controllers/audit-log.d.ts +31 -0
- package/dist/server/src/controllers/index.d.ts +42 -0
- package/dist/server/src/destroy.d.ts +19 -0
- package/dist/server/src/index.d.ts +328 -0
- package/dist/server/src/register.d.ts +31 -0
- package/dist/server/src/routes/admin.d.ts +26 -0
- package/dist/server/src/routes/index.d.ts +19 -0
- package/dist/server/src/services/access.d.ts +51 -0
- package/dist/server/src/services/audit.d.ts +25 -0
- package/dist/server/src/services/config.d.ts +30 -0
- package/dist/server/src/services/context.d.ts +33 -0
- package/dist/server/src/services/diff.d.ts +48 -0
- package/dist/server/src/services/immutability.d.ts +30 -0
- package/dist/server/src/services/index.d.ts +137 -0
- package/dist/server/src/services/retention.d.ts +22 -0
- package/dist/server/src/services/security.d.ts +54 -0
- package/dist/server/src/services/snapshot.d.ts +46 -0
- package/dist/server/src/services/tracker.d.ts +39 -0
- package/dist/server/src/types/index.d.ts +214 -0
- package/dist/server/src/utils/json.d.ts +17 -0
- package/dist/server/src/utils/paths.d.ts +34 -0
- package/dist/server/src/utils/sanitize.d.ts +16 -0
- 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,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,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,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,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,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
|
+
}
|