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,14 @@
1
+ import { Core } from '@strapi/strapi';
2
+ /**
3
+ * Wires the plugin up once Strapi is fully loaded.
4
+ *
5
+ * Order matters here. The immutability guard is registered *before* the tracker
6
+ * so that it sits earlier in the Document Service middleware chain: a forbidden
7
+ * write to the audit table is rejected before the tracker has done any work for
8
+ * it. Both are registered in `bootstrap` rather than `register` because
9
+ * `strapi.documents` is not available until the content types have loaded.
10
+ */
11
+ declare const bootstrap: ({ strapi }: {
12
+ strapi: Core.Strapi;
13
+ }) => void;
14
+ export default bootstrap;
@@ -0,0 +1,52 @@
1
+ import { AuditUserConfig } from '../types';
2
+ /**
3
+ * Defaults merged with the consumer's `config/plugins.ts` block.
4
+ *
5
+ * ## Why the collection-valued keys default to `null`
6
+ *
7
+ * Strapi merges plugin config with lodash's `defaultsDeep`, which merges arrays
8
+ * *element-wise*. With `ignoredFields: ['password', 'token', 'apiKey', 'secret']`
9
+ * as a literal default, a consumer writing `ignoredFields: ['internalNote']`
10
+ * would silently end up with `['internalNote', 'token', 'apiKey', 'secret']` —
11
+ * their first element replaced, the rest of ours retained. That is a trap, and a
12
+ * dangerous one for a redaction list.
13
+ *
14
+ * So every array/selector option defaults to `null` here, meaning "not set", and
15
+ * the real defaults are applied in `services/config.ts`. `null` is not an array,
16
+ * so `defaultsDeep` replaces it wholesale and the consumer gets exactly the list
17
+ * they wrote. `additionalIgnoredFields` exists for the common case of wanting
18
+ * ours *plus* a few of their own.
19
+ */
20
+ declare const _default: {
21
+ default: {
22
+ enabled: boolean;
23
+ actions: null;
24
+ contentTypes: null;
25
+ ignoredContentTypes: null;
26
+ ignoredFields: null;
27
+ additionalIgnoredFields: null;
28
+ ignoredChangeFields: null;
29
+ storeBefore: boolean;
30
+ storeAfter: boolean;
31
+ storeChanges: boolean;
32
+ retentionDays: number;
33
+ retentionCron: string;
34
+ failOnAuditError: boolean;
35
+ writeMode: string;
36
+ maxPopulateDepth: number;
37
+ maxSnapshotBytes: number;
38
+ auditSystemOperations: boolean;
39
+ securityEvents: null;
40
+ forwardToLogger: boolean;
41
+ forwardLogLevel: string;
42
+ };
43
+ /**
44
+ * Runs once at boot, before anything else touches the config. Throwing here
45
+ * fails startup with a clear message, which is far kinder than a plugin that
46
+ * silently audits nothing because of a typo.
47
+ */
48
+ validator(config: AuditUserConfig & {
49
+ enabled?: boolean;
50
+ }): void;
51
+ };
52
+ export default _default;
@@ -0,0 +1,140 @@
1
+ import { AuditAction, AuditSecurityAction } from './types';
2
+ /** Plugin id. Must match `strapi.name` in package.json — it namespaces routes, permissions and config. */
3
+ export declare const PLUGIN_ID = "audit-log";
4
+ /** UID of the plugin's own collection type. */
5
+ export declare const AUDIT_LOG_UID = "plugin::audit-log.audit-log";
6
+ export declare const ALL_ACTIONS: AuditAction[];
7
+ /** RBAC action uids, as they appear once namespaced by the admin permission provider. */
8
+ export declare const PERMISSIONS: {
9
+ readonly read: "plugin::audit-log.read";
10
+ readonly delete: "plugin::audit-log.delete";
11
+ readonly settings: "plugin::audit-log.settings";
12
+ };
13
+ /**
14
+ * Fields never written to `before`, `after` or `changes`.
15
+ *
16
+ * Matched by leaf name at any depth and case-insensitively, so `*token*` covers
17
+ * `token`, `accessToken`, `seo.internalToken` and `blocks[0].refreshTokenHash`
18
+ * alike. Substring patterns rather than an exhaustive list of exact names
19
+ * because the list can only ever be a guess at what a project calls its secrets:
20
+ * `internalToken` and `stripeSecretKey` are the names that actually turn up, and
21
+ * an exact-match list silently stores both.
22
+ *
23
+ * The trade is deliberate. `*token*` also redacts a field named `tokenCount`,
24
+ * which costs one uninteresting value in an audit record. Missing a credential
25
+ * writes it, in clear, into a table designed to be kept for a year.
26
+ *
27
+ * Replaced wholesale (not merged) when a consumer sets `ignoredFields`; use
28
+ * `additionalIgnoredFields` to extend rather than replace.
29
+ */
30
+ export declare const DEFAULT_IGNORED_FIELDS: string[];
31
+ /**
32
+ * Fields excluded from `changes` only.
33
+ *
34
+ * They still appear in `before`/`after` — they are real state — but they change
35
+ * on every single write, so surfacing them as "changes" buries the edit the
36
+ * editor actually made.
37
+ */
38
+ export declare const DEFAULT_IGNORED_CHANGE_FIELDS: string[];
39
+ /**
40
+ * Attributes that carry no auditable content and are stripped from a snapshot.
41
+ *
42
+ * `id`, `documentId` and `locale` are recorded as dedicated columns instead, so
43
+ * repeating them inside the JSON blobs is pure noise. `publishedAt` is fetched —
44
+ * the tracker needs it to tell a draft row from a published one — but not
45
+ * stored: the record's own `action` already says whether this was a publish or
46
+ * an unpublish, and leaving it in makes every single-field update snapshot
47
+ * contain a field the editor never touched.
48
+ */
49
+ export declare const SKIPPED_ATTRIBUTES: Set<string>;
50
+ /** Headers checked, in order, for a correlation id. */
51
+ export declare const REQUEST_ID_HEADERS: string[];
52
+ /** Name under which the retention job is registered with `strapi.cron`. */
53
+ export declare const RETENTION_JOB_NAME = "auditLogRetention";
54
+ /**
55
+ * Alias that says what {@link ALL_ACTIONS} actually is, now that the plugin also
56
+ * records events which are not content operations.
57
+ *
58
+ * `ALL_ACTIONS` keeps its name and its five members because `config.actions`
59
+ * validates against it. Widening that option to accept security actions would
60
+ * let a project write `actions: ['login.success']` and then wonder why none of
61
+ * its content was audited. Security events get their own switch —
62
+ * `securityEvents` — for the same reason: the two families are produced by
63
+ * different machinery, fail independently, and are turned on and off
64
+ * independently.
65
+ */
66
+ export declare const CONTENT_ACTIONS: AuditAction[];
67
+ /**
68
+ * Everything the plugin records that is *not* a content write.
69
+ *
70
+ * These arrive from `strapi.eventHub` and from a Koa middleware rather than from
71
+ * the Document Service, which is why they are listed apart. Every one of them is
72
+ * available in Strapi **Community Edition** — none needs an Enterprise licence.
73
+ */
74
+ export declare const ALL_SECURITY_ACTIONS: AuditSecurityAction[];
75
+ /**
76
+ * Pseudo content-type uids for events that are not about a content type.
77
+ *
78
+ * `contentType` is `required` on the schema and is what every existing filter,
79
+ * index and search path is built on, so a security row carries the uid of the
80
+ * subsystem it concerns rather than a null. `admin::user`, `admin::role` and
81
+ * `admin::permission` are Strapi's own real uids; `admin::auth` and
82
+ * `admin::access` are ours, naming the two subsystems Strapi does not model as
83
+ * content types.
84
+ */
85
+ export declare const SUBJECTS: {
86
+ readonly auth: "admin::auth";
87
+ readonly access: "admin::access";
88
+ readonly user: "admin::user";
89
+ readonly role: "admin::role";
90
+ readonly permission: "admin::permission";
91
+ readonly file: "plugin::upload.file";
92
+ readonly folder: "plugin::upload.folder";
93
+ };
94
+ /** Human labels for {@link SUBJECTS}, snapshotted into `contentTypeDisplayName`. */
95
+ export declare const SUBJECT_DISPLAY_NAMES: Record<string, string>;
96
+ /**
97
+ * `eventHub` event name -> the audit action and subject it becomes.
98
+ *
99
+ * Every one of these is emitted by Strapi itself; the plugin adds no
100
+ * instrumentation to Strapi's own code. Sources, for whoever has to re-verify
101
+ * this list against a Strapi upgrade:
102
+ *
103
+ * admin.auth.*, admin.logout @strapi/admin server/src/controllers/authentication.ts
104
+ * user.*, role.*, permission.* @strapi/admin server/src/services/{user,role,permission}.ts
105
+ * media.* @strapi/upload server/src/services/upload.ts
106
+ * media-folder.* @strapi/upload server/src/services/folder.ts
107
+ *
108
+ * Media and admin-user writes go through `strapi.db.query`, below the Document
109
+ * Service, so the tracker never sees them and there is no double-recording to
110
+ * guard against.
111
+ */
112
+ export declare const SECURITY_EVENT_MAP: Record<string, {
113
+ action: AuditSecurityAction;
114
+ subject: string;
115
+ }>;
116
+ /**
117
+ * Every value the `source` column can hold.
118
+ *
119
+ * Listed rather than derived from the rows already written, so the filter can
120
+ * ask "did anything come in through the content API?" before anything has.
121
+ */
122
+ export declare const ALL_SOURCES: readonly ["admin", "api", "system", "cron", "migration", "unknown"];
123
+ /**
124
+ * Response statuses the access middleware records as a denial.
125
+ *
126
+ * 401 is "you are not authenticated", 403 is "you are, and you still may not".
127
+ * Both are what a security review means by an unauthorised access attempt, and
128
+ * recording only 403 would miss every expired-session and bad-token probe.
129
+ */
130
+ export declare const DENIED_STATUSES: Set<number>;
131
+ /**
132
+ * Paths the access middleware never records.
133
+ *
134
+ * A failed *login* already produces a `login.failed` record from
135
+ * `admin.auth.error`, complete with the attempted email; letting the middleware
136
+ * add an `access.denied` for the same 401 would double every wrong-password
137
+ * attempt. `/admin/renew-token` 401s routinely whenever a tab is left open past
138
+ * the session window, which is noise rather than signal.
139
+ */
140
+ export declare const ACCESS_IGNORED_PATHS: RegExp[];
@@ -0,0 +1,86 @@
1
+ declare const _default: {
2
+ schema: {
3
+ kind: string;
4
+ collectionName: string;
5
+ info: {
6
+ singularName: string;
7
+ pluralName: string;
8
+ displayName: string;
9
+ description: string;
10
+ };
11
+ options: {
12
+ draftAndPublish: boolean;
13
+ };
14
+ pluginOptions: {
15
+ 'content-manager': {
16
+ visible: boolean;
17
+ };
18
+ 'content-type-builder': {
19
+ visible: boolean;
20
+ };
21
+ };
22
+ attributes: {
23
+ action: {
24
+ type: string;
25
+ required: boolean;
26
+ };
27
+ contentType: {
28
+ type: string;
29
+ required: boolean;
30
+ };
31
+ contentTypeDisplayName: {
32
+ type: string;
33
+ };
34
+ contentDocumentId: {
35
+ type: string;
36
+ };
37
+ contentId: {
38
+ type: string;
39
+ };
40
+ locale: {
41
+ type: string;
42
+ };
43
+ userId: {
44
+ type: string;
45
+ };
46
+ userEmail: {
47
+ type: string;
48
+ };
49
+ userName: {
50
+ type: string;
51
+ };
52
+ changes: {
53
+ type: string;
54
+ };
55
+ before: {
56
+ type: string;
57
+ };
58
+ after: {
59
+ type: string;
60
+ };
61
+ outcome: {
62
+ type: string;
63
+ };
64
+ metadata: {
65
+ type: string;
66
+ };
67
+ ipAddress: {
68
+ type: string;
69
+ };
70
+ userAgent: {
71
+ type: string;
72
+ };
73
+ source: {
74
+ type: string;
75
+ };
76
+ requestId: {
77
+ type: string;
78
+ };
79
+ };
80
+ indexes: {
81
+ name: string;
82
+ columns: string[];
83
+ }[];
84
+ };
85
+ };
86
+ export default _default;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * The audit-log collection type.
3
+ *
4
+ * Deliberately invisible to both the Content Manager and the Content-Type
5
+ * Builder. That is not cosmetic: the Content Manager only registers
6
+ * create/update/delete/publish RBAC actions for content types it *displays*
7
+ * (`services/permission.ts` in `@strapi/content-manager` filters by
8
+ * `isDisplayed`), so with `visible: false` there is no permission any admin
9
+ * role could be granted that would let it write these rows through the normal
10
+ * content APIs. Immutability is enforced by the absence of a permission rather
11
+ * than by a check that could be forgotten. `services/immutability.ts` adds a
12
+ * second, belt-and-braces guard at the Document Service layer.
13
+ */
14
+ declare const schema: {
15
+ kind: string;
16
+ collectionName: string;
17
+ info: {
18
+ singularName: string;
19
+ pluralName: string;
20
+ displayName: string;
21
+ description: string;
22
+ };
23
+ options: {
24
+ draftAndPublish: boolean;
25
+ };
26
+ pluginOptions: {
27
+ 'content-manager': {
28
+ visible: boolean;
29
+ };
30
+ 'content-type-builder': {
31
+ visible: boolean;
32
+ };
33
+ };
34
+ attributes: {
35
+ action: {
36
+ type: string;
37
+ required: boolean;
38
+ };
39
+ /** UID of the audited content type, e.g. `api::page.page`. */
40
+ contentType: {
41
+ type: string;
42
+ required: boolean;
43
+ };
44
+ /** Display name at write time, so the log stays readable if the type is renamed or removed. */
45
+ contentTypeDisplayName: {
46
+ type: string;
47
+ };
48
+ /**
49
+ * The audited document's Strapi v5 document id.
50
+ *
51
+ * NOT named `documentId`. Strapi reserves that attribute name on every
52
+ * content type — `transformContentTypesToModels` throws
53
+ * "The attribute "documentId" is reserved" at boot — because it injects its
54
+ * own `documentId` column into every collection type. Each audit row
55
+ * therefore still *has* a framework `documentId` (its own identity); this
56
+ * column is the id of the document the row is *about*.
57
+ */
58
+ contentDocumentId: {
59
+ type: string;
60
+ };
61
+ /** The audited entry's numeric database id, stored as a string. */
62
+ contentId: {
63
+ type: string;
64
+ };
65
+ locale: {
66
+ type: string;
67
+ };
68
+ /** Actor identity, snapshotted so the record survives the user being renamed or deleted. */
69
+ userId: {
70
+ type: string;
71
+ };
72
+ userEmail: {
73
+ type: string;
74
+ };
75
+ userName: {
76
+ type: string;
77
+ };
78
+ /** Field-level diff, keyed by dotted path. */
79
+ changes: {
80
+ type: string;
81
+ };
82
+ before: {
83
+ type: string;
84
+ };
85
+ after: {
86
+ type: string;
87
+ };
88
+ /**
89
+ * Whether the recorded attempt succeeded.
90
+ *
91
+ * Always `success` for a content write — the tracker runs after the
92
+ * operation resolved, so a save that threw produces no row at all. The
93
+ * column earns its place on the security side, where `login.failed` and
94
+ * `access.denied` are exactly the rows a reviewer opens the log to find, and
95
+ * where "show me every failure" has to be an indexed query rather than a
96
+ * scan against a hard-coded list of action names.
97
+ */
98
+ outcome: {
99
+ type: string;
100
+ };
101
+ /**
102
+ * Action-specific detail: the reason a login was refused, the method and
103
+ * path of a denied request, the filename of an upload.
104
+ *
105
+ * A loose JSON bag rather than a column each, because the useful fields
106
+ * differ per action and none of them is ever filtered or sorted on. Anything
107
+ * that needs to be queryable gets a real column instead.
108
+ */
109
+ metadata: {
110
+ type: string;
111
+ };
112
+ ipAddress: {
113
+ type: string;
114
+ };
115
+ userAgent: {
116
+ type: string;
117
+ };
118
+ source: {
119
+ type: string;
120
+ };
121
+ requestId: {
122
+ type: string;
123
+ };
124
+ };
125
+ /**
126
+ * Secondary indexes, declared here so Strapi's schema sync owns them and they
127
+ * are created on every supported database without hand-written DDL.
128
+ *
129
+ * Columns are DB column names (snake_cased attribute names), and names are
130
+ * kept short enough for PostgreSQL's 63-character identifier limit.
131
+ *
132
+ * `audit_logs_ct_created_idx` is composite and leading-column ordered for the
133
+ * admin list's default query — filter by content type, sort by date — which is
134
+ * the only query that runs on every page load.
135
+ */
136
+ indexes: {
137
+ name: string;
138
+ columns: string[];
139
+ }[];
140
+ };
141
+ export default schema;
@@ -0,0 +1,88 @@
1
+ declare const _default: {
2
+ 'audit-log': {
3
+ schema: {
4
+ kind: string;
5
+ collectionName: string;
6
+ info: {
7
+ singularName: string;
8
+ pluralName: string;
9
+ displayName: string;
10
+ description: string;
11
+ };
12
+ options: {
13
+ draftAndPublish: boolean;
14
+ };
15
+ pluginOptions: {
16
+ 'content-manager': {
17
+ visible: boolean;
18
+ };
19
+ 'content-type-builder': {
20
+ visible: boolean;
21
+ };
22
+ };
23
+ attributes: {
24
+ action: {
25
+ type: string;
26
+ required: boolean;
27
+ };
28
+ contentType: {
29
+ type: string;
30
+ required: boolean;
31
+ };
32
+ contentTypeDisplayName: {
33
+ type: string;
34
+ };
35
+ contentDocumentId: {
36
+ type: string;
37
+ };
38
+ contentId: {
39
+ type: string;
40
+ };
41
+ locale: {
42
+ type: string;
43
+ };
44
+ userId: {
45
+ type: string;
46
+ };
47
+ userEmail: {
48
+ type: string;
49
+ };
50
+ userName: {
51
+ type: string;
52
+ };
53
+ changes: {
54
+ type: string;
55
+ };
56
+ before: {
57
+ type: string;
58
+ };
59
+ after: {
60
+ type: string;
61
+ };
62
+ outcome: {
63
+ type: string;
64
+ };
65
+ metadata: {
66
+ type: string;
67
+ };
68
+ ipAddress: {
69
+ type: string;
70
+ };
71
+ userAgent: {
72
+ type: string;
73
+ };
74
+ source: {
75
+ type: string;
76
+ };
77
+ requestId: {
78
+ type: string;
79
+ };
80
+ };
81
+ indexes: {
82
+ name: string;
83
+ columns: string[];
84
+ }[];
85
+ };
86
+ };
87
+ };
88
+ export default _default;
@@ -0,0 +1,31 @@
1
+ import { Core } from '@strapi/strapi';
2
+ type KoaContext = {
3
+ query: Record<string, unknown>;
4
+ params: Record<string, string>;
5
+ body: unknown;
6
+ badRequest: (message: string) => unknown;
7
+ notFound: (message?: string) => unknown;
8
+ };
9
+ /**
10
+ * Admin-only HTTP surface.
11
+ *
12
+ * Every route is registered with `type: 'admin'` and guarded by
13
+ * `admin::isAuthenticatedAdmin` plus an explicit `admin::hasPermissions` check —
14
+ * see `routes/admin.ts`. Nothing here re-implements authentication or
15
+ * authorisation; the controller runs only once Strapi's own RBAC has said yes.
16
+ *
17
+ * There is deliberately no create or update handler. Audit records are written
18
+ * by the plugin and read by humans; nothing in between.
19
+ */
20
+ declare const auditLogController: ({ strapi }: {
21
+ strapi: Core.Strapi;
22
+ }) => {
23
+ find(ctx: KoaContext): Promise<void>;
24
+ findOne(ctx: KoaContext): Promise<unknown>;
25
+ delete(ctx: KoaContext): Promise<unknown>;
26
+ /** Filter dropdown options, derived from the rows that actually exist. */
27
+ filters(ctx: KoaContext): Promise<void>;
28
+ /** The effective configuration, so the UI can hide controls for disabled features. */
29
+ config(ctx: KoaContext): Promise<void>;
30
+ };
31
+ export default auditLogController;
@@ -0,0 +1,42 @@
1
+ declare const _default: {
2
+ 'audit-log': ({ strapi }: {
3
+ strapi: import('@strapi/types/dist/core').Strapi;
4
+ }) => {
5
+ find(ctx: {
6
+ query: Record<string, unknown>;
7
+ params: Record<string, string>;
8
+ body: unknown;
9
+ badRequest: (message: string) => unknown;
10
+ notFound: (message?: string) => unknown;
11
+ }): Promise<void>;
12
+ findOne(ctx: {
13
+ query: Record<string, unknown>;
14
+ params: Record<string, string>;
15
+ body: unknown;
16
+ badRequest: (message: string) => unknown;
17
+ notFound: (message?: string) => unknown;
18
+ }): Promise<unknown>;
19
+ delete(ctx: {
20
+ query: Record<string, unknown>;
21
+ params: Record<string, string>;
22
+ body: unknown;
23
+ badRequest: (message: string) => unknown;
24
+ notFound: (message?: string) => unknown;
25
+ }): Promise<unknown>;
26
+ filters(ctx: {
27
+ query: Record<string, unknown>;
28
+ params: Record<string, string>;
29
+ body: unknown;
30
+ badRequest: (message: string) => unknown;
31
+ notFound: (message?: string) => unknown;
32
+ }): Promise<void>;
33
+ config(ctx: {
34
+ query: Record<string, unknown>;
35
+ params: Record<string, string>;
36
+ body: unknown;
37
+ badRequest: (message: string) => unknown;
38
+ notFound: (message?: string) => unknown;
39
+ }): Promise<void>;
40
+ };
41
+ };
42
+ export default _default;
@@ -0,0 +1,19 @@
1
+ import { Core } from '@strapi/strapi';
2
+ /**
3
+ * Shuts the plugin down cleanly.
4
+ *
5
+ * Flushing pending writes is the part that matters. In `writeMode: 'async'` the
6
+ * audit inserts are in flight when the process is asked to stop, and an audit
7
+ * trail that silently drops its last few records on every deploy is worse than
8
+ * useless — it is misleading. Draining here turns "async loses data on restart"
9
+ * into "async loses data only on a hard kill", which is a trade-off a reader of
10
+ * the README can actually reason about.
11
+ *
12
+ * The cron job is removed too, so a `strapi.destroy()` followed by a fresh boot
13
+ * in the same process (which is what the test harness and `strapi console` do)
14
+ * does not end up with two retention jobs scheduled.
15
+ */
16
+ declare const destroy: ({ strapi }: {
17
+ strapi: Core.Strapi;
18
+ }) => Promise<void>;
19
+ export default destroy;