@viplance/nestjs-logger 0.3.8 → 0.4.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 +37 -4
- package/dist/defaults.js +1 -1
- package/dist/entities/log.entity.d.ts +2 -2
- package/dist/entities/log.entity.js +1 -1
- package/dist/guards/access.guard.d.ts +1 -1
- package/dist/guards/access.guard.js +2 -2
- package/dist/index.d.ts +4 -4
- package/dist/interceptors/log.interceptor.d.ts +4 -4
- package/dist/log.module.d.ts +1 -1
- package/dist/log.module.js +33 -7
- package/dist/log.module.js.map +1 -1
- package/dist/services/log.service.d.ts +15 -9
- package/dist/services/log.service.js +87 -30
- package/dist/services/log.service.js.map +1 -1
- package/dist/services/memory-db.service.d.ts +1 -1
- package/dist/services/memory-db.service.js +5 -5
- 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/index.d.ts +3 -3
- package/dist/types/options.type.d.ts +7 -0
- package/dist/utils/entity2table.d.ts +1 -1
- package/dist/utils/entity2table.js +6 -6
- package/package.json +9 -4
- package/public/index.html +7 -2
- package/public/scripts/common.js +74 -43
- package/public/scripts/details-popup.js +18 -9
- package/public/scripts/json-viewer.js +4 -4
- package/public/scripts/ws.js +83 -0
- package/public/styles/index.css +5 -0
- package/src/defaults.ts +1 -1
- package/src/entities/log.entity.ts +3 -3
- package/src/guards/access.guard.ts +5 -5
- package/src/index.ts +4 -4
- package/src/interceptors/log.interceptor.ts +5 -5
- package/src/log.module.ts +50 -17
- package/src/services/log.service.ts +118 -40
- package/src/services/memory-db.service.ts +9 -9
- package/src/services/ws.d.ts +1 -0
- package/src/services/ws.service.ts +110 -0
- package/src/types/index.ts +3 -3
- package/src/types/log.type.ts +5 -5
- package/src/types/options.type.ts +7 -0
- package/src/utils/entity2table.ts +8 -8
- package/public/json.html +0 -0
package/src/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
1
|
+
export * from './interceptors/log.interceptor';
|
|
2
|
+
export * from './log.module';
|
|
3
|
+
export * from './types/index';
|
|
4
|
+
export * from './services/log.service';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { CallHandler, NestInterceptor } from
|
|
2
|
-
import { Inject, Injectable } from
|
|
3
|
-
import { Observable, tap } from
|
|
4
|
-
import { LogService } from
|
|
5
|
-
import { ExecutionContextHost } from
|
|
1
|
+
import type { CallHandler, NestInterceptor } from '@nestjs/common';
|
|
2
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
3
|
+
import { Observable, tap } from 'rxjs';
|
|
4
|
+
import { LogService } from '../services/log.service';
|
|
5
|
+
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
|
|
6
6
|
|
|
7
7
|
@Injectable()
|
|
8
8
|
export class LogInterceptor implements NestInterceptor {
|
package/src/log.module.ts
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
|
-
import { Module, Global, HttpException } from
|
|
2
|
-
import { LogService } from
|
|
3
|
-
import { MemoryDbService } from
|
|
4
|
-
import { LogInterceptor } from
|
|
5
|
-
import { LogModuleOptions } from
|
|
6
|
-
import { TypeOrmModule } from
|
|
7
|
-
import querystring from
|
|
8
|
-
import { ApplicationConfig } from
|
|
9
|
-
import { join } from
|
|
10
|
-
import { LogAccessGuard } from
|
|
1
|
+
import { Module, Global, HttpException } from '@nestjs/common';
|
|
2
|
+
import { LogService } from './services/log.service';
|
|
3
|
+
import { MemoryDbService } from './services/memory-db.service';
|
|
4
|
+
import { LogInterceptor } from './interceptors/log.interceptor';
|
|
5
|
+
import { LogModuleOptions } from './types';
|
|
6
|
+
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
7
|
+
import querystring from 'node:querystring';
|
|
8
|
+
import { ApplicationConfig } from '@nestjs/core';
|
|
9
|
+
import { join } from 'node:path';
|
|
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) {
|
|
@@ -32,14 +40,34 @@ export class LogModule {
|
|
|
32
40
|
app.useGlobalInterceptors(new LogInterceptor(logService)); // intercept all errors
|
|
33
41
|
|
|
34
42
|
if (options?.path) {
|
|
35
|
-
app.useStaticAssets(join(__dirname,
|
|
43
|
+
app.useStaticAssets(join(__dirname, '..', 'public'), {
|
|
36
44
|
prefix: options.path,
|
|
37
45
|
});
|
|
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
|
-
httpAdapter.get(join(options.path,
|
|
70
|
+
httpAdapter.get(join(options.path, 'api'), async (req: any, res: any) => {
|
|
43
71
|
logAccessGuard.canActivate(req);
|
|
44
72
|
|
|
45
73
|
res.json(await logService.getAll());
|
|
@@ -47,19 +75,24 @@ export class LogModule {
|
|
|
47
75
|
|
|
48
76
|
// delete log endpoint
|
|
49
77
|
httpAdapter.delete(
|
|
50
|
-
join(options.path,
|
|
78
|
+
join(options.path, 'api'),
|
|
51
79
|
async (req: any, res: any) => {
|
|
52
80
|
logAccessGuard.canActivate(req);
|
|
53
81
|
|
|
54
|
-
const params = querystring.parse(req.url.split(
|
|
82
|
+
const params = querystring.parse(req.url.split('?')[1]);
|
|
55
83
|
|
|
56
84
|
if (!params.id) {
|
|
57
|
-
throw new HttpException(
|
|
85
|
+
throw new HttpException('id is required', 400);
|
|
58
86
|
}
|
|
59
87
|
|
|
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,36 +1,55 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
Injectable,
|
|
3
|
+
LoggerService,
|
|
4
|
+
OnApplicationShutdown,
|
|
5
|
+
Scope,
|
|
6
|
+
} from '@nestjs/common';
|
|
7
|
+
import { MemoryDbService } from './memory-db.service';
|
|
8
|
+
import { defaultTable } from '../defaults';
|
|
9
|
+
import { Context, LogModuleOptions, LogType } from '../types';
|
|
5
10
|
import {
|
|
6
11
|
DataSource,
|
|
7
12
|
DataSourceOptions,
|
|
8
13
|
EntityManager,
|
|
9
14
|
EntitySchema,
|
|
10
|
-
} from
|
|
11
|
-
import { createLogEntity } from
|
|
12
|
-
import { ExecutionContextHost } from
|
|
13
|
-
import { setInterval } from
|
|
14
|
-
import { entity2table } from
|
|
15
|
+
} from 'typeorm';
|
|
16
|
+
import { createLogEntity } from '../entities/log.entity';
|
|
17
|
+
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
|
|
18
|
+
import { setInterval } from 'timers';
|
|
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
|
-
static Log: EntitySchema = createLogEntity(defaultTable,
|
|
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(
|
|
29
46
|
options.database?.collection || options.database?.table || defaultTable,
|
|
30
|
-
options.database?.type ||
|
|
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,
|
|
@@ -43,9 +62,7 @@ export class LogService implements LoggerService {
|
|
|
43
62
|
LogService.connection = new DataSource(dataSourceOptions);
|
|
44
63
|
await LogService.connection.initialize();
|
|
45
64
|
|
|
46
|
-
if (dataSourceOptions.type !==
|
|
47
|
-
// LogService.idName = "id";
|
|
48
|
-
|
|
65
|
+
if (dataSourceOptions.type !== 'mongodb') {
|
|
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) {
|
|
@@ -124,22 +159,26 @@ export class LogService implements LoggerService {
|
|
|
124
159
|
async getAll(): Promise<any[]> {
|
|
125
160
|
return this.getConnection().find(LogService.Log, {
|
|
126
161
|
select: [
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
162
|
+
'_id',
|
|
163
|
+
'type',
|
|
164
|
+
'message',
|
|
165
|
+
'count',
|
|
166
|
+
'createdAt',
|
|
167
|
+
'updatedAt',
|
|
168
|
+
'context',
|
|
169
|
+
'trace',
|
|
170
|
+
'breadcrumbs',
|
|
136
171
|
],
|
|
137
|
-
order: { updatedAt:
|
|
172
|
+
order: { updatedAt: 'DESC' },
|
|
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: {
|
|
@@ -153,26 +192,45 @@ export class LogService implements LoggerService {
|
|
|
153
192
|
const connection = this.getConnection();
|
|
154
193
|
|
|
155
194
|
// find the same log in DB
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
195
|
+
let log;
|
|
196
|
+
|
|
197
|
+
if (LogService.options.join) {
|
|
198
|
+
log = await connection.findOne(LogService.Log, {
|
|
199
|
+
where: {
|
|
200
|
+
type: data.type,
|
|
201
|
+
message: data.message,
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
}
|
|
162
205
|
|
|
163
206
|
const context = data.context ? this.parseContext(data.context) : undefined;
|
|
164
207
|
|
|
165
208
|
if (log) {
|
|
166
|
-
|
|
209
|
+
const updatedLog = {
|
|
210
|
+
context,
|
|
211
|
+
trace: data.trace,
|
|
212
|
+
breadcrumbs: this.breadcrumbs,
|
|
213
|
+
count: log.count + 1,
|
|
214
|
+
updatedAt: currentDate,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
this.wsService.sendMessage({
|
|
218
|
+
action: 'update',
|
|
219
|
+
data: { ...log, ...updatedLog },
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
await connection.update(LogService.Log, log['_id'], {
|
|
167
223
|
context,
|
|
168
224
|
trace: data.trace,
|
|
169
225
|
breadcrumbs: this.breadcrumbs,
|
|
170
226
|
count: log.count + 1,
|
|
171
227
|
updatedAt: currentDate,
|
|
172
228
|
});
|
|
229
|
+
|
|
230
|
+
return { ...log, ...updatedLog };
|
|
173
231
|
}
|
|
174
232
|
|
|
175
|
-
|
|
233
|
+
const insertedLog = {
|
|
176
234
|
type: data.type,
|
|
177
235
|
message: data.message,
|
|
178
236
|
context,
|
|
@@ -181,7 +239,27 @@ export class LogService implements LoggerService {
|
|
|
181
239
|
count: 1,
|
|
182
240
|
createdAt: currentDate,
|
|
183
241
|
updatedAt: currentDate,
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
const res = await connection.insert(LogService.Log, insertedLog);
|
|
245
|
+
const _id = this.getNewObjectId(res);
|
|
246
|
+
|
|
247
|
+
this.wsService.sendMessage({
|
|
248
|
+
action: 'insert',
|
|
249
|
+
data: { _id, ...insertedLog },
|
|
184
250
|
});
|
|
251
|
+
|
|
252
|
+
return { _id, ...insertedLog };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private getNewObjectId(result: any): string | number {
|
|
256
|
+
if (result.identifiers) {
|
|
257
|
+
return result.identifiers[0]._id;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
console.log(result);
|
|
261
|
+
|
|
262
|
+
return result._id;
|
|
185
263
|
}
|
|
186
264
|
|
|
187
265
|
private getConnection(): EntityManager {
|
|
@@ -225,9 +303,9 @@ export class LogService implements LoggerService {
|
|
|
225
303
|
private async checkRecords() {
|
|
226
304
|
if (LogService.options?.maxSize) {
|
|
227
305
|
const latest = await this.getConnection().find(LogService.Log, {
|
|
228
|
-
order: { updatedAt:
|
|
306
|
+
order: { updatedAt: 'DESC' },
|
|
229
307
|
take: LogService.options?.maxSize,
|
|
230
|
-
select: [
|
|
308
|
+
select: ['_id'],
|
|
231
309
|
});
|
|
232
310
|
|
|
233
311
|
const latestIds = latest.map((item) => item.id);
|
|
@@ -237,7 +315,7 @@ export class LogService implements LoggerService {
|
|
|
237
315
|
.createQueryBuilder()
|
|
238
316
|
.delete()
|
|
239
317
|
.from(LogService.Log)
|
|
240
|
-
.where(
|
|
318
|
+
.where('_id NOT IN (:...ids)', { ids: latestIds })
|
|
241
319
|
.execute();
|
|
242
320
|
}
|
|
243
321
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// Memory DB layer
|
|
2
|
-
import { Injectable } from
|
|
3
|
-
import { createHash, randomBytes } from
|
|
4
|
-
import { defaultTable } from
|
|
5
|
-
import { EntitySchema, FindManyOptions } from
|
|
2
|
+
import { Injectable } from '@nestjs/common';
|
|
3
|
+
import { createHash, randomBytes } from 'crypto';
|
|
4
|
+
import { defaultTable } from '../defaults';
|
|
5
|
+
import { EntitySchema, FindManyOptions } from 'typeorm';
|
|
6
6
|
|
|
7
7
|
const tables = [defaultTable];
|
|
8
8
|
|
|
@@ -20,8 +20,8 @@ export class MemoryDbService {
|
|
|
20
20
|
const table = this.getTableName(entity);
|
|
21
21
|
|
|
22
22
|
// generate new random _id
|
|
23
|
-
const randomData = randomBytes(24).toString(
|
|
24
|
-
const _id = createHash(
|
|
23
|
+
const randomData = randomBytes(24).toString('hex');
|
|
24
|
+
const _id = createHash('sha256').update(randomData).digest('hex');
|
|
25
25
|
|
|
26
26
|
this.db[table].push({
|
|
27
27
|
...data,
|
|
@@ -39,7 +39,7 @@ export class MemoryDbService {
|
|
|
39
39
|
const table = this.getTableName(entity);
|
|
40
40
|
let index: number | null = null;
|
|
41
41
|
|
|
42
|
-
if (typeof condition ===
|
|
42
|
+
if (typeof condition === 'string') {
|
|
43
43
|
index = this.findIndex(entity, { where: { _id: condition } });
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -136,8 +136,8 @@ export class MemoryDbService {
|
|
|
136
136
|
if (
|
|
137
137
|
obj1 == null ||
|
|
138
138
|
obj2 == null ||
|
|
139
|
-
typeof obj1 !==
|
|
140
|
-
typeof obj2 !==
|
|
139
|
+
typeof obj1 !== 'object' ||
|
|
140
|
+
typeof obj2 !== 'object'
|
|
141
141
|
) {
|
|
142
142
|
return false;
|
|
143
143
|
}
|
|
@@ -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
|
+
}
|
package/src/types/index.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
1
|
+
export * from './context.type';
|
|
2
|
+
export * from './log.type';
|
|
3
|
+
export * from './options.type';
|
package/src/types/log.type.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { DataSourceOptions } from "typeorm";
|
|
|
3
3
|
export type LogModuleOptions = {
|
|
4
4
|
path?: string;
|
|
5
5
|
key?: string; // access key
|
|
6
|
+
join?: boolean; // merge the message duplicates
|
|
6
7
|
maxRecords?: number; // max log records
|
|
7
8
|
maxAge?: number; // in days
|
|
8
9
|
maxSize?: number; // in megabytes
|
|
@@ -12,4 +13,10 @@ export type LogModuleOptions = {
|
|
|
12
13
|
table?: string;
|
|
13
14
|
collection?: string;
|
|
14
15
|
};
|
|
16
|
+
websocket?: {
|
|
17
|
+
port?: number;
|
|
18
|
+
namespace?: string;
|
|
19
|
+
host?: string;
|
|
20
|
+
secure?: boolean;
|
|
21
|
+
};
|
|
15
22
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { EntitySchema, Table } from
|
|
2
|
-
import { defaultTable } from
|
|
1
|
+
import { EntitySchema, Table } from 'typeorm';
|
|
2
|
+
import { defaultTable } from '../defaults';
|
|
3
3
|
|
|
4
4
|
export function entity2table(entity: EntitySchema): Table {
|
|
5
5
|
return new Table({
|
|
@@ -9,7 +9,7 @@ export function entity2table(entity: EntitySchema): Table {
|
|
|
9
9
|
type: resolveColumnType(col?.type),
|
|
10
10
|
isPrimary: !!col?.primary,
|
|
11
11
|
isGenerated: !!col?.generated,
|
|
12
|
-
generationStrategy: col?.generated ?
|
|
12
|
+
generationStrategy: col?.generated ? 'increment' : undefined,
|
|
13
13
|
isUnique: !!col?.unique,
|
|
14
14
|
isNullable: !!col?.nullable,
|
|
15
15
|
default: col?.default,
|
|
@@ -20,14 +20,14 @@ export function entity2table(entity: EntitySchema): Table {
|
|
|
20
20
|
function resolveColumnType(type: any): string {
|
|
21
21
|
switch (type) {
|
|
22
22
|
case String:
|
|
23
|
-
return
|
|
23
|
+
return 'text';
|
|
24
24
|
case Number:
|
|
25
|
-
return
|
|
25
|
+
return 'int';
|
|
26
26
|
case Date:
|
|
27
|
-
return
|
|
27
|
+
return 'timestamp';
|
|
28
28
|
case Boolean:
|
|
29
|
-
return
|
|
29
|
+
return 'boolean';
|
|
30
30
|
default:
|
|
31
|
-
return
|
|
31
|
+
return 'text';
|
|
32
32
|
}
|
|
33
33
|
}
|
package/public/json.html
DELETED
|
File without changes
|