@serhii.mazur/directus-gu-logs 1.0.1
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 +53 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +83 -0
- package/docs/model_global.png +0 -0
- package/docs/model_logs.png +0 -0
- package/package.json +34 -0
- package/src/index.ts +114 -0
- package/tsconfig.json +12 -0
package/README.MD
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# 📦 Custom Directus Logs Utility
|
|
2
|
+
|
|
3
|
+
A lightweight utility class for logging errors, creating activity logs, and sending notifications in Directus extensions.
|
|
4
|
+
|
|
5
|
+
## 📋 Requirements:
|
|
6
|
+
|
|
7
|
+
### 1. Logs Collection (logs)
|
|
8
|
+
|
|
9
|
+
You need to create a collection named `logs` with the following fields:
|
|
10
|
+
|
|
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` | code | Error message/code block |
|
|
18
|
+
|
|
19
|
+
### 2. Global Collection (global)
|
|
20
|
+
|
|
21
|
+
Add the following field to the `global` collection:
|
|
22
|
+
|
|
23
|
+
| Field Name | Type | Description |
|
|
24
|
+
| ------------------ | -------------------- | --------------------------------- |
|
|
25
|
+
| `notice_recipient` | m2o → directus_users | Recipient for error notifications |
|
|
26
|
+
|
|
27
|
+
This recipient will be used to send internal notifications when errors occur.
|
|
28
|
+
|
|
29
|
+
## 🚀 Features
|
|
30
|
+
|
|
31
|
+
- Save error logs to the logs collection
|
|
32
|
+
|
|
33
|
+
- Create Directus activity records
|
|
34
|
+
|
|
35
|
+
- Send internal Directus notifications
|
|
36
|
+
|
|
37
|
+
## 📦 Installation
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npm install @serhii.mazur/directus-gu-logs
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 🧩 Usage
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { Logs } from "@serhii.mazur/directus-gu-logs";
|
|
47
|
+
|
|
48
|
+
const logs = new Logs(context, "logs", "my-extension");
|
|
49
|
+
|
|
50
|
+
await logs.printLogs("myFunction", "message");
|
|
51
|
+
await logs.createActivity("create", "collection", "id", "comment");
|
|
52
|
+
await logs.createNotification("An error occurred");
|
|
53
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ApiExtensionContext } from "@directus/extensions";
|
|
2
|
+
import type { PrimaryKey } from "@directus/types";
|
|
3
|
+
export declare class Logs {
|
|
4
|
+
protected context: ApiExtensionContext;
|
|
5
|
+
protected collectionName: string;
|
|
6
|
+
protected extension: string;
|
|
7
|
+
constructor(context: ApiExtensionContext, collectionName?: string, extension?: string);
|
|
8
|
+
private getSchema;
|
|
9
|
+
private createOne;
|
|
10
|
+
printLogs(functionName: string, error: string): Promise<void>;
|
|
11
|
+
createActivity(action: "create" | "update" | "delete" | undefined, collection: string, id: PrimaryKey, comment?: string): Promise<void>;
|
|
12
|
+
createNotification(message?: string): Promise<void>;
|
|
13
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export class Logs {
|
|
2
|
+
constructor(context, collectionName = "logs", extension = "unknown") {
|
|
3
|
+
this.context = context;
|
|
4
|
+
this.collectionName = collectionName;
|
|
5
|
+
this.extension = extension;
|
|
6
|
+
}
|
|
7
|
+
async getSchema() {
|
|
8
|
+
return await this.context.getSchema();
|
|
9
|
+
}
|
|
10
|
+
async createOne(data) {
|
|
11
|
+
const schema = await this.getSchema();
|
|
12
|
+
const itemsService = new this.context.services.ItemsService(this.collectionName, {
|
|
13
|
+
database: this.context.database,
|
|
14
|
+
schema,
|
|
15
|
+
});
|
|
16
|
+
return await itemsService.createOne(data);
|
|
17
|
+
}
|
|
18
|
+
async printLogs(functionName, error) {
|
|
19
|
+
const data = {
|
|
20
|
+
collection: this.collectionName,
|
|
21
|
+
date_created: new Date().toISOString(),
|
|
22
|
+
extension: this.extension,
|
|
23
|
+
function_name: functionName,
|
|
24
|
+
error,
|
|
25
|
+
};
|
|
26
|
+
try {
|
|
27
|
+
await this.createOne(data);
|
|
28
|
+
console.error(`🚀 [${this.extension}] ${functionName}:`, error);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error("❌ Failed to save logs:", error);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async createActivity(action = "create", collection, id, comment = "") {
|
|
35
|
+
try {
|
|
36
|
+
const schema = await this.getSchema();
|
|
37
|
+
const services = this.context.services;
|
|
38
|
+
const accountability = services.accountability;
|
|
39
|
+
const activityService = new services.ActivityService({
|
|
40
|
+
schema: schema,
|
|
41
|
+
accountability: accountability,
|
|
42
|
+
knex: this.context.database,
|
|
43
|
+
});
|
|
44
|
+
await activityService.createOne({
|
|
45
|
+
action,
|
|
46
|
+
comment,
|
|
47
|
+
user: accountability?.user ?? null,
|
|
48
|
+
collection,
|
|
49
|
+
ip: accountability?.ip ?? null,
|
|
50
|
+
user_agent: accountability?.userAgent ?? null,
|
|
51
|
+
origin: accountability?.origin ?? null,
|
|
52
|
+
item: id,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
console.error("❌ Failed to create activity log:", error);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async createNotification(message = "") {
|
|
60
|
+
try {
|
|
61
|
+
const schema = await this.getSchema();
|
|
62
|
+
const { database, services } = this.context;
|
|
63
|
+
const globalSettings = await database.select("notice_recipient").from("global").first();
|
|
64
|
+
const recipient = globalSettings?.notice_recipient;
|
|
65
|
+
if (!recipient) {
|
|
66
|
+
this.printLogs(this.extension, "No recipient defined in global settings");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const notificationService = new services.NotificationsService({ schema });
|
|
70
|
+
await notificationService.createOne({
|
|
71
|
+
recipient,
|
|
72
|
+
sender: recipient,
|
|
73
|
+
subject: "Directus Error Notification",
|
|
74
|
+
message,
|
|
75
|
+
collection: null,
|
|
76
|
+
item: null,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.error("❌ Failed to create notification:", error);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
Binary file
|
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@serhii.mazur/directus-gu-logs",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Helper class Logs for using in Directus extensions",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
10
|
+
},
|
|
11
|
+
"author": "Serhii Mazur",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"repository": {
|
|
14
|
+
"type": "git",
|
|
15
|
+
"url": "git+https://github.com/SerhiiMazurBeetroot/directus-gu-logs.git"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"typescript": "^5.0.0"
|
|
19
|
+
},
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@directus/extensions": "^3.0.4",
|
|
22
|
+
"@directus/types": "^13.1.0",
|
|
23
|
+
"pino": "^9.6.0",
|
|
24
|
+
"vue-router": "^4.5.0"
|
|
25
|
+
},
|
|
26
|
+
"directories": {
|
|
27
|
+
"doc": "docs"
|
|
28
|
+
},
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/SerhiiMazurBeetroot/directus-gu-logs/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/SerhiiMazurBeetroot/directus-gu-logs#readme"
|
|
34
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { ApiExtensionContext } from "@directus/extensions";
|
|
2
|
+
import type { PrimaryKey } from "@directus/types";
|
|
3
|
+
|
|
4
|
+
interface LogEntry {
|
|
5
|
+
collection: string;
|
|
6
|
+
date_created: string;
|
|
7
|
+
extension: string;
|
|
8
|
+
function_name: string;
|
|
9
|
+
error: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class Logs {
|
|
13
|
+
protected context: ApiExtensionContext;
|
|
14
|
+
protected collectionName: string;
|
|
15
|
+
protected extension: string;
|
|
16
|
+
|
|
17
|
+
constructor(context: ApiExtensionContext, collectionName = "logs", extension = "unknown") {
|
|
18
|
+
this.context = context;
|
|
19
|
+
this.collectionName = collectionName;
|
|
20
|
+
this.extension = extension;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private async getSchema() {
|
|
24
|
+
return await this.context.getSchema();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private async createOne(data: Record<string, any>) {
|
|
28
|
+
const schema = await this.getSchema();
|
|
29
|
+
|
|
30
|
+
const itemsService = new this.context.services.ItemsService(this.collectionName, {
|
|
31
|
+
database: this.context.database,
|
|
32
|
+
schema,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return await itemsService.createOne(data);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async printLogs(functionName: string, error: string) {
|
|
39
|
+
const data: LogEntry = {
|
|
40
|
+
collection: this.collectionName,
|
|
41
|
+
date_created: new Date().toISOString(),
|
|
42
|
+
extension: this.extension,
|
|
43
|
+
function_name: functionName,
|
|
44
|
+
error,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
await this.createOne(data);
|
|
49
|
+
console.error(`🚀 [${this.extension}] ${functionName}:`, error);
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error("❌ Failed to save logs:", error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async createActivity(
|
|
56
|
+
action: "create" | "update" | "delete" = "create",
|
|
57
|
+
collection: string,
|
|
58
|
+
id: PrimaryKey,
|
|
59
|
+
comment: string = ""
|
|
60
|
+
) {
|
|
61
|
+
try {
|
|
62
|
+
const schema = await this.getSchema();
|
|
63
|
+
const services = this.context.services;
|
|
64
|
+
const accountability = services.accountability;
|
|
65
|
+
|
|
66
|
+
const activityService = new services.ActivityService({
|
|
67
|
+
schema: schema,
|
|
68
|
+
accountability: accountability,
|
|
69
|
+
knex: this.context.database,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
await activityService.createOne({
|
|
73
|
+
action,
|
|
74
|
+
comment,
|
|
75
|
+
user: accountability?.user ?? null,
|
|
76
|
+
collection,
|
|
77
|
+
ip: accountability?.ip ?? null,
|
|
78
|
+
user_agent: accountability?.userAgent ?? null,
|
|
79
|
+
origin: accountability?.origin ?? null,
|
|
80
|
+
item: id,
|
|
81
|
+
});
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error("❌ Failed to create activity log:", error);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async createNotification(message = "") {
|
|
88
|
+
try {
|
|
89
|
+
const schema = await this.getSchema();
|
|
90
|
+
const { database, services } = this.context;
|
|
91
|
+
|
|
92
|
+
const globalSettings = await database.select("notice_recipient").from("global").first();
|
|
93
|
+
const recipient = globalSettings?.notice_recipient;
|
|
94
|
+
|
|
95
|
+
if (!recipient) {
|
|
96
|
+
this.printLogs(this.extension, "No recipient defined in global settings");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const notificationService = new services.NotificationsService({ schema });
|
|
101
|
+
|
|
102
|
+
await notificationService.createOne({
|
|
103
|
+
recipient,
|
|
104
|
+
sender: recipient,
|
|
105
|
+
subject: "Directus Error Notification",
|
|
106
|
+
message,
|
|
107
|
+
collection: null,
|
|
108
|
+
item: null,
|
|
109
|
+
});
|
|
110
|
+
} catch (error) {
|
|
111
|
+
console.error("❌ Failed to create notification:", error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|