@questpie/admin 3.2.7 → 3.4.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/README.md +4 -6
- package/dist/client/blocks/block-renderer.d.mts +2 -2
- package/dist/client/builder/admin-types.d.mts +3 -3
- package/dist/client/builder/types/action-types.d.mts +1 -1
- package/dist/client/builder/types/collection-types.d.mts +59 -2
- package/dist/client/components/blocks/block-editor-provider.mjs +13 -0
- package/dist/client/components/fields/array-field.mjs +105 -122
- package/dist/client/components/fields/asset-preview-field.mjs +1 -1
- package/dist/client/components/fields/blocks-field/blocks-field.mjs +1 -1
- package/dist/client/components/fields/boolean-field.mjs +1 -1
- package/dist/client/components/fields/date-field.mjs +1 -1
- package/dist/client/components/fields/datetime-field.mjs +1 -1
- package/dist/client/components/fields/email-field.mjs +1 -1
- package/dist/client/components/fields/field-wrapper.mjs +44 -15
- package/dist/client/components/fields/number-field.mjs +1 -1
- package/dist/client/components/fields/object-array-field.mjs +179 -149
- package/dist/client/components/fields/object-field.mjs +96 -87
- package/dist/client/components/fields/relation-picker.mjs +1 -1
- package/dist/client/components/fields/relation-select.mjs +1 -1
- package/dist/client/components/fields/rich-text-editor/index.mjs +1 -1
- package/dist/client/components/fields/select-field.mjs +1 -1
- package/dist/client/components/fields/text-field.mjs +1 -1
- package/dist/client/components/fields/textarea-field.mjs +1 -1
- package/dist/client/components/fields/time-field.mjs +1 -1
- package/dist/client/components/fields/upload-field.mjs +1 -1
- package/dist/client/components/history-sidebar.mjs +10 -4
- package/dist/client/components/structured-diff.mjs +367 -0
- package/dist/client/components/ui/sidebar.mjs +1 -1
- package/dist/client/hooks/use-field-options.mjs +34 -15
- package/dist/client/hooks/use-transition-stage.mjs +2 -2
- package/dist/client/modules/admin.d.mts +3 -0
- package/dist/client/modules/admin.mjs +3 -0
- package/dist/client/preview/block-scope-context.d.mts +2 -2
- package/dist/client/preview/preview-banner.d.mts +2 -2
- package/dist/client/preview/preview-field.d.mts +4 -4
- package/dist/client/utils/auto-expand-fields.mjs +1 -1
- package/dist/client/views/collection/auto-form-fields.mjs +23 -19
- package/dist/client/views/collection/cells/complex-cells.mjs +1 -1
- package/dist/client/views/collection/columns/build-columns.mjs +1 -1
- package/dist/client/views/collection/columns/column-defaults.mjs +17 -4
- package/dist/client/views/collection/field-renderer.mjs +19 -7
- package/dist/client/views/collection/form-view.mjs +10 -6
- package/dist/client/views/collection/list-view.mjs +830 -0
- package/dist/client/views/collection/outline.mjs +363 -0
- package/dist/client/views/collection/table-view.mjs +25 -16
- package/dist/client/views/globals/global-form-view.mjs +47 -27
- package/dist/client/views/layout/admin-layout.d.mts +15 -1
- package/dist/client/views/layout/admin-layout.mjs +95 -31
- package/dist/client/views/layout/admin-sidebar.mjs +2 -2
- package/dist/client.d.mts +6 -6
- package/dist/client.mjs +1 -1
- package/dist/components/rich-text/rich-text-renderer.d.mts +2 -2
- package/dist/factories.d.mts +19 -0
- package/dist/factories.mjs +11 -0
- package/dist/fields.d.mts +4 -0
- package/dist/fields.mjs +5 -0
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +1 -1
- package/dist/modules/admin.d.mts +10 -0
- package/dist/modules/admin.mjs +9 -0
- package/dist/modules/audit.d.mts +5 -0
- package/dist/modules/audit.mjs +5 -0
- package/dist/server/augmentation/form-layout.d.mts +57 -2
- package/dist/server/augmentation/index.d.mts +3 -1
- package/dist/server/augmentation/shell.d.mts +48 -0
- package/dist/server/augmentation.d.mts +2 -1
- package/dist/server/codegen/admin-client-template.mjs +11 -4
- package/dist/server/fields/blocks.d.mts +9 -2
- package/dist/server/fields/blocks.mjs +1 -1
- package/dist/server/fields/index.d.mts +2 -2
- package/dist/server/fields/index.mjs +2 -2
- package/dist/server/fields/rich-text.d.mts +9 -2
- package/dist/server/fields/rich-text.mjs +1 -1
- package/dist/server/i18n/messages/cs.mjs +8 -0
- package/dist/server/i18n/messages/de.mjs +8 -0
- package/dist/server/i18n/messages/en.mjs +8 -0
- package/dist/server/i18n/messages/es.mjs +8 -0
- package/dist/server/i18n/messages/fr.mjs +8 -0
- package/dist/server/i18n/messages/pl.mjs +8 -0
- package/dist/server/i18n/messages/pt.mjs +8 -0
- package/dist/server/i18n/messages/sk.mjs +8 -0
- package/dist/server/modules/admin/.generated/module.d.mts +24 -19
- package/dist/server/modules/admin/.generated/module.mjs +5 -1
- package/dist/server/modules/admin/.generated/registries.d.mts +6 -4
- package/dist/server/modules/admin/client/.generated/module.d.mts +70 -70
- package/dist/server/modules/admin/client/.generated/module.mjs +3 -1
- package/dist/server/modules/admin/client/views/collection-form.d.mts +6 -0
- package/dist/server/modules/admin/client/views/collection-table.d.mts +6 -0
- package/dist/server/modules/admin/client/views/global-form.d.mts +6 -0
- package/dist/server/modules/admin/client/views/list-view.d.mts +6 -0
- package/dist/server/modules/admin/client/views/list-view.mjs +10 -0
- package/dist/server/modules/admin/collections/account.d.mts +50 -50
- package/dist/server/modules/admin/collections/admin-locks.d.mts +54 -54
- package/dist/server/modules/admin/collections/admin-preferences.d.mts +39 -39
- package/dist/server/modules/admin/collections/admin-saved-views.d.mts +47 -47
- package/dist/server/modules/admin/collections/apikey.d.mts +39 -39
- package/dist/server/modules/admin/collections/assets.d.mts +39 -39
- package/dist/server/modules/admin/collections/session.d.mts +42 -42
- package/dist/server/modules/admin/collections/user.d.mts +63 -63
- package/dist/server/modules/admin/collections/verification.d.mts +36 -36
- package/dist/server/modules/admin/dto/admin-config.dto.mjs +17 -0
- package/dist/server/modules/admin/index.d.mts +30 -31
- package/dist/server/modules/admin/routes/admin-config.d.mts +2 -17
- package/dist/server/modules/admin/routes/admin-config.mjs +21 -5
- package/dist/server/modules/admin/routes/execute-action.d.mts +9 -9
- package/dist/server/modules/admin/routes/execute-action.mjs +18 -12
- package/dist/server/modules/admin/routes/i18n-helpers.d.mts +4 -0
- package/dist/server/modules/admin/routes/locales.d.mts +2 -2
- package/dist/server/modules/admin/routes/preview.d.mts +24 -19
- package/dist/server/modules/admin/routes/preview.mjs +83 -62
- package/dist/server/modules/admin/routes/reactive.d.mts +9 -9
- package/dist/server/modules/admin/routes/route-helpers.mjs +36 -1
- package/dist/server/modules/admin/routes/setup.d.mts +7 -14
- package/dist/server/modules/admin/routes/setup.mjs +16 -3
- package/dist/server/modules/admin/routes/translations.d.mts +4 -4
- package/dist/server/modules/admin/routes/widget-data.d.mts +5 -5
- package/dist/server/modules/admin/views/list-view.d.mts +8 -0
- package/dist/server/modules/admin/views/list-view.mjs +7 -0
- package/dist/server/modules/admin-preferences/collections/saved-views.d.mts +41 -41
- package/dist/server/modules/audit/.generated/module.d.mts +6 -6
- package/dist/server/modules/audit/collections/audit-log.d.mts +87 -80
- package/dist/server/modules/audit/collections/audit-log.mjs +7 -2
- package/dist/server/modules/audit/config/localize-title.mjs +67 -0
- package/dist/server/modules/audit/index.d.mts +3 -2
- package/dist/server/modules/audit/jobs/audit-cleanup.d.mts +2 -2
- package/dist/server/modules/audit/log-audit-entry.d.mts +85 -0
- package/dist/server/modules/audit/log-audit-entry.mjs +125 -0
- package/dist/server/plugin.d.mts +1 -1
- package/dist/server/plugin.mjs +31 -31
- package/dist/server.d.mts +6 -4
- package/dist/server.mjs +9 -8
- package/dist/shared/preview-utils.d.mts +4 -4
- package/dist/shared/preview-utils.mjs +5 -7
- package/package.json +13 -3
- package/dist/client/hooks/use-audit-history.mjs +0 -38
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import * as
|
|
3
|
-
import * as
|
|
4
|
-
import * as
|
|
5
|
-
import * as
|
|
6
|
-
import * as
|
|
1
|
+
import * as questpie_shared0 from "questpie/shared";
|
|
2
|
+
import * as questpie21 from "questpie";
|
|
3
|
+
import * as questpie_src_server_modules_core_fields_email_js0 from "questpie/src/server/modules/core/fields/email.js";
|
|
4
|
+
import * as questpie_src_server_modules_core_fields_json_js0 from "questpie/src/server/modules/core/fields/json.js";
|
|
5
|
+
import * as drizzle_orm_pg_core0 from "drizzle-orm/pg-core";
|
|
6
|
+
import * as drizzle_orm0 from "drizzle-orm";
|
|
7
7
|
import * as questpie_src_server_fields_operators_builtin_js0 from "questpie/src/server/fields/operators/builtin.js";
|
|
8
8
|
|
|
9
9
|
//#region src/server/modules/audit/collections/audit-log.d.ts
|
|
10
|
-
|
|
10
|
+
/**
|
|
11
|
+
* The collection slug used by the audit log.
|
|
12
|
+
* Exported so hooks and jobs can reference the collection dynamically
|
|
13
|
+
* instead of hardcoding the name.
|
|
14
|
+
*/
|
|
15
|
+
declare const AUDIT_LOG_COLLECTION: "admin_audit_log";
|
|
11
16
|
/**
|
|
12
17
|
* Audit Log Collection
|
|
13
18
|
*
|
|
@@ -19,140 +24,142 @@ import * as questpie_src_server_fields_operators_builtin_js0 from "questpie/src/
|
|
|
19
24
|
* - update: disallowed
|
|
20
25
|
* - read: allowed (for admin UI display)
|
|
21
26
|
*/
|
|
22
|
-
declare const auditLogCollection:
|
|
23
|
-
readonly text: typeof
|
|
24
|
-
readonly textarea: typeof
|
|
25
|
-
readonly email: typeof
|
|
26
|
-
readonly url: typeof
|
|
27
|
-
readonly number: typeof
|
|
28
|
-
readonly boolean: typeof
|
|
29
|
-
readonly date: typeof
|
|
30
|
-
readonly datetime: typeof
|
|
31
|
-
readonly time: typeof
|
|
32
|
-
readonly select: typeof
|
|
33
|
-
readonly upload: typeof
|
|
34
|
-
readonly relation: typeof
|
|
35
|
-
readonly object: typeof
|
|
36
|
-
readonly json: typeof
|
|
37
|
-
readonly from: typeof
|
|
27
|
+
declare const auditLogCollection: questpie21.CollectionBuilder<questpie_shared0.Override<questpie_shared0.Override<questpie_shared0.Override<questpie_shared0.Override<questpie_shared0.Override<questpie_shared0.Override<questpie21.EmptyCollectionState<"admin_audit_log", undefined, {
|
|
28
|
+
readonly text: typeof questpie21.text;
|
|
29
|
+
readonly textarea: typeof questpie21.textarea;
|
|
30
|
+
readonly email: typeof questpie_src_server_modules_core_fields_email_js0.email;
|
|
31
|
+
readonly url: typeof questpie21.url;
|
|
32
|
+
readonly number: typeof questpie21.number;
|
|
33
|
+
readonly boolean: typeof questpie21.boolean;
|
|
34
|
+
readonly date: typeof questpie21.date;
|
|
35
|
+
readonly datetime: typeof questpie21.datetime;
|
|
36
|
+
readonly time: typeof questpie21.time;
|
|
37
|
+
readonly select: typeof questpie21.select;
|
|
38
|
+
readonly upload: typeof questpie21.upload;
|
|
39
|
+
readonly relation: typeof questpie21.relation;
|
|
40
|
+
readonly object: typeof questpie21.object;
|
|
41
|
+
readonly json: typeof questpie_src_server_modules_core_fields_json_js0.json;
|
|
42
|
+
readonly from: typeof questpie21.from;
|
|
38
43
|
}>, {
|
|
39
44
|
fields: {
|
|
40
|
-
readonly action:
|
|
41
|
-
readonly resourceType:
|
|
42
|
-
readonly resource:
|
|
43
|
-
readonly resourceId:
|
|
44
|
-
readonly resourceLabel:
|
|
45
|
-
readonly userId:
|
|
46
|
-
readonly userName:
|
|
47
|
-
readonly locale:
|
|
48
|
-
readonly changes:
|
|
49
|
-
readonly metadata:
|
|
50
|
-
readonly title:
|
|
45
|
+
readonly action: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>>;
|
|
46
|
+
readonly resourceType: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>>;
|
|
47
|
+
readonly resource: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>>;
|
|
48
|
+
readonly resourceId: drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>;
|
|
49
|
+
readonly resourceLabel: drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>;
|
|
50
|
+
readonly userId: drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>;
|
|
51
|
+
readonly userName: drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>;
|
|
52
|
+
readonly locale: drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>;
|
|
53
|
+
readonly changes: drizzle_orm_pg_core0.PgJsonbBuilder;
|
|
54
|
+
readonly metadata: drizzle_orm_pg_core0.PgJsonbBuilder;
|
|
55
|
+
readonly title: drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>;
|
|
51
56
|
};
|
|
52
57
|
localized: readonly string[];
|
|
53
58
|
fieldDefinitions: {
|
|
54
59
|
/** Action performed: create, update, delete, transition, custom */
|
|
55
|
-
readonly action:
|
|
60
|
+
readonly action: questpie21.FieldWithMethods<Omit<questpie21.TextFieldState, "notNull" | "column"> & {
|
|
56
61
|
notNull: true;
|
|
57
|
-
column:
|
|
62
|
+
column: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>>;
|
|
58
63
|
} & {
|
|
59
|
-
label:
|
|
60
|
-
},
|
|
64
|
+
label: questpie_shared0.I18nText;
|
|
65
|
+
}, questpie21.TextFieldMethods>;
|
|
61
66
|
/** Resource type: collection, global, auth, system, custom */
|
|
62
|
-
readonly resourceType:
|
|
67
|
+
readonly resourceType: questpie21.FieldWithMethods<Omit<questpie21.TextFieldState, "notNull" | "column"> & {
|
|
63
68
|
notNull: true;
|
|
64
|
-
column:
|
|
69
|
+
column: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>>;
|
|
65
70
|
} & {
|
|
66
|
-
label:
|
|
67
|
-
},
|
|
71
|
+
label: questpie_shared0.I18nText;
|
|
72
|
+
}, questpie21.TextFieldMethods>;
|
|
68
73
|
/** Resource slug (collection/global name) */
|
|
69
|
-
readonly resource:
|
|
74
|
+
readonly resource: questpie21.FieldWithMethods<Omit<questpie21.TextFieldState, "notNull" | "column"> & {
|
|
70
75
|
notNull: true;
|
|
71
|
-
column:
|
|
76
|
+
column: drizzle_orm0.NotNull<drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>>;
|
|
72
77
|
} & {
|
|
73
|
-
label:
|
|
74
|
-
},
|
|
78
|
+
label: questpie_shared0.I18nText;
|
|
79
|
+
}, questpie21.TextFieldMethods>;
|
|
75
80
|
/** ID of the specific record (null for globals) */
|
|
76
|
-
readonly resourceId:
|
|
81
|
+
readonly resourceId: questpie21.FieldWithMethods<questpie21.DefaultFieldState & {
|
|
77
82
|
type: "text";
|
|
78
83
|
data: string;
|
|
79
|
-
column:
|
|
84
|
+
column: drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>;
|
|
80
85
|
operators: typeof questpie_src_server_fields_operators_builtin_js0.stringOps;
|
|
81
86
|
} & {
|
|
82
|
-
label:
|
|
83
|
-
},
|
|
87
|
+
label: questpie_shared0.I18nText;
|
|
88
|
+
}, questpie21.TextFieldMethods>;
|
|
84
89
|
/** Denormalized display label for the affected record */
|
|
85
|
-
readonly resourceLabel:
|
|
90
|
+
readonly resourceLabel: questpie21.FieldWithMethods<questpie21.DefaultFieldState & {
|
|
86
91
|
type: "text";
|
|
87
92
|
data: string;
|
|
88
|
-
column:
|
|
93
|
+
column: drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>;
|
|
89
94
|
operators: typeof questpie_src_server_fields_operators_builtin_js0.stringOps;
|
|
90
95
|
} & {
|
|
91
|
-
label:
|
|
92
|
-
},
|
|
96
|
+
label: questpie_shared0.I18nText;
|
|
97
|
+
}, questpie21.TextFieldMethods>;
|
|
93
98
|
/** User who performed the action */
|
|
94
|
-
readonly userId:
|
|
99
|
+
readonly userId: questpie21.FieldWithMethods<questpie21.DefaultFieldState & {
|
|
95
100
|
type: "text";
|
|
96
101
|
data: string;
|
|
97
|
-
column:
|
|
102
|
+
column: drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>;
|
|
98
103
|
operators: typeof questpie_src_server_fields_operators_builtin_js0.stringOps;
|
|
99
104
|
} & {
|
|
100
|
-
label:
|
|
101
|
-
},
|
|
105
|
+
label: questpie_shared0.I18nText;
|
|
106
|
+
}, questpie21.TextFieldMethods>;
|
|
102
107
|
/** Denormalized user display name */
|
|
103
|
-
readonly userName:
|
|
108
|
+
readonly userName: questpie21.FieldWithMethods<questpie21.DefaultFieldState & {
|
|
104
109
|
type: "text";
|
|
105
110
|
data: string;
|
|
106
|
-
column:
|
|
111
|
+
column: drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>;
|
|
107
112
|
operators: typeof questpie_src_server_fields_operators_builtin_js0.stringOps;
|
|
108
113
|
} & {
|
|
109
|
-
label:
|
|
110
|
-
},
|
|
114
|
+
label: questpie_shared0.I18nText;
|
|
115
|
+
}, questpie21.TextFieldMethods>;
|
|
111
116
|
/** Locale context of the operation */
|
|
112
|
-
readonly locale:
|
|
117
|
+
readonly locale: questpie21.FieldWithMethods<questpie21.DefaultFieldState & {
|
|
113
118
|
type: "text";
|
|
114
119
|
data: string;
|
|
115
|
-
column:
|
|
120
|
+
column: drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>;
|
|
116
121
|
operators: typeof questpie_src_server_fields_operators_builtin_js0.stringOps;
|
|
117
122
|
} & {
|
|
118
|
-
label:
|
|
119
|
-
},
|
|
123
|
+
label: questpie_shared0.I18nText;
|
|
124
|
+
}, questpie21.TextFieldMethods>;
|
|
120
125
|
/** Field-level diffs: { field: { from, to } } or null */
|
|
121
|
-
readonly changes:
|
|
126
|
+
readonly changes: questpie21.Field<questpie21.DefaultFieldState & {
|
|
122
127
|
type: "json";
|
|
123
|
-
data:
|
|
124
|
-
column:
|
|
128
|
+
data: questpie21.JsonValue;
|
|
129
|
+
column: drizzle_orm_pg_core0.PgJsonbBuilder;
|
|
125
130
|
operators: typeof questpie_src_server_fields_operators_builtin_js0.basicOps;
|
|
126
131
|
} & {
|
|
127
|
-
label:
|
|
132
|
+
label: questpie_shared0.I18nText;
|
|
128
133
|
}>;
|
|
129
134
|
/** Extra context metadata */
|
|
130
|
-
readonly metadata:
|
|
135
|
+
readonly metadata: questpie21.Field<questpie21.DefaultFieldState & {
|
|
131
136
|
type: "json";
|
|
132
|
-
data:
|
|
133
|
-
column:
|
|
137
|
+
data: questpie21.JsonValue;
|
|
138
|
+
column: drizzle_orm_pg_core0.PgJsonbBuilder;
|
|
134
139
|
operators: typeof questpie_src_server_fields_operators_builtin_js0.basicOps;
|
|
135
140
|
} & {
|
|
136
|
-
label:
|
|
141
|
+
label: questpie_shared0.I18nText;
|
|
137
142
|
}>;
|
|
138
143
|
/** Human-readable title: "User Action ResourceType 'ResourceLabel'" */
|
|
139
|
-
readonly title:
|
|
144
|
+
readonly title: questpie21.FieldWithMethods<questpie21.DefaultFieldState & {
|
|
140
145
|
type: "text";
|
|
141
146
|
data: string;
|
|
142
|
-
column:
|
|
147
|
+
column: drizzle_orm_pg_core0.PgVarcharBuilder<[string, ...string[]]>;
|
|
143
148
|
operators: typeof questpie_src_server_fields_operators_builtin_js0.stringOps;
|
|
144
149
|
} & {
|
|
145
|
-
label:
|
|
146
|
-
},
|
|
150
|
+
label: questpie_shared0.I18nText;
|
|
151
|
+
}, questpie21.TextFieldMethods>;
|
|
147
152
|
};
|
|
148
153
|
}>, {
|
|
149
154
|
options: {
|
|
150
155
|
timestamps: true;
|
|
151
156
|
};
|
|
152
157
|
}>, {
|
|
153
|
-
indexes:
|
|
158
|
+
indexes: drizzle_orm_pg_core0.IndexBuilder[];
|
|
154
159
|
}>, {
|
|
155
160
|
access: Record<string, any>;
|
|
161
|
+
}>, {
|
|
162
|
+
hooks: Record<string, any>;
|
|
156
163
|
}> & Record<"admin", {
|
|
157
164
|
label: {
|
|
158
165
|
key: string;
|
|
@@ -211,4 +218,4 @@ declare const auditLogCollection: questpie98.CollectionBuilder<questpie_shared6.
|
|
|
211
218
|
})[];
|
|
212
219
|
}>>;
|
|
213
220
|
//#endregion
|
|
214
|
-
export { auditLogCollection };
|
|
221
|
+
export { AUDIT_LOG_COLLECTION, auditLogCollection };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { localizeAuditTitle } from "../config/localize-title.mjs";
|
|
2
|
+
import { collection, tryGetContext } from "questpie";
|
|
2
3
|
import { index } from "questpie/drizzle-pg-core";
|
|
3
4
|
|
|
4
5
|
//#region src/server/modules/audit/collections/audit-log.ts
|
|
@@ -41,7 +42,11 @@ const auditLogCollection = collection(AUDIT_LOG_COLLECTION).fields(({ f }) => ({
|
|
|
41
42
|
update: false,
|
|
42
43
|
delete: false,
|
|
43
44
|
read: true
|
|
44
|
-
}).
|
|
45
|
+
}).hooks({ afterRead: ({ data, locale }) => {
|
|
46
|
+
if (!data) return;
|
|
47
|
+
const localized = localizeAuditTitle(data, locale, tryGetContext()?.app);
|
|
48
|
+
if (localized) data.title = localized;
|
|
49
|
+
} }).set("admin", {
|
|
45
50
|
label: { key: "audit.collection.label" },
|
|
46
51
|
icon: {
|
|
47
52
|
type: "icon",
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { getAdminMessagesForLocale } from "../../../i18n/index.mjs";
|
|
2
|
+
import { isI18nTranslationKey, resolveI18nText } from "questpie/shared";
|
|
3
|
+
|
|
4
|
+
//#region src/server/modules/audit/config/localize-title.ts
|
|
5
|
+
function lookupMessage(messages, key) {
|
|
6
|
+
const value = messages[key];
|
|
7
|
+
if (typeof value === "string") return value;
|
|
8
|
+
if (value && typeof value === "object") {
|
|
9
|
+
const plural = value.other;
|
|
10
|
+
if (typeof plural === "string") return plural;
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
function makeTranslator(messages) {
|
|
15
|
+
return (key) => lookupMessage(messages, key) ?? key;
|
|
16
|
+
}
|
|
17
|
+
function getResourceLabel(app, resourceType, resource) {
|
|
18
|
+
if (!app) return null;
|
|
19
|
+
if (resourceType === "collection") {
|
|
20
|
+
const config = app.getCollectionConfig?.(resource);
|
|
21
|
+
return config?.state?.admin?.label ?? config?.state?.label ?? null;
|
|
22
|
+
}
|
|
23
|
+
if (resourceType === "global") {
|
|
24
|
+
const config = app.getGlobals?.()?.[resource];
|
|
25
|
+
return config?.state?.admin?.label ?? config?.state?.label ?? null;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
function resolveLabel(label, locale, t) {
|
|
30
|
+
if (label == null) return null;
|
|
31
|
+
if (isI18nTranslationKey(label)) {
|
|
32
|
+
const resolved = t(label.key);
|
|
33
|
+
return resolved === label.key ? label.fallback ?? null : resolved;
|
|
34
|
+
}
|
|
35
|
+
return resolveI18nText(label, locale, t);
|
|
36
|
+
}
|
|
37
|
+
function interpolate(template, values) {
|
|
38
|
+
return template.replace(/\{\{\s*(\w+)\s*\}\}/g, (_m, k) => values[k] ?? `{{${k}}}`);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Recompute the audit log title in the viewer's locale.
|
|
42
|
+
* Returns null when no locale is provided so the caller can keep the stored value.
|
|
43
|
+
*/
|
|
44
|
+
function localizeAuditTitle(data, locale, app) {
|
|
45
|
+
if (!locale) return null;
|
|
46
|
+
const messages = getAdminMessagesForLocale(locale);
|
|
47
|
+
const t = makeTranslator(messages);
|
|
48
|
+
const action = String(data.action ?? "");
|
|
49
|
+
const resourceType = String(data.resourceType ?? "");
|
|
50
|
+
const resource = String(data.resource ?? "");
|
|
51
|
+
const userName = String(data.userName ?? "");
|
|
52
|
+
const actionText = lookupMessage(messages, `audit.action.${action}`) ?? action;
|
|
53
|
+
const resourceTypeText = lookupMessage(messages, `audit.resourceType.${resourceType}`) ?? resourceType;
|
|
54
|
+
const resourceName = resolveLabel(getResourceLabel(app, resourceType, resource), locale, t) ?? resource;
|
|
55
|
+
const rawLabel = typeof data.resourceLabel === "string" && data.resourceLabel.length > 0 ? data.resourceLabel : null;
|
|
56
|
+
const unnamed = lookupMessage(messages, "audit.unnamed") ?? "(unnamed)";
|
|
57
|
+
const resourceLabel = rawLabel ? `'${rawLabel}'` : unnamed;
|
|
58
|
+
return interpolate(lookupMessage(messages, "audit.title.template") ?? "{{userName}} {{action}} {{resourceType}} {{resourceLabel}}", {
|
|
59
|
+
userName,
|
|
60
|
+
action: actionText,
|
|
61
|
+
resourceType: `${resourceTypeText} ${resourceName}`,
|
|
62
|
+
resourceLabel
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
export { localizeAuditTitle };
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
import { auditLogCollection } from "./collections/audit-log.mjs";
|
|
2
|
-
import { AuditModule, _module } from "./.generated/module.mjs";
|
|
1
|
+
import { AUDIT_LOG_COLLECTION, auditLogCollection } from "./collections/audit-log.mjs";
|
|
2
|
+
import { AuditModule, _module } from "./.generated/module.mjs";
|
|
3
|
+
import { AuditActorType, AuditContext, LogAuditEntryOptions, logAuditEntry } from "./log-audit-entry.mjs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as questpie151 from "questpie";
|
|
2
2
|
|
|
3
3
|
//#region src/server/modules/audit/jobs/audit-cleanup.d.ts
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ import * as questpie461 from "questpie";
|
|
|
6
6
|
* Audit log cleanup job.
|
|
7
7
|
* Runs daily (via cron) to delete entries older than the configured retention period.
|
|
8
8
|
*/
|
|
9
|
-
declare const auditCleanupJob:
|
|
9
|
+
declare const auditCleanupJob: questpie151.JobDefinition<{
|
|
10
10
|
retentionDays: number;
|
|
11
11
|
}, void, "audit-cleanup">;
|
|
12
12
|
//#endregion
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
//#region src/server/modules/audit/log-audit-entry.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Options for a custom audit log entry.
|
|
4
|
+
*/
|
|
5
|
+
type AuditActorType = "anonymous" | "system" | "user" | (string & {});
|
|
6
|
+
interface LogAuditEntryOptions {
|
|
7
|
+
/** Action performed (e.g., "create", "bulk-email", "archive", "migrate"). */
|
|
8
|
+
action: string;
|
|
9
|
+
/**
|
|
10
|
+
* Category of the affected resource.
|
|
11
|
+
* Built-in: "collection" | "global"
|
|
12
|
+
* Custom: "system" | "job" | "webhook" | any string
|
|
13
|
+
*/
|
|
14
|
+
resourceType: string;
|
|
15
|
+
/** Resource identifier (e.g., collection slug, job name). */
|
|
16
|
+
resource: string;
|
|
17
|
+
/** Optional ID of a specific record. */
|
|
18
|
+
resourceId?: string | null;
|
|
19
|
+
/** Display label for the affected resource (e.g., record title, job description). */
|
|
20
|
+
resourceLabel?: string | null;
|
|
21
|
+
/** Override actor user name (auto-resolved from session when omitted). */
|
|
22
|
+
userName?: string;
|
|
23
|
+
/** Override actor user ID (auto-resolved from session when omitted). */
|
|
24
|
+
userId?: string | null;
|
|
25
|
+
/** Override actor category stored in metadata. */
|
|
26
|
+
actorType?: AuditActorType;
|
|
27
|
+
/** Locale context of the operation. */
|
|
28
|
+
locale?: string | null;
|
|
29
|
+
/** Field-level changes, or arbitrary structured data. */
|
|
30
|
+
changes?: Record<string, unknown> | null;
|
|
31
|
+
/** Extra metadata (e.g., job payload, webhook source). */
|
|
32
|
+
metadata?: Record<string, unknown> | null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Audit context — pass the hook/job context directly.
|
|
36
|
+
* Needs `collections` (or `app.collections`) and optionally `session`, `db`.
|
|
37
|
+
*/
|
|
38
|
+
interface AuditContext {
|
|
39
|
+
collections?: Record<string, any>;
|
|
40
|
+
app?: {
|
|
41
|
+
collections?: Record<string, any>;
|
|
42
|
+
getCollectionConfig?: (slug: string) => any;
|
|
43
|
+
getGlobals?: () => Record<string, any>;
|
|
44
|
+
};
|
|
45
|
+
session?: {
|
|
46
|
+
user?: Record<string, unknown> | null;
|
|
47
|
+
} | null;
|
|
48
|
+
accessMode?: string;
|
|
49
|
+
db?: unknown;
|
|
50
|
+
logger?: {
|
|
51
|
+
error?: (...args: any[]) => void;
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Write a custom audit log entry.
|
|
56
|
+
*
|
|
57
|
+
* Use this in jobs, custom actions, webhooks, or anywhere you want
|
|
58
|
+
* to record a meaningful event in the audit trail.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { logAuditEntry } from "@questpie/admin/server";
|
|
63
|
+
*
|
|
64
|
+
* // Inside a job handler:
|
|
65
|
+
* await logAuditEntry(ctx, {
|
|
66
|
+
* action: "bulk-email",
|
|
67
|
+
* resourceType: "system",
|
|
68
|
+
* resource: "newsletter",
|
|
69
|
+
* resourceLabel: "Q1 Campaign",
|
|
70
|
+
* metadata: { sentCount: 1234, jobId: ctx.id },
|
|
71
|
+
* });
|
|
72
|
+
*
|
|
73
|
+
* // Inside a collection hook with session:
|
|
74
|
+
* await logAuditEntry(ctx, {
|
|
75
|
+
* action: "export",
|
|
76
|
+
* resourceType: "collection",
|
|
77
|
+
* resource: "orders",
|
|
78
|
+
* resourceLabel: "Monthly export",
|
|
79
|
+
* changes: { format: { from: null, to: "csv" } },
|
|
80
|
+
* });
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
declare function logAuditEntry(ctx: AuditContext, options: LogAuditEntryOptions): Promise<void>;
|
|
84
|
+
//#endregion
|
|
85
|
+
export { AuditActorType, AuditContext, LogAuditEntryOptions, logAuditEntry };
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { AUDIT_LOG_COLLECTION } from "./collections/audit-log.mjs";
|
|
2
|
+
import { tryGetContext } from "questpie";
|
|
3
|
+
|
|
4
|
+
//#region src/server/modules/audit/log-audit-entry.ts
|
|
5
|
+
function resolveCollections(ctx) {
|
|
6
|
+
return ctx.collections ?? ctx.app?.collections ?? null;
|
|
7
|
+
}
|
|
8
|
+
function resolveActor(ctx) {
|
|
9
|
+
const user = ctx.session?.user;
|
|
10
|
+
const userId = user?.id != null ? String(user.id) : null;
|
|
11
|
+
for (const candidate of [
|
|
12
|
+
user?.name,
|
|
13
|
+
user?.email,
|
|
14
|
+
userId
|
|
15
|
+
]) if (typeof candidate === "string" && candidate.trim().length > 0) return {
|
|
16
|
+
actorType: "user",
|
|
17
|
+
userId,
|
|
18
|
+
userName: candidate.trim()
|
|
19
|
+
};
|
|
20
|
+
if (ctx.accessMode === "system") return {
|
|
21
|
+
actorType: "system",
|
|
22
|
+
userId: "system",
|
|
23
|
+
userName: "System"
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
actorType: "anonymous",
|
|
27
|
+
userId: null,
|
|
28
|
+
userName: "Anonymous"
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function getResourceTypeLabel(ctx, resourceType, resource) {
|
|
32
|
+
const stored = tryGetContext();
|
|
33
|
+
const app = ctx.app ?? stored?.app;
|
|
34
|
+
if (!app) return resource;
|
|
35
|
+
if (resourceType === "collection") {
|
|
36
|
+
const config = app.getCollectionConfig?.(resource);
|
|
37
|
+
const label = config?.state?.admin?.label ?? config?.state?.label;
|
|
38
|
+
if (typeof label === "string") return label;
|
|
39
|
+
return resource;
|
|
40
|
+
}
|
|
41
|
+
if (resourceType === "global") {
|
|
42
|
+
const config = app.getGlobals?.()?.[resource];
|
|
43
|
+
const label = config?.state?.admin?.label ?? config?.state?.label;
|
|
44
|
+
if (typeof label === "string") return label;
|
|
45
|
+
return resource;
|
|
46
|
+
}
|
|
47
|
+
return resource;
|
|
48
|
+
}
|
|
49
|
+
function resolveActorType(actor, options, userId) {
|
|
50
|
+
if (options.actorType) return options.actorType;
|
|
51
|
+
if (options.userId !== void 0 || options.userName !== void 0) return userId && userId !== "system" ? "user" : "system";
|
|
52
|
+
return actor.actorType;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Write a custom audit log entry.
|
|
56
|
+
*
|
|
57
|
+
* Use this in jobs, custom actions, webhooks, or anywhere you want
|
|
58
|
+
* to record a meaningful event in the audit trail.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { logAuditEntry } from "@questpie/admin/server";
|
|
63
|
+
*
|
|
64
|
+
* // Inside a job handler:
|
|
65
|
+
* await logAuditEntry(ctx, {
|
|
66
|
+
* action: "bulk-email",
|
|
67
|
+
* resourceType: "system",
|
|
68
|
+
* resource: "newsletter",
|
|
69
|
+
* resourceLabel: "Q1 Campaign",
|
|
70
|
+
* metadata: { sentCount: 1234, jobId: ctx.id },
|
|
71
|
+
* });
|
|
72
|
+
*
|
|
73
|
+
* // Inside a collection hook with session:
|
|
74
|
+
* await logAuditEntry(ctx, {
|
|
75
|
+
* action: "export",
|
|
76
|
+
* resourceType: "collection",
|
|
77
|
+
* resource: "orders",
|
|
78
|
+
* resourceLabel: "Monthly export",
|
|
79
|
+
* changes: { format: { from: null, to: "csv" } },
|
|
80
|
+
* });
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
83
|
+
async function logAuditEntry(ctx, options) {
|
|
84
|
+
const collections = resolveCollections(ctx);
|
|
85
|
+
if (!collections?.[AUDIT_LOG_COLLECTION]) {
|
|
86
|
+
ctx.logger?.error?.("[Audit] Cannot write audit entry — audit log collection not available");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const actor = resolveActor(ctx);
|
|
90
|
+
const userName = options.userName ?? actor.userName;
|
|
91
|
+
const userId = options.userId !== void 0 ? options.userId : actor.userId;
|
|
92
|
+
const actorType = resolveActorType(actor, options, userId);
|
|
93
|
+
const resourceTypeLabel = getResourceTypeLabel(ctx, options.resourceType, options.resource);
|
|
94
|
+
const resourceLabel = options.resourceLabel ?? null;
|
|
95
|
+
const label = resourceLabel || "(unnamed)";
|
|
96
|
+
const title = `${userName} ${{
|
|
97
|
+
create: "created",
|
|
98
|
+
update: "updated",
|
|
99
|
+
delete: "deleted",
|
|
100
|
+
transition: "changed status of"
|
|
101
|
+
}[options.action] || options.action} ${resourceTypeLabel} '${label}'`;
|
|
102
|
+
await collections[AUDIT_LOG_COLLECTION].create({
|
|
103
|
+
action: options.action,
|
|
104
|
+
resourceType: options.resourceType,
|
|
105
|
+
resource: options.resource,
|
|
106
|
+
resourceId: options.resourceId ?? null,
|
|
107
|
+
resourceLabel,
|
|
108
|
+
userId,
|
|
109
|
+
userName,
|
|
110
|
+
locale: options.locale ?? null,
|
|
111
|
+
changes: options.changes ?? null,
|
|
112
|
+
metadata: {
|
|
113
|
+
actorType,
|
|
114
|
+
accessMode: ctx.accessMode ?? "system",
|
|
115
|
+
...options.metadata ?? {}
|
|
116
|
+
},
|
|
117
|
+
title
|
|
118
|
+
}, {
|
|
119
|
+
accessMode: "system",
|
|
120
|
+
db: ctx.db
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
//#endregion
|
|
125
|
+
export { logAuditEntry };
|
package/dist/server/plugin.d.mts
CHANGED