@nest-omni/core 3.1.1-15 → 3.1.1-17

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.
@@ -53,7 +53,7 @@ let CacheModule = CacheModule_1 = class CacheModule {
53
53
  },
54
54
  cache_service_1.CacheService,
55
55
  ],
56
- exports: [cache_service_1.CacheService, exports.CACHE_SERVICE],
56
+ exports: [exports.CACHE_SERVICE, cache_service_1.CacheService],
57
57
  };
58
58
  }
59
59
  static forRootAsync(options) {
@@ -88,9 +88,13 @@ let CacheModule = CacheModule_1 = class CacheModule {
88
88
  inject: ['CACHE_MODULE_OPTIONS'],
89
89
  },
90
90
  providers_1.RedisCacheProvider,
91
+ {
92
+ provide: exports.CACHE_SERVICE,
93
+ useClass: cache_service_1.CacheService,
94
+ },
91
95
  cache_service_1.CacheService,
92
96
  ],
93
- exports: [cache_service_1.CacheService],
97
+ exports: [exports.CACHE_SERVICE, cache_service_1.CacheService],
94
98
  };
95
99
  }
96
100
  onModuleInit() {
@@ -1,11 +1,13 @@
1
1
  import type { AbstractDto, AbstractTranslationDto } from './dto/abstract.dto';
2
2
  import { LanguageCode } from '../constants';
3
- export declare abstract class AbstractBaseEntity<DTO, O> {
4
- createdAt: Date;
5
- updatedAt: Date;
3
+ export declare abstract class AbstractDtoEntity<DTO, O = never> {
6
4
  translations?: AbstractTranslationEntity[];
7
5
  toDto(options?: O): DTO;
8
6
  }
7
+ export declare abstract class AbstractBaseEntity<DTO, O> extends AbstractDtoEntity<DTO, O> {
8
+ createdAt: Date;
9
+ updatedAt: Date;
10
+ }
9
11
  export declare class AbstractEntity<DTO extends AbstractDto = AbstractDto, O = never> extends AbstractBaseEntity<DTO, O> {
10
12
  id: number;
11
13
  }
@@ -9,12 +9,12 @@ var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.AbstractUuidPrimaryEntity = exports.AbstractTranslationEntity = exports.AbstractEntity = exports.AbstractBaseEntity = void 0;
12
+ exports.AbstractUuidPrimaryEntity = exports.AbstractTranslationEntity = exports.AbstractEntity = exports.AbstractBaseEntity = exports.AbstractDtoEntity = void 0;
13
13
  const typeorm_1 = require("typeorm");
14
14
  const decorators_1 = require("../decorators");
15
15
  const constants_1 = require("../constants");
16
16
  const uuid_1 = require("uuid");
17
- class AbstractBaseEntity {
17
+ class AbstractDtoEntity {
18
18
  toDto(options) {
19
19
  const dtoClass = Object.getPrototypeOf(this).dtoClass;
20
20
  if (!dtoClass) {
@@ -23,6 +23,9 @@ class AbstractBaseEntity {
23
23
  return new dtoClass(this, options);
24
24
  }
25
25
  }
26
+ exports.AbstractDtoEntity = AbstractDtoEntity;
27
+ class AbstractBaseEntity extends AbstractDtoEntity {
28
+ }
26
29
  exports.AbstractBaseEntity = AbstractBaseEntity;
27
30
  __decorate([
28
31
  (0, typeorm_1.CreateDateColumn)({
@@ -31,6 +31,12 @@ declare module 'typeorm' {
31
31
  takeAll: boolean;
32
32
  skipCount: boolean;
33
33
  }>): Promise<[Entity[], PageMetaDto]>;
34
+ paginateAndMap<Dto extends AbstractDto, DtoOptions = unknown>(this: SelectQueryBuilder<Entity>, pageOptionsDto: PageOptionsDto, options?: Partial<{
35
+ dtoOptions: DtoOptions;
36
+ skipCount: boolean;
37
+ takeAll: boolean;
38
+ transform: (items: Entity[]) => Entity[];
39
+ }>): Promise<PageDto<Dto>>;
34
40
  leftJoinAndSelect<AliasEntity extends AbstractEntity, A extends string>(this: SelectQueryBuilder<Entity>, property: `${A}.${Exclude<KeyOfType<AliasEntity, AbstractEntity>, symbol>}`, alias: string, condition?: string, parameters?: ObjectLiteral): this;
35
41
  leftJoin<AliasEntity extends AbstractEntity, A extends string>(this: SelectQueryBuilder<Entity>, property: `${A}.${Exclude<KeyOfType<AliasEntity, AbstractEntity>, symbol>}`, alias: string, condition?: string, parameters?: ObjectLiteral): this;
36
42
  innerJoinAndSelect<AliasEntity extends AbstractEntity, A extends string>(this: SelectQueryBuilder<Entity>, property: `${A}.${Exclude<KeyOfType<AliasEntity, AbstractEntity>, symbol>}`, alias: string, condition?: string, parameters?: ObjectLiteral): this;
@@ -29,7 +29,12 @@ const page_dto_1 = require("./dto/page.dto");
29
29
  const page_meta_dto_1 = require("./dto/page-meta.dto");
30
30
  const providers_1 = require("../providers");
31
31
  Array.prototype.toDtos = function (options) {
32
- return (0, lodash_1.compact)((0, lodash_1.map)(this, (item) => item.toDto(options)));
32
+ return (0, lodash_1.compact)((0, lodash_1.map)(this, (item) => {
33
+ if (item && typeof item.toDto === 'function') {
34
+ return item.toDto(options);
35
+ }
36
+ return undefined;
37
+ }));
33
38
  };
34
39
  Array.prototype.getByLanguage = function (languageCode) {
35
40
  return this.find((translation) => languageCode === translation.languageCode)
@@ -72,6 +77,19 @@ typeorm_1.SelectQueryBuilder.prototype.paginate = function (pageOptionsDto, opti
72
77
  return [entities, pageMetaDto];
73
78
  });
74
79
  };
80
+ typeorm_1.SelectQueryBuilder.prototype.paginateAndMap = function (pageOptionsDto, options) {
81
+ return __awaiter(this, void 0, void 0, function* () {
82
+ const [entities, pageMetaDto] = yield this.paginate(pageOptionsDto, {
83
+ skipCount: options === null || options === void 0 ? void 0 : options.skipCount,
84
+ takeAll: options === null || options === void 0 ? void 0 : options.takeAll,
85
+ });
86
+ let transformedEntities = entities;
87
+ if (options === null || options === void 0 ? void 0 : options.transform) {
88
+ transformedEntities = options.transform(entities);
89
+ }
90
+ return transformedEntities.toPageDto(pageMetaDto, options === null || options === void 0 ? void 0 : options.dtoOptions);
91
+ });
92
+ };
75
93
  typeorm_1.SelectQueryBuilder.prototype.withTenant = function (tenantId, tenantFieldName = 'tenantId') {
76
94
  var _a;
77
95
  if (!tenantId) {
package/index.d.ts CHANGED
@@ -12,5 +12,6 @@ export * from './validator-json';
12
12
  export * from './helpers';
13
13
  export * from './providers';
14
14
  export * from './cache';
15
+ export * from './vault';
15
16
  export * from './setup';
16
17
  export * from './health-checker';
package/index.js CHANGED
@@ -28,5 +28,6 @@ __exportStar(require("./validator-json"), exports);
28
28
  __exportStar(require("./helpers"), exports);
29
29
  __exportStar(require("./providers"), exports);
30
30
  __exportStar(require("./cache"), exports);
31
+ __exportStar(require("./vault"), exports);
31
32
  __exportStar(require("./setup"), exports);
32
33
  __exportStar(require("./health-checker"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nest-omni/core",
3
- "version": "3.1.1-15",
3
+ "version": "3.1.1-17",
4
4
  "description": "A comprehensive NestJS framework for building enterprise-grade applications with best practices",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -41,116 +41,62 @@
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/compression": "^1.8.1",
44
- "@types/express": "^5.0.3",
44
+ "@types/express": "^5.0.4",
45
45
  "@types/express-session": "^1.18.2",
46
46
  "@types/lodash": "^4.17.20",
47
47
  "@types/node": "^24.9.1",
48
48
  "@types/sprintf-js": "^1.1.4",
49
- "@types/uuid": "^10.0.0",
49
+ "@types/uuid": "^11.0.0",
50
50
  "typescript": "^5.9.3"
51
51
  },
52
52
  "dependencies": {
53
+ "@dataui/crud": "^5.3.4",
54
+ "@dataui/crud-typeorm": "^5.3.4",
53
55
  "@nestjs-cls/transactional": "^3.1.0",
54
56
  "@nestjs-cls/transactional-adapter-typeorm": "^1.3.0",
55
- "@sentry/nestjs": "^10.21.0",
56
- "@sentry/profiling-node": "^10.21.0",
57
+ "@nestjs/bull": "^11.0.4",
58
+ "@nestjs/common": "^11.1.7",
59
+ "@nestjs/config": "^4.0.2",
60
+ "@nestjs/core": "^11.1.7",
61
+ "@nestjs/platform-express": "^11.1.7",
62
+ "@nestjs/schedule": "^6.0.1",
63
+ "@nestjs/swagger": "^11.2.1",
64
+ "@nestjs/terminus": "^11.0.0",
65
+ "@nestjs/typeorm": "^11.0.0",
66
+ "@sentry/nestjs": "^10.22.0",
67
+ "@sentry/profiling-node": "^10.22.0",
68
+ "@songkeys/nestjs-redis": "^11.0.0",
69
+ "@songkeys/nestjs-redis-health": "^11.0.0",
57
70
  "axios": "^1.12.2",
58
71
  "axios-retry": "^4.5.0",
72
+ "bcrypt": "^6.0.0",
73
+ "body-parser": "^2.2.0",
74
+ "bull": "^4.16.5",
59
75
  "class-transformer": "^0.5.1",
60
76
  "class-validator": "^0.14.2",
61
77
  "compression": "^1.8.1",
78
+ "connect-redis": "^9.0.0",
79
+ "dotenv": "^17.2.3",
80
+ "express": "^5.1.0",
81
+ "express-session": "^1.18.2",
62
82
  "hygen": "^6.2.11",
63
- "libphonenumber-js": "^1.12.24",
83
+ "ioredis": "^5.8.2",
84
+ "libphonenumber-js": "^1.12.25",
64
85
  "lodash": "^4.17.21",
86
+ "mime-types": "^3.0.1",
65
87
  "moment": "^2.30.1",
88
+ "mysql2": "^3.15.3",
89
+ "nestjs-cls": "^6.0.1",
90
+ "nestjs-i18n": "^10.5.1",
91
+ "nestjs-pino": "^4.4.1",
92
+ "pino-http": "^11.0.0",
93
+ "pino-pretty": "^13.1.2",
66
94
  "reflect-metadata": "^0.2.2",
67
95
  "rxjs": "^7.8.2",
68
- "sprintf-js": "^1.1.3",
69
- "uuid": "^13.0.0"
70
- },
71
- "peerDependencies": {
72
- "@dataui/crud": "^5.3.0",
73
- "@dataui/crud-typeorm": "^5.3.0",
74
- "@nestjs/bull": "^11.0.0",
75
- "@nestjs/common": "^11.0.0",
76
- "@nestjs/config": "^4.0.0",
77
- "@nestjs/core": "^11.0.0",
78
- "@nestjs/platform-express": "^11.0.0",
79
- "@nestjs/schedule": "^6.0.0",
80
- "@nestjs/swagger": "^11.0.0",
81
- "@nestjs/terminus": "^11.0.0",
82
- "@nestjs/typeorm": "^11.0.0",
83
- "@songkeys/nestjs-redis": "^11.0.0",
84
- "@songkeys/nestjs-redis-health": "^11.0.0",
85
- "bcrypt": "^6.0.0",
86
- "bull": "^4.16.0",
87
- "connect-redis": "^9.0.0",
88
- "express": "^5.0.0",
89
- "express-session": "^1.18.0",
90
- "mime-types": "^3.0.0",
91
- "mysql2": "^3.0.0",
92
- "nestjs-cls": "^6.0.0",
93
- "nestjs-i18n": "^10.5.0",
94
- "nestjs-pino": "^4.0.0",
95
- "pino-http": "^11.0.0",
96
- "pino-pretty": "^13.0.0",
97
96
  "source-map-support": "^0.5.21",
98
- "typeorm": "^0.3.20"
99
- },
100
- "peerDependenciesMeta": {
101
- "@dataui/crud": {
102
- "optional": false
103
- },
104
- "@dataui/crud-typeorm": {
105
- "optional": false
106
- },
107
- "@songkeys/nestjs-redis": {
108
- "optional": true
109
- },
110
- "@songkeys/nestjs-redis-health": {
111
- "optional": true
112
- },
113
- "@nestjs/bull": {
114
- "optional": false
115
- },
116
- "@nestjs/schedule": {
117
- "optional": false
118
- },
119
- "@nestjs/swagger": {
120
- "optional": true
121
- },
122
- "bcrypt": {
123
- "optional": false
124
- },
125
- "bull": {
126
- "optional": false
127
- },
128
- "connect-redis": {
129
- "optional": false
130
- },
131
- "express-session": {
132
- "optional": false
133
- },
134
- "mysql2": {
135
- "optional": false
136
- },
137
- "nestjs-cls": {
138
- "optional": false
139
- },
140
- "nestjs-i18n": {
141
- "optional": false
142
- },
143
- "nestjs-pino": {
144
- "optional": false
145
- },
146
- "pino-http": {
147
- "optional": false
148
- },
149
- "pino-pretty": {
150
- "optional": false
151
- },
152
- "typeorm": {
153
- "optional": false
154
- }
97
+ "sprintf-js": "^1.1.3",
98
+ "typeorm": "^0.3.27",
99
+ "uuid": "^13.0.0",
100
+ "node-vault": "^0.10.9"
155
101
  }
156
102
  }
@@ -1,2 +1,3 @@
1
+ import '../common/boilerplate.polyfill';
1
2
  import type { NestExpressApplication } from '@nestjs/platform-express';
2
3
  export declare function bootstrapSetup(AppModule: any, SetupSwagger: any): Promise<NestExpressApplication<import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>>>;
@@ -17,6 +17,7 @@ const fs_1 = require("fs");
17
17
  const path_1 = require("path");
18
18
  const process = require("process");
19
19
  const mode_setup_1 = require("./mode.setup");
20
+ require("../common/boilerplate.polyfill");
20
21
  function findValidRootPath() {
21
22
  const getAppRootPath = () => {
22
23
  if (require.main && require.main.filename) {
@@ -79,17 +80,6 @@ Sentry.init({
79
80
  integrations: [profiling_node_1.nodeProfilingIntegration],
80
81
  });
81
82
  const crud_1 = require("@dataui/crud");
82
- const core_1 = require("@nestjs/core");
83
- const nestjs_pino_1 = require("nestjs-pino");
84
- const session = require("express-session");
85
- const bodyParse = require("body-parser");
86
- const compression = require("compression");
87
- const __1 = require("../");
88
- const common_1 = require("@nestjs/common");
89
- const nestjs_i18n_1 = require("nestjs-i18n");
90
- const nestjs_cls_1 = require("nestjs-cls");
91
- const class_validator_1 = require("class-validator");
92
- const setup_1 = require("@sentry/nestjs/setup");
93
83
  crud_1.CrudConfigService.load({
94
84
  auth: {
95
85
  property: 'user',
@@ -117,8 +107,21 @@ crud_1.CrudConfigService.load({
117
107
  },
118
108
  },
119
109
  });
110
+ const core_1 = require("@nestjs/core");
111
+ const nestjs_pino_1 = require("nestjs-pino");
112
+ const session = require("express-session");
113
+ const bodyParse = require("body-parser");
114
+ const compression = require("compression");
115
+ const __1 = require("../");
116
+ const common_1 = require("@nestjs/common");
117
+ const nestjs_i18n_1 = require("nestjs-i18n");
118
+ const nestjs_cls_1 = require("nestjs-cls");
119
+ const class_validator_1 = require("class-validator");
120
+ const setup_1 = require("@sentry/nestjs/setup");
121
+ const vault_1 = require("../vault");
120
122
  function bootstrapSetup(AppModule, SetupSwagger) {
121
123
  return __awaiter(this, void 0, void 0, function* () {
124
+ yield vault_1.VaultConfigLoader.loadVaultConfig();
122
125
  const shouldStartHttp = (0, mode_setup_1.shouldStartHttpServer)();
123
126
  const app = yield core_1.NestFactory.create(AppModule, {
124
127
  bufferLogs: true,
@@ -36,6 +36,7 @@ const services_1 = require("./services");
36
36
  const nestjs_cls_1 = require("nestjs-cls");
37
37
  const redis_lock_1 = require("../redis-lock");
38
38
  const typeorm_2 = require("typeorm");
39
+ const vault_1 = require("../vault");
39
40
  const providers = [
40
41
  services_1.ApiConfigService,
41
42
  services_1.ValidatorService,
@@ -48,6 +49,7 @@ if (!((_a = process === null || process === void 0 ? void 0 : process.env) === n
48
49
  services_1.ApiConfigService.rootPath = process.env.ROOT_PATH;
49
50
  const modules = [
50
51
  setup_1.SentryModule.forRoot(),
52
+ vault_1.VaultModule,
51
53
  config_1.ConfigModule.forRoot({
52
54
  isGlobal: true,
53
55
  cache: true,
@@ -107,7 +109,6 @@ if (services_1.ApiConfigService.toBoolean(process.env.BULL_ENABLED)) {
107
109
  }
108
110
  if (services_1.ApiConfigService.toBoolean(process.env.CACHE_ENABLED)) {
109
111
  modules.push(cache_1.CacheModule.forRootAsync({
110
- imports: [nestjs_redis_1.RedisModule],
111
112
  inject: [services_1.ApiConfigService, nestjs_redis_1.RedisService, typeorm_2.DataSource],
112
113
  isGlobal: true,
113
114
  useFactory: (config, redisService, dataSource) => __awaiter(void 0, void 0, void 0, function* () {
@@ -131,7 +132,6 @@ if (services_1.ApiConfigService.toBoolean(process.env.CACHE_ENABLED)) {
131
132
  if (services_1.ApiConfigService.toBoolean(process.env.SCHEDULE_ENABLED) &&
132
133
  services_1.ApiConfigService.toBoolean(process.env.REDIS_LOCK_ENABLED, true)) {
133
134
  modules.push(redis_lock_1.RedisLockModule.forRootAsync({
134
- imports: [nestjs_redis_1.RedisModule],
135
135
  inject: [services_1.ApiConfigService, nestjs_redis_1.RedisService],
136
136
  isGlobal: true,
137
137
  useFactory: (config, redisService) => __awaiter(void 0, void 0, void 0, function* () {
@@ -0,0 +1,4 @@
1
+ export * from './vault-config.loader';
2
+ export * from './vault-config.service';
3
+ export * from './vault.module';
4
+ export * from './interfaces/vault-options.interface';
package/vault/index.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./vault-config.loader"), exports);
18
+ __exportStar(require("./vault-config.service"), exports);
19
+ __exportStar(require("./vault.module"), exports);
20
+ __exportStar(require("./interfaces/vault-options.interface"), exports);
@@ -0,0 +1,22 @@
1
+ export interface VaultAuthOptions {
2
+ token?: string;
3
+ roleId?: string;
4
+ secretId?: string;
5
+ k8sRole?: string;
6
+ k8sServiceAccountToken?: string;
7
+ }
8
+ export interface VaultOptions {
9
+ endpoint: string;
10
+ auth: VaultAuthOptions;
11
+ secretPaths: string[];
12
+ namespace?: string;
13
+ apiVersion?: 'v1' | 'v2';
14
+ timeout?: number;
15
+ enableHotReload?: boolean;
16
+ hotReloadInterval?: number;
17
+ failOnError?: boolean;
18
+ }
19
+ export interface VaultConfig {
20
+ enabled: boolean;
21
+ options?: VaultOptions;
22
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,13 @@
1
+ export declare class VaultConfigLoader {
2
+ private static readonly logger;
3
+ private static vaultClient;
4
+ private static hotReloadTimer;
5
+ private static buildVaultOptions;
6
+ private static initializeVaultClient;
7
+ private static readSecrets;
8
+ static loadVaultConfig(): Promise<void>;
9
+ private static startHotReload;
10
+ static stopHotReload(): void;
11
+ static getVaultClient(): any;
12
+ static isConnected(): boolean;
13
+ }
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.VaultConfigLoader = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ class VaultConfigLoader {
15
+ static buildVaultOptions() {
16
+ var _a;
17
+ const enabled = process.env.VAULT_ENABLED === 'true';
18
+ if (!enabled) {
19
+ this.logger.log('Vault is disabled (VAULT_ENABLED != true)');
20
+ return null;
21
+ }
22
+ const endpoint = process.env.VAULT_ENDPOINT;
23
+ const secretPaths = ((_a = process.env.VAULT_SECRET_PATHS) === null || _a === void 0 ? void 0 : _a.split(',').map(p => p.trim())) || [];
24
+ if (!endpoint) {
25
+ this.logger.warn('Vault enabled but VAULT_ENDPOINT is not set');
26
+ return null;
27
+ }
28
+ if (secretPaths.length === 0) {
29
+ this.logger.warn('Vault enabled but VAULT_SECRET_PATHS is empty');
30
+ return null;
31
+ }
32
+ const auth = {};
33
+ if (process.env.VAULT_TOKEN) {
34
+ auth.token = process.env.VAULT_TOKEN;
35
+ this.logger.log('Using Vault Token authentication');
36
+ }
37
+ else if (process.env.VAULT_ROLE_ID && process.env.VAULT_SECRET_ID) {
38
+ auth.roleId = process.env.VAULT_ROLE_ID;
39
+ auth.secretId = process.env.VAULT_SECRET_ID;
40
+ this.logger.log('Using Vault AppRole authentication');
41
+ }
42
+ else if (process.env.VAULT_K8S_ROLE) {
43
+ auth.k8sRole = process.env.VAULT_K8S_ROLE;
44
+ auth.k8sServiceAccountToken = process.env.VAULT_K8S_SA_TOKEN;
45
+ this.logger.log('Using Vault Kubernetes authentication');
46
+ }
47
+ else {
48
+ this.logger.warn('Vault enabled but no valid authentication method configured');
49
+ return null;
50
+ }
51
+ const options = {
52
+ endpoint,
53
+ auth,
54
+ secretPaths,
55
+ namespace: process.env.VAULT_NAMESPACE,
56
+ apiVersion: process.env.VAULT_API_VERSION || 'v2',
57
+ timeout: parseInt(process.env.VAULT_TIMEOUT || '5000', 10),
58
+ enableHotReload: process.env.VAULT_HOT_RELOAD === 'true',
59
+ hotReloadInterval: parseInt(process.env.VAULT_HOT_RELOAD_INTERVAL || '300', 10),
60
+ failOnError: process.env.VAULT_FAIL_ON_ERROR !== 'false',
61
+ };
62
+ this.logger.log(`Vault configuration loaded: ${secretPaths.length} secret path(s)`);
63
+ return options;
64
+ }
65
+ static initializeVaultClient(options) {
66
+ return __awaiter(this, void 0, void 0, function* () {
67
+ let vault;
68
+ try {
69
+ vault = yield Promise.resolve().then(() => require('node-vault'));
70
+ vault = vault.default || vault;
71
+ }
72
+ catch (error) {
73
+ throw new Error('node-vault package not installed. Run: npm install node-vault');
74
+ }
75
+ const client = vault({
76
+ endpoint: options.endpoint,
77
+ namespace: options.namespace,
78
+ requestOptions: {
79
+ timeout: options.timeout,
80
+ },
81
+ });
82
+ try {
83
+ if (options.auth.token) {
84
+ client.token = options.auth.token;
85
+ this.logger.log('Vault Token authentication configured');
86
+ }
87
+ else if (options.auth.roleId && options.auth.secretId) {
88
+ const result = yield client.approleLogin({
89
+ role_id: options.auth.roleId,
90
+ secret_id: options.auth.secretId,
91
+ });
92
+ client.token = result.auth.client_token;
93
+ this.logger.log('Vault AppRole authentication successful');
94
+ }
95
+ else if (options.auth.k8sRole) {
96
+ let jwt = options.auth.k8sServiceAccountToken;
97
+ if (!jwt) {
98
+ const fs = yield Promise.resolve().then(() => require('fs'));
99
+ const K8S_SA_TOKEN_PATH = '/var/run/secrets/kubernetes.io/serviceaccount/token';
100
+ if (fs.existsSync(K8S_SA_TOKEN_PATH)) {
101
+ jwt = fs.readFileSync(K8S_SA_TOKEN_PATH, 'utf8');
102
+ }
103
+ else {
104
+ throw new Error('Kubernetes service account token not found');
105
+ }
106
+ }
107
+ const result = yield client.kubernetesLogin({
108
+ role: options.auth.k8sRole,
109
+ jwt,
110
+ });
111
+ client.token = result.auth.client_token;
112
+ this.logger.log('Vault Kubernetes authentication successful');
113
+ }
114
+ else {
115
+ throw new Error('No valid Vault authentication method configured');
116
+ }
117
+ yield client.tokenLookupSelf();
118
+ this.logger.log('Vault authentication verified');
119
+ return client;
120
+ }
121
+ catch (error) {
122
+ this.logger.error('Vault authentication failed', error);
123
+ throw error;
124
+ }
125
+ });
126
+ }
127
+ static readSecrets(client, secretPath, apiVersion) {
128
+ return __awaiter(this, void 0, void 0, function* () {
129
+ try {
130
+ this.logger.debug(`Reading secrets from: ${secretPath}`);
131
+ const result = yield client.read(secretPath);
132
+ if (!result || !result.data) {
133
+ this.logger.warn(`No data found at path: ${secretPath}`);
134
+ return {};
135
+ }
136
+ const secrets = apiVersion === 'v2' ? result.data.data : result.data;
137
+ if (!secrets || typeof secrets !== 'object') {
138
+ this.logger.warn(`Invalid secret format at path: ${secretPath}`);
139
+ return {};
140
+ }
141
+ const secretCount = Object.keys(secrets).length;
142
+ this.logger.log(`Loaded ${secretCount} secret(s) from ${secretPath}`);
143
+ return secrets;
144
+ }
145
+ catch (error) {
146
+ this.logger.error(`Failed to read secrets from path: ${secretPath}`, error.message);
147
+ return {};
148
+ }
149
+ });
150
+ }
151
+ static loadVaultConfig() {
152
+ return __awaiter(this, void 0, void 0, function* () {
153
+ const options = this.buildVaultOptions();
154
+ if (!options) {
155
+ this.logger.log('Skipping Vault configuration loading');
156
+ return;
157
+ }
158
+ try {
159
+ this.logger.log(`Connecting to Vault at ${options.endpoint}`);
160
+ this.vaultClient = yield this.initializeVaultClient(options);
161
+ const allSecrets = {};
162
+ for (const secretPath of options.secretPaths) {
163
+ const secrets = yield this.readSecrets(this.vaultClient, secretPath, options.apiVersion);
164
+ Object.assign(allSecrets, secrets);
165
+ }
166
+ let injectedCount = 0;
167
+ let overriddenCount = 0;
168
+ for (const [key, value] of Object.entries(allSecrets)) {
169
+ const hadPreviousValue = key in process.env;
170
+ process.env[key] = String(value);
171
+ if (hadPreviousValue) {
172
+ overriddenCount++;
173
+ this.logger.debug(`Override ${key} with Vault value`);
174
+ }
175
+ else {
176
+ injectedCount++;
177
+ }
178
+ }
179
+ this.logger.log(`Vault configuration loaded: ${injectedCount} new, ${overriddenCount} overridden`);
180
+ if (options.enableHotReload) {
181
+ this.startHotReload(options);
182
+ }
183
+ }
184
+ catch (error) {
185
+ this.logger.error('Failed to load Vault configuration', error.message);
186
+ if (options.failOnError) {
187
+ this.logger.error('Application startup failed due to Vault error');
188
+ throw error;
189
+ }
190
+ else {
191
+ this.logger.warn('Continuing without Vault configuration (VAULT_FAIL_ON_ERROR=false)');
192
+ }
193
+ }
194
+ });
195
+ }
196
+ static startHotReload(options) {
197
+ this.logger.log(`Starting Vault hot reload with interval: ${options.hotReloadInterval}s`);
198
+ if (this.hotReloadTimer) {
199
+ clearInterval(this.hotReloadTimer);
200
+ }
201
+ this.hotReloadTimer = setInterval(() => __awaiter(this, void 0, void 0, function* () {
202
+ try {
203
+ this.logger.debug('Reloading Vault configuration...');
204
+ const allSecrets = {};
205
+ for (const secretPath of options.secretPaths) {
206
+ const secrets = yield this.readSecrets(this.vaultClient, secretPath, options.apiVersion);
207
+ Object.assign(allSecrets, secrets);
208
+ }
209
+ let changedCount = 0;
210
+ for (const [key, value] of Object.entries(allSecrets)) {
211
+ const newValue = String(value);
212
+ if (process.env[key] !== newValue) {
213
+ this.logger.log(`Configuration changed: ${key}`);
214
+ process.env[key] = newValue;
215
+ changedCount++;
216
+ }
217
+ }
218
+ if (changedCount > 0) {
219
+ this.logger.log(`Hot reload completed: ${changedCount} configuration(s) updated`);
220
+ }
221
+ else {
222
+ this.logger.debug('Hot reload completed: no changes detected');
223
+ }
224
+ }
225
+ catch (error) {
226
+ this.logger.error('Failed to reload Vault configuration', error.message);
227
+ }
228
+ }), options.hotReloadInterval * 1000);
229
+ }
230
+ static stopHotReload() {
231
+ if (this.hotReloadTimer) {
232
+ clearInterval(this.hotReloadTimer);
233
+ this.hotReloadTimer = null;
234
+ this.logger.log('Vault hot reload stopped');
235
+ }
236
+ }
237
+ static getVaultClient() {
238
+ return this.vaultClient;
239
+ }
240
+ static isConnected() {
241
+ return this.vaultClient !== null && this.vaultClient !== undefined;
242
+ }
243
+ }
244
+ exports.VaultConfigLoader = VaultConfigLoader;
245
+ VaultConfigLoader.logger = new common_1.Logger(VaultConfigLoader.name);
246
+ VaultConfigLoader.hotReloadTimer = null;
@@ -0,0 +1,18 @@
1
+ export declare class VaultConfigService {
2
+ private readonly logger;
3
+ private vaultClient;
4
+ constructor();
5
+ isAvailable(): boolean;
6
+ getSecret(path: string, key?: string): Promise<any>;
7
+ setSecret(path: string, data: Record<string, any>): Promise<void>;
8
+ deleteSecret(path: string): Promise<void>;
9
+ listSecrets(path: string): Promise<string[]>;
10
+ healthCheck(): Promise<boolean>;
11
+ getStatus(): Promise<{
12
+ initialized: boolean;
13
+ sealed: boolean;
14
+ standby: boolean;
15
+ version: string;
16
+ } | null>;
17
+ renewToken(increment?: number): Promise<void>;
18
+ }
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
12
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
13
+ return new (P || (P = Promise))(function (resolve, reject) {
14
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
15
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
16
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
17
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
18
+ });
19
+ };
20
+ var VaultConfigService_1;
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.VaultConfigService = void 0;
23
+ const common_1 = require("@nestjs/common");
24
+ const vault_config_loader_1 = require("./vault-config.loader");
25
+ let VaultConfigService = VaultConfigService_1 = class VaultConfigService {
26
+ constructor() {
27
+ this.logger = new common_1.Logger(VaultConfigService_1.name);
28
+ this.vaultClient = vault_config_loader_1.VaultConfigLoader.getVaultClient();
29
+ if (!this.vaultClient) {
30
+ this.logger.warn('Vault client not initialized. VaultConfigService methods will throw errors.');
31
+ }
32
+ }
33
+ isAvailable() {
34
+ return vault_config_loader_1.VaultConfigLoader.isConnected();
35
+ }
36
+ getSecret(path, key) {
37
+ return __awaiter(this, void 0, void 0, function* () {
38
+ if (!this.vaultClient) {
39
+ throw new Error('Vault client not initialized. Check VAULT_ENABLED configuration.');
40
+ }
41
+ try {
42
+ const apiVersion = process.env.VAULT_API_VERSION || 'v2';
43
+ const result = yield this.vaultClient.read(path);
44
+ if (!result || !result.data) {
45
+ throw new Error(`No data found at path: ${path}`);
46
+ }
47
+ const data = apiVersion === 'v2' ? result.data.data : result.data;
48
+ if (!data || typeof data !== 'object') {
49
+ throw new Error(`Invalid secret format at path: ${path}`);
50
+ }
51
+ if (key) {
52
+ if (!(key in data)) {
53
+ throw new Error(`Key '${key}' not found in secret at path: ${path}`);
54
+ }
55
+ return data[key];
56
+ }
57
+ return data;
58
+ }
59
+ catch (error) {
60
+ this.logger.error(`Failed to read secret from Vault: ${path}`, error.message);
61
+ throw error;
62
+ }
63
+ });
64
+ }
65
+ setSecret(path, data) {
66
+ return __awaiter(this, void 0, void 0, function* () {
67
+ if (!this.vaultClient) {
68
+ throw new Error('Vault client not initialized. Check VAULT_ENABLED configuration.');
69
+ }
70
+ try {
71
+ const apiVersion = process.env.VAULT_API_VERSION || 'v2';
72
+ if (apiVersion === 'v2') {
73
+ yield this.vaultClient.write(path, { data });
74
+ }
75
+ else {
76
+ yield this.vaultClient.write(path, data);
77
+ }
78
+ this.logger.log(`Successfully wrote secret to Vault: ${path}`);
79
+ }
80
+ catch (error) {
81
+ this.logger.error(`Failed to write secret to Vault: ${path}`, error.message);
82
+ throw error;
83
+ }
84
+ });
85
+ }
86
+ deleteSecret(path) {
87
+ return __awaiter(this, void 0, void 0, function* () {
88
+ if (!this.vaultClient) {
89
+ throw new Error('Vault client not initialized. Check VAULT_ENABLED configuration.');
90
+ }
91
+ try {
92
+ yield this.vaultClient.delete(path);
93
+ this.logger.log(`Successfully deleted secret from Vault: ${path}`);
94
+ }
95
+ catch (error) {
96
+ this.logger.error(`Failed to delete secret from Vault: ${path}`, error.message);
97
+ throw error;
98
+ }
99
+ });
100
+ }
101
+ listSecrets(path) {
102
+ return __awaiter(this, void 0, void 0, function* () {
103
+ if (!this.vaultClient) {
104
+ throw new Error('Vault client not initialized. Check VAULT_ENABLED configuration.');
105
+ }
106
+ try {
107
+ const result = yield this.vaultClient.list(path);
108
+ if (!result || !result.data || !result.data.keys) {
109
+ return [];
110
+ }
111
+ return result.data.keys;
112
+ }
113
+ catch (error) {
114
+ this.logger.error(`Failed to list secrets from Vault: ${path}`, error.message);
115
+ throw error;
116
+ }
117
+ });
118
+ }
119
+ healthCheck() {
120
+ return __awaiter(this, void 0, void 0, function* () {
121
+ if (!this.vaultClient) {
122
+ return false;
123
+ }
124
+ try {
125
+ const health = yield this.vaultClient.health();
126
+ return health && !health.sealed;
127
+ }
128
+ catch (error) {
129
+ this.logger.error('Vault health check failed', error.message);
130
+ return false;
131
+ }
132
+ });
133
+ }
134
+ getStatus() {
135
+ return __awaiter(this, void 0, void 0, function* () {
136
+ if (!this.vaultClient) {
137
+ return null;
138
+ }
139
+ try {
140
+ const health = yield this.vaultClient.health();
141
+ return {
142
+ initialized: health.initialized || false,
143
+ sealed: health.sealed || false,
144
+ standby: health.standby || false,
145
+ version: health.version || 'unknown',
146
+ };
147
+ }
148
+ catch (error) {
149
+ this.logger.error('Failed to get Vault status', error.message);
150
+ return null;
151
+ }
152
+ });
153
+ }
154
+ renewToken(increment) {
155
+ return __awaiter(this, void 0, void 0, function* () {
156
+ if (!this.vaultClient) {
157
+ throw new Error('Vault client not initialized. Check VAULT_ENABLED configuration.');
158
+ }
159
+ try {
160
+ yield this.vaultClient.tokenRenewSelf({ increment });
161
+ this.logger.log('Vault token renewed successfully');
162
+ }
163
+ catch (error) {
164
+ this.logger.error('Failed to renew Vault token', error.message);
165
+ throw error;
166
+ }
167
+ });
168
+ }
169
+ };
170
+ exports.VaultConfigService = VaultConfigService;
171
+ exports.VaultConfigService = VaultConfigService = VaultConfigService_1 = __decorate([
172
+ (0, common_1.Injectable)(),
173
+ __metadata("design:paramtypes", [])
174
+ ], VaultConfigService);
@@ -0,0 +1,2 @@
1
+ export declare class VaultModule {
2
+ }
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.VaultModule = void 0;
10
+ const common_1 = require("@nestjs/common");
11
+ const vault_config_service_1 = require("./vault-config.service");
12
+ let VaultModule = class VaultModule {
13
+ };
14
+ exports.VaultModule = VaultModule;
15
+ exports.VaultModule = VaultModule = __decorate([
16
+ (0, common_1.Global)(),
17
+ (0, common_1.Module)({
18
+ providers: [vault_config_service_1.VaultConfigService],
19
+ exports: [vault_config_service_1.VaultConfigService],
20
+ })
21
+ ], VaultModule);