@jrosadob/ms-sophia-framework-mysql 0.0.1 → 0.0.3
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/.prettierrc +4 -0
- package/README.md +21 -0
- package/bootstrap.yml +193 -0
- package/eslint.config.mjs +35 -0
- package/libs/ms-sophia-framework-mysql/src/app.module.ts +14 -0
- package/libs/ms-sophia-framework-mysql/src/core/amqp-clients/amqp-clients.module.ts +43 -0
- package/libs/ms-sophia-framework-mysql/src/core/amqp-clients/amqp-clients.ts +22 -0
- package/libs/ms-sophia-framework-mysql/src/core/amqp-clients/amqp-config.ts +3 -0
- package/libs/ms-sophia-framework-mysql/src/core/amqp-clients/amqp-pattern.ts +61 -0
- package/libs/ms-sophia-framework-mysql/src/core/amqp-clients/amqp-register.options.ts +6 -0
- package/libs/ms-sophia-framework-mysql/src/core/amqp-clients/amqp-sample.module.ts +20 -0
- package/libs/ms-sophia-framework-mysql/src/core/amqp-clients/amqp-sample.service.ts +34 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/base/error.ts +5 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/base/result.ts +15 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/base/status-base.ts +12 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/dto/change-status.dto.ts +13 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/dto/log.dto.ts +29 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/dto/order-by.dto.ts +9 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/dto/pagination.dto.ts +10 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/dto/search.dto.ts +8 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/dto/select.dto.ts +23 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/entity/base.entity.ts +16 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/exceptions/app-badrequest.exception.ts +31 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/exceptions/app-http.exception.ts +35 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/exceptions/app-unauthorized.exception.ts +36 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/filters/all-exception.filter.ts +42 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/index.ts +24 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/pipes/app-validation.pipe.ts +35 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/result/bad-request.result.ts +17 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/result/property.result.ts +8 -0
- package/libs/ms-sophia-framework-mysql/src/core/common/result/unauthorized.result.ts +14 -0
- package/libs/ms-sophia-framework-mysql/src/core/config/config.module.ts +22 -0
- package/libs/ms-sophia-framework-mysql/src/core/config/config.service.ts +37 -0
- package/libs/ms-sophia-framework-mysql/src/core/config/index.ts +2 -0
- package/libs/ms-sophia-framework-mysql/src/core/database/database.module.ts +18 -0
- package/libs/ms-sophia-framework-mysql/src/core/database/database.provider.ts +70 -0
- package/libs/ms-sophia-framework-mysql/src/core/database/index.ts +3 -0
- package/libs/ms-sophia-framework-mysql/src/core/database/providers.ts +3 -0
- package/libs/ms-sophia-framework-mysql/src/core/health/health.controller.ts +36 -0
- package/libs/ms-sophia-framework-mysql/src/core/health/health.module.ts +16 -0
- package/libs/ms-sophia-framework-mysql/src/core/interceptors/headers.interceptor.ts +16 -0
- package/libs/ms-sophia-framework-mysql/src/core/interceptors/index.ts +2 -0
- package/libs/ms-sophia-framework-mysql/src/core/interceptors/logging.interceptor.ts +43 -0
- package/libs/ms-sophia-framework-mysql/src/core/logger/index.ts +4 -0
- package/libs/ms-sophia-framework-mysql/src/core/logger/logger.module.ts +10 -0
- package/libs/ms-sophia-framework-mysql/src/core/logger/logger.request.ts +137 -0
- package/libs/ms-sophia-framework-mysql/src/core/logger/logger.ts +146 -0
- package/libs/ms-sophia-framework-mysql/src/core/logger/typeorm-logger.service.ts +76 -0
- package/libs/ms-sophia-framework-mysql/src/core/subscribers/entity.subscriber.ts +79 -0
- package/libs/ms-sophia-framework-mysql/src/core/subscribers/log.entity.ts +35 -0
- package/libs/ms-sophia-framework-mysql/src/core/tools/env-vars.ts +26 -0
- package/libs/ms-sophia-framework-mysql/src/core/tools/index.ts +4 -0
- package/libs/ms-sophia-framework-mysql/src/core/tools/nanoid.ts +4 -0
- package/libs/ms-sophia-framework-mysql/src/core/tools/query.ts +186 -0
- package/libs/ms-sophia-framework-mysql/src/core/tools/send-to-logstash.ts +103 -0
- package/libs/ms-sophia-framework-mysql/src/index.ts +1 -0
- package/libs/ms-sophia-framework-mysql/src/main.ts +230 -0
- package/libs/ms-sophia-framework-mysql/tsconfig.lib.json +9 -0
- package/nest-cli.json +20 -0
- package/package.json +43 -55
- package/pnpm-workspace.yaml +3 -0
- package/publish.sh +18 -0
- package/src/app.controller.spec.ts +22 -0
- package/src/app.controller.ts +12 -0
- package/src/app.module.ts +10 -0
- package/src/app.service.ts +8 -0
- package/src/main.ts +8 -0
- package/test/app.e2e-spec.ts +29 -0
- package/test/jest-e2e.json +16 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +33 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConsoleLogger,
|
|
3
|
+
INestApplicationContext,
|
|
4
|
+
Injectable,
|
|
5
|
+
Logger as NestLogger,
|
|
6
|
+
LoggerService as NestLoggerService,
|
|
7
|
+
} from '@nestjs/common';
|
|
8
|
+
import { ConfigService } from '../config/config.service';
|
|
9
|
+
import { REQUEST } from '@nestjs/core';
|
|
10
|
+
import { sendToLogstash } from '../tools/send-to-logstash';
|
|
11
|
+
// import { get } from 'http'
|
|
12
|
+
|
|
13
|
+
@Injectable()
|
|
14
|
+
export class Logger extends ConsoleLogger implements NestLoggerService {
|
|
15
|
+
private static logstashUrl: string | undefined;
|
|
16
|
+
private static appContext: INestApplicationContext;
|
|
17
|
+
private static configService: ConfigService;
|
|
18
|
+
|
|
19
|
+
constructor(private readonly configService: ConfigService) {
|
|
20
|
+
super();
|
|
21
|
+
Logger.configService = this.configService;
|
|
22
|
+
Logger.logstashUrl = this.configService.get<string>('logger.http.host');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
private static getConfigService(): ConfigService | undefined {
|
|
26
|
+
if (!this.appContext) {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
return this.appContext.get(ConfigService);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
private static async getRequest() {
|
|
33
|
+
const request = this.appContext
|
|
34
|
+
? await this.appContext.resolve<any>(REQUEST).catch(() => null)
|
|
35
|
+
: null;
|
|
36
|
+
const value = typeof request?.method === 'string' ? request : null;
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private static async getResponse() {
|
|
41
|
+
const request = await this.getRequest();
|
|
42
|
+
return request?.response ?? null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private static async sendToLogstash(
|
|
46
|
+
level: string,
|
|
47
|
+
message: string,
|
|
48
|
+
meta?: any,
|
|
49
|
+
): Promise<void> {
|
|
50
|
+
await sendToLogstash(
|
|
51
|
+
this.configService,
|
|
52
|
+
this.logstashUrl,
|
|
53
|
+
level,
|
|
54
|
+
message,
|
|
55
|
+
meta,
|
|
56
|
+
await this.getRequest(),
|
|
57
|
+
await this.getResponse(),
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
setContext(context: string): void {
|
|
62
|
+
super.setContext(context);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static setLogstashUrl(url: string) {
|
|
66
|
+
this.logstashUrl = url;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
static setApplicationContext(appContext: INestApplicationContext) {
|
|
70
|
+
this.appContext = appContext;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//#region Public methods
|
|
74
|
+
setLogstashUrl(url: string) {
|
|
75
|
+
Logger.setLogstashUrl(url);
|
|
76
|
+
}
|
|
77
|
+
log(message: string): void;
|
|
78
|
+
log(message: any, context: string): void;
|
|
79
|
+
log(message: any, context?: string): void {
|
|
80
|
+
if (context) {
|
|
81
|
+
super.log('- ' + message, context);
|
|
82
|
+
Logger.sendToLogstash('log', message, { context: context });
|
|
83
|
+
} else {
|
|
84
|
+
super.log('- ' + message);
|
|
85
|
+
Logger.sendToLogstash('log', message);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
warn(message: string) {
|
|
90
|
+
super.warn('- ' + message);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
error(message: any, ...optionalParams: any[]) {
|
|
94
|
+
super.error('0' + message, ...optionalParams)
|
|
95
|
+
//this.safeSend('error', message, optionalParams)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// error(message: string) {
|
|
99
|
+
// super.error('- ' + message);
|
|
100
|
+
// }
|
|
101
|
+
|
|
102
|
+
debug(message: string) {
|
|
103
|
+
super.debug('- ' + message);
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
|
|
107
|
+
//#region Static methods
|
|
108
|
+
static async log(message: string, meta?: any) {
|
|
109
|
+
NestLogger.log(message, meta);
|
|
110
|
+
//await Logger.sendToLogstash('log', message, { meta: meta })
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
static async warn(message: string, meta?: any) {
|
|
114
|
+
NestLogger.warn(message, meta);
|
|
115
|
+
await Logger.sendToLogstash('warn', message, { meta: meta });
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static async error(message: any, ...optionalParams: any[])
|
|
119
|
+
static async error(message: string);
|
|
120
|
+
static async error(message: string, trace: string);
|
|
121
|
+
static async error(message: string, trace: string, meta: any);
|
|
122
|
+
static async error(message: string, trace?: string, meta?: any) {
|
|
123
|
+
if (meta) {
|
|
124
|
+
// Caso con mensaje, traza y metadatos
|
|
125
|
+
console.error('Error message1:', message);
|
|
126
|
+
NestLogger.error('1' + message, trace, meta);
|
|
127
|
+
await Logger.sendToLogstash('error', message, { trace, meta });
|
|
128
|
+
} else if (trace) {
|
|
129
|
+
// Caso con mensaje y traza
|
|
130
|
+
console.error('Error message2:', message);
|
|
131
|
+
NestLogger.error('2' + message, trace);
|
|
132
|
+
await Logger.sendToLogstash('error', message, { trace });
|
|
133
|
+
} else {
|
|
134
|
+
// Caso solo con mensaje
|
|
135
|
+
console.error('Error message3:', message);
|
|
136
|
+
NestLogger.error('3' + message);
|
|
137
|
+
await Logger.sendToLogstash('error', message);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
static async debug(message: string, meta?: any) {
|
|
142
|
+
NestLogger.debug(message, meta);
|
|
143
|
+
await Logger.sendToLogstash('debug', message, { meta: meta });
|
|
144
|
+
}
|
|
145
|
+
//#endregion
|
|
146
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Logger as TypeOrmLogger, QueryRunner } from 'typeorm'
|
|
2
|
+
import { Logger } from './logger'
|
|
3
|
+
|
|
4
|
+
export class TypeOrmLoggerService implements TypeOrmLogger {
|
|
5
|
+
//private readonly logger = new Logger(TypeOrmLoggerService.name)
|
|
6
|
+
|
|
7
|
+
constructor(private readonly loggerHttpService: Logger, private readonly logging: boolean) {
|
|
8
|
+
//Logger.setContext(TypeOrmLoggerService.name)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
logQuery(query: string, parameters?: any[], queryRunner?: QueryRunner) {
|
|
12
|
+
const message = `Query: ${query} | Parameters: ${JSON.stringify(parameters)}`
|
|
13
|
+
if (!this.logging) return;
|
|
14
|
+
//this.logger.log(message)
|
|
15
|
+
//this.loggerHttpService.log(message, 'TypeORM')
|
|
16
|
+
Logger.log(message, 'TypeORM')
|
|
17
|
+
//this.loggerHttpService.log(message)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
logQueryError(error: string, query: string, parameters?: any[], queryRunner?: QueryRunner) {
|
|
21
|
+
const message = `Query Error: ${error} | Query: ${query} | Parameters: ${JSON.stringify(parameters)}`
|
|
22
|
+
if (!this.logging) return;
|
|
23
|
+
//this.loggerHttpService.error(message, undefined, 'TypeORM')
|
|
24
|
+
//this.loggerHttpService.error(message)
|
|
25
|
+
Logger.error(message, 'undefined', 'TypeORM')
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
logQuerySlow(time: number, query: string, parameters?: any[], queryRunner?: QueryRunner) {
|
|
29
|
+
const message = `Slow Query (${time}ms): ${query} | Parameters: ${JSON.stringify(parameters)}`
|
|
30
|
+
if (!this.logging) return;
|
|
31
|
+
//this.logger.warn(message)
|
|
32
|
+
// this.loggerHttpService.warn(message, 'TypeORM')
|
|
33
|
+
//this.loggerHttpService.warn(message)
|
|
34
|
+
Logger.warn(message, 'TypeORM')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
logSchemaBuild(message: string, queryRunner?: QueryRunner) {
|
|
38
|
+
if (!this.logging) return;
|
|
39
|
+
//this.logger.log(`Schema Build: ${message}`)
|
|
40
|
+
//this.loggerHttpService.log(`Schema Build: ${message}`, 'TypeORM')
|
|
41
|
+
this.loggerHttpService.log(`Schema Build: ${message}`)
|
|
42
|
+
Logger.log(`Schema Build: ${message}`)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
logMigration(message: string, queryRunner?: QueryRunner) {
|
|
46
|
+
if (!this.logging) return;
|
|
47
|
+
//this.logger.log(`Migration: ${message}`)
|
|
48
|
+
//this.loggerHttpService.log(`Migration: ${message}`, 'TypeORM')
|
|
49
|
+
//this.loggerHttpService.log(`- Migration: ${message}`)
|
|
50
|
+
Logger.log(`Migration: ${message}`)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
log(level: 'log' | 'info' | 'warn', message: any, queryRunner?: QueryRunner) {
|
|
54
|
+
if (!this.logging) return;
|
|
55
|
+
switch (level) {
|
|
56
|
+
case 'log':
|
|
57
|
+
//this.logger.log(message)
|
|
58
|
+
//this.loggerHttpService.log(message, 'TypeORM')
|
|
59
|
+
//this.loggerHttpService.log(message)
|
|
60
|
+
Logger.log(message, 'TypeORM')
|
|
61
|
+
break
|
|
62
|
+
case 'info':
|
|
63
|
+
//this.logger.log(message)
|
|
64
|
+
// this.loggerHttpService.log(message, 'TypeORM')
|
|
65
|
+
//this.loggerHttpService.log(message)
|
|
66
|
+
Logger.log(message, 'TypeORM')
|
|
67
|
+
break
|
|
68
|
+
case 'warn':
|
|
69
|
+
//this.logger.warn(message)
|
|
70
|
+
// this.loggerHttpService.warn(message, 'TypeORM')
|
|
71
|
+
//this.loggerHttpService.warn(message)
|
|
72
|
+
Logger.warn(message, 'TypeORM')
|
|
73
|
+
break
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { DataSource, EntitySubscriberInterface, EventSubscriber, InsertEvent, RemoveEvent, UpdateEvent } from "typeorm";
|
|
2
|
+
import { Logs } from "./log.entity";
|
|
3
|
+
import { Inject } from "@nestjs/common";
|
|
4
|
+
import { Providers } from "../database/providers";
|
|
5
|
+
import { nanoid } from "../tools";
|
|
6
|
+
import { Logger } from "../logger";
|
|
7
|
+
import { BaseEntity } from "../common";
|
|
8
|
+
import { ConfigService } from "../config";
|
|
9
|
+
|
|
10
|
+
@EventSubscriber()
|
|
11
|
+
export class EntitySubscriber implements EntitySubscriberInterface<BaseEntity>
|
|
12
|
+
{
|
|
13
|
+
constructor(
|
|
14
|
+
@Inject(Providers.DATA_SOURCE)
|
|
15
|
+
private readonly dataSource: DataSource,
|
|
16
|
+
// private readonly configService: ConfigService
|
|
17
|
+
) {
|
|
18
|
+
if (dataSource && dataSource.subscribers) {
|
|
19
|
+
dataSource.subscribers.push(this);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
afterInsert(event: InsertEvent<any>) {
|
|
24
|
+
|
|
25
|
+
if (event.metadata.target === Logs) {
|
|
26
|
+
Logger.log(`Logs. EntitySubscriber: afterInsert skipped on ${event.metadata.name} for Logs entity`);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (!(event.entity instanceof BaseEntity)) {
|
|
30
|
+
Logger.log(`BaseEntity. EntitySubscriber: afterInsert skipped on ${event.metadata.name} for non-BaseEntity`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
Logger.log(`Nothing. EntitySubscriber: afterInsert on ${event.metadata.name}`);
|
|
34
|
+
return event.manager.save(Logs, this.getLog(event.entity));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
afterUpdate(event: UpdateEvent<any>) {
|
|
38
|
+
if (event.metadata.target === Logs) return;
|
|
39
|
+
if (!(event.entity instanceof BaseEntity)) return;
|
|
40
|
+
Logger.log(`EntitySubscriber: afterUpdate on ${event.metadata.name}`);
|
|
41
|
+
return event.manager.save(Logs, this.getLog(event.entity));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
afterRemove(event: RemoveEvent<any>) {
|
|
45
|
+
if (event.metadata.target === Logs) return;
|
|
46
|
+
if (!(event.entity instanceof BaseEntity)) return;
|
|
47
|
+
Logger.log(`EntitySubscriber: afterRemove on ${event.metadata.name}`);
|
|
48
|
+
return event.manager.save(Logs, this.getLog(event.entity));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getLog(entity: any): Logs {
|
|
52
|
+
var log: Logs = new Logs();
|
|
53
|
+
// Implement custom logging logic here
|
|
54
|
+
log.logId = nanoid();
|
|
55
|
+
log.entity = entity.constructor.name;
|
|
56
|
+
if (typeof entity.id === 'string') {
|
|
57
|
+
log.primaryKeyGuid = entity.id;
|
|
58
|
+
log.primaryKeyNumber = 0;
|
|
59
|
+
} else if (typeof entity.id === 'number') {
|
|
60
|
+
log.primaryKeyGuid = '';
|
|
61
|
+
log.primaryKeyNumber = entity.id;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Custom data
|
|
65
|
+
log.action = 'CUSTOM_ACTION';
|
|
66
|
+
log.reason = 'Custom reason';
|
|
67
|
+
|
|
68
|
+
log.typeId = 0;
|
|
69
|
+
log.userId = 'system';
|
|
70
|
+
log.roleId = 0;
|
|
71
|
+
log.bossId = 'system';
|
|
72
|
+
|
|
73
|
+
log.statusId = 0;
|
|
74
|
+
log.correlationId = nanoid();
|
|
75
|
+
// Custom data
|
|
76
|
+
|
|
77
|
+
return log;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// See: https://typeorm.io/entities#column-types
|
|
2
|
+
// See: https://github.com/paralleldrive/cuid2
|
|
3
|
+
// import { createId } from '@paralleldrive/cuid2';
|
|
4
|
+
// import { nanoid } from 'nanoid'
|
|
5
|
+
import { Column, Entity, PrimaryColumn } from 'typeorm'
|
|
6
|
+
|
|
7
|
+
@Entity('logs')
|
|
8
|
+
export class Logs {
|
|
9
|
+
@PrimaryColumn({ name: 'log_id', type: 'varchar', length: 21 })
|
|
10
|
+
logId: string
|
|
11
|
+
@Column({ type: 'varchar', length: 32 })
|
|
12
|
+
entity: string
|
|
13
|
+
@Column({ name: 'primary_key_guid', type: 'varchar', length: 21 })
|
|
14
|
+
primaryKeyGuid: string
|
|
15
|
+
@Column({ name: 'primary_key_number', type: 'int' })
|
|
16
|
+
primaryKeyNumber: number
|
|
17
|
+
@Column({ type: 'varchar', length: 16 })
|
|
18
|
+
action: string
|
|
19
|
+
@Column({ type: 'varchar', length: 128 })
|
|
20
|
+
reason: string
|
|
21
|
+
@Column({ name: 'type_id', type: 'int' })
|
|
22
|
+
typeId: number
|
|
23
|
+
@Column({ name: 'user_id', type: 'varchar', length: 21 })
|
|
24
|
+
userId: string
|
|
25
|
+
@Column({ name: 'role_id', type: 'int' })
|
|
26
|
+
roleId: number
|
|
27
|
+
@Column({ name: 'boss_id', type: 'varchar', length: 21 })
|
|
28
|
+
bossId: string
|
|
29
|
+
@Column({ name: 'status_id', type: 'int' })
|
|
30
|
+
statusId: number
|
|
31
|
+
@Column({ name: 'correlation_id', type: 'varchar', length: 21 })
|
|
32
|
+
correlationId: string
|
|
33
|
+
@Column({ name: 'date_log', type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
|
|
34
|
+
dateLog: string
|
|
35
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function replaceEnvVars(str) {
|
|
2
|
+
return str.replaceAll(/\${([^}]+)}/g, (_, name) => process.env[name] || '')
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
// export function replaceEnvVars(str) {
|
|
6
|
+
// return str.replaceAll(/${([^}]+)}/g, (_, name) => {
|
|
7
|
+
// const value = process.env[name] || '';
|
|
8
|
+
// // Convierte los saltos de línea literales en saltos reales para las private keys u otros valores multilínea
|
|
9
|
+
// return typeof value === 'string' ? value.replace(/\n/g, '\n') : value;
|
|
10
|
+
// });
|
|
11
|
+
// }
|
|
12
|
+
|
|
13
|
+
// export function replaceEnvVars(str) {
|
|
14
|
+
// return str.replaceAll(/\${([^}]+)}/g, (_, name) => {
|
|
15
|
+
// const value = process.env[name] || '';
|
|
16
|
+
// Convierte los saltos de línea literales (\n) en saltos reales
|
|
17
|
+
// if (name === 'FIREBASE_PRIVATE_KEY') {
|
|
18
|
+
// console.log('value FIREBASE_PRIVATE_KEY:', value);
|
|
19
|
+
// }
|
|
20
|
+
// const returnValue = typeof value === 'string' ? value.replace(/\\n/g, '\n') : value;
|
|
21
|
+
// if (name === 'FIREBASE_PRIVATE_KEY') {
|
|
22
|
+
// console.log('returnValue FIREBASE_PRIVATE_KEY:', returnValue);
|
|
23
|
+
// }
|
|
24
|
+
// return returnValue;
|
|
25
|
+
// });
|
|
26
|
+
// }
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { SearchDto } from '../common/dto/search.dto'
|
|
2
|
+
import { ObjectLiteral, SelectQueryBuilder } from 'typeorm'
|
|
3
|
+
|
|
4
|
+
export function applyWhere<E extends ObjectLiteral>(
|
|
5
|
+
query: SelectQueryBuilder<E>,
|
|
6
|
+
where: Record<string, any> | Record<string, any>[] | null,
|
|
7
|
+
): SelectQueryBuilder<E> {
|
|
8
|
+
if (!where) return query
|
|
9
|
+
|
|
10
|
+
for (const [key, value] of Object.entries(where)) {
|
|
11
|
+
if (typeof value === 'string' && value.startsWith('LIKE:')) {
|
|
12
|
+
const likeValue = value.replace('LIKE:', '')
|
|
13
|
+
query = query.andWhere(`${key} LIKE :${key}`, {
|
|
14
|
+
[key]: `%${likeValue}%`,
|
|
15
|
+
})
|
|
16
|
+
} else {
|
|
17
|
+
query = query.andWhere(`${key} = :${key}`, { [key]: value })
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return query
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export async function applyOrderAndPagination<E extends ObjectLiteral>(
|
|
24
|
+
query: SelectQueryBuilder<E>,
|
|
25
|
+
orderBy: Array<{ orderBy: string; orderSort: 'ASC' | 'DESC' }>,
|
|
26
|
+
pagination?: { page: number; pageSize: number; totalPages?: number },
|
|
27
|
+
): Promise<SelectQueryBuilder<E>> {
|
|
28
|
+
if (orderBy !== undefined && orderBy.length > 0) {
|
|
29
|
+
query = query.orderBy(orderBy[0].orderBy, orderBy[0].orderSort)
|
|
30
|
+
for (let i = 1; i < orderBy.length; i++) {
|
|
31
|
+
query = query.addOrderBy(orderBy[i].orderBy, orderBy[i].orderSort)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (pagination !== undefined) {
|
|
36
|
+
query = query.skip((pagination.page - 1) * pagination.pageSize)
|
|
37
|
+
query = query.take(pagination.pageSize)
|
|
38
|
+
|
|
39
|
+
// Calcula el total de páginas
|
|
40
|
+
pagination.totalPages = Math.ceil((await query.getCount()) / pagination.pageSize)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return query
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function applyWhereOrderAndPagination<E extends ObjectLiteral>(
|
|
47
|
+
query: SelectQueryBuilder<E>,
|
|
48
|
+
where: Record<string, any> | Record<string, any>[] | null,
|
|
49
|
+
orderBy: Array<{ orderBy: string; orderSort: 'ASC' | 'DESC' }>,
|
|
50
|
+
pagination?: { page: number; pageSize: number; totalPages?: number },
|
|
51
|
+
): Promise<SelectQueryBuilder<E>> {
|
|
52
|
+
// Apply where
|
|
53
|
+
if (where) {
|
|
54
|
+
for (const [key, value] of Object.entries(where)) {
|
|
55
|
+
if (typeof value === 'string' && value.startsWith('LIKE:')) {
|
|
56
|
+
const likeValue = value.replace('LIKE:', '')
|
|
57
|
+
query = query.andWhere(`${key} LIKE :${key}`, {
|
|
58
|
+
[key]: `%${likeValue}%`,
|
|
59
|
+
})
|
|
60
|
+
} else {
|
|
61
|
+
query = query.andWhere(`${key} = :${key}`, { [key]: value })
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Apply order
|
|
67
|
+
if (orderBy !== undefined && orderBy.length > 0) {
|
|
68
|
+
query = query.orderBy(orderBy[0].orderBy, orderBy[0].orderSort)
|
|
69
|
+
for (let i = 1; i < orderBy.length; i++) {
|
|
70
|
+
query = query.addOrderBy(orderBy[i].orderBy, orderBy[i].orderSort)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Apply pagination
|
|
75
|
+
if (pagination !== undefined) {
|
|
76
|
+
query = query.skip((pagination.page - 1) * pagination.pageSize)
|
|
77
|
+
query = query.take(pagination.pageSize)
|
|
78
|
+
|
|
79
|
+
// Calcula el total de páginas
|
|
80
|
+
pagination.totalPages = Math.ceil((await query.getCount()) / pagination.pageSize)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return query
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export async function applySearchWhereOrderAndPagination<E extends ObjectLiteral>(
|
|
87
|
+
query: SelectQueryBuilder<E>,
|
|
88
|
+
search: SearchDto | null,
|
|
89
|
+
where: Record<string, any> | Record<string, any>[] | null,
|
|
90
|
+
orderBy: Array<{ orderBy: string; orderSort: 'ASC' | 'DESC' }>,
|
|
91
|
+
pagination?: { page: number; pageSize: number; totalPages?: number },
|
|
92
|
+
): Promise<SelectQueryBuilder<E>> {
|
|
93
|
+
// Apply search agrupando los OR
|
|
94
|
+
let searchApplied = false
|
|
95
|
+
if (search?.text && Array.isArray(search.properties) && search.properties.length > 0) {
|
|
96
|
+
const orConditions: string[] = []
|
|
97
|
+
const params: Record<string, any> = {}
|
|
98
|
+
search.properties.forEach((prop, idx) => {
|
|
99
|
+
const paramName = `searchText${idx}`
|
|
100
|
+
orConditions.push(`${prop} LIKE :${paramName}`)
|
|
101
|
+
params[paramName] = `%${search.text}%`
|
|
102
|
+
})
|
|
103
|
+
if (orConditions.length > 0) {
|
|
104
|
+
query = query.where(`(${orConditions.join(' OR ')})`, params)
|
|
105
|
+
searchApplied = true
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Apply where (si ya se aplicó search, usar andWhere, si no, usar where para la primera condición)
|
|
110
|
+
if (where) {
|
|
111
|
+
const entries = Object.entries(where)
|
|
112
|
+
entries.forEach(([key, value], idx) => {
|
|
113
|
+
const useAnd = searchApplied || idx > 0
|
|
114
|
+
if (typeof value === 'string' && value.startsWith('LIKE:')) {
|
|
115
|
+
const likeValue = value.replace('LIKE:', '')
|
|
116
|
+
if (useAnd) {
|
|
117
|
+
query = query.andWhere(`${key} LIKE :${key}`, {
|
|
118
|
+
[key]: `%${likeValue}%`,
|
|
119
|
+
})
|
|
120
|
+
} else {
|
|
121
|
+
query = query.where(`${key} LIKE :${key}`, {
|
|
122
|
+
[key]: `%${likeValue}%`,
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
} else {
|
|
126
|
+
if (useAnd) {
|
|
127
|
+
query = query.andWhere(`${key} = :${key}`, { [key]: value })
|
|
128
|
+
} else {
|
|
129
|
+
query = query.where(`${key} = :${key}`, { [key]: value })
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Apply order
|
|
136
|
+
if (orderBy !== undefined && orderBy.length > 0) {
|
|
137
|
+
query = query.orderBy(orderBy[0].orderBy, orderBy[0].orderSort)
|
|
138
|
+
for (let i = 1; i < orderBy.length; i++) {
|
|
139
|
+
query = query.addOrderBy(orderBy[i].orderBy, orderBy[i].orderSort)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Apply pagination
|
|
144
|
+
if (pagination !== undefined) {
|
|
145
|
+
query = query.skip((pagination.page - 1) * pagination.pageSize)
|
|
146
|
+
query = query.take(pagination.pageSize)
|
|
147
|
+
|
|
148
|
+
// Calcula el total de páginas
|
|
149
|
+
pagination.totalPages = Math.ceil((await query.getCount()) / pagination.pageSize)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return query
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// Original code in Service
|
|
156
|
+
// Extraer: queryService.applyWhere(query, where): query
|
|
157
|
+
// if (where !== undefined) {
|
|
158
|
+
// for (const [key, value] of Object.entries(where)) {
|
|
159
|
+
// if (typeof value === 'string' && value.startsWith('LIKE:')) {
|
|
160
|
+
// const likeValue = value.replace('LIKE:', '');
|
|
161
|
+
// query = query.andWhere(`${key} LIKE :${key}`, {
|
|
162
|
+
// [key]: `%${likeValue}%`,
|
|
163
|
+
// });
|
|
164
|
+
// } else {
|
|
165
|
+
// query = query.andWhere(`${key} = :${key}`, { [key]: value });
|
|
166
|
+
// }
|
|
167
|
+
// }
|
|
168
|
+
// }
|
|
169
|
+
// Estraer: queryService.applyOrderAndPagination(query, orderBy, pagination): query
|
|
170
|
+
// if (orderBy !== undefined) {
|
|
171
|
+
// if (orderBy.length > 0) {
|
|
172
|
+
// query = query.orderBy(orderBy[0].orderBy, orderBy[0].orderSort);
|
|
173
|
+
// }
|
|
174
|
+
// for (let i = 1; i < orderBy.length; i++) {
|
|
175
|
+
// query = query.addOrderBy(orderBy[i].orderBy, orderBy[i].orderSort);
|
|
176
|
+
// }
|
|
177
|
+
// query = query.skip((pagination.page - 1) * pagination.pageSize);
|
|
178
|
+
// query = query.take(pagination.pageSize);
|
|
179
|
+
// }
|
|
180
|
+
// if (pagination !== undefined) {
|
|
181
|
+
// pagination.totalPages = Math.ceil(
|
|
182
|
+
// (await query.getCount()) / pagination.pageSize,
|
|
183
|
+
// );
|
|
184
|
+
// }
|
|
185
|
+
// query = applyWhere(query, where);
|
|
186
|
+
// query = await applyOrderAndPagination(query, orderBy, pagination);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import axios from 'axios'
|
|
2
|
+
import * as os from 'os'
|
|
3
|
+
import { Logger as NestLogger } from '@nestjs/common'
|
|
4
|
+
//import { ConfigService } from '../config'
|
|
5
|
+
import { ConfigService } from '../config/config.service'
|
|
6
|
+
|
|
7
|
+
// export interface LogstashPayload {
|
|
8
|
+
// level: string
|
|
9
|
+
// message: string
|
|
10
|
+
// timestamp?: string
|
|
11
|
+
// requestId?: string
|
|
12
|
+
// appName?: string
|
|
13
|
+
// appVersion?: string
|
|
14
|
+
// appPort?: number | string
|
|
15
|
+
// hostname?: string
|
|
16
|
+
// [key: string]: any
|
|
17
|
+
// }
|
|
18
|
+
|
|
19
|
+
export async function sendToLogstash(
|
|
20
|
+
configService: ConfigService,
|
|
21
|
+
logstashUrl: string | undefined,
|
|
22
|
+
level: string,
|
|
23
|
+
message: string,
|
|
24
|
+
meta?: any,
|
|
25
|
+
request?: any,
|
|
26
|
+
response?: any,
|
|
27
|
+
): Promise<void> {
|
|
28
|
+
try {
|
|
29
|
+
if (request === null) {
|
|
30
|
+
return
|
|
31
|
+
}
|
|
32
|
+
const responseHeaders = response?.getHeaderNames().reduce(
|
|
33
|
+
(acc, headerName) => {
|
|
34
|
+
acc[headerName] = response.getHeader(headerName)
|
|
35
|
+
return acc
|
|
36
|
+
},
|
|
37
|
+
{} as Record<string, any>,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if (request.requestId === '') {
|
|
41
|
+
console.log('Request ID is empty')
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const payload = {
|
|
45
|
+
level,
|
|
46
|
+
message,
|
|
47
|
+
timestamp: new Date().toISOString(),
|
|
48
|
+
requestId: request?.requestId ?? '',
|
|
49
|
+
appName: configService.get<string>('app.name') ?? '',
|
|
50
|
+
appVersion: configService.get<string>('app.version') ?? '',
|
|
51
|
+
appPort: configService.get<number>('app.port') ?? '',
|
|
52
|
+
hostname: os.hostname(),
|
|
53
|
+
method: request?.method ?? '', // Método HTTP (GET, POST, etc.)
|
|
54
|
+
url: request?.originalUrl ?? '', // URL original de la solicitud
|
|
55
|
+
ip: request?.ip ?? '', // Dirección IP del cliente
|
|
56
|
+
userAgent: request?.headers['user-agent'] ?? '', // Información del cliente
|
|
57
|
+
query: request?.query ?? {}, // Parámetros de consulta
|
|
58
|
+
requestBody: request?.body ?? {}, // Cuerpo de la solicitud (si no contiene datos sensibles)
|
|
59
|
+
requestHeaders: filterHeaders(request?.headers ?? {}),
|
|
60
|
+
statusCode: response?.statusCode ?? '', // Código de estado de la respuesta
|
|
61
|
+
responseHeaders: responseHeaders ?? {}, // Encabezados de la respuesta
|
|
62
|
+
responseTime: response?.responseTime ?? 0, // Tiempo de respuesta en milisegundos
|
|
63
|
+
...meta,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Implementar reintentos
|
|
67
|
+
if (logstashUrl) {
|
|
68
|
+
await axios.post(logstashUrl, payload)
|
|
69
|
+
}
|
|
70
|
+
} catch (error: unknown) {
|
|
71
|
+
const err = error as { code?: string; message?: string; stack?: string }
|
|
72
|
+
NestLogger.error(
|
|
73
|
+
`${err.message}\n${' '.repeat(61)}When trying to register: ${message}\nError trace: ${err.stack}`,
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function filterHeaders(headers: Record<string, any>): Record<string, any> {
|
|
79
|
+
const sensitiveHeaders = ['authorization', 'cookie', 'set-cookie']
|
|
80
|
+
|
|
81
|
+
return Object.keys(headers).reduce(
|
|
82
|
+
(filtered, key) => {
|
|
83
|
+
if (sensitiveHeaders.includes(key.toLowerCase())) {
|
|
84
|
+
const value = headers[key]
|
|
85
|
+
if (typeof value === 'string') {
|
|
86
|
+
// Enmascarar el valor sensible
|
|
87
|
+
filtered[key] =
|
|
88
|
+
value.length > 10
|
|
89
|
+
? `${value.slice(0, 10)}${'*'.repeat(value.length - 10)}`
|
|
90
|
+
: '*'.repeat(value.length)
|
|
91
|
+
} else {
|
|
92
|
+
// Si el valor no es una cadena, lo dejamos como está
|
|
93
|
+
filtered[key] = value
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
// Mantener los encabezados no sensibles sin cambios
|
|
97
|
+
filtered[key] = headers[key]
|
|
98
|
+
}
|
|
99
|
+
return filtered
|
|
100
|
+
},
|
|
101
|
+
{} as Record<string, any>,
|
|
102
|
+
)
|
|
103
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './main'
|