From cc6fe98e20df643ba3deabc0e467c9787e01ab22 Mon Sep 17 00:00:00 2001 From: Francesco Spilla Date: Fri, 7 Feb 2025 19:03:56 +0100 Subject: [PATCH] chore(repo) init AppFileTransactions module --- apps/ebitemp-api/src/app/app.controller.ts | 4 +- apps/ebitemp-api/src/app/app.module.ts | 9 +- apps/ebitemp-api/src/app/app.service.ts | 4 +- .../src/app/modules/config/config.module.ts | 8 +- .../file-transactions/file-transaction.ts | 137 ++++++++++++++++++ .../file-transactions.module.ts | 11 ++ .../file-transactions.service.ts | 54 +++++++ .../modules/file-transactions/file-utils.ts | 106 ++++++++++++++ .../src/app/modules/keyv/keyv.config.ts | 17 +++ .../src/app/modules/keyv/keyv.module.ts | 9 ++ .../src/app/modules/keyv/keyv.service.ts | 16 ++ docker-compose.yml | 11 ++ package.json | 4 + pnpm-lock.yaml | 119 ++++++++++++++- .../src/lib/__fileName__.service.ts.template | 2 +- 15 files changed, 500 insertions(+), 11 deletions(-) create mode 100644 apps/ebitemp-api/src/app/modules/file-transactions/file-transaction.ts create mode 100644 apps/ebitemp-api/src/app/modules/file-transactions/file-transactions.module.ts create mode 100644 apps/ebitemp-api/src/app/modules/file-transactions/file-transactions.service.ts create mode 100644 apps/ebitemp-api/src/app/modules/file-transactions/file-utils.ts create mode 100644 apps/ebitemp-api/src/app/modules/keyv/keyv.config.ts create mode 100644 apps/ebitemp-api/src/app/modules/keyv/keyv.module.ts create mode 100644 apps/ebitemp-api/src/app/modules/keyv/keyv.service.ts diff --git a/apps/ebitemp-api/src/app/app.controller.ts b/apps/ebitemp-api/src/app/app.controller.ts index aa4a3dd..8064b06 100644 --- a/apps/ebitemp-api/src/app/app.controller.ts +++ b/apps/ebitemp-api/src/app/app.controller.ts @@ -6,7 +6,7 @@ export class AppController { constructor(private readonly appService: AppService) {} @Get() - getData() { - return this.appService.getData(); + async getData() { + return await this.appService.getData(); } } diff --git a/apps/ebitemp-api/src/app/app.module.ts b/apps/ebitemp-api/src/app/app.module.ts index 416e34e..363f9f0 100644 --- a/apps/ebitemp-api/src/app/app.module.ts +++ b/apps/ebitemp-api/src/app/app.module.ts @@ -1,12 +1,19 @@ import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; + import { AppConfigModule } from './modules/config/config.module'; import { AppDatabaseModule } from './modules/database/database.module'; +import { AppFileTransactionsModule } from './modules/file-transactions/file-transactions.module'; import { AppLoggerModule } from './modules/logger/logger.module'; @Module({ - imports: [AppConfigModule, AppLoggerModule, AppDatabaseModule], + imports: [ + AppConfigModule, + AppLoggerModule, + AppDatabaseModule, + AppFileTransactionsModule, + ], controllers: [AppController], providers: [AppService], }) diff --git a/apps/ebitemp-api/src/app/app.service.ts b/apps/ebitemp-api/src/app/app.service.ts index b474014..a0ea348 100644 --- a/apps/ebitemp-api/src/app/app.service.ts +++ b/apps/ebitemp-api/src/app/app.service.ts @@ -2,9 +2,9 @@ import { Injectable, Logger } from '@nestjs/common'; @Injectable() export class AppService { - logger = new Logger(AppService.name); + private readonly logger = new Logger(AppService.name); - getData(): { message: string } { + async getData() { return ({ message: 'Hello API' }); } } diff --git a/apps/ebitemp-api/src/app/modules/config/config.module.ts b/apps/ebitemp-api/src/app/modules/config/config.module.ts index 0cacf02..2160bde 100644 --- a/apps/ebitemp-api/src/app/modules/config/config.module.ts +++ b/apps/ebitemp-api/src/app/modules/config/config.module.ts @@ -1,16 +1,16 @@ - import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; -import { localConfig } from './local.config'; -import { loggerConfig } from '../logger/logger.config'; import { databaseConfig } from '../database/database.config'; +import { keyvConfig } from '../keyv/keyv.config'; +import { loggerConfig } from '../logger/logger.config'; +import { localConfig } from './local.config'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, expandVariables: true, - load: [localConfig, loggerConfig, databaseConfig], + load: [localConfig, loggerConfig, databaseConfig, keyvConfig], }), ], }) diff --git a/apps/ebitemp-api/src/app/modules/file-transactions/file-transaction.ts b/apps/ebitemp-api/src/app/modules/file-transactions/file-transaction.ts new file mode 100644 index 0000000..2c55258 --- /dev/null +++ b/apps/ebitemp-api/src/app/modules/file-transactions/file-transaction.ts @@ -0,0 +1,137 @@ +import { Logger } from '@nestjs/common'; +import fs from 'node:fs'; +import { randomUUID } from 'node:crypto'; +import path from 'node:path'; +import { FileTransactionsService } from './file-transactions.service'; +import { + copyFilesToFolderOrFail, + deleteFiles, + renameFiles, + renameFilesOrFail, +} from './file-utils'; +import dayjs from 'dayjs'; + +export class FileTransaction { + public static readonly TO_ADD_SUFFIX = 'toAdd'; + public static readonly TO_DELETE_SUFFIX = 'toDelete'; + + private readonly manager: FileTransactionsService; + private readonly logger: Logger; + + public readonly transactionId: string; + private readonly ts: Date; + public readonly filesToAdd: { src: string; dest: string }[]; + public readonly filesToDelete: string[]; + + constructor( + manager: FileTransactionsService, + opts: Partial<{ + transactionId: string; + filesToAdd: { src: string; dest: string }[]; + filesToDelete: string[]; + logger: Logger; + }> + ) { + this.manager = manager; + this.logger = opts.logger ?? new Logger(FileTransaction.name); + this.filesToAdd = opts.filesToAdd ?? []; + this.filesToDelete = opts.filesToDelete ?? []; + this.transactionId = opts.transactionId ?? randomUUID().split('-').pop() as string; + this.ts = new Date(); + } + + async addFile(src: string, dest: string) { + const destFolderName = path.dirname(dest); + const destFileName = path.basename(dest); + const destFileNameWithSuffix = `${destFileName}.${this.transactionId}.${FileTransaction.TO_ADD_SUFFIX}`; + const destFilePathWithSuffix = path.join( + destFolderName, + destFileNameWithSuffix + ); + + this.logger.log( + `Adding file '${destFilePathWithSuffix}' in fileTransaction '${ + this.transactionId + }' (started at ${dayjs(this.ts).format('YYYY-MM-DD HH:mm:ss')})` + ); + this.filesToAdd.push({ src: src, dest: destFilePathWithSuffix }); + + if (fs.existsSync(dest)) { + await this.deleteFile(dest); + } + + await copyFilesToFolderOrFail(this.logger, destFolderName, { + srcpath: src, + name: destFileNameWithSuffix, + }); + await deleteFiles(this.logger, src); + this.manager.persist(this); + } + + async deleteFile(src: string) { + const destFolderName = path.dirname(src); + const destFileName = path.basename(src); + const destFileNameWithSuffix = `${destFileName}.${this.transactionId}.${FileTransaction.TO_DELETE_SUFFIX}`; + const destFilePathWithSuffix = path.join( + destFolderName, + destFileNameWithSuffix + ); + + this.logger.log( + `Deleting file '${destFilePathWithSuffix}' in fileTransaction '${ + this.transactionId + }' (started at ${dayjs(this.ts).format('YYYY-MM-DD HH:mm:ss')})` + ); + this.filesToDelete.push(destFilePathWithSuffix); + await renameFilesOrFail(this.logger, { src, dest: destFilePathWithSuffix }); + this.manager.persist(this); + } + + async commitTransaction() { + this.logger.log( + `Committing fileTransaction '${this.transactionId}' (started at ${dayjs( + this.ts + ).format('YYYY-MM-DD HH:mm:ss')})` + ); + await renameFiles( + this.logger, + ...this.filesToAdd.map((fileToAdd) => ({ + src: fileToAdd.dest, + dest: fileToAdd.dest.replace( + `.${this.transactionId}.${FileTransaction.TO_ADD_SUFFIX}`, + '' + ), + })) + ); + await deleteFiles(this.logger, ...this.filesToDelete); + this.manager.unregister(this); + } + + async rollbackTransaction() { + this.logger.log( + `Rollbacking fileTransaction '${this.transactionId}' (started at ${dayjs( + this.ts + ).format('YYYY-MM-DD HH:mm:ss')})` + ); + + await renameFiles( + this.logger, + ...this.filesToAdd.map((fileToMove) => ({ + src: fileToMove.dest, + dest: fileToMove.src, + })) + ); + + await renameFiles( + this.logger, + ...this.filesToDelete.map((fileToRestore) => ({ + src: fileToRestore, + dest: fileToRestore.replace( + `.${this.transactionId}.${FileTransaction.TO_DELETE_SUFFIX}`, + '' + ), + })) + ); + this.manager.unregister(this); + } +} diff --git a/apps/ebitemp-api/src/app/modules/file-transactions/file-transactions.module.ts b/apps/ebitemp-api/src/app/modules/file-transactions/file-transactions.module.ts new file mode 100644 index 0000000..8d4c27c --- /dev/null +++ b/apps/ebitemp-api/src/app/modules/file-transactions/file-transactions.module.ts @@ -0,0 +1,11 @@ +import { Global, Module } from '@nestjs/common'; +import { FileTransactionsService } from './file-transactions.service'; +import { KeyvModule } from '../keyv/keyv.module'; + +@Global() +@Module({ + imports: [KeyvModule], + providers: [FileTransactionsService], + exports: [FileTransactionsService], +}) +export class AppFileTransactionsModule {} diff --git a/apps/ebitemp-api/src/app/modules/file-transactions/file-transactions.service.ts b/apps/ebitemp-api/src/app/modules/file-transactions/file-transactions.service.ts new file mode 100644 index 0000000..7ead12b --- /dev/null +++ b/apps/ebitemp-api/src/app/modules/file-transactions/file-transactions.service.ts @@ -0,0 +1,54 @@ +import { Injectable, Logger, OnModuleInit } from '@nestjs/common'; +import { FileTransaction } from './file-transaction'; +import { createKeyv, Keyv } from '@keyv/redis'; +import { parse, stringify } from 'flatted'; +import { KeyvService } from '../keyv/keyv.service'; + +const FILE_TRANSACTIONS_KEY = `FILE_TRANSACTIONS_KEY`; + +@Injectable() +export class FileTransactionsService implements OnModuleInit { + private readonly logger = new Logger(FileTransactionsService.name); + private readonly map: Record = {}; + + private readonly keyv: Keyv; + + constructor(readonly keyvService: KeyvService) { + this.keyv = this.keyvService.create({ + namespace: FileTransactionsService.name, + }); + } + + register(logger?: Logger) { + const tx = new FileTransaction(this, { logger }); + this.persist(tx); + return tx; + } + + persist(tx: FileTransaction) { + this.map[tx.transactionId] = tx; + this.keyv.set(FILE_TRANSACTIONS_KEY, this.map); + } + + unregister(tx: FileTransaction) { + delete this.map[tx.transactionId]; + this.keyv.set(FILE_TRANSACTIONS_KEY, this.map); + } + + async onModuleInit() { + this.logger.log('Checking for dangling file transactions...'); + const dangling = await this.keyv.get>( + FILE_TRANSACTIONS_KEY + ); + + if (dangling) { + for (const danglingTx of Object.values(dangling)) { + const tx = new FileTransaction(this, { + ...danglingTx, + logger: this.logger, + }); + await tx.rollbackTransaction(); + } + } + } +} diff --git a/apps/ebitemp-api/src/app/modules/file-transactions/file-utils.ts b/apps/ebitemp-api/src/app/modules/file-transactions/file-utils.ts new file mode 100644 index 0000000..1b15eb4 --- /dev/null +++ b/apps/ebitemp-api/src/app/modules/file-transactions/file-utils.ts @@ -0,0 +1,106 @@ +import { Logger } from '@nestjs/common'; +import { promises as fs } from 'node:fs'; +import path from 'path'; + +interface File { + srcpath: string; + name: string; +} + +export const copyFilesToFolder = (logger: Logger, destFolder: string, ...files: File[]) => + _copyFilesToFolder(logger, destFolder, false, ...files); +export const copyFilesToFolderOrFail = (logger: Logger, destFolder: string, ...files: File[]) => + _copyFilesToFolder(logger, destFolder, true, ...files); +export const deleteFolder = (logger: Logger, folder: string) => _deleteFolder(logger, false, folder); +export const deleteFolderOrFail = (logger: Logger, folder: string) => _deleteFolder(logger, true, folder); +export const deleteFiles = (logger: Logger, ...files: string[]) => _deleteFiles(logger, false, ...files); +export const deleteFilesOrFail = (logger: Logger, ...files: string[]) => _deleteFiles(logger, true, ...files); +export const renameFiles = (logger: Logger, ...files: Array<{ src: string; dest: string }>) => + _renameFiles(logger, false, ...files); +export const renameFilesOrFail = (logger: Logger, ...files: Array<{ src: string; dest: string }>) => + _renameFiles(logger, true, ...files); + +export const normalizeWin32PathToPosix = (filepath: string): string => + path.win32.normalize(filepath).split(path.win32.sep).join(path.posix.sep); + +const _copyFilesToFolder = async ( + logger: Logger, + destFolder: string, + throwsOnError = false, + ...files: File[] +): Promise => { + await fs.mkdir(destFolder, { recursive: true }); + + for (const file of files) { + try { + const destpath = path.join(destFolder, file.name); + await fs.copyFile(file.srcpath, destpath); + logger.debug(`Copying file '${file.srcpath}' to '${destpath}'`); + } catch (err: any) { + const msg = `Could not copy '${file.srcpath}' to '${destFolder}'. Inner exception: ${err.message}`; + if (throwsOnError) { + logger.error(msg); + err.message = msg; + throw err; + } + logger.warn(msg); + } + } +}; + +const _deleteFolder = async (logger: Logger, throwsOnError = false, folder: string): Promise => { + try { + await fs.rmdir(folder, { recursive: true }); + logger.debug(`Deleting folder '${folder}'`); + } catch (err: any) { + if (err.code === 'ENOENT') { + return; + } + const msg = `Could not delete '${folder}'. Inner exception: ${err.message}`; + if (throwsOnError) { + logger.error(msg); + throw err; + } + logger.warn(msg); + } +}; + +const _deleteFiles = async (logger: Logger, throwsOnError = false, ...files: string[]): Promise => { + for (const file of files) { + try { + await fs.unlink(file); + logger.debug(`Deleting file '${file}'`); + } catch (err: any) { + if (err.code === 'ENOENT') { + return; + } + const msg = `Could not delete '${file}'. Inner exception: ${err.message}`; + if (throwsOnError) { + logger.error(msg); + throw err; + } + logger.warn(msg); + } + } +}; + +const _renameFiles = async ( + logger: Logger, + throwsOnError = false, + ...files: Array<{ src: string; dest: string }> +): Promise => { + for (const file of files) { + try { + await fs.rename(file.src, file.dest); + logger.debug(`Renaming file '${file.src}' to '${file.dest}'`); + } catch (err: any) { + const msg = `Could not rename '${file.src}' to '${file.dest}'. Inner exception: ${err.message}`; + if (throwsOnError) { + logger.error(msg); + err.message = msg; + throw err; + } + logger.warn(msg); + } + } +}; diff --git a/apps/ebitemp-api/src/app/modules/keyv/keyv.config.ts b/apps/ebitemp-api/src/app/modules/keyv/keyv.config.ts new file mode 100644 index 0000000..3b58958 --- /dev/null +++ b/apps/ebitemp-api/src/app/modules/keyv/keyv.config.ts @@ -0,0 +1,17 @@ +import coerceRecordTypes from '../config/utils/coerce-record-types'; +import { registerAs } from '@nestjs/config'; +import { z } from 'zod'; + +export const keyvSchema = z.object({ + redis: z.string(), +}); +export type KeyvConfig = z.infer; + +export const keyvConfig = registerAs('keyv', () => { + const env = coerceRecordTypes(process.env); + + const config: KeyvConfig = keyvSchema.strict().parse({ + redis: env['REDIS_CONNECTION_STRING'], + }); + return config; +}); diff --git a/apps/ebitemp-api/src/app/modules/keyv/keyv.module.ts b/apps/ebitemp-api/src/app/modules/keyv/keyv.module.ts new file mode 100644 index 0000000..e308b72 --- /dev/null +++ b/apps/ebitemp-api/src/app/modules/keyv/keyv.module.ts @@ -0,0 +1,9 @@ +import { Global, Module } from '@nestjs/common'; +import { KeyvService } from './keyv.service'; + +@Module({ + imports: [], + providers: [KeyvService], + exports: [KeyvService], +}) +export class KeyvModule {} diff --git a/apps/ebitemp-api/src/app/modules/keyv/keyv.service.ts b/apps/ebitemp-api/src/app/modules/keyv/keyv.service.ts new file mode 100644 index 0000000..c6d6987 --- /dev/null +++ b/apps/ebitemp-api/src/app/modules/keyv/keyv.service.ts @@ -0,0 +1,16 @@ +import { createKeyv, KeyvRedisOptions } from '@keyv/redis'; +import { Inject, Injectable } from '@nestjs/common'; +import { parse, stringify } from 'flatted'; +import { keyvConfig, KeyvConfig } from './keyv.config'; + +@Injectable() +export class KeyvService { + constructor(@Inject(keyvConfig.KEY) private readonly config: KeyvConfig) {} + + create(options?: KeyvRedisOptions) { + const keyv = createKeyv(this.config.redis, options); + keyv.serialize = stringify; + keyv.deserialize = parse; + return keyv; + } +} diff --git a/docker-compose.yml b/docker-compose.yml index f75ecf6..105abe4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -15,3 +15,14 @@ services: memory: 4096M reservations: memory: 2048M + redis: + image: redis:alpine + ports: + - '${REDIS_PORT}:6379' + restart: unless-stopped + deploy: + resources: + limits: + memory: 4096M + reservations: + memory: 2048M diff --git a/package.json b/package.json index ef3e4fe..9297a66 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "10": "^0.0.1", "@autotelic/pino-seq-transport": "^0.1.0", + "@keyv/redis": "^4.2.0", "@nestjs/common": "^10.0.2", "@nestjs/config": "^4.0.0", "@nestjs/core": "^10.0.2", @@ -19,7 +20,10 @@ "@nestjs/typeorm": "^11.0.0", "@nx/devkit": "20.4.1", "axios": "^1.6.0", + "cacheable": "^1.8.8", "connection-string": "^4.4.0", + "dayjs": "^1.11.13", + "flatted": "^3.3.2", "lodash": "^4.17.21", "nestjs-pino": "^4.3.0", "patch-package": "^8.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e7993b9..37087d3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ dependencies: '@autotelic/pino-seq-transport': specifier: ^0.1.0 version: 0.1.0 + '@keyv/redis': + specifier: ^4.2.0 + version: 4.2.0 '@nestjs/common': specifier: ^10.0.2 version: 10.4.15(reflect-metadata@0.1.14)(rxjs@7.8.1) @@ -32,9 +35,18 @@ dependencies: axios: specifier: ^1.6.0 version: 1.7.9 + cacheable: + specifier: ^1.8.8 + version: 1.8.8 connection-string: specifier: ^4.4.0 version: 4.4.0 + dayjs: + specifier: ^1.11.13 + version: 1.11.13 + flatted: + specifier: ^3.3.2 + version: 3.3.2 lodash: specifier: ^4.17.21 version: 4.17.21 @@ -2124,6 +2136,21 @@ packages: tslib: 2.8.1 dev: true + /@keyv/redis@4.2.0: + resolution: {integrity: sha512-QszmBfZZ3wOKJ5z1hn0CTLf04WN/552ITrSDYC3Yg4jT6yVdlz2fJxi5CNrnZ8NIu/Qaj7OAkbSL+pyFUXp6oA==} + engines: {node: '>= 18'} + dependencies: + cluster-key-slot: 1.1.2 + keyv: 5.2.3 + redis: 4.7.0 + dev: false + + /@keyv/serialize@1.0.2: + resolution: {integrity: sha512-+E/LyaAeuABniD/RvUezWVXKpeuvwLEA9//nE9952zBaOdBd2mQ3pPoM8cUe2X6IcMByfuSLzmYqnYshG60+HQ==} + dependencies: + buffer: 6.0.3 + dev: false + /@leichtgewicht/ip-codec@2.0.5: resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} dev: true @@ -3081,6 +3108,55 @@ packages: requiresBuild: true optional: true + /@redis/bloom@1.2.0(@redis/client@1.6.0): + resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.6.0 + dev: false + + /@redis/client@1.6.0: + resolution: {integrity: sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==} + engines: {node: '>=14'} + dependencies: + cluster-key-slot: 1.1.2 + generic-pool: 3.9.0 + yallist: 4.0.0 + dev: false + + /@redis/graph@1.1.1(@redis/client@1.6.0): + resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.6.0 + dev: false + + /@redis/json@1.0.7(@redis/client@1.6.0): + resolution: {integrity: sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.6.0 + dev: false + + /@redis/search@1.2.0(@redis/client@1.6.0): + resolution: {integrity: sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.6.0 + dev: false + + /@redis/time-series@1.1.0(@redis/client@1.6.0): + resolution: {integrity: sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==} + peerDependencies: + '@redis/client': ^1.0.0 + dependencies: + '@redis/client': 1.6.0 + dev: false + /@sinclair/typebox@0.27.8: resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -4455,6 +4531,13 @@ packages: responselike: 2.0.1 dev: true + /cacheable@1.8.8: + resolution: {integrity: sha512-OE1/jlarWxROUIpd0qGBSKFLkNsotY8pt4GeiVErUYh/NUeTNrT+SBksUgllQv4m6a0W/VZsLuiHb88maavqEw==} + dependencies: + hookified: 1.7.0 + keyv: 5.2.3 + dev: false + /call-bind-apply-helpers@1.0.1: resolution: {integrity: sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==} engines: {node: '>= 0.4'} @@ -4660,6 +4743,11 @@ packages: semver: 5.7.2 dev: false + /cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + dev: false + /co@4.6.0: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} @@ -5922,7 +6010,6 @@ packages: /flatted@3.3.2: resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==} - dev: true /follow-redirects@1.15.9: resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} @@ -6031,6 +6118,11 @@ packages: /function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + /generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + dev: false + /gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -6244,6 +6336,10 @@ packages: /highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} + /hookified@1.7.0: + resolution: {integrity: sha512-XQdMjqC1AyeOzfs+17cnIk7Wdfu1hh2JtcyNfBf5u9jHrT3iZUlGHxLTntFBuk5lwkqJ6l3+daeQdHK5yByHVA==} + dev: false + /hosted-git-info@7.0.2: resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} engines: {node: ^16.14.0 || >=18.0.0} @@ -7293,6 +7389,12 @@ packages: json-buffer: 3.0.1 dev: true + /keyv@5.2.3: + resolution: {integrity: sha512-AGKecUfzrowabUv0bH1RIR5Vf7w+l4S3xtQAypKaUpTdIR1EbrAcTxHCrpo9Q+IWeUlFE2palRtgIQcgm+PQJw==} + dependencies: + '@keyv/serialize': 1.0.2 + dev: false + /kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -8968,6 +9070,17 @@ packages: resolve: 1.22.10 dev: true + /redis@4.7.0: + resolution: {integrity: sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==} + dependencies: + '@redis/bloom': 1.2.0(@redis/client@1.6.0) + '@redis/client': 1.6.0 + '@redis/graph': 1.1.1(@redis/client@1.6.0) + '@redis/json': 1.0.7(@redis/client@1.6.0) + '@redis/search': 1.2.0(@redis/client@1.6.0) + '@redis/time-series': 1.1.0(@redis/client@1.6.0) + dev: false + /reflect-metadata@0.1.14: resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==} @@ -10751,6 +10864,10 @@ packages: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} dev: true + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: false + /yaml@1.10.2: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} diff --git a/tools/nest/src/generators/library/files/common/src/lib/__fileName__.service.ts.template b/tools/nest/src/generators/library/files/common/src/lib/__fileName__.service.ts.template index 3955814..4fdf316 100644 --- a/tools/nest/src/generators/library/files/common/src/lib/__fileName__.service.ts.template +++ b/tools/nest/src/generators/library/files/common/src/lib/__fileName__.service.ts.template @@ -2,5 +2,5 @@ import { Injectable, Logger } from '@nestjs/common'; @Injectable() export class <%= className %>Service { - protected readonly logger = new Logger(<%= className %>Service.name); + private readonly logger = new Logger(<%= className %>Service.name); }