@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.
- package/lib/alertsV2/alertInstances.repo.js +2 -26
- package/lib/alertsV2/alertsV2.helper.d.ts +4 -3
- package/lib/alertsV2/alertsV2.helper.js +3 -49
- package/lib/alertsV2/alertsV2.service.js +5 -14
- package/lib/auth/authMiddlewares.js +3 -68
- package/lib/auth/firebase-auth.service.js +3 -3
- package/lib/auth/implementations/bik-admin-auth-service.d.ts +1 -1
- package/lib/auth/implementations/bik-admin-auth-service.js +1 -2
- package/lib/auth/index.d.ts +0 -3
- package/lib/auth/index.js +0 -3
- package/lib/auth/secret-manager/env-variables/variables.list.d.ts +0 -3
- package/lib/auth/secret-manager/env-variables/variables.list.js +0 -3
- package/lib/chat-handover-protocol/chat-handover-protocol.js +41 -26
- package/lib/core/setup.d.ts +0 -3
- package/lib/core/setup.js +2 -24
- package/lib/elastic/counter/ingestion.js +0 -1
- package/lib/elastic/queries/campaign/getBroadcastDetailedStats.d.ts +0 -117
- package/lib/elastic/queries/campaign/getBroadcastDetailedStats.js +1 -94
- package/lib/elastic/queries/campaign/getUniqueCustomerCnt.d.ts +0 -1
- package/lib/elastic/queries/campaign/getUniqueCustomerCnt.js +0 -1
- package/lib/elastic/queries/chatbot/getAiOperations.d.ts +5 -5
- package/lib/elastic/queries/chatbot/getAiOperations.js +3 -3
- package/lib/elastic/queries/chatbot/index.d.ts +0 -1
- package/lib/elastic/queries/chatbot/index.js +0 -1
- package/lib/elastic/queries/crm/getActivityTimelineByAgent.js +1 -1
- package/lib/elastic/queries/crm/getBreachedSLACount.d.ts +0 -1
- package/lib/elastic/queries/crm/getBreachedSLACount.js +5 -8
- package/lib/elastic/queries/crm/getFirstResponseTime.d.ts +0 -1
- package/lib/elastic/queries/crm/getFirstResponseTime.js +5 -8
- package/lib/elastic/queries/integrations/index.d.ts +0 -1
- package/lib/elastic/queries/integrations/index.js +0 -1
- package/lib/elastic/queries/openAi/addToCartSession.d.ts +0 -1
- package/lib/elastic/queries/openAi/addToCartSession.js +3 -11
- package/lib/elastic/queries/openAi/checkoutCompletedSession.d.ts +0 -1
- package/lib/elastic/queries/openAi/checkoutCompletedSession.js +3 -11
- package/lib/elastic/reports/crm/index.d.ts +0 -1
- package/lib/elastic/reports/crm/index.js +0 -1
- package/lib/elastic/reports/reports.service.js +8 -17
- package/lib/events/events.d.ts +0 -8
- package/lib/events/events.js +1 -25
- package/lib/events/schema/events.helper.d.ts +0 -1
- package/lib/events/schema/events.helper.js +6 -12
- package/lib/index.d.ts +0 -1
- package/lib/index.js +0 -1
- package/lib/merchant-events/elastic.search.d.ts +0 -5
- package/lib/merchant-events/elastic.search.js +2 -23
- package/lib/merchant-events/merchant.service.d.ts +1 -1
- package/lib/merchant-events/merchant.service.js +11 -12
- package/lib/recordAnalytics/recordAnalytics.service.js +4 -5
- package/lib/redis/redisPubSubService.d.ts +6 -4
- package/lib/redis/redisPubSubService.js +123 -211
- package/lib/redis/redisService.js +8 -14
- package/lib/swagger/SwaggerSchemaHelper.js +17 -21
- package/lib/user-properties/userProperties.service.js +0 -1
- package/package.json +2 -2
- package/lib/alerts/templates/campaign/campaignMovedToDraftSegmentSyncDelay.d.ts +0 -1
- package/lib/alerts/templates/campaign/campaignMovedToDraftSegmentSyncDelay.js +0 -4
- package/lib/alerts/templates/campaign/campaignMovedToDraftSegmentSyncFailed.d.ts +0 -1
- package/lib/alerts/templates/campaign/campaignMovedToDraftSegmentSyncFailed.js +0 -4
- package/lib/alerts/templates/campaign/campaignScheduledTimeExceededDueToSyncDelay.d.ts +0 -1
- package/lib/alerts/templates/campaign/campaignScheduledTimeExceededDueToSyncDelay.js +0 -4
- package/lib/auth/secret-manager/configManager.firestore.d.ts +0 -22
- package/lib/auth/secret-manager/configManager.firestore.js +0 -166
- package/lib/auth/secret-manager/configManager.helper.d.ts +0 -13
- package/lib/auth/secret-manager/configManager.helper.js +0 -32
- package/lib/auth/secret-manager/configManager.model.d.ts +0 -38
- package/lib/auth/secret-manager/configManager.model.js +0 -2
- package/lib/auth/secret-manager/configManager.service.d.ts +0 -17
- package/lib/auth/secret-manager/configManager.service.js +0 -138
- package/lib/core/local_runner.d.ts +0 -1
- package/lib/core/local_runner.js +0 -60
- package/lib/database/database.model.d.ts +0 -95
- package/lib/database/database.model.js +0 -5
- package/lib/database/database.service.d.ts +0 -90
- package/lib/database/database.service.js +0 -382
- package/lib/database/index.d.ts +0 -7
- package/lib/database/index.js +0 -23
- package/lib/elastic/queries/chatbot/getAgentCostForBroadcast.d.ts +0 -117
- package/lib/elastic/queries/chatbot/getAgentCostForBroadcast.js +0 -98
- package/lib/elastic/queries/integrations/getOrdersShadowServices.d.ts +0 -76
- 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.
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
|
152
|
-
console.log('[RedisPubSubService] Redis pub/sub connections closed successfully');
|
|
118
|
+
return yield fn();
|
|
153
119
|
}
|
|
154
120
|
catch (err) {
|
|
155
|
-
|
|
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
|
-
|
|
159
|
-
|
|
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
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
//
|
|
187
|
-
//
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
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
|
-
|
|
166
|
+
const parsed = JSON.parse(message);
|
|
167
|
+
console.log(`Message received on ${this.channel}:`, parsed);
|
|
168
|
+
callback(parsed);
|
|
239
169
|
}
|
|
240
|
-
catch (
|
|
241
|
-
console.
|
|
170
|
+
catch (e) {
|
|
171
|
+
console.error('Error parsing message:', e);
|
|
242
172
|
}
|
|
243
173
|
});
|
|
244
174
|
}
|
|
245
|
-
|
|
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
|
-
|
|
251
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
-
|
|
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
|
|
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[
|
|
111
|
-
|
|
112
|
-
description:
|
|
106
|
+
swaggerConfig["responses"] = {
|
|
107
|
+
"200": {
|
|
108
|
+
"description": "Default Response"
|
|
113
109
|
},
|
|
114
|
-
|
|
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[
|
|
119
|
-
content: {
|
|
120
|
-
|
|
121
|
-
schema: {
|
|
122
|
-
type:
|
|
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
|
|
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
|
|
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
|
-
}
|