@jrosadob/ms-sophia-framework-mysql 0.0.1 → 0.0.2
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 +30 -42
- 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,35 @@
|
|
|
1
|
+
import { HttpException, HttpStatus, Logger } from '@nestjs/common';
|
|
2
|
+
// import { nanoid } from 'src/core/tools';
|
|
3
|
+
import { Result } from '../base/result';
|
|
4
|
+
import { nanoid } from '../../tools';
|
|
5
|
+
|
|
6
|
+
export class AppHttpException extends HttpException {
|
|
7
|
+
constructor(e: any, result: Result<any>) {
|
|
8
|
+
const loggerId = nanoid();
|
|
9
|
+
let status: HttpStatus;
|
|
10
|
+
let loggerMessage: string;
|
|
11
|
+
|
|
12
|
+
const errorType = e.constructor.name;
|
|
13
|
+
switch (errorType) {
|
|
14
|
+
case 'QueryFailedError':
|
|
15
|
+
status = HttpStatus.BAD_REQUEST;
|
|
16
|
+
super(result, status);
|
|
17
|
+
result.entity = undefined;
|
|
18
|
+
result.entities = undefined;
|
|
19
|
+
loggerMessage = `[${loggerId}] SqlState: ${e.driverError.sqlState} | QueryFailedError: ${e.driverError.sqlMessage}`;
|
|
20
|
+
Logger.log(loggerMessage, 'AppHttpException');
|
|
21
|
+
result.addError(loggerId, 400, 'Database error (QueryFailedError)');
|
|
22
|
+
break;
|
|
23
|
+
default:
|
|
24
|
+
status = HttpStatus.SERVICE_UNAVAILABLE;
|
|
25
|
+
super(result, status);
|
|
26
|
+
result.entity = undefined;
|
|
27
|
+
result.entities = undefined;
|
|
28
|
+
loggerMessage = `[${loggerId}] ${e.message}`;
|
|
29
|
+
Logger.log(loggerMessage, 'AppHttpException');
|
|
30
|
+
result.addError(loggerId, 503, 'Service unavailable');
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
// super(result, status)
|
|
34
|
+
}
|
|
35
|
+
}
|
package/libs/ms-sophia-framework-mysql/src/core/common/exceptions/app-unauthorized.exception.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { UnauthorizedException, Logger, HttpStatus } from '@nestjs/common';
|
|
2
|
+
// import { nanoid } from 'src/core/tools';
|
|
3
|
+
import { UnauthorizedResult } from '../result/unauthorized.result';
|
|
4
|
+
import { nanoid } from '../../tools';
|
|
5
|
+
|
|
6
|
+
const Logging = new Logger('AppUnauthorizedException');
|
|
7
|
+
|
|
8
|
+
export class AppUnauthorizedException extends UnauthorizedException {
|
|
9
|
+
constructor(message: string) {
|
|
10
|
+
super(message);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
getResponse(): UnauthorizedResult {
|
|
14
|
+
const response = super.getResponse();
|
|
15
|
+
const loggerId = nanoid();
|
|
16
|
+
if (typeof response === 'string') {
|
|
17
|
+
Logging.log(`[${loggerId}]: ${response}`);
|
|
18
|
+
return {
|
|
19
|
+
message: response,
|
|
20
|
+
error: 'Unauthorized',
|
|
21
|
+
statusCode: HttpStatus.UNAUTHORIZED,
|
|
22
|
+
loggerId: loggerId,
|
|
23
|
+
} as UnauthorizedResult;
|
|
24
|
+
}
|
|
25
|
+
// @ts-ignore: This line is to ignore the TypeScript error
|
|
26
|
+
Logging.log(`[${loggerId}]: ${response.message ?? response.error}`);
|
|
27
|
+
|
|
28
|
+
return {
|
|
29
|
+
error: 'Unauthorized',
|
|
30
|
+
// @ts-ignore: This line is to ignore the TypeScript error
|
|
31
|
+
message: response.message,
|
|
32
|
+
statusCode: HttpStatus.UNAUTHORIZED,
|
|
33
|
+
loggerId: loggerId,
|
|
34
|
+
} as UnauthorizedResult;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ExceptionFilter,
|
|
3
|
+
Catch,
|
|
4
|
+
ArgumentsHost,
|
|
5
|
+
HttpException,
|
|
6
|
+
HttpStatus,
|
|
7
|
+
Logger,
|
|
8
|
+
} from '@nestjs/common';
|
|
9
|
+
import { Response } from 'express';
|
|
10
|
+
import { nanoid } from '../../tools';
|
|
11
|
+
// import { nanoid } from 'src/core/tools';
|
|
12
|
+
|
|
13
|
+
@Catch()
|
|
14
|
+
export class AllExceptionsFilter implements ExceptionFilter {
|
|
15
|
+
catch(exception: unknown, host: ArgumentsHost) {
|
|
16
|
+
const ctx = host.switchToHttp();
|
|
17
|
+
const response = ctx.getResponse<Response>();
|
|
18
|
+
|
|
19
|
+
const status =
|
|
20
|
+
exception instanceof HttpException
|
|
21
|
+
? exception.getStatus()
|
|
22
|
+
: HttpStatus.INTERNAL_SERVER_ERROR;
|
|
23
|
+
|
|
24
|
+
const message =
|
|
25
|
+
exception instanceof HttpException ? exception.getResponse() : exception;
|
|
26
|
+
|
|
27
|
+
const loggerId = nanoid();
|
|
28
|
+
const loggerMessage = `[${loggerId}] ${message}`;
|
|
29
|
+
Logger.error({ message: loggerMessage, context: 'AllExceptionsFilter' });
|
|
30
|
+
|
|
31
|
+
response.status(status).json({
|
|
32
|
+
hasError: true,
|
|
33
|
+
errors: [
|
|
34
|
+
{
|
|
35
|
+
message: message,
|
|
36
|
+
statusCode: status,
|
|
37
|
+
loggerId: loggerId,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export * from './base/error';
|
|
2
|
+
export * from './base/result';
|
|
3
|
+
export * from './base/status-base'; // Reemplazado por BaseEntity
|
|
4
|
+
|
|
5
|
+
export * from './dto/change-status.dto';
|
|
6
|
+
export * from './dto/log.dto';
|
|
7
|
+
export * from './dto/order-by.dto';
|
|
8
|
+
export * from './dto/pagination.dto';
|
|
9
|
+
export * from './dto/search.dto';
|
|
10
|
+
export * from './dto/select.dto';
|
|
11
|
+
|
|
12
|
+
export * from './entity/base.entity';
|
|
13
|
+
|
|
14
|
+
export * from './exceptions/app-badrequest.exception';
|
|
15
|
+
export * from './exceptions/app-http.exception';
|
|
16
|
+
export * from './exceptions/app-unauthorized.exception';
|
|
17
|
+
|
|
18
|
+
export * from './filters/all-exception.filter';
|
|
19
|
+
|
|
20
|
+
export * from './pipes/app-validation.pipe';
|
|
21
|
+
|
|
22
|
+
export * from './result/bad-request.result';
|
|
23
|
+
export * from './result/property.result';
|
|
24
|
+
export * from './result/unauthorized.result';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ValidationPipe,
|
|
3
|
+
ValidationError,
|
|
4
|
+
BadRequestException,
|
|
5
|
+
HttpStatus,
|
|
6
|
+
ValidationPipeOptions,
|
|
7
|
+
} from '@nestjs/common';
|
|
8
|
+
import { BadRequestResult } from '../result/bad-request.result';
|
|
9
|
+
import { PropertyResult } from '../result/property.result';
|
|
10
|
+
import { nanoid } from '../../tools';
|
|
11
|
+
//import { nanoid } from 'src/core/tools';
|
|
12
|
+
|
|
13
|
+
export class AppValidationPipe extends ValidationPipe {
|
|
14
|
+
constructor(options?: ValidationPipeOptions) {
|
|
15
|
+
super(options);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
createExceptionFactory() {
|
|
19
|
+
return (validationErrors: ValidationError[] = []) => {
|
|
20
|
+
const loggerId = nanoid();
|
|
21
|
+
const customErrors = validationErrors.map((error) => ({
|
|
22
|
+
property: error.property,
|
|
23
|
+
message: Object.values(error.constraints || {}).join(', '),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
return new BadRequestException({
|
|
27
|
+
error: 'Bad Request',
|
|
28
|
+
message: 'Validation failed',
|
|
29
|
+
statusCode: HttpStatus.BAD_REQUEST,
|
|
30
|
+
errors: [...customErrors] as PropertyResult[],
|
|
31
|
+
loggerId: loggerId,
|
|
32
|
+
} as BadRequestResult);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ApiProperty } from '@nestjs/swagger'
|
|
2
|
+
import { PropertyResult } from './property.result'
|
|
3
|
+
|
|
4
|
+
export class BadRequestResult {
|
|
5
|
+
@ApiProperty({ description: 'Mensaje de error' })
|
|
6
|
+
message: string
|
|
7
|
+
@ApiProperty({
|
|
8
|
+
description: 'Nombre del error según StatusCode, p.e: BAD REQUEST, NOT FOUND, CREATED, etc',
|
|
9
|
+
})
|
|
10
|
+
error: string
|
|
11
|
+
@ApiProperty({ description: 'Código de error HttpStatusCode: 200, 201, 400, 401, etc.' })
|
|
12
|
+
statusCode: number
|
|
13
|
+
@ApiProperty({ description: 'loggerId del mensaje de error, para ubicarlo en el Logger' })
|
|
14
|
+
loggerId: string
|
|
15
|
+
@ApiProperty({ description: 'Array de errores de validación', type: [PropertyResult] })
|
|
16
|
+
errors: PropertyResult[] | null
|
|
17
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ApiProperty } from '@nestjs/swagger'
|
|
2
|
+
|
|
3
|
+
export class PropertyResult {
|
|
4
|
+
@ApiProperty({ description: 'Nombre de la property que no cumple con la validación' })
|
|
5
|
+
property: string
|
|
6
|
+
@ApiProperty({ description: 'Descripción de la validación que la property no cumple' })
|
|
7
|
+
message: string
|
|
8
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ApiProperty } from '@nestjs/swagger'
|
|
2
|
+
|
|
3
|
+
export class UnauthorizedResult {
|
|
4
|
+
@ApiProperty({ description: 'Mensaje de error' })
|
|
5
|
+
message: string
|
|
6
|
+
@ApiProperty({
|
|
7
|
+
description: 'Nombre del error según StatusCode, p.e: BAD REQUEST, NOT FOUND, CREATED, etc',
|
|
8
|
+
})
|
|
9
|
+
error: string
|
|
10
|
+
@ApiProperty({ description: 'Código de error HttpStatusCode: 200, 201, 400, 401, etc.' })
|
|
11
|
+
statusCode: number
|
|
12
|
+
@ApiProperty({ description: 'loggerId del mensaje de error, para ubicarlo en el Logger' })
|
|
13
|
+
loggerId: string
|
|
14
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Module, Global } from '@nestjs/common'
|
|
2
|
+
import { ConfigService } from './config.service'
|
|
3
|
+
import * as yaml from 'js-yaml'
|
|
4
|
+
import { readFileSync } from 'node:fs'
|
|
5
|
+
import { replaceEnvVars } from '../tools'
|
|
6
|
+
|
|
7
|
+
@Global()
|
|
8
|
+
@Module({
|
|
9
|
+
providers: [
|
|
10
|
+
{
|
|
11
|
+
provide: ConfigService,
|
|
12
|
+
useFactory: () => {
|
|
13
|
+
const configData = yaml.load(
|
|
14
|
+
replaceEnvVars(readFileSync('bootstrap.yml', 'utf8')),
|
|
15
|
+
) as Record<string, any>
|
|
16
|
+
return new ConfigService(configData)
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
],
|
|
20
|
+
exports: [ConfigService],
|
|
21
|
+
})
|
|
22
|
+
export class ConfigModule {}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Injectable } from '@nestjs/common'
|
|
2
|
+
import { Logger } from '../logger/logger'
|
|
3
|
+
|
|
4
|
+
@Injectable()
|
|
5
|
+
export class ConfigService {
|
|
6
|
+
private readonly config: Record<string, any>
|
|
7
|
+
|
|
8
|
+
constructor(config: Record<string, any>) {
|
|
9
|
+
this.config = config
|
|
10
|
+
this.getKey = this.getKey.bind(this)
|
|
11
|
+
this.get = this.get.bind(this)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
getKey<T>(key: string): T | undefined {
|
|
15
|
+
const keys = key.split('.')
|
|
16
|
+
let current: any = this.config
|
|
17
|
+
|
|
18
|
+
for (const k of keys) {
|
|
19
|
+
if (current && typeof current === 'object' && k in current) {
|
|
20
|
+
current = current[k]
|
|
21
|
+
} else {
|
|
22
|
+
return undefined
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return current as T | undefined
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get<T>(key: string): T | undefined {
|
|
30
|
+
const value = this.getKey<T>(key)
|
|
31
|
+
if (value === undefined) {
|
|
32
|
+
Logger.warn(`[ConfigService] Missing property: ${key}`)
|
|
33
|
+
return undefined
|
|
34
|
+
}
|
|
35
|
+
return value
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Global, Module } from '@nestjs/common'
|
|
2
|
+
import { databaseProviders } from './database.provider'
|
|
3
|
+
import { ConfigService } from '@nestjs/config'
|
|
4
|
+
import { ConfigModule } from '../config/config.module'
|
|
5
|
+
import { Logger } from '../logger/logger'
|
|
6
|
+
// import { ConfigService } from '@nestjs/config'
|
|
7
|
+
// import { LoggerHttpService } from 'src/logger/logger-http.service'
|
|
8
|
+
// import { LoggerModule } from 'src/logger/logger.module'
|
|
9
|
+
// import { LoggerService } from 'src/logger/logger.service'
|
|
10
|
+
// import { LoggerHttpService } from 'src/logger/logger-http.service'
|
|
11
|
+
|
|
12
|
+
@Global()
|
|
13
|
+
@Module({
|
|
14
|
+
imports: [ConfigModule],
|
|
15
|
+
providers: [ConfigService, ...databaseProviders, Logger],
|
|
16
|
+
exports: [...databaseProviders],
|
|
17
|
+
})
|
|
18
|
+
export class DatabaseModule {}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
//import { DatabaseType, DataSource, LoggerOptions } from 'typeorm'
|
|
2
|
+
import { DataSource, EntitySchema, MixedList } from 'typeorm';
|
|
3
|
+
import { Providers } from './providers';
|
|
4
|
+
import { ConfigModule } from '../config/config.module';
|
|
5
|
+
import { ConfigService } from '../config/config.service';
|
|
6
|
+
import { Logger } from '../logger/logger';
|
|
7
|
+
import { LoggerModule } from '../logger/logger.module';
|
|
8
|
+
import { TypeOrmLoggerService } from '../logger/typeorm-logger.service';
|
|
9
|
+
import { EntitySubscriber } from '../subscribers/entity.subscriber';
|
|
10
|
+
import { Logs } from '../subscribers/log.entity';
|
|
11
|
+
|
|
12
|
+
type MySqlDatabaseType = 'mysql' | 'mariadb';
|
|
13
|
+
export const databaseProviders = [
|
|
14
|
+
{
|
|
15
|
+
provide: Providers.DATA_SOURCE,
|
|
16
|
+
imports: [ConfigModule, LoggerModule],
|
|
17
|
+
inject: [ConfigService, Logger],
|
|
18
|
+
useFactory: async (configService: ConfigService, logger: Logger) => {
|
|
19
|
+
const includeLogs: boolean = getIncludeLogs(configService);
|
|
20
|
+
logger.log('Value process.cwd(): ' + process.cwd(), 'DatabaseProvider');
|
|
21
|
+
|
|
22
|
+
const entities: MixedList<string | Function | EntitySchema<any>> | undefined = [
|
|
23
|
+
process.cwd() + configService.get<string>('typeorm.mysql.entities'),
|
|
24
|
+
];
|
|
25
|
+
if (includeLogs) {
|
|
26
|
+
entities.push(Logs);
|
|
27
|
+
}
|
|
28
|
+
const subscribers = includeLogs ? [EntitySubscriber] : undefined;
|
|
29
|
+
const synchronize = configService.get<boolean>('typeorm.mysql.synchronize');
|
|
30
|
+
const logging = configService.get<boolean>('typeorm.mysql.logging') as boolean;
|
|
31
|
+
const typeOrmLogger = logging ? new TypeOrmLoggerService(logger, logging) : undefined;
|
|
32
|
+
|
|
33
|
+
const dataSource = new DataSource({
|
|
34
|
+
type: configService.get<string>(
|
|
35
|
+
'typeorm.mysql.type',
|
|
36
|
+
) as MySqlDatabaseType,
|
|
37
|
+
host: configService.get<string>('typeorm.mysql.host'),
|
|
38
|
+
port: configService.get<number>('typeorm.mysql.port'),
|
|
39
|
+
database: configService.get<string>('typeorm.mysql.database'),
|
|
40
|
+
username: configService.get<string>('typeorm.mysql.username'),
|
|
41
|
+
password: configService.get<string>('typeorm.mysql.password'),
|
|
42
|
+
// Se define las entities, subscribers, synchronize, logging, logger dinámicamente, mas arriba, según la configuración
|
|
43
|
+
// entities: [
|
|
44
|
+
// process.cwd() + configService.get<string>('typeorm.mysql.entities'),
|
|
45
|
+
// Logs
|
|
46
|
+
// ],
|
|
47
|
+
// subscribers: [EntitySubscriber],
|
|
48
|
+
// NOT USE. La sincronización elimina todas las columnas y las crea nuevas
|
|
49
|
+
// synchronize: configService.get<boolean>('typeorm.mysql.synchronize'),
|
|
50
|
+
entities,
|
|
51
|
+
subscribers,
|
|
52
|
+
synchronize,
|
|
53
|
+
logging,
|
|
54
|
+
logger: typeOrmLogger,
|
|
55
|
+
});
|
|
56
|
+
const data = await dataSource.initialize();
|
|
57
|
+
logger.log('Database connection established', 'DatabaseProvider');
|
|
58
|
+
//return dataSource.initialize()
|
|
59
|
+
return data;
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
function getIncludeLogs(configService: ConfigService): boolean {
|
|
65
|
+
const includeLogs = configService.get<boolean>('entityLogs.insertEnabled') ||
|
|
66
|
+
configService.get<boolean>('entityLogs.updateEnabled') ||
|
|
67
|
+
configService.get<boolean>('entityLogs.removeEnabled') ||
|
|
68
|
+
false;
|
|
69
|
+
return includeLogs
|
|
70
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Controller, Get, HttpException, HttpStatus, Inject } from '@nestjs/common'
|
|
2
|
+
import { ApiOperation, ApiResponse } from '@nestjs/swagger'
|
|
3
|
+
import { DataSource } from 'typeorm'
|
|
4
|
+
import { Providers } from '../database'
|
|
5
|
+
|
|
6
|
+
@Controller('health')
|
|
7
|
+
export class HealthController {
|
|
8
|
+
constructor(
|
|
9
|
+
@Inject(Providers.DATA_SOURCE)
|
|
10
|
+
private readonly dataSource: DataSource,
|
|
11
|
+
) {}
|
|
12
|
+
|
|
13
|
+
@Get('liveness')
|
|
14
|
+
@ApiOperation({ summary: 'Enpoint liveness for Kubernetes' })
|
|
15
|
+
@ApiResponse({ status: 200, description: 'Ok' })
|
|
16
|
+
@ApiResponse({ status: 500, description: 'Internal server error' })
|
|
17
|
+
liveness() {
|
|
18
|
+
return { status: 'ok' }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@Get('readiness')
|
|
22
|
+
@ApiOperation({ summary: 'Enpoint readiness for Kubernetes' })
|
|
23
|
+
@ApiResponse({ status: 200, description: 'Ok' })
|
|
24
|
+
@ApiResponse({ status: 503, description: 'Service unaivalable' })
|
|
25
|
+
async readiness() {
|
|
26
|
+
try {
|
|
27
|
+
await this.dataSource.query('SELECT 1')
|
|
28
|
+
return { status: 'ok' }
|
|
29
|
+
} catch (error) {
|
|
30
|
+
throw new HttpException(
|
|
31
|
+
{ status: 'error', message: 'Database connection failed' },
|
|
32
|
+
HttpStatus.SERVICE_UNAVAILABLE,
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common'
|
|
2
|
+
import { HealthController } from './health.controller'
|
|
3
|
+
import { Logger } from '../logger'
|
|
4
|
+
// import { ConfigModule } from './core/config/config.module'
|
|
5
|
+
// import { DatabaseModule } from './core/database/database.module'
|
|
6
|
+
// import { LoggerModule } from './core/logger/logger.module'
|
|
7
|
+
// import { Logger } from './core/logger/logger'
|
|
8
|
+
// import { HealthController } from './core/health/HealthController'
|
|
9
|
+
|
|
10
|
+
@Module({
|
|
11
|
+
imports: [],
|
|
12
|
+
controllers: [HealthController],
|
|
13
|
+
providers: [Logger],
|
|
14
|
+
exports: [Logger],
|
|
15
|
+
})
|
|
16
|
+
export class HealthModule {}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'
|
|
2
|
+
import { Observable } from 'rxjs'
|
|
3
|
+
|
|
4
|
+
@Injectable()
|
|
5
|
+
export class HeadersInterceptor implements NestInterceptor {
|
|
6
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
7
|
+
const request = context.switchToHttp().getRequest()
|
|
8
|
+
request.startTime = Date.now()
|
|
9
|
+
request.appName = request.headers['app-name'] ?? ''
|
|
10
|
+
request.scopes = request.headers['scopes'] ?? ''
|
|
11
|
+
|
|
12
|
+
return next.handle()
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
//Pasar todo esto al Loggin interceptor
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common'
|
|
2
|
+
import { Observable } from 'rxjs'
|
|
3
|
+
import { tap } from 'rxjs/operators'
|
|
4
|
+
import { LoggerRequest } from '../logger/logger.request'
|
|
5
|
+
import { ConfigService } from '../config'
|
|
6
|
+
import { nanoid } from '../tools/nanoid'
|
|
7
|
+
|
|
8
|
+
@Injectable()
|
|
9
|
+
export class LoggingInterceptor implements NestInterceptor {
|
|
10
|
+
//private readonly configService: ConfigService
|
|
11
|
+
constructor(private readonly configService: ConfigService) {
|
|
12
|
+
this.configService = configService
|
|
13
|
+
}
|
|
14
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
|
15
|
+
const request = context.switchToHttp().getRequest()
|
|
16
|
+
const response = context.switchToHttp().getResponse()
|
|
17
|
+
const { method, url } = request
|
|
18
|
+
const startTime = Date.now()
|
|
19
|
+
|
|
20
|
+
request.requestId = request.headers['request-id'] ?? nanoid()
|
|
21
|
+
|
|
22
|
+
response.setHeader('Request-ID', request.requestId)
|
|
23
|
+
response['responseTime'] = Date.now() - startTime
|
|
24
|
+
request.response = response
|
|
25
|
+
|
|
26
|
+
LoggerRequest.setConfigService(this.configService)
|
|
27
|
+
LoggerRequest.setResponse(response)
|
|
28
|
+
LoggerRequest.setRequest(request)
|
|
29
|
+
LoggerRequest.log(
|
|
30
|
+
`[${request.requestId}] Incoming Request: ${method} ${url}`,
|
|
31
|
+
'LoggingInterceptor',
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return next.handle().pipe(
|
|
35
|
+
tap((resp) => {
|
|
36
|
+
LoggerRequest.log(
|
|
37
|
+
`[${request.requestId}] Outgoing Response: ${method} ${url} - ${response['responseTime']}ms`,
|
|
38
|
+
'LoggingInterceptor',
|
|
39
|
+
)
|
|
40
|
+
}),
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Module } from '@nestjs/common'
|
|
2
|
+
import { ConfigModule } from '../config/config.module'
|
|
3
|
+
import { Logger, TypeOrmLoggerService } from '.'
|
|
4
|
+
|
|
5
|
+
@Module({
|
|
6
|
+
imports: [ConfigModule],
|
|
7
|
+
providers: [Logger, TypeOrmLoggerService],
|
|
8
|
+
exports: [Logger, TypeOrmLoggerService],
|
|
9
|
+
})
|
|
10
|
+
export class LoggerModule {}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ConsoleLogger,
|
|
3
|
+
Injectable,
|
|
4
|
+
Logger,
|
|
5
|
+
Logger as NestLogger,
|
|
6
|
+
LoggerService as NestLoggerService,
|
|
7
|
+
Scope,
|
|
8
|
+
} from '@nestjs/common'
|
|
9
|
+
import { ConfigService } from '../config/config.service'
|
|
10
|
+
import { sendToLogstash } from '../tools/send-to-logstash'
|
|
11
|
+
import { Request, Response } from 'express'
|
|
12
|
+
|
|
13
|
+
@Injectable({ scope: Scope.REQUEST })
|
|
14
|
+
export class LoggerRequest extends ConsoleLogger implements NestLoggerService {
|
|
15
|
+
private static logstashUrl: string | undefined
|
|
16
|
+
private static configService: ConfigService
|
|
17
|
+
private static request: Request
|
|
18
|
+
private static response: Response
|
|
19
|
+
|
|
20
|
+
constructor(private readonly configService: ConfigService) {
|
|
21
|
+
super()
|
|
22
|
+
LoggerRequest.configService = this.configService
|
|
23
|
+
LoggerRequest.logstashUrl = this.configService.get<string>('logger.http.host')
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
static setConfigService(configService: ConfigService) {
|
|
27
|
+
this.configService = configService
|
|
28
|
+
this.logstashUrl = this.configService.get<string>('logger.http.host')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static async setRequest(request: any): Promise<void> {
|
|
32
|
+
this.request = request
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
static async setResponse(response: any): Promise<void> {
|
|
36
|
+
this.response = response
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private static async sendToLogstash(level: string, message: string, meta?: any): Promise<void> {
|
|
40
|
+
await sendToLogstash(
|
|
41
|
+
this.configService,
|
|
42
|
+
this.logstashUrl,
|
|
43
|
+
level,
|
|
44
|
+
message,
|
|
45
|
+
meta,
|
|
46
|
+
this.request,
|
|
47
|
+
this.response,
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// _log(message: string): void
|
|
52
|
+
// _log(message: any, context: string): void
|
|
53
|
+
// _log(message: any, context?: string): void {
|
|
54
|
+
// if (context) {
|
|
55
|
+
// super.log('- ' + message, context)
|
|
56
|
+
// LoggerRequest.sendToLogstash('log', message, { context: context })
|
|
57
|
+
// } else {
|
|
58
|
+
// super.log('- ' + message)
|
|
59
|
+
// LoggerRequest.sendToLogstash('log', message)
|
|
60
|
+
// }
|
|
61
|
+
// }
|
|
62
|
+
|
|
63
|
+
warn(message: any)
|
|
64
|
+
warn(message: any, context: string)
|
|
65
|
+
warn(message: any, context?: string) {
|
|
66
|
+
if (context) {
|
|
67
|
+
super.warn('- ' + message, context)
|
|
68
|
+
LoggerRequest.sendToLogstash('warn', message, { context: context })
|
|
69
|
+
} else {
|
|
70
|
+
super.warn('- ' + message)
|
|
71
|
+
LoggerRequest.sendToLogstash('warn', message)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
error(message: string)
|
|
76
|
+
error(message: string, trace: string)
|
|
77
|
+
error(message: string, trace: string, meta: any)
|
|
78
|
+
error(message: string, trace?: string, meta?: any) {
|
|
79
|
+
if (meta) {
|
|
80
|
+
super.error('- ' + message, trace, meta)
|
|
81
|
+
LoggerRequest.sendToLogstash('error', message, { trace, meta })
|
|
82
|
+
} else if (trace) {
|
|
83
|
+
super.error('- ' + message, trace)
|
|
84
|
+
LoggerRequest.sendToLogstash('error', message, { trace })
|
|
85
|
+
} else {
|
|
86
|
+
super.error('- ' + message)
|
|
87
|
+
LoggerRequest.sendToLogstash('error', message)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
debug(message: string)
|
|
92
|
+
debug(message: any, context: string)
|
|
93
|
+
debug(message: any, context?: string) {
|
|
94
|
+
if (context) {
|
|
95
|
+
super.debug('- ' + message, context)
|
|
96
|
+
LoggerRequest.sendToLogstash('warn', message, { context: context })
|
|
97
|
+
} else {
|
|
98
|
+
super.debug('- ' + message)
|
|
99
|
+
LoggerRequest.sendToLogstash('warn', message)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
|
|
104
|
+
//#region Static methods
|
|
105
|
+
static async log(message: string, meta?: any) {
|
|
106
|
+
const logger = new Logger()
|
|
107
|
+
logger.log(message, meta)
|
|
108
|
+
await LoggerRequest.sendToLogstash('log', message, { meta: meta })
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
static async warn(message: string, meta?: any) {
|
|
112
|
+
NestLogger.warn(message, meta)
|
|
113
|
+
await LoggerRequest.sendToLogstash('warn', message, { meta: meta })
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static async error(message: string)
|
|
117
|
+
static async error(message: string, trace: string)
|
|
118
|
+
static async error(message: string, trace: string, meta: any)
|
|
119
|
+
static async error(message: string, trace?: string, meta?: any) {
|
|
120
|
+
if (meta) {
|
|
121
|
+
NestLogger.error(message, trace, meta)
|
|
122
|
+
// await LoggerRequest.sendToLogstash('error', message, { trace, meta })
|
|
123
|
+
} else if (trace) {
|
|
124
|
+
NestLogger.error(message, trace)
|
|
125
|
+
// await LoggerRequest.sendToLogstash('error', message, { trace })
|
|
126
|
+
} else {
|
|
127
|
+
NestLogger.error(message)
|
|
128
|
+
// await LoggerRequest.sendToLogstash('error', message)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
static async debug(message: string, meta?: any) {
|
|
133
|
+
NestLogger.debug(message, meta)
|
|
134
|
+
await LoggerRequest.sendToLogstash('debug', message, { meta: meta })
|
|
135
|
+
}
|
|
136
|
+
//#endregion
|
|
137
|
+
}
|