@brandlift/payload-loggs 1.0.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/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/collections/SiteActivityCollection.js +154 -0
- package/dist/collections/SiteActivityCollection.js.map +1 -0
- package/dist/collections/siteActivity.js +6 -0
- package/dist/collections/siteActivity.js.map +1 -0
- package/dist/core/automation/tasks/cleanup/cleanup.js +41 -0
- package/dist/core/automation/tasks/cleanup/cleanup.js.map +1 -0
- package/dist/core/buffer/bufferManager.js +41 -0
- package/dist/core/buffer/bufferManager.js.map +1 -0
- package/dist/core/buffer/types.js +3 -0
- package/dist/core/buffer/types.js.map +1 -0
- package/dist/core/events/emitter.js +16 -0
- package/dist/core/events/emitter.js.map +1 -0
- package/dist/core/log-builders/helpers/extractOperation/extractOperation.js +11 -0
- package/dist/core/log-builders/helpers/extractOperation/extractOperation.js.map +1 -0
- package/dist/core/log-builders/helpers/handleDebugMode.js +15 -0
- package/dist/core/log-builders/helpers/handleDebugMode.js.map +1 -0
- package/dist/core/log-builders/helpers/isOperationEnabled/isOperationEnabled.js +8 -0
- package/dist/core/log-builders/helpers/isOperationEnabled/isOperationEnabled.js.map +1 -0
- package/dist/core/log-builders/logBuilderManager/logBuilderManager.js +49 -0
- package/dist/core/log-builders/logBuilderManager/logBuilderManager.js.map +1 -0
- package/dist/hooks/collectionHooks.js +197 -0
- package/dist/hooks/collectionHooks.js.map +1 -0
- package/dist/hooks/globalHooks.js +56 -0
- package/dist/hooks/globalHooks.js.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.js +46 -0
- package/dist/plugin.js.map +1 -0
- package/dist/pluginUtils/attachCollectionConfig/attachCollectionConfig.js +77 -0
- package/dist/pluginUtils/attachCollectionConfig/attachCollectionConfig.js.map +1 -0
- package/dist/pluginUtils/attachGlobalConfig/attachGlobalConfig.js +54 -0
- package/dist/pluginUtils/attachGlobalConfig/attachGlobalConfig.js.map +1 -0
- package/dist/pluginUtils/configHelpers.js +40 -0
- package/dist/pluginUtils/configHelpers.js.map +1 -0
- package/dist/pluginUtils/formatUserDisplay.js +19 -0
- package/dist/pluginUtils/formatUserDisplay.js.map +1 -0
- package/dist/pluginUtils/getCollectionLabel.js +41 -0
- package/dist/pluginUtils/getCollectionLabel.js.map +1 -0
- package/dist/pluginUtils/getResourceTitle.js +31 -0
- package/dist/pluginUtils/getResourceTitle.js.map +1 -0
- package/dist/types/collection.js +3 -0
- package/dist/types/collection.js.map +1 -0
- package/dist/types/config.js +3 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/global.js +3 -0
- package/dist/types/global.js.map +1 -0
- package/dist/types/types.d.js +3 -0
- package/dist/types/types.d.js.map +1 -0
- package/dist/utils/prettyDebugLog.js +15 -0
- package/dist/utils/prettyDebugLog.js.map +1 -0
- package/package.json +160 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rushikesh Shinde
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# ๐ฆ Payload Loggs (`@brandlift/payload-loggs`)
|
|
2
|
+
|
|
3
|
+
**Payload Loggs** is a fast, zero-config plugin for [Payload CMS](https://payloadcms.com) that provides **high-level site activity feeds, security auditing, and event tracking**.
|
|
4
|
+
|
|
5
|
+
Designed for developers and teams, `payload-loggs` automatically records human-readable activity feeds (`"Page created"`, `"Page modified"`, `"Global Header V2 updated"`), attributes user actions with email shortforms (e.g., `rushikesh.s`), retains 30 days of activity data with automated daily cleanup at 00:01 AM, and enforces strict read-only audit access control.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## โก Key Features
|
|
10
|
+
|
|
11
|
+
- ๐ **Zero-Config Plug-and-Play**: Automatically tracks all collections & globals out-of-the-box without requiring tedious manual configuration.
|
|
12
|
+
- ๐ **High-Level Activity Feed**: Stores clean, human-readable activity titles (`"Page created"`, `"Page modified"`, `"Global Header V2 updated"`), resource names, collection labels, and document IDs.
|
|
13
|
+
- ๐ค **Shortform Email Attribution**: Formats user emails into clean display names (`rushikesh.s@brandlift.com` $\rightarrow$ `rushikesh.s`) while linking the user relationship and full email.
|
|
14
|
+
- ๐งน **Automated 30-Day Cleanup**: Automatically runs a background cleanup job daily at 00:01 AM (`1 0 * * *`) retaining the last 30 days of site activity and purging older logs.
|
|
15
|
+
- ๐ **Strict Read-Only Protection**: Entire `site-activity` collection is read-only in Admin UI & API (`create`, `update`, `delete` disabled). Log cleanup is handled strictly via automated background tasks.
|
|
16
|
+
- ๐ **Localization Support**: Automatically detects and records document locales when Payload localization is enabled.
|
|
17
|
+
- ๐ก๏ธ **Payload 3.0 Loop Prevention**: Uses atomic `req` context threading (`skipLogs: true`) to prevent infinite recursion loops.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## โ๏ธ Installation & Usage
|
|
22
|
+
|
|
23
|
+
Install with your preferred package manager:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add @brandlift/payload-loggs
|
|
27
|
+
|
|
28
|
+
# or
|
|
29
|
+
|
|
30
|
+
npm install @brandlift/payload-loggs
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then, register the plugin in your `payload.config.ts`:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { buildConfig } from 'payload'
|
|
37
|
+
import { siteActivity } from '@brandlift/payload-loggs'
|
|
38
|
+
|
|
39
|
+
export default buildConfig({
|
|
40
|
+
// ... your collections & globals ...
|
|
41
|
+
plugins: [
|
|
42
|
+
siteActivity(), // Zero-config: tracks all collections & globals automatically!
|
|
43
|
+
],
|
|
44
|
+
})
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## ๐ง Granular Configuration (Optional)
|
|
50
|
+
|
|
51
|
+
You can selectively configure tracking if you want to filter specific collections or custom cleanup rules:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
import { siteActivity } from '@brandlift/payload-loggs'
|
|
55
|
+
|
|
56
|
+
export default buildConfig({
|
|
57
|
+
plugins: [
|
|
58
|
+
siteActivity({
|
|
59
|
+
automation: {
|
|
60
|
+
logCleanup: {
|
|
61
|
+
cronTime: '1 0 * * *', // Runs daily at 00:01 AM (default)
|
|
62
|
+
olderThan: 2592000000, // Retains 30 days of logs in milliseconds (default)
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
collections: {
|
|
66
|
+
track: [
|
|
67
|
+
{ slug: 'pages' },
|
|
68
|
+
{ slug: 'posts' },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
globals: {
|
|
72
|
+
track: [
|
|
73
|
+
{ slug: 'header' },
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
77
|
+
],
|
|
78
|
+
})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## ๐ License
|
|
84
|
+
|
|
85
|
+
[MIT License](./LICENSE) - Copyright (c) 2026 Rushikesh Shinde
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
export const SITE_ACTIVITY_SLUG = 'site-activity';
|
|
2
|
+
export const SiteActivityCollection = {
|
|
3
|
+
slug: SITE_ACTIVITY_SLUG,
|
|
4
|
+
labels: {
|
|
5
|
+
plural: 'Site Activities',
|
|
6
|
+
singular: 'Site Activity'
|
|
7
|
+
},
|
|
8
|
+
admin: {
|
|
9
|
+
defaultColumns: [
|
|
10
|
+
'title',
|
|
11
|
+
'operation',
|
|
12
|
+
'resourceName',
|
|
13
|
+
'collectionLabel',
|
|
14
|
+
'userDisplay',
|
|
15
|
+
'createdAt'
|
|
16
|
+
],
|
|
17
|
+
useAsTitle: 'title'
|
|
18
|
+
},
|
|
19
|
+
access: {
|
|
20
|
+
read: ({ req })=>Boolean(req.user),
|
|
21
|
+
create: ()=>false,
|
|
22
|
+
update: ()=>false,
|
|
23
|
+
delete: ()=>false
|
|
24
|
+
},
|
|
25
|
+
fields: [
|
|
26
|
+
{
|
|
27
|
+
name: 'title',
|
|
28
|
+
type: 'text',
|
|
29
|
+
required: true,
|
|
30
|
+
admin: {
|
|
31
|
+
readOnly: true
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'operation',
|
|
36
|
+
type: 'select',
|
|
37
|
+
required: true,
|
|
38
|
+
options: [
|
|
39
|
+
{
|
|
40
|
+
label: 'Create',
|
|
41
|
+
value: 'create'
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: 'Update',
|
|
45
|
+
value: 'update'
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
label: 'Delete',
|
|
49
|
+
value: 'delete'
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
label: 'Login',
|
|
53
|
+
value: 'login'
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
label: 'Logout',
|
|
57
|
+
value: 'logout'
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
admin: {
|
|
61
|
+
readOnly: true
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: 'resourceName',
|
|
66
|
+
type: 'text',
|
|
67
|
+
admin: {
|
|
68
|
+
readOnly: true
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'resourceType',
|
|
73
|
+
type: 'select',
|
|
74
|
+
required: true,
|
|
75
|
+
options: [
|
|
76
|
+
{
|
|
77
|
+
label: 'Collection',
|
|
78
|
+
value: 'collection'
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
label: 'Global',
|
|
82
|
+
value: 'global'
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
admin: {
|
|
86
|
+
readOnly: true
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: 'collectionLabel',
|
|
91
|
+
type: 'text',
|
|
92
|
+
admin: {
|
|
93
|
+
readOnly: true
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'slug',
|
|
98
|
+
type: 'text',
|
|
99
|
+
required: true,
|
|
100
|
+
admin: {
|
|
101
|
+
readOnly: true
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: 'documentId',
|
|
106
|
+
type: 'text',
|
|
107
|
+
admin: {
|
|
108
|
+
readOnly: true
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'locale',
|
|
113
|
+
type: 'text',
|
|
114
|
+
admin: {
|
|
115
|
+
readOnly: true
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
name: 'user',
|
|
120
|
+
type: 'relationship',
|
|
121
|
+
relationTo: 'users',
|
|
122
|
+
admin: {
|
|
123
|
+
readOnly: true
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'userEmail',
|
|
128
|
+
type: 'text',
|
|
129
|
+
admin: {
|
|
130
|
+
readOnly: true
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
name: 'userDisplay',
|
|
135
|
+
type: 'text',
|
|
136
|
+
admin: {
|
|
137
|
+
readOnly: true
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'createdAt',
|
|
142
|
+
type: 'date',
|
|
143
|
+
admin: {
|
|
144
|
+
readOnly: true
|
|
145
|
+
},
|
|
146
|
+
defaultValue: ()=>new Date().toISOString(),
|
|
147
|
+
required: true
|
|
148
|
+
}
|
|
149
|
+
],
|
|
150
|
+
timestamps: false
|
|
151
|
+
};
|
|
152
|
+
export default SiteActivityCollection;
|
|
153
|
+
|
|
154
|
+
//# sourceMappingURL=SiteActivityCollection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/collections/SiteActivityCollection.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload';\n\nexport type SiteActivityLogData = {\n title: string;\n operation: 'create' | 'update' | 'delete' | 'login' | 'logout';\n resourceName?: string;\n resourceType: 'collection' | 'global';\n collectionLabel?: string;\n slug: string;\n documentId?: string;\n locale?: string;\n user?: string | number;\n userEmail?: string;\n userDisplay?: string;\n createdAt?: string;\n};\n\nexport const SITE_ACTIVITY_SLUG = 'site-activity';\n\nexport const SiteActivityCollection: CollectionConfig = {\n slug: SITE_ACTIVITY_SLUG,\n labels: {\n plural: 'Site Activities',\n singular: 'Site Activity',\n },\n admin: {\n defaultColumns: ['title', 'operation', 'resourceName', 'collectionLabel', 'userDisplay', 'createdAt'],\n useAsTitle: 'title',\n },\n access: {\n read: ({ req }) => Boolean(req.user),\n create: () => false,\n update: () => false,\n delete: () => false,\n },\n fields: [\n {\n name: 'title',\n type: 'text',\n required: true,\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'operation',\n type: 'select',\n required: true,\n options: [\n { label: 'Create', value: 'create' },\n { label: 'Update', value: 'update' },\n { label: 'Delete', value: 'delete' },\n { label: 'Login', value: 'login' },\n { label: 'Logout', value: 'logout' },\n ],\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'resourceName',\n type: 'text',\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'resourceType',\n type: 'select',\n required: true,\n options: [\n { label: 'Collection', value: 'collection' },\n { label: 'Global', value: 'global' },\n ],\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'collectionLabel',\n type: 'text',\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'slug',\n type: 'text',\n required: true,\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'documentId',\n type: 'text',\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'locale',\n type: 'text',\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'user',\n type: 'relationship',\n relationTo: 'users',\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'userEmail',\n type: 'text',\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'userDisplay',\n type: 'text',\n admin: {\n readOnly: true,\n },\n },\n {\n name: 'createdAt',\n type: 'date',\n admin: {\n readOnly: true,\n },\n defaultValue: () => new Date().toISOString(),\n required: true,\n },\n ],\n timestamps: false,\n};\n\nexport default SiteActivityCollection;\n"],"names":["SITE_ACTIVITY_SLUG","SiteActivityCollection","slug","labels","plural","singular","admin","defaultColumns","useAsTitle","access","read","req","Boolean","user","create","update","delete","fields","name","type","required","readOnly","options","label","value","relationTo","defaultValue","Date","toISOString","timestamps"],"mappings":"AAiBA,OAAO,MAAMA,qBAAqB,gBAAgB;AAElD,OAAO,MAAMC,yBAA2C;IACtDC,MAAMF;IACNG,QAAQ;QACNC,QAAQ;QACRC,UAAU;IACZ;IACAC,OAAO;QACLC,gBAAgB;YAAC;YAAS;YAAa;YAAgB;YAAmB;YAAe;SAAY;QACrGC,YAAY;IACd;IACAC,QAAQ;QACNC,MAAM,CAAC,EAAEC,GAAG,EAAE,GAAKC,QAAQD,IAAIE,IAAI;QACnCC,QAAQ,IAAM;QACdC,QAAQ,IAAM;QACdC,QAAQ,IAAM;IAChB;IACAC,QAAQ;QACN;YACEC,MAAM;YACNC,MAAM;YACNC,UAAU;YACVd,OAAO;gBACLe,UAAU;YACZ;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNC,UAAU;YACVE,SAAS;gBACP;oBAAEC,OAAO;oBAAUC,OAAO;gBAAS;gBACnC;oBAAED,OAAO;oBAAUC,OAAO;gBAAS;gBACnC;oBAAED,OAAO;oBAAUC,OAAO;gBAAS;gBACnC;oBAAED,OAAO;oBAASC,OAAO;gBAAQ;gBACjC;oBAAED,OAAO;oBAAUC,OAAO;gBAAS;aACpC;YACDlB,OAAO;gBACLe,UAAU;YACZ;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNb,OAAO;gBACLe,UAAU;YACZ;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNC,UAAU;YACVE,SAAS;gBACP;oBAAEC,OAAO;oBAAcC,OAAO;gBAAa;gBAC3C;oBAAED,OAAO;oBAAUC,OAAO;gBAAS;aACpC;YACDlB,OAAO;gBACLe,UAAU;YACZ;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNb,OAAO;gBACLe,UAAU;YACZ;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNC,UAAU;YACVd,OAAO;gBACLe,UAAU;YACZ;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNb,OAAO;gBACLe,UAAU;YACZ;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNb,OAAO;gBACLe,UAAU;YACZ;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNM,YAAY;YACZnB,OAAO;gBACLe,UAAU;YACZ;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNb,OAAO;gBACLe,UAAU;YACZ;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNb,OAAO;gBACLe,UAAU;YACZ;QACF;QACA;YACEH,MAAM;YACNC,MAAM;YACNb,OAAO;gBACLe,UAAU;YACZ;YACAK,cAAc,IAAM,IAAIC,OAAOC,WAAW;YAC1CR,UAAU;QACZ;KACD;IACDS,YAAY;AACd,EAAE;AAEF,eAAe5B,uBAAuB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/collections/siteActivity.ts"],"sourcesContent":["import type { GlobalHooksKeys } from '../types/global.js';\nimport { SiteActivityCollection } from './SiteActivityCollection.js';\nimport type { SiteActivityLogData } from './SiteActivityCollection.js';\nimport type { CollectionHooksKeys, CollectionHooksOperation } from '../types/collection.js';\n\nexport interface SiteActivityLog extends Omit<Partial<SiteActivityLogData>, 'operation'> {\n scope?: 'collection' | 'global' | 'field';\n hook?: CollectionHooksKeys | GlobalHooksKeys | string;\n operation: CollectionHooksOperation;\n timestamp?: Date;\n userAgent?: string;\n identifier?: string;\n}\n\nexport type { SiteActivityLogData };\nexport type TypedRootCollection = typeof SiteActivityCollection;\nexport const siteActivity = SiteActivityCollection;\nexport const siteActivityCollection = SiteActivityCollection;\n\nexport default siteActivity;\n"],"names":["SiteActivityCollection","siteActivity","siteActivityCollection"],"mappings":"AACA,SAASA,sBAAsB,QAAQ,8BAA8B;AAerE,OAAO,MAAMC,eAAeD,uBAAuB;AACnD,OAAO,MAAME,yBAAyBF,uBAAuB;AAE7D,eAAeC,aAAa"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export const DEFAULT_OLDER_THAN = 2592000000; // 30 days (30 * 24 * 60 * 60 * 1000)
|
|
2
|
+
export const DEFAULT_CRON_TIME = '1 0 * * *'; // At 00:01 AM daily
|
|
3
|
+
export const DEFAULT_QUEUE_NAME = 'payload-loggs-queue'; // default queue name
|
|
4
|
+
export const CLEANUP_TASK_SLUG = 'cleanup-site-activity-log';
|
|
5
|
+
export const CLEANUP_TASK_LABEL = 'payload loggs - cleanup site activities';
|
|
6
|
+
export const cleanupLogsTask = (params)=>{
|
|
7
|
+
const cronTime = params.pluginConfig.automation?.logCleanup?.cronTime ?? DEFAULT_CRON_TIME;
|
|
8
|
+
const queueName = params.pluginConfig.automation?.logCleanup?.queueName ?? DEFAULT_QUEUE_NAME;
|
|
9
|
+
const olderThan = params.pluginConfig.automation?.logCleanup?.olderThan ?? DEFAULT_OLDER_THAN;
|
|
10
|
+
return {
|
|
11
|
+
slug: CLEANUP_TASK_SLUG,
|
|
12
|
+
label: CLEANUP_TASK_LABEL,
|
|
13
|
+
schedule: [
|
|
14
|
+
{
|
|
15
|
+
cron: cronTime,
|
|
16
|
+
queue: queueName
|
|
17
|
+
}
|
|
18
|
+
],
|
|
19
|
+
handler: async ({ req })=>{
|
|
20
|
+
const millisecondsAgo = new Date(Date.now() - olderThan);
|
|
21
|
+
try {
|
|
22
|
+
await req.payload.delete({
|
|
23
|
+
collection: params.internalCollectionConfig.slug,
|
|
24
|
+
where: {
|
|
25
|
+
createdAt: {
|
|
26
|
+
less_than: millisecondsAgo.toISOString()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
} // eslint-disable-next-line unused-imports/no-unused-vars
|
|
31
|
+
catch (error) {
|
|
32
|
+
req.payload.logger.error(`Error while cleaning old logs โ task: ${CLEANUP_TASK_SLUG}`);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
output: {}
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=cleanup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/core/automation/tasks/cleanup/cleanup.ts"],"sourcesContent":["import type { CollectionConfig, TaskConfig } from 'payload';\n\nimport type { PluginConfig } from '../../../../types/config.js';\n\nexport const DEFAULT_OLDER_THAN = 2592000000; // 30 days (30 * 24 * 60 * 60 * 1000)\nexport const DEFAULT_CRON_TIME = '1 0 * * *'; // At 00:01 AM daily\nexport const DEFAULT_QUEUE_NAME = 'payload-loggs-queue'; // default queue name\nexport const CLEANUP_TASK_SLUG = 'cleanup-site-activity-log';\nexport const CLEANUP_TASK_LABEL = 'payload loggs - cleanup site activities';\n\ntype CleanupLogsTaskParams = {\n pluginConfig: PluginConfig;\n internalCollectionConfig: CollectionConfig;\n};\nexport const cleanupLogsTask = (params: CleanupLogsTaskParams): TaskConfig<typeof CLEANUP_TASK_SLUG> => {\n const cronTime = params.pluginConfig.automation?.logCleanup?.cronTime ?? DEFAULT_CRON_TIME;\n const queueName = params.pluginConfig.automation?.logCleanup?.queueName ?? DEFAULT_QUEUE_NAME;\n const olderThan = params.pluginConfig.automation?.logCleanup?.olderThan ?? DEFAULT_OLDER_THAN;\n\n return {\n slug: CLEANUP_TASK_SLUG,\n label: CLEANUP_TASK_LABEL,\n schedule: [{ cron: cronTime, queue: queueName }],\n handler: async ({ req }) => {\n const millisecondsAgo = new Date(Date.now() - olderThan);\n try {\n await req.payload.delete({\n collection: params.internalCollectionConfig.slug,\n where: { createdAt: { less_than: millisecondsAgo.toISOString() } },\n });\n }\n // eslint-disable-next-line unused-imports/no-unused-vars\n catch (error) {\n req.payload.logger.error(`Error while cleaning old logs โ task: ${CLEANUP_TASK_SLUG}`);\n }\n\n return { output: {} };\n },\n };\n};\n"],"names":["DEFAULT_OLDER_THAN","DEFAULT_CRON_TIME","DEFAULT_QUEUE_NAME","CLEANUP_TASK_SLUG","CLEANUP_TASK_LABEL","cleanupLogsTask","params","cronTime","pluginConfig","automation","logCleanup","queueName","olderThan","slug","label","schedule","cron","queue","handler","req","millisecondsAgo","Date","now","payload","delete","collection","internalCollectionConfig","where","createdAt","less_than","toISOString","error","logger","output"],"mappings":"AAIA,OAAO,MAAMA,qBAAqB,WAAW,CAAC,qCAAqC;AACnF,OAAO,MAAMC,oBAAoB,YAAY,CAAC,oBAAoB;AAClE,OAAO,MAAMC,qBAAqB,sBAAsB,CAAC,qBAAqB;AAC9E,OAAO,MAAMC,oBAAoB,4BAA4B;AAC7D,OAAO,MAAMC,qBAAqB,0CAA0C;AAM5E,OAAO,MAAMC,kBAAkB,CAACC;IAC9B,MAAMC,WAAWD,OAAOE,YAAY,CAACC,UAAU,EAAEC,YAAYH,YAAYN;IACzE,MAAMU,YAAYL,OAAOE,YAAY,CAACC,UAAU,EAAEC,YAAYC,aAAaT;IAC3E,MAAMU,YAAYN,OAAOE,YAAY,CAACC,UAAU,EAAEC,YAAYE,aAAaZ;IAE3E,OAAO;QACLa,MAAMV;QACNW,OAAOV;QACPW,UAAU;YAAC;gBAAEC,MAAMT;gBAAUU,OAAON;YAAU;SAAE;QAChDO,SAAS,OAAO,EAAEC,GAAG,EAAE;YACrB,MAAMC,kBAAkB,IAAIC,KAAKA,KAAKC,GAAG,KAAKV;YAC9C,IAAI;gBACF,MAAMO,IAAII,OAAO,CAACC,MAAM,CAAC;oBACvBC,YAAYnB,OAAOoB,wBAAwB,CAACb,IAAI;oBAChDc,OAAO;wBAAEC,WAAW;4BAAEC,WAAWT,gBAAgBU,WAAW;wBAAG;oBAAE;gBACnE;YACF,EACA,yDAAyD;YACzD,OAAOC,OAAO;gBACZZ,IAAII,OAAO,CAACS,MAAM,CAACD,KAAK,CAAC,CAAC,sCAAsC,EAAE5B,mBAAmB;YACvF;YAEA,OAAO;gBAAE8B,QAAQ,CAAC;YAAE;QACtB;IACF;AACF,EAAE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { onEventLog } from './../../core/events/emitter.js';
|
|
2
|
+
export const DEFAULT_INTERVAL_BUFFER = 10000;
|
|
3
|
+
export const DEFAULT_BUFFER_SIZE = 10;
|
|
4
|
+
export const DEFAULT_BUFFER_STRATEGY = 'time';
|
|
5
|
+
export const bufferStore = [];
|
|
6
|
+
const flushBuffer = async (params)=>{
|
|
7
|
+
const logsToInsert = [
|
|
8
|
+
...bufferStore
|
|
9
|
+
];
|
|
10
|
+
bufferStore.length = 0;
|
|
11
|
+
await Promise.all(logsToInsert.map((log)=>params.payload.create({
|
|
12
|
+
collection: params.internalCollectionConfig?.slug,
|
|
13
|
+
data: log
|
|
14
|
+
})));
|
|
15
|
+
};
|
|
16
|
+
export const bufferManager = (params)=>{
|
|
17
|
+
const bufferConfig = params.bufferConfig;
|
|
18
|
+
const size = bufferConfig?.size ?? DEFAULT_BUFFER_SIZE;
|
|
19
|
+
const interval = bufferConfig?.time ?? DEFAULT_INTERVAL_BUFFER;
|
|
20
|
+
const flushStrategy = bufferConfig?.flushStrategy ?? DEFAULT_BUFFER_STRATEGY;
|
|
21
|
+
onEventLog('logGenerated', async (log)=>{
|
|
22
|
+
bufferStore.push(log);
|
|
23
|
+
if (flushStrategy === 'size') {
|
|
24
|
+
if (bufferStore.length >= size) {
|
|
25
|
+
await flushBuffer(params);
|
|
26
|
+
}
|
|
27
|
+
} else if (flushStrategy === 'realtime') {
|
|
28
|
+
await flushBuffer(params);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
if (flushStrategy === 'time') {
|
|
32
|
+
// Every few seconds, empty the buffer (even if it's not full)
|
|
33
|
+
setInterval(async ()=>{
|
|
34
|
+
if (bufferStore.length > 0) {
|
|
35
|
+
await flushBuffer(params);
|
|
36
|
+
}
|
|
37
|
+
}, interval);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=bufferManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/core/buffer/bufferManager.ts"],"sourcesContent":["import type { CollectionConfig, Payload } from 'payload';\n\nimport type { BufferConfig } from './types.js';\nimport { onEventLog } from './../../core/events/emitter.js';\nimport type { SiteActivityLog } from '../../collections/siteActivity.js';\n\nexport const DEFAULT_INTERVAL_BUFFER = 10000 as NonNullable<BufferConfig['time']>;\nexport const DEFAULT_BUFFER_SIZE = 10 as NonNullable<BufferConfig['size']>;\nexport const DEFAULT_BUFFER_STRATEGY = 'time' as NonNullable<BufferConfig['flushStrategy']>;\n\nexport const bufferStore: SiteActivityLog[] = [];\n\ntype FlushBufferParams = {\n payload: Payload;\n internalCollectionConfig: CollectionConfig;\n};\n\nconst flushBuffer = async (params: FlushBufferParams) => {\n const logsToInsert = [...bufferStore];\n bufferStore.length = 0;\n await Promise.all(\n logsToInsert.map(log =>\n params.payload.create({\n collection: params.internalCollectionConfig?.slug,\n data: log,\n }),\n ),\n );\n};\n\ntype BufferConfigParams = {\n payload: Payload;\n bufferConfig?: BufferConfig;\n internalCollectionConfig: CollectionConfig;\n};\n\nexport const bufferManager = (params: BufferConfigParams) => {\n const bufferConfig = params.bufferConfig;\n const size = bufferConfig?.size ?? DEFAULT_BUFFER_SIZE;\n const interval = bufferConfig?.time ?? DEFAULT_INTERVAL_BUFFER;\n const flushStrategy = bufferConfig?.flushStrategy ?? DEFAULT_BUFFER_STRATEGY;\n\n onEventLog('logGenerated', async (log: SiteActivityLog) => {\n bufferStore.push(log);\n\n if (flushStrategy === 'size') {\n if (bufferStore.length >= size) {\n await flushBuffer(params);\n }\n }\n else if (flushStrategy === 'realtime') {\n await flushBuffer(params);\n }\n });\n\n if (flushStrategy === 'time') {\n // Every few seconds, empty the buffer (even if it's not full)\n setInterval(async () => {\n if (bufferStore.length > 0) {\n await flushBuffer(params);\n }\n }, interval);\n }\n};\n"],"names":["onEventLog","DEFAULT_INTERVAL_BUFFER","DEFAULT_BUFFER_SIZE","DEFAULT_BUFFER_STRATEGY","bufferStore","flushBuffer","params","logsToInsert","length","Promise","all","map","log","payload","create","collection","internalCollectionConfig","slug","data","bufferManager","bufferConfig","size","interval","time","flushStrategy","push","setInterval"],"mappings":"AAGA,SAASA,UAAU,QAAQ,iCAAiC;AAG5D,OAAO,MAAMC,0BAA0B,MAA2C;AAClF,OAAO,MAAMC,sBAAsB,GAAwC;AAC3E,OAAO,MAAMC,0BAA0B,OAAqD;AAE5F,OAAO,MAAMC,cAAiC,EAAE,CAAC;AAOjD,MAAMC,cAAc,OAAOC;IACzB,MAAMC,eAAe;WAAIH;KAAY;IACrCA,YAAYI,MAAM,GAAG;IACrB,MAAMC,QAAQC,GAAG,CACfH,aAAaI,GAAG,CAACC,CAAAA,MACfN,OAAOO,OAAO,CAACC,MAAM,CAAC;YACpBC,YAAYT,OAAOU,wBAAwB,EAAEC;YAC7CC,MAAMN;QACR;AAGN;AAQA,OAAO,MAAMO,gBAAgB,CAACb;IAC5B,MAAMc,eAAed,OAAOc,YAAY;IACxC,MAAMC,OAAOD,cAAcC,QAAQnB;IACnC,MAAMoB,WAAWF,cAAcG,QAAQtB;IACvC,MAAMuB,gBAAgBJ,cAAcI,iBAAiBrB;IAErDH,WAAW,gBAAgB,OAAOY;QAChCR,YAAYqB,IAAI,CAACb;QAEjB,IAAIY,kBAAkB,QAAQ;YAC5B,IAAIpB,YAAYI,MAAM,IAAIa,MAAM;gBAC9B,MAAMhB,YAAYC;YACpB;QACF,OACK,IAAIkB,kBAAkB,YAAY;YACrC,MAAMnB,YAAYC;QACpB;IACF;IAEA,IAAIkB,kBAAkB,QAAQ;QAC5B,8DAA8D;QAC9DE,YAAY;YACV,IAAItB,YAAYI,MAAM,GAAG,GAAG;gBAC1B,MAAMH,YAAYC;YACpB;QACF,GAAGgB;IACL;AACF,EAAE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/core/buffer/types.ts"],"sourcesContent":["export interface BufferDebugFields {\n flushStrategy: boolean;\n interval: boolean;\n size: boolean;\n}\n\nexport type BufferDebugModeConfig = {\n /**\n * ๐ Enable or disable debug mode\n *\n * ๐ long.\n *\n * ๐@type {'manual' | 'table'}\n *\n * @default false\n *\n */\n enabled?: boolean;\n} | true;\n\nexport interface BufferConfig {\n /**\n * ๐ The basics of injecting logs into the database\n *\n *\n *\n * @default \"time\"\n *\n *\n * ๐ฆ Usage Example\n *\n * @example <caption>๐งช Data injection based on log count</caption>\n * ```ts\n * flushStrategy: 'size',\n * ```\n * ### โ ๏ธ Critical Notes\n * - If you use the size method, your logs will be stored in RAM before being injected into the database.\n *\n */\n flushStrategy?: 'realtime' | 'size' | 'time';\n /**\n * ๐ Maximum number of logs before injection\n *\n * ๐ If the number of logs stored in the buffer memory reaches this number, data will be injected.\n *\n * ๐@type {number}\n *\n * @default 10\n *\n *\n * ๐ฆ Usage Example\n *\n * @example <caption>๐งช Up to 18 logs can be stored in memory</caption>\n * ```ts\n * size: 18\n * ```\n *\n * ---\n * ### โ ๏ธ Critical Notes\n * - Works when flushStrategy is equal to size\n * - If you enter the number 1, use the realtime method and do not define a value for this.\n * ```ts\n * flushStrategy: 'realtime',\n * ```\n * - Very high numbers will increase RAM usage.\n * - If you set a high number and on the other hand the logs produced are low, the logs will be recorded later.\n *\n */\n size?: number;\n\n /**\n * ๐ Maximum time to inject logs into the buffer relative to the last injection\n *\n *\n * ๐@type {Duration}\n *\n * @default 10000\n *\n *\n * ๐ฆ Usage Example\n *\n * @example <caption>๐งช Inject logs every 1 minute</caption>\n * ```ts\n * time: 5000\n * ```\n *\n * ---\n * ### โ ๏ธ Critical Notes\n * - If you want to enter a value of \"1s\", set the flushStrategy value to time instead and do not define a value for the time property.\n * ```ts\n * flushStrategy: 'realtime',\n * ```\n * - If you set the time too low, the CPU will be severely affected.\n * - If you allow too much time, the logs will be recorded later. If the server crashes, the logs will be lost.\n */\n time?: number;\n}\n"],"names":[],"mappings":"AAoBA,WA4EC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/* eslint-disable no-restricted-globals */ import { EventEmitter } from 'node:events';
|
|
2
|
+
const globalEmitter = global.payloadAuditorEmitter || new EventEmitter();
|
|
3
|
+
if (!global.payloadAuditorEmitter) {
|
|
4
|
+
;
|
|
5
|
+
global.payloadAuditorEmitter = globalEmitter;
|
|
6
|
+
}
|
|
7
|
+
export const emitEvent = (event, data)=>{
|
|
8
|
+
setImmediate(()=>{
|
|
9
|
+
globalEmitter.emit(event, data);
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
export const onEventLog = (event, handler)=>{
|
|
13
|
+
globalEmitter.on(event, handler);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=emitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/core/events/emitter.ts"],"sourcesContent":["/* eslint-disable no-restricted-globals */\nimport { EventEmitter } from 'node:events';\n\nimport type { SiteActivityLog } from '../../collections/siteActivity.js';\n\nconst globalEmitter = (global as any).payloadAuditorEmitter || new EventEmitter();\n\nif (!(global as any).payloadAuditorEmitter) {\n ;(global as any).payloadAuditorEmitter = globalEmitter;\n}\n\nexport const emitEvent = <T>(event: string, data: T) => {\n setImmediate(() => {\n globalEmitter.emit(event, data);\n });\n};\n\nexport const onEventLog = (event: string, handler: (log: SiteActivityLog) => Promise<void>) => {\n globalEmitter.on(event, handler);\n};\n"],"names":["EventEmitter","globalEmitter","global","payloadAuditorEmitter","emitEvent","event","data","setImmediate","emit","onEventLog","handler","on"],"mappings":"AAAA,wCAAwC,GACxC,SAASA,YAAY,QAAQ,cAAc;AAI3C,MAAMC,gBAAgB,AAACC,OAAeC,qBAAqB,IAAI,IAAIH;AAEnE,IAAI,CAAC,AAACE,OAAeC,qBAAqB,EAAE;;IACxCD,OAAeC,qBAAqB,GAAGF;AAC3C;AAEA,OAAO,MAAMG,YAAY,CAAIC,OAAeC;IAC1CC,aAAa;QACXN,cAAcO,IAAI,CAACH,OAAOC;IAC5B;AACF,EAAE;AAEF,OAAO,MAAMG,aAAa,CAACJ,OAAeK;IACxCT,cAAcU,EAAE,CAACN,OAAOK;AAC1B,EAAE"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
const regex = /[A-Z]?[a-z]+/g;
|
|
2
|
+
const splitCamelCase = (str)=>{
|
|
3
|
+
return str.match(regex) || [];
|
|
4
|
+
};
|
|
5
|
+
export const extractOperation = (params)=>{
|
|
6
|
+
const operation = params.hookArgs// @ts-expect-error
|
|
7
|
+
.operation || splitCamelCase(params.targetHookName).splice(1).join('-').toLowerCase();
|
|
8
|
+
return operation;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=extractOperation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/core/log-builders/helpers/extractOperation/extractOperation.ts"],"sourcesContent":["import type { GlobalHooksArgsParameterUnion, GlobalHooksKeys } from '../../../../types/global.js';\nimport type { CollectionHooksArgsParameterUnion, CollectionHooksKeys, CollectionHooksOperation } from '../../../../types/collection.js';\n\ninterface Params {\n hookArgs: CollectionHooksArgsParameterUnion | GlobalHooksArgsParameterUnion;\n targetHookName: CollectionHooksKeys | GlobalHooksKeys;\n}\n\nconst regex = /[A-Z]?[a-z]+/g;\nconst splitCamelCase = (str: string) => {\n return str.match(regex) || [];\n};\n\nexport const extractOperation = (params: Params): CollectionHooksOperation => {\n const operation = params\n .hookArgs\n // @ts-expect-error\n .operation\n || splitCamelCase(params.targetHookName).splice(1).join('-').toLowerCase();\n\n return operation as CollectionHooksOperation;\n};\n"],"names":["regex","splitCamelCase","str","match","extractOperation","params","operation","hookArgs","targetHookName","splice","join","toLowerCase"],"mappings":"AAQA,MAAMA,QAAQ;AACd,MAAMC,iBAAiB,CAACC;IACtB,OAAOA,IAAIC,KAAK,CAACH,UAAU,EAAE;AAC/B;AAEA,OAAO,MAAMI,mBAAmB,CAACC;IAC/B,MAAMC,YAAYD,OACfE,QAAQ,AACX,mBAAmB;KAChBD,SAAS,IACPL,eAAeI,OAAOG,cAAc,EAAEC,MAAM,CAAC,GAAGC,IAAI,CAAC,KAAKC,WAAW;IAE1E,OAAOL;AACT,EAAE"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { prettyDebugLog } from '../../../utils/prettyDebugLog.js';
|
|
2
|
+
export const handleDebugMode = (params)=>{
|
|
3
|
+
const hookDebugConfig = typeof params.hookLevelConfig === 'boolean' ? undefined : params.hookLevelConfig?.debug;
|
|
4
|
+
const operationDebugConfig = params.operationLevelConfig?.debug;
|
|
5
|
+
const isDebugEnabled = operationDebugConfig || hookDebugConfig;
|
|
6
|
+
if (isDebugEnabled) {
|
|
7
|
+
prettyDebugLog({
|
|
8
|
+
title: params.hookName,
|
|
9
|
+
data: params.logData,
|
|
10
|
+
subtitle: params.operation
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
//# sourceMappingURL=handleDebugMode.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/log-builders/helpers/handleDebugMode.ts"],"sourcesContent":["import { prettyDebugLog } from '../../../utils/prettyDebugLog.js';\nimport type { SiteActivityLog } from '../../../collections/siteActivity.js';\nimport type { GlobalHookConfigForTracking, GlobalHooksKeys, GlobalOperationLogConfig } from '../../../types/global.js';\nimport type { CollectionHooksKeys, CollectionHooksOperation, CollectionsHookConfigForTracking } from '../../../types/collection.js';\n\ntype Params = {\n hookLevelConfig?: CollectionsHookConfigForTracking[CollectionHooksKeys] | GlobalHookConfigForTracking[GlobalHooksKeys];\n operationLevelConfig?: GlobalOperationLogConfig;\n logData: SiteActivityLog;\n operation: CollectionHooksOperation;\n hookName: CollectionHooksKeys;\n};\n\nexport const handleDebugMode = (params: Params) => {\n const hookDebugConfig = typeof params.hookLevelConfig === 'boolean'\n ? undefined\n : params.hookLevelConfig?.debug;\n const operationDebugConfig = params.operationLevelConfig?.debug;\n\n const isDebugEnabled = operationDebugConfig || hookDebugConfig;\n\n if (isDebugEnabled) {\n prettyDebugLog({\n title: params.hookName,\n data: params.logData,\n subtitle: params.operation,\n });\n }\n};\n"],"names":["prettyDebugLog","handleDebugMode","params","hookDebugConfig","hookLevelConfig","undefined","debug","operationDebugConfig","operationLevelConfig","isDebugEnabled","title","hookName","data","logData","subtitle","operation"],"mappings":"AAAA,SAASA,cAAc,QAAQ,mCAAmC;AAalE,OAAO,MAAMC,kBAAkB,CAACC;IAC9B,MAAMC,kBAAkB,OAAOD,OAAOE,eAAe,KAAK,YACtDC,YACAH,OAAOE,eAAe,EAAEE;IAC5B,MAAMC,uBAAuBL,OAAOM,oBAAoB,EAAEF;IAE1D,MAAMG,iBAAiBF,wBAAwBJ;IAE/C,IAAIM,gBAAgB;QAClBT,eAAe;YACbU,OAAOR,OAAOS,QAAQ;YACtBC,MAAMV,OAAOW,OAAO;YACpBC,UAAUZ,OAAOa,SAAS;QAC5B;IACF;AACF,EAAE"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const isBooleanConfig = (v)=>typeof v === 'boolean' ? v : v?.enabled;
|
|
2
|
+
export const checkIsOperationEnabled = (params)=>{
|
|
3
|
+
const hookLevelEnabled = isBooleanConfig(params.hookLevelConfig);
|
|
4
|
+
const operationLevelEnabled = isBooleanConfig(params.hookOperationLevelConfig);
|
|
5
|
+
return operationLevelEnabled !== false && hookLevelEnabled !== false && !!(operationLevelEnabled || hookLevelEnabled);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
//# sourceMappingURL=isOperationEnabled.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../src/core/log-builders/helpers/isOperationEnabled/isOperationEnabled.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload';\n\nimport type { CollectionOperationLogConfig, CollectionsHookConfigForTracking } from '../../../../types/collection.js';\nimport type { GlobalHookConfigForTracking, GlobalHooksKeys, GlobalOperationLogConfig } from '../../../../types/global.js';\n\ntype CollectionHooksKeys = keyof NonNullable<CollectionConfig['hooks']>;\n\ninterface Params {\n hookOperationLevelConfig?: CollectionOperationLogConfig<CollectionHooksKeys> | GlobalOperationLogConfig<GlobalHooksKeys> | true;\n hookLevelConfig?: CollectionsHookConfigForTracking[CollectionHooksKeys] | GlobalHookConfigForTracking[GlobalHooksKeys];\n}\n\nexport const isBooleanConfig = <T>(v: T): boolean | undefined => typeof v === 'boolean'\n ? v\n // @ts-expect-error\n : v?.enabled;\n\nexport const checkIsOperationEnabled = (params: Params): boolean => {\n const hookLevelEnabled = isBooleanConfig(params.hookLevelConfig);\n const operationLevelEnabled = isBooleanConfig(params.hookOperationLevelConfig);\n\n return (operationLevelEnabled !== false) && (hookLevelEnabled !== false)\n && !!(operationLevelEnabled || hookLevelEnabled);\n};\n"],"names":["isBooleanConfig","v","enabled","checkIsOperationEnabled","params","hookLevelEnabled","hookLevelConfig","operationLevelEnabled","hookOperationLevelConfig"],"mappings":"AAYA,OAAO,MAAMA,kBAAkB,CAAIC,IAA8B,OAAOA,MAAM,YAC1EA,IAEAA,GAAGC,QAAQ;AAEf,OAAO,MAAMC,0BAA0B,CAACC;IACtC,MAAMC,mBAAmBL,gBAAgBI,OAAOE,eAAe;IAC/D,MAAMC,wBAAwBP,gBAAgBI,OAAOI,wBAAwB;IAE7E,OAAO,AAACD,0BAA0B,SAAWF,qBAAqB,SAC7D,CAAC,CAAEE,CAAAA,yBAAyBF,gBAAe;AAClD,EAAE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { emitEvent } from '../../events/emitter.js';
|
|
2
|
+
import { handleDebugMode } from '../helpers/handleDebugMode.js';
|
|
3
|
+
import { extractOperation } from '../helpers/extractOperation/extractOperation.js';
|
|
4
|
+
import { checkIsOperationEnabled } from '../helpers/isOperationEnabled/isOperationEnabled.js';
|
|
5
|
+
export const logBuilderManager = async (params)=>{
|
|
6
|
+
const operation = extractOperation(params);
|
|
7
|
+
// @ts-expect-error
|
|
8
|
+
const hookOperationLevelConfig = params.targetHookLevelConfig?.[operation];
|
|
9
|
+
const hookLevelConfig = typeof params.targetHookLevelConfig === 'boolean' ? undefined : params.targetHookLevelConfig;
|
|
10
|
+
const isOperationEnabled = checkIsOperationEnabled({
|
|
11
|
+
hookLevelConfig: params.targetHookLevelConfig,
|
|
12
|
+
hookOperationLevelConfig
|
|
13
|
+
});
|
|
14
|
+
const baseLog = {
|
|
15
|
+
scope: params.scopeSlug,
|
|
16
|
+
hook: params.targetHookName,
|
|
17
|
+
identifier: params.identifier,
|
|
18
|
+
operation,
|
|
19
|
+
timestamp: new Date(),
|
|
20
|
+
// @ts-expect-error
|
|
21
|
+
userAgent: params.hookArgs.req.headers?.get?.('user-agent') || 'unknown'
|
|
22
|
+
};
|
|
23
|
+
if (isOperationEnabled) {
|
|
24
|
+
const { hook, operation, ...otherLogData } = baseLog;
|
|
25
|
+
const customLogData = {
|
|
26
|
+
...baseLog,
|
|
27
|
+
...await hookLevelConfig?.customLogger?.(// @ts-expect-error
|
|
28
|
+
params.hookArgs, otherLogData),
|
|
29
|
+
...await hookOperationLevelConfig?.customLogger?.(// @ts-expect-error
|
|
30
|
+
params.hookArgs, otherLogData),
|
|
31
|
+
hook,
|
|
32
|
+
operation
|
|
33
|
+
};
|
|
34
|
+
const skipToDatabase = hookLevelConfig?.debug === true || hookLevelConfig?.debug?.skipDatabaseSave || hookOperationLevelConfig?.debug === true || hookOperationLevelConfig?.debug?.skipDatabaseSave;
|
|
35
|
+
if (!skipToDatabase) {
|
|
36
|
+
emitEvent('logGenerated', customLogData);
|
|
37
|
+
}
|
|
38
|
+
handleDebugMode({
|
|
39
|
+
logData: customLogData,
|
|
40
|
+
hookLevelConfig: params.targetHookLevelConfig,
|
|
41
|
+
hookName: params.targetHookName,
|
|
42
|
+
operation,
|
|
43
|
+
// @ts-expect-error
|
|
44
|
+
operationLevelConfig: hookOperationLevelConfig
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=logBuilderManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../src/core/log-builders/logBuilderManager/logBuilderManager.ts"],"sourcesContent":["import { emitEvent } from '../../events/emitter.js';\nimport type { PluginConfig } from '../../../types/config.js';\nimport { handleDebugMode } from '../helpers/handleDebugMode.js';\nimport type { SiteActivityLog } from '../../../collections/siteActivity.js';\nimport { extractOperation } from '../helpers/extractOperation/extractOperation.js';\nimport { checkIsOperationEnabled } from '../helpers/isOperationEnabled/isOperationEnabled.js';\nimport type { GlobalHookConfigForTracking, GlobalHooksArgsParameterUnion, GlobalHooksKeys, GlobalOperationLogConfig } from '../../../types/global.js';\nimport type { CollectionHooksArgsParameterUnion, CollectionHooksKeys, CollectionOperationLogConfig, CollectionsHookConfigForTracking } from '../../../types/collection.js';\n\nexport type LogBuilderManager = {\n hookArgs: CollectionHooksArgsParameterUnion | GlobalHooksArgsParameterUnion;\n pluginConfig: PluginConfig;\n targetHookName: CollectionHooksKeys;\n targetHookLevelConfig: CollectionsHookConfigForTracking[CollectionHooksKeys] | GlobalHookConfigForTracking[GlobalHooksKeys];\n scopeSlug: NonNullable<SiteActivityLog['scope']>;\n identifier: string;\n};\n\nexport const logBuilderManager = async (params: LogBuilderManager) => {\n const operation = extractOperation(params) as any;\n\n // @ts-expect-error\n const hookOperationLevelConfig = params\n .targetHookLevelConfig?.[operation] as CollectionOperationLogConfig | GlobalOperationLogConfig | undefined;\n const hookLevelConfig = typeof params.targetHookLevelConfig === 'boolean'\n ? undefined\n : params.targetHookLevelConfig;\n const isOperationEnabled = checkIsOperationEnabled({\n hookLevelConfig: params.targetHookLevelConfig,\n hookOperationLevelConfig,\n });\n\n const baseLog: SiteActivityLog = {\n scope: params.scopeSlug,\n hook: params.targetHookName,\n identifier: params.identifier,\n operation,\n timestamp: new Date(),\n // @ts-expect-error\n userAgent: params.hookArgs.req.headers?.get?.('user-agent') || 'unknown',\n };\n\n if (isOperationEnabled) {\n const { hook, operation, ...otherLogData } = baseLog;\n const customLogData = {\n ...baseLog,\n ...(await hookLevelConfig?.customLogger?.(\n // @ts-expect-error\n params.hookArgs,\n otherLogData,\n )),\n ...(await hookOperationLevelConfig?.customLogger?.(\n // @ts-expect-error\n params.hookArgs,\n otherLogData,\n )),\n hook,\n operation,\n } as SiteActivityLog;\n\n const skipToDatabase = hookLevelConfig?.debug === true\n || hookLevelConfig?.debug?.skipDatabaseSave\n || hookOperationLevelConfig?.debug === true\n || hookOperationLevelConfig?.debug?.skipDatabaseSave;\n\n if (!skipToDatabase) {\n emitEvent<SiteActivityLog>('logGenerated', customLogData);\n }\n\n handleDebugMode({\n logData: customLogData,\n hookLevelConfig: params.targetHookLevelConfig,\n hookName: params.targetHookName,\n operation,\n // @ts-expect-error\n operationLevelConfig: hookOperationLevelConfig,\n });\n }\n};\n"],"names":["emitEvent","handleDebugMode","extractOperation","checkIsOperationEnabled","logBuilderManager","params","operation","hookOperationLevelConfig","targetHookLevelConfig","hookLevelConfig","undefined","isOperationEnabled","baseLog","scope","scopeSlug","hook","targetHookName","identifier","timestamp","Date","userAgent","hookArgs","req","headers","get","otherLogData","customLogData","customLogger","skipToDatabase","debug","skipDatabaseSave","logData","hookName","operationLevelConfig"],"mappings":"AAAA,SAASA,SAAS,QAAQ,0BAA0B;AAEpD,SAASC,eAAe,QAAQ,gCAAgC;AAEhE,SAASC,gBAAgB,QAAQ,kDAAkD;AACnF,SAASC,uBAAuB,QAAQ,sDAAsD;AAa9F,OAAO,MAAMC,oBAAoB,OAAOC;IACtC,MAAMC,YAAYJ,iBAAiBG;IAEnC,mBAAmB;IACnB,MAAME,2BAA2BF,OAC9BG,qBAAqB,EAAE,CAACF,UAAU;IACrC,MAAMG,kBAAkB,OAAOJ,OAAOG,qBAAqB,KAAK,YAC5DE,YACAL,OAAOG,qBAAqB;IAChC,MAAMG,qBAAqBR,wBAAwB;QACjDM,iBAAiBJ,OAAOG,qBAAqB;QAC7CD;IACF;IAEA,MAAMK,UAA2B;QAC/BC,OAAOR,OAAOS,SAAS;QACvBC,MAAMV,OAAOW,cAAc;QAC3BC,YAAYZ,OAAOY,UAAU;QAC7BX;QACAY,WAAW,IAAIC;QACf,mBAAmB;QACnBC,WAAWf,OAAOgB,QAAQ,CAACC,GAAG,CAACC,OAAO,EAAEC,MAAM,iBAAiB;IACjE;IAEA,IAAIb,oBAAoB;QACtB,MAAM,EAAEI,IAAI,EAAET,SAAS,EAAE,GAAGmB,cAAc,GAAGb;QAC7C,MAAMc,gBAAgB;YACpB,GAAGd,OAAO;YACV,GAAI,MAAMH,iBAAiBkB,eACzB,mBAAmB;YACnBtB,OAAOgB,QAAQ,EACfI,aACD;YACD,GAAI,MAAMlB,0BAA0BoB,eAClC,mBAAmB;YACnBtB,OAAOgB,QAAQ,EACfI,aACD;YACDV;YACAT;QACF;QAEA,MAAMsB,iBAAiBnB,iBAAiBoB,UAAU,QAC7CpB,iBAAiBoB,OAAOC,oBACxBvB,0BAA0BsB,UAAU,QACpCtB,0BAA0BsB,OAAOC;QAEtC,IAAI,CAACF,gBAAgB;YACnB5B,UAA2B,gBAAgB0B;QAC7C;QAEAzB,gBAAgB;YACd8B,SAASL;YACTjB,iBAAiBJ,OAAOG,qBAAqB;YAC7CwB,UAAU3B,OAAOW,cAAc;YAC/BV;YACA,mBAAmB;YACnB2B,sBAAsB1B;QACxB;IACF;AACF,EAAE"}
|