node-server-engine 1.3.2 → 1.3.4
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/.env.example +14 -1
- package/dist/entities/Server/Server.d.ts +2 -2
- package/dist/entities/Server/Server.js +8 -16
- package/dist/entities/Server/Server.js.map +1 -1
- package/dist/middleware/userResolver/userResolver.d.ts +4 -0
- package/dist/middleware/userResolver/userResolver.js +18 -4
- package/dist/middleware/userResolver/userResolver.js.map +1 -1
- package/dist/utils/createHttpServer/createHttpServer.d.ts +5 -2
- package/dist/utils/createHttpServer/createHttpServer.js +17 -12
- package/dist/utils/createHttpServer/createHttpServer.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +11 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/jwt/jwt.js +5 -5
- package/dist/utils/jwt/jwt.js.map +1 -1
- package/dist/utils/secretManager/index.d.ts +2 -0
- package/dist/utils/secretManager/index.js +28 -0
- package/dist/utils/secretManager/index.js.map +1 -0
- package/dist/utils/secretManager/secretLoader.d.ts +8 -0
- package/dist/utils/secretManager/secretLoader.js +64 -0
- package/dist/utils/secretManager/secretLoader.js.map +1 -0
- package/dist/utils/secretManager/secretResolver.d.ts +5 -0
- package/dist/utils/secretManager/secretResolver.js +42 -0
- package/dist/utils/secretManager/secretResolver.js.map +1 -0
- package/dist/utils/tlsConfig/httpsAgent.js +8 -4
- package/dist/utils/tlsConfig/httpsAgent.js.map +1 -1
- package/dist/utils/tlsConfig/tlsConfig.d.ts +1 -1
- package/dist/utils/tlsConfig/tlsConfig.js +14 -8
- package/dist/utils/tlsConfig/tlsConfig.js.map +1 -1
- package/package.json +2 -1
- package/src/entities/Server/Server.ts +7 -25
- package/src/middleware/userResolver/userResolver.ts +19 -1
- package/src/utils/createHttpServer/createHttpServer.ts +18 -13
- package/src/utils/index.ts +2 -0
- package/src/utils/jwt/jwt.ts +8 -7
- package/src/utils/secretManager/index.ts +2 -0
- package/src/utils/secretManager/secretLoader.ts +62 -0
- package/src/utils/secretManager/secretResolver.ts +36 -0
- package/src/utils/tlsConfig/httpsAgent.ts +22 -13
- package/src/utils/tlsConfig/tlsConfig.ts +22 -20
- package/src/middleware/userResolver/userResolver.test.ts +0 -35
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.secretCache = exports.initializeSecrets = void 0;
|
|
7
|
+
var _secretManager = require("@google-cloud/secret-manager");
|
|
8
|
+
var _EngineError = require("../../entities/EngineError");
|
|
9
|
+
// Cache to store preloaded secrets
|
|
10
|
+
const secretCache = exports.secretCache = {};
|
|
11
|
+
let secretClient;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Initialize the Secret Manager client and preload secrets.
|
|
15
|
+
* @param secretEnvVars List of env var names that contain secret paths
|
|
16
|
+
*/
|
|
17
|
+
const initializeSecrets = async secretEnvVars => {
|
|
18
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
19
|
+
console.log('Development mode → using local files for secrets');
|
|
20
|
+
return; // No need to preload anything
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
// Default creds (for GCP runtime)
|
|
24
|
+
secretClient = new _secretManager.SecretManagerServiceClient();
|
|
25
|
+
const secretNames = secretEnvVars.filter(name => process.env[name]);
|
|
26
|
+
if (secretNames.length === 0) {
|
|
27
|
+
throw new _EngineError.EngineError({
|
|
28
|
+
message: 'No secret names defined in environment variables'
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Fetch all secrets from GCP Secret Manager
|
|
33
|
+
for (const name of secretNames) {
|
|
34
|
+
const secretPath = process.env[name];
|
|
35
|
+
if (!secretPath) {
|
|
36
|
+
throw new _EngineError.EngineError({
|
|
37
|
+
message: `Secret path for ${name} is not defined`
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const [version] = await secretClient.accessSecretVersion({
|
|
42
|
+
name: secretPath
|
|
43
|
+
});
|
|
44
|
+
const payload = version.payload?.data?.toString('utf-8');
|
|
45
|
+
if (!payload) {
|
|
46
|
+
throw new _EngineError.EngineError({
|
|
47
|
+
message: `Secret ${name} not found or empty`
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
secretCache[name] = payload;
|
|
51
|
+
} catch (err) {
|
|
52
|
+
throw new _EngineError.EngineError({
|
|
53
|
+
message: `Error accessing secret ${name}: ${err.message}`
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} catch (err) {
|
|
58
|
+
throw new _EngineError.EngineError({
|
|
59
|
+
message: `Failed to initialize secrets: ${err.message}`
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
exports.initializeSecrets = initializeSecrets;
|
|
64
|
+
//# sourceMappingURL=secretLoader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secretLoader.js","names":["_secretManager","require","_EngineError","secretCache","exports","secretClient","initializeSecrets","secretEnvVars","process","env","NODE_ENV","console","log","SecretManagerServiceClient","secretNames","filter","name","length","EngineError","message","secretPath","version","accessSecretVersion","payload","data","toString","err"],"sources":["../../../src/utils/secretManager/secretLoader.ts"],"sourcesContent":["import { SecretManagerServiceClient } from '@google-cloud/secret-manager';\nimport { EngineError } from 'entities/EngineError';\n\n// Cache to store preloaded secrets\nexport const secretCache: { [key: string]: string } = {};\nlet secretClient: SecretManagerServiceClient;\n\n/**\n * Initialize the Secret Manager client and preload secrets.\n * @param secretEnvVars List of env var names that contain secret paths\n */\nexport const initializeSecrets = async (\n secretEnvVars: string[]\n): Promise<void> => {\n if (process.env.NODE_ENV !== 'production') {\n console.log('Development mode → using local files for secrets');\n return; // No need to preload anything\n }\n\n try {\n // Default creds (for GCP runtime)\n secretClient = new SecretManagerServiceClient();\n\n const secretNames = secretEnvVars.filter((name) => process.env[name]);\n if (secretNames.length === 0) {\n throw new EngineError({\n message: 'No secret names defined in environment variables'\n });\n }\n\n // Fetch all secrets from GCP Secret Manager\n for (const name of secretNames) {\n const secretPath = process.env[name];\n if (!secretPath) {\n throw new EngineError({\n message: `Secret path for ${name} is not defined`\n });\n }\n\n try {\n const [version] = await secretClient.accessSecretVersion({\n name: secretPath\n });\n const payload = version.payload?.data?.toString('utf-8');\n if (!payload) {\n throw new EngineError({\n message: `Secret ${name} not found or empty`\n });\n }\n secretCache[name] = payload;\n } catch (err) {\n throw new EngineError({\n message: `Error accessing secret ${name}: ${(err as Error).message}`\n });\n }\n }\n } catch (err) {\n throw new EngineError({\n message: `Failed to initialize secrets: ${(err as Error).message}`\n });\n }\n};\n"],"mappings":";;;;;;AAAA,IAAAA,cAAA,GAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AAEA;AACO,MAAME,WAAsC,GAAAC,OAAA,CAAAD,WAAA,GAAG,CAAC,CAAC;AACxD,IAAIE,YAAwC;;AAE5C;AACA;AACA;AACA;AACO,MAAMC,iBAAiB,GAAG,MAC/BC,aAAuB,IACL;EAClB,IAAIC,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,YAAY,EAAE;IACzCC,OAAO,CAACC,GAAG,CAAC,kDAAkD,CAAC;IAC/D,OAAO,CAAC;EACV;EAEA,IAAI;IACF;IACAP,YAAY,GAAG,IAAIQ,yCAA0B,CAAC,CAAC;IAE/C,MAAMC,WAAW,GAAGP,aAAa,CAACQ,MAAM,CAAEC,IAAI,IAAKR,OAAO,CAACC,GAAG,CAACO,IAAI,CAAC,CAAC;IACrE,IAAIF,WAAW,CAACG,MAAM,KAAK,CAAC,EAAE;MAC5B,MAAM,IAAIC,wBAAW,CAAC;QACpBC,OAAO,EAAE;MACX,CAAC,CAAC;IACJ;;IAEA;IACA,KAAK,MAAMH,IAAI,IAAIF,WAAW,EAAE;MAC9B,MAAMM,UAAU,GAAGZ,OAAO,CAACC,GAAG,CAACO,IAAI,CAAC;MACpC,IAAI,CAACI,UAAU,EAAE;QACf,MAAM,IAAIF,wBAAW,CAAC;UACpBC,OAAO,EAAE,mBAAmBH,IAAI;QAClC,CAAC,CAAC;MACJ;MAEA,IAAI;QACF,MAAM,CAACK,OAAO,CAAC,GAAG,MAAMhB,YAAY,CAACiB,mBAAmB,CAAC;UACvDN,IAAI,EAAEI;QACR,CAAC,CAAC;QACF,MAAMG,OAAO,GAAGF,OAAO,CAACE,OAAO,EAAEC,IAAI,EAAEC,QAAQ,CAAC,OAAO,CAAC;QACxD,IAAI,CAACF,OAAO,EAAE;UACZ,MAAM,IAAIL,wBAAW,CAAC;YACpBC,OAAO,EAAE,UAAUH,IAAI;UACzB,CAAC,CAAC;QACJ;QACAb,WAAW,CAACa,IAAI,CAAC,GAAGO,OAAO;MAC7B,CAAC,CAAC,OAAOG,GAAG,EAAE;QACZ,MAAM,IAAIR,wBAAW,CAAC;UACpBC,OAAO,EAAE,0BAA0BH,IAAI,KAAMU,GAAG,CAAWP,OAAO;QACpE,CAAC,CAAC;MACJ;IACF;EACF,CAAC,CAAC,OAAOO,GAAG,EAAE;IACZ,MAAM,IAAIR,wBAAW,CAAC;MACpBC,OAAO,EAAE,iCAAkCO,GAAG,CAAWP,OAAO;IAClE,CAAC,CAAC;EACJ;AACF,CAAC;AAACf,OAAA,CAAAE,iBAAA,GAAAA,iBAAA","ignoreList":[]}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.getSecretOrFile = void 0;
|
|
8
|
+
var _fs = _interopRequireDefault(require("fs"));
|
|
9
|
+
var _EngineError = require("../../entities/EngineError");
|
|
10
|
+
var _secretLoader = require("./secretLoader");
|
|
11
|
+
/**
|
|
12
|
+
* Retrieve a secret value (production) or file content (development).
|
|
13
|
+
* @param name The env var name that contains either the secret path (prod) or local file path (dev)
|
|
14
|
+
*/
|
|
15
|
+
const getSecretOrFile = name => {
|
|
16
|
+
if (process.env.NODE_ENV === 'production') {
|
|
17
|
+
const secretValue = _secretLoader.secretCache[name];
|
|
18
|
+
console.log('SecrrtCj', _secretLoader.secretCache);
|
|
19
|
+
if (!secretValue) {
|
|
20
|
+
throw new _EngineError.EngineError({
|
|
21
|
+
message: `Secret value for ${name} not found in cache`
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
return secretValue;
|
|
25
|
+
} else {
|
|
26
|
+
const localPath = process.env[name];
|
|
27
|
+
if (!localPath) {
|
|
28
|
+
throw new _EngineError.EngineError({
|
|
29
|
+
message: `Local path for ${name} is not defined`
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
return _fs.default.readFileSync(localPath, 'utf-8');
|
|
34
|
+
} catch (err) {
|
|
35
|
+
throw new _EngineError.EngineError({
|
|
36
|
+
message: `Error reading local file for ${name}: ${err.message}`
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
exports.getSecretOrFile = getSecretOrFile;
|
|
42
|
+
//# sourceMappingURL=secretResolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secretResolver.js","names":["_fs","_interopRequireDefault","require","_EngineError","_secretLoader","getSecretOrFile","name","process","env","NODE_ENV","secretValue","secretCache","console","log","EngineError","message","localPath","fs","readFileSync","err","exports"],"sources":["../../../src/utils/secretManager/secretResolver.ts"],"sourcesContent":["import fs from 'fs';\nimport { EngineError } from 'entities/EngineError';\nimport { secretCache } from './secretLoader';\n\n/**\n * Retrieve a secret value (production) or file content (development).\n * @param name The env var name that contains either the secret path (prod) or local file path (dev)\n */\nexport const getSecretOrFile = (name: string): string => {\n if (process.env.NODE_ENV === 'production') {\n const secretValue = secretCache[name];\n console.log('SecrrtCj', secretCache);\n\n if (!secretValue) {\n throw new EngineError({\n message: `Secret value for ${name} not found in cache`\n });\n }\n return secretValue;\n } else {\n const localPath = process.env[name];\n if (!localPath) {\n throw new EngineError({\n message: `Local path for ${name} is not defined`\n });\n }\n\n try {\n return fs.readFileSync(localPath, 'utf-8');\n } catch (err) {\n throw new EngineError({\n message: `Error reading local file for ${name}: ${(err as Error).message}`\n });\n }\n }\n};\n"],"mappings":";;;;;;;AAAA,IAAAA,GAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,YAAA,GAAAD,OAAA;AACA,IAAAE,aAAA,GAAAF,OAAA;AAEA;AACA;AACA;AACA;AACO,MAAMG,eAAe,GAAIC,IAAY,IAAa;EACvD,IAAIC,OAAO,CAACC,GAAG,CAACC,QAAQ,KAAK,YAAY,EAAE;IACzC,MAAMC,WAAW,GAAGC,yBAAW,CAACL,IAAI,CAAC;IACrCM,OAAO,CAACC,GAAG,CAAC,UAAU,EAAEF,yBAAW,CAAC;IAEpC,IAAI,CAACD,WAAW,EAAE;MAChB,MAAM,IAAII,wBAAW,CAAC;QACpBC,OAAO,EAAE,oBAAoBT,IAAI;MACnC,CAAC,CAAC;IACJ;IACA,OAAOI,WAAW;EACpB,CAAC,MAAM;IACL,MAAMM,SAAS,GAAGT,OAAO,CAACC,GAAG,CAACF,IAAI,CAAC;IACnC,IAAI,CAACU,SAAS,EAAE;MACd,MAAM,IAAIF,wBAAW,CAAC;QACpBC,OAAO,EAAE,kBAAkBT,IAAI;MACjC,CAAC,CAAC;IACJ;IAEA,IAAI;MACF,OAAOW,WAAE,CAACC,YAAY,CAACF,SAAS,EAAE,OAAO,CAAC;IAC5C,CAAC,CAAC,OAAOG,GAAG,EAAE;MACZ,MAAM,IAAIL,wBAAW,CAAC;QACpBC,OAAO,EAAE,gCAAgCT,IAAI,KAAMa,GAAG,CAAWJ,OAAO;MAC1E,CAAC,CAAC;IACJ;EACF;AACF,CAAC;AAACK,OAAA,CAAAf,eAAA,GAAAA,eAAA","ignoreList":[]}
|
|
@@ -5,17 +5,21 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
7
|
exports.getHttpsAgent = getHttpsAgent;
|
|
8
|
-
var _fs = _interopRequireDefault(require("fs"));
|
|
9
8
|
var _https = _interopRequireDefault(require("https"));
|
|
10
9
|
var _tlsConfig = require("./tlsConfig.validate");
|
|
11
10
|
var _EngineError = require("../../entities/EngineError");
|
|
11
|
+
var _ = require("./..");
|
|
12
12
|
let httpsAgent;
|
|
13
13
|
function getHttpsAgent() {
|
|
14
14
|
if (httpsAgent) return httpsAgent;
|
|
15
15
|
(0, _tlsConfig.validateTlsConfig)();
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const
|
|
16
|
+
|
|
17
|
+
// Always pass env var *names*, not their values
|
|
18
|
+
const key = (0, _.getSecretOrFile)(process.env.TLS_REQUEST_KEY ? 'TLS_REQUEST_KEY' : 'TLS_SERVER_KEY');
|
|
19
|
+
const cert = (0, _.getSecretOrFile)(process.env.TLS_REQUEST_CERT ? 'TLS_REQUEST_CERT' : 'TLS_SERVER_CERT');
|
|
20
|
+
|
|
21
|
+
// CA is optional → only load if env var is set
|
|
22
|
+
const ca = process.env.TLS_REQUEST_CA ? (0, _.getSecretOrFile)('TLS_REQUEST_CA') : process.env.TLS_CA ? (0, _.getSecretOrFile)('TLS_CA') : undefined;
|
|
19
23
|
const passphrase = process.env.TLS_REQUEST_KEY_PASSPHRASE ?? process.env.TLS_SERVER_KEY_PASSPHRASE;
|
|
20
24
|
if (!key || !cert || !ca || !passphrase) {
|
|
21
25
|
throw new _EngineError.EngineError({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"httpsAgent.js","names":["
|
|
1
|
+
{"version":3,"file":"httpsAgent.js","names":["_https","_interopRequireDefault","require","_tlsConfig","_EngineError","_","httpsAgent","getHttpsAgent","validateTlsConfig","key","getSecretOrFile","process","env","TLS_REQUEST_KEY","cert","TLS_REQUEST_CERT","ca","TLS_REQUEST_CA","TLS_CA","undefined","passphrase","TLS_REQUEST_KEY_PASSPHRASE","TLS_SERVER_KEY_PASSPHRASE","EngineError","message","https","Agent"],"sources":["../../../src/utils/tlsConfig/httpsAgent.ts"],"sourcesContent":["import https, { Agent } from 'https';\nimport { validateTlsConfig } from './tlsConfig.validate';\nimport { EngineError } from 'entities/EngineError';\nimport { getSecretOrFile } from 'utils';\n\nlet httpsAgent: Agent | undefined;\nexport function getHttpsAgent(): Agent {\n if (httpsAgent) return httpsAgent;\n\n validateTlsConfig();\n\n // Always pass env var *names*, not their values\n const key = getSecretOrFile(\n process.env.TLS_REQUEST_KEY ? 'TLS_REQUEST_KEY' : 'TLS_SERVER_KEY'\n );\n\n const cert = getSecretOrFile(\n process.env.TLS_REQUEST_CERT ? 'TLS_REQUEST_CERT' : 'TLS_SERVER_CERT'\n );\n\n // CA is optional → only load if env var is set\n const ca = process.env.TLS_REQUEST_CA\n ? getSecretOrFile('TLS_REQUEST_CA')\n : process.env.TLS_CA\n ? getSecretOrFile('TLS_CA')\n : undefined;\n\n const passphrase =\n process.env.TLS_REQUEST_KEY_PASSPHRASE ??\n process.env.TLS_SERVER_KEY_PASSPHRASE;\n\n if (!key || !cert || !ca || !passphrase) {\n throw new EngineError({\n message: 'TLS environment variables are not set correctly'\n });\n }\n\n httpsAgent = new https.Agent({\n key,\n cert,\n ca,\n passphrase\n });\n\n return httpsAgent;\n}\n"],"mappings":";;;;;;;AAAA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,UAAA,GAAAD,OAAA;AACA,IAAAE,YAAA,GAAAF,OAAA;AACA,IAAAG,CAAA,GAAAH,OAAA;AAEA,IAAII,UAA6B;AAC1B,SAASC,aAAaA,CAAA,EAAU;EACrC,IAAID,UAAU,EAAE,OAAOA,UAAU;EAEjC,IAAAE,4BAAiB,EAAC,CAAC;;EAEnB;EACA,MAAMC,GAAG,GAAG,IAAAC,iBAAe,EACzBC,OAAO,CAACC,GAAG,CAACC,eAAe,GAAG,iBAAiB,GAAG,gBACpD,CAAC;EAED,MAAMC,IAAI,GAAG,IAAAJ,iBAAe,EAC1BC,OAAO,CAACC,GAAG,CAACG,gBAAgB,GAAG,kBAAkB,GAAG,iBACtD,CAAC;;EAED;EACA,MAAMC,EAAE,GAAGL,OAAO,CAACC,GAAG,CAACK,cAAc,GACjC,IAAAP,iBAAe,EAAC,gBAAgB,CAAC,GACjCC,OAAO,CAACC,GAAG,CAACM,MAAM,GAChB,IAAAR,iBAAe,EAAC,QAAQ,CAAC,GACzBS,SAAS;EAEf,MAAMC,UAAU,GACdT,OAAO,CAACC,GAAG,CAACS,0BAA0B,IACtCV,OAAO,CAACC,GAAG,CAACU,yBAAyB;EAEvC,IAAI,CAACb,GAAG,IAAI,CAACK,IAAI,IAAI,CAACE,EAAE,IAAI,CAACI,UAAU,EAAE;IACvC,MAAM,IAAIG,wBAAW,CAAC;MACpBC,OAAO,EAAE;IACX,CAAC,CAAC;EACJ;EAEAlB,UAAU,GAAG,IAAImB,cAAK,CAACC,KAAK,CAAC;IAC3BjB,GAAG;IACHK,IAAI;IACJE,EAAE;IACFI;EACF,CAAC,CAAC;EAEF,OAAOd,UAAU;AACnB","ignoreList":[]}
|
|
@@ -3,6 +3,6 @@ import { TlsConfig } from './tlsConfig.types';
|
|
|
3
3
|
export declare let tlsConfig: undefined | TlsConfig;
|
|
4
4
|
export declare let httpsAgent: undefined | Agent;
|
|
5
5
|
/**
|
|
6
|
-
* Load TLS config by its
|
|
6
|
+
* Load TLS config by its environment setting (local files or GCP secrets)
|
|
7
7
|
*/
|
|
8
8
|
export declare function loadTlsConfig(): void;
|
|
@@ -7,26 +7,32 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
7
7
|
exports.httpsAgent = void 0;
|
|
8
8
|
exports.loadTlsConfig = loadTlsConfig;
|
|
9
9
|
exports.tlsConfig = void 0;
|
|
10
|
-
var _fs = _interopRequireDefault(require("fs"));
|
|
11
10
|
var _https = _interopRequireDefault(require("https"));
|
|
12
11
|
var _tlsConfig = require("./tlsConfig.validate");
|
|
13
12
|
var _EngineError = require("../../entities/EngineError");
|
|
13
|
+
var _ = require("./..");
|
|
14
14
|
let tlsConfig = exports.tlsConfig = void 0;
|
|
15
15
|
let httpsAgent = exports.httpsAgent = void 0;
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
* Load TLS config by its
|
|
18
|
+
* Load TLS config by its environment setting (local files or GCP secrets)
|
|
19
19
|
*/
|
|
20
20
|
function loadTlsConfig() {
|
|
21
|
-
// Check that environment variables are correctly set
|
|
22
21
|
(0, _tlsConfig.validateTlsConfig)();
|
|
23
22
|
exports.tlsConfig = tlsConfig = undefined;
|
|
24
23
|
if (process.env.TLS_REQUEST_KEY || process.env.TLS_SERVER_KEY) {
|
|
24
|
+
// Fetch TLS secrets using unified utility
|
|
25
|
+
const key = (0, _.getSecretOrFile)(process.env.TLS_REQUEST_KEY ?? process.env.TLS_SERVER_KEY);
|
|
26
|
+
const cert = (0, _.getSecretOrFile)(process.env.TLS_REQUEST_CERT ?? process.env.TLS_SERVER_CERT);
|
|
27
|
+
const ca = (0, _.getSecretOrFile)(process.env.TLS_REQUEST_CA ?? process.env.TLS_CA);
|
|
28
|
+
|
|
29
|
+
// Passphrase is read directly from env variables
|
|
30
|
+
const passphrase = process.env.TLS_REQUEST_KEY_PASSPHRASE ?? process.env.TLS_SERVER_KEY_PASSPHRASE;
|
|
25
31
|
exports.tlsConfig = tlsConfig = {
|
|
26
|
-
key
|
|
27
|
-
cert
|
|
28
|
-
ca
|
|
29
|
-
passphrase
|
|
32
|
+
key,
|
|
33
|
+
cert,
|
|
34
|
+
ca,
|
|
35
|
+
passphrase
|
|
30
36
|
};
|
|
31
37
|
exports.httpsAgent = httpsAgent = new _https.default.Agent({
|
|
32
38
|
key: tlsConfig.key,
|
|
@@ -35,7 +41,7 @@ function loadTlsConfig() {
|
|
|
35
41
|
passphrase: tlsConfig.passphrase
|
|
36
42
|
});
|
|
37
43
|
}
|
|
38
|
-
if (!tlsConfig || !(tlsConfig.
|
|
44
|
+
if (!tlsConfig || !(tlsConfig.key && tlsConfig.cert && tlsConfig.ca)) {
|
|
39
45
|
throw new _EngineError.EngineError({
|
|
40
46
|
message: 'TLS environment variables are not set correctly'
|
|
41
47
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tlsConfig.js","names":["
|
|
1
|
+
{"version":3,"file":"tlsConfig.js","names":["_https","_interopRequireDefault","require","_tlsConfig","_EngineError","_","tlsConfig","exports","httpsAgent","loadTlsConfig","validateTlsConfig","undefined","process","env","TLS_REQUEST_KEY","TLS_SERVER_KEY","key","getSecretOrFile","cert","TLS_REQUEST_CERT","TLS_SERVER_CERT","ca","TLS_REQUEST_CA","TLS_CA","passphrase","TLS_REQUEST_KEY_PASSPHRASE","TLS_SERVER_KEY_PASSPHRASE","https","Agent","EngineError","message"],"sources":["../../../src/utils/tlsConfig/tlsConfig.ts"],"sourcesContent":["import https, { Agent } from 'https';\nimport { TlsConfig } from './tlsConfig.types';\nimport { validateTlsConfig } from './tlsConfig.validate';\nimport { EngineError } from 'entities/EngineError';\nimport { getSecretOrFile } from 'utils';\n\nexport let tlsConfig: undefined | TlsConfig;\nexport let httpsAgent: undefined | Agent;\n\n/**\n * Load TLS config by its environment setting (local files or GCP secrets)\n */\nexport function loadTlsConfig(): void {\n validateTlsConfig();\n tlsConfig = undefined;\n\n if (process.env.TLS_REQUEST_KEY || process.env.TLS_SERVER_KEY) {\n // Fetch TLS secrets using unified utility\n const key = getSecretOrFile(\n process.env.TLS_REQUEST_KEY ?? process.env.TLS_SERVER_KEY!\n );\n const cert = getSecretOrFile(\n process.env.TLS_REQUEST_CERT ?? process.env.TLS_SERVER_CERT!\n );\n const ca = getSecretOrFile(\n process.env.TLS_REQUEST_CA ?? process.env.TLS_CA!\n );\n\n // Passphrase is read directly from env variables\n const passphrase =\n process.env.TLS_REQUEST_KEY_PASSPHRASE ??\n process.env.TLS_SERVER_KEY_PASSPHRASE;\n\n tlsConfig = { key, cert, ca, passphrase } as TlsConfig;\n\n httpsAgent = new https.Agent({\n key: tlsConfig.key,\n cert: tlsConfig.cert,\n ca: tlsConfig.ca,\n passphrase: tlsConfig.passphrase\n });\n }\n\n if (!tlsConfig || !(tlsConfig.key && tlsConfig.cert && tlsConfig.ca)) {\n throw new EngineError({\n message: 'TLS environment variables are not set correctly'\n });\n }\n}\n"],"mappings":";;;;;;;;;AAAA,IAAAA,MAAA,GAAAC,sBAAA,CAAAC,OAAA;AAEA,IAAAC,UAAA,GAAAD,OAAA;AACA,IAAAE,YAAA,GAAAF,OAAA;AACA,IAAAG,CAAA,GAAAH,OAAA;AAEO,IAAII,SAAgC,GAAAC,OAAA,CAAAD,SAAA;AACpC,IAAIE,UAA6B,GAAAD,OAAA,CAAAC,UAAA;;AAExC;AACA;AACA;AACO,SAASC,aAAaA,CAAA,EAAS;EACpC,IAAAC,4BAAiB,EAAC,CAAC;EACnBH,OAAA,CAAAD,SAAA,GAAAA,SAAS,GAAGK,SAAS;EAErB,IAAIC,OAAO,CAACC,GAAG,CAACC,eAAe,IAAIF,OAAO,CAACC,GAAG,CAACE,cAAc,EAAE;IAC7D;IACA,MAAMC,GAAG,GAAG,IAAAC,iBAAe,EACzBL,OAAO,CAACC,GAAG,CAACC,eAAe,IAAIF,OAAO,CAACC,GAAG,CAACE,cAC7C,CAAC;IACD,MAAMG,IAAI,GAAG,IAAAD,iBAAe,EAC1BL,OAAO,CAACC,GAAG,CAACM,gBAAgB,IAAIP,OAAO,CAACC,GAAG,CAACO,eAC9C,CAAC;IACD,MAAMC,EAAE,GAAG,IAAAJ,iBAAe,EACxBL,OAAO,CAACC,GAAG,CAACS,cAAc,IAAIV,OAAO,CAACC,GAAG,CAACU,MAC5C,CAAC;;IAED;IACA,MAAMC,UAAU,GACdZ,OAAO,CAACC,GAAG,CAACY,0BAA0B,IACtCb,OAAO,CAACC,GAAG,CAACa,yBAAyB;IAEvCnB,OAAA,CAAAD,SAAA,GAAAA,SAAS,GAAG;MAAEU,GAAG;MAAEE,IAAI;MAAEG,EAAE;MAAEG;IAAW,CAAc;IAEtDjB,OAAA,CAAAC,UAAA,GAAAA,UAAU,GAAG,IAAImB,cAAK,CAACC,KAAK,CAAC;MAC3BZ,GAAG,EAAEV,SAAS,CAACU,GAAG;MAClBE,IAAI,EAAEZ,SAAS,CAACY,IAAI;MACpBG,EAAE,EAAEf,SAAS,CAACe,EAAE;MAChBG,UAAU,EAAElB,SAAS,CAACkB;IACxB,CAAC,CAAC;EACJ;EAEA,IAAI,CAAClB,SAAS,IAAI,EAAEA,SAAS,CAACU,GAAG,IAAIV,SAAS,CAACY,IAAI,IAAIZ,SAAS,CAACe,EAAE,CAAC,EAAE;IACpE,MAAM,IAAIQ,wBAAW,CAAC;MACpBC,OAAO,EAAE;IACX,CAAC,CAAC;EACJ;AACF","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "node-server-engine",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.4",
|
|
4
4
|
"description": "Framework used to develop backend services. This package ships with a lot of features to standardize the creation of services, letting you focus on the business logic.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
"@babel/runtime": "^7.26.0",
|
|
42
42
|
"@elastic/elasticsearch": "^8.17.0",
|
|
43
43
|
"@google-cloud/pubsub": "^4.9.0",
|
|
44
|
+
"@google-cloud/secret-manager": "^6.1.0",
|
|
44
45
|
"@google-cloud/storage": "^7.14.0",
|
|
45
46
|
"@google/generative-ai": "^0.21.0",
|
|
46
47
|
"ajv": "^8.17.1",
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
1
|
import http from 'http';
|
|
3
2
|
import https from 'https';
|
|
4
3
|
import { AddressInfo } from 'net';
|
|
5
|
-
import path from 'path';
|
|
6
4
|
import { setTimeout as setTimeoutPromise } from 'timers/promises';
|
|
7
5
|
import { ValidatorSpec } from 'envalid';
|
|
8
6
|
import express, { Application } from 'express';
|
|
@@ -203,8 +201,8 @@ export class Server {
|
|
|
203
201
|
|
|
204
202
|
this.handleSignal();
|
|
205
203
|
|
|
206
|
-
//
|
|
207
|
-
this.
|
|
204
|
+
// Load TSL config files
|
|
205
|
+
this.loadTls();
|
|
208
206
|
}
|
|
209
207
|
|
|
210
208
|
/** Graceful shutdown */
|
|
@@ -344,27 +342,11 @@ export class Server {
|
|
|
344
342
|
);
|
|
345
343
|
}
|
|
346
344
|
|
|
347
|
-
/**
|
|
348
|
-
private
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
'TLS_REQUEST_CA',
|
|
353
|
-
'TLS_SERVER_KEY',
|
|
354
|
-
'TLS_SERVER_CERT',
|
|
355
|
-
'TLS_CA'
|
|
356
|
-
].forEach((key) => {
|
|
357
|
-
const file = process.env[key];
|
|
358
|
-
if (file)
|
|
359
|
-
fs.watch(path.resolve(file), (event) => {
|
|
360
|
-
if (event !== 'change') return;
|
|
361
|
-
reportInfo(`Reloading TLS config [${file} changed]`);
|
|
362
|
-
loadTlsConfig();
|
|
363
|
-
(this.httpServer as https.Server).setSecureContext(
|
|
364
|
-
tlsConfig as TlsConfig
|
|
365
|
-
);
|
|
366
|
-
});
|
|
367
|
-
});
|
|
345
|
+
/** Load TLS Config */
|
|
346
|
+
private loadTls(): void {
|
|
347
|
+
reportInfo(`Loading TLS config`);
|
|
348
|
+
loadTlsConfig();
|
|
349
|
+
(this.httpServer as https.Server).setSecureContext(tlsConfig as TlsConfig);
|
|
368
350
|
}
|
|
369
351
|
|
|
370
352
|
/** Executes a recurring job */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Request, Response, NextFunction } from 'express';
|
|
2
2
|
import { EngineError } from 'entities/EngineError';
|
|
3
3
|
import { reportDebug } from 'utils';
|
|
4
|
+
import { tlsRequest } from 'utils/tlsRequest';
|
|
4
5
|
|
|
5
6
|
const namespace = 'engine:middleware:userResolver';
|
|
6
7
|
|
|
@@ -15,7 +16,7 @@ export async function userResolver(
|
|
|
15
16
|
throw new EngineError({
|
|
16
17
|
message: 'Could not resolve user as the request is missing his ID'
|
|
17
18
|
});
|
|
18
|
-
const user =
|
|
19
|
+
const user = await getUserById(id);
|
|
19
20
|
if (!user)
|
|
20
21
|
throw new EngineError({
|
|
21
22
|
message: 'Could not resolve user as it could not be fetched',
|
|
@@ -33,3 +34,20 @@ export async function userResolver(
|
|
|
33
34
|
|
|
34
35
|
next();
|
|
35
36
|
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Call the GET /user/{id} endpoint using TLS
|
|
40
|
+
*/
|
|
41
|
+
export async function getUserById(userId: number): Promise<unknown> {
|
|
42
|
+
const userServiceUrl = process.env.USER_SERVICE_URL;
|
|
43
|
+
if (!userServiceUrl) {
|
|
44
|
+
throw new Error('USER_SERVICE_URL is not defined in environment variables');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const response = await tlsRequest<unknown>({
|
|
48
|
+
method: 'GET',
|
|
49
|
+
url: `${userServiceUrl}/${userId}`
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
return response.data;
|
|
53
|
+
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
1
|
import { createServer, RequestListener, Server } from 'http';
|
|
3
2
|
import {
|
|
4
3
|
createServer as createSecureServer,
|
|
@@ -6,23 +5,29 @@ import {
|
|
|
6
5
|
} from 'https';
|
|
7
6
|
import { Application } from 'express';
|
|
8
7
|
import { validateHttpEnvironment } from './createHttpServer.validate';
|
|
8
|
+
import { getSecretOrFile } from 'utils';
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Create and configure an HTTP/HTTPS server.
|
|
12
|
+
* Supports local files in dev and GCP secrets in prod.
|
|
13
|
+
*/
|
|
14
|
+
export const createHttpServer = (
|
|
12
15
|
handler?: Application | RequestListener
|
|
13
|
-
): Server | SecureServer {
|
|
14
|
-
// Validate the environment variables
|
|
16
|
+
): Server | SecureServer => {
|
|
15
17
|
validateHttpEnvironment();
|
|
16
|
-
|
|
18
|
+
|
|
17
19
|
if (process.env.TLS_SERVER_KEY && process.env.TLS_SERVER_CERT) {
|
|
20
|
+
const key = getSecretOrFile('TLS_SERVER_KEY');
|
|
21
|
+
const cert = getSecretOrFile('TLS_SERVER_CERT');
|
|
22
|
+
const ca = process.env.TLS_CA ? getSecretOrFile('TLS_CA') : undefined;
|
|
23
|
+
const passphrase = process.env.TLS_SERVER_KEY_PASSPHRASE;
|
|
24
|
+
|
|
18
25
|
return createSecureServer(
|
|
19
26
|
{
|
|
20
|
-
key
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
? fs.readFileSync(process.env.TLS_CA)
|
|
25
|
-
: undefined,
|
|
27
|
+
key,
|
|
28
|
+
cert,
|
|
29
|
+
ca,
|
|
30
|
+
passphrase,
|
|
26
31
|
requestCert: true,
|
|
27
32
|
rejectUnauthorized: false
|
|
28
33
|
},
|
|
@@ -31,4 +36,4 @@ export function createHttpServer(
|
|
|
31
36
|
} else {
|
|
32
37
|
return createServer(handler);
|
|
33
38
|
}
|
|
34
|
-
}
|
|
39
|
+
};
|
package/src/utils/index.ts
CHANGED
package/src/utils/jwt/jwt.ts
CHANGED
|
@@ -6,7 +6,6 @@ import {
|
|
|
6
6
|
VerifyOptions,
|
|
7
7
|
sign
|
|
8
8
|
} from 'jsonwebtoken';
|
|
9
|
-
import fs from 'fs';
|
|
10
9
|
import { UserTokenPayload } from './jwt.types';
|
|
11
10
|
import { TokenIssuer } from 'const';
|
|
12
11
|
import { EngineError } from 'entities/EngineError';
|
|
@@ -15,6 +14,7 @@ import { WebError } from 'entities/WebError';
|
|
|
15
14
|
import { assertEnvironment } from 'utils/checkEnvironment';
|
|
16
15
|
import { envAssert } from 'utils/envAssert';
|
|
17
16
|
import { reportDebug } from 'utils/report';
|
|
17
|
+
import { getSecretOrFile } from 'utils';
|
|
18
18
|
|
|
19
19
|
export let keySet: KeySet | undefined;
|
|
20
20
|
|
|
@@ -125,7 +125,8 @@ export async function jwtVerify(
|
|
|
125
125
|
}
|
|
126
126
|
assertEnvironment({
|
|
127
127
|
ACCESS_TOKEN_AUDIENCE: envAssert.isString(),
|
|
128
|
-
ACCESS_TOKEN_ISSUER: envAssert.isString()
|
|
128
|
+
ACCESS_TOKEN_ISSUER: envAssert.isString(),
|
|
129
|
+
ACCESS_TOKEN_EXPIRATION_TIME: envAssert.isString()
|
|
129
130
|
});
|
|
130
131
|
let options: VerifyOptions | undefined;
|
|
131
132
|
|
|
@@ -165,16 +166,16 @@ export async function jwtVerify(
|
|
|
165
166
|
export function generateJwtToken(user: unknown) {
|
|
166
167
|
const signOptions = {
|
|
167
168
|
algorithm: 'ES256' as const, // or 'ES256' as Algorithm
|
|
168
|
-
expiresIn:
|
|
169
|
+
expiresIn: process.env.ACCESS_TOKEN_EXPIRATION_TIME
|
|
170
|
+
? parseInt(process.env.ACCESS_TOKEN_EXPIRATION_TIME, 10)
|
|
171
|
+
: undefined,
|
|
169
172
|
issuer: process.env.ACCESS_TOKEN_ISSUER,
|
|
170
173
|
audience: process.env.ACCESS_TOKEN_AUDIENCE,
|
|
171
174
|
keyid: Object.keys(keySet?.getKeys() ?? {})[0]
|
|
172
175
|
};
|
|
173
176
|
|
|
174
|
-
const privateKey =
|
|
175
|
-
|
|
176
|
-
'utf8'
|
|
177
|
-
);
|
|
177
|
+
const privateKey = getSecretOrFile('PRIVATE_KEY_PATH');
|
|
178
|
+
|
|
178
179
|
const token = sign({ user }, privateKey, signOptions);
|
|
179
180
|
|
|
180
181
|
return token;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
|
|
2
|
+
import { EngineError } from 'entities/EngineError';
|
|
3
|
+
|
|
4
|
+
// Cache to store preloaded secrets
|
|
5
|
+
export const secretCache: { [key: string]: string } = {};
|
|
6
|
+
let secretClient: SecretManagerServiceClient;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Initialize the Secret Manager client and preload secrets.
|
|
10
|
+
* @param secretEnvVars List of env var names that contain secret paths
|
|
11
|
+
*/
|
|
12
|
+
export const initializeSecrets = async (
|
|
13
|
+
secretEnvVars: string[]
|
|
14
|
+
): Promise<void> => {
|
|
15
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
16
|
+
console.log('Development mode → using local files for secrets');
|
|
17
|
+
return; // No need to preload anything
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
// Default creds (for GCP runtime)
|
|
22
|
+
secretClient = new SecretManagerServiceClient();
|
|
23
|
+
|
|
24
|
+
const secretNames = secretEnvVars.filter((name) => process.env[name]);
|
|
25
|
+
if (secretNames.length === 0) {
|
|
26
|
+
throw new EngineError({
|
|
27
|
+
message: 'No secret names defined in environment variables'
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Fetch all secrets from GCP Secret Manager
|
|
32
|
+
for (const name of secretNames) {
|
|
33
|
+
const secretPath = process.env[name];
|
|
34
|
+
if (!secretPath) {
|
|
35
|
+
throw new EngineError({
|
|
36
|
+
message: `Secret path for ${name} is not defined`
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const [version] = await secretClient.accessSecretVersion({
|
|
42
|
+
name: secretPath
|
|
43
|
+
});
|
|
44
|
+
const payload = version.payload?.data?.toString('utf-8');
|
|
45
|
+
if (!payload) {
|
|
46
|
+
throw new EngineError({
|
|
47
|
+
message: `Secret ${name} not found or empty`
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
secretCache[name] = payload;
|
|
51
|
+
} catch (err) {
|
|
52
|
+
throw new EngineError({
|
|
53
|
+
message: `Error accessing secret ${name}: ${(err as Error).message}`
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} catch (err) {
|
|
58
|
+
throw new EngineError({
|
|
59
|
+
message: `Failed to initialize secrets: ${(err as Error).message}`
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import { EngineError } from 'entities/EngineError';
|
|
3
|
+
import { secretCache } from './secretLoader';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Retrieve a secret value (production) or file content (development).
|
|
7
|
+
* @param name The env var name that contains either the secret path (prod) or local file path (dev)
|
|
8
|
+
*/
|
|
9
|
+
export const getSecretOrFile = (name: string): string => {
|
|
10
|
+
if (process.env.NODE_ENV === 'production') {
|
|
11
|
+
const secretValue = secretCache[name];
|
|
12
|
+
console.log('SecrrtCj', secretCache);
|
|
13
|
+
|
|
14
|
+
if (!secretValue) {
|
|
15
|
+
throw new EngineError({
|
|
16
|
+
message: `Secret value for ${name} not found in cache`
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return secretValue;
|
|
20
|
+
} else {
|
|
21
|
+
const localPath = process.env[name];
|
|
22
|
+
if (!localPath) {
|
|
23
|
+
throw new EngineError({
|
|
24
|
+
message: `Local path for ${name} is not defined`
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
return fs.readFileSync(localPath, 'utf-8');
|
|
30
|
+
} catch (err) {
|
|
31
|
+
throw new EngineError({
|
|
32
|
+
message: `Error reading local file for ${name}: ${(err as Error).message}`
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
@@ -1,27 +1,30 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
1
|
import https, { Agent } from 'https';
|
|
3
2
|
import { validateTlsConfig } from './tlsConfig.validate';
|
|
4
3
|
import { EngineError } from 'entities/EngineError';
|
|
4
|
+
import { getSecretOrFile } from 'utils';
|
|
5
5
|
|
|
6
6
|
let httpsAgent: Agent | undefined;
|
|
7
|
-
|
|
8
7
|
export function getHttpsAgent(): Agent {
|
|
9
8
|
if (httpsAgent) return httpsAgent;
|
|
10
9
|
|
|
11
10
|
validateTlsConfig();
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
'
|
|
16
|
-
);
|
|
17
|
-
const cert = fs.readFileSync(
|
|
18
|
-
process.env.TLS_REQUEST_CERT ?? process.env.TLS_SERVER_CERT!,
|
|
19
|
-
'utf-8'
|
|
12
|
+
// Always pass env var *names*, not their values
|
|
13
|
+
const key = getSecretOrFile(
|
|
14
|
+
process.env.TLS_REQUEST_KEY ? 'TLS_REQUEST_KEY' : 'TLS_SERVER_KEY'
|
|
20
15
|
);
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
'
|
|
16
|
+
|
|
17
|
+
const cert = getSecretOrFile(
|
|
18
|
+
process.env.TLS_REQUEST_CERT ? 'TLS_REQUEST_CERT' : 'TLS_SERVER_CERT'
|
|
24
19
|
);
|
|
20
|
+
|
|
21
|
+
// CA is optional → only load if env var is set
|
|
22
|
+
const ca = process.env.TLS_REQUEST_CA
|
|
23
|
+
? getSecretOrFile('TLS_REQUEST_CA')
|
|
24
|
+
: process.env.TLS_CA
|
|
25
|
+
? getSecretOrFile('TLS_CA')
|
|
26
|
+
: undefined;
|
|
27
|
+
|
|
25
28
|
const passphrase =
|
|
26
29
|
process.env.TLS_REQUEST_KEY_PASSPHRASE ??
|
|
27
30
|
process.env.TLS_SERVER_KEY_PASSPHRASE;
|
|
@@ -32,6 +35,12 @@ export function getHttpsAgent(): Agent {
|
|
|
32
35
|
});
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
httpsAgent = new https.Agent({
|
|
38
|
+
httpsAgent = new https.Agent({
|
|
39
|
+
key,
|
|
40
|
+
cert,
|
|
41
|
+
ca,
|
|
42
|
+
passphrase
|
|
43
|
+
});
|
|
44
|
+
|
|
36
45
|
return httpsAgent;
|
|
37
46
|
}
|