@onlineapps/service-wrapper 3.0.17 → 3.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/package.json +3 -2
- package/src/ContextBuilder.js +7 -1
- package/src/ServiceWrapper.js +67 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/service-wrapper",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Thin orchestration layer for microservices - delegates all infrastructure concerns to specialized connectors",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -29,8 +29,9 @@
|
|
|
29
29
|
"@onlineapps/conn-base-state": "1.0.1",
|
|
30
30
|
"@onlineapps/conn-infra-error-handler": "1.0.11",
|
|
31
31
|
"@onlineapps/conn-infra-mq": "1.1.70",
|
|
32
|
+
"@onlineapps/conn-infra-secrets": "1.0.0",
|
|
32
33
|
"@onlineapps/conn-orch-cookbook": "2.1.3",
|
|
33
|
-
"@onlineapps/conn-orch-orchestrator": "2.0
|
|
34
|
+
"@onlineapps/conn-orch-orchestrator": "2.1.0",
|
|
34
35
|
"@onlineapps/conn-orch-registry": "1.2.1",
|
|
35
36
|
"@onlineapps/conn-orch-validator": "3.1.2",
|
|
36
37
|
"@onlineapps/monitoring-core": "1.0.23",
|
package/src/ContextBuilder.js
CHANGED
|
@@ -92,6 +92,9 @@ class ContextBuilder {
|
|
|
92
92
|
const httpClient = this._connectors.http;
|
|
93
93
|
const secretsAdapter = this._connectors.secrets;
|
|
94
94
|
const secrets = {
|
|
95
|
+
// Handlers call ctx.secrets.get(ref); the facade injects the invocation
|
|
96
|
+
// scope so the connector can resolve the tenant/workspace projection
|
|
97
|
+
// (see api/docs/architecture/secretbox.md §6).
|
|
95
98
|
get: (name) => {
|
|
96
99
|
if (!secretsAdapter || typeof secretsAdapter.get !== 'function') {
|
|
97
100
|
throw new Error(
|
|
@@ -99,7 +102,10 @@ class ContextBuilder {
|
|
|
99
102
|
'wire a secrets connector before using ctx.secrets'
|
|
100
103
|
);
|
|
101
104
|
}
|
|
102
|
-
return secretsAdapter.get(name
|
|
105
|
+
return secretsAdapter.get(name, {
|
|
106
|
+
tenant_id: envelope.tenant_id ?? null,
|
|
107
|
+
workspace_id: envelope.workspace_id ?? null
|
|
108
|
+
});
|
|
103
109
|
}
|
|
104
110
|
};
|
|
105
111
|
|
package/src/ServiceWrapper.js
CHANGED
|
@@ -20,6 +20,7 @@ const OrchestratorConnector = require('@onlineapps/conn-orch-orchestrator');
|
|
|
20
20
|
const CookbookConnector = require('@onlineapps/conn-orch-cookbook');
|
|
21
21
|
const CacheConnector = require('@onlineapps/conn-base-cache');
|
|
22
22
|
const StateConnector = require('@onlineapps/conn-base-state');
|
|
23
|
+
const SecretsConnector = require('@onlineapps/conn-infra-secrets');
|
|
23
24
|
const ErrorHandlerConnector = require('@onlineapps/conn-infra-error-handler');
|
|
24
25
|
const { ValidationOrchestrator } = require('@onlineapps/conn-orch-validator');
|
|
25
26
|
const runtimeCfg = require('./config');
|
|
@@ -146,6 +147,7 @@ class ServiceWrapper {
|
|
|
146
147
|
this.registryClient = null;
|
|
147
148
|
this.orchestrator = null;
|
|
148
149
|
this.cacheConnector = null;
|
|
150
|
+
this.secretsConnector = null;
|
|
149
151
|
this.monitoring = null;
|
|
150
152
|
this.logger = null;
|
|
151
153
|
|
|
@@ -734,6 +736,15 @@ class ServiceWrapper {
|
|
|
734
736
|
}
|
|
735
737
|
}
|
|
736
738
|
|
|
739
|
+
if (this.secretsConnector) {
|
|
740
|
+
try {
|
|
741
|
+
await this.secretsConnector.disconnect();
|
|
742
|
+
console.log(`[CLEANUP] ✓ Closed Redis secrets connection`);
|
|
743
|
+
} catch (err) {
|
|
744
|
+
console.warn(`[CLEANUP] Failed to close Redis secrets connection:`, err.message);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
737
748
|
// 5. Clear state variables
|
|
738
749
|
this.workflowInitConsumerTag = null;
|
|
739
750
|
this.workflowControlConsumerTag = null;
|
|
@@ -897,6 +908,12 @@ class ServiceWrapper {
|
|
|
897
908
|
await this._initializeState();
|
|
898
909
|
}
|
|
899
910
|
|
|
911
|
+
// Initialize secrets connector if configured (SecretBox resolution for
|
|
912
|
+
// ctx.secrets.get(ref)). Critical when enabled — let it throw.
|
|
913
|
+
if (this.config.wrapper?.secrets?.enabled === true) {
|
|
914
|
+
await this._initializeSecrets();
|
|
915
|
+
}
|
|
916
|
+
|
|
900
917
|
// v3 handler-registry pipeline (RFC §5.9). Replaces the v2 HTTP-loopback
|
|
901
918
|
// invocation path. Must run AFTER connectors are instantiated (storage,
|
|
902
919
|
// cache, monitoring, mq) and BEFORE MQ messages are dispatched.
|
|
@@ -1463,6 +1480,32 @@ class ServiceWrapper {
|
|
|
1463
1480
|
this.logger?.info('State connector initialized');
|
|
1464
1481
|
}
|
|
1465
1482
|
|
|
1483
|
+
/**
|
|
1484
|
+
* Initialize secrets connector (SecretBox). Resolves ctx.secrets.get(ref) by
|
|
1485
|
+
* reading the encrypted Redis projection written by api_secrets and decrypting
|
|
1486
|
+
* in-process. Redis reuses wrapper.cache.url (shared api_node_cache); the AES
|
|
1487
|
+
* master key is required from env (no fallback).
|
|
1488
|
+
* @private
|
|
1489
|
+
*/
|
|
1490
|
+
async _initializeSecrets() {
|
|
1491
|
+
const { requireEnv } = require('@onlineapps/service-common');
|
|
1492
|
+
const secretsConfig = this.config.wrapper?.secrets || {};
|
|
1493
|
+
const redisUrl = secretsConfig.url || this.config.wrapper?.cache?.url
|
|
1494
|
+
|| requireEnv('REDIS_URL', 'Redis connection URL for secrets projection');
|
|
1495
|
+
const masterKeyBase64 = secretsConfig.masterKey
|
|
1496
|
+
|| requireEnv('SECRETS_MASTER_KEY', 'base64-encoded 32-byte AES-256-GCM master key for secret resolution');
|
|
1497
|
+
|
|
1498
|
+
this.secretsConnector = new SecretsConnector({
|
|
1499
|
+
redisUrl,
|
|
1500
|
+
masterKeyBase64,
|
|
1501
|
+
password: secretsConfig.password,
|
|
1502
|
+
db: secretsConfig.db
|
|
1503
|
+
});
|
|
1504
|
+
|
|
1505
|
+
await this.secretsConnector.connect();
|
|
1506
|
+
this.logger?.info('Secrets connector initialized');
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1466
1509
|
/**
|
|
1467
1510
|
* Setup health check endpoint
|
|
1468
1511
|
* @private
|
|
@@ -1761,7 +1804,24 @@ class ServiceWrapper {
|
|
|
1761
1804
|
cache: this.cacheConnector,
|
|
1762
1805
|
errorHandler: errorHandler,
|
|
1763
1806
|
logger: this.logger,
|
|
1764
|
-
defaultTimeout: timeout
|
|
1807
|
+
defaultTimeout: timeout,
|
|
1808
|
+
operationMutatesLookup: (_executingServiceName, step) => {
|
|
1809
|
+
const opsMap =
|
|
1810
|
+
(this.operations && this.operations.operations) ||
|
|
1811
|
+
this.operations ||
|
|
1812
|
+
{};
|
|
1813
|
+
const opKey =
|
|
1814
|
+
step && typeof step.operation === 'string' && step.operation.length > 0
|
|
1815
|
+
? step.operation
|
|
1816
|
+
: step && typeof step.step_id === 'string'
|
|
1817
|
+
? step.step_id
|
|
1818
|
+
: '';
|
|
1819
|
+
const spec = opsMap[opKey];
|
|
1820
|
+
return {
|
|
1821
|
+
mutates: !!(spec && spec.mutates === true),
|
|
1822
|
+
resource_type: spec ? spec.resource_type : undefined
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1765
1825
|
});
|
|
1766
1826
|
|
|
1767
1827
|
this.logger?.info('Orchestrator initialized');
|
|
@@ -2270,7 +2330,7 @@ class ServiceWrapper {
|
|
|
2270
2330
|
storage: null,
|
|
2271
2331
|
cache: this.cacheConnector || null,
|
|
2272
2332
|
http: null,
|
|
2273
|
-
secrets: null,
|
|
2333
|
+
secrets: this.secretsConnector || null,
|
|
2274
2334
|
monitoring: this.monitoring || null,
|
|
2275
2335
|
mq: this.mqClient || null,
|
|
2276
2336
|
state: this.stateConnector || null
|
|
@@ -2748,6 +2808,11 @@ class ServiceWrapper {
|
|
|
2748
2808
|
await this.stateConnector.disconnect();
|
|
2749
2809
|
}
|
|
2750
2810
|
|
|
2811
|
+
// Disconnect from secrets
|
|
2812
|
+
if (this.secretsConnector) {
|
|
2813
|
+
await this.secretsConnector.disconnect();
|
|
2814
|
+
}
|
|
2815
|
+
|
|
2751
2816
|
// Disconnect from MQ
|
|
2752
2817
|
if (this.mqClient) {
|
|
2753
2818
|
await this.mqClient.disconnect();
|