@bikdotai/bik-shared-backend 20.4.1-beta.0 → 20.4.1

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.
Files changed (81) hide show
  1. package/lib/alertsV2/alertInstances.repo.js +2 -26
  2. package/lib/alertsV2/alertsV2.helper.d.ts +4 -3
  3. package/lib/alertsV2/alertsV2.helper.js +3 -49
  4. package/lib/alertsV2/alertsV2.service.js +5 -14
  5. package/lib/auth/authMiddlewares.js +3 -68
  6. package/lib/auth/firebase-auth.service.js +3 -3
  7. package/lib/auth/implementations/bik-admin-auth-service.d.ts +1 -1
  8. package/lib/auth/implementations/bik-admin-auth-service.js +1 -2
  9. package/lib/auth/index.d.ts +0 -3
  10. package/lib/auth/index.js +0 -3
  11. package/lib/auth/secret-manager/env-variables/variables.list.d.ts +0 -3
  12. package/lib/auth/secret-manager/env-variables/variables.list.js +0 -3
  13. package/lib/chat-handover-protocol/chat-handover-protocol.js +41 -26
  14. package/lib/core/setup.d.ts +0 -3
  15. package/lib/core/setup.js +2 -24
  16. package/lib/elastic/counter/ingestion.js +0 -1
  17. package/lib/elastic/queries/campaign/getBroadcastDetailedStats.d.ts +0 -117
  18. package/lib/elastic/queries/campaign/getBroadcastDetailedStats.js +1 -94
  19. package/lib/elastic/queries/campaign/getUniqueCustomerCnt.d.ts +0 -1
  20. package/lib/elastic/queries/campaign/getUniqueCustomerCnt.js +0 -1
  21. package/lib/elastic/queries/chatbot/getAiOperations.d.ts +5 -5
  22. package/lib/elastic/queries/chatbot/getAiOperations.js +3 -3
  23. package/lib/elastic/queries/chatbot/index.d.ts +0 -1
  24. package/lib/elastic/queries/chatbot/index.js +0 -1
  25. package/lib/elastic/queries/crm/getActivityTimelineByAgent.js +1 -1
  26. package/lib/elastic/queries/crm/getBreachedSLACount.d.ts +0 -1
  27. package/lib/elastic/queries/crm/getBreachedSLACount.js +5 -8
  28. package/lib/elastic/queries/crm/getFirstResponseTime.d.ts +0 -1
  29. package/lib/elastic/queries/crm/getFirstResponseTime.js +5 -8
  30. package/lib/elastic/queries/integrations/index.d.ts +0 -1
  31. package/lib/elastic/queries/integrations/index.js +0 -1
  32. package/lib/elastic/queries/openAi/addToCartSession.d.ts +0 -1
  33. package/lib/elastic/queries/openAi/addToCartSession.js +3 -11
  34. package/lib/elastic/queries/openAi/checkoutCompletedSession.d.ts +0 -1
  35. package/lib/elastic/queries/openAi/checkoutCompletedSession.js +3 -11
  36. package/lib/elastic/reports/crm/index.d.ts +0 -1
  37. package/lib/elastic/reports/crm/index.js +0 -1
  38. package/lib/elastic/reports/reports.service.js +8 -17
  39. package/lib/events/events.d.ts +0 -8
  40. package/lib/events/events.js +1 -25
  41. package/lib/events/schema/events.helper.d.ts +0 -1
  42. package/lib/events/schema/events.helper.js +6 -12
  43. package/lib/index.d.ts +0 -1
  44. package/lib/index.js +0 -1
  45. package/lib/merchant-events/elastic.search.d.ts +0 -5
  46. package/lib/merchant-events/elastic.search.js +2 -23
  47. package/lib/merchant-events/merchant.service.d.ts +1 -1
  48. package/lib/merchant-events/merchant.service.js +11 -12
  49. package/lib/recordAnalytics/recordAnalytics.service.js +4 -5
  50. package/lib/redis/redisPubSubService.d.ts +6 -4
  51. package/lib/redis/redisPubSubService.js +123 -211
  52. package/lib/redis/redisService.js +8 -14
  53. package/lib/swagger/SwaggerSchemaHelper.js +17 -21
  54. package/lib/user-properties/userProperties.service.js +0 -1
  55. package/package.json +2 -2
  56. package/lib/alerts/templates/campaign/campaignMovedToDraftSegmentSyncDelay.d.ts +0 -1
  57. package/lib/alerts/templates/campaign/campaignMovedToDraftSegmentSyncDelay.js +0 -4
  58. package/lib/alerts/templates/campaign/campaignMovedToDraftSegmentSyncFailed.d.ts +0 -1
  59. package/lib/alerts/templates/campaign/campaignMovedToDraftSegmentSyncFailed.js +0 -4
  60. package/lib/alerts/templates/campaign/campaignScheduledTimeExceededDueToSyncDelay.d.ts +0 -1
  61. package/lib/alerts/templates/campaign/campaignScheduledTimeExceededDueToSyncDelay.js +0 -4
  62. package/lib/auth/secret-manager/configManager.firestore.d.ts +0 -22
  63. package/lib/auth/secret-manager/configManager.firestore.js +0 -166
  64. package/lib/auth/secret-manager/configManager.helper.d.ts +0 -13
  65. package/lib/auth/secret-manager/configManager.helper.js +0 -32
  66. package/lib/auth/secret-manager/configManager.model.d.ts +0 -38
  67. package/lib/auth/secret-manager/configManager.model.js +0 -2
  68. package/lib/auth/secret-manager/configManager.service.d.ts +0 -17
  69. package/lib/auth/secret-manager/configManager.service.js +0 -138
  70. package/lib/core/local_runner.d.ts +0 -1
  71. package/lib/core/local_runner.js +0 -60
  72. package/lib/database/database.model.d.ts +0 -95
  73. package/lib/database/database.model.js +0 -5
  74. package/lib/database/database.service.d.ts +0 -90
  75. package/lib/database/database.service.js +0 -382
  76. package/lib/database/index.d.ts +0 -7
  77. package/lib/database/index.js +0 -23
  78. package/lib/elastic/queries/chatbot/getAgentCostForBroadcast.d.ts +0 -117
  79. package/lib/elastic/queries/chatbot/getAgentCostForBroadcast.js +0 -98
  80. package/lib/elastic/queries/integrations/getOrdersShadowServices.d.ts +0 -76
  81. package/lib/elastic/queries/integrations/getOrdersShadowServices.js +0 -61
