@ciq-dev/neoiq-foundation-node 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bootstrap.js +1 -1
- package/dist/bootstrap.mjs +1 -1
- package/dist/index.d.mts +183 -23
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +162 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +184 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +181 -23
- package/dist/index.mjs.map +1 -1
- package/dist/{tracing-CcsyQIpB.mjs → tracing-BwTyDuEv.mjs} +22 -3
- package/dist/tracing-BwTyDuEv.mjs.map +1 -0
- package/dist/{tracing-B8-Ntcll.js → tracing-CMXNnEXN.js} +33 -2
- package/dist/tracing-CMXNnEXN.js.map +1 -0
- package/package.json +5 -1
- package/dist/tracing-B8-Ntcll.js.map +0 -1
- package/dist/tracing-CcsyQIpB.mjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
const require_tracing = require('./tracing-
|
|
2
|
+
const require_tracing = require('./tracing-CMXNnEXN.js');
|
|
3
3
|
const __opentelemetry_resources = require_tracing.__toESM(require("@opentelemetry/resources"));
|
|
4
4
|
const __opentelemetry_semantic_conventions = require_tracing.__toESM(require("@opentelemetry/semantic-conventions"));
|
|
5
5
|
const __opentelemetry_api = require_tracing.__toESM(require("@opentelemetry/api"));
|
|
@@ -12,6 +12,7 @@ const crypto = require_tracing.__toESM(require("crypto"));
|
|
|
12
12
|
const axios = require_tracing.__toESM(require("axios"));
|
|
13
13
|
const axios_retry = require_tracing.__toESM(require("axios-retry"));
|
|
14
14
|
const opossum = require_tracing.__toESM(require("opossum"));
|
|
15
|
+
const node_fs_promises = require_tracing.__toESM(require("node:fs/promises"));
|
|
15
16
|
const node_crypto = require_tracing.__toESM(require("node:crypto"));
|
|
16
17
|
const node_stream = require_tracing.__toESM(require("node:stream"));
|
|
17
18
|
|
|
@@ -731,6 +732,136 @@ function createHttpClient(options) {
|
|
|
731
732
|
return client;
|
|
732
733
|
}
|
|
733
734
|
|
|
735
|
+
//#endregion
|
|
736
|
+
//#region src/features/vault.ts
|
|
737
|
+
async function loadNodeVault() {
|
|
738
|
+
try {
|
|
739
|
+
const mod = await import("node-vault");
|
|
740
|
+
return mod.default ?? mod;
|
|
741
|
+
} catch {
|
|
742
|
+
throw new Error("node-vault is not installed. Install the optional peer dependency: npm install node-vault");
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
var VaultClient = class {
|
|
746
|
+
client = null;
|
|
747
|
+
clientPromise;
|
|
748
|
+
authenticated = false;
|
|
749
|
+
cache = new Map();
|
|
750
|
+
config;
|
|
751
|
+
logger;
|
|
752
|
+
cacheEnabled;
|
|
753
|
+
cacheTtlMs;
|
|
754
|
+
constructor(options) {
|
|
755
|
+
this.config = {
|
|
756
|
+
address: "https://vault.beta.commerceiq.ai/",
|
|
757
|
+
authMethod: "kubernetes",
|
|
758
|
+
role: "",
|
|
759
|
+
mountPoint: "secret",
|
|
760
|
+
tokenPath: "/var/run/secrets/kubernetes.io/serviceaccount/token",
|
|
761
|
+
...options.config
|
|
762
|
+
};
|
|
763
|
+
this.logger = options.logger;
|
|
764
|
+
this.cacheEnabled = this.config.cache?.enabled !== false;
|
|
765
|
+
this.cacheTtlMs = (this.config.cache?.ttlSeconds ?? 300) * 1e3;
|
|
766
|
+
this.clientPromise = this.initClient();
|
|
767
|
+
}
|
|
768
|
+
async initClient() {
|
|
769
|
+
const nodeVault = await loadNodeVault();
|
|
770
|
+
const client = nodeVault({
|
|
771
|
+
apiVersion: "v1",
|
|
772
|
+
endpoint: this.config.address,
|
|
773
|
+
namespace: this.config.namespace
|
|
774
|
+
});
|
|
775
|
+
this.client = client;
|
|
776
|
+
return client;
|
|
777
|
+
}
|
|
778
|
+
async authenticate() {
|
|
779
|
+
if (this.authenticated) return;
|
|
780
|
+
const client = await this.clientPromise;
|
|
781
|
+
if (this.config.authMethod === "token") {
|
|
782
|
+
const token = process.env.VAULT_TOKEN;
|
|
783
|
+
if (!token) throw new Error("VAULT_TOKEN environment variable is required for token-based authentication");
|
|
784
|
+
client.token = token;
|
|
785
|
+
this.authenticated = true;
|
|
786
|
+
this.logger.info({ authMethod: "token" }, "Vault authenticated via token");
|
|
787
|
+
return;
|
|
788
|
+
}
|
|
789
|
+
const role = this.config.role;
|
|
790
|
+
if (!role) throw new Error("Vault role is required for Kubernetes authentication. Set vault.role in config or VAULT_AUTH_ROLE env var.");
|
|
791
|
+
let jwt;
|
|
792
|
+
try {
|
|
793
|
+
jwt = await (0, node_fs_promises.readFile)(this.config.tokenPath, "utf8");
|
|
794
|
+
} catch (err) {
|
|
795
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
796
|
+
throw new Error(`Failed to read Kubernetes service account token from ${this.config.tokenPath}: ${message}`);
|
|
797
|
+
}
|
|
798
|
+
const result = await client.kubernetesLogin({
|
|
799
|
+
role,
|
|
800
|
+
jwt
|
|
801
|
+
});
|
|
802
|
+
client.token = result.auth.client_token;
|
|
803
|
+
this.authenticated = true;
|
|
804
|
+
this.logger.info({
|
|
805
|
+
authMethod: "kubernetes",
|
|
806
|
+
role,
|
|
807
|
+
leaseDuration: result.auth.lease_duration
|
|
808
|
+
}, "Vault authenticated via Kubernetes service account");
|
|
809
|
+
}
|
|
810
|
+
async getSecret(path) {
|
|
811
|
+
const cacheKey = path;
|
|
812
|
+
if (this.cacheEnabled) {
|
|
813
|
+
const cached = this.cache.get(cacheKey);
|
|
814
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
815
|
+
this.logger.debug({
|
|
816
|
+
path,
|
|
817
|
+
cache: "hit"
|
|
818
|
+
}, "Vault secret cache hit");
|
|
819
|
+
return cached.data;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
await this.authenticate();
|
|
823
|
+
const client = await this.clientPromise;
|
|
824
|
+
const fullPath = `${this.config.mountPoint}/data/${path}`;
|
|
825
|
+
this.logger.debug({ path: fullPath }, "Reading secret from Vault");
|
|
826
|
+
const response = await client.read(fullPath);
|
|
827
|
+
const data = response.data.data;
|
|
828
|
+
if (this.cacheEnabled) {
|
|
829
|
+
this.cache.set(cacheKey, {
|
|
830
|
+
data,
|
|
831
|
+
expiresAt: Date.now() + this.cacheTtlMs
|
|
832
|
+
});
|
|
833
|
+
this.logger.debug({
|
|
834
|
+
path,
|
|
835
|
+
cache: "miss",
|
|
836
|
+
ttlMs: this.cacheTtlMs
|
|
837
|
+
}, "Vault secret cached");
|
|
838
|
+
}
|
|
839
|
+
return data;
|
|
840
|
+
}
|
|
841
|
+
async getSecretValue(path, key) {
|
|
842
|
+
const secrets = await this.getSecret(path);
|
|
843
|
+
return secrets[key];
|
|
844
|
+
}
|
|
845
|
+
isAuthenticated() {
|
|
846
|
+
return this.authenticated;
|
|
847
|
+
}
|
|
848
|
+
clearCache() {
|
|
849
|
+
this.cache.clear();
|
|
850
|
+
this.logger.debug({}, "Vault secret cache cleared");
|
|
851
|
+
}
|
|
852
|
+
async checkHealth() {
|
|
853
|
+
try {
|
|
854
|
+
await this.authenticate();
|
|
855
|
+
return true;
|
|
856
|
+
} catch {
|
|
857
|
+
return false;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
function createVaultClient(options) {
|
|
862
|
+
return new VaultClient(options);
|
|
863
|
+
}
|
|
864
|
+
|
|
734
865
|
//#endregion
|
|
735
866
|
//#region src/foundation.ts
|
|
736
867
|
const deprecationWarnings = new Set();
|
|
@@ -748,11 +879,12 @@ function warnDeprecation(oldPath, newPath, logger) {
|
|
|
748
879
|
function createFoundation(input) {
|
|
749
880
|
const startTime = Date.now();
|
|
750
881
|
const config = require_tracing.parseConfig(input);
|
|
751
|
-
const { serviceName, serviceVersion, environment, features: featuresConfig, otel, logging: loggingConfig, requestLogging: requestLoggingConfig, redaction: redactionConfig, shutdown: shutdownConfig } = config;
|
|
882
|
+
const { serviceName, serviceVersion, environment, features: featuresConfig, otel, logging: loggingConfig, requestLogging: requestLoggingConfig, redaction: redactionConfig, shutdown: shutdownConfig, vault: vaultConfig } = config;
|
|
752
883
|
const features = {
|
|
753
884
|
tracing: featuresConfig.tracing ?? true,
|
|
754
885
|
metrics: featuresConfig.metrics ?? true,
|
|
755
|
-
logging: featuresConfig.logging ?? true
|
|
886
|
+
logging: featuresConfig.logging ?? true,
|
|
887
|
+
vault: vaultConfig.enabled ?? false
|
|
756
888
|
};
|
|
757
889
|
const contextManager = createContextManager();
|
|
758
890
|
let logger;
|
|
@@ -830,6 +962,22 @@ function createFoundation(input) {
|
|
|
830
962
|
metricsError = err.message;
|
|
831
963
|
logger.error({ error: metricsError }, "Metrics setup failed - continuing without metrics");
|
|
832
964
|
}
|
|
965
|
+
let vaultClient = null;
|
|
966
|
+
let vaultError;
|
|
967
|
+
if (features.vault) try {
|
|
968
|
+
vaultClient = createVaultClient({
|
|
969
|
+
config: vaultConfig,
|
|
970
|
+
logger
|
|
971
|
+
});
|
|
972
|
+
logger.info({
|
|
973
|
+
feature: "vault",
|
|
974
|
+
address: vaultConfig.address,
|
|
975
|
+
authMethod: vaultConfig.authMethod
|
|
976
|
+
}, "Vault client created (lazy auth on first secret read)");
|
|
977
|
+
} catch (err) {
|
|
978
|
+
vaultError = err.message;
|
|
979
|
+
logger.error({ error: vaultError }, "Vault setup failed - continuing without vault");
|
|
980
|
+
}
|
|
833
981
|
logger.info({
|
|
834
982
|
serviceName,
|
|
835
983
|
serviceVersion,
|
|
@@ -897,37 +1045,45 @@ function createFoundation(input) {
|
|
|
897
1045
|
...options,
|
|
898
1046
|
foundation
|
|
899
1047
|
}) };
|
|
1048
|
+
const secretsModule = vaultClient;
|
|
900
1049
|
const buildHealthStatus = () => {
|
|
901
1050
|
const tracingUp = !features.tracing || !tracingError && require_tracing.isTracingEnabled();
|
|
902
1051
|
const metricsUp = !features.metrics || !metricsError && isMetricsEnabled();
|
|
903
1052
|
const loggingUp = !loggingError;
|
|
904
|
-
const
|
|
905
|
-
const
|
|
1053
|
+
const vaultUp = !features.vault || !vaultError && !!vaultClient;
|
|
1054
|
+
const allUp = tracingUp && metricsUp && loggingUp && vaultUp;
|
|
1055
|
+
const allDown = (!tracingUp || !features.tracing) && (!metricsUp || !features.metrics) && (!vaultUp || !features.vault) && !loggingUp;
|
|
906
1056
|
let status = "healthy";
|
|
907
1057
|
if (!allUp) status = allDown ? "unhealthy" : "degraded";
|
|
1058
|
+
const components = {
|
|
1059
|
+
tracing: {
|
|
1060
|
+
enabled: features.tracing,
|
|
1061
|
+
status: !features.tracing ? "disabled" : tracingError ? "down" : "up",
|
|
1062
|
+
message: tracingError
|
|
1063
|
+
},
|
|
1064
|
+
metrics: {
|
|
1065
|
+
enabled: features.metrics,
|
|
1066
|
+
status: !features.metrics ? "disabled" : metricsError ? "down" : "up",
|
|
1067
|
+
message: metricsError
|
|
1068
|
+
},
|
|
1069
|
+
logging: {
|
|
1070
|
+
enabled: features.logging,
|
|
1071
|
+
status: loggingError ? "down" : "up",
|
|
1072
|
+
message: loggingError
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
if (features.vault) components.vault = {
|
|
1076
|
+
enabled: true,
|
|
1077
|
+
status: vaultError ? "down" : "up",
|
|
1078
|
+
message: vaultError
|
|
1079
|
+
};
|
|
908
1080
|
return {
|
|
909
1081
|
status,
|
|
910
1082
|
timestamp: new Date().toISOString(),
|
|
911
1083
|
service: serviceName,
|
|
912
1084
|
version: serviceVersion,
|
|
913
1085
|
uptime: Math.floor((Date.now() - startTime) / 1e3),
|
|
914
|
-
components
|
|
915
|
-
tracing: {
|
|
916
|
-
enabled: features.tracing,
|
|
917
|
-
status: !features.tracing ? "disabled" : tracingError ? "down" : "up",
|
|
918
|
-
message: tracingError
|
|
919
|
-
},
|
|
920
|
-
metrics: {
|
|
921
|
-
enabled: features.metrics,
|
|
922
|
-
status: !features.metrics ? "disabled" : metricsError ? "down" : "up",
|
|
923
|
-
message: metricsError
|
|
924
|
-
},
|
|
925
|
-
logging: {
|
|
926
|
-
enabled: features.logging,
|
|
927
|
-
status: loggingError ? "down" : "up",
|
|
928
|
-
message: loggingError
|
|
929
|
-
}
|
|
930
|
-
}
|
|
1086
|
+
components
|
|
931
1087
|
};
|
|
932
1088
|
};
|
|
933
1089
|
const shutdownFn = async () => {
|
|
@@ -935,6 +1091,7 @@ function createFoundation(input) {
|
|
|
935
1091
|
const promises = [];
|
|
936
1092
|
if (features.tracing && require_tracing.isTracingEnabled()) promises.push(require_tracing.shutdownTracing());
|
|
937
1093
|
if (features.metrics && isMetricsEnabled()) promises.push(shutdownMetrics());
|
|
1094
|
+
if (vaultClient) vaultClient.clearCache();
|
|
938
1095
|
await Promise.all(promises);
|
|
939
1096
|
logger.info({}, "Foundation shutdown complete");
|
|
940
1097
|
};
|
|
@@ -967,6 +1124,7 @@ function createFoundation(input) {
|
|
|
967
1124
|
observability,
|
|
968
1125
|
http: httpModule,
|
|
969
1126
|
lifecycle,
|
|
1127
|
+
secrets: secretsModule,
|
|
970
1128
|
logger,
|
|
971
1129
|
context: contextManager,
|
|
972
1130
|
fastifyPlugin: createObservabilityPlugin({
|
|
@@ -1324,6 +1482,9 @@ exports.RedactionConfigSchema = require_tracing.RedactionConfigSchema
|
|
|
1324
1482
|
exports.RequestLoggingConfigSchema = require_tracing.RequestLoggingConfigSchema
|
|
1325
1483
|
exports.ShutdownConfigSchema = require_tracing.ShutdownConfigSchema
|
|
1326
1484
|
exports.SpanStatusCode = __opentelemetry_api.SpanStatusCode
|
|
1485
|
+
exports.VaultCacheConfigSchema = require_tracing.VaultCacheConfigSchema
|
|
1486
|
+
exports.VaultClient = VaultClient
|
|
1487
|
+
exports.VaultConfigSchema = require_tracing.VaultConfigSchema
|
|
1327
1488
|
exports.buildPinoRedactConfig = buildPinoRedactConfig
|
|
1328
1489
|
exports.context = __opentelemetry_api.context
|
|
1329
1490
|
exports.createContextManager = createContextManager
|
|
@@ -1332,6 +1493,7 @@ exports.createFoundation = createFoundation
|
|
|
1332
1493
|
exports.createHttpClient = createHttpClient
|
|
1333
1494
|
exports.createLogger = createLogger
|
|
1334
1495
|
exports.createObservabilityPlugin = createObservabilityPlugin
|
|
1496
|
+
exports.createVaultClient = createVaultClient
|
|
1335
1497
|
exports.getActiveSpan = require_tracing.getActiveSpan
|
|
1336
1498
|
exports.getDefaultOtelEndpoint = require_tracing.getDefaultOtelEndpoint
|
|
1337
1499
|
exports.getGlobalLogger = getGlobalLogger
|