@serhii.mazur/directus-gu-logs 1.0.7 → 1.0.8
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 +88 -21
- package/dist/index.js +19 -5
- package/package.json +1 -1
- package/src/index.ts +26 -8
package/README.MD
CHANGED
|
@@ -8,32 +8,32 @@ A lightweight utility class for logging errors, creating activity logs, and send
|
|
|
8
8
|
|
|
9
9
|
You need to create a collection named `logs` with the following fields:
|
|
10
10
|
|
|
11
|
-
| Field Name | Type
|
|
12
|
-
| --------------- |
|
|
13
|
-
| `collection` | string
|
|
14
|
-
| `date_created` | datetime
|
|
15
|
-
| `extension` | string
|
|
16
|
-
| `function_name` | string
|
|
17
|
-
| `error` | code
|
|
11
|
+
| Field Name | Type | Description |
|
|
12
|
+
| --------------- | --------- | ---------------------------- |
|
|
13
|
+
| `collection` | string | Name of the collection |
|
|
14
|
+
| `date_created` | datetime | Timestamp of the log |
|
|
15
|
+
| `extension` | string | Extension identifier |
|
|
16
|
+
| `function_name` | string | Name of the function |
|
|
17
|
+
| `error` | text/code | Error message or stack trace |
|
|
18
18
|
|
|
19
19
|
### 2. Global Collection (global)
|
|
20
20
|
|
|
21
21
|
Add the following field to the `global` collection:
|
|
22
22
|
|
|
23
|
-
| Field Name
|
|
24
|
-
|
|
|
25
|
-
| `
|
|
26
|
-
| `developer_notice_recipient` | m2o → directus_users | Recipient for error notifications |
|
|
23
|
+
| Field Name | Type | Description |
|
|
24
|
+
| ------------------------ | ------------------------------- | ---------------------------------- |
|
|
25
|
+
| `error_notice_recipient` | m2m → directus_users (junction) | Recipients for error notifications |
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
All linked users from this field will receive internal notifications when errors occur.
|
|
29
28
|
|
|
30
29
|
## 🚀 Features
|
|
31
30
|
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
|
|
36
|
-
-
|
|
31
|
+
- Save structured error logs to a collection
|
|
32
|
+
- Create Directus activity records
|
|
33
|
+
- Send internal Directus notifications
|
|
34
|
+
- Automatic recipient resolution from global settings
|
|
35
|
+
- Support for manual recipient override
|
|
36
|
+
- Includes project metadata in notifications (environment, URL, timestamp)
|
|
37
37
|
|
|
38
38
|
## 📦 Installation
|
|
39
39
|
|
|
@@ -46,10 +46,77 @@ npm i @serhii.mazur/directus-gu-logs
|
|
|
46
46
|
```ts
|
|
47
47
|
import { Logs } from "@serhii.mazur/directus-gu-logs";
|
|
48
48
|
|
|
49
|
-
const logs = new Logs(context, "my-extension"
|
|
49
|
+
const logs = new Logs(context, "my-extension");
|
|
50
|
+
|
|
51
|
+
// Save error log
|
|
52
|
+
await logs.printLogs("myFunction", "Something went wrong");
|
|
50
53
|
|
|
51
|
-
|
|
52
|
-
await logs.createActivity("create", "
|
|
54
|
+
// Create activity record
|
|
55
|
+
await logs.createActivity("create", "collection_name", "item_id");
|
|
56
|
+
|
|
57
|
+
// Send notification (auto recipients from global settings)
|
|
53
58
|
await logs.createNotification("An error occurred");
|
|
54
|
-
|
|
59
|
+
|
|
60
|
+
// Send notification with custom subject
|
|
61
|
+
await logs.createNotification("An error occurred", "Custom Subject");
|
|
62
|
+
|
|
63
|
+
// Send notification to specific recipient
|
|
64
|
+
await logs.createNotification("An error occurred", "Custom Subject", "user_id");
|
|
65
|
+
|
|
66
|
+
// Send notification with related collection + item
|
|
67
|
+
await logs.createNotification("An error occurred", "Custom Subject", null, "collection_name", "item_id");
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## ⚙️ Constructor
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
constructor(
|
|
76
|
+
context: ApiExtensionContext,
|
|
77
|
+
extension: string,
|
|
78
|
+
collectionName: string = "logs"
|
|
79
|
+
)
|
|
55
80
|
```
|
|
81
|
+
|
|
82
|
+
| Param | Type | Description |
|
|
83
|
+
| ---------------- | ------------------- | ------------------------------------ |
|
|
84
|
+
| `context` | ApiExtensionContext | Directus extension context |
|
|
85
|
+
| `extension` | string | Name of your extension |
|
|
86
|
+
| `collectionName` | string (optional) | Logs collection name (default: logs) |
|
|
87
|
+
|
|
88
|
+
## 🧠 How It Works
|
|
89
|
+
|
|
90
|
+
### Logging
|
|
91
|
+
|
|
92
|
+
- Saves structured logs into the logs collection
|
|
93
|
+
- Automatically attaches timestamp and extension name
|
|
94
|
+
- Fallback to console if DB write fails
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
### Activity Tracking
|
|
99
|
+
|
|
100
|
+
- Uses Directus ActivityService
|
|
101
|
+
- Automatically includes:
|
|
102
|
+
- user
|
|
103
|
+
- IP address
|
|
104
|
+
- user agent
|
|
105
|
+
- origin
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### Notifications
|
|
110
|
+
|
|
111
|
+
- Resolves recipients in this order:
|
|
112
|
+
1. Explicit recipientOverride
|
|
113
|
+
2. global.notice_recipient
|
|
114
|
+
3. global.developer_notice_recipient
|
|
115
|
+
- Automatically:
|
|
116
|
+
- Deduplicates recipients
|
|
117
|
+
- Includes project metadata:
|
|
118
|
+
- Project name
|
|
119
|
+
- Backend URL
|
|
120
|
+
- Environment (branch)
|
|
121
|
+
- UTC timestamp
|
|
122
|
+
- Sends one notification per recipient
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
function extractRecipientIds(rows) {
|
|
2
|
+
const recipients = rows
|
|
3
|
+
?.map(row => {
|
|
4
|
+
const user = row?.directus_users_id;
|
|
5
|
+
if (typeof user === "string")
|
|
6
|
+
return user;
|
|
7
|
+
return typeof user?.id === "string" ? user.id : null;
|
|
8
|
+
})
|
|
9
|
+
.filter((value) => Boolean(value)) ?? [];
|
|
10
|
+
return Array.from(new Set(recipients));
|
|
11
|
+
}
|
|
1
12
|
export class Logs {
|
|
2
13
|
constructor(context, extension, collectionName = "logs") {
|
|
3
14
|
this.context = context;
|
|
@@ -59,17 +70,20 @@ export class Logs {
|
|
|
59
70
|
try {
|
|
60
71
|
const schema = await this.getSchema();
|
|
61
72
|
const { database, services } = this.context;
|
|
73
|
+
const globalService = new services.ItemsService("global", {
|
|
74
|
+
database,
|
|
75
|
+
schema,
|
|
76
|
+
});
|
|
62
77
|
// Check for passed recipient, fallback to global settings
|
|
63
78
|
let recipients = [];
|
|
64
79
|
if (recipientOverride) {
|
|
65
80
|
recipients = [recipientOverride];
|
|
66
81
|
}
|
|
67
82
|
else {
|
|
68
|
-
const globalSettings = await
|
|
69
|
-
.
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
recipients = [globalSettings?.notice_recipient, globalSettings?.developer_notice_recipient].filter(Boolean);
|
|
83
|
+
const globalSettings = await globalService.readSingleton({
|
|
84
|
+
fields: ["error_notice_recipient.directus_users_id.id"],
|
|
85
|
+
});
|
|
86
|
+
recipients = extractRecipientIds(globalSettings?.error_notice_recipient);
|
|
73
87
|
}
|
|
74
88
|
if (recipients.length === 0) {
|
|
75
89
|
this.printLogs(this.extension, "No recipients defined (override or global settings)");
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -9,6 +9,23 @@ interface LogEntry {
|
|
|
9
9
|
error: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
interface RecipientLinkRow {
|
|
13
|
+
directus_users_id?: string | { id?: string | null } | null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function extractRecipientIds(rows: RecipientLinkRow[] | undefined): string[] {
|
|
17
|
+
const recipients =
|
|
18
|
+
rows
|
|
19
|
+
?.map(row => {
|
|
20
|
+
const user = row?.directus_users_id;
|
|
21
|
+
if (typeof user === "string") return user;
|
|
22
|
+
return typeof user?.id === "string" ? user.id : null;
|
|
23
|
+
})
|
|
24
|
+
.filter((value): value is string => Boolean(value)) ?? [];
|
|
25
|
+
|
|
26
|
+
return Array.from(new Set(recipients));
|
|
27
|
+
}
|
|
28
|
+
|
|
12
29
|
export class Logs {
|
|
13
30
|
protected context: ApiExtensionContext;
|
|
14
31
|
protected extension: string;
|
|
@@ -88,6 +105,10 @@ export class Logs {
|
|
|
88
105
|
try {
|
|
89
106
|
const schema = await this.getSchema();
|
|
90
107
|
const { database, services } = this.context;
|
|
108
|
+
const globalService = new services.ItemsService("global", {
|
|
109
|
+
database,
|
|
110
|
+
schema,
|
|
111
|
+
});
|
|
91
112
|
|
|
92
113
|
// Check for passed recipient, fallback to global settings
|
|
93
114
|
let recipients: string[] = [];
|
|
@@ -95,14 +116,11 @@ export class Logs {
|
|
|
95
116
|
if (recipientOverride) {
|
|
96
117
|
recipients = [recipientOverride];
|
|
97
118
|
} else {
|
|
98
|
-
const globalSettings = await
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
recipients = [globalSettings?.notice_recipient, globalSettings?.developer_notice_recipient].filter(
|
|
104
|
-
Boolean
|
|
105
|
-
);
|
|
119
|
+
const globalSettings = await globalService.readSingleton({
|
|
120
|
+
fields: ["error_notice_recipient.directus_users_id.id"],
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
recipients = extractRecipientIds(globalSettings?.error_notice_recipient);
|
|
106
124
|
}
|
|
107
125
|
|
|
108
126
|
if (recipients.length === 0) {
|