node-server-engine 1.10.14 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +200 -1
- package/dist/entities/PubSub/handleMessage/handleMessage.js +1 -1
- package/dist/entities/PubSub/handleMessage/handleMessage.js.map +1 -1
- package/dist/entities/Redis/Redis.d.ts +66 -24
- package/dist/entities/Redis/Redis.js +272 -144
- package/dist/entities/Redis/Redis.js.map +1 -1
- package/dist/entities/Redis/index.d.ts +1 -1
- package/dist/entities/Redis/index.js +10 -38
- package/dist/entities/Redis/index.js.map +1 -1
- package/dist/entities/SecretManager/SecretManager.d.ts +87 -0
- package/dist/entities/SecretManager/SecretManager.js +385 -0
- package/dist/entities/SecretManager/SecretManager.js.map +1 -0
- package/dist/entities/SecretManager/SecretManager.types.d.ts +71 -0
- package/dist/entities/SecretManager/SecretManager.types.js +2 -0
- package/dist/entities/SecretManager/SecretManager.types.js.map +1 -0
- package/dist/entities/SecretManager/index.d.ts +2 -0
- package/dist/entities/SecretManager/index.js +28 -0
- package/dist/entities/SecretManager/index.js.map +1 -0
- package/dist/entities/Sequelize/Sequelize.js +5 -3
- package/dist/entities/Sequelize/Sequelize.js.map +1 -1
- package/dist/entities/Server/Server.d.ts +2 -0
- package/dist/entities/Server/Server.js +9 -0
- package/dist/entities/Server/Server.js.map +1 -1
- package/dist/entities/Server/Server.types.d.ts +3 -0
- package/dist/entities/Server/Server.types.js.map +1 -1
- package/dist/entities/index.d.ts +1 -0
- package/dist/entities/index.js +11 -0
- package/dist/entities/index.js.map +1 -1
- package/dist/middleware/requestMetrics/requestMetrics.js +15 -5
- package/dist/middleware/requestMetrics/requestMetrics.js.map +1 -1
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/index.js +0 -11
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/jwt/jwt.js +25 -2
- package/dist/utils/jwt/jwt.js.map +1 -1
- package/dist/utils/sendEmail/sendEmail.js +4 -2
- package/dist/utils/sendEmail/sendEmail.js.map +1 -1
- package/package.json +1 -1
- package/dist/utils/secretManager/index.d.ts +0 -2
- package/dist/utils/secretManager/index.js +0 -28
- package/dist/utils/secretManager/index.js.map +0 -1
- package/dist/utils/secretManager/secretLoader.d.ts +0 -8
- package/dist/utils/secretManager/secretLoader.js +0 -68
- package/dist/utils/secretManager/secretLoader.js.map +0 -1
- package/dist/utils/secretManager/secretResolver.d.ts +0 -5
- package/dist/utils/secretManager/secretResolver.js +0 -41
- package/dist/utils/secretManager/secretResolver.js.map +0 -1
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.SecretManager = void 0;
|
|
8
|
+
var _secretManager = require("@google-cloud/secret-manager");
|
|
9
|
+
var _promises = _interopRequireDefault(require("fs/promises"));
|
|
10
|
+
var _path = _interopRequireDefault(require("path"));
|
|
11
|
+
var _os = _interopRequireDefault(require("os"));
|
|
12
|
+
var _EngineError = require("../EngineError");
|
|
13
|
+
var _LifecycleController = require("../LifecycleController");
|
|
14
|
+
var _report = require("../../utils/report");
|
|
15
|
+
const namespace = 'engine:SecretManager';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Generic GCP Secret Manager wrapper for managing application secrets
|
|
19
|
+
* Provides methods for loading secrets at startup, runtime fetching, and cleanup
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* // Initialize with configuration
|
|
24
|
+
* await SecretManager.init({
|
|
25
|
+
* enabled: true,
|
|
26
|
+
* projectId: 'my-project',
|
|
27
|
+
* prefix: 'my-service',
|
|
28
|
+
* secrets: ['DB_PASSWORD', { name: 'PRIVATE_KEY', type: 'file' }]
|
|
29
|
+
* });
|
|
30
|
+
*
|
|
31
|
+
* // Get a cached secret
|
|
32
|
+
* const password = SecretManager.getSecret('DB_PASSWORD');
|
|
33
|
+
*
|
|
34
|
+
* // Fetch a secret on-demand
|
|
35
|
+
* const apiKey = await SecretManager.fetchSecret('API_KEY');
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
const SecretManager = exports.SecretManager = {
|
|
39
|
+
/** Singleton client instance */
|
|
40
|
+
client: undefined,
|
|
41
|
+
/** Configuration options */
|
|
42
|
+
options: undefined,
|
|
43
|
+
/** Cache of loaded secrets */
|
|
44
|
+
secretCache: new Map(),
|
|
45
|
+
/** Temporary files created by Secret Manager */
|
|
46
|
+
tempFiles: [],
|
|
47
|
+
/**
|
|
48
|
+
* Initialize the Secret Manager
|
|
49
|
+
* @param config - Secret Manager configuration options
|
|
50
|
+
* @returns Promise with load results
|
|
51
|
+
*/
|
|
52
|
+
async init(config = {}) {
|
|
53
|
+
(0, _report.reportDebug)({
|
|
54
|
+
namespace,
|
|
55
|
+
message: 'Initializing Secret Manager'
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Set default options
|
|
59
|
+
this.options = {
|
|
60
|
+
enabled: process.env.NODE_ENV === 'production',
|
|
61
|
+
projectId: process.env.GCP_PROJECT_ID,
|
|
62
|
+
cache: true,
|
|
63
|
+
fallbackToEnv: true,
|
|
64
|
+
tempDir: _os.default.tmpdir(),
|
|
65
|
+
...config
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// Skip if disabled
|
|
69
|
+
if (!this.options.enabled) {
|
|
70
|
+
(0, _report.reportInfo)({
|
|
71
|
+
message: 'Secret Manager disabled - using environment variables',
|
|
72
|
+
data: {
|
|
73
|
+
nodeEnv: process.env.NODE_ENV
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
loaded: 0,
|
|
78
|
+
failed: 0,
|
|
79
|
+
fallback: 0,
|
|
80
|
+
details: []
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Validate configuration
|
|
85
|
+
if (!this.options.projectId) {
|
|
86
|
+
throw new _EngineError.EngineError({
|
|
87
|
+
message: 'GCP_PROJECT_ID is required when Secret Manager is enabled'
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Create client
|
|
92
|
+
this.client = new _secretManager.SecretManagerServiceClient();
|
|
93
|
+
|
|
94
|
+
// Register for shutdown
|
|
95
|
+
_LifecycleController.LifecycleController.register({
|
|
96
|
+
shutdown: () => this.shutdown()
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Load secrets
|
|
100
|
+
const result = await this.loadSecrets(this.options);
|
|
101
|
+
(0, _report.reportInfo)({
|
|
102
|
+
message: 'Secret Manager initialized',
|
|
103
|
+
data: {
|
|
104
|
+
loaded: result.loaded,
|
|
105
|
+
failed: result.failed,
|
|
106
|
+
fallback: result.fallback
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
return result;
|
|
110
|
+
},
|
|
111
|
+
/**
|
|
112
|
+
* Load secrets from Secret Manager
|
|
113
|
+
* @private
|
|
114
|
+
*/
|
|
115
|
+
async loadSecrets(config) {
|
|
116
|
+
const result = {
|
|
117
|
+
loaded: 0,
|
|
118
|
+
failed: 0,
|
|
119
|
+
fallback: 0,
|
|
120
|
+
details: []
|
|
121
|
+
};
|
|
122
|
+
if (!config.secrets || config.secrets.length === 0) {
|
|
123
|
+
(0, _report.reportInfo)('No secrets configured to load');
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Normalize secret configurations
|
|
128
|
+
const secretConfigs = config.secrets.map(this.normalizeSecretConfig);
|
|
129
|
+
|
|
130
|
+
// Load each secret
|
|
131
|
+
for (const secretConfig of secretConfigs) {
|
|
132
|
+
try {
|
|
133
|
+
const loaded = await this.loadSecret(secretConfig, config);
|
|
134
|
+
if (loaded) {
|
|
135
|
+
result.loaded++;
|
|
136
|
+
result.details.push({
|
|
137
|
+
name: loaded.name,
|
|
138
|
+
type: loaded.config.type,
|
|
139
|
+
source: 'secret-manager',
|
|
140
|
+
filePath: loaded.filePath
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Cache if enabled
|
|
144
|
+
if (config.cache) {
|
|
145
|
+
this.secretCache.set(loaded.name, loaded);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
} catch (error) {
|
|
149
|
+
(0, _report.reportError)(error);
|
|
150
|
+
|
|
151
|
+
// Try fallback
|
|
152
|
+
if (config.fallbackToEnv && process.env[secretConfig.targetEnvVar || secretConfig.name]) {
|
|
153
|
+
result.fallback++;
|
|
154
|
+
result.details.push({
|
|
155
|
+
name: secretConfig.name,
|
|
156
|
+
type: secretConfig.type,
|
|
157
|
+
source: 'fallback'
|
|
158
|
+
});
|
|
159
|
+
(0, _report.reportInfo)(`Using fallback value for secret: ${secretConfig.name}`);
|
|
160
|
+
} else {
|
|
161
|
+
result.failed++;
|
|
162
|
+
(0, _report.reportError)(new _EngineError.EngineError({
|
|
163
|
+
message: `Failed to load secret: ${secretConfig.name}`,
|
|
164
|
+
data: {
|
|
165
|
+
error: error.message
|
|
166
|
+
}
|
|
167
|
+
}));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
},
|
|
173
|
+
/**
|
|
174
|
+
* Load a single secret from Secret Manager
|
|
175
|
+
* @private
|
|
176
|
+
*/
|
|
177
|
+
async loadSecret(config, globalConfig) {
|
|
178
|
+
if (!this.client || !globalConfig.projectId) {
|
|
179
|
+
throw new _EngineError.EngineError({
|
|
180
|
+
message: 'Secret Manager not initialized'
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Build secret name with prefix (unless it's a common secret)
|
|
185
|
+
const secretName = config.common || !globalConfig.prefix ? config.name : `${globalConfig.prefix}-${config.name}`;
|
|
186
|
+
const version = config.version || 'latest';
|
|
187
|
+
const name = `projects/${globalConfig.projectId}/secrets/${secretName}/versions/${version}`;
|
|
188
|
+
(0, _report.reportDebug)({
|
|
189
|
+
namespace,
|
|
190
|
+
message: `Loading secret: ${secretName}`,
|
|
191
|
+
data: {
|
|
192
|
+
version,
|
|
193
|
+
type: config.type,
|
|
194
|
+
common: config.common
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
try {
|
|
198
|
+
// Access the secret
|
|
199
|
+
const [response] = await this.client.accessSecretVersion({
|
|
200
|
+
name
|
|
201
|
+
});
|
|
202
|
+
if (!response.payload?.data) {
|
|
203
|
+
throw new _EngineError.EngineError({
|
|
204
|
+
message: `Secret ${secretName} has no data`
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Convert to string
|
|
209
|
+
const secretValue = response.payload.data.toString('utf8');
|
|
210
|
+
const loaded = {
|
|
211
|
+
name: config.name,
|
|
212
|
+
value: secretValue,
|
|
213
|
+
config
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// Handle based on type
|
|
217
|
+
if (config.type === 'env') {
|
|
218
|
+
// Set environment variable
|
|
219
|
+
const envVarName = config.targetEnvVar || config.name;
|
|
220
|
+
process.env[envVarName] = secretValue;
|
|
221
|
+
(0, _report.reportDebug)({
|
|
222
|
+
namespace,
|
|
223
|
+
message: `Set environment variable: ${envVarName}`
|
|
224
|
+
});
|
|
225
|
+
} else if (config.type === 'file') {
|
|
226
|
+
// Write to temp file
|
|
227
|
+
const filename = config.filename || config.name;
|
|
228
|
+
const filePath = _path.default.join(globalConfig.tempDir, filename);
|
|
229
|
+
await _promises.default.writeFile(filePath, secretValue, {
|
|
230
|
+
mode: config.mode || 0o600
|
|
231
|
+
});
|
|
232
|
+
loaded.filePath = filePath;
|
|
233
|
+
this.tempFiles.push(filePath);
|
|
234
|
+
|
|
235
|
+
// Update environment variable to point to file
|
|
236
|
+
const envVarName = config.targetEnvVar || `${config.name}_PATH`;
|
|
237
|
+
process.env[envVarName] = filePath;
|
|
238
|
+
(0, _report.reportDebug)({
|
|
239
|
+
namespace,
|
|
240
|
+
message: `Wrote secret to file: ${filePath}`,
|
|
241
|
+
data: {
|
|
242
|
+
envVar: envVarName
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
return loaded;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
if (error.code === 5) {
|
|
249
|
+
// NOT_FOUND error
|
|
250
|
+
(0, _report.reportInfo)(`Secret not found in Secret Manager: ${secretName}`);
|
|
251
|
+
}
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
},
|
|
255
|
+
/**
|
|
256
|
+
* Normalize secret configuration
|
|
257
|
+
* @private
|
|
258
|
+
*/
|
|
259
|
+
normalizeSecretConfig(input) {
|
|
260
|
+
if (typeof input === 'string') {
|
|
261
|
+
// Simple string becomes an env type secret
|
|
262
|
+
return {
|
|
263
|
+
name: input,
|
|
264
|
+
type: 'env'
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return input;
|
|
268
|
+
},
|
|
269
|
+
/**
|
|
270
|
+
* Get a secret value from cache (if caching is enabled)
|
|
271
|
+
* @param name - Secret name
|
|
272
|
+
* @returns Secret value or undefined
|
|
273
|
+
*/
|
|
274
|
+
getSecret(name) {
|
|
275
|
+
const cached = this.secretCache.get(name);
|
|
276
|
+
if (cached) {
|
|
277
|
+
return cached.value;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Fallback to environment variable
|
|
281
|
+
return process.env[name];
|
|
282
|
+
},
|
|
283
|
+
/**
|
|
284
|
+
* Get secret from Secret Manager on-demand (useful for runtime secret rotation)
|
|
285
|
+
* @param name - Secret name
|
|
286
|
+
* @param version - Secret version (default: 'latest')
|
|
287
|
+
* @returns Promise with secret value
|
|
288
|
+
*/
|
|
289
|
+
async fetchSecret(name, version = 'latest') {
|
|
290
|
+
if (!this.client || !this.options?.projectId) {
|
|
291
|
+
throw new _EngineError.EngineError({
|
|
292
|
+
message: 'Secret Manager not initialized'
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
const secretName = this.options.prefix ? `${this.options.prefix}-${name}` : name;
|
|
296
|
+
const secretPath = `projects/${this.options.projectId}/secrets/${secretName}/versions/${version}`;
|
|
297
|
+
const [response] = await this.client.accessSecretVersion({
|
|
298
|
+
name: secretPath
|
|
299
|
+
});
|
|
300
|
+
if (!response.payload?.data) {
|
|
301
|
+
throw new _EngineError.EngineError({
|
|
302
|
+
message: `Secret ${secretName} has no data`
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
return response.payload.data.toString('utf8');
|
|
306
|
+
},
|
|
307
|
+
/**
|
|
308
|
+
* Reload all secrets (useful for secret rotation)
|
|
309
|
+
* @returns Promise with reload results
|
|
310
|
+
*/
|
|
311
|
+
async reload() {
|
|
312
|
+
(0, _report.reportInfo)({
|
|
313
|
+
message: 'Reloading secrets from Secret Manager'
|
|
314
|
+
});
|
|
315
|
+
if (!this.options) {
|
|
316
|
+
throw new _EngineError.EngineError({
|
|
317
|
+
message: 'Secret Manager not initialized'
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Clear cache
|
|
322
|
+
this.secretCache.clear();
|
|
323
|
+
|
|
324
|
+
// Reload secrets
|
|
325
|
+
return this.loadSecrets(this.options);
|
|
326
|
+
},
|
|
327
|
+
/**
|
|
328
|
+
* Shutdown Secret Manager and cleanup
|
|
329
|
+
* @returns Promise that resolves when cleanup is complete
|
|
330
|
+
*/
|
|
331
|
+
async shutdown() {
|
|
332
|
+
(0, _report.reportDebug)({
|
|
333
|
+
namespace,
|
|
334
|
+
message: 'Shutting down Secret Manager'
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
// Delete temporary files
|
|
338
|
+
for (const filePath of this.tempFiles) {
|
|
339
|
+
try {
|
|
340
|
+
await _promises.default.unlink(filePath);
|
|
341
|
+
(0, _report.reportDebug)({
|
|
342
|
+
namespace,
|
|
343
|
+
message: `Deleted temporary file: ${filePath}`
|
|
344
|
+
});
|
|
345
|
+
} catch {
|
|
346
|
+
(0, _report.reportInfo)(`Failed to delete temporary file: ${filePath}`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
this.tempFiles.length = 0;
|
|
350
|
+
this.secretCache.clear();
|
|
351
|
+
|
|
352
|
+
// Close client
|
|
353
|
+
if (this.client) {
|
|
354
|
+
await this.client.close();
|
|
355
|
+
this.client = undefined;
|
|
356
|
+
}
|
|
357
|
+
this.options = undefined;
|
|
358
|
+
(0, _report.reportDebug)({
|
|
359
|
+
namespace,
|
|
360
|
+
message: 'Secret Manager shut down'
|
|
361
|
+
});
|
|
362
|
+
},
|
|
363
|
+
/**
|
|
364
|
+
* Check if Secret Manager is initialized
|
|
365
|
+
* @returns True if initialized
|
|
366
|
+
*/
|
|
367
|
+
isInitialized() {
|
|
368
|
+
return this.client !== undefined;
|
|
369
|
+
},
|
|
370
|
+
/**
|
|
371
|
+
* Get current configuration (without sensitive data)
|
|
372
|
+
* @returns Configuration object or undefined
|
|
373
|
+
*/
|
|
374
|
+
getConfig() {
|
|
375
|
+
if (!this.options) return undefined;
|
|
376
|
+
return {
|
|
377
|
+
enabled: this.options.enabled,
|
|
378
|
+
projectId: this.options.projectId,
|
|
379
|
+
prefix: this.options.prefix,
|
|
380
|
+
cache: this.options.cache,
|
|
381
|
+
fallbackToEnv: this.options.fallbackToEnv
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
//# sourceMappingURL=SecretManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SecretManager.js","names":["_secretManager","require","_promises","_interopRequireDefault","_path","_os","_EngineError","_LifecycleController","_report","namespace","SecretManager","exports","client","undefined","options","secretCache","Map","tempFiles","init","config","reportDebug","message","enabled","process","env","NODE_ENV","projectId","GCP_PROJECT_ID","cache","fallbackToEnv","tempDir","os","tmpdir","reportInfo","data","nodeEnv","loaded","failed","fallback","details","EngineError","SecretManagerServiceClient","LifecycleController","register","shutdown","result","loadSecrets","secrets","length","secretConfigs","map","normalizeSecretConfig","secretConfig","loadSecret","push","name","type","source","filePath","set","error","reportError","targetEnvVar","globalConfig","secretName","common","prefix","version","response","accessSecretVersion","payload","secretValue","toString","value","envVarName","filename","path","join","fs","writeFile","mode","envVar","code","input","getSecret","cached","get","fetchSecret","secretPath","reload","clear","unlink","close","isInitialized","getConfig"],"sources":["../../../src/entities/SecretManager/SecretManager.ts"],"sourcesContent":["import { SecretManagerServiceClient } from '@google-cloud/secret-manager';\nimport fs from 'fs/promises';\nimport path from 'path';\nimport os from 'os';\nimport {\n SecretManagerOptions,\n SecretConfig,\n LoadedSecret,\n SecretLoadResult\n} from './SecretManager.types';\nimport { EngineError } from 'entities/EngineError';\nimport { LifecycleController } from 'entities/LifecycleController';\nimport { reportDebug, reportInfo, reportError } from 'utils/report';\n\nconst namespace = 'engine:SecretManager';\n\n/**\n * Generic GCP Secret Manager wrapper for managing application secrets\n * Provides methods for loading secrets at startup, runtime fetching, and cleanup\n * \n * @example\n * ```typescript\n * // Initialize with configuration\n * await SecretManager.init({\n * enabled: true,\n * projectId: 'my-project',\n * prefix: 'my-service',\n * secrets: ['DB_PASSWORD', { name: 'PRIVATE_KEY', type: 'file' }]\n * });\n * \n * // Get a cached secret\n * const password = SecretManager.getSecret('DB_PASSWORD');\n * \n * // Fetch a secret on-demand\n * const apiKey = await SecretManager.fetchSecret('API_KEY');\n * ```\n */\nexport const SecretManager = {\n /** Singleton client instance */\n client: undefined as SecretManagerServiceClient | undefined,\n\n /** Configuration options */\n options: undefined as SecretManagerOptions | undefined,\n\n /** Cache of loaded secrets */\n secretCache: new Map<string, LoadedSecret>(),\n\n /** Temporary files created by Secret Manager */\n tempFiles: [] as string[],\n\n /**\n * Initialize the Secret Manager\n * @param config - Secret Manager configuration options\n * @returns Promise with load results\n */\n async init(config: SecretManagerOptions = {}): Promise<SecretLoadResult> {\n reportDebug({ namespace, message: 'Initializing Secret Manager' });\n\n // Set default options\n this.options = {\n enabled: process.env.NODE_ENV === 'production',\n projectId: process.env.GCP_PROJECT_ID,\n cache: true,\n fallbackToEnv: true,\n tempDir: os.tmpdir(),\n ...config\n };\n\n // Skip if disabled\n if (!this.options.enabled) {\n reportInfo({\n message: 'Secret Manager disabled - using environment variables',\n data: { nodeEnv: process.env.NODE_ENV }\n });\n return {\n loaded: 0,\n failed: 0,\n fallback: 0,\n details: []\n };\n }\n\n // Validate configuration\n if (!this.options.projectId) {\n throw new EngineError({\n message: 'GCP_PROJECT_ID is required when Secret Manager is enabled'\n });\n }\n\n // Create client\n this.client = new SecretManagerServiceClient();\n\n // Register for shutdown\n LifecycleController.register({ shutdown: () => this.shutdown() });\n\n // Load secrets\n const result = await this.loadSecrets(this.options);\n\n reportInfo({\n message: 'Secret Manager initialized',\n data: {\n loaded: result.loaded,\n failed: result.failed,\n fallback: result.fallback\n }\n });\n\n return result;\n },\n\n /**\n * Load secrets from Secret Manager\n * @private\n */\n async loadSecrets(config: SecretManagerOptions): Promise<SecretLoadResult> {\n const result: SecretLoadResult = {\n loaded: 0,\n failed: 0,\n fallback: 0,\n details: []\n };\n\n if (!config.secrets || config.secrets.length === 0) {\n reportInfo('No secrets configured to load');\n return result;\n }\n\n // Normalize secret configurations\n const secretConfigs: SecretConfig[] = config.secrets.map(this.normalizeSecretConfig);\n\n // Load each secret\n for (const secretConfig of secretConfigs) {\n try {\n const loaded = await this.loadSecret(secretConfig, config);\n \n if (loaded) {\n result.loaded++;\n result.details.push({\n name: loaded.name,\n type: loaded.config.type,\n source: 'secret-manager',\n filePath: loaded.filePath\n });\n\n // Cache if enabled\n if (config.cache) {\n this.secretCache.set(loaded.name, loaded);\n }\n }\n } catch (error) {\n reportError(error);\n \n // Try fallback\n if (config.fallbackToEnv && process.env[secretConfig.targetEnvVar || secretConfig.name]) {\n result.fallback++;\n result.details.push({\n name: secretConfig.name,\n type: secretConfig.type,\n source: 'fallback'\n });\n reportInfo(`Using fallback value for secret: ${secretConfig.name}`);\n } else {\n result.failed++;\n reportError(new EngineError({\n message: `Failed to load secret: ${secretConfig.name}`,\n data: { error: (error as Error).message }\n }));\n }\n }\n }\n\n return result;\n },\n\n /**\n * Load a single secret from Secret Manager\n * @private\n */\n async loadSecret(\n config: SecretConfig,\n globalConfig: SecretManagerOptions\n ): Promise<LoadedSecret | null> {\n if (!this.client || !globalConfig.projectId) {\n throw new EngineError({ message: 'Secret Manager not initialized' });\n }\n\n // Build secret name with prefix (unless it's a common secret)\n const secretName = config.common || !globalConfig.prefix\n ? config.name\n : `${globalConfig.prefix}-${config.name}`;\n\n const version = config.version || 'latest';\n const name = `projects/${globalConfig.projectId}/secrets/${secretName}/versions/${version}`;\n\n reportDebug({\n namespace,\n message: `Loading secret: ${secretName}`,\n data: { version, type: config.type, common: config.common }\n });\n\n try {\n // Access the secret\n const [response] = await this.client.accessSecretVersion({ name });\n \n if (!response.payload?.data) {\n throw new EngineError({\n message: `Secret ${secretName} has no data`\n });\n }\n\n // Convert to string\n const secretValue = response.payload.data.toString('utf8');\n\n const loaded: LoadedSecret = {\n name: config.name,\n value: secretValue,\n config\n };\n\n // Handle based on type\n if (config.type === 'env') {\n // Set environment variable\n const envVarName = config.targetEnvVar || config.name;\n process.env[envVarName] = secretValue;\n \n reportDebug({\n namespace,\n message: `Set environment variable: ${envVarName}`\n });\n } else if (config.type === 'file') {\n // Write to temp file\n const filename = config.filename || config.name;\n const filePath = path.join(globalConfig.tempDir!, filename);\n \n await fs.writeFile(filePath, secretValue, {\n mode: config.mode || 0o600\n });\n \n loaded.filePath = filePath;\n this.tempFiles.push(filePath);\n\n // Update environment variable to point to file\n const envVarName = config.targetEnvVar || `${config.name}_PATH`;\n process.env[envVarName] = filePath;\n \n reportDebug({\n namespace,\n message: `Wrote secret to file: ${filePath}`,\n data: { envVar: envVarName }\n });\n }\n\n return loaded;\n } catch (error) {\n if ((error as { code?: number }).code === 5) {\n // NOT_FOUND error\n reportInfo(`Secret not found in Secret Manager: ${secretName}`);\n }\n throw error;\n }\n },\n\n /**\n * Normalize secret configuration\n * @private\n */\n normalizeSecretConfig(input: SecretConfig | string): SecretConfig {\n if (typeof input === 'string') {\n // Simple string becomes an env type secret\n return {\n name: input,\n type: 'env'\n };\n }\n return input;\n },\n\n /**\n * Get a secret value from cache (if caching is enabled)\n * @param name - Secret name\n * @returns Secret value or undefined\n */\n getSecret(name: string): string | undefined {\n const cached = this.secretCache.get(name);\n if (cached) {\n return cached.value;\n }\n \n // Fallback to environment variable\n return process.env[name];\n },\n\n /**\n * Get secret from Secret Manager on-demand (useful for runtime secret rotation)\n * @param name - Secret name\n * @param version - Secret version (default: 'latest')\n * @returns Promise with secret value\n */\n async fetchSecret(\n name: string,\n version: string = 'latest'\n ): Promise<string> {\n if (!this.client || !this.options?.projectId) {\n throw new EngineError({ message: 'Secret Manager not initialized' });\n }\n\n const secretName = this.options.prefix ? `${this.options.prefix}-${name}` : name;\n const secretPath = `projects/${this.options.projectId}/secrets/${secretName}/versions/${version}`;\n\n const [response] = await this.client.accessSecretVersion({ name: secretPath });\n \n if (!response.payload?.data) {\n throw new EngineError({ message: `Secret ${secretName} has no data` });\n }\n\n return response.payload.data.toString('utf8');\n },\n\n /**\n * Reload all secrets (useful for secret rotation)\n * @returns Promise with reload results\n */\n async reload(): Promise<SecretLoadResult> {\n reportInfo({ message: 'Reloading secrets from Secret Manager' });\n \n if (!this.options) {\n throw new EngineError({ message: 'Secret Manager not initialized' });\n }\n\n // Clear cache\n this.secretCache.clear();\n\n // Reload secrets\n return this.loadSecrets(this.options);\n },\n\n /**\n * Shutdown Secret Manager and cleanup\n * @returns Promise that resolves when cleanup is complete\n */\n async shutdown(): Promise<void> {\n reportDebug({ namespace, message: 'Shutting down Secret Manager' });\n\n // Delete temporary files\n for (const filePath of this.tempFiles) {\n try {\n await fs.unlink(filePath);\n reportDebug({\n namespace,\n message: `Deleted temporary file: ${filePath}`\n });\n } catch {\n reportInfo(`Failed to delete temporary file: ${filePath}`);\n }\n }\n\n this.tempFiles.length = 0;\n this.secretCache.clear();\n\n // Close client\n if (this.client) {\n await this.client.close();\n this.client = undefined;\n }\n\n this.options = undefined;\n \n reportDebug({ namespace, message: 'Secret Manager shut down' });\n },\n\n /**\n * Check if Secret Manager is initialized\n * @returns True if initialized\n */\n isInitialized(): boolean {\n return this.client !== undefined;\n },\n\n /**\n * Get current configuration (without sensitive data)\n * @returns Configuration object or undefined\n */\n getConfig(): Partial<SecretManagerOptions> | undefined {\n if (!this.options) return undefined;\n \n return {\n enabled: this.options.enabled,\n projectId: this.options.projectId,\n prefix: this.options.prefix,\n cache: this.options.cache,\n fallbackToEnv: this.options.fallbackToEnv\n };\n }\n};\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for a single secret to be loaded
|
|
3
|
+
*/
|
|
4
|
+
export interface SecretConfig {
|
|
5
|
+
/** Name of the secret in Secret Manager (without prefix) */
|
|
6
|
+
name: string;
|
|
7
|
+
/** Type of secret - 'env' for environment variable, 'file' for file-based secret */
|
|
8
|
+
type: 'env' | 'file';
|
|
9
|
+
/** Target environment variable name (defaults to secret name) */
|
|
10
|
+
targetEnvVar?: string;
|
|
11
|
+
/** For file type: filename to write (optional, uses secret name if not provided) */
|
|
12
|
+
filename?: string;
|
|
13
|
+
/** For file type: file permissions in octal (default: 0o600 for security) */
|
|
14
|
+
mode?: number;
|
|
15
|
+
/** Version of the secret to fetch (default: 'latest') */
|
|
16
|
+
version?: string;
|
|
17
|
+
/** Common secret shared across all services (no prefix) - e.g., PRIVATE_KEY, JWKS */
|
|
18
|
+
common?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Options for initializing Secret Manager
|
|
22
|
+
*/
|
|
23
|
+
export interface SecretManagerOptions {
|
|
24
|
+
/** Enable Secret Manager (default: true in production, false otherwise) */
|
|
25
|
+
enabled?: boolean;
|
|
26
|
+
/** GCP project ID */
|
|
27
|
+
projectId?: string;
|
|
28
|
+
/** Prefix for secret names (e.g., 'omg-identity-service') */
|
|
29
|
+
prefix?: string;
|
|
30
|
+
/** List of secrets to load */
|
|
31
|
+
secrets?: Array<SecretConfig | string>;
|
|
32
|
+
/** Load all secrets matching the prefix */
|
|
33
|
+
loadAll?: boolean;
|
|
34
|
+
/** Cache secrets in memory after loading */
|
|
35
|
+
cache?: boolean;
|
|
36
|
+
/** Fall back to process.env if secret not found in Secret Manager */
|
|
37
|
+
fallbackToEnv?: boolean;
|
|
38
|
+
/** Directory to write file-based secrets (default: OS temp directory) */
|
|
39
|
+
tempDir?: string;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Internal representation of a loaded secret
|
|
43
|
+
*/
|
|
44
|
+
export interface LoadedSecret {
|
|
45
|
+
/** Secret name */
|
|
46
|
+
name: string;
|
|
47
|
+
/** Secret value (string content) */
|
|
48
|
+
value: string;
|
|
49
|
+
/** Configuration used to load this secret */
|
|
50
|
+
config: SecretConfig;
|
|
51
|
+
/** Path to file if written to disk */
|
|
52
|
+
filePath?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Result of secret loading operation
|
|
56
|
+
*/
|
|
57
|
+
export interface SecretLoadResult {
|
|
58
|
+
/** Number of secrets successfully loaded */
|
|
59
|
+
loaded: number;
|
|
60
|
+
/** Number of secrets failed to load */
|
|
61
|
+
failed: number;
|
|
62
|
+
/** Number of secrets using fallback values */
|
|
63
|
+
fallback: number;
|
|
64
|
+
/** Details of loaded secrets (without values for security) */
|
|
65
|
+
details: Array<{
|
|
66
|
+
name: string;
|
|
67
|
+
type: 'env' | 'file';
|
|
68
|
+
source: 'secret-manager' | 'fallback';
|
|
69
|
+
filePath?: string;
|
|
70
|
+
}>;
|
|
71
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SecretManager.types.js","names":[],"sources":["../../../src/entities/SecretManager/SecretManager.types.ts"],"sourcesContent":["/**\n * Configuration for a single secret to be loaded\n */\nexport interface SecretConfig {\n /** Name of the secret in Secret Manager (without prefix) */\n name: string;\n /** Type of secret - 'env' for environment variable, 'file' for file-based secret */\n type: 'env' | 'file';\n /** Target environment variable name (defaults to secret name) */\n targetEnvVar?: string;\n /** For file type: filename to write (optional, uses secret name if not provided) */\n filename?: string;\n /** For file type: file permissions in octal (default: 0o600 for security) */\n mode?: number;\n /** Version of the secret to fetch (default: 'latest') */\n version?: string;\n /** Common secret shared across all services (no prefix) - e.g., PRIVATE_KEY, JWKS */\n common?: boolean;\n}\n\n/**\n * Options for initializing Secret Manager\n */\nexport interface SecretManagerOptions {\n /** Enable Secret Manager (default: true in production, false otherwise) */\n enabled?: boolean;\n /** GCP project ID */\n projectId?: string;\n /** Prefix for secret names (e.g., 'omg-identity-service') */\n prefix?: string;\n /** List of secrets to load */\n secrets?: Array<SecretConfig | string>;\n /** Load all secrets matching the prefix */\n loadAll?: boolean;\n /** Cache secrets in memory after loading */\n cache?: boolean;\n /** Fall back to process.env if secret not found in Secret Manager */\n fallbackToEnv?: boolean;\n /** Directory to write file-based secrets (default: OS temp directory) */\n tempDir?: string;\n}\n\n/**\n * Internal representation of a loaded secret\n */\nexport interface LoadedSecret {\n /** Secret name */\n name: string;\n /** Secret value (string content) */\n value: string;\n /** Configuration used to load this secret */\n config: SecretConfig;\n /** Path to file if written to disk */\n filePath?: string;\n}\n\n/**\n * Result of secret loading operation\n */\nexport interface SecretLoadResult {\n /** Number of secrets successfully loaded */\n loaded: number;\n /** Number of secrets failed to load */\n failed: number;\n /** Number of secrets using fallback values */\n fallback: number;\n /** Details of loaded secrets (without values for security) */\n details: Array<{\n name: string;\n type: 'env' | 'file';\n source: 'secret-manager' | 'fallback';\n filePath?: string;\n }>;\n}\n"],"mappings":"","ignoreList":[]}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
var _SecretManager = require("./SecretManager");
|
|
7
|
+
Object.keys(_SecretManager).forEach(function (key) {
|
|
8
|
+
if (key === "default" || key === "__esModule") return;
|
|
9
|
+
if (key in exports && exports[key] === _SecretManager[key]) return;
|
|
10
|
+
Object.defineProperty(exports, key, {
|
|
11
|
+
enumerable: true,
|
|
12
|
+
get: function () {
|
|
13
|
+
return _SecretManager[key];
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
var _SecretManager2 = require("./SecretManager.types");
|
|
18
|
+
Object.keys(_SecretManager2).forEach(function (key) {
|
|
19
|
+
if (key === "default" || key === "__esModule") return;
|
|
20
|
+
if (key in exports && exports[key] === _SecretManager2[key]) return;
|
|
21
|
+
Object.defineProperty(exports, key, {
|
|
22
|
+
enumerable: true,
|
|
23
|
+
get: function () {
|
|
24
|
+
return _SecretManager2[key];
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["_SecretManager","require","Object","keys","forEach","key","exports","defineProperty","enumerable","get","_SecretManager2"],"sources":["../../../src/entities/SecretManager/index.ts"],"sourcesContent":["export * from './SecretManager';\nexport * from './SecretManager.types';\n"],"mappings":";;;;;AAAA,IAAAA,cAAA,GAAAC,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAH,cAAA,EAAAI,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAL,cAAA,CAAAK,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAT,cAAA,CAAAK,GAAA;IAAA;EAAA;AAAA;AACA,IAAAK,eAAA,GAAAT,OAAA;AAAAC,MAAA,CAAAC,IAAA,CAAAO,eAAA,EAAAN,OAAA,WAAAC,GAAA;EAAA,IAAAA,GAAA,kBAAAA,GAAA;EAAA,IAAAA,GAAA,IAAAC,OAAA,IAAAA,OAAA,CAAAD,GAAA,MAAAK,eAAA,CAAAL,GAAA;EAAAH,MAAA,CAAAK,cAAA,CAAAD,OAAA,EAAAD,GAAA;IAAAG,UAAA;IAAAC,GAAA,WAAAA,CAAA;MAAA,OAAAC,eAAA,CAAAL,GAAA;IAAA;EAAA;AAAA","ignoreList":[]}
|
|
@@ -123,12 +123,14 @@ function addModels(models) {
|
|
|
123
123
|
sequelizeClient.addHook('afterFind', result => {
|
|
124
124
|
if (!result) return;
|
|
125
125
|
const convertToPlain = instance => {
|
|
126
|
-
if (!instance ||
|
|
127
|
-
const
|
|
126
|
+
if (!instance || typeof instance !== 'object') return instance;
|
|
127
|
+
const modelInstance = instance;
|
|
128
|
+
if (!modelInstance.toJSON) return instance;
|
|
129
|
+
const plain = modelInstance.toJSON();
|
|
128
130
|
// Copy the plain object properties back to the instance
|
|
129
131
|
// This makes property access work without .toJSON() in compiled code
|
|
130
132
|
Object.keys(plain).forEach(key => {
|
|
131
|
-
|
|
133
|
+
modelInstance[key] = plain[key];
|
|
132
134
|
});
|
|
133
135
|
};
|
|
134
136
|
if (Array.isArray(result)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Sequelize.js","names":["_sequelizeTypescript","require","_Sequelize","_interopRequireDefault","_Sequelize2","_EngineError","_LifecycleController","_report","sequelizeClient","exports","sequelize","Proxy","get","target","property","init","shutdown","addModels","EngineError","message","reportInfo","data","host","process","env","SQL_HOST","database","SQL_DB","user","SQL_USER","port","SQL_PORT","dialect","SQL_TYPE","isUnixSocket","startsWith","createSequelizeClient","NODE_ENV","authenticate","getDialect","err","reportError","error","Error","String","stack","undefined","LifecycleController","register","close","console","models","addHook","result","convertToPlain","instance","toJSON","plain","Object","keys","forEach","key","Array","isArray","sync","Sequelize","storage","logging","validateSequelizeEnvironment","username","dbConfig","poolMax","pool","max","poolMin","min","SQL_PASSWORD"],"sources":["../../../src/entities/Sequelize/Sequelize.ts"],"sourcesContent":["import { Sequelize, ModelCtor } from 'sequelize-typescript';\nimport dbConfig from './Sequelize.config';\nimport { Sequelize as SequelizeInterface } from './Sequelize.types';\nimport { validateSequelizeEnvironment } from './Sequelize.validate';\nimport { EngineError } from 'entities/EngineError';\nimport { LifecycleController } from 'entities/LifecycleController';\nimport { reportInfo, reportError } from 'utils/report';\n\nexport let sequelizeClient: undefined | Sequelize;\n\n/**\n * We use a proxy to extend the Sequelize behavior and allow initialization of the client only when needed while keeping the same interface\n */\nexport const sequelize = new Proxy<SequelizeInterface>(\n // Due to how we do the proxying, we are forced to ignore ts-errors, we could never create an object that respects the interface properly\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n {},\n {\n get: function (target, property): unknown {\n if (property === 'init') return init;\n if (property === 'shutdown') return shutdown;\n if (property === 'addModels') return addModels;\n // Create the sequelize client if it does not exist\n if (!sequelizeClient)\n throw new EngineError({\n message: 'Sequelize client was not initialized'\n });\n // Call the function on the sequelize client\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n return sequelizeClient[property] as unknown;\n }\n }\n);\n\n/** On init create client and connect */\nexport async function init(): Promise<void> {\n // Ignore if already initialized\n if (sequelizeClient) return;\n \n reportInfo({\n message: 'Initializing Sequelize client',\n data: {\n host: process.env.SQL_HOST,\n database: process.env.SQL_DB,\n user: process.env.SQL_USER,\n port: process.env.SQL_PORT,\n dialect: process.env.SQL_TYPE || 'postgres',\n isUnixSocket: process.env.SQL_HOST?.startsWith('/')\n }\n });\n \n sequelizeClient = createSequelizeClient();\n \n // In test environment, authentication happens automatically with SQLite\n // For other environments, authenticate and log the result\n if (process.env.NODE_ENV !== 'test') {\n reportInfo({\n message: 'Starting database authentication'\n });\n\n try {\n await sequelizeClient.authenticate();\n reportInfo({ \n message: 'Connected to database successfully',\n data: {\n host: process.env.SQL_HOST,\n database: process.env.SQL_DB,\n dialect: sequelizeClient?.getDialect()\n }\n });\n } catch (err) {\n reportError({\n message: 'Failed to connect to database',\n data: {\n error: err instanceof Error ? err.message : String(err),\n stack: err instanceof Error ? err.stack : undefined,\n host: process.env.SQL_HOST,\n database: process.env.SQL_DB,\n user: process.env.SQL_USER,\n port: process.env.SQL_PORT\n }\n });\n if (err instanceof Error) {\n reportError(err);\n }\n throw err;\n }\n }\n \n LifecycleController.register(sequelize);\n}\n\n/** On shutdown close connection and clear client */\nexport async function shutdown(): Promise<void> {\n if (sequelizeClient) {\n try {\n // For SQLite, just close the client (closing connection manager first causes SQLITE_MISUSE)\n // For other dialects, sequelize.close() handles everything properly\n await sequelizeClient.close();\n } catch (error) {\n // Ignore errors during shutdown\n if (process.env.NODE_ENV !== 'test') {\n console.error('Error closing sequelize client:', error);\n }\n }\n }\n sequelizeClient = undefined;\n}\n\nexport function addModels(models: Array<ModelCtor>): void {\n if (!sequelizeClient)\n throw new EngineError({\n message: 'Sequelize client was not initialized'\n });\n sequelizeClient.addModels(models);\n \n // Override toJSON behavior globally to ensure consistency between dev and prod\n // In dev (ts-node), property access works directly on instances\n // In prod (compiled), we need explicit serialization\n // This hook intercepts all afterFind results and converts them to plain objects\n sequelizeClient.addHook('afterFind', (result) => {\n if (!result) return;\n \n const convertToPlain = (instance: any) => {\n if (!instance || !instance.toJSON) return instance;\n const plain = instance.toJSON();\n // Copy the plain object properties back to the instance\n // This makes property access work without .toJSON() in compiled code\n Object.keys(plain).forEach(key => {\n instance[key] = plain[key];\n });\n };\n \n if (Array.isArray(result)) {\n result.forEach(convertToPlain);\n } else {\n convertToPlain(result);\n }\n });\n \n sequelizeClient.sync();\n}\n\n/** Create Sequelize client */\nexport function createSequelizeClient(): Sequelize {\n // Use in-memory SQLite for test environment to avoid real database connections\n if (process.env.NODE_ENV === 'test') {\n return new Sequelize({\n dialect: 'sqlite',\n storage: ':memory:',\n logging: false\n });\n }\n \n // Throw an error if there is no host initial\n validateSequelizeEnvironment();\n \n reportInfo({\n message: 'Creating Sequelize client with config',\n data: {\n database: process.env.SQL_DB,\n username: process.env.SQL_USER,\n host: process.env.SQL_HOST,\n port: dbConfig.port,\n dialect: dbConfig.dialect,\n isUnixSocket: process.env.SQL_HOST?.startsWith('/'),\n poolMax: dbConfig.pool?.max,\n poolMin: dbConfig.pool?.min\n }\n });\n \n return new Sequelize(\n process.env.SQL_DB as string,\n process.env.SQL_USER as string,\n process.env.SQL_PASSWORD as string,\n dbConfig\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"Sequelize.js","names":["_sequelizeTypescript","require","_Sequelize","_interopRequireDefault","_Sequelize2","_EngineError","_LifecycleController","_report","sequelizeClient","exports","sequelize","Proxy","get","target","property","init","shutdown","addModels","EngineError","message","reportInfo","data","host","process","env","SQL_HOST","database","SQL_DB","user","SQL_USER","port","SQL_PORT","dialect","SQL_TYPE","isUnixSocket","startsWith","createSequelizeClient","NODE_ENV","authenticate","getDialect","err","reportError","error","Error","String","stack","undefined","LifecycleController","register","close","console","models","addHook","result","convertToPlain","instance","modelInstance","toJSON","plain","Object","keys","forEach","key","Array","isArray","sync","Sequelize","storage","logging","validateSequelizeEnvironment","username","dbConfig","poolMax","pool","max","poolMin","min","SQL_PASSWORD"],"sources":["../../../src/entities/Sequelize/Sequelize.ts"],"sourcesContent":["import { Sequelize, ModelCtor } from 'sequelize-typescript';\nimport dbConfig from './Sequelize.config';\nimport { Sequelize as SequelizeInterface } from './Sequelize.types';\nimport { validateSequelizeEnvironment } from './Sequelize.validate';\nimport { EngineError } from 'entities/EngineError';\nimport { LifecycleController } from 'entities/LifecycleController';\nimport { reportInfo, reportError } from 'utils/report';\n\nexport let sequelizeClient: undefined | Sequelize;\n\n/**\n * We use a proxy to extend the Sequelize behavior and allow initialization of the client only when needed while keeping the same interface\n */\nexport const sequelize = new Proxy<SequelizeInterface>(\n // Due to how we do the proxying, we are forced to ignore ts-errors, we could never create an object that respects the interface properly\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n {},\n {\n get: function (target, property): unknown {\n if (property === 'init') return init;\n if (property === 'shutdown') return shutdown;\n if (property === 'addModels') return addModels;\n // Create the sequelize client if it does not exist\n if (!sequelizeClient)\n throw new EngineError({\n message: 'Sequelize client was not initialized'\n });\n // Call the function on the sequelize client\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n return sequelizeClient[property] as unknown;\n }\n }\n);\n\n/** On init create client and connect */\nexport async function init(): Promise<void> {\n // Ignore if already initialized\n if (sequelizeClient) return;\n \n reportInfo({\n message: 'Initializing Sequelize client',\n data: {\n host: process.env.SQL_HOST,\n database: process.env.SQL_DB,\n user: process.env.SQL_USER,\n port: process.env.SQL_PORT,\n dialect: process.env.SQL_TYPE || 'postgres',\n isUnixSocket: process.env.SQL_HOST?.startsWith('/')\n }\n });\n \n sequelizeClient = createSequelizeClient();\n \n // In test environment, authentication happens automatically with SQLite\n // For other environments, authenticate and log the result\n if (process.env.NODE_ENV !== 'test') {\n reportInfo({\n message: 'Starting database authentication'\n });\n\n try {\n await sequelizeClient.authenticate();\n reportInfo({ \n message: 'Connected to database successfully',\n data: {\n host: process.env.SQL_HOST,\n database: process.env.SQL_DB,\n dialect: sequelizeClient?.getDialect()\n }\n });\n } catch (err) {\n reportError({\n message: 'Failed to connect to database',\n data: {\n error: err instanceof Error ? err.message : String(err),\n stack: err instanceof Error ? err.stack : undefined,\n host: process.env.SQL_HOST,\n database: process.env.SQL_DB,\n user: process.env.SQL_USER,\n port: process.env.SQL_PORT\n }\n });\n if (err instanceof Error) {\n reportError(err);\n }\n throw err;\n }\n }\n \n LifecycleController.register(sequelize);\n}\n\n/** On shutdown close connection and clear client */\nexport async function shutdown(): Promise<void> {\n if (sequelizeClient) {\n try {\n // For SQLite, just close the client (closing connection manager first causes SQLITE_MISUSE)\n // For other dialects, sequelize.close() handles everything properly\n await sequelizeClient.close();\n } catch (error) {\n // Ignore errors during shutdown\n if (process.env.NODE_ENV !== 'test') {\n console.error('Error closing sequelize client:', error);\n }\n }\n }\n sequelizeClient = undefined;\n}\n\nexport function addModels(models: Array<ModelCtor>): void {\n if (!sequelizeClient)\n throw new EngineError({\n message: 'Sequelize client was not initialized'\n });\n sequelizeClient.addModels(models);\n \n // Override toJSON behavior globally to ensure consistency between dev and prod\n // In dev (ts-node), property access works directly on instances\n // In prod (compiled), we need explicit serialization\n // This hook intercepts all afterFind results and converts them to plain objects\n sequelizeClient.addHook('afterFind', (result) => {\n if (!result) return;\n \n const convertToPlain = (instance: unknown) => {\n if (!instance || typeof instance !== 'object') return instance;\n const modelInstance = instance as { toJSON?: () => Record<string, unknown> } & Record<string, unknown>;\n if (!modelInstance.toJSON) return instance;\n const plain = modelInstance.toJSON();\n // Copy the plain object properties back to the instance\n // This makes property access work without .toJSON() in compiled code\n Object.keys(plain).forEach(key => {\n modelInstance[key] = plain[key];\n });\n };\n \n if (Array.isArray(result)) {\n result.forEach(convertToPlain);\n } else {\n convertToPlain(result);\n }\n });\n \n sequelizeClient.sync();\n}\n\n/** Create Sequelize client */\nexport function createSequelizeClient(): Sequelize {\n // Use in-memory SQLite for test environment to avoid real database connections\n if (process.env.NODE_ENV === 'test') {\n return new Sequelize({\n dialect: 'sqlite',\n storage: ':memory:',\n logging: false\n });\n }\n \n // Throw an error if there is no host initial\n validateSequelizeEnvironment();\n \n reportInfo({\n message: 'Creating Sequelize client with config',\n data: {\n database: process.env.SQL_DB,\n username: process.env.SQL_USER,\n host: process.env.SQL_HOST,\n port: dbConfig.port,\n dialect: dbConfig.dialect,\n isUnixSocket: process.env.SQL_HOST?.startsWith('/'),\n poolMax: dbConfig.pool?.max,\n poolMin: dbConfig.pool?.min\n }\n });\n \n return new Sequelize(\n process.env.SQL_DB as string,\n process.env.SQL_USER as string,\n process.env.SQL_PASSWORD as string,\n dbConfig\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -28,6 +28,8 @@ export declare class Server {
|
|
|
28
28
|
private cron?;
|
|
29
29
|
/** Global auth related configuration */
|
|
30
30
|
private auth;
|
|
31
|
+
/** Secret Manager configuration */
|
|
32
|
+
private secretManagerConfig?;
|
|
31
33
|
/** WebSocket server instance, if configured */
|
|
32
34
|
private wss?;
|
|
33
35
|
/** Abort controller that handles signals for the server */
|