@viplance/nestjs-logger 0.3.8 → 0.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 +14 -1
- package/dist/log.module.js +28 -2
- package/dist/log.module.js.map +1 -1
- package/dist/services/log.service.d.ts +10 -4
- package/dist/services/log.service.js +62 -8
- package/dist/services/log.service.js.map +1 -1
- package/dist/services/ws.service.d.ts +18 -0
- package/dist/services/ws.service.js +106 -0
- package/dist/services/ws.service.js.map +1 -0
- package/dist/types/options.type.d.ts +6 -0
- package/package.json +6 -4
- package/public/index.html +7 -2
- package/public/scripts/common.js +62 -31
- package/public/scripts/details-popup.js +11 -2
- package/public/scripts/ws.js +83 -0
- package/public/styles/index.css +5 -0
- package/src/log.module.ts +35 -2
- package/src/services/log.service.ts +84 -10
- package/src/services/ws.d.ts +1 -0
- package/src/services/ws.service.ts +110 -0
- package/src/types/options.type.ts +6 -0
package/README.md
CHANGED
|
@@ -24,12 +24,13 @@
|
|
|
24
24
|
import { LogModule } from '@viplance/nestjs-logger';
|
|
25
25
|
|
|
26
26
|
await LogModule.init(app, {
|
|
27
|
+
..., // some other properties
|
|
27
28
|
path: '/logs', // define the public URL for the log list
|
|
28
29
|
key: 'kjhjmi321lqq7a', // use the key to protect data from unauthorized access
|
|
29
30
|
});
|
|
30
31
|
```
|
|
31
32
|
|
|
32
|
-
Connect
|
|
33
|
+
Connect a SQL or NoSQL database to store logs.<br />
|
|
33
34
|
```typescript
|
|
34
35
|
await LogModule.init(app, {
|
|
35
36
|
...,
|
|
@@ -42,6 +43,18 @@ Connect the database to store logs.<br />
|
|
|
42
43
|
});
|
|
43
44
|
```
|
|
44
45
|
|
|
46
|
+
Enable a WebSocket connection to receive the logs in real time.<br />
|
|
47
|
+
```typescript
|
|
48
|
+
await LogModule.init(app, {
|
|
49
|
+
...,
|
|
50
|
+
websocket: {
|
|
51
|
+
secure: true, // enable WSS
|
|
52
|
+
port: 81,
|
|
53
|
+
host: 'your-domain.name',
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
45
58
|
4. Use the `LogService` in case of custom logs to debug the application.<br />
|
|
46
59
|
```typescript
|
|
47
60
|
import { LogService } from '@viplance/nestjs-logger';
|
package/dist/log.module.js
CHANGED
|
@@ -19,10 +19,12 @@ const node_querystring_1 = __importDefault(require("node:querystring"));
|
|
|
19
19
|
const core_1 = require("@nestjs/core");
|
|
20
20
|
const node_path_1 = require("node:path");
|
|
21
21
|
const access_guard_1 = require("./guards/access.guard");
|
|
22
|
+
const ws_service_1 = require("./services/ws.service");
|
|
22
23
|
let LogModule = class LogModule {
|
|
23
24
|
static async init(app, options) {
|
|
24
25
|
app.resolve(log_service_1.LogService);
|
|
25
26
|
const logService = await app.resolve(log_service_1.LogService);
|
|
27
|
+
const wsService = await app.resolve(ws_service_1.WsService);
|
|
26
28
|
const logAccessGuard = await app.get(access_guard_1.LogAccessGuard);
|
|
27
29
|
if (options) {
|
|
28
30
|
logService.setOptions(options);
|
|
@@ -33,6 +35,20 @@ let LogModule = class LogModule {
|
|
|
33
35
|
prefix: options.path,
|
|
34
36
|
});
|
|
35
37
|
const httpAdapter = app.getHttpAdapter();
|
|
38
|
+
// frontend settings endpoint
|
|
39
|
+
httpAdapter.get((0, node_path_1.join)(options.path, "settings"), async (req, res) => {
|
|
40
|
+
var _a, _b, _c, _d;
|
|
41
|
+
logAccessGuard.canActivate(req);
|
|
42
|
+
const result = {};
|
|
43
|
+
if (options === null || options === void 0 ? void 0 : options.websocket) {
|
|
44
|
+
result.websocket = {
|
|
45
|
+
namespace: (_a = options.websocket) === null || _a === void 0 ? void 0 : _a.namespace,
|
|
46
|
+
port: (_b = options.websocket) === null || _b === void 0 ? void 0 : _b.port,
|
|
47
|
+
host: ((_c = options.websocket) === null || _c === void 0 ? void 0 : _c.host) || ((_d = req.headers) === null || _d === void 0 ? void 0 : _d.host.split(":")[0]),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
res.json(result);
|
|
51
|
+
});
|
|
36
52
|
// get all logs endpoint
|
|
37
53
|
httpAdapter.get((0, node_path_1.join)(options.path, "api"), async (req, res) => {
|
|
38
54
|
logAccessGuard.canActivate(req);
|
|
@@ -47,6 +63,10 @@ let LogModule = class LogModule {
|
|
|
47
63
|
}
|
|
48
64
|
res.json(await logService.delete(params.id.toString()));
|
|
49
65
|
});
|
|
66
|
+
// set up WebSocket connection
|
|
67
|
+
if (options === null || options === void 0 ? void 0 : options.websocket) {
|
|
68
|
+
wsService.setupConnection(options.websocket, options.key);
|
|
69
|
+
}
|
|
50
70
|
}
|
|
51
71
|
if (options === null || options === void 0 ? void 0 : options.database) {
|
|
52
72
|
await logService.connectDb(options);
|
|
@@ -58,8 +78,14 @@ exports.LogModule = LogModule = __decorate([
|
|
|
58
78
|
(0, common_1.Global)(),
|
|
59
79
|
(0, common_1.Module)({
|
|
60
80
|
imports: [typeorm_1.TypeOrmModule],
|
|
61
|
-
providers: [
|
|
62
|
-
|
|
81
|
+
providers: [
|
|
82
|
+
core_1.ApplicationConfig,
|
|
83
|
+
access_guard_1.LogAccessGuard,
|
|
84
|
+
log_service_1.LogService,
|
|
85
|
+
memory_db_service_1.MemoryDbService,
|
|
86
|
+
ws_service_1.WsService,
|
|
87
|
+
],
|
|
88
|
+
exports: [typeorm_1.TypeOrmModule, log_service_1.LogService, memory_db_service_1.MemoryDbService, ws_service_1.WsService],
|
|
63
89
|
})
|
|
64
90
|
], LogModule);
|
|
65
91
|
//# sourceMappingURL=log.module.js.map
|
package/dist/log.module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.module.js","sourceRoot":"","sources":["../src/log.module.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAA+D;AAC/D,wDAAoD;AACpD,oEAA+D;AAC/D,oEAAgE;AAEhE,6CAAgD;AAChD,wEAA2C;AAC3C,uCAAiD;AACjD,yCAAiC;AACjC,wDAAuD;
|
|
1
|
+
{"version":3,"file":"log.module.js","sourceRoot":"","sources":["../src/log.module.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,17 +1,22 @@
|
|
|
1
|
-
import { LoggerService } from "@nestjs/common";
|
|
1
|
+
import { LoggerService, OnApplicationShutdown } from "@nestjs/common";
|
|
2
2
|
import { MemoryDbService } from "./memory-db.service";
|
|
3
3
|
import { LogModuleOptions } from "../types";
|
|
4
4
|
import { DataSource, EntitySchema } from "typeorm";
|
|
5
5
|
import { ExecutionContextHost } from "@nestjs/core/helpers/execution-context-host";
|
|
6
6
|
import { setInterval } from "timers";
|
|
7
|
-
|
|
7
|
+
import { WsService } from "./ws.service";
|
|
8
|
+
import { Subscription } from "rxjs";
|
|
9
|
+
export declare class LogService implements LoggerService, OnApplicationShutdown {
|
|
8
10
|
private readonly memoryDbService;
|
|
11
|
+
private readonly wsService;
|
|
9
12
|
static connection: DataSource;
|
|
10
13
|
static options: LogModuleOptions;
|
|
11
14
|
static Log: EntitySchema;
|
|
12
15
|
static timer: ReturnType<typeof setInterval>;
|
|
16
|
+
static subscription: Subscription;
|
|
13
17
|
breadcrumbs: any[];
|
|
14
|
-
constructor(memoryDbService: MemoryDbService);
|
|
18
|
+
constructor(memoryDbService: MemoryDbService, wsService: WsService);
|
|
19
|
+
onApplicationShutdown(): void;
|
|
15
20
|
connectDb(options: LogModuleOptions): Promise<DataSource>;
|
|
16
21
|
setOptions(options: LogModuleOptions): void;
|
|
17
22
|
addBreadcrumb(breadcrumb: any): void;
|
|
@@ -22,8 +27,9 @@ export declare class LogService implements LoggerService {
|
|
|
22
27
|
debug(message: string, context?: ExecutionContextHost): void;
|
|
23
28
|
verbose(message: string, context?: ExecutionContextHost): void;
|
|
24
29
|
getAll(): Promise<any[]>;
|
|
25
|
-
delete(
|
|
30
|
+
delete(_id: string): Promise<import("typeorm").DeleteResult>;
|
|
26
31
|
private smartInsert;
|
|
32
|
+
private getNewObjectId;
|
|
27
33
|
private getConnection;
|
|
28
34
|
private parseContext;
|
|
29
35
|
private checkRecords;
|
|
@@ -19,15 +19,24 @@ const typeorm_1 = require("typeorm");
|
|
|
19
19
|
const log_entity_1 = require("../entities/log.entity");
|
|
20
20
|
const timers_1 = require("timers");
|
|
21
21
|
const entity2table_1 = require("../utils/entity2table");
|
|
22
|
+
const ws_service_1 = require("./ws.service");
|
|
22
23
|
let LogService = LogService_1 = class LogService {
|
|
23
|
-
constructor(memoryDbService) {
|
|
24
|
+
constructor(memoryDbService, wsService) {
|
|
24
25
|
this.memoryDbService = memoryDbService;
|
|
26
|
+
this.wsService = wsService;
|
|
25
27
|
this.breadcrumbs = [];
|
|
26
28
|
}
|
|
29
|
+
onApplicationShutdown() {
|
|
30
|
+
if (LogService_1.timer) {
|
|
31
|
+
clearInterval(LogService_1.timer);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
27
34
|
async connectDb(options) {
|
|
28
35
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
29
36
|
LogService_1.Log = (0, log_entity_1.createLogEntity)(((_a = options.database) === null || _a === void 0 ? void 0 : _a.collection) || ((_b = options.database) === null || _b === void 0 ? void 0 : _b.table) || defaults_1.defaultTable, ((_c = options.database) === null || _c === void 0 ? void 0 : _c.type) || "mongodb");
|
|
30
|
-
|
|
37
|
+
if (!LogService_1.options) {
|
|
38
|
+
this.setOptions(options);
|
|
39
|
+
}
|
|
31
40
|
const dataSourceOptions = {
|
|
32
41
|
type: (_d = options.database) === null || _d === void 0 ? void 0 : _d.type,
|
|
33
42
|
database: (_e = options.database) === null || _e === void 0 ? void 0 : _e.database,
|
|
@@ -38,7 +47,6 @@ let LogService = LogService_1 = class LogService {
|
|
|
38
47
|
LogService_1.connection = new typeorm_1.DataSource(dataSourceOptions);
|
|
39
48
|
await LogService_1.connection.initialize();
|
|
40
49
|
if (dataSourceOptions.type !== "mongodb") {
|
|
41
|
-
// LogService.idName = "id";
|
|
42
50
|
const queryRunner = LogService_1.connection.createQueryRunner();
|
|
43
51
|
try {
|
|
44
52
|
await queryRunner.connect();
|
|
@@ -57,6 +65,21 @@ let LogService = LogService_1 = class LogService {
|
|
|
57
65
|
}
|
|
58
66
|
setOptions(options) {
|
|
59
67
|
LogService_1.options = options;
|
|
68
|
+
if (options.websocket && !LogService_1.subscription) {
|
|
69
|
+
LogService_1.subscription = this.wsService.onMessage.subscribe(async (message) => {
|
|
70
|
+
switch (message.action) {
|
|
71
|
+
case "getLogs":
|
|
72
|
+
this.wsService.sendMessage({
|
|
73
|
+
action: "list",
|
|
74
|
+
data: await this.getAll(),
|
|
75
|
+
});
|
|
76
|
+
break;
|
|
77
|
+
case "delete":
|
|
78
|
+
this.delete(message.data._id);
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
60
83
|
}
|
|
61
84
|
addBreadcrumb(breadcrumb) {
|
|
62
85
|
this.breadcrumbs.push(breadcrumb);
|
|
@@ -116,8 +139,12 @@ let LogService = LogService_1 = class LogService {
|
|
|
116
139
|
order: { updatedAt: "DESC" },
|
|
117
140
|
});
|
|
118
141
|
}
|
|
119
|
-
async delete(
|
|
120
|
-
|
|
142
|
+
async delete(_id) {
|
|
143
|
+
this.wsService.sendMessage({
|
|
144
|
+
action: "delete",
|
|
145
|
+
data: { _id },
|
|
146
|
+
});
|
|
147
|
+
return this.getConnection().delete(LogService_1.Log, _id);
|
|
121
148
|
}
|
|
122
149
|
async smartInsert(data) {
|
|
123
150
|
const currentDate = new Date();
|
|
@@ -131,15 +158,27 @@ let LogService = LogService_1 = class LogService {
|
|
|
131
158
|
});
|
|
132
159
|
const context = data.context ? this.parseContext(data.context) : undefined;
|
|
133
160
|
if (log) {
|
|
134
|
-
|
|
161
|
+
const updatedLog = {
|
|
135
162
|
context,
|
|
136
163
|
trace: data.trace,
|
|
137
164
|
breadcrumbs: this.breadcrumbs,
|
|
138
165
|
count: log.count + 1,
|
|
139
166
|
updatedAt: currentDate,
|
|
167
|
+
};
|
|
168
|
+
this.wsService.sendMessage({
|
|
169
|
+
action: "update",
|
|
170
|
+
data: { ...log, ...updatedLog },
|
|
140
171
|
});
|
|
172
|
+
await connection.update(LogService_1.Log, log["_id"], {
|
|
173
|
+
context,
|
|
174
|
+
trace: data.trace,
|
|
175
|
+
breadcrumbs: this.breadcrumbs,
|
|
176
|
+
count: log.count + 1,
|
|
177
|
+
updatedAt: currentDate,
|
|
178
|
+
});
|
|
179
|
+
return { ...log, ...updatedLog };
|
|
141
180
|
}
|
|
142
|
-
|
|
181
|
+
const insertedLog = {
|
|
143
182
|
type: data.type,
|
|
144
183
|
message: data.message,
|
|
145
184
|
context,
|
|
@@ -148,7 +187,21 @@ let LogService = LogService_1 = class LogService {
|
|
|
148
187
|
count: 1,
|
|
149
188
|
createdAt: currentDate,
|
|
150
189
|
updatedAt: currentDate,
|
|
190
|
+
};
|
|
191
|
+
const res = await connection.insert(LogService_1.Log, insertedLog);
|
|
192
|
+
const _id = this.getNewObjectId(res);
|
|
193
|
+
this.wsService.sendMessage({
|
|
194
|
+
action: "insert",
|
|
195
|
+
data: { _id, ...insertedLog },
|
|
151
196
|
});
|
|
197
|
+
return { _id, ...insertedLog };
|
|
198
|
+
}
|
|
199
|
+
getNewObjectId(result) {
|
|
200
|
+
if (result.identifiers) {
|
|
201
|
+
return result.identifiers[0]._id;
|
|
202
|
+
}
|
|
203
|
+
console.log(result);
|
|
204
|
+
return result._id;
|
|
152
205
|
}
|
|
153
206
|
getConnection() {
|
|
154
207
|
var _a;
|
|
@@ -203,6 +256,7 @@ exports.LogService = LogService;
|
|
|
203
256
|
LogService.Log = (0, log_entity_1.createLogEntity)(defaults_1.defaultTable, "memory");
|
|
204
257
|
exports.LogService = LogService = LogService_1 = __decorate([
|
|
205
258
|
(0, common_1.Injectable)({ scope: common_1.Scope.TRANSIENT }),
|
|
206
|
-
__metadata("design:paramtypes", [memory_db_service_1.MemoryDbService
|
|
259
|
+
__metadata("design:paramtypes", [memory_db_service_1.MemoryDbService,
|
|
260
|
+
ws_service_1.WsService])
|
|
207
261
|
], LogService);
|
|
208
262
|
//# sourceMappingURL=log.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log.service.js","sourceRoot":"","sources":["../../src/services/log.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,
|
|
1
|
+
{"version":3,"file":"log.service.js","sourceRoot":"","sources":["../../src/services/log.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { LogModuleOptions } from "../types";
|
|
2
|
+
import { Subject } from "rxjs";
|
|
3
|
+
export declare class WsService {
|
|
4
|
+
onMessage: Subject<any>;
|
|
5
|
+
private ws;
|
|
6
|
+
private connected;
|
|
7
|
+
private connectionTimeout;
|
|
8
|
+
private options;
|
|
9
|
+
private key;
|
|
10
|
+
setupConnection(options: LogModuleOptions["websocket"], key?: string): void;
|
|
11
|
+
sendMessage(message: any): void;
|
|
12
|
+
private handleError;
|
|
13
|
+
private closeConnection;
|
|
14
|
+
private ping;
|
|
15
|
+
private handleMessage;
|
|
16
|
+
private getServerUrl;
|
|
17
|
+
private handleOpenConnection;
|
|
18
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.WsService = void 0;
|
|
10
|
+
const common_1 = require("@nestjs/common");
|
|
11
|
+
const ws_1 = require("ws");
|
|
12
|
+
const rxjs_1 = require("rxjs");
|
|
13
|
+
let WsService = class WsService {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.onMessage = new rxjs_1.Subject();
|
|
16
|
+
this.ws = null;
|
|
17
|
+
this.connected = false;
|
|
18
|
+
this.connectionTimeout = 500;
|
|
19
|
+
this.options = {
|
|
20
|
+
port: 8080,
|
|
21
|
+
host: "localhost",
|
|
22
|
+
};
|
|
23
|
+
this.key = "";
|
|
24
|
+
this.handleError = () => {
|
|
25
|
+
const serverUrl = this.getServerUrl();
|
|
26
|
+
console.error(`Server ${serverUrl} is not available.`);
|
|
27
|
+
setTimeout(this.setupConnection, this.connectionTimeout);
|
|
28
|
+
};
|
|
29
|
+
this.closeConnection = (connection) => {
|
|
30
|
+
clearTimeout(connection.pingTimeout);
|
|
31
|
+
if (this.connected) {
|
|
32
|
+
console.log("Connection has been closed by server.");
|
|
33
|
+
this.connected = false;
|
|
34
|
+
this.handleError();
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
this.ping = (connection) => {
|
|
38
|
+
console.log("Ping remote server.");
|
|
39
|
+
clearTimeout(connection.pingTimeout);
|
|
40
|
+
connection.pingTimeout = setTimeout(() => {
|
|
41
|
+
connection.terminate();
|
|
42
|
+
}, 30000 + this.connectionTimeout);
|
|
43
|
+
};
|
|
44
|
+
this.handleMessage = (message) => {
|
|
45
|
+
try {
|
|
46
|
+
const data = JSON.parse((message.data || message).toString());
|
|
47
|
+
if (this.key !== "" && data.key !== this.key) {
|
|
48
|
+
throw new Error("WebSocket unauthorized");
|
|
49
|
+
}
|
|
50
|
+
if (this.options)
|
|
51
|
+
if (data.action) {
|
|
52
|
+
this.onMessage.next(data);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
console.error(err);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
this.getServerUrl = () => {
|
|
60
|
+
var _a, _b, _c;
|
|
61
|
+
return `${((_a = this.options) === null || _a === void 0 ? void 0 : _a.secure) ? "wss" : "ws"}://${(_b = this.options) === null || _b === void 0 ? void 0 : _b.host}:${(_c = this.options) === null || _c === void 0 ? void 0 : _c.port}`;
|
|
62
|
+
};
|
|
63
|
+
this.handleOpenConnection = async () => {
|
|
64
|
+
this.connected = true;
|
|
65
|
+
const serverUrl = this.getServerUrl();
|
|
66
|
+
console.log(`${serverUrl} has been connected.`);
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
setupConnection(options, key = "") {
|
|
70
|
+
var _a;
|
|
71
|
+
this.options = {
|
|
72
|
+
...this.options,
|
|
73
|
+
...options,
|
|
74
|
+
};
|
|
75
|
+
this.key = key;
|
|
76
|
+
// Set up Web Socket server
|
|
77
|
+
if (this.ws) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const wsServer = new ws_1.WebSocketServer({
|
|
81
|
+
retryCount: 1,
|
|
82
|
+
reconnectInterval: 1,
|
|
83
|
+
handshakeTimeout: this.connectionTimeout,
|
|
84
|
+
port: (_a = this.options) === null || _a === void 0 ? void 0 : _a.port,
|
|
85
|
+
});
|
|
86
|
+
console.log(`Logs WebSocket server is listening on port ${this.options.port}`);
|
|
87
|
+
wsServer.on("error", this.handleError);
|
|
88
|
+
wsServer.on("open", () => this.handleOpenConnection());
|
|
89
|
+
wsServer.on("ping", () => this.ping(this.ws));
|
|
90
|
+
wsServer.on("close", () => this.closeConnection(this.ws));
|
|
91
|
+
wsServer.on("message", this.handleMessage);
|
|
92
|
+
wsServer.on("connection", (connection) => {
|
|
93
|
+
this.ws = connection;
|
|
94
|
+
connection.onmessage = this.handleMessage;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
sendMessage(message) {
|
|
98
|
+
var _a;
|
|
99
|
+
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(message));
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
exports.WsService = WsService;
|
|
103
|
+
exports.WsService = WsService = __decorate([
|
|
104
|
+
(0, common_1.Injectable)()
|
|
105
|
+
], WsService);
|
|
106
|
+
//# sourceMappingURL=ws.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws.service.js","sourceRoot":"","sources":["../../src/services/ws.service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viplance/nestjs-logger",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "NestJS internal logging system",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -26,12 +26,14 @@
|
|
|
26
26
|
"author": "Dzmitry Sharko",
|
|
27
27
|
"license": "ISC",
|
|
28
28
|
"peerDependencies": {
|
|
29
|
-
"@nestjs/common": "^
|
|
30
|
-
"@nestjs/core": "^
|
|
29
|
+
"@nestjs/common": "^11.0.0",
|
|
30
|
+
"@nestjs/core": "^11.0.0",
|
|
31
31
|
"@nestjs/typeorm": "^11.0.0",
|
|
32
|
+
"@nestjs/websockets": "^11.1.7",
|
|
32
33
|
"reflect-metadata": "^0.2.2",
|
|
33
34
|
"rxjs": "^7.8.2",
|
|
34
|
-
"typeorm": "^0.3.27"
|
|
35
|
+
"typeorm": "^0.3.27",
|
|
36
|
+
"ws": "^8.18.3"
|
|
35
37
|
},
|
|
36
38
|
"devDependencies": {
|
|
37
39
|
"@types/node": "^24.5.2",
|
package/public/index.html
CHANGED
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
<link rel="stylesheet" href="styles/reset.css" />
|
|
9
9
|
<link rel="stylesheet" href="styles/colors.css" />
|
|
10
10
|
<link rel="stylesheet" href="styles/index.css" />
|
|
11
|
+
<script
|
|
12
|
+
defer="defer"
|
|
13
|
+
src="scripts/ws.js
|
|
14
|
+
"
|
|
15
|
+
></script>
|
|
11
16
|
<script
|
|
12
17
|
defer="defer"
|
|
13
18
|
src="scripts/json-viewer.js
|
|
@@ -42,12 +47,12 @@
|
|
|
42
47
|
</ul>
|
|
43
48
|
</nav>
|
|
44
49
|
<input id="search" onkeyup="search(event)" type="text" placeholder="Search" />
|
|
45
|
-
<div onclick="getLogs()"><button class="white">Refresh</button></div>
|
|
50
|
+
<div id="refresh" onclick="getLogs()"><button class="white">Refresh</button></div>
|
|
51
|
+
<div id="freeze" onclick="toggleFreeze()"><button class="white">Freeze</button></div>
|
|
46
52
|
</div>
|
|
47
53
|
<div class="container table-header">
|
|
48
54
|
<div class="col">Type</div>
|
|
49
55
|
<div class="col">Info</div>
|
|
50
|
-
<div class="col">Trace</div>
|
|
51
56
|
<div class="col">Count</div>
|
|
52
57
|
</div>
|
|
53
58
|
</header>
|
package/public/scripts/common.js
CHANGED
|
@@ -12,9 +12,7 @@ const logTypes = Object.keys(selectedLogTypes).filter((key) => key !== `all`);
|
|
|
12
12
|
let logs = [];
|
|
13
13
|
let text = "";
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
getLogs();
|
|
17
|
-
});
|
|
15
|
+
connectWebSocket();
|
|
18
16
|
|
|
19
17
|
document.addEventListener(`click`, (e) => {
|
|
20
18
|
const target = e.target;
|
|
@@ -140,10 +138,10 @@ function getLogHtmlElement(log) {
|
|
|
140
138
|
</div>`;
|
|
141
139
|
}
|
|
142
140
|
|
|
143
|
-
function renderLogs() {
|
|
141
|
+
function renderLogs(logList = logs) {
|
|
144
142
|
let html = "";
|
|
145
143
|
|
|
146
|
-
|
|
144
|
+
logList
|
|
147
145
|
.filter((log) => {
|
|
148
146
|
return selectedLogTypes["all"] || selectedLogTypes[log.type];
|
|
149
147
|
})
|
|
@@ -165,50 +163,83 @@ function renderLogs() {
|
|
|
165
163
|
document.getElementById("logs").innerHTML = html;
|
|
166
164
|
}
|
|
167
165
|
|
|
166
|
+
async function checkElementsVisibility(logList = logs) {
|
|
167
|
+
if (logList.length === 0) {
|
|
168
|
+
document.getElementById("no-logs").style.display = "block";
|
|
169
|
+
document.getElementById("search").style.display = "none";
|
|
170
|
+
document.querySelector(".table-header").style.display = "none";
|
|
171
|
+
document.querySelector("nav").style.display = "none";
|
|
172
|
+
} else {
|
|
173
|
+
document.getElementById("no-logs").style.display = "none";
|
|
174
|
+
document.getElementById("search").style.display = "inline-block";
|
|
175
|
+
document.querySelector(".table-header").style.display = "flex";
|
|
176
|
+
document.querySelector("nav").style.display = "flex";
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
168
180
|
async function getLogs() {
|
|
169
181
|
const { origin, pathname, search } = window.location;
|
|
182
|
+
const searchParams = new URLSearchParams(search);
|
|
183
|
+
const key = searchParams.get("key");
|
|
184
|
+
|
|
185
|
+
if (!!socket) {
|
|
186
|
+
socket.send(
|
|
187
|
+
JSON.stringify({
|
|
188
|
+
action: "getLogs",
|
|
189
|
+
key,
|
|
190
|
+
})
|
|
191
|
+
);
|
|
192
|
+
} else {
|
|
193
|
+
const res = await fetch(`${origin}${pathname}api${search}`);
|
|
170
194
|
|
|
171
|
-
|
|
195
|
+
if (res.ok) {
|
|
196
|
+
logs = await res.json();
|
|
172
197
|
|
|
173
|
-
|
|
174
|
-
logs = await res.json();
|
|
198
|
+
checkElementsVisibility();
|
|
175
199
|
|
|
176
|
-
|
|
177
|
-
document.getElementById("no-logs").style.display = "block";
|
|
178
|
-
document.querySelector(".table-header").style.display = "none";
|
|
179
|
-
document.querySelector("nav").style.display = "none";
|
|
200
|
+
renderLogs();
|
|
180
201
|
} else {
|
|
181
|
-
|
|
182
|
-
document.querySelector(".table-header").style.display = "flex";
|
|
183
|
-
document.querySelector("nav").style.display = "flex";
|
|
202
|
+
alert("An error occurred while fetching logs.");
|
|
184
203
|
}
|
|
185
|
-
|
|
186
|
-
renderLogs();
|
|
187
|
-
} else {
|
|
188
|
-
alert("An error occurred while fetching logs.");
|
|
189
204
|
}
|
|
190
205
|
}
|
|
191
206
|
|
|
192
|
-
async function deleteLog(
|
|
207
|
+
async function deleteLog(_id) {
|
|
193
208
|
if (!confirm("Are you sure? It can't be undone.")) return;
|
|
194
209
|
|
|
195
210
|
const { origin, pathname, search: searchParams } = window.location;
|
|
196
211
|
|
|
197
212
|
const searchParamsWithId = new URLSearchParams(searchParams);
|
|
198
|
-
searchParamsWithId.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
213
|
+
const key = searchParamsWithId.get("key");
|
|
214
|
+
|
|
215
|
+
if (!!socket) {
|
|
216
|
+
socket.send(
|
|
217
|
+
JSON.stringify({
|
|
218
|
+
action: "delete",
|
|
219
|
+
key,
|
|
220
|
+
data: {
|
|
221
|
+
_id,
|
|
222
|
+
},
|
|
223
|
+
})
|
|
224
|
+
);
|
|
208
225
|
closePopup();
|
|
209
226
|
getLogs();
|
|
210
227
|
} else {
|
|
211
|
-
|
|
228
|
+
searchParamsWithId.set("id", _id);
|
|
229
|
+
|
|
230
|
+
const res = await fetch(
|
|
231
|
+
`${origin}${pathname}api?${searchParamsWithId.toString()}`,
|
|
232
|
+
{
|
|
233
|
+
method: "DELETE",
|
|
234
|
+
}
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
if (res.ok) {
|
|
238
|
+
closePopup();
|
|
239
|
+
getLogs();
|
|
240
|
+
} else {
|
|
241
|
+
alert("An error occurred while deleting log.");
|
|
242
|
+
}
|
|
212
243
|
}
|
|
213
244
|
}
|
|
214
245
|
|
|
@@ -3,11 +3,20 @@ function showLogDetails(log) {
|
|
|
3
3
|
const context = getObject(log.context);
|
|
4
4
|
const breadcrumbs = getObject(log.breadcrumbs);
|
|
5
5
|
|
|
6
|
+
const timeInfo =
|
|
7
|
+
log.updatedAt === log.createdAt
|
|
8
|
+
? getDate(log.updatedAt)
|
|
9
|
+
: `Updated: ${getDate(log.updatedAt)}. First seen: ${getDate(
|
|
10
|
+
log.createdAt
|
|
11
|
+
)}`;
|
|
12
|
+
|
|
6
13
|
popup.innerHTML = `
|
|
7
14
|
<div class="content center">
|
|
8
15
|
<div class="container">
|
|
9
|
-
<h2 class="popup-title ${log.type}">${log.type}: ${log.message}
|
|
10
|
-
|
|
16
|
+
<h2 class="popup-title ${log.type}">${log.type}: ${log.message} (${
|
|
17
|
+
log.count
|
|
18
|
+
})</h2>
|
|
19
|
+
<div class="mt-05">${timeInfo}</div>
|
|
11
20
|
${
|
|
12
21
|
log.trace
|
|
13
22
|
? `
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
// WebSocket connection
|
|
2
|
+
let socket;
|
|
3
|
+
let frozen = false;
|
|
4
|
+
|
|
5
|
+
async function connectWebSocket() {
|
|
6
|
+
const { hostname, origin, pathname, search } = window.location;
|
|
7
|
+
|
|
8
|
+
const res = await fetch(`${origin}${pathname}settings${search}`);
|
|
9
|
+
|
|
10
|
+
if (!res.ok) {
|
|
11
|
+
alert('An error occurred while fetching settings.');
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const settings = await res.json();
|
|
16
|
+
|
|
17
|
+
if (!settings.websocket) {
|
|
18
|
+
getLogs();
|
|
19
|
+
document.getElementById('refresh').style.display = 'block';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
document.getElementById('freeze').style.display = 'block';
|
|
23
|
+
|
|
24
|
+
if (!settings.websocket?.port) {
|
|
25
|
+
alert('WebSocket port is not configured.');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const socketUrl = `ws://${hostname}:${settings.websocket.port}`;
|
|
30
|
+
socket = new WebSocket(socketUrl, 'json');
|
|
31
|
+
|
|
32
|
+
socket.onerror = (error) => {
|
|
33
|
+
console.error(error);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
socket.onopen = (event) => {
|
|
37
|
+
getLogs();
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
socket.onclose = (event) => {
|
|
41
|
+
console.log(event);
|
|
42
|
+
setTimeout(connectWebSocket, 5000);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
socket.onmessage = (event) => {
|
|
46
|
+
const data = JSON.parse(event.data.toString());
|
|
47
|
+
|
|
48
|
+
if (data['action'] && !frozen) {
|
|
49
|
+
switch (data['action']) {
|
|
50
|
+
case 'list':
|
|
51
|
+
logs = data['data'];
|
|
52
|
+
checkElementsVisibility(logs);
|
|
53
|
+
renderLogs(logs);
|
|
54
|
+
break;
|
|
55
|
+
case 'insert':
|
|
56
|
+
getLogs();
|
|
57
|
+
break;
|
|
58
|
+
case 'update':
|
|
59
|
+
getLogs();
|
|
60
|
+
return;
|
|
61
|
+
case 'delete':
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function sendMessage(message) {
|
|
68
|
+
socket.send(JSON.stringify(message));
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function toggleFreeze() {
|
|
72
|
+
frozen = !frozen;
|
|
73
|
+
const button = document.querySelector('#freeze button');
|
|
74
|
+
button.innerHTML = frozen ? 'Frozen' : 'Freeze';
|
|
75
|
+
|
|
76
|
+
if (frozen) {
|
|
77
|
+
button.classList.remove('white');
|
|
78
|
+
button.classList.add('light');
|
|
79
|
+
} else {
|
|
80
|
+
button.classList.remove('light');
|
|
81
|
+
button.classList.add('white');
|
|
82
|
+
}
|
|
83
|
+
}
|
package/public/styles/index.css
CHANGED
package/src/log.module.ts
CHANGED
|
@@ -8,12 +8,19 @@ import querystring from "node:querystring";
|
|
|
8
8
|
import { ApplicationConfig } from "@nestjs/core";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
import { LogAccessGuard } from "./guards/access.guard";
|
|
11
|
+
import { WsService } from "./services/ws.service";
|
|
11
12
|
|
|
12
13
|
@Global()
|
|
13
14
|
@Module({
|
|
14
15
|
imports: [TypeOrmModule],
|
|
15
|
-
providers: [
|
|
16
|
-
|
|
16
|
+
providers: [
|
|
17
|
+
ApplicationConfig,
|
|
18
|
+
LogAccessGuard,
|
|
19
|
+
LogService,
|
|
20
|
+
MemoryDbService,
|
|
21
|
+
WsService,
|
|
22
|
+
],
|
|
23
|
+
exports: [TypeOrmModule, LogService, MemoryDbService, WsService],
|
|
17
24
|
})
|
|
18
25
|
export class LogModule {
|
|
19
26
|
public static async init(
|
|
@@ -23,6 +30,7 @@ export class LogModule {
|
|
|
23
30
|
app.resolve(LogService);
|
|
24
31
|
|
|
25
32
|
const logService: LogService = await app.resolve(LogService);
|
|
33
|
+
const wsService: WsService = await app.resolve(WsService);
|
|
26
34
|
const logAccessGuard: LogAccessGuard = await app.get(LogAccessGuard);
|
|
27
35
|
|
|
28
36
|
if (options) {
|
|
@@ -38,6 +46,26 @@ export class LogModule {
|
|
|
38
46
|
|
|
39
47
|
const httpAdapter = app.getHttpAdapter();
|
|
40
48
|
|
|
49
|
+
// frontend settings endpoint
|
|
50
|
+
httpAdapter.get(
|
|
51
|
+
join(options.path, "settings"),
|
|
52
|
+
async (req: any, res: any) => {
|
|
53
|
+
logAccessGuard.canActivate(req);
|
|
54
|
+
|
|
55
|
+
const result: { [key: string]: any } = {};
|
|
56
|
+
|
|
57
|
+
if (options?.websocket) {
|
|
58
|
+
result.websocket = {
|
|
59
|
+
namespace: options.websocket?.namespace,
|
|
60
|
+
port: options.websocket?.port,
|
|
61
|
+
host: options.websocket?.host || req.headers?.host.split(":")[0],
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
res.json(result);
|
|
66
|
+
}
|
|
67
|
+
);
|
|
68
|
+
|
|
41
69
|
// get all logs endpoint
|
|
42
70
|
httpAdapter.get(join(options.path, "api"), async (req: any, res: any) => {
|
|
43
71
|
logAccessGuard.canActivate(req);
|
|
@@ -60,6 +88,11 @@ export class LogModule {
|
|
|
60
88
|
res.json(await logService.delete(params.id.toString()));
|
|
61
89
|
}
|
|
62
90
|
);
|
|
91
|
+
|
|
92
|
+
// set up WebSocket connection
|
|
93
|
+
if (options?.websocket) {
|
|
94
|
+
wsService.setupConnection(options.websocket, options.key);
|
|
95
|
+
}
|
|
63
96
|
}
|
|
64
97
|
|
|
65
98
|
if (options?.database) {
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Injectable,
|
|
3
|
+
LoggerService,
|
|
4
|
+
OnApplicationShutdown,
|
|
5
|
+
Scope,
|
|
6
|
+
} from "@nestjs/common";
|
|
2
7
|
import { MemoryDbService } from "./memory-db.service";
|
|
3
8
|
import { defaultTable } from "../defaults";
|
|
4
9
|
import { Context, LogModuleOptions, LogType } from "../types";
|
|
@@ -12,17 +17,29 @@ import { createLogEntity } from "../entities/log.entity";
|
|
|
12
17
|
import { ExecutionContextHost } from "@nestjs/core/helpers/execution-context-host";
|
|
13
18
|
import { setInterval } from "timers";
|
|
14
19
|
import { entity2table } from "../utils/entity2table";
|
|
20
|
+
import { WsService } from "./ws.service";
|
|
21
|
+
import { Subscription } from "rxjs";
|
|
15
22
|
|
|
16
23
|
@Injectable({ scope: Scope.TRANSIENT })
|
|
17
|
-
export class LogService implements LoggerService {
|
|
24
|
+
export class LogService implements LoggerService, OnApplicationShutdown {
|
|
18
25
|
static connection: DataSource;
|
|
19
26
|
static options: LogModuleOptions;
|
|
20
27
|
static Log: EntitySchema = createLogEntity(defaultTable, "memory");
|
|
21
28
|
static timer: ReturnType<typeof setInterval>;
|
|
29
|
+
static subscription: Subscription;
|
|
22
30
|
|
|
23
31
|
breadcrumbs: any[] = [];
|
|
24
32
|
|
|
25
|
-
constructor(
|
|
33
|
+
constructor(
|
|
34
|
+
private readonly memoryDbService: MemoryDbService,
|
|
35
|
+
private readonly wsService: WsService
|
|
36
|
+
) {}
|
|
37
|
+
|
|
38
|
+
onApplicationShutdown() {
|
|
39
|
+
if (LogService.timer) {
|
|
40
|
+
clearInterval(LogService.timer);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
26
43
|
|
|
27
44
|
async connectDb(options: LogModuleOptions): Promise<DataSource> {
|
|
28
45
|
LogService.Log = createLogEntity(
|
|
@@ -30,7 +47,9 @@ export class LogService implements LoggerService {
|
|
|
30
47
|
options.database?.type || "mongodb"
|
|
31
48
|
);
|
|
32
49
|
|
|
33
|
-
|
|
50
|
+
if (!LogService.options) {
|
|
51
|
+
this.setOptions(options);
|
|
52
|
+
}
|
|
34
53
|
|
|
35
54
|
const dataSourceOptions = {
|
|
36
55
|
type: options.database?.type,
|
|
@@ -44,8 +63,6 @@ export class LogService implements LoggerService {
|
|
|
44
63
|
await LogService.connection.initialize();
|
|
45
64
|
|
|
46
65
|
if (dataSourceOptions.type !== "mongodb") {
|
|
47
|
-
// LogService.idName = "id";
|
|
48
|
-
|
|
49
66
|
const queryRunner = LogService.connection.createQueryRunner();
|
|
50
67
|
|
|
51
68
|
try {
|
|
@@ -70,6 +87,24 @@ export class LogService implements LoggerService {
|
|
|
70
87
|
|
|
71
88
|
setOptions(options: LogModuleOptions) {
|
|
72
89
|
LogService.options = options;
|
|
90
|
+
|
|
91
|
+
if (options.websocket && !LogService.subscription) {
|
|
92
|
+
LogService.subscription = this.wsService.onMessage.subscribe(
|
|
93
|
+
async (message) => {
|
|
94
|
+
switch (message.action) {
|
|
95
|
+
case "getLogs":
|
|
96
|
+
this.wsService.sendMessage({
|
|
97
|
+
action: "list",
|
|
98
|
+
data: await this.getAll(),
|
|
99
|
+
});
|
|
100
|
+
break;
|
|
101
|
+
case "delete":
|
|
102
|
+
this.delete(message.data._id);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
);
|
|
107
|
+
}
|
|
73
108
|
}
|
|
74
109
|
|
|
75
110
|
addBreadcrumb(breadcrumb: any) {
|
|
@@ -138,8 +173,12 @@ export class LogService implements LoggerService {
|
|
|
138
173
|
});
|
|
139
174
|
}
|
|
140
175
|
|
|
141
|
-
async delete(
|
|
142
|
-
|
|
176
|
+
async delete(_id: string) {
|
|
177
|
+
this.wsService.sendMessage({
|
|
178
|
+
action: "delete",
|
|
179
|
+
data: { _id },
|
|
180
|
+
});
|
|
181
|
+
return this.getConnection().delete(LogService.Log, _id);
|
|
143
182
|
}
|
|
144
183
|
|
|
145
184
|
private async smartInsert(data: {
|
|
@@ -163,16 +202,31 @@ export class LogService implements LoggerService {
|
|
|
163
202
|
const context = data.context ? this.parseContext(data.context) : undefined;
|
|
164
203
|
|
|
165
204
|
if (log) {
|
|
166
|
-
|
|
205
|
+
const updatedLog = {
|
|
167
206
|
context,
|
|
168
207
|
trace: data.trace,
|
|
169
208
|
breadcrumbs: this.breadcrumbs,
|
|
170
209
|
count: log.count + 1,
|
|
171
210
|
updatedAt: currentDate,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
this.wsService.sendMessage({
|
|
214
|
+
action: "update",
|
|
215
|
+
data: { ...log, ...updatedLog },
|
|
172
216
|
});
|
|
217
|
+
|
|
218
|
+
await connection.update(LogService.Log, log["_id"], {
|
|
219
|
+
context,
|
|
220
|
+
trace: data.trace,
|
|
221
|
+
breadcrumbs: this.breadcrumbs,
|
|
222
|
+
count: log.count + 1,
|
|
223
|
+
updatedAt: currentDate,
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
return { ...log, ...updatedLog };
|
|
173
227
|
}
|
|
174
228
|
|
|
175
|
-
|
|
229
|
+
const insertedLog = {
|
|
176
230
|
type: data.type,
|
|
177
231
|
message: data.message,
|
|
178
232
|
context,
|
|
@@ -181,7 +235,27 @@ export class LogService implements LoggerService {
|
|
|
181
235
|
count: 1,
|
|
182
236
|
createdAt: currentDate,
|
|
183
237
|
updatedAt: currentDate,
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const res = await connection.insert(LogService.Log, insertedLog);
|
|
241
|
+
const _id = this.getNewObjectId(res);
|
|
242
|
+
|
|
243
|
+
this.wsService.sendMessage({
|
|
244
|
+
action: "insert",
|
|
245
|
+
data: { _id, ...insertedLog },
|
|
184
246
|
});
|
|
247
|
+
|
|
248
|
+
return { _id, ...insertedLog };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
private getNewObjectId(result: any): string | number {
|
|
252
|
+
if (result.identifiers) {
|
|
253
|
+
return result.identifiers[0]._id;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
console.log(result);
|
|
257
|
+
|
|
258
|
+
return result._id;
|
|
185
259
|
}
|
|
186
260
|
|
|
187
261
|
private getConnection(): EntityManager {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
declare module "ws";
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { Injectable } from "@nestjs/common";
|
|
2
|
+
import WebSocket, { WebSocketServer } from "ws";
|
|
3
|
+
import { LogModuleOptions } from "../types";
|
|
4
|
+
import { Subject } from "rxjs";
|
|
5
|
+
|
|
6
|
+
@Injectable()
|
|
7
|
+
export class WsService {
|
|
8
|
+
public onMessage: Subject<any> = new Subject();
|
|
9
|
+
private ws: WebSocket | null = null;
|
|
10
|
+
private connected: boolean = false;
|
|
11
|
+
private connectionTimeout: number = 500;
|
|
12
|
+
private options: LogModuleOptions["websocket"] = {
|
|
13
|
+
port: 8080,
|
|
14
|
+
host: "localhost",
|
|
15
|
+
};
|
|
16
|
+
private key: string = "";
|
|
17
|
+
|
|
18
|
+
setupConnection(options: LogModuleOptions["websocket"], key = "") {
|
|
19
|
+
this.options = {
|
|
20
|
+
...this.options,
|
|
21
|
+
...options,
|
|
22
|
+
};
|
|
23
|
+
this.key = key;
|
|
24
|
+
|
|
25
|
+
// Set up Web Socket server
|
|
26
|
+
if (this.ws) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const wsServer = new WebSocketServer({
|
|
31
|
+
retryCount: 1,
|
|
32
|
+
reconnectInterval: 1,
|
|
33
|
+
handshakeTimeout: this.connectionTimeout,
|
|
34
|
+
port: this.options?.port,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
console.log(
|
|
38
|
+
`Logs WebSocket server is listening on port ${this.options.port}`
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
wsServer.on("error", this.handleError);
|
|
42
|
+
wsServer.on("open", () => this.handleOpenConnection());
|
|
43
|
+
wsServer.on("ping", () => this.ping(this.ws));
|
|
44
|
+
wsServer.on("close", () => this.closeConnection(this.ws));
|
|
45
|
+
wsServer.on("message", this.handleMessage);
|
|
46
|
+
wsServer.on("connection", (connection: WebSocket) => {
|
|
47
|
+
this.ws = connection;
|
|
48
|
+
connection.onmessage = this.handleMessage;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
sendMessage(message: any) {
|
|
53
|
+
this.ws?.send(JSON.stringify(message));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private handleError = () => {
|
|
57
|
+
const serverUrl = this.getServerUrl();
|
|
58
|
+
console.error(`Server ${serverUrl} is not available.`);
|
|
59
|
+
|
|
60
|
+
setTimeout(this.setupConnection, this.connectionTimeout);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
private closeConnection = (connection: any) => {
|
|
64
|
+
clearTimeout(connection.pingTimeout);
|
|
65
|
+
|
|
66
|
+
if (this.connected) {
|
|
67
|
+
console.log("Connection has been closed by server.");
|
|
68
|
+
this.connected = false;
|
|
69
|
+
this.handleError();
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
private ping = (connection: any) => {
|
|
74
|
+
console.log("Ping remote server.");
|
|
75
|
+
clearTimeout(connection.pingTimeout);
|
|
76
|
+
|
|
77
|
+
connection.pingTimeout = setTimeout(() => {
|
|
78
|
+
connection.terminate();
|
|
79
|
+
}, 30000 + this.connectionTimeout);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
private handleMessage = (message: any) => {
|
|
83
|
+
try {
|
|
84
|
+
const data = JSON.parse((message.data || message).toString());
|
|
85
|
+
|
|
86
|
+
if (this.key !== "" && data.key !== this.key) {
|
|
87
|
+
throw new Error("WebSocket unauthorized");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (this.options)
|
|
91
|
+
if (data.action) {
|
|
92
|
+
this.onMessage.next(data);
|
|
93
|
+
}
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.error(err);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
private getServerUrl = (): string => {
|
|
100
|
+
return `${this.options?.secure ? "wss" : "ws"}://${this.options?.host}:${
|
|
101
|
+
this.options?.port
|
|
102
|
+
}`;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
private handleOpenConnection = async () => {
|
|
106
|
+
this.connected = true;
|
|
107
|
+
const serverUrl = this.getServerUrl();
|
|
108
|
+
console.log(`${serverUrl} has been connected.`);
|
|
109
|
+
};
|
|
110
|
+
}
|