@ciq-dev/neoiq-foundation-node 1.0.1-beta.6 → 1.0.2-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/dist/bootstrap.js +1 -1
- package/dist/bootstrap.mjs +1 -1
- package/dist/index.d.mts +162 -2
- 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.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AutoInstrumentationConfigSchema, FeaturesConfigSchema, FoundationConfigSchema, LoggingConfigSchema, OtelConfigSchema, RedactionConfigSchema, RequestLoggingConfigSchema, ShutdownConfigSchema, SpanStatusCode, context, getActiveSpan, getDefaultOtelEndpoint, getTraceContext, getTracer, isTracingEnabled, parseConfig, propagation, setupTracing, shutdownTracing, trace } from "./tracing-
|
|
1
|
+
import { AutoInstrumentationConfigSchema, FeaturesConfigSchema, FoundationConfigSchema, LoggingConfigSchema, OtelConfigSchema, RedactionConfigSchema, RequestLoggingConfigSchema, ShutdownConfigSchema, SpanStatusCode, VaultCacheConfigSchema, VaultConfigSchema, context, getActiveSpan, getDefaultOtelEndpoint, getTraceContext, getTracer, isTracingEnabled, parseConfig, propagation, setupTracing, shutdownTracing, trace } from "./tracing-BwTyDuEv.mjs";
|
|
2
2
|
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
3
3
|
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from "@opentelemetry/semantic-conventions";
|
|
4
4
|
import { SpanStatusCode as SpanStatusCode$1, context as context$1, metrics, propagation as propagation$1, trace as trace$1 } from "@opentelemetry/api";
|
|
@@ -11,6 +11,7 @@ import { randomUUID } from "crypto";
|
|
|
11
11
|
import axios from "axios";
|
|
12
12
|
import axiosRetry from "axios-retry";
|
|
13
13
|
import CircuitBreaker from "opossum";
|
|
14
|
+
import { readFile } from "node:fs/promises";
|
|
14
15
|
import { createHash } from "node:crypto";
|
|
15
16
|
import { Readable } from "node:stream";
|
|
16
17
|
|
|
@@ -730,6 +731,136 @@ function createHttpClient(options) {
|
|
|
730
731
|
return client;
|
|
731
732
|
}
|
|
732
733
|
|
|
734
|
+
//#endregion
|
|
735
|
+
//#region src/features/vault.ts
|
|
736
|
+
async function loadNodeVault() {
|
|
737
|
+
try {
|
|
738
|
+
const mod = await import("node-vault");
|
|
739
|
+
return mod.default ?? mod;
|
|
740
|
+
} catch {
|
|
741
|
+
throw new Error("node-vault is not installed. Install the optional peer dependency: npm install node-vault");
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
var VaultClient = class {
|
|
745
|
+
client = null;
|
|
746
|
+
clientPromise;
|
|
747
|
+
authenticated = false;
|
|
748
|
+
cache = new Map();
|
|
749
|
+
config;
|
|
750
|
+
logger;
|
|
751
|
+
cacheEnabled;
|
|
752
|
+
cacheTtlMs;
|
|
753
|
+
constructor(options) {
|
|
754
|
+
this.config = {
|
|
755
|
+
address: "https://vault.beta.commerceiq.ai/",
|
|
756
|
+
authMethod: "kubernetes",
|
|
757
|
+
role: "",
|
|
758
|
+
mountPoint: "secret",
|
|
759
|
+
tokenPath: "/var/run/secrets/kubernetes.io/serviceaccount/token",
|
|
760
|
+
...options.config
|
|
761
|
+
};
|
|
762
|
+
this.logger = options.logger;
|
|
763
|
+
this.cacheEnabled = this.config.cache?.enabled !== false;
|
|
764
|
+
this.cacheTtlMs = (this.config.cache?.ttlSeconds ?? 300) * 1e3;
|
|
765
|
+
this.clientPromise = this.initClient();
|
|
766
|
+
}
|
|
767
|
+
async initClient() {
|
|
768
|
+
const nodeVault = await loadNodeVault();
|
|
769
|
+
const client = nodeVault({
|
|
770
|
+
apiVersion: "v1",
|
|
771
|
+
endpoint: this.config.address,
|
|
772
|
+
namespace: this.config.namespace
|
|
773
|
+
});
|
|
774
|
+
this.client = client;
|
|
775
|
+
return client;
|
|
776
|
+
}
|
|
777
|
+
async authenticate() {
|
|
778
|
+
if (this.authenticated) return;
|
|
779
|
+
const client = await this.clientPromise;
|
|
780
|
+
if (this.config.authMethod === "token") {
|
|
781
|
+
const token = process.env.VAULT_TOKEN;
|
|
782
|
+
if (!token) throw new Error("VAULT_TOKEN environment variable is required for token-based authentication");
|
|
783
|
+
client.token = token;
|
|
784
|
+
this.authenticated = true;
|
|
785
|
+
this.logger.info({ authMethod: "token" }, "Vault authenticated via token");
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
const role = this.config.role;
|
|
789
|
+
if (!role) throw new Error("Vault role is required for Kubernetes authentication. Set vault.role in config or VAULT_AUTH_ROLE env var.");
|
|
790
|
+
let jwt;
|
|
791
|
+
try {
|
|
792
|
+
jwt = await readFile(this.config.tokenPath, "utf8");
|
|
793
|
+
} catch (err) {
|
|
794
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
795
|
+
throw new Error(`Failed to read Kubernetes service account token from ${this.config.tokenPath}: ${message}`);
|
|
796
|
+
}
|
|
797
|
+
const result = await client.kubernetesLogin({
|
|
798
|
+
role,
|
|
799
|
+
jwt
|
|
800
|
+
});
|
|
801
|
+
client.token = result.auth.client_token;
|
|
802
|
+
this.authenticated = true;
|
|
803
|
+
this.logger.info({
|
|
804
|
+
authMethod: "kubernetes",
|
|
805
|
+
role,
|
|
806
|
+
leaseDuration: result.auth.lease_duration
|
|
807
|
+
}, "Vault authenticated via Kubernetes service account");
|
|
808
|
+
}
|
|
809
|
+
async getSecret(path) {
|
|
810
|
+
const cacheKey = path;
|
|
811
|
+
if (this.cacheEnabled) {
|
|
812
|
+
const cached = this.cache.get(cacheKey);
|
|
813
|
+
if (cached && cached.expiresAt > Date.now()) {
|
|
814
|
+
this.logger.debug({
|
|
815
|
+
path,
|
|
816
|
+
cache: "hit"
|
|
817
|
+
}, "Vault secret cache hit");
|
|
818
|
+
return cached.data;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
await this.authenticate();
|
|
822
|
+
const client = await this.clientPromise;
|
|
823
|
+
const fullPath = `${this.config.mountPoint}/data/${path}`;
|
|
824
|
+
this.logger.debug({ path: fullPath }, "Reading secret from Vault");
|
|
825
|
+
const response = await client.read(fullPath);
|
|
826
|
+
const data = response.data.data;
|
|
827
|
+
if (this.cacheEnabled) {
|
|
828
|
+
this.cache.set(cacheKey, {
|
|
829
|
+
data,
|
|
830
|
+
expiresAt: Date.now() + this.cacheTtlMs
|
|
831
|
+
});
|
|
832
|
+
this.logger.debug({
|
|
833
|
+
path,
|
|
834
|
+
cache: "miss",
|
|
835
|
+
ttlMs: this.cacheTtlMs
|
|
836
|
+
}, "Vault secret cached");
|
|
837
|
+
}
|
|
838
|
+
return data;
|
|
839
|
+
}
|
|
840
|
+
async getSecretValue(path, key) {
|
|
841
|
+
const secrets = await this.getSecret(path);
|
|
842
|
+
return secrets[key];
|
|
843
|
+
}
|
|
844
|
+
isAuthenticated() {
|
|
845
|
+
return this.authenticated;
|
|
846
|
+
}
|
|
847
|
+
clearCache() {
|
|
848
|
+
this.cache.clear();
|
|
849
|
+
this.logger.debug({}, "Vault secret cache cleared");
|
|
850
|
+
}
|
|
851
|
+
async checkHealth() {
|
|
852
|
+
try {
|
|
853
|
+
await this.authenticate();
|
|
854
|
+
return true;
|
|
855
|
+
} catch {
|
|
856
|
+
return false;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
function createVaultClient(options) {
|
|
861
|
+
return new VaultClient(options);
|
|
862
|
+
}
|
|
863
|
+
|
|
733
864
|
//#endregion
|
|
734
865
|
//#region src/foundation.ts
|
|
735
866
|
const deprecationWarnings = new Set();
|
|
@@ -747,11 +878,12 @@ function warnDeprecation(oldPath, newPath, logger) {
|
|
|
747
878
|
function createFoundation(input) {
|
|
748
879
|
const startTime = Date.now();
|
|
749
880
|
const config = parseConfig(input);
|
|
750
|
-
const { serviceName, serviceVersion, environment, features: featuresConfig, otel, logging: loggingConfig, requestLogging: requestLoggingConfig, redaction: redactionConfig, shutdown: shutdownConfig } = config;
|
|
881
|
+
const { serviceName, serviceVersion, environment, features: featuresConfig, otel, logging: loggingConfig, requestLogging: requestLoggingConfig, redaction: redactionConfig, shutdown: shutdownConfig, vault: vaultConfig } = config;
|
|
751
882
|
const features = {
|
|
752
883
|
tracing: featuresConfig.tracing ?? true,
|
|
753
884
|
metrics: featuresConfig.metrics ?? true,
|
|
754
|
-
logging: featuresConfig.logging ?? true
|
|
885
|
+
logging: featuresConfig.logging ?? true,
|
|
886
|
+
vault: vaultConfig.enabled ?? false
|
|
755
887
|
};
|
|
756
888
|
const contextManager = createContextManager();
|
|
757
889
|
let logger;
|
|
@@ -829,6 +961,22 @@ function createFoundation(input) {
|
|
|
829
961
|
metricsError = err.message;
|
|
830
962
|
logger.error({ error: metricsError }, "Metrics setup failed - continuing without metrics");
|
|
831
963
|
}
|
|
964
|
+
let vaultClient = null;
|
|
965
|
+
let vaultError;
|
|
966
|
+
if (features.vault) try {
|
|
967
|
+
vaultClient = createVaultClient({
|
|
968
|
+
config: vaultConfig,
|
|
969
|
+
logger
|
|
970
|
+
});
|
|
971
|
+
logger.info({
|
|
972
|
+
feature: "vault",
|
|
973
|
+
address: vaultConfig.address,
|
|
974
|
+
authMethod: vaultConfig.authMethod
|
|
975
|
+
}, "Vault client created (lazy auth on first secret read)");
|
|
976
|
+
} catch (err) {
|
|
977
|
+
vaultError = err.message;
|
|
978
|
+
logger.error({ error: vaultError }, "Vault setup failed - continuing without vault");
|
|
979
|
+
}
|
|
832
980
|
logger.info({
|
|
833
981
|
serviceName,
|
|
834
982
|
serviceVersion,
|
|
@@ -896,37 +1044,45 @@ function createFoundation(input) {
|
|
|
896
1044
|
...options,
|
|
897
1045
|
foundation
|
|
898
1046
|
}) };
|
|
1047
|
+
const secretsModule = vaultClient;
|
|
899
1048
|
const buildHealthStatus = () => {
|
|
900
1049
|
const tracingUp = !features.tracing || !tracingError && isTracingEnabled();
|
|
901
1050
|
const metricsUp = !features.metrics || !metricsError && isMetricsEnabled();
|
|
902
1051
|
const loggingUp = !loggingError;
|
|
903
|
-
const
|
|
904
|
-
const
|
|
1052
|
+
const vaultUp = !features.vault || !vaultError && !!vaultClient;
|
|
1053
|
+
const allUp = tracingUp && metricsUp && loggingUp && vaultUp;
|
|
1054
|
+
const allDown = (!tracingUp || !features.tracing) && (!metricsUp || !features.metrics) && (!vaultUp || !features.vault) && !loggingUp;
|
|
905
1055
|
let status = "healthy";
|
|
906
1056
|
if (!allUp) status = allDown ? "unhealthy" : "degraded";
|
|
1057
|
+
const components = {
|
|
1058
|
+
tracing: {
|
|
1059
|
+
enabled: features.tracing,
|
|
1060
|
+
status: !features.tracing ? "disabled" : tracingError ? "down" : "up",
|
|
1061
|
+
message: tracingError
|
|
1062
|
+
},
|
|
1063
|
+
metrics: {
|
|
1064
|
+
enabled: features.metrics,
|
|
1065
|
+
status: !features.metrics ? "disabled" : metricsError ? "down" : "up",
|
|
1066
|
+
message: metricsError
|
|
1067
|
+
},
|
|
1068
|
+
logging: {
|
|
1069
|
+
enabled: features.logging,
|
|
1070
|
+
status: loggingError ? "down" : "up",
|
|
1071
|
+
message: loggingError
|
|
1072
|
+
}
|
|
1073
|
+
};
|
|
1074
|
+
if (features.vault) components.vault = {
|
|
1075
|
+
enabled: true,
|
|
1076
|
+
status: vaultError ? "down" : "up",
|
|
1077
|
+
message: vaultError
|
|
1078
|
+
};
|
|
907
1079
|
return {
|
|
908
1080
|
status,
|
|
909
1081
|
timestamp: new Date().toISOString(),
|
|
910
1082
|
service: serviceName,
|
|
911
1083
|
version: serviceVersion,
|
|
912
1084
|
uptime: Math.floor((Date.now() - startTime) / 1e3),
|
|
913
|
-
components
|
|
914
|
-
tracing: {
|
|
915
|
-
enabled: features.tracing,
|
|
916
|
-
status: !features.tracing ? "disabled" : tracingError ? "down" : "up",
|
|
917
|
-
message: tracingError
|
|
918
|
-
},
|
|
919
|
-
metrics: {
|
|
920
|
-
enabled: features.metrics,
|
|
921
|
-
status: !features.metrics ? "disabled" : metricsError ? "down" : "up",
|
|
922
|
-
message: metricsError
|
|
923
|
-
},
|
|
924
|
-
logging: {
|
|
925
|
-
enabled: features.logging,
|
|
926
|
-
status: loggingError ? "down" : "up",
|
|
927
|
-
message: loggingError
|
|
928
|
-
}
|
|
929
|
-
}
|
|
1085
|
+
components
|
|
930
1086
|
};
|
|
931
1087
|
};
|
|
932
1088
|
const shutdownFn = async () => {
|
|
@@ -934,6 +1090,7 @@ function createFoundation(input) {
|
|
|
934
1090
|
const promises = [];
|
|
935
1091
|
if (features.tracing && isTracingEnabled()) promises.push(shutdownTracing());
|
|
936
1092
|
if (features.metrics && isMetricsEnabled()) promises.push(shutdownMetrics());
|
|
1093
|
+
if (vaultClient) vaultClient.clearCache();
|
|
937
1094
|
await Promise.all(promises);
|
|
938
1095
|
logger.info({}, "Foundation shutdown complete");
|
|
939
1096
|
};
|
|
@@ -966,6 +1123,7 @@ function createFoundation(input) {
|
|
|
966
1123
|
observability,
|
|
967
1124
|
http: httpModule,
|
|
968
1125
|
lifecycle,
|
|
1126
|
+
secrets: secretsModule,
|
|
969
1127
|
logger,
|
|
970
1128
|
context: contextManager,
|
|
971
1129
|
fastifyPlugin: createObservabilityPlugin({
|
|
@@ -1311,5 +1469,5 @@ var InMemoryObjectStore = class {
|
|
|
1311
1469
|
};
|
|
1312
1470
|
|
|
1313
1471
|
//#endregion
|
|
1314
|
-
export { AutoInstrumentationConfigSchema, AwsS3ObjectStore, FeaturesConfigSchema, FoundationConfigSchema, InMemoryObjectStore, LoggingConfigSchema, OtelConfigSchema, REDACT_PATHS, RedactionConfigSchema, RequestLoggingConfigSchema, ShutdownConfigSchema, SpanStatusCode, buildPinoRedactConfig, context, createContextManager, createFallbackLogger, createFoundation, createHttpClient, createLogger, createObservabilityPlugin, getActiveSpan, getDefaultOtelEndpoint, getGlobalLogger, getMeter, getTraceContext, getTracer, isMetricsEnabled, isTracingEnabled, metrics, parseConfig, propagation, sanitizeBody, setGlobalLogger, setupMetrics, setupObservability, setupTracing, shutdownMetrics, shutdownTracing, trace };
|
|
1472
|
+
export { AutoInstrumentationConfigSchema, AwsS3ObjectStore, FeaturesConfigSchema, FoundationConfigSchema, InMemoryObjectStore, LoggingConfigSchema, OtelConfigSchema, REDACT_PATHS, RedactionConfigSchema, RequestLoggingConfigSchema, ShutdownConfigSchema, SpanStatusCode, VaultCacheConfigSchema, VaultClient, VaultConfigSchema, buildPinoRedactConfig, context, createContextManager, createFallbackLogger, createFoundation, createHttpClient, createLogger, createObservabilityPlugin, createVaultClient, getActiveSpan, getDefaultOtelEndpoint, getGlobalLogger, getMeter, getTraceContext, getTracer, isMetricsEnabled, isTracingEnabled, metrics, parseConfig, propagation, sanitizeBody, setGlobalLogger, setupMetrics, setupObservability, setupTracing, shutdownMetrics, shutdownTracing, trace };
|
|
1315
1473
|
//# sourceMappingURL=index.mjs.map
|