@ductape/sdk 0.0.4-v99 → 0.0.5
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/agents/agent-context.d.ts +3 -1
- package/dist/agents/agent-context.js +19 -3
- package/dist/agents/agent-context.js.map +1 -1
- package/dist/agents/agents.service.js +9 -5
- package/dist/agents/agents.service.js.map +1 -1
- package/dist/agents/types/agents.types.d.ts +14 -1
- package/dist/agents/types/agents.types.js.map +1 -1
- package/dist/api/services/appApi.service.js +15 -6
- package/dist/api/services/appApi.service.js.map +1 -1
- package/dist/api/services/logsApi.service.d.ts +51 -0
- package/dist/api/services/logsApi.service.js +19 -2
- package/dist/api/services/logsApi.service.js.map +1 -1
- package/dist/api/services/processorApi.service.d.ts +568 -2
- package/dist/api/services/processorApi.service.js +412 -21
- package/dist/api/services/processorApi.service.js.map +1 -1
- package/dist/api/services/productsApi.service.d.ts +20 -0
- package/dist/api/services/productsApi.service.js +44 -15
- package/dist/api/services/productsApi.service.js.map +1 -1
- package/dist/api/services/secretsApi.service.js +0 -11
- package/dist/api/services/secretsApi.service.js.map +1 -1
- package/dist/api/services/webhooksApi.service.js +13 -6
- package/dist/api/services/webhooksApi.service.js.map +1 -1
- package/dist/api/services/workflowApi.service.js +30 -12
- package/dist/api/services/workflowApi.service.js.map +1 -1
- package/dist/api/urls.d.ts +26 -0
- package/dist/api/urls.js +30 -3
- package/dist/api/urls.js.map +1 -1
- package/dist/apps/validators/joi-validators/update.appAction.validator.js +12 -1
- package/dist/apps/validators/joi-validators/update.appAction.validator.js.map +1 -1
- package/dist/brokers/brokers.service.d.ts +140 -2
- package/dist/brokers/brokers.service.js +444 -36
- package/dist/brokers/brokers.service.js.map +1 -1
- package/dist/brokers/types/index.d.ts +251 -0
- package/dist/brokers/utils/providers/rabbitmq.service.d.ts +14 -4
- package/dist/brokers/utils/providers/rabbitmq.service.js +22 -35
- package/dist/brokers/utils/providers/rabbitmq.service.js.map +1 -1
- package/dist/cache/cache.manager.d.ts +84 -5
- package/dist/cache/cache.manager.js +478 -38
- package/dist/cache/cache.manager.js.map +1 -1
- package/dist/cache/cache.service.d.ts +5 -0
- package/dist/cache/cache.service.js +163 -6
- package/dist/cache/cache.service.js.map +1 -1
- package/dist/cache/index.d.ts +1 -1
- package/dist/cache/index.js +3 -1
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/types/index.d.ts +4 -0
- package/dist/database/databases.service.d.ts +70 -2
- package/dist/database/databases.service.js +150 -35
- package/dist/database/databases.service.js.map +1 -1
- package/dist/database/types/dashboard.interface.d.ts +74 -0
- package/dist/database/types/dashboard.interface.js +7 -0
- package/dist/database/types/dashboard.interface.js.map +1 -0
- package/dist/database/types/index.d.ts +1 -0
- package/dist/graph/graphs.service.d.ts +50 -12
- package/dist/graph/graphs.service.js +689 -203
- package/dist/graph/graphs.service.js.map +1 -1
- package/dist/graph/types/connection.interface.d.ts +2 -2
- package/dist/index.d.ts +286 -4
- package/dist/index.js +370 -21
- package/dist/index.js.map +1 -1
- package/dist/logs/logs.types.d.ts +16 -3
- package/dist/logs/logs.types.js +11 -0
- package/dist/logs/logs.types.js.map +1 -1
- package/dist/notifications/notifications.service.d.ts +3 -0
- package/dist/notifications/notifications.service.js +57 -14
- package/dist/notifications/notifications.service.js.map +1 -1
- package/dist/notifications/types/notifications.types.d.ts +50 -0
- package/dist/processor/services/processor.service.d.ts +132 -8
- package/dist/processor/services/processor.service.js +1174 -267
- package/dist/processor/services/processor.service.js.map +1 -1
- package/dist/processor/utils/processor.utils.d.ts +5 -0
- package/dist/processor/utils/processor.utils.js +65 -12
- package/dist/processor/utils/processor.utils.js.map +1 -1
- package/dist/processor/utils/storage.util.js +11 -2
- package/dist/processor/utils/storage.util.js.map +1 -1
- package/dist/products/services/products.service.d.ts +19 -2
- package/dist/products/services/products.service.js +388 -54
- package/dist/products/services/products.service.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productFeature.validator.js +1 -1
- package/dist/products/validators/joi-validators/create.productJob.validator.js +2 -2
- package/dist/products/validators/joi-validators/create.productJob.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productMessageBrokerTopic.validator.js +1 -0
- package/dist/products/validators/joi-validators/create.productMessageBrokerTopic.validator.js.map +1 -1
- package/dist/products/validators/joi-validators/create.productNotification.validator.js +23 -12
- package/dist/products/validators/joi-validators/create.productNotification.validator.js.map +1 -1
- package/dist/resilience/fallback.service.d.ts +1 -0
- package/dist/resilience/fallback.service.js +2 -0
- package/dist/resilience/fallback.service.js.map +1 -1
- package/dist/resilience/quota.service.d.ts +1 -0
- package/dist/resilience/quota.service.js +2 -0
- package/dist/resilience/quota.service.js.map +1 -1
- package/dist/secrets/secrets.resolver.js +4 -1
- package/dist/secrets/secrets.resolver.js.map +1 -1
- package/dist/secrets/secrets.service.js +0 -12
- package/dist/secrets/secrets.service.js.map +1 -1
- package/dist/secrets/secrets.types.d.ts +3 -3
- package/dist/secrets/secrets.types.js +5 -5
- package/dist/secrets/secrets.types.js.map +1 -1
- package/dist/sessions/index.d.ts +1 -1
- package/dist/sessions/index.js +4 -1
- package/dist/sessions/index.js.map +1 -1
- package/dist/sessions/sessions.helper.d.ts +23 -3
- package/dist/sessions/sessions.helper.js +20 -3
- package/dist/sessions/sessions.helper.js.map +1 -1
- package/dist/sessions/sessions.resolver.d.ts +31 -0
- package/dist/sessions/sessions.resolver.js +230 -1
- package/dist/sessions/sessions.resolver.js.map +1 -1
- package/dist/sessions/sessions.service.d.ts +18 -2
- package/dist/sessions/sessions.service.js +121 -57
- package/dist/sessions/sessions.service.js.map +1 -1
- package/dist/sessions/types/index.d.ts +65 -21
- package/dist/storage/storage.service.d.ts +3 -0
- package/dist/storage/storage.service.js +179 -39
- package/dist/storage/storage.service.js.map +1 -1
- package/dist/types/index.types.d.ts +2 -0
- package/dist/types/inputs.types.d.ts +1 -0
- package/dist/types/inputs.types.js +3 -2
- package/dist/types/inputs.types.js.map +1 -1
- package/dist/types/processor.types.d.ts +72 -6
- package/dist/types/processor.types.js.map +1 -1
- package/dist/types/productsBuilder.types.d.ts +10 -0
- package/dist/types/productsBuilder.types.js +4 -0
- package/dist/types/productsBuilder.types.js.map +1 -1
- package/dist/vector/adapters/weaviate.adapter.js +22 -6
- package/dist/vector/adapters/weaviate.adapter.js.map +1 -1
- package/dist/vector/vector-database.service.d.ts +4 -2
- package/dist/vector/vector-database.service.js +71 -29
- package/dist/vector/vector-database.service.js.map +1 -1
- package/dist/workflows/types/workflows.types.d.ts +61 -3
- package/dist/workflows/types/workflows.types.js.map +1 -1
- package/dist/workflows/workflow-builder.js +17 -2
- package/dist/workflows/workflow-builder.js.map +1 -1
- package/dist/workflows/workflow-executor.d.ts +97 -18
- package/dist/workflows/workflow-executor.js +1449 -244
- package/dist/workflows/workflow-executor.js.map +1 -1
- package/dist/workflows/workflows.service.d.ts +2 -0
- package/dist/workflows/workflows.service.js +538 -74
- package/dist/workflows/workflows.service.js.map +1 -1
- package/package.json +11 -1
- package/dist/processor/services/fallback.service.d.ts +0 -5
- package/dist/processor/services/fallback.service.js +0 -43
- package/dist/processor/services/fallback.service.js.map +0 -1
- package/dist/processor/services/messagebrokers/aws-sqs.service.d.ts +0 -15
- package/dist/processor/services/messagebrokers/aws-sqs.service.js +0 -77
- package/dist/processor/services/messagebrokers/aws-sqs.service.js.map +0 -1
- package/dist/processor/services/messagebrokers/google-pubsub.service.d.ts +0 -16
- package/dist/processor/services/messagebrokers/google-pubsub.service.js +0 -34
- package/dist/processor/services/messagebrokers/google-pubsub.service.js.map +0 -1
- package/dist/processor/services/messagebrokers/index.d.ts +0 -3
- package/dist/processor/services/messagebrokers/index.js +0 -26
- package/dist/processor/services/messagebrokers/index.js.map +0 -1
- package/dist/processor/services/messagebrokers/kafka.service.d.ts +0 -14
- package/dist/processor/services/messagebrokers/kafka.service.js +0 -45
- package/dist/processor/services/messagebrokers/kafka.service.js.map +0 -1
- package/dist/processor/services/messagebrokers/messagebrokers.type.d.ts +0 -6
- package/dist/processor/services/messagebrokers/messagebrokers.type.js +0 -3
- package/dist/processor/services/messagebrokers/messagebrokers.type.js.map +0 -1
- package/dist/processor/services/messagebrokers/rabbitmq.service.d.ts +0 -14
- package/dist/processor/services/messagebrokers/rabbitmq.service.js +0 -67
- package/dist/processor/services/messagebrokers/rabbitmq.service.js.map +0 -1
- package/dist/processor/services/messagebrokers/redis.service.d.ts +0 -18
- package/dist/processor/services/messagebrokers/redis.service.js +0 -82
- package/dist/processor/services/messagebrokers/redis.service.js.map +0 -1
- package/dist/processor/services/quota.service.d.ts +0 -15
- package/dist/processor/services/quota.service.js +0 -63
- package/dist/processor/services/quota.service.js.map +0 -1
- package/dist/test/index.d.ts +0 -3
- package/dist/test/index.js +0 -11
- package/dist/test/index.js.map +0 -1
- package/dist/test/test.appBuilder.d.ts +0 -0
- package/dist/test/test.appBuilder.js +0 -1
- package/dist/test/test.appBuilder.js.map +0 -1
- package/dist/test/test.broker.kafka.d.ts +0 -1
- package/dist/test/test.broker.kafka.js +0 -172
- package/dist/test/test.broker.kafka.js.map +0 -1
- package/dist/test/test.broker.nats.d.ts +0 -1
- package/dist/test/test.broker.nats.js +0 -193
- package/dist/test/test.broker.nats.js.map +0 -1
- package/dist/test/test.broker.pubsub.d.ts +0 -1
- package/dist/test/test.broker.pubsub.js +0 -171
- package/dist/test/test.broker.pubsub.js.map +0 -1
- package/dist/test/test.broker.rabbitmq.d.ts +0 -1
- package/dist/test/test.broker.rabbitmq.js +0 -164
- package/dist/test/test.broker.rabbitmq.js.map +0 -1
- package/dist/test/test.broker.redis.d.ts +0 -1
- package/dist/test/test.broker.redis.js +0 -168
- package/dist/test/test.broker.redis.js.map +0 -1
- package/dist/test/test.broker.sqs.d.ts +0 -1
- package/dist/test/test.broker.sqs.js +0 -158
- package/dist/test/test.broker.sqs.js.map +0 -1
- package/dist/test/test.caches.d.ts +0 -1
- package/dist/test/test.caches.js +0 -231
- package/dist/test/test.caches.js.map +0 -1
- package/dist/test/test.database.d.ts +0 -1
- package/dist/test/test.database.dynamo.d.ts +0 -1
- package/dist/test/test.database.dynamo.js +0 -265
- package/dist/test/test.database.dynamo.js.map +0 -1
- package/dist/test/test.database.js +0 -140
- package/dist/test/test.database.js.map +0 -1
- package/dist/test/test.database.mongo.d.ts +0 -1
- package/dist/test/test.database.mongo.js +0 -371
- package/dist/test/test.database.mongo.js.map +0 -1
- package/dist/test/test.database.mysql.d.ts +0 -1
- package/dist/test/test.database.mysql.js +0 -415
- package/dist/test/test.database.mysql.js.map +0 -1
- package/dist/test/test.database.postgres.d.ts +0 -1
- package/dist/test/test.database.postgres.js +0 -412
- package/dist/test/test.database.postgres.js.map +0 -1
- package/dist/test/test.email.brevo.d.ts +0 -1
- package/dist/test/test.email.brevo.js +0 -326
- package/dist/test/test.email.brevo.js.map +0 -1
- package/dist/test/test.email.mailgun.d.ts +0 -1
- package/dist/test/test.email.mailgun.js +0 -352
- package/dist/test/test.email.mailgun.js.map +0 -1
- package/dist/test/test.email.postmark.d.ts +0 -1
- package/dist/test/test.email.postmark.js +0 -316
- package/dist/test/test.email.postmark.js.map +0 -1
- package/dist/test/test.email.sendgrid.d.ts +0 -1
- package/dist/test/test.email.sendgrid.js +0 -365
- package/dist/test/test.email.sendgrid.js.map +0 -1
- package/dist/test/test.email.smtp.d.ts +0 -1
- package/dist/test/test.email.smtp.js +0 -323
- package/dist/test/test.email.smtp.js.map +0 -1
- package/dist/test/test.graph.arangodb.d.ts +0 -1
- package/dist/test/test.graph.arangodb.js +0 -358
- package/dist/test/test.graph.arangodb.js.map +0 -1
- package/dist/test/test.graph.memgraph.d.ts +0 -1
- package/dist/test/test.graph.memgraph.js +0 -320
- package/dist/test/test.graph.memgraph.js.map +0 -1
- package/dist/test/test.graph.neo4j.d.ts +0 -1
- package/dist/test/test.graph.neo4j.js +0 -218
- package/dist/test/test.graph.neo4j.js.map +0 -1
- package/dist/test/test.graph.neptune.d.ts +0 -1
- package/dist/test/test.graph.neptune.js +0 -331
- package/dist/test/test.graph.neptune.js.map +0 -1
- package/dist/test/test.health.d.ts +0 -0
- package/dist/test/test.health.js +0 -1
- package/dist/test/test.health.js.map +0 -1
- package/dist/test/test.import.d.ts +0 -0
- package/dist/test/test.import.js +0 -1
- package/dist/test/test.import.js.map +0 -1
- package/dist/test/test.import.openapi.d.ts +0 -0
- package/dist/test/test.import.openapi.js +0 -1
- package/dist/test/test.import.openapi.js.map +0 -1
- package/dist/test/test.imports.d.ts +0 -1
- package/dist/test/test.imports.js +0 -21
- package/dist/test/test.imports.js.map +0 -1
- package/dist/test/test.logs.d.ts +0 -0
- package/dist/test/test.logs.js +0 -1
- package/dist/test/test.logs.js.map +0 -1
- package/dist/test/test.notifications.d.ts +0 -1
- package/dist/test/test.notifications.js +0 -198
- package/dist/test/test.notifications.js.map +0 -1
- package/dist/test/test.notifiers.d.ts +0 -0
- package/dist/test/test.notifiers.js +0 -1
- package/dist/test/test.notifiers.js.map +0 -1
- package/dist/test/test.processor.d.ts +0 -0
- package/dist/test/test.processor.js +0 -1
- package/dist/test/test.processor.js.map +0 -1
- package/dist/test/test.productBuilder.d.ts +0 -0
- package/dist/test/test.productBuilder.js +0 -1
- package/dist/test/test.productBuilder.js.map +0 -1
- package/dist/test/test.products.d.ts +0 -0
- package/dist/test/test.products.js +0 -1
- package/dist/test/test.products.js.map +0 -1
- package/dist/test/test.push.expo.d.ts +0 -1
- package/dist/test/test.push.expo.js +0 -442
- package/dist/test/test.push.expo.js.map +0 -1
- package/dist/test/test.push.firebase.d.ts +0 -1
- package/dist/test/test.push.firebase.js +0 -409
- package/dist/test/test.push.firebase.js.map +0 -1
- package/dist/test/test.session.d.ts +0 -1
- package/dist/test/test.session.js +0 -299
- package/dist/test/test.session.js.map +0 -1
- package/dist/test/test.sms.nexmo.d.ts +0 -1
- package/dist/test/test.sms.nexmo.js +0 -278
- package/dist/test/test.sms.nexmo.js.map +0 -1
- package/dist/test/test.sms.twilio.d.ts +0 -1
- package/dist/test/test.sms.twilio.js +0 -275
- package/dist/test/test.sms.twilio.js.map +0 -1
- package/dist/test/test.storage.d.ts +0 -1
- package/dist/test/test.storage.js +0 -202
- package/dist/test/test.storage.js.map +0 -1
- package/dist/test/test.triggers.d.ts +0 -1
- package/dist/test/test.triggers.js +0 -314
- package/dist/test/test.triggers.js.map +0 -1
- package/dist/test/test.vector.pinecone.d.ts +0 -1
- package/dist/test/test.vector.pinecone.js +0 -238
- package/dist/test/test.vector.pinecone.js.map +0 -1
- package/dist/test/test.vector.qdrant.d.ts +0 -1
- package/dist/test/test.vector.qdrant.js +0 -307
- package/dist/test/test.vector.qdrant.js.map +0 -1
- package/dist/test/test.vector.weaviate.d.ts +0 -1
- package/dist/test/test.vector.weaviate.js +0 -325
- package/dist/test/test.vector.weaviate.js.map +0 -1
|
@@ -46,6 +46,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
46
46
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
47
47
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
48
48
|
};
|
|
49
|
+
var _a, _b;
|
|
49
50
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
50
51
|
const products_service_1 = __importDefault(require("../../products/services/products.service"));
|
|
51
52
|
const types_1 = require("../../types");
|
|
@@ -54,6 +55,7 @@ const inputs_service_1 = __importDefault(require("../../inputs/inputs.service"))
|
|
|
54
55
|
const processor_utils_1 = require("../utils/processor.utils");
|
|
55
56
|
const axios_1 = __importDefault(require("axios"));
|
|
56
57
|
const processorApi_service_1 = require("../../api/services/processorApi.service");
|
|
58
|
+
const cache_service_1 = require("../../cache/cache.service");
|
|
57
59
|
const expo_client_1 = __importDefault(require("../../clients/expo.client"));
|
|
58
60
|
const handlebars_1 = require("handlebars");
|
|
59
61
|
const string_utils_1 = require("../../products/utils/string.utils");
|
|
@@ -86,8 +88,19 @@ async function loadJWT() {
|
|
|
86
88
|
}
|
|
87
89
|
return null;
|
|
88
90
|
}
|
|
91
|
+
/** Only log when DUCTAPE_DEBUG is set to avoid sync I/O and serialization cost in hot path */
|
|
92
|
+
const _processorDebug = typeof process !== 'undefined' && (((_a = process.env) === null || _a === void 0 ? void 0 : _a.DUCTAPE_DEBUG) === 'true' || ((_b = process.env) === null || _b === void 0 ? void 0 : _b.DUCTAPE_DEBUG) === '1');
|
|
93
|
+
const debugLog = _processorDebug ? (...args) => console.log(...args) : () => { };
|
|
89
94
|
class ProcessorService {
|
|
90
|
-
constructor({ workspace_id, public_key, user_id, token, env_type, private_key, access_key, redis_client, queues }) {
|
|
95
|
+
constructor({ workspace_id, public_key, user_id, token, env_type, private_key, access_key, redis_client, queues, preInitializedProductBuilder }) {
|
|
96
|
+
/** Reuse broker connections when multiple produce steps use the same broker (avoids ~1–3s connection setup per step). Never logged or exposed. */
|
|
97
|
+
this.brokerServicePool = new Map();
|
|
98
|
+
/** Reuse SMTP transporter so we don't open a new connection per email (avoids multi-second handshake per send). Never logged or exposed. */
|
|
99
|
+
this.mailTransporterPool = new Map();
|
|
100
|
+
/** Reuse Firebase Admin app per project (avoids re-init per push). Never logged or exposed. */
|
|
101
|
+
this.firebaseAppPool = new Map();
|
|
102
|
+
/** Reuse SMS client per config (Twilio/Nexmo/Plivo). Never logged or exposed. */
|
|
103
|
+
this.smsClientPool = new Map();
|
|
91
104
|
this.workspace_id = workspace_id;
|
|
92
105
|
this.public_key = public_key;
|
|
93
106
|
this.user_id = user_id;
|
|
@@ -95,13 +108,15 @@ class ProcessorService {
|
|
|
95
108
|
this.accessKey = access_key;
|
|
96
109
|
this.token = token;
|
|
97
110
|
this.published = false;
|
|
98
|
-
this.productBuilderService = new products_service_1.default({
|
|
111
|
+
this.productBuilderService = preInitializedProductBuilder !== null && preInitializedProductBuilder !== void 0 ? preInitializedProductBuilder : new products_service_1.default({
|
|
99
112
|
workspace_id,
|
|
100
113
|
public_key,
|
|
101
114
|
user_id,
|
|
102
115
|
token,
|
|
103
116
|
env_type,
|
|
104
117
|
redis_client,
|
|
118
|
+
access_key,
|
|
119
|
+
workspace_private_key: private_key || undefined,
|
|
105
120
|
});
|
|
106
121
|
this.appBuilderService = new app_service_1.default({
|
|
107
122
|
workspace_id,
|
|
@@ -132,9 +147,20 @@ class ProcessorService {
|
|
|
132
147
|
this.requestTrackerService = request_service_1.default.getInstance(redis_client);
|
|
133
148
|
this.processorApiService = new processorApi_service_1.ProcessorApiService(env_type);
|
|
134
149
|
this.environment = env_type;
|
|
135
|
-
//
|
|
150
|
+
// Initialize CacheService
|
|
151
|
+
this.cacheService = new cache_service_1.CacheService({
|
|
152
|
+
workspace_id,
|
|
153
|
+
public_key,
|
|
154
|
+
user_id,
|
|
155
|
+
token,
|
|
156
|
+
env_type,
|
|
157
|
+
});
|
|
158
|
+
// Connect CacheService to Redis if available
|
|
136
159
|
if (redis_client) {
|
|
137
160
|
this.redisClient = redis_client;
|
|
161
|
+
// Note: CacheService.connectRedis() requires a URL string, but we have a client instance
|
|
162
|
+
// For now, CacheService will work with remote cache only
|
|
163
|
+
// TODO: Consider adding a method to CacheService to accept an existing Redis client
|
|
138
164
|
}
|
|
139
165
|
// Start healthcheck workers only if product tag is available
|
|
140
166
|
if (redis_client && this.productTag) {
|
|
@@ -144,6 +170,192 @@ class ProcessorService {
|
|
|
144
170
|
this.queues = queues;
|
|
145
171
|
}
|
|
146
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Pool key for broker service reuse (same broker type + config => same connection).
|
|
175
|
+
*/
|
|
176
|
+
getBrokerPoolKey(type, config) {
|
|
177
|
+
var _a;
|
|
178
|
+
const configKey = (config === null || config === void 0 ? void 0 : config.url) || (config === null || config === void 0 ? void 0 : config.host) || ((_a = config === null || config === void 0 ? void 0 : config.brokers) === null || _a === void 0 ? void 0 : _a[0]) || JSON.stringify(config !== null && config !== void 0 ? config : {});
|
|
179
|
+
return `${type}:${configKey}`;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Get or create a broker service instance so we reuse the same connection for the same broker.
|
|
183
|
+
*/
|
|
184
|
+
async getBrokerServiceForPublish(type, config) {
|
|
185
|
+
const poolKey = this.getBrokerPoolKey(type, config);
|
|
186
|
+
const existing = this.brokerServicePool.get(poolKey);
|
|
187
|
+
if (existing)
|
|
188
|
+
return existing;
|
|
189
|
+
const createBrokerService = await loadBrokerService();
|
|
190
|
+
if (!createBrokerService)
|
|
191
|
+
throw new Error('Broker service not loaded (e.g. browser).');
|
|
192
|
+
const service = createBrokerService(type, config);
|
|
193
|
+
this.brokerServicePool.set(poolKey, service);
|
|
194
|
+
return service;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Disconnect all pooled broker connections (e.g. at end of workflow run).
|
|
198
|
+
* Safe to call if pool is empty or a broker has no disconnect().
|
|
199
|
+
*/
|
|
200
|
+
async disconnectBrokerConnections() {
|
|
201
|
+
for (const service of this.brokerServicePool.values()) {
|
|
202
|
+
try {
|
|
203
|
+
const disconnect = service.disconnect;
|
|
204
|
+
if (typeof disconnect === 'function')
|
|
205
|
+
await disconnect();
|
|
206
|
+
}
|
|
207
|
+
catch (_a) {
|
|
208
|
+
// Non-fatal
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
this.brokerServicePool.clear();
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Pool key for mail transporter reuse (host/port/user only; no password). Same SMTP config => same connection.
|
|
215
|
+
*/
|
|
216
|
+
getMailTransporterPoolKey(auth) {
|
|
217
|
+
var _a, _b, _c, _d;
|
|
218
|
+
const h = (_a = auth === null || auth === void 0 ? void 0 : auth.host) !== null && _a !== void 0 ? _a : '';
|
|
219
|
+
const p = (_b = auth === null || auth === void 0 ? void 0 : auth.port) !== null && _b !== void 0 ? _b : '';
|
|
220
|
+
const u = (_d = (_c = auth === null || auth === void 0 ? void 0 : auth.auth) === null || _c === void 0 ? void 0 : _c.user) !== null && _d !== void 0 ? _d : '';
|
|
221
|
+
const secure = (auth === null || auth === void 0 ? void 0 : auth.secure) === true ? '1' : '0';
|
|
222
|
+
return `mail:${h}:${p}:${secure}:${u}`;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Get or create a mail transporter so we reuse the same SMTP connection instead of opening a new one per send.
|
|
226
|
+
*/
|
|
227
|
+
async getMailTransporter(auth) {
|
|
228
|
+
const key = this.getMailTransporterPoolKey(auth);
|
|
229
|
+
const existing = this.mailTransporterPool.get(key);
|
|
230
|
+
if (existing)
|
|
231
|
+
return existing;
|
|
232
|
+
const transporter = await (0, processor_utils_1.mailerClient)(auth);
|
|
233
|
+
this.mailTransporterPool.set(key, transporter);
|
|
234
|
+
return transporter;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Close all pooled mail transporters (e.g. at end of workflow run). Safe if pool is empty.
|
|
238
|
+
*/
|
|
239
|
+
async disconnectMailTransporters() {
|
|
240
|
+
for (const transporter of this.mailTransporterPool.values()) {
|
|
241
|
+
try {
|
|
242
|
+
const close = transporter.close;
|
|
243
|
+
if (typeof close === 'function')
|
|
244
|
+
await close();
|
|
245
|
+
}
|
|
246
|
+
catch (_a) {
|
|
247
|
+
// Non-fatal
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
this.mailTransporterPool.clear();
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Get or create Firebase Admin app by project_id so we reuse the same app per project (Expo client is already module-level singleton).
|
|
254
|
+
*/
|
|
255
|
+
async getFirebaseApp(credentials) {
|
|
256
|
+
var _a, _b;
|
|
257
|
+
const creds = typeof credentials === 'string' ? JSON.parse(credentials) : credentials;
|
|
258
|
+
const projectId = (_a = creds === null || creds === void 0 ? void 0 : creds.project_id) !== null && _a !== void 0 ? _a : `fb-${String((_b = creds === null || creds === void 0 ? void 0 : creds.client_email) !== null && _b !== void 0 ? _b : '').slice(0, 20)}`;
|
|
259
|
+
const existing = this.firebaseAppPool.get(projectId);
|
|
260
|
+
if (existing)
|
|
261
|
+
return existing;
|
|
262
|
+
const admin = require('firebase-admin');
|
|
263
|
+
try {
|
|
264
|
+
const app = admin.app(projectId);
|
|
265
|
+
this.firebaseAppPool.set(projectId, app);
|
|
266
|
+
return app;
|
|
267
|
+
}
|
|
268
|
+
catch (_c) {
|
|
269
|
+
const app = admin.initializeApp({ credential: admin.credential.cert(credentials) }, projectId);
|
|
270
|
+
this.firebaseAppPool.set(projectId, app);
|
|
271
|
+
return app;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Close all pooled Firebase apps (e.g. at end of workflow run). Safe if pool is empty.
|
|
276
|
+
*/
|
|
277
|
+
async disconnectFirebaseApps() {
|
|
278
|
+
for (const app of this.firebaseAppPool.values()) {
|
|
279
|
+
try {
|
|
280
|
+
const del = app.delete;
|
|
281
|
+
if (typeof del === 'function')
|
|
282
|
+
await del();
|
|
283
|
+
}
|
|
284
|
+
catch (_a) {
|
|
285
|
+
// Non-fatal
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
this.firebaseAppPool.clear();
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Pool key for SMS client (provider + identifier + sender; no secrets). Same config => reuse client.
|
|
292
|
+
*/
|
|
293
|
+
getSmsClientPoolKey(config) {
|
|
294
|
+
var _a, _b, _c, _d;
|
|
295
|
+
const p = (_a = config === null || config === void 0 ? void 0 : config.provider) !== null && _a !== void 0 ? _a : '';
|
|
296
|
+
const id = (_c = (_b = config === null || config === void 0 ? void 0 : config.accountSid) !== null && _b !== void 0 ? _b : config === null || config === void 0 ? void 0 : config.apiKey) !== null && _c !== void 0 ? _c : '';
|
|
297
|
+
const sender = (_d = config === null || config === void 0 ? void 0 : config.sender) !== null && _d !== void 0 ? _d : '';
|
|
298
|
+
return `sms:${p}:${id}:${sender}`;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Get or create SMS client so we reuse Twilio/Nexmo/Plivo client per config.
|
|
302
|
+
*/
|
|
303
|
+
async getSmsClient(config) {
|
|
304
|
+
const key = this.getSmsClientPoolKey(config);
|
|
305
|
+
const existing = this.smsClientPool.get(key);
|
|
306
|
+
if (existing)
|
|
307
|
+
return existing;
|
|
308
|
+
const SmsClientClass = await (0, sms_repo_1.loadSMSClient)();
|
|
309
|
+
if (!SmsClientClass)
|
|
310
|
+
throw new Error('SMS client not loaded (e.g. browser).');
|
|
311
|
+
const client = new SmsClientClass(config);
|
|
312
|
+
this.smsClientPool.set(key, client);
|
|
313
|
+
return client;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Clear pooled SMS clients (e.g. at end of workflow run). Safe if pool is empty.
|
|
317
|
+
*/
|
|
318
|
+
async disconnectSmsClients() {
|
|
319
|
+
this.smsClientPool.clear();
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Pre-warm broker connections for the given product/env and broker tags.
|
|
323
|
+
* Call this before step execution so the first produce step does not pay connection setup cost.
|
|
324
|
+
* Security: same auth as runBrokerPublish (product + env from caller). Secrets resolved in-memory
|
|
325
|
+
* only; no config/URLs/credentials logged or persisted. Pool is per-processor-instance (one run).
|
|
326
|
+
*/
|
|
327
|
+
async warmBrokerConnections(options) {
|
|
328
|
+
const { product, env, brokerTags } = options;
|
|
329
|
+
if (brokerTags.length === 0)
|
|
330
|
+
return;
|
|
331
|
+
await this.productBuilderService.initializeProductByTag(product);
|
|
332
|
+
for (const brokerTag of brokerTags) {
|
|
333
|
+
try {
|
|
334
|
+
const broker = await this.productBuilderService.fetchMessageBroker(brokerTag);
|
|
335
|
+
if (!broker)
|
|
336
|
+
continue;
|
|
337
|
+
const brokerEnv = broker.envs.find((el) => el.slug === env);
|
|
338
|
+
if (!brokerEnv)
|
|
339
|
+
continue;
|
|
340
|
+
let brokerConfig = brokerEnv.config;
|
|
341
|
+
if (brokerConfig && (0, secrets_1.mightContainSecrets)(brokerConfig)) {
|
|
342
|
+
const secretsService = (0, secrets_1.getSecretsService)();
|
|
343
|
+
if (secretsService) {
|
|
344
|
+
const resolved = await secretsService.resolve(brokerConfig, { env });
|
|
345
|
+
brokerConfig = resolved.value;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
const service = await this.getBrokerServiceForPublish(brokerEnv.type, brokerConfig);
|
|
349
|
+
const connect = service.connect;
|
|
350
|
+
if (typeof connect === 'function')
|
|
351
|
+
await connect();
|
|
352
|
+
}
|
|
353
|
+
catch (_a) {
|
|
354
|
+
// Intentionally no log: avoid leaking any error text (could contain URLs or backend details).
|
|
355
|
+
// Step will connect on first use or fail at runtime with same auth.
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
147
359
|
/**
|
|
148
360
|
* Start healthcheck workers for all products/environments after Redis is connected.
|
|
149
361
|
* This is called automatically in the constructor if redisClient is present.
|
|
@@ -303,7 +515,7 @@ class ProcessorService {
|
|
|
303
515
|
return res.data;
|
|
304
516
|
}
|
|
305
517
|
catch (e) {
|
|
306
|
-
|
|
518
|
+
debugLog(e);
|
|
307
519
|
throw new Error('Invalid/Expired token');
|
|
308
520
|
}
|
|
309
521
|
}
|
|
@@ -450,7 +662,13 @@ class ProcessorService {
|
|
|
450
662
|
if (workspace_id !== this.workspace_id) {
|
|
451
663
|
throw new Error('Access Denied');
|
|
452
664
|
}
|
|
453
|
-
this.logService.add(
|
|
665
|
+
/*this.logService.add({
|
|
666
|
+
...this.baseLogs,
|
|
667
|
+
...additional_logs,
|
|
668
|
+
message: 'Product initialize - success',
|
|
669
|
+
data: { product_id: this.productId },
|
|
670
|
+
status: LogEventStatus.SUCCESS,
|
|
671
|
+
} as unknown as ILogData);*/
|
|
454
672
|
}
|
|
455
673
|
catch (e) {
|
|
456
674
|
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Product initialize - failed', data: e, status: types_1.LogEventStatus.FAIL }));
|
|
@@ -469,9 +687,9 @@ class ProcessorService {
|
|
|
469
687
|
});
|
|
470
688
|
}
|
|
471
689
|
try {
|
|
472
|
-
|
|
690
|
+
debugLog(`Initializing pricing for access tag: ${access_tag}`);
|
|
473
691
|
const product_app = await this.productBuilderService.fetchApp(access_tag); // validate app exists
|
|
474
|
-
|
|
692
|
+
debugLog(`Found product app: ${JSON.stringify(product_app)}`);
|
|
475
693
|
const app = await this.productBuilderService.fetchThirdPartyAppByAccessTag(product_app.access_tag);
|
|
476
694
|
await this.pricingService.initializePricingByTag(product_app.pricing_tag, app._id);
|
|
477
695
|
const { pricing_tag } = this.pricingService.fetchPricing();
|
|
@@ -489,7 +707,13 @@ class ProcessorService {
|
|
|
489
707
|
if (!product_env) {
|
|
490
708
|
throw new Error(`Env ${env} not found`);
|
|
491
709
|
}
|
|
492
|
-
this.logService.add(
|
|
710
|
+
/*this.logService.add({
|
|
711
|
+
...this.baseLogs,
|
|
712
|
+
...additional_logs,
|
|
713
|
+
message: 'Fetch environment - success',
|
|
714
|
+
data: { slug: env, env: product_env },
|
|
715
|
+
status: LogEventStatus.SUCCESS,
|
|
716
|
+
} as unknown as ILogData);*/
|
|
493
717
|
return product_env;
|
|
494
718
|
}
|
|
495
719
|
catch (e) {
|
|
@@ -545,7 +769,13 @@ class ProcessorService {
|
|
|
545
769
|
params,
|
|
546
770
|
});
|
|
547
771
|
}
|
|
548
|
-
this.logService.add(
|
|
772
|
+
/*this.logService.add({
|
|
773
|
+
...this.baseLogs,
|
|
774
|
+
...additional_logs,
|
|
775
|
+
message: 'Construct JSON payloads - success',
|
|
776
|
+
data: { payload: anonymizeObject(payload) },
|
|
777
|
+
status: LogEventStatus.SUCCESS,
|
|
778
|
+
} as unknown as ILogData);*/
|
|
549
779
|
return payload;
|
|
550
780
|
}
|
|
551
781
|
catch (e) {
|
|
@@ -556,7 +786,6 @@ class ProcessorService {
|
|
|
556
786
|
async generatePayload(obj, event, additional_logs, sample = [], index = {}, loopIndex = null) {
|
|
557
787
|
try {
|
|
558
788
|
const payload = {};
|
|
559
|
-
console.log('Payload Construction', { obj, event, sample, index, loopIndex });
|
|
560
789
|
const keys = Object.keys(obj);
|
|
561
790
|
for (let i = 0; i < keys.length; i++) {
|
|
562
791
|
const key = keys[i];
|
|
@@ -588,7 +817,6 @@ class ProcessorService {
|
|
|
588
817
|
});
|
|
589
818
|
}
|
|
590
819
|
}
|
|
591
|
-
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Generate payload - success', data: { payload: (0, processor_utils_1.anonymizeObject)(payload) }, status: types_1.LogEventStatus.SUCCESS }));
|
|
592
820
|
return payload;
|
|
593
821
|
}
|
|
594
822
|
catch (e) {
|
|
@@ -752,14 +980,14 @@ class ProcessorService {
|
|
|
752
980
|
const placeholdersStr = match[1];
|
|
753
981
|
const separator = match[2];
|
|
754
982
|
const placeHolders = placeholdersStr.split(',').map((data) => data.trim());
|
|
755
|
-
|
|
983
|
+
debugLog('placeHolders', { placeHolders, separator });
|
|
756
984
|
const values = await Promise.all(placeHolders.map(async (holder) => {
|
|
757
985
|
return await this.generateStringValues(holder, app, additional_logs, sample, index, key, loopIndex);
|
|
758
986
|
}));
|
|
759
987
|
return values.join(separator);
|
|
760
988
|
}
|
|
761
989
|
else {
|
|
762
|
-
|
|
990
|
+
debugLog('No match found');
|
|
763
991
|
}
|
|
764
992
|
}
|
|
765
993
|
async uppercaseValue(value, app, additional_logs, sample = [], index = {}, key = '', loopIndex = null) {
|
|
@@ -772,7 +1000,7 @@ class ProcessorService {
|
|
|
772
1000
|
return resolvedValue.toUpperCase();
|
|
773
1001
|
}
|
|
774
1002
|
else {
|
|
775
|
-
//
|
|
1003
|
+
//debugLog('No match found');
|
|
776
1004
|
}
|
|
777
1005
|
}
|
|
778
1006
|
async lowercaseValue(value, app, additional_logs, sample = [], index = {}, key = '', loopIndex = null) {
|
|
@@ -785,7 +1013,7 @@ class ProcessorService {
|
|
|
785
1013
|
return resolvedValue.toLowerCase();
|
|
786
1014
|
}
|
|
787
1015
|
else {
|
|
788
|
-
//
|
|
1016
|
+
//debugLog('No match found');
|
|
789
1017
|
}
|
|
790
1018
|
}
|
|
791
1019
|
async dateFormatValue(value, app, additional_logs, sample = [], index = {}, key = '', loopIndex = null) {
|
|
@@ -801,7 +1029,7 @@ class ProcessorService {
|
|
|
801
1029
|
return (0, date_fns_1.format)(parsedDate, formatStr);
|
|
802
1030
|
}
|
|
803
1031
|
else {
|
|
804
|
-
//
|
|
1032
|
+
//debugLog('No match found');
|
|
805
1033
|
}
|
|
806
1034
|
}
|
|
807
1035
|
async replaceValue(value, app, additional_logs, sample = [], index = {}, key = '', loopIndex = null) {
|
|
@@ -815,7 +1043,7 @@ class ProcessorService {
|
|
|
815
1043
|
return resolvedValue.split(searchStr).join(replaceStr);
|
|
816
1044
|
}
|
|
817
1045
|
else {
|
|
818
|
-
|
|
1046
|
+
debugLog('No match found');
|
|
819
1047
|
}
|
|
820
1048
|
}
|
|
821
1049
|
async substringValues(value, app, additional_logs, sample = [], index = {}, key = '', loopIndex = null) {
|
|
@@ -834,7 +1062,7 @@ class ProcessorService {
|
|
|
834
1062
|
return resolvedString.substring(start, end);
|
|
835
1063
|
}
|
|
836
1064
|
else {
|
|
837
|
-
|
|
1065
|
+
debugLog('No match found');
|
|
838
1066
|
return '';
|
|
839
1067
|
}
|
|
840
1068
|
}
|
|
@@ -849,7 +1077,7 @@ class ProcessorService {
|
|
|
849
1077
|
return resolvedString.trim();
|
|
850
1078
|
}
|
|
851
1079
|
else {
|
|
852
|
-
|
|
1080
|
+
debugLog('No match found');
|
|
853
1081
|
return '';
|
|
854
1082
|
}
|
|
855
1083
|
}
|
|
@@ -873,7 +1101,7 @@ class ProcessorService {
|
|
|
873
1101
|
}
|
|
874
1102
|
}
|
|
875
1103
|
else {
|
|
876
|
-
|
|
1104
|
+
debugLog('No match found');
|
|
877
1105
|
}
|
|
878
1106
|
}
|
|
879
1107
|
async filterValue(value, app, additional_logs, sample = [], index = {}, key = '', loopIndex = null) {
|
|
@@ -892,7 +1120,7 @@ class ProcessorService {
|
|
|
892
1120
|
}
|
|
893
1121
|
return resolvedArray.filter((item) => (0, processor_utils_1.compareValues)(item, operator, resolvedValue));
|
|
894
1122
|
}
|
|
895
|
-
|
|
1123
|
+
debugLog('No $Filter match found');
|
|
896
1124
|
return [];
|
|
897
1125
|
}
|
|
898
1126
|
async findValue(value, app, additional_logs, sample = [], index = {}, key = '', loopIndex = null) {
|
|
@@ -911,7 +1139,7 @@ class ProcessorService {
|
|
|
911
1139
|
}
|
|
912
1140
|
return resolvedArray.find((item) => (0, processor_utils_1.compareValues)(item, operator, resolvedValue));
|
|
913
1141
|
}
|
|
914
|
-
|
|
1142
|
+
debugLog('No $Find match found');
|
|
915
1143
|
return null;
|
|
916
1144
|
}
|
|
917
1145
|
async splitValues(value, app, additional_logs, sample = [], index = {}, key = '', loopIndex = null) {
|
|
@@ -926,7 +1154,7 @@ class ProcessorService {
|
|
|
926
1154
|
return resolvedString.split(separator);
|
|
927
1155
|
}
|
|
928
1156
|
else {
|
|
929
|
-
|
|
1157
|
+
debugLog('No match found');
|
|
930
1158
|
return [];
|
|
931
1159
|
}
|
|
932
1160
|
}
|
|
@@ -947,7 +1175,7 @@ class ProcessorService {
|
|
|
947
1175
|
return mergedArray;
|
|
948
1176
|
}
|
|
949
1177
|
else {
|
|
950
|
-
|
|
1178
|
+
debugLog('No match found');
|
|
951
1179
|
}
|
|
952
1180
|
}
|
|
953
1181
|
async generateInputValue(input, stages) {
|
|
@@ -1071,7 +1299,7 @@ class ProcessorService {
|
|
|
1071
1299
|
}
|
|
1072
1300
|
async generateAuthValue(stages, app, sample, additional_logs) {
|
|
1073
1301
|
try {
|
|
1074
|
-
|
|
1302
|
+
debugLog('Generate Auth Data', { stages, app, sample });
|
|
1075
1303
|
let auth_data = await this.fetchAuthData(app, additional_logs); //TODO: should use stages[0]
|
|
1076
1304
|
// take the app tag in index 0..
|
|
1077
1305
|
if (!auth_data) {
|
|
@@ -1098,7 +1326,7 @@ class ProcessorService {
|
|
|
1098
1326
|
if (!env.auth) {
|
|
1099
1327
|
throw new Error(`App ${app_tag} in auth needs to have a definition for auth in env: ${this.processEnv.slug}`);
|
|
1100
1328
|
}
|
|
1101
|
-
|
|
1329
|
+
debugLog('Envroment', env.auth);
|
|
1102
1330
|
let values = env.auth.values;
|
|
1103
1331
|
if (!values) {
|
|
1104
1332
|
// no auth values
|
|
@@ -1106,7 +1334,7 @@ class ProcessorService {
|
|
|
1106
1334
|
}
|
|
1107
1335
|
if (!env.auth.expiry || (env.auth.expiry && Date.now() > new Date(env.auth.expiry).getTime())) {
|
|
1108
1336
|
// refresh
|
|
1109
|
-
|
|
1337
|
+
debugLog('REFRESH DATA', env, app_tag);
|
|
1110
1338
|
values = await this.getAndStoreAuth(env, app_tag);
|
|
1111
1339
|
}
|
|
1112
1340
|
const decrypted = (0, processor_utils_1.decrypt)(values, this.productBuilderService.fetchPrivateKey());
|
|
@@ -1219,12 +1447,68 @@ class ProcessorService {
|
|
|
1219
1447
|
}
|
|
1220
1448
|
}
|
|
1221
1449
|
async runJobs(job, additional_logs = {}) {
|
|
1222
|
-
var _a;
|
|
1450
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
|
|
1223
1451
|
const jobId = (_a = job.data) === null || _a === void 0 ? void 0 : _a._job_id;
|
|
1224
1452
|
const jobType = job.name;
|
|
1453
|
+
const startedAt = Date.now();
|
|
1454
|
+
const eventStr = ((_c = (_b = job.data) === null || _b === void 0 ? void 0 : _b.event) !== null && _c !== void 0 ? _c : job.event);
|
|
1455
|
+
const eventParts = typeof eventStr === 'string' ? eventStr.split(':') : [];
|
|
1456
|
+
const jobParentTag = (_e = (_d = eventParts[0]) !== null && _d !== void 0 ? _d : eventStr) !== null && _e !== void 0 ? _e : undefined;
|
|
1457
|
+
const jobChildTag = eventParts.length >= 2 ? eventParts.slice(1).join(':') : undefined;
|
|
1458
|
+
// Resolve product and ensure logService is initialized so job lifecycle is always logged
|
|
1459
|
+
const productTag = (_f = job.data) === null || _f === void 0 ? void 0 : _f.product;
|
|
1460
|
+
if (productTag && this.workspace_id) {
|
|
1461
|
+
try {
|
|
1462
|
+
await this.productBuilderService.initializeProductByTag(productTag);
|
|
1463
|
+
this.productId = this.productBuilderService.fetchProductId();
|
|
1464
|
+
if (this.productId && !this.logService) {
|
|
1465
|
+
this.logService = new logs_service_1.default({
|
|
1466
|
+
product_id: this.productId,
|
|
1467
|
+
workspace_id: this.workspace_id,
|
|
1468
|
+
public_key: this.public_key,
|
|
1469
|
+
user_id: this.user_id,
|
|
1470
|
+
token: this.token,
|
|
1471
|
+
env_type: this.environment,
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
if (this.logService) {
|
|
1475
|
+
this.logService.add({
|
|
1476
|
+
product_tag: productTag,
|
|
1477
|
+
product_id: this.productId,
|
|
1478
|
+
workspace_id: this.workspace_id,
|
|
1479
|
+
env: (_h = (_g = job.data) === null || _g === void 0 ? void 0 : _g.env) !== null && _h !== void 0 ? _h : '',
|
|
1480
|
+
process_id: jobId !== null && jobId !== void 0 ? jobId : (0, processor_utils_1.generateObjectId)(),
|
|
1481
|
+
type: types_1.LogEventTypes.JOB,
|
|
1482
|
+
parent_tag: jobParentTag,
|
|
1483
|
+
child_tag: jobChildTag,
|
|
1484
|
+
message: 'Job picked up for execution',
|
|
1485
|
+
data: {
|
|
1486
|
+
job_id: jobId,
|
|
1487
|
+
job_type: jobType,
|
|
1488
|
+
event: (_j = job.data) === null || _j === void 0 ? void 0 : _j.event,
|
|
1489
|
+
env: (_k = job.data) === null || _k === void 0 ? void 0 : _k.env,
|
|
1490
|
+
},
|
|
1491
|
+
status: types_1.LogEventStatus.PROCESSING,
|
|
1492
|
+
start: startedAt,
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1496
|
+
catch (e) {
|
|
1497
|
+
// Non-fatal: continue without job-level logging if product init fails
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1225
1500
|
// Update job status to running
|
|
1226
1501
|
if (jobId && this.redisClient) {
|
|
1227
|
-
await this.updateJobStatus(jobId, 'running', { started_at:
|
|
1502
|
+
await this.updateJobStatus(jobId, 'running', { started_at: startedAt });
|
|
1503
|
+
}
|
|
1504
|
+
// Fire-and-forget: record running phase (do not await)
|
|
1505
|
+
if (jobId && this.workspace_id) {
|
|
1506
|
+
const auth = Object.assign(Object.assign({}, this.getUserAccess()), { access_key: this.accessKey });
|
|
1507
|
+
if (!this.accessKey)
|
|
1508
|
+
console.warn('[runJobs] Updating job execution phase (running) without access_key', { jobId });
|
|
1509
|
+
void this.processorApiService
|
|
1510
|
+
.updateJobExecutionPhase({ workspace_id: this.workspace_id, job_id: jobId, phase: 'running', started_at: startedAt }, auth)
|
|
1511
|
+
.catch((err) => console.error('[runJobs] job execution phase running failed', { jobId, error: err }));
|
|
1228
1512
|
}
|
|
1229
1513
|
try {
|
|
1230
1514
|
let result;
|
|
@@ -1260,56 +1544,193 @@ class ProcessorService {
|
|
|
1260
1544
|
else {
|
|
1261
1545
|
throw new Error(`Unknown job type: ${jobType}`);
|
|
1262
1546
|
}
|
|
1547
|
+
const completedAt = Date.now();
|
|
1548
|
+
const durationMs = completedAt - startedAt;
|
|
1263
1549
|
// Update job status to completed
|
|
1264
1550
|
if (jobId && this.redisClient) {
|
|
1265
1551
|
await this.updateJobStatus(jobId, 'completed', {
|
|
1266
|
-
completed_at:
|
|
1552
|
+
completed_at: completedAt,
|
|
1267
1553
|
result,
|
|
1268
1554
|
});
|
|
1269
1555
|
}
|
|
1556
|
+
// Fire-and-forget: record completed phase (do not await)
|
|
1557
|
+
if (jobId && this.workspace_id) {
|
|
1558
|
+
const auth = Object.assign(Object.assign({}, this.getUserAccess()), { access_key: this.accessKey });
|
|
1559
|
+
if (!this.accessKey)
|
|
1560
|
+
console.warn('[runJobs] Updating job execution phase (completed) without access_key', { jobId });
|
|
1561
|
+
const operation = typeof job.event === 'string' && job.event.includes(':')
|
|
1562
|
+
? job.event.split(':').pop()
|
|
1563
|
+
: (_l = job.event) !== null && _l !== void 0 ? _l : undefined;
|
|
1564
|
+
const outputPayload = result != null
|
|
1565
|
+
? typeof result === 'object' && result !== null && !Array.isArray(result)
|
|
1566
|
+
? result
|
|
1567
|
+
: { value: result }
|
|
1568
|
+
: undefined;
|
|
1569
|
+
void this.processorApiService
|
|
1570
|
+
.updateJobExecutionPhase({
|
|
1571
|
+
workspace_id: this.workspace_id,
|
|
1572
|
+
job_id: jobId,
|
|
1573
|
+
phase: 'completed',
|
|
1574
|
+
completed_at: completedAt,
|
|
1575
|
+
duration_ms: durationMs,
|
|
1576
|
+
result_metadata: result != null ? { result: typeof result === 'object' ? result : { value: result } } : undefined,
|
|
1577
|
+
output: outputPayload,
|
|
1578
|
+
operation,
|
|
1579
|
+
}, auth)
|
|
1580
|
+
.catch((err) => console.error('[runJobs] job execution phase completed failed', { jobId, error: err }));
|
|
1581
|
+
}
|
|
1582
|
+
// Log job execution completed so it shows in logs
|
|
1583
|
+
if (this.logService) {
|
|
1584
|
+
this.logService.add({
|
|
1585
|
+
product_tag: (_m = this.productTag) !== null && _m !== void 0 ? _m : (_o = job.data) === null || _o === void 0 ? void 0 : _o.product,
|
|
1586
|
+
product_id: this.productId,
|
|
1587
|
+
workspace_id: this.workspace_id,
|
|
1588
|
+
env: (_q = (_p = job.data) === null || _p === void 0 ? void 0 : _p.env) !== null && _q !== void 0 ? _q : '',
|
|
1589
|
+
process_id: jobId !== null && jobId !== void 0 ? jobId : this.process_id,
|
|
1590
|
+
type: types_1.LogEventTypes.JOB,
|
|
1591
|
+
parent_tag: jobParentTag,
|
|
1592
|
+
child_tag: jobChildTag,
|
|
1593
|
+
message: 'Job execution completed',
|
|
1594
|
+
data: { job_id: jobId, job_type: jobType, duration_ms: durationMs, result },
|
|
1595
|
+
status: types_1.LogEventStatus.SUCCESS,
|
|
1596
|
+
start: startedAt,
|
|
1597
|
+
end: completedAt,
|
|
1598
|
+
});
|
|
1599
|
+
await this.logService.publish();
|
|
1600
|
+
}
|
|
1270
1601
|
return result;
|
|
1271
1602
|
}
|
|
1272
1603
|
catch (error) {
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1604
|
+
const failedAt = Date.now();
|
|
1605
|
+
const jobData = jobId && this.redisClient ? await this.getJobData(jobId) : null;
|
|
1606
|
+
// Update job status to failed or schedule retry (when we have Redis job data)
|
|
1607
|
+
if (jobId && this.redisClient && jobData) {
|
|
1608
|
+
const { shouldRetry, delay } = this.calculateJobRetry(jobData, error.code);
|
|
1609
|
+
if (shouldRetry) {
|
|
1610
|
+
const retryCount = (jobData.retry_count || 0) + 1;
|
|
1611
|
+
await this.updateJobStatus(jobId, 'scheduled', {
|
|
1612
|
+
retry_count: retryCount,
|
|
1613
|
+
last_error: error.message,
|
|
1614
|
+
last_error_code: error.code,
|
|
1615
|
+
scheduled_at: Date.now() + delay,
|
|
1616
|
+
});
|
|
1617
|
+
// Fire-and-forget: record retry_scheduled phase (do not await)
|
|
1618
|
+
if (jobId && this.workspace_id) {
|
|
1619
|
+
const auth = Object.assign(Object.assign({}, this.getUserAccess()), { access_key: this.accessKey });
|
|
1620
|
+
if (!this.accessKey) {
|
|
1621
|
+
console.warn('[runJobs] Updating job execution phase without access_key — backend may return 401', { jobId, phase: 'retry_scheduled' });
|
|
1622
|
+
}
|
|
1623
|
+
void this.processorApiService
|
|
1624
|
+
.updateJobExecutionPhase({
|
|
1625
|
+
workspace_id: this.workspace_id,
|
|
1626
|
+
job_id: jobId,
|
|
1627
|
+
phase: 'retry_scheduled',
|
|
1628
|
+
error: error.message,
|
|
1629
|
+
error_code: error === null || error === void 0 ? void 0 : error.code,
|
|
1630
|
+
retry_count: retryCount,
|
|
1631
|
+
}, auth)
|
|
1632
|
+
.catch((err) => console.error('[runJobs] job execution phase retry_scheduled failed', { jobId, error: err }));
|
|
1297
1633
|
}
|
|
1298
|
-
//
|
|
1299
|
-
await this.
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
completed_at: Date.now(),
|
|
1303
|
-
duration_ms: Date.now() - (jobData.started_at || Date.now()),
|
|
1304
|
-
status: 'failed',
|
|
1305
|
-
error: error.message,
|
|
1306
|
-
error_code: error.code,
|
|
1634
|
+
// Re-queue the job with delay
|
|
1635
|
+
await this.queues.jobs.add(jobType, job.data, {
|
|
1636
|
+
jobId: `${jobId}_retry_${jobData.retry_count + 1}`,
|
|
1637
|
+
delay,
|
|
1307
1638
|
});
|
|
1308
1639
|
}
|
|
1640
|
+
else {
|
|
1641
|
+
await this.updateJobStatus(jobId, 'failed', {
|
|
1642
|
+
completed_at: failedAt,
|
|
1643
|
+
last_error: error.message,
|
|
1644
|
+
last_error_code: error.code,
|
|
1645
|
+
});
|
|
1646
|
+
}
|
|
1647
|
+
// Add to execution history
|
|
1648
|
+
await this.addJobExecution(jobId, {
|
|
1649
|
+
number: (jobData.execution_count || 0) + 1,
|
|
1650
|
+
started_at: jobData.started_at || Date.now(),
|
|
1651
|
+
completed_at: Date.now(),
|
|
1652
|
+
duration_ms: Date.now() - (jobData.started_at || Date.now()),
|
|
1653
|
+
status: 'failed',
|
|
1654
|
+
error: error.message,
|
|
1655
|
+
error_code: error.code,
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
// Always record failed phase on JobExecutionTracker when we have jobId + workspace_id
|
|
1659
|
+
// (even when Redis job data is missing, so failures are visible in the tracker)
|
|
1660
|
+
if (jobId && this.workspace_id) {
|
|
1661
|
+
const auth = Object.assign(Object.assign({}, this.getUserAccess()), { access_key: this.accessKey });
|
|
1662
|
+
if (!this.accessKey) {
|
|
1663
|
+
console.warn('[runJobs] Updating job execution phase (failed) without access_key — backend may return 401', { jobId });
|
|
1664
|
+
}
|
|
1665
|
+
const durationMs = (jobData === null || jobData === void 0 ? void 0 : jobData.started_at) != null ? failedAt - jobData.started_at : failedAt - startedAt;
|
|
1666
|
+
const operation = typeof job.event === 'string' && job.event.includes(':')
|
|
1667
|
+
? job.event.split(':').pop()
|
|
1668
|
+
: (_r = job.event) !== null && _r !== void 0 ? _r : undefined;
|
|
1669
|
+
void this.processorApiService
|
|
1670
|
+
.updateJobExecutionPhase({
|
|
1671
|
+
workspace_id: this.workspace_id,
|
|
1672
|
+
job_id: jobId,
|
|
1673
|
+
phase: 'failed',
|
|
1674
|
+
completed_at: failedAt,
|
|
1675
|
+
duration_ms: durationMs,
|
|
1676
|
+
error: (error === null || error === void 0 ? void 0 : error.message) != null ? String(error.message) : undefined,
|
|
1677
|
+
error_code: (error === null || error === void 0 ? void 0 : error.code) != null ? String(error.code) : undefined,
|
|
1678
|
+
operation,
|
|
1679
|
+
}, auth)
|
|
1680
|
+
.catch((err) => console.error('[runJobs] job execution phase failed (tracker update)', { jobId, error: err }));
|
|
1681
|
+
}
|
|
1682
|
+
// Log job execution failed so it shows in logs
|
|
1683
|
+
if (this.logService) {
|
|
1684
|
+
this.logService.add({
|
|
1685
|
+
product_tag: (_s = this.productTag) !== null && _s !== void 0 ? _s : (_t = job.data) === null || _t === void 0 ? void 0 : _t.product,
|
|
1686
|
+
product_id: this.productId,
|
|
1687
|
+
workspace_id: this.workspace_id,
|
|
1688
|
+
env: (_v = (_u = job.data) === null || _u === void 0 ? void 0 : _u.env) !== null && _v !== void 0 ? _v : '',
|
|
1689
|
+
process_id: jobId !== null && jobId !== void 0 ? jobId : this.process_id,
|
|
1690
|
+
type: types_1.LogEventTypes.JOB,
|
|
1691
|
+
parent_tag: jobParentTag,
|
|
1692
|
+
child_tag: jobChildTag,
|
|
1693
|
+
message: 'Job execution failed',
|
|
1694
|
+
data: {
|
|
1695
|
+
job_id: jobId,
|
|
1696
|
+
job_type: jobType,
|
|
1697
|
+
error: (error === null || error === void 0 ? void 0 : error.message) != null ? String(error.message) : undefined,
|
|
1698
|
+
error_code: (error === null || error === void 0 ? void 0 : error.code) != null ? String(error.code) : undefined,
|
|
1699
|
+
},
|
|
1700
|
+
status: types_1.LogEventStatus.FAIL,
|
|
1701
|
+
start: startedAt,
|
|
1702
|
+
end: failedAt,
|
|
1703
|
+
});
|
|
1704
|
+
await this.logService.publish();
|
|
1309
1705
|
}
|
|
1310
1706
|
throw error;
|
|
1311
1707
|
}
|
|
1312
1708
|
}
|
|
1709
|
+
/**
|
|
1710
|
+
* Record a failed phase on the job execution tracker (e.g. from BullMQ 'failed' handler).
|
|
1711
|
+
* Call when the worker job fails so the tracker is updated even if runJobs' catch didn't run.
|
|
1712
|
+
*/
|
|
1713
|
+
async recordJobExecutionPhaseFailed(jobId, errorMessage, errorCode) {
|
|
1714
|
+
if (!jobId || !this.workspace_id)
|
|
1715
|
+
return;
|
|
1716
|
+
const auth = Object.assign(Object.assign({}, this.getUserAccess()), { access_key: this.accessKey });
|
|
1717
|
+
if (!this.accessKey)
|
|
1718
|
+
console.warn('[recordJobExecutionPhaseFailed] No access_key — backend may return 401', { jobId });
|
|
1719
|
+
const failedAt = Date.now();
|
|
1720
|
+
try {
|
|
1721
|
+
await this.processorApiService.updateJobExecutionPhase({
|
|
1722
|
+
workspace_id: this.workspace_id,
|
|
1723
|
+
job_id: jobId,
|
|
1724
|
+
phase: 'failed',
|
|
1725
|
+
completed_at: failedAt,
|
|
1726
|
+
error: errorMessage,
|
|
1727
|
+
error_code: errorCode,
|
|
1728
|
+
}, auth);
|
|
1729
|
+
}
|
|
1730
|
+
catch (err) {
|
|
1731
|
+
console.error('[recordJobExecutionPhaseFailed] tracker update failed', { jobId, error: err });
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1313
1734
|
/**
|
|
1314
1735
|
* Get job data from Redis
|
|
1315
1736
|
*/
|
|
@@ -1785,7 +2206,7 @@ class ProcessorService {
|
|
|
1785
2206
|
const payload = JSON.parse((0, processor_utils_1.decrypt)(String(appEnv.auth.data), this.productBuilderService.fetchPrivateKey()));
|
|
1786
2207
|
let app = await this.fetchThirdPartyApp(access_tag);
|
|
1787
2208
|
const auth = app.auths.find((item) => item.tag === appEnv.auth.auth_tag);
|
|
1788
|
-
|
|
2209
|
+
debugLog('JAMESY', auth);
|
|
1789
2210
|
if (!auth) {
|
|
1790
2211
|
// throw an error
|
|
1791
2212
|
throw new Error(`Cannot find auth ${appEnv.auth.auth_tag} on environment ${appEnv.product_env_slug}`);
|
|
@@ -1806,7 +2227,7 @@ class ProcessorService {
|
|
|
1806
2227
|
request_base_url = env.base_url;
|
|
1807
2228
|
}
|
|
1808
2229
|
}
|
|
1809
|
-
|
|
2230
|
+
debugLog('payloadabi!!!!', payload);
|
|
1810
2231
|
const results = await this.sendActionRequest(request_base_url, url, payload, method, appEnv.app_env_slug);
|
|
1811
2232
|
const values = (0, processor_utils_1.encrypt)(JSON.stringify(results), this.productBuilderService.fetchPrivateKey());
|
|
1812
2233
|
const productApp = await this.productBuilderService.fetchApp(access_tag);
|
|
@@ -1980,7 +2401,8 @@ class ProcessorService {
|
|
|
1980
2401
|
let payloads;
|
|
1981
2402
|
let result;
|
|
1982
2403
|
//const product = this.productBuilderService.fetchProduct();
|
|
1983
|
-
|
|
2404
|
+
// Check cache for existing result (skip if early cache check already done)
|
|
2405
|
+
if (cache_tag && this.redisClient && !event.skipCacheFetch) {
|
|
1984
2406
|
const productCache = await this.productBuilderService.fetchCache(cache_tag);
|
|
1985
2407
|
if (!productCache) {
|
|
1986
2408
|
throw new Error('Invalid cache tag ');
|
|
@@ -2024,7 +2446,8 @@ class ProcessorService {
|
|
|
2024
2446
|
throw new Error('Invalid cache tag ');
|
|
2025
2447
|
}
|
|
2026
2448
|
const inputString = JSON.stringify(event.input);
|
|
2027
|
-
|
|
2449
|
+
// Fire-and-forget cache store
|
|
2450
|
+
this.addToCache({
|
|
2028
2451
|
input: inputString,
|
|
2029
2452
|
privateKey: this.productBuilderService.fetchPrivateKey(),
|
|
2030
2453
|
data: JSON.stringify(result),
|
|
@@ -2079,7 +2502,7 @@ class ProcessorService {
|
|
|
2079
2502
|
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Process http request - failed', failed_execution: true, data: { e: error, input: (0, processor_utils_1.anonymizeObject)(payloads) }, status: types_1.LogEventStatus.FAIL, app_id, action: event.event, start,
|
|
2080
2503
|
end }));
|
|
2081
2504
|
try {
|
|
2082
|
-
|
|
2505
|
+
debugLog(e);
|
|
2083
2506
|
const value = await this.addToFailureOutput(e, event, {
|
|
2084
2507
|
request_base_url,
|
|
2085
2508
|
resource,
|
|
@@ -2305,7 +2728,7 @@ class ProcessorService {
|
|
|
2305
2728
|
timeout: 15000,
|
|
2306
2729
|
withCredentials: false,
|
|
2307
2730
|
};
|
|
2308
|
-
|
|
2731
|
+
debugLog('REQUEST!!!!', request);
|
|
2309
2732
|
const response = await axios_1.default.request(request);
|
|
2310
2733
|
return response.data;
|
|
2311
2734
|
}
|
|
@@ -2316,7 +2739,10 @@ class ProcessorService {
|
|
|
2316
2739
|
}
|
|
2317
2740
|
async processStorage(action) {
|
|
2318
2741
|
//TODO: schema validation
|
|
2319
|
-
|
|
2742
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
2743
|
+
const { env, input, retries, event, product: product_tag, session, cache, preloadedBootstrap } = action;
|
|
2744
|
+
// event is "storage_tag:operation" (e.g. gcp-storage:upload); backend looks up storage by tag only
|
|
2745
|
+
const storageTag = event.includes(':') ? event.split(':')[0] : event;
|
|
2320
2746
|
const additional_logs = {
|
|
2321
2747
|
parent_tag: event,
|
|
2322
2748
|
type: types_1.LogEventTypes.STORAGE,
|
|
@@ -2337,19 +2763,26 @@ class ProcessorService {
|
|
|
2337
2763
|
const process_id = (0, processor_utils_1.generateObjectId)();
|
|
2338
2764
|
this.baseLogs = Object.assign({ product_tag: this.productTag, product_id: this.productId, workspace_id: this.workspace_id, env,
|
|
2339
2765
|
process_id, data: this.clone }, additional_logs);
|
|
2340
|
-
//
|
|
2341
|
-
const bootstrapData =
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2766
|
+
// Use preloaded bootstrap when provided (e.g. workflow batch prefetch), otherwise fetch
|
|
2767
|
+
const bootstrapData = preloadedBootstrap
|
|
2768
|
+
? preloadedBootstrap
|
|
2769
|
+
: await this.productBuilderService.bootstrapStorage({
|
|
2770
|
+
product_tag,
|
|
2771
|
+
env_slug: env,
|
|
2772
|
+
storage_tag: storageTag,
|
|
2773
|
+
});
|
|
2346
2774
|
// Initialize from bootstrap data
|
|
2347
2775
|
this.productId = bootstrapData.product_id;
|
|
2348
2776
|
this.processEnv = bootstrapData.env;
|
|
2349
2777
|
// Process session if provided - verify and resolve $Session{} references
|
|
2350
2778
|
if (session && bootstrapData.private_key) {
|
|
2351
2779
|
const { processSessionForExecution } = await Promise.resolve().then(() => __importStar(require('../../sessions')));
|
|
2352
|
-
const sessionResult = await processSessionForExecution(session, bootstrapData.private_key, input, env
|
|
2780
|
+
const sessionResult = await processSessionForExecution(session, bootstrapData.private_key, input, env, {
|
|
2781
|
+
fetchSessionSelector: async (sessionTag) => {
|
|
2782
|
+
const sessionConfig = await this.productBuilderService.fetchSession(sessionTag);
|
|
2783
|
+
return sessionConfig === null || sessionConfig === void 0 ? void 0 : sessionConfig.selector;
|
|
2784
|
+
},
|
|
2785
|
+
});
|
|
2353
2786
|
if (sessionResult.error) {
|
|
2354
2787
|
throw new Error(`Session validation failed: ${sessionResult.error}`);
|
|
2355
2788
|
}
|
|
@@ -2387,18 +2820,30 @@ class ProcessorService {
|
|
|
2387
2820
|
const result = await this.runStorage(payload, additional_logs, {
|
|
2388
2821
|
storage: bootstrapData.storage,
|
|
2389
2822
|
storage_env: bootstrapData.storage_env,
|
|
2823
|
+
private_key: bootstrapData.private_key,
|
|
2390
2824
|
});
|
|
2391
2825
|
this.end = Date.now();
|
|
2392
|
-
this.logService
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2826
|
+
if (this.logService) {
|
|
2827
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Storing file - success', data: { input: this.clone, result }, status: types_1.LogEventStatus.SUCCESS }));
|
|
2828
|
+
await this.writeResult(types_1.LogEventStatus.SUCCESS);
|
|
2829
|
+
await this.logService.publish();
|
|
2830
|
+
}
|
|
2831
|
+
return { process_id: this.process_id, output: result !== null && result !== void 0 ? result : {} };
|
|
2396
2832
|
}
|
|
2397
2833
|
catch (e) {
|
|
2398
|
-
|
|
2834
|
+
const err = e;
|
|
2835
|
+
const apiReason = (_b = (_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.errors;
|
|
2836
|
+
const reason = typeof apiReason === 'string'
|
|
2837
|
+
? apiReason
|
|
2838
|
+
: (_g = (_f = (_c = apiReason === null || apiReason === void 0 ? void 0 : apiReason.message) !== null && _c !== void 0 ? _c : (_e = (_d = err === null || err === void 0 ? void 0 : err.response) === null || _d === void 0 ? void 0 : _d.data) === null || _e === void 0 ? void 0 : _e.message) !== null && _f !== void 0 ? _f : err === null || err === void 0 ? void 0 : err.message) !== null && _g !== void 0 ? _g : String(e);
|
|
2839
|
+
const message = `Storing file - failed: ${reason}`;
|
|
2399
2840
|
this.end = Date.now();
|
|
2400
|
-
|
|
2401
|
-
|
|
2841
|
+
if (this.logService) {
|
|
2842
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message, data: { e, reason }, status: types_1.LogEventStatus.FAIL }));
|
|
2843
|
+
await this.logService.publish();
|
|
2844
|
+
}
|
|
2845
|
+
// Rethrow so the workflow step is marked failed and the executor can persist the error reason
|
|
2846
|
+
throw e;
|
|
2402
2847
|
}
|
|
2403
2848
|
}
|
|
2404
2849
|
async processMessageBrokerSubscribe(data) {
|
|
@@ -2451,6 +2896,7 @@ class ProcessorService {
|
|
|
2451
2896
|
}
|
|
2452
2897
|
}
|
|
2453
2898
|
async processMessageBrokerPublish(data) {
|
|
2899
|
+
var _a, _b, _c, _d;
|
|
2454
2900
|
const [brokerTag, topicTag] = data.event.split(':');
|
|
2455
2901
|
if (!brokerTag || !topicTag) {
|
|
2456
2902
|
throw new Error(`message broker events should be in the format broker_tag:event_tag`);
|
|
@@ -2477,11 +2923,26 @@ class ProcessorService {
|
|
|
2477
2923
|
await this.intializeProduct(additional_logs);
|
|
2478
2924
|
this.baseLogs.product_id = this.productId;
|
|
2479
2925
|
this.process_id = process_id;
|
|
2926
|
+
if (!this.logService) {
|
|
2927
|
+
this.logService = new logs_service_1.default({
|
|
2928
|
+
product_id: this.productId,
|
|
2929
|
+
workspace_id: this.workspace_id,
|
|
2930
|
+
public_key: this.public_key,
|
|
2931
|
+
user_id: this.user_id,
|
|
2932
|
+
token: this.token,
|
|
2933
|
+
env_type: this.environment,
|
|
2934
|
+
});
|
|
2935
|
+
}
|
|
2480
2936
|
// Process session if provided - verify and resolve $Session{} references
|
|
2481
2937
|
const privateKey = this.productBuilderService.fetchPrivateKey();
|
|
2482
2938
|
if (data.session && privateKey) {
|
|
2483
2939
|
const { processSessionForExecution } = await Promise.resolve().then(() => __importStar(require('../../sessions')));
|
|
2484
|
-
const sessionResult = await processSessionForExecution(data.session, privateKey, data.input, data.env
|
|
2940
|
+
const sessionResult = await processSessionForExecution(data.session, privateKey, data.input, data.env, {
|
|
2941
|
+
fetchSessionSelector: async (sessionTag) => {
|
|
2942
|
+
const sessionConfig = await this.productBuilderService.fetchSession(sessionTag);
|
|
2943
|
+
return sessionConfig === null || sessionConfig === void 0 ? void 0 : sessionConfig.selector;
|
|
2944
|
+
},
|
|
2945
|
+
});
|
|
2485
2946
|
if (sessionResult.error) {
|
|
2486
2947
|
throw new Error(`Session validation failed: ${sessionResult.error}`);
|
|
2487
2948
|
}
|
|
@@ -2506,24 +2967,47 @@ class ProcessorService {
|
|
|
2506
2967
|
};
|
|
2507
2968
|
const result = await this.runBrokerPublish(payload);
|
|
2508
2969
|
this.end = Date.now();
|
|
2509
|
-
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: '
|
|
2970
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Produce to topic - success', data: { input: (0, processor_utils_1.anonymizeObject)(this.clone), result: (0, processor_utils_1.anonymizeObject)(result !== null && result !== void 0 ? result : {}) }, status: types_1.LogEventStatus.SUCCESS }));
|
|
2510
2971
|
await this.writeResult(types_1.LogEventStatus.SUCCESS);
|
|
2511
|
-
|
|
2512
|
-
|
|
2972
|
+
// Fire-and-forget so step latency isn't dominated by log upload (20–50x slower otherwise)
|
|
2973
|
+
this.logService.publish().catch(() => { });
|
|
2974
|
+
return { process_id: this.process_id, output: Object.assign(Object.assign({}, (result !== null && result !== void 0 ? result : {})), { published: true }) };
|
|
2513
2975
|
}
|
|
2514
2976
|
catch (e) {
|
|
2515
|
-
|
|
2977
|
+
const err = e;
|
|
2978
|
+
const apiReason = (_b = (_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.errors;
|
|
2979
|
+
const reason = typeof apiReason === 'string'
|
|
2980
|
+
? apiReason
|
|
2981
|
+
: (_d = (_c = apiReason === null || apiReason === void 0 ? void 0 : apiReason.message) !== null && _c !== void 0 ? _c : err === null || err === void 0 ? void 0 : err.message) !== null && _d !== void 0 ? _d : String(e);
|
|
2982
|
+
const message = `Publishing to topic - failed: ${reason}`;
|
|
2983
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message, data: { e, reason }, status: types_1.LogEventStatus.FAIL }));
|
|
2516
2984
|
this.end = Date.now();
|
|
2517
2985
|
await this.logService.publish();
|
|
2518
|
-
|
|
2986
|
+
// Rethrow so the workflow step is marked failed and the executor can persist the error reason
|
|
2987
|
+
throw e;
|
|
2519
2988
|
}
|
|
2520
2989
|
}
|
|
2521
2990
|
async processJob(job, additional_logs = {}) {
|
|
2522
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2991
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
2992
|
+
await this.productBuilderService.initializeProductByTag(job.product);
|
|
2993
|
+
const productId = this.productBuilderService.fetchProductId();
|
|
2994
|
+
if (productId)
|
|
2995
|
+
this.productId = productId;
|
|
2523
2996
|
const productJob = await this.productBuilderService.fetchJob(job.event);
|
|
2524
2997
|
if (!productJob) {
|
|
2525
2998
|
throw new Error(`Job ${job.event} not found`);
|
|
2526
2999
|
}
|
|
3000
|
+
// Ensure logService exists so dispatch event is logged
|
|
3001
|
+
if (!this.logService && this.productId && this.workspace_id) {
|
|
3002
|
+
this.logService = new logs_service_1.default({
|
|
3003
|
+
product_id: this.productId,
|
|
3004
|
+
workspace_id: this.workspace_id,
|
|
3005
|
+
public_key: this.public_key,
|
|
3006
|
+
user_id: this.user_id,
|
|
3007
|
+
token: this.token,
|
|
3008
|
+
env_type: this.environment,
|
|
3009
|
+
});
|
|
3010
|
+
}
|
|
2527
3011
|
await this.validateActionDataMappingInput(job.input, productJob.type);
|
|
2528
3012
|
const NOW = Date.now();
|
|
2529
3013
|
// Treat anything above Jan 1, 2023 as a timestamp (to be safe and future-proof)
|
|
@@ -2745,15 +3229,79 @@ class ProcessorService {
|
|
|
2745
3229
|
// Add job input with the job ID for tracking
|
|
2746
3230
|
jobInput._job_id = jobId;
|
|
2747
3231
|
// Add job to queue
|
|
3232
|
+
if (!((_j = this.queues) === null || _j === void 0 ? void 0 : _j.jobs)) {
|
|
3233
|
+
throw new Error('Queues not configured. dispatch() requires a queue connection.');
|
|
3234
|
+
}
|
|
2748
3235
|
await this.queues.jobs.add(productJob.type, jobInput, options);
|
|
3236
|
+
// Record job execution in integrations (so jobexecutiontrackers table is populated)
|
|
3237
|
+
if (this.workspace_id && this.productId) {
|
|
3238
|
+
const auth = Object.assign(Object.assign({}, this.getUserAccess()), { access_key: this.accessKey });
|
|
3239
|
+
if (!this.accessKey) {
|
|
3240
|
+
console.warn('[processJob] Creating job execution without access_key — backend may return 401', { jobId });
|
|
3241
|
+
}
|
|
3242
|
+
const operation = typeof job.event === 'string' && job.event.includes(':')
|
|
3243
|
+
? job.event.split(':').pop()
|
|
3244
|
+
: (_k = job.event) !== null && _k !== void 0 ? _k : undefined;
|
|
3245
|
+
void this.processorApiService
|
|
3246
|
+
.createJobExecution({
|
|
3247
|
+
job_id: jobId,
|
|
3248
|
+
workspace_id: this.workspace_id,
|
|
3249
|
+
product_id: this.productId,
|
|
3250
|
+
product_tag: job.product,
|
|
3251
|
+
env: job.env,
|
|
3252
|
+
job_tag: job.event,
|
|
3253
|
+
job_type: productJob.type,
|
|
3254
|
+
phase: delay > 0 ? 'scheduled' : 'queued',
|
|
3255
|
+
scheduled_at: delay > 0 ? scheduled_at : undefined,
|
|
3256
|
+
triggered_by: job.triggered_by,
|
|
3257
|
+
input: job.input != null && typeof job.input === 'object'
|
|
3258
|
+
? (0, processor_utils_1.truncateInputForApi)(job.input)
|
|
3259
|
+
: undefined,
|
|
3260
|
+
operation,
|
|
3261
|
+
}, auth)
|
|
3262
|
+
.catch((err) => console.error('[processJob] job execution create failed', { jobId, error: err }));
|
|
3263
|
+
}
|
|
3264
|
+
else {
|
|
3265
|
+
const missing = [];
|
|
3266
|
+
if (!this.workspace_id)
|
|
3267
|
+
missing.push('workspace_id');
|
|
3268
|
+
if (!this.productId)
|
|
3269
|
+
missing.push('productId');
|
|
3270
|
+
console.warn('[processJob] Skipping job execution create — missing:', missing.join(', '), { jobId });
|
|
3271
|
+
}
|
|
3272
|
+
// Log dispatch event so it shows in logs
|
|
3273
|
+
const dispatchStatus = delay > 0 ? 'scheduled' : 'queued';
|
|
3274
|
+
if (this.logService) {
|
|
3275
|
+
this.logService.add({
|
|
3276
|
+
product_tag: job.product,
|
|
3277
|
+
product_id: this.productId,
|
|
3278
|
+
workspace_id: this.workspace_id,
|
|
3279
|
+
env: job.env,
|
|
3280
|
+
process_id: jobId,
|
|
3281
|
+
type: types_1.LogEventTypes.JOB,
|
|
3282
|
+
message: 'Job dispatched',
|
|
3283
|
+
data: {
|
|
3284
|
+
job_id: jobId,
|
|
3285
|
+
job_type: productJob.type,
|
|
3286
|
+
event: job.event,
|
|
3287
|
+
status: dispatchStatus,
|
|
3288
|
+
scheduled_at,
|
|
3289
|
+
recurring: isRecurring,
|
|
3290
|
+
next_run_at,
|
|
3291
|
+
},
|
|
3292
|
+
status: types_1.LogEventStatus.SUCCESS,
|
|
3293
|
+
});
|
|
3294
|
+
await this.logService.publish();
|
|
3295
|
+
}
|
|
2749
3296
|
return {
|
|
2750
3297
|
job_id: jobId,
|
|
2751
|
-
status:
|
|
3298
|
+
status: dispatchStatus,
|
|
2752
3299
|
scheduled_at,
|
|
2753
3300
|
recurring: isRecurring,
|
|
2754
3301
|
next_run_at,
|
|
2755
3302
|
};
|
|
2756
3303
|
}
|
|
3304
|
+
/** Expo: expoClient() is already a module-level singleton (one axios instance), so no per-run pool needed. */
|
|
2757
3305
|
async sendExpoNotification(payload, device_tokens) {
|
|
2758
3306
|
const message = {
|
|
2759
3307
|
to: device_tokens,
|
|
@@ -2778,12 +3326,8 @@ class ProcessorService {
|
|
|
2778
3326
|
tokens: device_tokens,
|
|
2779
3327
|
};
|
|
2780
3328
|
try {
|
|
2781
|
-
const
|
|
2782
|
-
|
|
2783
|
-
admin.initializeApp({
|
|
2784
|
-
credential: admin.credential.cert(serviceAccount),
|
|
2785
|
-
});
|
|
2786
|
-
await admin.messaging().sendMulticast(message);
|
|
3329
|
+
const app = await this.getFirebaseApp(credentials);
|
|
3330
|
+
await app.messaging().sendEachForMulticast(message);
|
|
2787
3331
|
}
|
|
2788
3332
|
catch (e) {
|
|
2789
3333
|
if (this.environment === types_1.EnvType.PRODUCTION) {
|
|
@@ -2791,49 +3335,73 @@ class ProcessorService {
|
|
|
2791
3335
|
}
|
|
2792
3336
|
}
|
|
2793
3337
|
}
|
|
2794
|
-
async ProcessExpoNotification(notification, template, payload, additional_logs) {
|
|
3338
|
+
async ProcessExpoNotification(notification, template, payload, additional_logs, logType = types_1.LogEventTypes.PUSH) {
|
|
3339
|
+
var _a, _b;
|
|
3340
|
+
debugLog('[ProcessExpoNotification] ENTRY', { deviceTokens: (_a = payload === null || payload === void 0 ? void 0 : payload.device_tokens) === null || _a === void 0 ? void 0 : _a.length, logType });
|
|
2795
3341
|
(0, processor_utils_1.validateNotification)(template, payload);
|
|
2796
|
-
|
|
3342
|
+
debugLog('[ProcessExpoNotification] Validation passed');
|
|
2797
3343
|
const { title, body, data } = (0, processor_utils_1.generateNotificationTemplate)(template, payload);
|
|
2798
|
-
|
|
3344
|
+
debugLog('[ProcessExpoNotification] Template generated', { title: (_b = title === null || title === void 0 ? void 0 : title.slice) === null || _b === void 0 ? void 0 : _b.call(title, 0, 40), bodyLength: body === null || body === void 0 ? void 0 : body.length });
|
|
3345
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: logType, message: 'Generate notification template - success', data: { title, body, data }, status: types_1.LogEventStatus.SUCCESS }));
|
|
3346
|
+
debugLog('[ProcessExpoNotification] Sending to Expo');
|
|
2799
3347
|
await this.sendExpoNotification({ title, body, data }, payload.device_tokens);
|
|
2800
|
-
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Send Expo notification - success', data: { title, body, data
|
|
3348
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: logType, message: 'Send Expo notification - success', data: { title, body, data }, status: types_1.LogEventStatus.SUCCESS }));
|
|
2801
3349
|
}
|
|
2802
|
-
async ProcessFirebaseNotification(notification, template, payload, additional_logs) {
|
|
3350
|
+
async ProcessFirebaseNotification(notification, template, payload, additional_logs, logType = types_1.LogEventTypes.PUSH) {
|
|
3351
|
+
var _a, _b;
|
|
3352
|
+
debugLog('[ProcessFirebaseNotification] ENTRY', { deviceTokens: (_a = payload === null || payload === void 0 ? void 0 : payload.device_tokens) === null || _a === void 0 ? void 0 : _a.length, logType });
|
|
2803
3353
|
(0, processor_utils_1.validateNotification)(template, payload);
|
|
2804
|
-
|
|
3354
|
+
debugLog('[ProcessFirebaseNotification] Validation passed');
|
|
2805
3355
|
const { title, body, data } = (0, processor_utils_1.generateNotificationTemplate)(template, payload);
|
|
2806
|
-
|
|
3356
|
+
debugLog('[ProcessFirebaseNotification] Template generated', { title: (_b = title === null || title === void 0 ? void 0 : title.slice) === null || _b === void 0 ? void 0 : _b.call(title, 0, 40), bodyLength: body === null || body === void 0 ? void 0 : body.length });
|
|
3357
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: logType, message: 'Generate Firebase notification template - success', data: { title, body, data }, status: types_1.LogEventStatus.SUCCESS }));
|
|
3358
|
+
debugLog('[ProcessFirebaseNotification] Sending to Firebase');
|
|
2807
3359
|
await this.sendFirebaseNotification({ title, body, data }, payload.device_tokens, notification.credentials);
|
|
2808
|
-
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Send Firebase notification - success', data: { title, body
|
|
3360
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: logType, message: 'Send Firebase notification - success', data: { title, body, data }, status: types_1.LogEventStatus.SUCCESS }));
|
|
2809
3361
|
}
|
|
2810
3362
|
async runNotification(notification, additional_logs, bootstrapData) {
|
|
2811
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
3363
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
|
|
2812
3364
|
const { event } = notification;
|
|
2813
3365
|
const input = notification.input;
|
|
3366
|
+
const logMessageLog = (channelType, status, err) => {
|
|
3367
|
+
if (!this.productId || !this.workspace_id || (bootstrapData === null || bootstrapData === void 0 ? void 0 : bootstrapData.product_tag) == null || (bootstrapData === null || bootstrapData === void 0 ? void 0 : bootstrapData.env) == null)
|
|
3368
|
+
return;
|
|
3369
|
+
this.fireAndForgetLogNotificationMessage(Object.assign({ workspace_id: this.workspace_id, product_id: this.productId, product_tag: bootstrapData.product_tag, env: bootstrapData.env, notification_tag: event, input: (input !== null && input !== void 0 ? input : {}), type: channelType, status, process_id: bootstrapData.process_id, retries: 0, session: bootstrapData.session, cache: bootstrapData.cache }, (status === 'failed' && err !== undefined && { error: err instanceof Error ? err.message : String(err) })));
|
|
3370
|
+
};
|
|
3371
|
+
debugLog('[runNotification] ENTRY', { event, env: (_a = notification.env) === null || _a === void 0 ? void 0 : _a.slug, hasBootstrap: !!bootstrapData, inputKeys: input ? Object.keys(input) : [] });
|
|
2814
3372
|
try {
|
|
2815
3373
|
let notificationEvent;
|
|
2816
3374
|
let message;
|
|
2817
3375
|
let envConfig;
|
|
2818
3376
|
// Use bootstrap data if provided, otherwise fetch via API
|
|
2819
3377
|
if (bootstrapData) {
|
|
3378
|
+
debugLog('[runNotification] Using bootstrap data');
|
|
2820
3379
|
notificationEvent = bootstrapData.notification;
|
|
2821
3380
|
message = bootstrapData.message;
|
|
2822
3381
|
envConfig = bootstrapData.env_config;
|
|
2823
3382
|
}
|
|
2824
3383
|
else {
|
|
3384
|
+
debugLog('[runNotification] Fetching notification via API');
|
|
2825
3385
|
// Fallback to original API-based fetching
|
|
2826
3386
|
notificationEvent = (await this.productBuilderService.fetchNotification(event.split(':')[0]));
|
|
2827
3387
|
message = await this.productBuilderService.fetchNotificationMessage(event);
|
|
2828
3388
|
if (!message) {
|
|
2829
3389
|
throw new Error(`Message ${event} not found`);
|
|
2830
3390
|
}
|
|
2831
|
-
envConfig = (
|
|
3391
|
+
envConfig = (_b = notificationEvent.envs) === null || _b === void 0 ? void 0 : _b.find((data) => data.slug === notification.env.slug);
|
|
2832
3392
|
}
|
|
2833
3393
|
if (!envConfig) {
|
|
3394
|
+
debugLog('[runNotification] ERROR: env config not found', { envSlug: (_c = notification.env) === null || _c === void 0 ? void 0 : _c.slug });
|
|
2834
3395
|
throw new Error(`Notification env config for ${notification.env.slug} not found`);
|
|
2835
3396
|
}
|
|
2836
3397
|
let { push_notifications: notifications, emails, callbacks, sms: smses, } = envConfig;
|
|
3398
|
+
debugLog('[runNotification] Env config channels', {
|
|
3399
|
+
hasPush: !!notifications,
|
|
3400
|
+
pushType: notifications === null || notifications === void 0 ? void 0 : notifications.type,
|
|
3401
|
+
hasEmails: !!emails,
|
|
3402
|
+
hasCallbacks: !!callbacks,
|
|
3403
|
+
hasSms: !!smses,
|
|
3404
|
+
});
|
|
2837
3405
|
// Resolve any $Secret{} references in notification configs
|
|
2838
3406
|
const secretsService = (0, secrets_1.getSecretsService)();
|
|
2839
3407
|
if (secretsService) {
|
|
@@ -2855,36 +3423,73 @@ class ProcessorService {
|
|
|
2855
3423
|
}
|
|
2856
3424
|
}
|
|
2857
3425
|
const { push_notification: push, email, callback, sms } = message;
|
|
2858
|
-
|
|
2859
|
-
|
|
3426
|
+
debugLog('[runNotification] Message template flags', { push: !!push, email: !!email, callback: !!callback, sms: !!sms });
|
|
3427
|
+
debugLog('[runNotification] Input channels present', {
|
|
3428
|
+
push_notification: !!input.push_notification,
|
|
3429
|
+
email: !!input.email,
|
|
3430
|
+
callback: !!input.callback,
|
|
3431
|
+
sms: !!input.sms,
|
|
3432
|
+
});
|
|
3433
|
+
if (!push && (input.push_notification || notifications)) {
|
|
3434
|
+
debugLog('[runNotification] PUSH: skipped (message has no push template)', { hasInputPush: !!input.push_notification, hasNotifications: !!notifications });
|
|
3435
|
+
}
|
|
3436
|
+
if (push && !input.push_notification) {
|
|
3437
|
+
debugLog('[runNotification] PUSH: skipped (no input.push_notification)');
|
|
3438
|
+
}
|
|
3439
|
+
if (push && input.push_notification) {
|
|
3440
|
+
debugLog('[runNotification] PUSH: resolving title/body/data');
|
|
2860
3441
|
input.push_notification.title = (0, processor_utils_1.replacePlaceholderString)(push.title, input.push_notification.title || {});
|
|
2861
3442
|
input.push_notification.body = await this.generatePayload(input.push_notification.body || {}, notification, additional_logs, message.push_notification_data.filter((data) => data.parent_key === 'body') || []);
|
|
2862
3443
|
input.push_notification.data = await this.generatePayload(input.push_notification.data || {}, notification, additional_logs, message.push_notification_data.filter((data) => data.parent_key === 'data') || []);
|
|
2863
3444
|
}
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
3445
|
+
if (push && input.push_notification) {
|
|
3446
|
+
const validationPayload = (await this.inputService.parseJson({
|
|
3447
|
+
data: Object.assign(Object.assign({}, input.push_notification.title), input.push_notification.body),
|
|
3448
|
+
expected: types_1.ExpectedValues.PARSEINPUT,
|
|
3449
|
+
}));
|
|
3450
|
+
//await this.inputService.validateInput(validationPayload, message.push_notification_data, "Push Notifications");
|
|
3451
|
+
}
|
|
3452
|
+
if (push && input.push_notification && (notifications === null || notifications === void 0 ? void 0 : notifications.type) !== types_1.Notifiers.EXPO && (notifications === null || notifications === void 0 ? void 0 : notifications.type) !== types_1.Notifiers.FIREBASE) {
|
|
3453
|
+
debugLog('[runNotification] PUSH: skipped (not Expo or Firebase)', { notificationsType: notifications === null || notifications === void 0 ? void 0 : notifications.type });
|
|
3454
|
+
this.lastNotificationFailureType = types_1.LogEventTypes.PUSH;
|
|
3455
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.PUSH, message: 'Push skipped - no Expo or Firebase provider configured', data: { push_type: notifications === null || notifications === void 0 ? void 0 : notifications.type }, status: types_1.LogEventStatus.SUCCESS }));
|
|
3456
|
+
}
|
|
3457
|
+
if (push && input.push_notification && (notifications === null || notifications === void 0 ? void 0 : notifications.type) === types_1.Notifiers.EXPO) {
|
|
3458
|
+
debugLog('[runNotification] PUSH: processing Expo');
|
|
3459
|
+
this.lastNotificationFailureType = types_1.LogEventTypes.PUSH;
|
|
2870
3460
|
try {
|
|
2871
3461
|
await this.ProcessExpoNotification(notifications, message, input.push_notification, additional_logs);
|
|
2872
|
-
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { successful_execution: true, message: 'Processing Expo notification - success', data: {}, status: types_1.LogEventStatus.SUCCESS }));
|
|
3462
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.PUSH, successful_execution: true, message: 'Processing Expo notification - success', data: {}, status: types_1.LogEventStatus.SUCCESS }));
|
|
3463
|
+
logMessageLog('push', 'sent');
|
|
3464
|
+
debugLog('[runNotification] PUSH (Expo): success');
|
|
2873
3465
|
}
|
|
2874
3466
|
catch (e) {
|
|
2875
|
-
|
|
3467
|
+
debugLog('[runNotification] PUSH (Expo): failed', e);
|
|
3468
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.PUSH, failed_execution: true, message: 'Processing Expo notification - failed', data: {}, status: types_1.LogEventStatus.FAIL }));
|
|
3469
|
+
logMessageLog('push', 'failed', e);
|
|
2876
3470
|
}
|
|
2877
3471
|
}
|
|
2878
|
-
if (push && notifications.type === types_1.Notifiers.FIREBASE) {
|
|
3472
|
+
if (push && input.push_notification && (notifications === null || notifications === void 0 ? void 0 : notifications.type) === types_1.Notifiers.FIREBASE) {
|
|
3473
|
+
debugLog('[runNotification] PUSH: processing Firebase');
|
|
3474
|
+
this.lastNotificationFailureType = types_1.LogEventTypes.PUSH;
|
|
2879
3475
|
try {
|
|
2880
3476
|
await this.ProcessFirebaseNotification(notifications, message, input.push_notification, additional_logs);
|
|
2881
|
-
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { successful_execution: true, message: 'Processing Firebase notification - success', data: { notification
|
|
3477
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.PUSH, successful_execution: true, message: 'Processing Firebase notification - success', data: { notification }, status: types_1.LogEventStatus.SUCCESS }));
|
|
3478
|
+
logMessageLog('push', 'sent');
|
|
3479
|
+
debugLog('[runNotification] PUSH (Firebase): success');
|
|
2882
3480
|
}
|
|
2883
3481
|
catch (e) {
|
|
2884
|
-
|
|
3482
|
+
debugLog('[runNotification] PUSH (Firebase): failed', e);
|
|
3483
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.PUSH, failed_execution: true, message: 'Processing Firebase notification - failed', data: { notification, input: input.push_notification }, status: types_1.LogEventStatus.FAIL }));
|
|
3484
|
+
logMessageLog('push', 'failed', e);
|
|
2885
3485
|
}
|
|
2886
3486
|
}
|
|
2887
|
-
if (email && emails) {
|
|
3487
|
+
if (email && emails && !input.email) {
|
|
3488
|
+
debugLog('[runNotification] EMAIL: skipped (no input.email)');
|
|
3489
|
+
}
|
|
3490
|
+
if (email && emails && input.email) {
|
|
3491
|
+
debugLog('[runNotification] EMAIL: processing (subject, template, recipients)');
|
|
3492
|
+
this.lastNotificationFailureType = types_1.LogEventTypes.EMAIL;
|
|
2888
3493
|
input.email.subject = await this.generatePayload(input.email.subject, notification, additional_logs, message.email_data.filter((data) => data.parent_key === 'subject'));
|
|
2889
3494
|
input.email.template = await this.generatePayload(input.email.template, notification, additional_logs, message.email_data.filter((data) => data.parent_key === 'template'));
|
|
2890
3495
|
input.email.recipients = await Promise.all(input.email.recipients.map(async (email) => await this.generateStringValues(email, notification.event, additional_logs)));
|
|
@@ -2895,14 +3500,13 @@ class ProcessorService {
|
|
|
2895
3500
|
//await this.inputService.validateInput(validationPayload, message.email_data);
|
|
2896
3501
|
input.email.recipients.map((email) => this.inputService.validateEmailString({ key: 'to', value: email }));
|
|
2897
3502
|
const { sender_email: from } = emails, auth = __rest(emails, ["sender_email"]);
|
|
2898
|
-
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Attempt email auth fetch - success', data: { from: (0, processor_utils_1.anonymizeValue)(from), host: (0, processor_utils_1.anonymizeValue)(auth.host), port: (0, processor_utils_1.anonymizeValue)(auth.port) }, status: types_1.LogEventStatus.SUCCESS }));
|
|
2899
3503
|
const templateMaker = (0, handlebars_1.compile)(email.template);
|
|
2900
3504
|
const template = templateMaker(input.email.template);
|
|
2901
3505
|
const subject = (0, processor_utils_1.replacePlaceholderString)(email.subject, input.email.subject || {});
|
|
2902
|
-
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Generate email template - success', data: {
|
|
2903
|
-
template
|
|
2904
|
-
subject
|
|
2905
|
-
input:
|
|
3506
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.EMAIL, message: 'Generate email template - success', data: {
|
|
3507
|
+
template,
|
|
3508
|
+
subject,
|
|
3509
|
+
input: input.email,
|
|
2906
3510
|
}, status: types_1.LogEventStatus.SUCCESS }));
|
|
2907
3511
|
const mailOptions = {
|
|
2908
3512
|
from,
|
|
@@ -2910,57 +3514,115 @@ class ProcessorService {
|
|
|
2910
3514
|
subject,
|
|
2911
3515
|
template,
|
|
2912
3516
|
};
|
|
3517
|
+
debugLog('[runNotification] EMAIL: sending mail', { to: (_d = mailOptions.to) === null || _d === void 0 ? void 0 : _d.length, subject: (_f = (_e = mailOptions.subject) === null || _e === void 0 ? void 0 : _e.slice) === null || _f === void 0 ? void 0 : _f.call(_e, 0, 50) });
|
|
2913
3518
|
try {
|
|
2914
|
-
const transporter = await
|
|
2915
|
-
|
|
2916
|
-
|
|
3519
|
+
const transporter = await this.getMailTransporter(auth);
|
|
3520
|
+
await transporter.sendMail(mailOptions);
|
|
3521
|
+
logMessageLog('email', 'sent');
|
|
2917
3522
|
}
|
|
2918
3523
|
catch (e) {
|
|
2919
|
-
|
|
3524
|
+
debugLog('[runNotification] EMAIL: send failed', e);
|
|
3525
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.EMAIL, failed_execution: true, message: 'Send email - failed', data: { e }, status: types_1.LogEventStatus.FAIL }));
|
|
3526
|
+
logMessageLog('email', 'failed', e);
|
|
2920
3527
|
}
|
|
2921
3528
|
}
|
|
2922
|
-
if (callback && callbacks) {
|
|
3529
|
+
if (callback && !callbacks) {
|
|
3530
|
+
debugLog('[runNotification] CALLBACK: skipped (no callback config in env)');
|
|
3531
|
+
this.lastNotificationFailureType = types_1.LogEventTypes.CALLBACKS;
|
|
3532
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.CALLBACKS, message: 'Callback skipped - no callback config in env', data: {}, status: types_1.LogEventStatus.SUCCESS }));
|
|
3533
|
+
}
|
|
3534
|
+
if (callback && callbacks && !input.callback) {
|
|
3535
|
+
debugLog('[runNotification] CALLBACK: skipped (no input.callback)');
|
|
3536
|
+
}
|
|
3537
|
+
if (callback && callbacks && input.callback) {
|
|
3538
|
+
debugLog('[runNotification] CALLBACK: processing', { url: callbacks.url, method: callbacks.method });
|
|
3539
|
+
this.lastNotificationFailureType = types_1.LogEventTypes.CALLBACKS;
|
|
2923
3540
|
const payload = {
|
|
2924
|
-
query: Object.assign(Object.assign({}, (
|
|
2925
|
-
headers: Object.assign(Object.assign({}, (
|
|
2926
|
-
params: Object.assign(Object.assign({}, (
|
|
2927
|
-
body: Object.assign(Object.assign({}, (
|
|
3541
|
+
query: Object.assign(Object.assign({}, (_g = input.callback) === null || _g === void 0 ? void 0 : _g.query), (_h = callbacks.auth) === null || _h === void 0 ? void 0 : _h.query),
|
|
3542
|
+
headers: Object.assign(Object.assign({}, (_j = input.callback) === null || _j === void 0 ? void 0 : _j.headers), (_k = callbacks.auth) === null || _k === void 0 ? void 0 : _k.headers),
|
|
3543
|
+
params: Object.assign(Object.assign({}, (_l = input.callback) === null || _l === void 0 ? void 0 : _l.params), (_m = callbacks.auth) === null || _m === void 0 ? void 0 : _m.params),
|
|
3544
|
+
body: Object.assign(Object.assign({}, (_o = input.callback) === null || _o === void 0 ? void 0 : _o.body), (_p = callbacks.auth) === null || _p === void 0 ? void 0 : _p.body),
|
|
2928
3545
|
};
|
|
2929
3546
|
input.callback.body = await this.generatePayload(payload.body, notification, additional_logs, message.callback_data.filter((data) => data.parent_key === 'body'));
|
|
2930
3547
|
input.callback.query = await this.generatePayload(payload.query, notification, additional_logs, message.callback_data.filter((data) => data.parent_key === 'query'));
|
|
2931
|
-
input.callback.params = await this.generatePayload(payload.
|
|
2932
|
-
input.callback.headers = await this.generatePayload(payload.
|
|
3548
|
+
input.callback.params = await this.generatePayload(payload.params, notification, additional_logs, message.callback_data.filter((data) => data.parent_key === 'params'));
|
|
3549
|
+
input.callback.headers = await this.generatePayload(payload.headers, notification, additional_logs, message.callback_data.filter((data) => data.parent_key === 'headers'));
|
|
2933
3550
|
const validationPayload = (await this.inputService.parseJson({
|
|
2934
3551
|
data: input.callback,
|
|
2935
3552
|
expected: types_1.ExpectedValues.PARSEINPUT,
|
|
2936
3553
|
}));
|
|
2937
3554
|
//this.inputService.validateInput(validationPayload, message.callback_data);
|
|
2938
|
-
const
|
|
3555
|
+
const callbackUrl = callbacks === null || callbacks === void 0 ? void 0 : callbacks.url;
|
|
3556
|
+
if (callbackUrl == null || callbackUrl === '') {
|
|
3557
|
+
throw new Error(`Callback config has no url. Env callback config: ${JSON.stringify({ url: callbacks === null || callbacks === void 0 ? void 0 : callbacks.url, method: callbacks === null || callbacks === void 0 ? void 0 : callbacks.method, keys: callbacks ? Object.keys(callbacks) : [] })}`);
|
|
3558
|
+
}
|
|
3559
|
+
const url = new URL(callbackUrl);
|
|
3560
|
+
const requestPayload = {
|
|
3561
|
+
query: Object.assign(Object.assign({}, (_q = input.callback) === null || _q === void 0 ? void 0 : _q.query), (_r = callbacks.auth) === null || _r === void 0 ? void 0 : _r.query),
|
|
3562
|
+
headers: Object.assign(Object.assign({}, (_s = input.callback) === null || _s === void 0 ? void 0 : _s.headers), (_t = callbacks.auth) === null || _t === void 0 ? void 0 : _t.headers),
|
|
3563
|
+
params: Object.assign(Object.assign({}, (_u = input.callback) === null || _u === void 0 ? void 0 : _u.params), (_v = callbacks.auth) === null || _v === void 0 ? void 0 : _v.params),
|
|
3564
|
+
body: Object.assign(Object.assign({}, (_w = input.callback) === null || _w === void 0 ? void 0 : _w.body), (_x = callbacks.auth) === null || _x === void 0 ? void 0 : _x.body),
|
|
3565
|
+
};
|
|
2939
3566
|
try {
|
|
2940
|
-
await this.sendActionRequest(url.origin, url.pathname,
|
|
2941
|
-
|
|
3567
|
+
await this.sendActionRequest(url.origin, url.pathname, requestPayload, callbacks.method, '');
|
|
3568
|
+
debugLog('[runNotification] CALLBACK: success');
|
|
3569
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.CALLBACKS, successful_execution: true, name: 'Send callback - success', data: {}, status: types_1.LogEventStatus.SUCCESS }));
|
|
3570
|
+
logMessageLog('callback', 'sent');
|
|
2942
3571
|
}
|
|
2943
3572
|
catch (e) {
|
|
2944
|
-
|
|
3573
|
+
debugLog('[runNotification] CALLBACK: failed', e);
|
|
3574
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.CALLBACKS, failed_execution: true, message: 'Send callback - failed', data: { e }, status: types_1.LogEventStatus.FAIL }));
|
|
3575
|
+
logMessageLog('callback', 'failed', e);
|
|
2945
3576
|
}
|
|
2946
3577
|
}
|
|
2947
|
-
if (sms && smses) {
|
|
3578
|
+
if (sms && !smses) {
|
|
3579
|
+
debugLog('[runNotification] SMS: skipped (no SMS config in env)');
|
|
3580
|
+
this.lastNotificationFailureType = types_1.LogEventTypes.SMS;
|
|
3581
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.SMS, message: 'SMS skipped - no SMS config in env', data: {}, status: types_1.LogEventStatus.SUCCESS }));
|
|
3582
|
+
}
|
|
3583
|
+
if (sms && smses && !input.sms) {
|
|
3584
|
+
debugLog('[runNotification] SMS: skipped (no input.sms)');
|
|
3585
|
+
}
|
|
3586
|
+
if (sms && smses && input.sms) {
|
|
3587
|
+
debugLog('[runNotification] SMS: processing', { recipients: (_y = input.sms.recipients) === null || _y === void 0 ? void 0 : _y.length });
|
|
3588
|
+
this.lastNotificationFailureType = types_1.LogEventTypes.SMS;
|
|
2948
3589
|
try {
|
|
2949
3590
|
input.sms.body = (await (0, processor_utils_1.replacePlaceholderString)(sms, input.sms.body));
|
|
2950
|
-
const
|
|
2951
|
-
|
|
2952
|
-
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { name: 'Send sms - initiated', data: { message: input.sms.body, config: (0, processor_utils_1.anonymizeObject)(smses) }, status: types_1.LogEventStatus.SUCCESS }));
|
|
3591
|
+
const smsClient = await this.getSmsClient(smses);
|
|
3592
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.SMS, name: 'Send sms - initiated', data: { message: input.sms.body, config: smses }, status: types_1.LogEventStatus.SUCCESS }));
|
|
2953
3593
|
const res = await smsClient.sendMessage(input.sms.body, input.sms.recipients);
|
|
2954
|
-
|
|
3594
|
+
if (res && res.success !== undefined && !res.success) {
|
|
3595
|
+
throw new Error(res.error);
|
|
3596
|
+
}
|
|
3597
|
+
debugLog('[runNotification] SMS: success', { res: typeof res });
|
|
3598
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.SMS, name: 'Send sms - success', successful_execution: true, data: res, status: types_1.LogEventStatus.SUCCESS }));
|
|
3599
|
+
logMessageLog('sms', 'sent');
|
|
2955
3600
|
}
|
|
2956
3601
|
catch (e) {
|
|
2957
|
-
|
|
3602
|
+
debugLog('[runNotification] SMS: failed', e);
|
|
3603
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: types_1.LogEventTypes.SMS, failed_execution: true, message: 'Process sms - failed', data: { e }, status: types_1.LogEventStatus.FAIL }));
|
|
3604
|
+
logMessageLog('sms', 'failed', e);
|
|
2958
3605
|
}
|
|
2959
3606
|
}
|
|
2960
|
-
|
|
3607
|
+
debugLog('[runNotification] DONE: all channels processed', { lastChannelType: (_z = this.lastNotificationFailureType) !== null && _z !== void 0 ? _z : 'NOTIFICATIONS' });
|
|
3608
|
+
// Only add aggregate success log when we have a subtype (no NOTIFICATIONS-typed logs)
|
|
3609
|
+
/*if (this.lastNotificationFailureType != null) {
|
|
3610
|
+
this.logService.add({
|
|
3611
|
+
...this.baseLogs,
|
|
3612
|
+
...additional_logs,
|
|
3613
|
+
type: this.lastNotificationFailureType,
|
|
3614
|
+
message: 'Attempt notification - success',
|
|
3615
|
+
data: notification,
|
|
3616
|
+
status: LogEventStatus.SUCCESS,
|
|
3617
|
+
});
|
|
3618
|
+
}*/
|
|
2961
3619
|
}
|
|
2962
3620
|
catch (e) {
|
|
2963
|
-
|
|
3621
|
+
debugLog('[runNotification] ERROR', { error: e, lastChannelType: (_0 = this.lastNotificationFailureType) !== null && _0 !== void 0 ? _0 : 'NOTIFICATIONS' });
|
|
3622
|
+
// Only add aggregate failure log when we have a subtype (no NOTIFICATIONS-typed logs)
|
|
3623
|
+
if (this.lastNotificationFailureType != null) {
|
|
3624
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: this.lastNotificationFailureType, failed_execution: true, message: 'Attempt notification - failed', data: { e: e.toString() }, status: types_1.LogEventStatus.FAIL }));
|
|
3625
|
+
}
|
|
2964
3626
|
//await this.logService.publish();
|
|
2965
3627
|
throw e;
|
|
2966
3628
|
}
|
|
@@ -3070,9 +3732,10 @@ class ProcessorService {
|
|
|
3070
3732
|
storageEnv = bootstrapData.storage_env;
|
|
3071
3733
|
}
|
|
3072
3734
|
else {
|
|
3073
|
-
// Fallback to original API-based fetching
|
|
3735
|
+
// Fallback to original API-based fetching (event may be "storage_tag:operation", lookup by storage tag only)
|
|
3736
|
+
const storageTag = event.includes(':') ? event.split(':')[0] : event;
|
|
3074
3737
|
await this.intializeProduct(additional_logs);
|
|
3075
|
-
storage = await this.productBuilderService.fetchStorage(
|
|
3738
|
+
storage = await this.productBuilderService.fetchStorage(storageTag);
|
|
3076
3739
|
storageEnv = storage.envs.find((el) => el.slug === env.slug);
|
|
3077
3740
|
}
|
|
3078
3741
|
if (!storageEnv) {
|
|
@@ -3105,14 +3768,15 @@ class ProcessorService {
|
|
|
3105
3768
|
input.mimeType = input.mimeType
|
|
3106
3769
|
? await this.generateStringValues(input.mimeType, '', additional_logs, [])
|
|
3107
3770
|
: undefined;
|
|
3108
|
-
result = await this.processStorageRequest(data, input, storageEnv, additional_logs);
|
|
3771
|
+
result = await this.processStorageRequest(data, input, storageEnv, additional_logs, bootstrapData === null || bootstrapData === void 0 ? void 0 : bootstrapData.private_key);
|
|
3109
3772
|
if (cache_tag && this.redisClient) {
|
|
3110
3773
|
const productCache = await this.productBuilderService.fetchCache(cache_tag);
|
|
3111
3774
|
if (!productCache) {
|
|
3112
3775
|
throw new Error('Invalid cache tag ');
|
|
3113
3776
|
}
|
|
3114
3777
|
const inputString = JSON.stringify(input);
|
|
3115
|
-
|
|
3778
|
+
// Fire-and-forget cache store
|
|
3779
|
+
this.addToCache({
|
|
3116
3780
|
input: inputString,
|
|
3117
3781
|
privateKey: this.productBuilderService.fetchPrivateKey(),
|
|
3118
3782
|
data: JSON.stringify(result),
|
|
@@ -3158,10 +3822,25 @@ class ProcessorService {
|
|
|
3158
3822
|
}
|
|
3159
3823
|
url = queueUrl.url;
|
|
3160
3824
|
}
|
|
3161
|
-
this.logService.add(
|
|
3825
|
+
/*this.logService.add({
|
|
3826
|
+
...this.baseLogs,
|
|
3827
|
+
...additional_logs,
|
|
3828
|
+
message: 'Fetch broker details - success',
|
|
3829
|
+
data: { event, broker },
|
|
3830
|
+
status: LogEventStatus.SUCCESS,
|
|
3831
|
+
});*/
|
|
3832
|
+
let brokerConfig = brokerEnv.config;
|
|
3833
|
+
if (brokerConfig && (0, secrets_1.mightContainSecrets)(brokerConfig)) {
|
|
3834
|
+
const secretsService = (0, secrets_1.getSecretsService)();
|
|
3835
|
+
if (!secretsService) {
|
|
3836
|
+
throw new Error('Broker configuration contains secret references ($secret{...}) but secrets service is not initialized. Ensure secrets are configured for this environment.');
|
|
3837
|
+
}
|
|
3838
|
+
const resolved = await secretsService.resolve(brokerConfig, { env: env.slug });
|
|
3839
|
+
brokerConfig = resolved.value;
|
|
3840
|
+
}
|
|
3162
3841
|
const createBrokerService = await loadBrokerService();
|
|
3163
3842
|
if (createBrokerService) {
|
|
3164
|
-
const brokerService = createBrokerService(brokerEnv.type,
|
|
3843
|
+
const brokerService = createBrokerService(brokerEnv.type, brokerConfig);
|
|
3165
3844
|
await brokerService.subscribe(url, input.callback);
|
|
3166
3845
|
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { successful_execution: true, message: 'Subscribe to broker topic - success', data: { event }, status: types_1.LogEventStatus.SUCCESS }));
|
|
3167
3846
|
return;
|
|
@@ -3177,10 +3856,14 @@ class ProcessorService {
|
|
|
3177
3856
|
}
|
|
3178
3857
|
}
|
|
3179
3858
|
async runBrokerPublish(data, additional_logs = {}) {
|
|
3859
|
+
var _a, _b, _c, _d;
|
|
3180
3860
|
const { env, event } = data;
|
|
3181
3861
|
const input = data.input;
|
|
3182
3862
|
try {
|
|
3183
|
-
|
|
3863
|
+
// Skip redundant init when already initialized (e.g. same workflow run / same product)
|
|
3864
|
+
if (!this.productId) {
|
|
3865
|
+
await this.intializeProduct(additional_logs);
|
|
3866
|
+
}
|
|
3184
3867
|
const [brokerTag, topicTag] = event.split(':');
|
|
3185
3868
|
const broker = await this.productBuilderService.fetchMessageBroker(brokerTag);
|
|
3186
3869
|
if (!broker) {
|
|
@@ -3205,25 +3888,48 @@ class ProcessorService {
|
|
|
3205
3888
|
}
|
|
3206
3889
|
url = queueUrl.url;
|
|
3207
3890
|
}
|
|
3208
|
-
this.logService.add(
|
|
3209
|
-
|
|
3210
|
-
|
|
3211
|
-
|
|
3212
|
-
|
|
3213
|
-
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3891
|
+
/* this.logService.add({
|
|
3892
|
+
...this.baseLogs,
|
|
3893
|
+
...additional_logs,
|
|
3894
|
+
message: 'Fetch broker details - success',
|
|
3895
|
+
data: { event, broker },
|
|
3896
|
+
status: LogEventStatus.SUCCESS,
|
|
3897
|
+
});*/
|
|
3898
|
+
let brokerConfig = brokerEnv.config;
|
|
3899
|
+
if (brokerConfig && (0, secrets_1.mightContainSecrets)(brokerConfig)) {
|
|
3900
|
+
const secretsService = (0, secrets_1.getSecretsService)();
|
|
3901
|
+
if (!secretsService) {
|
|
3902
|
+
throw new Error('Broker configuration contains secret references ($secret{...}) but secrets service is not initialized. Ensure secrets are configured for this environment.');
|
|
3903
|
+
}
|
|
3904
|
+
const resolved = await secretsService.resolve(brokerConfig, { env: env.slug });
|
|
3905
|
+
brokerConfig = resolved.value;
|
|
3906
|
+
}
|
|
3907
|
+
const brokerService = await this.getBrokerServiceForPublish(brokerEnv.type, brokerConfig);
|
|
3908
|
+
await brokerService.publish(url, input.message);
|
|
3909
|
+
/*this.logService.add({
|
|
3910
|
+
...this.baseLogs,
|
|
3911
|
+
...additional_logs,
|
|
3912
|
+
message: 'Publish to broker topic - success',
|
|
3913
|
+
successful_execution: true,
|
|
3914
|
+
data: { event },
|
|
3915
|
+
status: LogEventStatus.SUCCESS,
|
|
3916
|
+
});*/
|
|
3917
|
+
return { published: true };
|
|
3219
3918
|
//return this.processStorageRequest(data, input, storageEnv, additional_logs);
|
|
3220
3919
|
}
|
|
3221
3920
|
catch (e) {
|
|
3222
|
-
|
|
3921
|
+
const err = e;
|
|
3922
|
+
const apiReason = (_b = (_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.errors;
|
|
3923
|
+
const reason = typeof apiReason === 'string'
|
|
3924
|
+
? apiReason
|
|
3925
|
+
: (_d = (_c = apiReason === null || apiReason === void 0 ? void 0 : apiReason.message) !== null && _c !== void 0 ? _c : err === null || err === void 0 ? void 0 : err.message) !== null && _d !== void 0 ? _d : String(e);
|
|
3926
|
+
const message = `Attempt publish to broker topic - failed: ${reason}`;
|
|
3927
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { failed_execution: true, message, data: { e, reason }, status: types_1.LogEventStatus.FAIL }));
|
|
3223
3928
|
throw e;
|
|
3224
3929
|
}
|
|
3225
3930
|
}
|
|
3226
|
-
async processStorageRequest(data, input, storageEnv, additional_logs) {
|
|
3931
|
+
async processStorageRequest(data, input, storageEnv, additional_logs, decryptionKey) {
|
|
3932
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
3227
3933
|
try {
|
|
3228
3934
|
/*const result = await this.processorApiService.processProduct(
|
|
3229
3935
|
this.productId,
|
|
@@ -3236,7 +3942,23 @@ class ProcessorService {
|
|
|
3236
3942
|
},
|
|
3237
3943
|
this.getUserAccess(),
|
|
3238
3944
|
);*/
|
|
3239
|
-
|
|
3945
|
+
// Bootstrap returns raw storage_env; config may be encrypted - decrypt before use
|
|
3946
|
+
// Prefer decryptionKey from bootstrap (workflow path) so we use the exact key the backend returned
|
|
3947
|
+
let cloudConfig = storageEnv.config;
|
|
3948
|
+
if (typeof cloudConfig === 'string') {
|
|
3949
|
+
const privateKey = decryptionKey !== null && decryptionKey !== void 0 ? decryptionKey : this.productBuilderService.fetchPrivateKey();
|
|
3950
|
+
if (!privateKey)
|
|
3951
|
+
throw new Error('Storage config is encrypted but private_key is not available');
|
|
3952
|
+
cloudConfig = JSON.parse((0, processor_utils_1.decrypt)(cloudConfig, privateKey));
|
|
3953
|
+
}
|
|
3954
|
+
// Resolve $Secret{...} references in config (e.g. GCP private_key) so upload receives real credentials
|
|
3955
|
+
if (cloudConfig && typeof cloudConfig === 'object' && (0, secrets_1.mightContainSecrets)(cloudConfig)) {
|
|
3956
|
+
const secretsService = (0, secrets_1.getSecretsService)();
|
|
3957
|
+
if (secretsService) {
|
|
3958
|
+
const resolved = await secretsService.resolve(cloudConfig, { env: storageEnv.slug });
|
|
3959
|
+
cloudConfig = resolved.value;
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3240
3962
|
const config = {
|
|
3241
3963
|
provider: storageEnv.type,
|
|
3242
3964
|
};
|
|
@@ -3246,8 +3968,8 @@ class ProcessorService {
|
|
|
3246
3968
|
if (storageEnv.type === types_1.StorageProviders.GCP) {
|
|
3247
3969
|
Object.assign(config, { gcpConfig: cloudConfig });
|
|
3248
3970
|
}
|
|
3249
|
-
if (storageEnv.type === types_1.StorageProviders.
|
|
3250
|
-
Object.assign(config, {
|
|
3971
|
+
if (storageEnv.type === types_1.StorageProviders.AZURE) {
|
|
3972
|
+
Object.assign(config, { azureConfig: cloudConfig });
|
|
3251
3973
|
}
|
|
3252
3974
|
const result = { url: await (0, storage_util_1.uploadBlobToCloud)({ data: input.buffer, destinationPath: input.fileName, config }) };
|
|
3253
3975
|
try {
|
|
@@ -3269,7 +3991,13 @@ class ProcessorService {
|
|
|
3269
3991
|
return result;
|
|
3270
3992
|
}
|
|
3271
3993
|
catch (e) {
|
|
3272
|
-
|
|
3994
|
+
const err = e;
|
|
3995
|
+
const apiReason = (_b = (_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.errors;
|
|
3996
|
+
const reason = typeof apiReason === 'string'
|
|
3997
|
+
? apiReason
|
|
3998
|
+
: (_g = (_f = (_c = apiReason === null || apiReason === void 0 ? void 0 : apiReason.message) !== null && _c !== void 0 ? _c : (_e = (_d = err === null || err === void 0 ? void 0 : err.response) === null || _d === void 0 ? void 0 : _d.data) === null || _e === void 0 ? void 0 : _e.message) !== null && _f !== void 0 ? _f : err === null || err === void 0 ? void 0 : err.message) !== null && _g !== void 0 ? _g : String(e);
|
|
3999
|
+
const message = `Run storage request - failed: ${reason}`;
|
|
4000
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { failed_execution: true, message, data: { e, reason }, status: types_1.LogEventStatus.FAIL }));
|
|
3273
4001
|
const value = await this.addToFailureOutput(e, data, { storageEnv }, additional_logs);
|
|
3274
4002
|
if (value) {
|
|
3275
4003
|
return value;
|
|
@@ -3405,7 +4133,7 @@ class ProcessorService {
|
|
|
3405
4133
|
}
|
|
3406
4134
|
async processAction(action) {
|
|
3407
4135
|
//TODO: schema validation
|
|
3408
|
-
const { env, input, retries, action: event, app, product: product_tag, session, cache } = action;
|
|
4136
|
+
const { env, input, retries, action: event, app, product: product_tag, session, cache, preloadedBootstrap } = action;
|
|
3409
4137
|
const additional_logs = {
|
|
3410
4138
|
parent_tag: (0, string_utils_1.extractOriginAndTag)(app),
|
|
3411
4139
|
child_tag: event,
|
|
@@ -3431,13 +4159,47 @@ class ProcessorService {
|
|
|
3431
4159
|
process_id,
|
|
3432
4160
|
data: input,
|
|
3433
4161
|
};
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
4162
|
+
this.process_id = process_id;
|
|
4163
|
+
// Initialize log service early to ensure error handling works
|
|
4164
|
+
if (!this.logService) {
|
|
4165
|
+
this.logService = new logs_service_1.default({
|
|
4166
|
+
product_id: this.productId,
|
|
4167
|
+
workspace_id: this.workspace_id,
|
|
4168
|
+
public_key: this.public_key,
|
|
4169
|
+
user_id: this.user_id,
|
|
4170
|
+
token: this.token,
|
|
4171
|
+
env_type: this.environment,
|
|
4172
|
+
});
|
|
4173
|
+
}
|
|
4174
|
+
// Early cache check - before expensive bootstrap/API calls
|
|
4175
|
+
if (cache && this.redisClient) {
|
|
4176
|
+
const productCache = await this.productBuilderService.fetchCache(cache);
|
|
4177
|
+
if (productCache) {
|
|
4178
|
+
const inputString = JSON.stringify(input);
|
|
4179
|
+
const cachedResult = await this.fetchFromCache({
|
|
4180
|
+
cache_tag: cache,
|
|
4181
|
+
input: inputString,
|
|
4182
|
+
privateKey: this.productBuilderService.fetchPrivateKey(),
|
|
4183
|
+
expiry: productCache.expiry,
|
|
4184
|
+
}, additional_logs);
|
|
4185
|
+
if (cachedResult) {
|
|
4186
|
+
const result = JSON.parse(cachedResult);
|
|
4187
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Action response from cache (early check)', data: { result: (0, processor_utils_1.anonymizeObject)(result) }, status: types_1.LogEventStatus.SUCCESS, cache_tag: cache, action: event }));
|
|
4188
|
+
this.end = Date.now();
|
|
4189
|
+
await this.logService.publish();
|
|
4190
|
+
return result;
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
}
|
|
4194
|
+
// Use preloaded bootstrap when provided (e.g. workflow batch prefetch), otherwise fetch
|
|
4195
|
+
const bootstrapData = preloadedBootstrap
|
|
4196
|
+
? preloadedBootstrap
|
|
4197
|
+
: await this.productBuilderService.bootstrapAction({
|
|
4198
|
+
product_tag,
|
|
4199
|
+
env_slug: env,
|
|
4200
|
+
access_tag: app,
|
|
4201
|
+
action_tag: event,
|
|
4202
|
+
});
|
|
3441
4203
|
// Initialize from bootstrap data
|
|
3442
4204
|
if (bootstrapData.product_id) {
|
|
3443
4205
|
this.productId = bootstrapData.product_id;
|
|
@@ -3508,25 +4270,19 @@ class ProcessorService {
|
|
|
3508
4270
|
// Process session if provided - verify and resolve $Session{} references
|
|
3509
4271
|
if (session && bootstrapData.private_key) {
|
|
3510
4272
|
const { processSessionForExecution } = await Promise.resolve().then(() => __importStar(require('../../sessions')));
|
|
3511
|
-
const sessionResult = await processSessionForExecution(session, bootstrapData.private_key, resolvedInput, env
|
|
4273
|
+
const sessionResult = await processSessionForExecution(session, bootstrapData.private_key, resolvedInput, env, {
|
|
4274
|
+
fetchSessionSelector: async (sessionTag) => {
|
|
4275
|
+
const sessionConfig = await this.productBuilderService.fetchSession(sessionTag);
|
|
4276
|
+
return sessionConfig === null || sessionConfig === void 0 ? void 0 : sessionConfig.selector;
|
|
4277
|
+
},
|
|
4278
|
+
});
|
|
3512
4279
|
if (sessionResult.error) {
|
|
3513
4280
|
throw new Error(`Session validation failed: ${sessionResult.error}`);
|
|
3514
4281
|
}
|
|
3515
4282
|
resolvedInput = sessionResult.input;
|
|
3516
4283
|
sessionLogFields = sessionResult.logFields;
|
|
3517
4284
|
}
|
|
3518
|
-
//
|
|
3519
|
-
if (!this.logService) {
|
|
3520
|
-
this.logService = new logs_service_1.default({
|
|
3521
|
-
product_id: this.productId,
|
|
3522
|
-
workspace_id: this.workspace_id,
|
|
3523
|
-
public_key: this.public_key,
|
|
3524
|
-
user_id: this.user_id,
|
|
3525
|
-
token: this.token,
|
|
3526
|
-
env_type: this.environment,
|
|
3527
|
-
});
|
|
3528
|
-
}
|
|
3529
|
-
this.process_id = process_id;
|
|
4285
|
+
// Update log service with product_id from bootstrap
|
|
3530
4286
|
this.baseLogs.product_id = this.productId;
|
|
3531
4287
|
// Add session fields to base logs
|
|
3532
4288
|
this.baseLogs = Object.assign(Object.assign({}, this.baseLogs), sessionLogFields);
|
|
@@ -3535,6 +4291,7 @@ class ProcessorService {
|
|
|
3535
4291
|
type: types_1.FeatureEventTypes.ACTION,
|
|
3536
4292
|
event,
|
|
3537
4293
|
cache: cache,
|
|
4294
|
+
skipCacheFetch: true, // Early cache check already done above, skip duplicate fetch
|
|
3538
4295
|
app,
|
|
3539
4296
|
input: resolvedInput,
|
|
3540
4297
|
env: this.processEnv,
|
|
@@ -3555,20 +4312,23 @@ class ProcessorService {
|
|
|
3555
4312
|
return result;
|
|
3556
4313
|
}
|
|
3557
4314
|
catch (e) {
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
4315
|
+
if (this.logService) {
|
|
4316
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Execute action - failed', data: { e: e.toString() }, status: types_1.LogEventStatus.FAIL }));
|
|
4317
|
+
this.end = Date.now();
|
|
4318
|
+
await this.writeResult(types_1.LogEventStatus.FAIL);
|
|
4319
|
+
await this.logService.publish();
|
|
4320
|
+
}
|
|
4321
|
+
throw e;
|
|
3564
4322
|
}
|
|
3565
4323
|
}
|
|
3566
4324
|
async processNotification(action) {
|
|
3567
4325
|
//TODO: schema validation
|
|
3568
|
-
var _a;
|
|
3569
|
-
const { env, input, retries, event, product: product_tag, session, cache } = action;
|
|
4326
|
+
var _a, _b, _c, _d, _e;
|
|
4327
|
+
const { env, input, retries, event, product: product_tag, session, cache, preloadedBootstrap } = action;
|
|
4328
|
+
debugLog('[processNotification] ENTRY', { product: product_tag, env, event, inputKeys: input ? Object.keys(input) : [], hasSession: !!session, hasCache: !!cache });
|
|
3570
4329
|
const [parent_tag, child_tag] = event.split(':');
|
|
3571
4330
|
if (!parent_tag || !child_tag) {
|
|
4331
|
+
debugLog('[processNotification] ERROR: invalid event format', { event });
|
|
3572
4332
|
throw new Error(`database action events should be in the format notification_tag:message_tag`);
|
|
3573
4333
|
}
|
|
3574
4334
|
this.component = types_1.LogEventTypes.NOTIFICATIONS;
|
|
@@ -3582,34 +4342,34 @@ class ProcessorService {
|
|
|
3582
4342
|
let sessionLogFields = {};
|
|
3583
4343
|
let resolvedInput = input;
|
|
3584
4344
|
try {
|
|
4345
|
+
this.productTag = product_tag;
|
|
4346
|
+
if (!preloadedBootstrap) {
|
|
4347
|
+
debugLog('[processNotification] Initializing product by tag', { product_tag });
|
|
4348
|
+
await this.productBuilderService.initializeProductByTag(product_tag);
|
|
4349
|
+
}
|
|
4350
|
+
debugLog('[processNotification] Validating input');
|
|
3585
4351
|
await this.validateActionDataMappingInput(input, types_1.FeatureEventTypes.NOTIFICATION);
|
|
3586
4352
|
this.input = action;
|
|
3587
4353
|
this.start = Date.now();
|
|
3588
|
-
this.productTag = product_tag;
|
|
3589
4354
|
const process_id = (0, processor_utils_1.generateObjectId)();
|
|
4355
|
+
debugLog('[processNotification] process_id', process_id);
|
|
3590
4356
|
this.baseLogs = Object.assign({ product_tag: this.productTag, workspace_id: this.workspace_id, env,
|
|
3591
4357
|
process_id, data: input }, additional_logs);
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
4358
|
+
this.lastNotificationFailureType = undefined;
|
|
4359
|
+
// Use preloaded bootstrap when provided (e.g. workflow batch prefetch), otherwise fetch
|
|
4360
|
+
const bootstrapData = preloadedBootstrap
|
|
4361
|
+
? preloadedBootstrap
|
|
4362
|
+
: await this.productBuilderService.bootstrapNotification({
|
|
4363
|
+
product_tag,
|
|
4364
|
+
env_slug: env,
|
|
4365
|
+
notification_tag: parent_tag,
|
|
4366
|
+
message_tag: child_tag,
|
|
4367
|
+
});
|
|
4368
|
+
debugLog('[processNotification] Bootstrap result', { product_id: bootstrapData === null || bootstrapData === void 0 ? void 0 : bootstrapData.product_id, hasNotification: !!(bootstrapData === null || bootstrapData === void 0 ? void 0 : bootstrapData.notification), hasMessage: !!(bootstrapData === null || bootstrapData === void 0 ? void 0 : bootstrapData.message), envActive: (_a = bootstrapData === null || bootstrapData === void 0 ? void 0 : bootstrapData.env) === null || _a === void 0 ? void 0 : _a.active });
|
|
3599
4369
|
// Initialize from bootstrap data
|
|
3600
4370
|
this.productId = bootstrapData.product_id;
|
|
3601
4371
|
this.processEnv = bootstrapData.env;
|
|
3602
|
-
//
|
|
3603
|
-
if (session && bootstrapData.private_key) {
|
|
3604
|
-
const { processSessionForExecution } = await Promise.resolve().then(() => __importStar(require('../../sessions')));
|
|
3605
|
-
const sessionResult = await processSessionForExecution(session, bootstrapData.private_key, input, env);
|
|
3606
|
-
if (sessionResult.error) {
|
|
3607
|
-
throw new Error(`Session validation failed: ${sessionResult.error}`);
|
|
3608
|
-
}
|
|
3609
|
-
resolvedInput = sessionResult.input;
|
|
3610
|
-
sessionLogFields = sessionResult.logFields;
|
|
3611
|
-
}
|
|
3612
|
-
// Initialize log service if needed
|
|
4372
|
+
// Initialize log service as soon as we have productId so the catch block can record failures
|
|
3613
4373
|
if (!this.logService) {
|
|
3614
4374
|
this.logService = new logs_service_1.default({
|
|
3615
4375
|
product_id: this.productId,
|
|
@@ -3621,6 +4381,21 @@ class ProcessorService {
|
|
|
3621
4381
|
});
|
|
3622
4382
|
}
|
|
3623
4383
|
this.process_id = process_id;
|
|
4384
|
+
// Process session if provided - verify and resolve $Session{} references
|
|
4385
|
+
if (session && bootstrapData.private_key) {
|
|
4386
|
+
const { processSessionForExecution } = await Promise.resolve().then(() => __importStar(require('../../sessions')));
|
|
4387
|
+
const sessionResult = await processSessionForExecution(session, bootstrapData.private_key, input, env, {
|
|
4388
|
+
fetchSessionSelector: async (sessionTag) => {
|
|
4389
|
+
const sessionConfig = await this.productBuilderService.fetchSession(sessionTag);
|
|
4390
|
+
return sessionConfig === null || sessionConfig === void 0 ? void 0 : sessionConfig.selector;
|
|
4391
|
+
},
|
|
4392
|
+
});
|
|
4393
|
+
if (sessionResult.error) {
|
|
4394
|
+
throw new Error(`Session validation failed: ${sessionResult.error}`);
|
|
4395
|
+
}
|
|
4396
|
+
resolvedInput = sessionResult.input;
|
|
4397
|
+
sessionLogFields = sessionResult.logFields;
|
|
4398
|
+
}
|
|
3624
4399
|
this.baseLogs.product_id = this.productId;
|
|
3625
4400
|
// Add session fields to base logs
|
|
3626
4401
|
this.baseLogs = Object.assign(Object.assign({}, this.baseLogs), sessionLogFields);
|
|
@@ -3638,25 +4413,152 @@ class ProcessorService {
|
|
|
3638
4413
|
allow_fail: false,
|
|
3639
4414
|
};
|
|
3640
4415
|
// Find the env config for the notification
|
|
3641
|
-
const envConfig = (
|
|
4416
|
+
const envConfig = (_b = bootstrapData.notification.envs) === null || _b === void 0 ? void 0 : _b.find((data) => data.slug === env);
|
|
4417
|
+
debugLog('[processNotification] Calling runNotification', { event, resolvedInputKeys: resolvedInput ? Object.keys(resolvedInput) : [] });
|
|
3642
4418
|
const result = await this.runNotification(payload, additional_logs, {
|
|
3643
4419
|
notification: bootstrapData.notification,
|
|
3644
4420
|
message: bootstrapData.message,
|
|
3645
4421
|
env_config: envConfig,
|
|
4422
|
+
product_tag,
|
|
4423
|
+
env,
|
|
4424
|
+
process_id,
|
|
4425
|
+
session,
|
|
4426
|
+
cache,
|
|
3646
4427
|
});
|
|
3647
4428
|
this.end = Date.now();
|
|
3648
|
-
|
|
4429
|
+
debugLog('[processNotification] SUCCESS', { process_id, lastChannelType: (_c = this.lastNotificationFailureType) !== null && _c !== void 0 ? _c : 'NOTIFICATIONS', durationMs: this.end - this.start });
|
|
4430
|
+
// Only add success log when we have a subtype (no NOTIFICATIONS-typed logs)
|
|
4431
|
+
/* if (this.lastNotificationFailureType != null) {
|
|
4432
|
+
this.logService.add({
|
|
4433
|
+
...this.baseLogs,
|
|
4434
|
+
...additional_logs,
|
|
4435
|
+
type: this.lastNotificationFailureType,
|
|
4436
|
+
message: 'Send notification - success',
|
|
4437
|
+
data: { input, result },
|
|
4438
|
+
status: LogEventStatus.SUCCESS,
|
|
4439
|
+
});
|
|
4440
|
+
}*/
|
|
3649
4441
|
await this.writeResult(types_1.LogEventStatus.SUCCESS);
|
|
3650
|
-
|
|
3651
|
-
|
|
4442
|
+
// Fire-and-forget so step latency isn't dominated by log upload
|
|
4443
|
+
this.logService.publish().catch(() => { });
|
|
4444
|
+
// Per-channel message logs are emitted inside runNotification (type: email | push | sms | callback).
|
|
4445
|
+
return { process_id, output: { sent: true } };
|
|
3652
4446
|
}
|
|
3653
4447
|
catch (e) {
|
|
3654
|
-
this.logService
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
4448
|
+
if (this.logService) {
|
|
4449
|
+
debugLog('[processNotification] FAILED', { error: e, lastChannelType: (_d = this.lastNotificationFailureType) !== null && _d !== void 0 ? _d : 'NOTIFICATIONS' });
|
|
4450
|
+
console.error(e);
|
|
4451
|
+
// Only add failure log when we have a subtype (no NOTIFICATIONS-typed logs)
|
|
4452
|
+
if (this.lastNotificationFailureType != null) {
|
|
4453
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { type: this.lastNotificationFailureType, message: 'Send notification - failed', data: { e: e.toString() }, status: types_1.LogEventStatus.FAIL }));
|
|
4454
|
+
}
|
|
4455
|
+
this.end = Date.now();
|
|
4456
|
+
this.logService.publish().catch(() => { });
|
|
4457
|
+
}
|
|
4458
|
+
// Fire-and-forget: log failed notification for reprocessing (only when we have productId from bootstrap). Use actual channel type when known.
|
|
4459
|
+
if (this.productId && this.workspace_id) {
|
|
4460
|
+
const channelType = this.lastNotificationFailureType === types_1.LogEventTypes.PUSH ? 'push' : this.lastNotificationFailureType === types_1.LogEventTypes.EMAIL ? 'email' : this.lastNotificationFailureType === types_1.LogEventTypes.SMS ? 'sms' : this.lastNotificationFailureType === types_1.LogEventTypes.CALLBACKS ? 'callback' : 'notification';
|
|
4461
|
+
this.fireAndForgetLogNotificationMessage({
|
|
4462
|
+
workspace_id: this.workspace_id,
|
|
4463
|
+
product_id: this.productId,
|
|
4464
|
+
product_tag,
|
|
4465
|
+
env,
|
|
4466
|
+
notification_tag: event,
|
|
4467
|
+
input: ((_e = resolvedInput !== null && resolvedInput !== void 0 ? resolvedInput : input) !== null && _e !== void 0 ? _e : {}),
|
|
4468
|
+
type: channelType,
|
|
4469
|
+
status: 'failed',
|
|
4470
|
+
process_id: this.process_id,
|
|
4471
|
+
retries: retries !== null && retries !== void 0 ? retries : 0,
|
|
4472
|
+
session,
|
|
4473
|
+
cache,
|
|
4474
|
+
error: e instanceof Error ? e.message : String(e),
|
|
4475
|
+
});
|
|
4476
|
+
}
|
|
4477
|
+
// Rethrow so workflow/callers see the step as failed (e.g. bootstrap "Notification not found" or runNotification failure)
|
|
4478
|
+
throw e;
|
|
3658
4479
|
}
|
|
3659
4480
|
}
|
|
4481
|
+
/**
|
|
4482
|
+
* Fire-and-forget log of notification message send to integrations backend (for reprocessing).
|
|
4483
|
+
* Input is encrypted with the product private key before sending. Does not block or affect latency; errors are swallowed.
|
|
4484
|
+
*/
|
|
4485
|
+
fireAndForgetLogNotificationMessage(payload) {
|
|
4486
|
+
debugLog('[fireAndForgetLogNotificationMessage] ENTRY', {
|
|
4487
|
+
workspace_id: payload.workspace_id,
|
|
4488
|
+
product_tag: payload.product_tag,
|
|
4489
|
+
env: payload.env,
|
|
4490
|
+
notification_tag: payload.notification_tag,
|
|
4491
|
+
type: payload.type,
|
|
4492
|
+
status: payload.status,
|
|
4493
|
+
process_id: payload.process_id,
|
|
4494
|
+
inputKeys: payload.input ? Object.keys(payload.input) : [],
|
|
4495
|
+
});
|
|
4496
|
+
let inputToSend = payload.input;
|
|
4497
|
+
try {
|
|
4498
|
+
const privateKey = this.productBuilderService.fetchPrivateKey();
|
|
4499
|
+
if (privateKey) {
|
|
4500
|
+
inputToSend = (0, processor_utils_1.encrypt)(JSON.stringify(payload.input), privateKey);
|
|
4501
|
+
debugLog('[fireAndForgetLogNotificationMessage] input encrypted', { inputLength: String(inputToSend).length });
|
|
4502
|
+
}
|
|
4503
|
+
else {
|
|
4504
|
+
debugLog('[fireAndForgetLogNotificationMessage] no private key, sending input as plain object');
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
catch (e) {
|
|
4508
|
+
debugLog('[fireAndForgetLogNotificationMessage] encrypt failed, sending input as-is', e);
|
|
4509
|
+
// No private key or encrypt failed: send input as-is (e.g. legacy or edge case)
|
|
4510
|
+
}
|
|
4511
|
+
void this.processorApiService
|
|
4512
|
+
.logNotificationMessage(Object.assign(Object.assign({}, payload), { input: inputToSend }), Object.assign(Object.assign({}, this.getUserAccess()), { access_key: this.accessKey }))
|
|
4513
|
+
.then(() => debugLog('[fireAndForgetLogNotificationMessage] POST success', { process_id: payload.process_id }))
|
|
4514
|
+
.catch((err) => console.error('[fireAndForgetLogNotificationMessage] POST failed', { process_id: payload.process_id, error: err }));
|
|
4515
|
+
}
|
|
4516
|
+
/**
|
|
4517
|
+
* Fetch notification message logs (send history) with time filters. Secured via user auth.
|
|
4518
|
+
* Each item's input is decrypted with the product private key when stored as an encrypted string.
|
|
4519
|
+
*/
|
|
4520
|
+
async getNotificationMessageLogs(options) {
|
|
4521
|
+
const result = await this.processorApiService.queryNotificationMessageLogs(Object.assign({ workspace_id: this.workspace_id }, options), this.getUserAccess());
|
|
4522
|
+
// Initialize product so productBuilderService has private_key for decryption
|
|
4523
|
+
const productRef = options.product_tag || options.product_id;
|
|
4524
|
+
if (productRef) {
|
|
4525
|
+
try {
|
|
4526
|
+
if (options.product_id) {
|
|
4527
|
+
await this.productBuilderService.initializeProduct(options.product_id);
|
|
4528
|
+
}
|
|
4529
|
+
else {
|
|
4530
|
+
await this.productBuilderService.initializeProductByTag(options.product_tag);
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
catch (_a) {
|
|
4534
|
+
// Return result without decryption if product init fails
|
|
4535
|
+
return result;
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
let privateKey;
|
|
4539
|
+
try {
|
|
4540
|
+
privateKey = this.productBuilderService.fetchPrivateKey();
|
|
4541
|
+
}
|
|
4542
|
+
catch (_b) {
|
|
4543
|
+
return result;
|
|
4544
|
+
}
|
|
4545
|
+
if (!privateKey) {
|
|
4546
|
+
return result;
|
|
4547
|
+
}
|
|
4548
|
+
const items = result.items.map((item) => {
|
|
4549
|
+
if (typeof item.input !== 'string') {
|
|
4550
|
+
return item;
|
|
4551
|
+
}
|
|
4552
|
+
try {
|
|
4553
|
+
const decrypted = (0, processor_utils_1.decrypt)(item.input, privateKey);
|
|
4554
|
+
return Object.assign(Object.assign({}, item), { input: JSON.parse(decrypted) });
|
|
4555
|
+
}
|
|
4556
|
+
catch (_a) {
|
|
4557
|
+
return item;
|
|
4558
|
+
}
|
|
4559
|
+
});
|
|
4560
|
+
return Object.assign(Object.assign({}, result), { items });
|
|
4561
|
+
}
|
|
3660
4562
|
async fetchRemoteCaches(payload) {
|
|
3661
4563
|
try {
|
|
3662
4564
|
const data = await this.processorApiService.fetchRemoteCaches(payload, this.getUserAccess());
|
|
@@ -3677,57 +4579,62 @@ class ProcessorService {
|
|
|
3677
4579
|
public_key: this.public_key,
|
|
3678
4580
|
};
|
|
3679
4581
|
}
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
4582
|
+
/**
|
|
4583
|
+
* Add data to cache - fire-and-forget pattern.
|
|
4584
|
+
* Does not block the main operation.
|
|
4585
|
+
*/
|
|
4586
|
+
addToCache(payload, additional_logs) {
|
|
3684
4587
|
const { input, privateKey, data, cache_tag, timestamp, expiry, product_tag, component_tag, component_type } = payload;
|
|
3685
4588
|
// sha-512 instead of md5, private key is fetch product.privatekey
|
|
3686
4589
|
const key = `${cache_tag}-${CryptoJS.SHA512(input)}`;
|
|
3687
4590
|
const encryptedData = (0, processor_utils_1.encrypt)(data, privateKey);
|
|
3688
|
-
let expiresAt =
|
|
4591
|
+
let expiresAt = undefined;
|
|
3689
4592
|
if (expiry) {
|
|
3690
|
-
expiresAt = timestamp + expiry;
|
|
3691
|
-
}
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
4593
|
+
expiresAt = new Date(timestamp + expiry);
|
|
4594
|
+
}
|
|
4595
|
+
// Use CacheService to store the value (fire-and-forget)
|
|
4596
|
+
this.cacheService.set({
|
|
4597
|
+
product: product_tag,
|
|
4598
|
+
cache: cache_tag,
|
|
4599
|
+
key,
|
|
4600
|
+
value: encryptedData,
|
|
4601
|
+
componentTag: component_tag,
|
|
4602
|
+
componentType: component_type,
|
|
4603
|
+
expiry: expiresAt,
|
|
4604
|
+
}).then(() => {
|
|
4605
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Cache value stored', data: { key }, successful_execution: true, status: types_1.LogEventStatus.SUCCESS, cache_tag, cache_key: key }));
|
|
4606
|
+
}).catch((error) => {
|
|
4607
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Cache store failed (non-blocking)', data: { key, error: String(error) }, status: types_1.LogEventStatus.FAIL, cache_tag, cache_key: key }));
|
|
4608
|
+
});
|
|
3699
4609
|
}
|
|
3700
4610
|
async fetchFromCache(payload, additional_logs) {
|
|
3701
|
-
var _a;
|
|
3702
4611
|
const { input, privateKey, cache_tag, expiry } = payload;
|
|
3703
4612
|
const key = `${cache_tag}-${CryptoJS.SHA512(input)}`;
|
|
3704
|
-
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Fetching from Cache', data: { key }, status: types_1.LogEventStatus.PROCESSING, cache_tag }));
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
const checkRemote = await this.processorApiService.fetchRemoteCacheByKey(key, this.getUserAccess());
|
|
3711
|
-
if (!checkRemote) {
|
|
3712
|
-
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Remote Cache Not Found', data: { key }, successful_execution: true, status: types_1.LogEventStatus.FAIL, cache_tag }));
|
|
4613
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Fetching from Cache', data: { key }, status: types_1.LogEventStatus.PROCESSING, cache_tag, cache_key: key }));
|
|
4614
|
+
try {
|
|
4615
|
+
// Use CacheService to fetch the value
|
|
4616
|
+
const cacheValue = await this.cacheService.get({ key });
|
|
4617
|
+
if (!cacheValue) {
|
|
4618
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Remote Cache Not Found', data: { key }, successful_execution: true, status: types_1.LogEventStatus.FAIL, cache_tag, cache_key: key, cache_status: false }));
|
|
3713
4619
|
return null;
|
|
3714
4620
|
}
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
if (record.lastUpdated) {
|
|
3719
|
-
const lastUpdated = Number(record.lastUpdated);
|
|
3720
|
-
if (expiry) {
|
|
3721
|
-
const expiryTime = lastUpdated + expiry;
|
|
4621
|
+
// Check expiry if provided
|
|
4622
|
+
if (expiry && cacheValue.expiry) {
|
|
4623
|
+
const expiryTime = new Date(cacheValue.expiry).getTime();
|
|
3722
4624
|
if (Date.now() > expiryTime) {
|
|
3723
|
-
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Deleting expired Cache Value', data: { key }, successful_execution: true, status: types_1.LogEventStatus.SUCCESS, cache_tag }));
|
|
3724
|
-
await this.
|
|
4625
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Deleting expired Cache Value', data: { key }, successful_execution: true, status: types_1.LogEventStatus.SUCCESS, cache_tag, cache_key: key }));
|
|
4626
|
+
await this.cacheService.clear({ key });
|
|
3725
4627
|
return null;
|
|
3726
4628
|
}
|
|
3727
4629
|
}
|
|
4630
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Cache Found', data: { key }, successful_execution: true, status: types_1.LogEventStatus.SUCCESS, cache_tag, cache_key: key, cache_status: true }));
|
|
4631
|
+
// Decrypt the value (CacheService returns encrypted value)
|
|
4632
|
+
return (0, processor_utils_1.decrypt)(cacheValue.value, privateKey);
|
|
4633
|
+
}
|
|
4634
|
+
catch (error) {
|
|
4635
|
+
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Cache fetch error', data: { key, error: String(error) }, successful_execution: false, status: types_1.LogEventStatus.FAIL, cache_tag, cache_key: key, cache_status: false }));
|
|
4636
|
+
return null;
|
|
3728
4637
|
}
|
|
3729
|
-
this.logService.add(Object.assign(Object.assign(Object.assign({}, this.baseLogs), additional_logs), { message: 'Cache Found', data: { key }, successful_execution: true, status: types_1.LogEventStatus.SUCCESS, cache_tag }));
|
|
3730
|
-
return (0, processor_utils_1.decrypt)(record.data, privateKey);
|
|
3731
4638
|
}
|
|
3732
4639
|
/**
|
|
3733
4640
|
* Writes the healthcheck result to Redis cache for fast status retrieval.
|
|
@@ -3736,9 +4643,9 @@ class ProcessorService {
|
|
|
3736
4643
|
if (!this.redisClient)
|
|
3737
4644
|
return;
|
|
3738
4645
|
const key = `healthcheck:${data.product}:${data.healthcheck}:${data.env}`;
|
|
3739
|
-
|
|
4646
|
+
debugLog('LOG TO CACHE', key, JSON.stringify(result));
|
|
3740
4647
|
await this.redisClient.set(key, JSON.stringify(result));
|
|
3741
|
-
|
|
4648
|
+
debugLog();
|
|
3742
4649
|
}
|
|
3743
4650
|
/**
|
|
3744
4651
|
* Fetches the latest healthcheck status for a product/env from Redis cache.
|