@@ -15,99 +15,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.RedisPubSubService = void 0;
16
16
  const ioredis_1 = __importDefault(require("ioredis"));
17
17
  const auth_1 = require("../auth");
18
- // One publisher + one subscriber shared across ALL RedisPubSubService instances.
19
- // This ensures the entire process uses exactly 2 pub/sub connections regardless
20
- // of how many channels are subscribed to.
21
- let sharedPublisher = null;
22
- let sharedSubscriber = null;
23
- let shutdownRegistered = false;
24
- // Stored so closeAll() can remove the handlers and avoid accumulation across
25
- // closeAll() + reinit cycles (e.g. in tests).
26
- let signalHandler = null;
27
- // Registry mapping channel name → message callback for all active subscriptions.
28
- // A single 'message' listener on sharedSubscriber dispatches via this map,
29
- // avoiding MaxListenersExceededWarning when many channels are subscribed.
30
- const channelCallbacks = new Map();
31
- // Guards registration of the shared 'ready' and 'message' listeners so they
32
- // are added to sharedSubscriber exactly once regardless of how many instances
33
- // call subscribe().
34
- let sharedListenersRegistered = false;
35
- const MAX_RETRIES = 3;
36
- const BASE_DELAY_MS = 200;
37
- // Single authoritative predicate for transient Redis errors, used by both the
38
- // client error handler and retryWithBackoff — no duplicate classification logic.
39
- function isTransientError(err) {
40
- const code = err === null || err === void 0 ? void 0 : err.code;
41
- const name = err === null || err === void 0 ? void 0 : err.name;
42
- const message = err === null || err === void 0 ? void 0 : err.message;
43
- const transientCodes = new Set([
44
- 'ECONNRESET',
45
- 'ETIMEDOUT',
46
- 'EHOSTUNREACH',
47
- 'ENETUNREACH',
48
- 'ECONNREFUSED',
49
- 'NR_CLOSED',
50
- 'EPIPE',
51
- ]);
52
- if (name === 'TimeoutError')
53
- return true;
54
- if (code && transientCodes.has(code))
55
- return true;
56
- if (message) {
57
- return [
58
- /connection is closed/i,
59
- /socket closed/i,
60
- /not yet established/i,
61
- /still connecting/i,
62
- /Ready check failed/i,
63
- ].some((r) => r.test(message));
64
- }
65
- return false;
66
- }
67
- function createRedisClient(opts) {
68
- var _a, _b, _c;
69
- const client = new ioredis_1.default({
70
- host: opts.host,
71
- password: opts.password,
72
- port: opts.port,
73
- keepAlive: 30000,
74
- lazyConnect: (_a = opts.lazyConnect) !== null && _a !== void 0 ? _a : false,
75
- autoResubscribe: (_b = opts.autoResubscribe) !== null && _b !== void 0 ? _b : true,
76
- enableReadyCheck: (_c = opts.enableReadyCheck) !== null && _c !== void 0 ? _c : true,
77
- });
78
- client.on('error', (err) => {
79
- if (isTransientError(err))
80
- return;
81
- console.error('RedisPubSubService client error:', err);
82
- });
83
- return client;
84
- }
85
- // Module-level so the shared 'ready' listener (registered once) can call it
86
- // without requiring a class instance reference.
87
- function retryWithBackoff(fn) {
88
- return __awaiter(this, void 0, void 0, function* () {
89
- let attempt = 0;
90
- let lastError;
91
- while (attempt <= MAX_RETRIES) {
92
- try {
93
- return yield fn();
94
- }
95
- catch (err) {
96
- lastError = err;
97
- if (!isTransientError(err) || attempt === MAX_RETRIES) {
98
- throw err;
99
- }
100
- const delay = Math.min(BASE_DELAY_MS * Math.pow(2, attempt), 2000);
101
- yield new Promise((res) => setTimeout(res, delay));
102
- attempt++;
103
- }
104
- }
105
- throw lastError;
106
- });
107
- }
108
18
  class RedisPubSubService {
109
19
  constructor(config) {
110
- this.subscribed = false;
20
+ this.maxRetries = 3;
21
+ this.baseDelayMs = 200;
111
22
  const host = auth_1.EnvVariableHelper.access(auth_1.ServiceName.REDIS, auth_1.RedisKeys.HOST, 'REDIS_HOST') || 'localhost';
112
23
  const portStr = auth_1.EnvVariableHelper.access(auth_1.ServiceName.REDIS, auth_1.RedisKeys.PORT, 'REDIS_PORT');
113
24
  const port = portStr ? parseInt(portStr) : 6379;
@@ -116,60 +27,114 @@ class RedisPubSubService {
116
27
  if (!host || !password) {
117
28
  throw new Error('Missing environment variables for Redis Cloud from bik-shared-backend');
118
29
  }
119
- // Initialise shared clients once per process.
120
- if (!sharedPublisher) {
121
- // lazyConnect: true publisher only opens a connection when publish() is
122
- // first called, saving a connection on pods that only subscribe.
123
- sharedPublisher = createRedisClient({ host, password, port, lazyConnect: true });
124
- }
125
- if (!sharedSubscriber) {
126
- // autoResubscribe: false — we re-subscribe manually in the 'ready' handler
127
- // so ioredis's built-in re-subscribe doesn't double-up and cause the
128
- // "Connection in subscriber mode" error on reconnect.
129
- // enableReadyCheck: false — avoids the INFO ready-check command racing with
130
- // SUBSCRIBE on reconnect.
131
- sharedSubscriber = createRedisClient({
132
- host,
133
- password,
134
- port,
135
- autoResubscribe: false,
136
- enableReadyCheck: false,
137
- });
30
+ this.publisher = new ioredis_1.default({ host, password, port });
31
+ // autoResubscribe: false — we re-subscribe manually in the 'ready' handler,
32
+ // so ioredis's built-in re-subscribe doesn't double-up and cause the
33
+ // "Connection in subscriber mode" error on reconnect.
34
+ // enableReadyCheck: false avoids the INFO ready-check command racing with
35
+ // our SUBSCRIBE and producing the same "subscriber mode" error.
36
+ this.subscriber = new ioredis_1.default({
37
+ host,
38
+ password,
39
+ port,
40
+ autoResubscribe: false,
41
+ enableReadyCheck: false,
42
+ });
43
+ const isTransient = (err) => {
44
+ const code = err === null || err === void 0 ? void 0 : err.code;
45
+ const name = err === null || err === void 0 ? void 0 : err.name;
46
+ const message = err === null || err === void 0 ? void 0 : err.message;
47
+ const transientCodes = new Set([
48
+ 'ECONNRESET',
49
+ 'ETIMEDOUT',
50
+ 'EHOSTUNREACH',
51
+ 'ENETUNREACH',
52
+ 'ECONNREFUSED',
53
+ 'NR_CLOSED',
54
+ 'EPIPE',
55
+ ]);
56
+ if (name === 'TimeoutError')
57
+ return true;
58
+ if (code && transientCodes.has(code))
59
+ return true;
60
+ if (message) {
61
+ return [
62
+ /connection is closed/i,
63
+ /socket closed/i,
64
+ /not yet established/i,
65
+ /still connecting/i,
66
+ /Ready check failed/i,
67
+ ].some((r) => r.test(message));
68
+ }
69
+ return false;
70
+ };
71
+ this.publisher.on('error', (err) => {
72
+ if (isTransient(err))
73
+ return; // suppress transient errors
74
+ console.error('Redis publisher error:', err);
75
+ });
76
+ this.subscriber.on('error', (err) => {
77
+ if (isTransient(err))
78
+ return; // suppress transient errors
79
+ console.error('Redis subscriber error:', err);
80
+ });
81
+ }
82
+ isRetriableRedisError(error) {
83
+ const code = error === null || error === void 0 ? void 0 : error.code;
84
+ const name = error === null || error === void 0 ? void 0 : error.name;
85
+ const message = error === null || error === void 0 ? void 0 : error.message;
86
+ const transientCodes = new Set([
87
+ 'ECONNRESET',
88
+ 'ETIMEDOUT',
89
+ 'EHOSTUNREACH',
90
+ 'ENETUNREACH',
91
+ 'ECONNREFUSED',
92
+ 'NR_CLOSED',
93
+ 'EPIPE',
94
+ ]);
95
+ if (name === 'TimeoutError')
96
+ return true;
97
+ if (code && transientCodes.has(code))
98
+ return true;
99
+ if (message) {
100
+ const patterns = [
101
+ /connection is closed/i,
102
+ /socket closed/i,
103
+ /The connection is not yet established/i,
104
+ /ioredis is still connecting/i,
105
+ /Ready check failed/i,
106
+ ];
107
+ if (patterns.some((re) => re.test(message)))
108
+ return true;
138
109
  }
139
- // Register SIGTERM/SIGINT cleanup once per process. This handler closes the
140
- // shared Redis connections gracefully. process.exit() is intentionally NOT
141
- // called here — that responsibility belongs to the host service (e.g. via
142
- // Fastify server.close() in main.ts) so it can drain HTTP and perform its
143
- // own cleanup before exiting.
144
- // signalHandler is stored so closeAll() can remove it via process.off(),
145
- // preventing handler accumulation across closeAll() + reinit cycles.
146
- if (!shutdownRegistered) {
147
- shutdownRegistered = true;
148
- signalHandler = () => __awaiter(this, void 0, void 0, function* () {
149
- console.log('[RedisPubSubService] Signal received — closing shared Redis pub/sub connections');
110
+ return false;
111
+ }
112
+ retryWithBackoff(fn) {
113
+ return __awaiter(this, void 0, void 0, function* () {
114
+ let attempt = 0;
115
+ let lastError;
116
+ while (attempt <= this.maxRetries) {
150
117
  try {
151
- yield Promise.all([sharedPublisher === null || sharedPublisher === void 0 ? void 0 : sharedPublisher.quit(), sharedSubscriber === null || sharedSubscriber === void 0 ? void 0 : sharedSubscriber.quit()]);
152
- console.log('[RedisPubSubService] Redis pub/sub connections closed successfully');
118
+ return yield fn();
153
119
  }
154
120
  catch (err) {
155
- console.warn('[RedisPubSubService] Error closing pub/sub connections:', err);
121
+ lastError = err;
122
+ if (!this.isRetriableRedisError(err) || attempt === this.maxRetries) {
123
+ throw err;
124
+ }
125
+ const delay = Math.min(this.baseDelayMs * Math.pow(2, attempt), 2000);
126
+ yield new Promise((res) => setTimeout(res, delay));
127
+ attempt++;
156
128
  }
157
- });
158
- process.on('SIGTERM', signalHandler);
159
- process.on('SIGINT', signalHandler);
160
- }
161
- }
162
- get publisher() {
163
- return sharedPublisher;
164
- }
165
- get subscriber() {
166
- return sharedSubscriber;
129
+ }
130
+ throw lastError;
131
+ });
167
132
  }
168
133
  publish(data) {
169
134
  return __awaiter(this, void 0, void 0, function* () {
170
135
  try {
171
136
  const payload = JSON.stringify(data);
172
- yield retryWithBackoff(() => this.publisher.publish(this.channel, payload));
137
+ yield this.retryWithBackoff(() => this.publisher.publish(this.channel, payload));
173
138
  console.log(`Published to ${this.channel}:`, data);
174
139
  }
175
140
  catch (error) {
@@ -178,92 +143,39 @@ class RedisPubSubService {
178
143
  });
179
144
  }
180
145
  subscribe(callback) {
181
- if (this.subscribed) {
182
- console.warn(`RedisPubSubService: already subscribed to channel "${this.channel}", ignoring duplicate call.`);
183
- return;
184
- }
185
- this.subscribed = true;
186
- // Register the callback before attaching listeners so the 'ready' handler
187
- // sees this channel immediately on the first connect.
188
- channelCallbacks.set(this.channel, callback);
189
- if (!sharedListenersRegistered) {
190
- sharedListenersRegistered = true;
191
- // Single 'ready' listener re-subscribes ALL registered channels on every
192
- // connect/reconnect. autoResubscribe is disabled so we own this entirely.
193
- // 'ready' fires after AUTH + SELECT, safe to send SUBSCRIBE here.
194
- sharedSubscriber.on('ready', () => {
195
- const channels = Array.from(channelCallbacks.keys());
196
- if (channels.length === 0)
197
- return;
198
- console.log(`[RedisPubSubService] Subscriber ready — resubscribing to: ${channels.join(', ')}`);
199
- retryWithBackoff(() => sharedSubscriber.subscribe(...channels))
200
- .then(() => console.log(`[RedisPubSubService] Subscribed to: ${channels.join(', ')}`))
201
- .catch((err) => console.error('[RedisPubSubService] Subscription failed:', err));
202
- });
203
- // Single 'message' listener dispatches to the correct per-channel callback
204
- // via the registry. This replaces per-instance listeners and prevents
205
- // MaxListenersExceededWarning regardless of how many channels are active.
206
- sharedSubscriber.on('message', (incomingChannel, message) => {
207
- const cb = channelCallbacks.get(incomingChannel);
208
- if (!cb)
209
- return;
210
- try {
211
- const parsed = JSON.parse(message);
212
- console.log(`Message received on ${incomingChannel}:`, parsed);
213
- cb(parsed);
214
- }
215
- catch (e) {
216
- console.error(`[RedisPubSubService] Error parsing message on ${incomingChannel}:`, e);
217
- }
218
- });
219
- }
146
+ const doSubscribe = () => {
147
+ this.retryWithBackoff(() => this.subscriber.subscribe(this.channel))
148
+ .then(() => console.log(`Subscribed to channel: ${this.channel}`))
149
+ .catch((err) => console.error('Redis subscription failed:', err));
150
+ };
151
+ // 'ready' fires after AUTH + SELECT (and INFO if enableReadyCheck is on),
152
+ // so it's safe to send SUBSCRIBE here without conflicting with ioredis internals.
153
+ // It also fires on every reconnect, handling re-subscription since
154
+ // autoResubscribe is disabled.
155
+ this.subscriber.on('ready', () => {
156
+ console.log(`Redis Subscriber ready on channel "${this.channel}"`);
157
+ doSubscribe();
158
+ });
220
159
  // Handle the case where the connection is already established before
221
160
  // subscribe() is called (e.g. delayed initialization).
222
161
  if (this.subscriber.status === 'ready') {
223
- retryWithBackoff(() => this.subscriber.subscribe(this.channel))
224
- .then(() => console.log(`[RedisPubSubService] Subscribed to channel: ${this.channel}`))
225
- .catch((err) => console.error('[RedisPubSubService] Subscription failed:', err));
162
+ doSubscribe();
226
163
  }
227
- }
228
- // Unsubscribes this instance's channel and removes its callback from the
229
- // shared registry. The shared publisher/subscriber remain open for other
230
- // active channels and are closed on SIGTERM or via closeAll().
231
- close() {
232
- return __awaiter(this, void 0, void 0, function* () {
233
- if (!this.subscribed)
234
- return;
235
- this.subscribed = false;
236
- channelCallbacks.delete(this.channel);
164
+ this.subscriber.on('message', (_channel, message) => {
237
165
  try {
238
- yield (sharedSubscriber === null || sharedSubscriber === void 0 ? void 0 : sharedSubscriber.unsubscribe(this.channel));
166
+ const parsed = JSON.parse(message);
167
+ console.log(`Message received on ${this.channel}:`, parsed);
168
+ callback(parsed);
239
169
  }
240
- catch (err) {
241
- console.warn(`[RedisPubSubService] Error unsubscribing from ${this.channel}:`, err);
170
+ catch (e) {
171
+ console.error('Error parsing message:', e);
242
172
  }
243
173
  });
244
174
  }
245
- // Closes both shared connections and resets all module-level state including
246
- // signal handler registration. Intended for tests and explicit full teardown;
247
- // in production the SIGTERM handler handles shutdown automatically.
248
- static closeAll() {
175
+ close() {
249
176
  return __awaiter(this, void 0, void 0, function* () {
250
- try {
251
- yield Promise.all([sharedPublisher === null || sharedPublisher === void 0 ? void 0 : sharedPublisher.quit(), sharedSubscriber === null || sharedSubscriber === void 0 ? void 0 : sharedSubscriber.quit()]);
252
- sharedPublisher = null;
253
- sharedSubscriber = null;
254
- channelCallbacks.clear();
255
- sharedListenersRegistered = false;
256
- if (signalHandler) {
257
- process.off('SIGTERM', signalHandler);
258
- process.off('SIGINT', signalHandler);
259
- signalHandler = null;
260
- }
261
- shutdownRegistered = false;
262
- console.log('[RedisPubSubService] All Redis pub/sub connections closed.');
263
- }
264
- catch (err) {
265
- console.warn('[RedisPubSubService] Error closing Redis pub/sub connections:', err);
266
- }
177
+ yield Promise.all([this.publisher.quit(), this.subscriber.quit()]);
178
+ console.log('Redis connections closed.');
267
179
  });
268
180
  }
269
181
  }
@@ -46,13 +46,12 @@ class RedisAppService {
46
46
  host: redisHost,
47
47
  password: redisKey,
48
48
  port: redisPort ? Number(redisPort) : 6379,
49
- keepAlive: 30000,
49
+ keepAlive: 1,
50
50
  maxRetriesPerRequest: null,
51
51
  enableReadyCheck: false,
52
52
  connectionName: logger_1.LOGGING_CONFIGURATION.serviceName,
53
53
  connectTimeout: 5000,
54
54
  commandTimeout: 5000,
55
- disconnectTimeout: 10000,
56
55
  reconnectOnError: (err) => {
57
56
  if (err.code === 'ECONNREFUSED') {
58
57
  console.error('REDIS QUOTA LIKELY EXCEEDED - Check Redis Labs dashboard', err);
@@ -139,7 +138,7 @@ class RedisAppService {
139
138
  return __awaiter(this, void 0, void 0, function* () {
140
139
  let attempt = 0;
141
140
  let lastError;
142
- while (attempt <= this.maxRetries) {
141
+ while (attempt < this.maxRetries) {
143
142
  try {
144
143
  return yield fn();
145
144
  }
@@ -566,20 +565,15 @@ class RedisAppService {
566
565
  exports.RedisAppService = RedisAppService;
567
566
  // Dangerous - this will impact any service that bik shared is injected with.
568
567
  const cleanup = () => __awaiter(void 0, void 0, void 0, function* () {
569
- console.log('[RedisAppService] SIGTERM received — closing Redis connection');
570
568
  if (!RedisAppService.getInstance().getClient()) {
571
- console.log('[RedisAppService] No client found, skipping');
572
569
  return;
573
570
  }
574
- try {
575
- yield RedisAppService.getInstance().gracefulShutdown();
576
- console.log('[RedisAppService] Redis connection closed successfully');
577
- }
578
- catch (err) {
579
- console.warn('[RedisAppService] Error closing Redis connection:', err);
580
- }
571
+ // console.log(
572
+ // '@bik-shared (redisService.ts) Received SIGTERM - closing Redis connection and shutting down gracefully.'
573
+ // );
574
+ // Close the Redis connection
575
+ yield RedisAppService.getInstance().gracefulShutdown();
581
576
  });
582
577
  // Register the cleanup function for SIGTERM signal
583
578
  process.on('SIGTERM', cleanup);
584
- // Also handle SIGINT (Ctrl+C / local dev)
585
- process.on('SIGINT', cleanup);
579
+ process.on('exit', cleanup);
@@ -52,11 +52,7 @@ class SwaggerSchemaHelper {
52
52
  }
53
53
  });
54
54
  // Convert camelCase or kebab-case to human-readable format
55
- return (prefix.charAt(0).toUpperCase() +
56
- prefix
57
- .slice(1)
58
- .replace(/([A-Z])/g, ' $1')
59
- .trim());
55
+ return prefix.charAt(0).toUpperCase() + prefix.slice(1).replace(/([A-Z])/g, ' $1').trim();
60
56
  }
61
57
  static loadSwaggerConfig(apiPath) {
62
58
  try {
@@ -94,10 +90,10 @@ class SwaggerSchemaHelper {
94
90
  security: [
95
91
  {
96
92
  Authorization: [],
97
- bikReferer: [],
98
- },
93
+ bikReferer: []
94
+ }
99
95
  ],
100
- 'x-timeout': existingSchema === null || existingSchema === void 0 ? void 0 : existingSchema['x-timeout'],
96
+ 'x-timeout': existingSchema === null || existingSchema === void 0 ? void 0 : existingSchema['x-timeout']
101
97
  };
102
98
  if (swaggerConfig['x-timeout']) {
103
99
  swaggerConfig.description += `\n\n**Timeout:** ${swaggerConfig['x-timeout']} seconds`;
@@ -107,23 +103,23 @@ class SwaggerSchemaHelper {
107
103
  acc[example.summary] = { value: example.value };
108
104
  return acc;
109
105
  }, {});
110
- swaggerConfig['responses'] = {
111
- '200': {
112
- description: 'Default Response',
106
+ swaggerConfig["responses"] = {
107
+ "200": {
108
+ "description": "Default Response"
113
109
  },
114
- '408': {
115
- description: `Request Timeout - Operation exceeded ${swaggerConfig['x-timeout'] || 'configured'} seconds`,
110
+ "503": {
111
+ "description": `Request Timeout - Operation exceeded ${swaggerConfig['x-timeout'] || 'configured'} seconds`
116
112
  },
117
113
  };
118
- swaggerConfig['requestBody'] = {
119
- content: {
120
- 'application/json': {
121
- schema: {
122
- type: 'object',
114
+ swaggerConfig["requestBody"] = {
115
+ "content": {
116
+ "application/json": {
117
+ "schema": {
118
+ "type": "object"
123
119
  },
124
- examples: flattenedExamples,
125
- },
126
- },
120
+ "examples": flattenedExamples
121
+ }
122
+ }
127
123
  };
128
124
  return swaggerConfig;
129
125
  }
@@ -111,7 +111,6 @@ class BikUserPropertiesService {
111
111
  return;
112
112
  }
113
113
  const userPropertiesPayload = userPropertiesHelper.constructUserProperties(currObj, (customerTagsMap === null || customerTagsMap === void 0 ? void 0 : customerTagsMap[currObj.customerId.toString()]) || [], undefined, currObj.tagsToRemove);
114
- console.log('Final userPropertiesPayload:', JSON.stringify(userPropertiesPayload === null || userPropertiesPayload === void 0 ? void 0 : userPropertiesPayload.data, null, 2));
115
114
  const isUatStore = (0, events_1.getUatStoreIds)().includes(currObj.storeId || storeId || '');
116
115
  const upTopic = isUatStore ? 'trackUserProperty-uat' : 'trackUserProperty';
117
116
  return (0, pubsub_listener_1.publishMessageToTopic)(upTopic, userPropertiesPayload === null || userPropertiesPayload === void 0 ? void 0 : userPropertiesPayload.data);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bikdotai/bik-shared-backend",
3
- "version": "20.4.1-beta.0",
3
+ "version": "20.4.1",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -35,7 +35,7 @@
35
35
  "dependencies": {
36
36
  "@amplitude/identify": "^1.10.0",
37
37
  "@amplitude/node": "^1.10.0",
38
- "@bikdotai/bik-models": "11.7.1-beta.0",
38
+ "@bikdotai/bik-models": "^11.7.1",
39
39
  "@elastic/elasticsearch": "^8.7.0",
40
40
  "@fastify/cookie": "^9.4.0",
41
41
  "@fastify/cors": "^9.0.1",
@@ -1 +0,0 @@
1
- export declare const email = "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Campaign Moved to Draft</title><style>body { margin: 0; padding: 0; background-color: #f5f5f5; font-family: Arial, Helvetica, sans-serif;}table { border-collapse: collapse;}.container { width: 100%; max-width: 520px; margin: 0 auto; background-color: #ffffff;}.content { padding: 24px; color: #000000; font-size: 14px; line-height: 1.6;}h1 { font-size: 22px; text-align: center; margin: 20px 0;}.button { display: inline-block; background-color: #4a136a; color: #ffffff !important; text-decoration: none; padding: 12px 24px; border-radius: 4px; font-size: 14px; font-weight: bold;}.footer { font-size: 12px; color: #777777; text-align: center; padding: 20px;}.divider { border-top: 1px solid #dddddd; margin: 30px 0 20px;}@media only screen and (max-width: 520px) { .content { padding: 18px; }}</style></head><body><table width=\"100%\"><tr><td align=\"center\"><table class=\"container\" role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\"><tr><td align=\"center\" style=\"padding: 24px 0;\"><img src=\"https://d15k2d11r6t6rl.cloudfront.net/public/users/Integrators/7d081d61-4dc8-48b8-a19d-9d907d27d124/XJdIdqNvP6gN7jZQIETZMADLDue2/b222a835-aaf6-4085-ac3d-a1992aa21e61.png\" width=\"100\" alt=\"BIK Logo\" style=\"display:block;\"></td></tr><tr><td class=\"content\"><h1>Campaign Moved to Draft \u2013 Segment Sync Delay</h1><p>Hi {{Store Name}} team,</p><p>Your broadcast \"<strong>{{Broadcast Name}}</strong>\" has been moved to Draft because the associated segment sync is taking longer than 3 hours to complete.</p> <div style=\"text-align:center; margin:30px 0;\"><!--[if mso]><v:roundrect xmlns:v=\"urn:schemas-microsoft-com:vml\" href=\"https://dashboard.bik.ai/campaigns/overview\"style=\"height:40px;v-text-anchor:middle;width:260px;\" arcsize=\"50%\" stroke=\"f\" fillcolor=\"#4a136a\"><w:anchorlock/><center style=\"color:#ffffff;font-family:Arial;font-size:14px;font-weight:bold;\">View Campaign Details</center></v:roundrect><![endif]--><!--[if !mso]><!-- --><a href=\"https://dashboard.bik.ai/campaigns/overview\"class=\"button\"target=\"_blank\">View Campaign Details</a><!--<![endif]--></div> <p>To ensure accurate audience targeting, we have not sent the campaign.</p><p>We recommend rescheduling the campaign once the segment sync is complete.</p><p>If the delay continues or you need assistance, please contact BIK Support.</p><p>Best,<br>Team BIK</p></td></tr> </table></body></html>";
@@ -1,4 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.email = void 0;
4
- exports.email = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Campaign Moved to Draft</title><style>body { margin: 0; padding: 0; background-color: #f5f5f5; font-family: Arial, Helvetica, sans-serif;}table { border-collapse: collapse;}.container { width: 100%; max-width: 520px; margin: 0 auto; background-color: #ffffff;}.content { padding: 24px; color: #000000; font-size: 14px; line-height: 1.6;}h1 { font-size: 22px; text-align: center; margin: 20px 0;}.button { display: inline-block; background-color: #4a136a; color: #ffffff !important; text-decoration: none; padding: 12px 24px; border-radius: 4px; font-size: 14px; font-weight: bold;}.footer { font-size: 12px; color: #777777; text-align: center; padding: 20px;}.divider { border-top: 1px solid #dddddd; margin: 30px 0 20px;}@media only screen and (max-width: 520px) { .content { padding: 18px; }}</style></head><body><table width="100%"><tr><td align="center"><table class="container" role="presentation" cellpadding="0" cellspacing="0"><tr><td align="center" style="padding: 24px 0;"><img src="https://d15k2d11r6t6rl.cloudfront.net/public/users/Integrators/7d081d61-4dc8-48b8-a19d-9d907d27d124/XJdIdqNvP6gN7jZQIETZMADLDue2/b222a835-aaf6-4085-ac3d-a1992aa21e61.png" width="100" alt="BIK Logo" style="display:block;"></td></tr><tr><td class="content"><h1>Campaign Moved to Draft – Segment Sync Delay</h1><p>Hi {{Store Name}} team,</p><p>Your broadcast "<strong>{{Broadcast Name}}</strong>" has been moved to Draft because the associated segment sync is taking longer than 3 hours to complete.</p> <div style="text-align:center; margin:30px 0;"><!--[if mso]><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" href="https://dashboard.bik.ai/campaigns/overview"style="height:40px;v-text-anchor:middle;width:260px;" arcsize="50%" stroke="f" fillcolor="#4a136a"><w:anchorlock/><center style="color:#ffffff;font-family:Arial;font-size:14px;font-weight:bold;">View Campaign Details</center></v:roundrect><![endif]--><!--[if !mso]><!-- --><a href="https://dashboard.bik.ai/campaigns/overview"class="button"target="_blank">View Campaign Details</a><!--<![endif]--></div> <p>To ensure accurate audience targeting, we have not sent the campaign.</p><p>We recommend rescheduling the campaign once the segment sync is complete.</p><p>If the delay continues or you need assistance, please contact BIK Support.</p><p>Best,<br>Team BIK</p></td></tr> </table></body></html>`;
@@ -1 +0,0 @@
1
- export declare const email = "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Campaign Moved to Draft</title><style>body { margin: 0; padding: 0; background-color: #f5f5f5; font-family: Arial, Helvetica, sans-serif;}table { border-collapse: collapse;}.container { width: 100%; max-width: 520px; margin: 0 auto; background-color: #ffffff;}.content { padding: 24px; color: #000000; font-size: 14px; line-height: 1.6;}h1 { font-size: 22px; text-align: center; margin: 20px 0;}.button { display: inline-block; background-color: #4a136a; color: #ffffff !important; text-decoration: none; padding: 12px 24px; border-radius: 4px; font-size: 14px; font-weight: bold;}@media only screen and (max-width: 520px) { .content { padding: 18px; }}</style></head><body><table width=\"100%\"><tr><td align=\"center\"><table class=\"container\" role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\"><tr><td align=\"center\" style=\"padding: 24px 0;\"><img src=\"https://d15k2d11r6t6rl.cloudfront.net/public/users/Integrators/7d081d61-4dc8-48b8-a19d-9d907d27d124/XJdIdqNvP6gN7jZQIETZMADLDue2/b222a835-aaf6-4085-ac3d-a1992aa21e61.png\" width=\"100\" alt=\"BIK Logo\" style=\"display:block;\"></td></tr><tr><td class=\"content\"><h1>Campaign Moved to Draft \u2013 Segment Sync Failed</h1><p>Hi {{Store Name}} team,</p><p>Your campaign \"<strong>{{Campaign Name}}</strong>\" has been moved to Draft because the associated segment sync failed.</p><div style=\"text-align:center; margin:30px 0;\"><!--[if mso]><v:roundrect xmlns:v=\"urn:schemas-microsoft-com:vml\" href=\"https://dashboard.bik.ai/campaigns/overview\"style=\"height:40px;v-text-anchor:middle;width:260px;\" arcsize=\"50%\" stroke=\"f\" fillcolor=\"#4a136a\"><w:anchorlock/><center style=\"color:#ffffff;font-family:Arial;font-size:14px;font-weight:bold;\">View Campaign Details</center></v:roundrect><![endif]--><!--[if !mso]><!-- --><a href=\"https://dashboard.bik.ai/campaigns/overview\"class=\"button\"target=\"_blank\">View Campaign Details</a><!--<![endif]--></div><p>To prevent sending to an incomplete or incorrect audience, we have paused the campaign.</p><p>Please review the segment, retry the sync, and reschedule the campaign once the sync completes successfully.</p><p>Best,<br>Team BIK</p></td></tr></table></td></tr></table></body></html>";
@@ -1,4 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.email = void 0;
4
- exports.email = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Campaign Moved to Draft</title><style>body { margin: 0; padding: 0; background-color: #f5f5f5; font-family: Arial, Helvetica, sans-serif;}table { border-collapse: collapse;}.container { width: 100%; max-width: 520px; margin: 0 auto; background-color: #ffffff;}.content { padding: 24px; color: #000000; font-size: 14px; line-height: 1.6;}h1 { font-size: 22px; text-align: center; margin: 20px 0;}.button { display: inline-block; background-color: #4a136a; color: #ffffff !important; text-decoration: none; padding: 12px 24px; border-radius: 4px; font-size: 14px; font-weight: bold;}@media only screen and (max-width: 520px) { .content { padding: 18px; }}</style></head><body><table width="100%"><tr><td align="center"><table class="container" role="presentation" cellpadding="0" cellspacing="0"><tr><td align="center" style="padding: 24px 0;"><img src="https://d15k2d11r6t6rl.cloudfront.net/public/users/Integrators/7d081d61-4dc8-48b8-a19d-9d907d27d124/XJdIdqNvP6gN7jZQIETZMADLDue2/b222a835-aaf6-4085-ac3d-a1992aa21e61.png" width="100" alt="BIK Logo" style="display:block;"></td></tr><tr><td class="content"><h1>Campaign Moved to Draft – Segment Sync Failed</h1><p>Hi {{Store Name}} team,</p><p>Your campaign "<strong>{{Campaign Name}}</strong>" has been moved to Draft because the associated segment sync failed.</p><div style="text-align:center; margin:30px 0;"><!--[if mso]><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" href="https://dashboard.bik.ai/campaigns/overview"style="height:40px;v-text-anchor:middle;width:260px;" arcsize="50%" stroke="f" fillcolor="#4a136a"><w:anchorlock/><center style="color:#ffffff;font-family:Arial;font-size:14px;font-weight:bold;">View Campaign Details</center></v:roundrect><![endif]--><!--[if !mso]><!-- --><a href="https://dashboard.bik.ai/campaigns/overview"class="button"target="_blank">View Campaign Details</a><!--<![endif]--></div><p>To prevent sending to an incomplete or incorrect audience, we have paused the campaign.</p><p>Please review the segment, retry the sync, and reschedule the campaign once the sync completes successfully.</p><p>Best,<br>Team BIK</p></td></tr></table></td></tr></table></body></html>`;
@@ -1 +0,0 @@
1
- export declare const email = "<!DOCTYPE html><html lang=\"en\"><head><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><title>Campaign Scheduled Time Exceeded</title><style>body { margin: 0; padding: 0; background-color: #f5f5f5; font-family: Arial, Helvetica, sans-serif;}table { border-collapse: collapse;}.container { width: 100%; max-width: 520px; margin: 0 auto; background-color: #ffffff;}.content { padding: 24px; color: #000000; font-size: 14px; line-height: 1.6;}h1 { font-size: 22px; text-align: center; margin: 20px 0;}.button { display: inline-block; background-color: #4a136a; color: #ffffff !important; text-decoration: none; padding: 12px 24px; border-radius: 4px; font-size: 14px; font-weight: bold;}@media only screen and (max-width: 520px) { .content { padding: 18px; }}</style></head><body><table width=\"100%\"><tr><td align=\"center\"><table class=\"container\" role=\"presentation\" cellpadding=\"0\" cellspacing=\"0\"><tr><td align=\"center\" style=\"padding: 24px 0;\"><img src=\"https://d15k2d11r6t6rl.cloudfront.net/public/users/Integrators/7d081d61-4dc8-48b8-a19d-9d907d27d124/XJdIdqNvP6gN7jZQIETZMADLDue2/b222a835-aaf6-4085-ac3d-a1992aa21e61.png\" width=\"100\" alt=\"BIK Logo\" style=\"display:block;\"></td></tr><tr><td class=\"content\"><h1>Campaign Scheduled Time Exceeded Due to Sync Delay</h1><p>Hi {{Store Name}} team,</p><p>Your broadcast \"<strong>{{Broadcast Name}}</strong>\" is currently scheduled, and the associated segment sync is still in progress.</p><p>To ensure accurate audience targeting, the campaign will only be executed once the segment sync is successfully completed.</p><p><strong>Please note:</strong> If the sync takes longer than 3 hours and does not complete before the scheduled send time, the campaign will not be executed and will automatically move to Draft.</p><p>We recommend monitoring the segment sync status and rescheduling the campaign if needed.</p><div style=\"text-align:center; margin:30px 0;\"><!--[if mso]><v:roundrect xmlns:v=\"urn:schemas-microsoft-com:vml\" href=\"https://dashboard.bik.ai/campaigns/overview\"style=\"height:40px;v-text-anchor:middle;width:260px;\" arcsize=\"50%\" stroke=\"f\" fillcolor=\"#4a136a\"><w:anchorlock/><center style=\"color:#ffffff;font-family:Arial;font-size:14px;font-weight:bold;\">View Campaign Details</center></v:roundrect><![endif]--><!--[if !mso]><!-- --><a href=\"https://dashboard.bik.ai/campaigns/overview\"class=\"button\"target=\"_blank\">View Campaign Details</a><!--<![endif]--></div><p>If you need any assistance, please contact BIK Support.</p><p>Best,<br>Team BIK</p></td></tr></table></td></tr></table></body></html>";
@@ -1,4 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.email = void 0;
4
- exports.email = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Campaign Scheduled Time Exceeded</title><style>body { margin: 0; padding: 0; background-color: #f5f5f5; font-family: Arial, Helvetica, sans-serif;}table { border-collapse: collapse;}.container { width: 100%; max-width: 520px; margin: 0 auto; background-color: #ffffff;}.content { padding: 24px; color: #000000; font-size: 14px; line-height: 1.6;}h1 { font-size: 22px; text-align: center; margin: 20px 0;}.button { display: inline-block; background-color: #4a136a; color: #ffffff !important; text-decoration: none; padding: 12px 24px; border-radius: 4px; font-size: 14px; font-weight: bold;}@media only screen and (max-width: 520px) { .content { padding: 18px; }}</style></head><body><table width="100%"><tr><td align="center"><table class="container" role="presentation" cellpadding="0" cellspacing="0"><tr><td align="center" style="padding: 24px 0;"><img src="https://d15k2d11r6t6rl.cloudfront.net/public/users/Integrators/7d081d61-4dc8-48b8-a19d-9d907d27d124/XJdIdqNvP6gN7jZQIETZMADLDue2/b222a835-aaf6-4085-ac3d-a1992aa21e61.png" width="100" alt="BIK Logo" style="display:block;"></td></tr><tr><td class="content"><h1>Campaign Scheduled Time Exceeded Due to Sync Delay</h1><p>Hi {{Store Name}} team,</p><p>Your broadcast "<strong>{{Broadcast Name}}</strong>" is currently scheduled, and the associated segment sync is still in progress.</p><p>To ensure accurate audience targeting, the campaign will only be executed once the segment sync is successfully completed.</p><p><strong>Please note:</strong> If the sync takes longer than 3 hours and does not complete before the scheduled send time, the campaign will not be executed and will automatically move to Draft.</p><p>We recommend monitoring the segment sync status and rescheduling the campaign if needed.</p><div style="text-align:center; margin:30px 0;"><!--[if mso]><v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" href="https://dashboard.bik.ai/campaigns/overview"style="height:40px;v-text-anchor:middle;width:260px;" arcsize="50%" stroke="f" fillcolor="#4a136a"><w:anchorlock/><center style="color:#ffffff;font-family:Arial;font-size:14px;font-weight:bold;">View Campaign Details</center></v:roundrect><![endif]--><!--[if !mso]><!-- --><a href="https://dashboard.bik.ai/campaigns/overview"class="button"target="_blank">View Campaign Details</a><!--<![endif]--></div><p>If you need any assistance, please contact BIK Support.</p><p>Best,<br>Team BIK</p></td></tr></table></td></tr></table></body></html>`;
@@ -1,22 +0,0 @@
1
- import { PodNameOrGlobal, SecretSchema } from './secretManager.model';
2
- import { ConfigDocument, ConfigSection } from './configManager.model';
3
- export declare class FirestoreConfigManager {
4
- private static instance;
5
- private db;
6
- private secretsCollection;
7
- private readonly envDocPrefix;
8
- private readonly globalSecretsDocId;
9
- private constructor();
10
- static getInstance(): FirestoreConfigManager;
11
- private getDocId;
12
- private isDocFromSection;
13
- private getPodNameFromDocId;
14
- private toEmptySchema;
15
- getConfig(section: ConfigSection, podName: PodNameOrGlobal): Promise<ConfigDocument>;
16
- storeConfig(section: ConfigSection, podName: PodNameOrGlobal, schema: SecretSchema, updatedBy?: string): Promise<void>;
17
- getAll(section: ConfigSection): Promise<Record<string, SecretSchema>>;
18
- getAllConfigs(): Promise<{
19
- envVariables: Record<string, SecretSchema>;
20
- secrets: Record<string, SecretSchema>;
21
- }>;
22
- }