shuttlepro-shared 1.4.2 → 1.4.4

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.
@@ -60,10 +60,41 @@ const deleteCustomerProfile = async (id) => {
60
60
  const deleteAllCustomerProfilesByWorkspace = async (workspaceId) => {
61
61
  await CustomerProfile.deleteMany({ workspaceId }).exec();
62
62
  };
63
+
63
64
  const findAllCustomerProfilesByFilterFromDb = async (filter = {}) => {
64
65
  const profiles = await CustomerProfile.find(filter).lean().exec();
65
66
  return profiles ?? [];
66
67
  };
68
+
69
+ const searchCustomerProfilesByName = async (
70
+ { searchValue, workspaceId },
71
+ page = 1,
72
+ limit = 10
73
+ ) => {
74
+ const queryObj = {};
75
+
76
+ if (workspaceId) {
77
+ queryObj.workspaceId = workspaceId;
78
+ }
79
+
80
+ if (searchValue) {
81
+ queryObj.name = { $regex: searchValue, $options: "i" };
82
+ }
83
+
84
+ const skip = (page - 1) * limit;
85
+
86
+ const [profiles, total] = await Promise.all([
87
+ CustomerProfile.find(queryObj).skip(skip).limit(limit).lean().exec(),
88
+ CustomerProfile.countDocuments(queryObj),
89
+ ]);
90
+
91
+ return {
92
+ data: profiles,
93
+ page,
94
+ total,
95
+ totalPages: Math.ceil(total / limit),
96
+ };
97
+ };
67
98
  module.exports = {
68
99
  createCustomerProfile,
69
100
  findCustomerProfilesByWorkspaceId,
@@ -75,4 +106,5 @@ module.exports = {
75
106
  deleteAllCustomerProfilesByWorkspace,
76
107
  findAllCustomerProfilesByFilter,
77
108
  findAllCustomerProfilesByFilterFromDb,
109
+ searchCustomerProfilesByName,
78
110
  };
@@ -15,8 +15,7 @@ const notificationSettingsRepository = require("./notificationSettings.repositor
15
15
  const customerProfileRepository = require("./customerProfile.repository");
16
16
  const customerTimelineRepository = require("./customerTimeline.repository");
17
17
  const shopRepository = require("./shop.repository");
18
- const callRepository = require("./call.repository");
19
-
18
+ const interactiveRepository = require("./interactive.repository");
20
19
  exports.module = {
21
20
  workspaceRepository,
22
21
  integrationRepository,
@@ -35,5 +34,5 @@ exports.module = {
35
34
  customerProfileRepository,
36
35
  customerTimelineRepository,
37
36
  shopRepository,
38
- callRepository,
37
+ interactiveRepository,
39
38
  };
@@ -0,0 +1,124 @@
1
+ // repositories/interactive.repository.js
2
+ const Interactive = require("../../models/Interactive");
3
+ const { getRedisData, setRedisData } = require("../../config/redis");
4
+
5
+ const CACHE_KEY_ALL = "interactive_all";
6
+
7
+ /**
8
+ * Get cached interactive for all workspaces.
9
+ */
10
+ const getCachedAllInteractive = async () => {
11
+ let interactive = await getRedisData(CACHE_KEY_ALL);
12
+ if (!interactive) {
13
+ interactive = await Interactive.find({}).lean().exec();
14
+ await setRedisData(CACHE_KEY_ALL, interactive);
15
+ }
16
+ return interactive;
17
+ };
18
+
19
+ /**
20
+ * Update cached interactive for all workspaces.
21
+ */
22
+ const updateCachedAllInteractive = async () => {
23
+ const interactive = await Interactive.find({}).lean().exec();
24
+ await setRedisData(CACHE_KEY_ALL, interactive);
25
+ };
26
+
27
+ /**
28
+ * Get interactive for a specific workspace from cache.
29
+ */
30
+ const getWorkspaceInteractive = async (workspaceId) => {
31
+ if (!workspaceId) return [];
32
+ const allInteractive = await getCachedAllInteractive();
33
+ return allInteractive.filter(
34
+ (item) => item.workspaceId?.toString() === workspaceId.toString()
35
+ );
36
+ };
37
+ /**
38
+ * Get interactive by ID (cache first).
39
+ */
40
+ const getInteractiveById = async (id) => {
41
+ if (!id) return null;
42
+
43
+ const allInteractive = await getCachedAllInteractive();
44
+ let interactive = allInteractive.find(
45
+ (item) => item._id?.toString() === id.toString()
46
+ );
47
+
48
+ // If not found in cache, fetch from DB and update cache
49
+ if (!interactive) {
50
+ interactive = await Interactive.findById(id).lean().exec();
51
+ if (interactive) {
52
+ await updateCachedAllInteractive();
53
+ }
54
+ }
55
+
56
+ return interactive || null;
57
+ };
58
+
59
+ /**
60
+ * Create new interactive and refresh cache.
61
+ */
62
+ const createInteractive = async (data) => {
63
+ const newInteractive = await Interactive.create(data);
64
+ if (newInteractive) {
65
+ await updateCachedAllInteractive();
66
+ }
67
+ return newInteractive;
68
+ };
69
+
70
+ /**
71
+ * Update interactive by ID and refresh cache.
72
+ */
73
+ const updateInteractiveById = async (id, data) => {
74
+ const updated = await Interactive.findByIdAndUpdate(id, data, {
75
+ new: true,
76
+ }).exec();
77
+ if (updated) {
78
+ await updateCachedAllInteractive();
79
+ }
80
+ return updated;
81
+ };
82
+
83
+ /**
84
+ * Update interactive by filter and refresh cache.
85
+ */
86
+ const updateSingleInteractiveByFilter = async (filter, data) => {
87
+ const updated = await Interactive.findOneAndUpdate(filter, data, {
88
+ new: true,
89
+ }).exec();
90
+ if (updated) {
91
+ await updateCachedAllInteractive();
92
+ }
93
+ return updated;
94
+ };
95
+
96
+ /**
97
+ * Delete interactive by ID and refresh cache.
98
+ */
99
+ const deleteInteractiveById = async (id) => {
100
+ const deleted = await Interactive.findByIdAndDelete(id).exec();
101
+ if (deleted) {
102
+ await updateCachedAllInteractive();
103
+ }
104
+ return deleted;
105
+ };
106
+
107
+ /**
108
+ * Delete interactive by filter and refresh cache.
109
+ */
110
+ const deleteInteractiveByFilter = async (filter) => {
111
+ await Interactive.deleteMany(filter).exec();
112
+ await updateCachedAllInteractive();
113
+ };
114
+
115
+ module.exports = {
116
+ updateCachedAllInteractive,
117
+ getWorkspaceInteractive,
118
+ createInteractive,
119
+ updateInteractiveById,
120
+ updateSingleInteractiveByFilter,
121
+ deleteInteractiveById,
122
+ deleteInteractiveByFilter,
123
+ getInteractiveById,
124
+ };
package/config/socket.js CHANGED
@@ -4,46 +4,27 @@ require("dotenv").config();
4
4
 
5
5
  let io;
6
6
  const namespaceSockets = {}; // Store different namespace sockets
7
- const namespaceHandlers = {}; // Store custom handlers for each namespace
8
-
9
- /**
10
- * Register custom event handlers for a specific namespace
11
- * @param {string} namespace - Namespace name
12
- * @param {Function} handlerFunction - Function that sets up socket event handlers
13
- */
14
- const registerNamespaceHandlers = (namespace, handlerFunction) => {
15
- namespaceHandlers[namespace] = handlerFunction;
16
- console.log(`📋 Registered custom handlers for namespace: /${namespace}`);
17
- };
18
7
 
19
8
  /**
20
9
  * Initialize Socket.IO server with namespaces
21
10
  * @param {http.Server} server - HTTP server instance
22
11
  * @param {string} namespaceParam - Socket namespace name (default: "conversation")
23
12
  * @param {string} redisChannel - Redis channel for this namespace (default: "socket_events")
24
- * @param {Object} options - Additional options for namespace configuration
25
- * @param {Function} options.customHandlers - Custom socket event handlers for this namespace
26
- * @param {boolean} options.requireWorkspaceId - Whether workspace ID is required (default: true)
27
13
  * @returns {SocketIO.Namespace} - The initialized namespace
28
14
  */
29
15
  const initializeSocket = async (
30
16
  server,
31
17
  namespaceParam = "conversation",
32
- redisChannel = "socket_events",
33
- options = {}
18
+ redisChannel = "socket_events"
34
19
  ) => {
35
- const {
36
- customHandlers,
37
- requireWorkspaceId = true,
38
- cors = {
39
- origin: "*",
40
- methods: ["GET", "POST"],
41
- },
42
- } = options;
43
-
44
20
  // Initialize Socket.IO server if not already done
45
21
  if (!io) {
46
- io = new Server(server, { cors });
22
+ io = new Server(server, {
23
+ cors: {
24
+ origin: "*",
25
+ methods: ["GET", "POST"],
26
+ },
27
+ });
47
28
  console.log("✅ Socket.IO server initialized");
48
29
  }
49
30
 
@@ -68,36 +49,21 @@ const initializeSocket = async (
68
49
  await subscriber.subscribe(nsRedisChannel, (messageStr) => {
69
50
  try {
70
51
  const message = JSON.parse(messageStr);
71
- const { workspaceId, event, data, target } = message;
52
+ const { workspaceId, event, data } = message;
72
53
 
73
54
  console.log(`📥 Redis message received on ${nsRedisChannel}:`, {
74
55
  workspaceId,
75
56
  event,
76
- target,
77
57
  });
78
58
 
79
- // Handle different targeting options
80
- if (target === "broadcast") {
81
- // Broadcast to all clients in this namespace
82
- namespace.emit(event, data);
83
- console.log(
84
- `📢 Event ${event} broadcasted to all clients in /${namespaceParam}`
85
- );
86
- } else if (target && target.startsWith("socket:")) {
87
- // Target specific socket ID
88
- const socketId = target.replace("socket:", "");
89
- namespace.to(socketId).emit(event, data);
90
- console.log(
91
- `📢 Event ${event} emitted to socket ${socketId} in /${namespaceParam}`
92
- );
93
- } else if (workspaceId) {
94
- // Emit to specific workspace room in this namespace
59
+ // Emit to specific workspace room in this namespace
60
+ if (workspaceId) {
95
61
  namespace.to(workspaceId).emit(event, data);
96
62
  console.log(
97
63
  `📢 Event ${event} emitted to workspace ${workspaceId} in /${namespaceParam}`
98
64
  );
99
65
  } else {
100
- // Default: broadcast to all clients in this namespace
66
+ // Broadcast to all clients in this namespace if no workspaceId specified
101
67
  namespace.emit(event, data);
102
68
  console.log(
103
69
  `📢 Event ${event} broadcasted to all clients in /${namespaceParam}`
@@ -113,44 +79,20 @@ const initializeSocket = async (
113
79
 
114
80
  // Handle new socket connections to this namespace
115
81
  namespace.on("connection", (socket) => {
116
- const { workspaceId, callId, agentId } = socket.handshake.query;
82
+ const { workspaceId } = socket.handshake.query;
117
83
 
118
- // Workspace ID validation (can be disabled for certain namespaces)
119
- if (requireWorkspaceId && !workspaceId) {
120
- console.warn(
121
- `⚠️ Connection rejected: No workspaceId provided for namespace /${namespaceParam}`
122
- );
84
+ if (!workspaceId) {
85
+ console.warn(`⚠️ Connection rejected: No workspaceId provided`);
123
86
  socket.disconnect(true);
124
87
  return;
125
88
  }
126
89
 
127
90
  console.log(
128
- `✅ Client connected: ${socket.id} (Workspace: ${
129
- workspaceId || "N/A"
130
- }, CallId: ${callId || "N/A"}) in /${namespaceParam}`
91
+ `✅ Client connected: ${socket.id} (Workspace: ${workspaceId}) in /${namespaceParam}`
131
92
  );
132
93
 
133
- // Join workspace room if workspaceId is provided
134
- if (workspaceId) {
135
- socket.join(workspaceId);
136
- }
137
-
138
- // Join additional rooms based on query parameters
139
- if (callId) {
140
- socket.join(`call:${callId}`);
141
- }
142
- if (agentId) {
143
- socket.join(`agent:${agentId}`);
144
- }
145
-
146
- // Store socket metadata
147
- socket.metadata = {
148
- workspaceId,
149
- callId,
150
- agentId,
151
- namespace: namespaceParam,
152
- connectedAt: new Date(),
153
- };
94
+ // Join workspace room
95
+ socket.join(workspaceId);
154
96
 
155
97
  // Send connection confirmation to client
156
98
  socket.emit("connected", {
@@ -158,70 +100,25 @@ const initializeSocket = async (
158
100
  socketId: socket.id,
159
101
  namespace: namespaceParam,
160
102
  workspaceId,
161
- callId,
162
- agentId,
163
103
  });
164
104
 
165
- // Apply custom handlers if provided
166
- if (customHandlers && typeof customHandlers === "function") {
167
- try {
168
- customHandlers(socket, namespace);
169
- console.log(
170
- `📋 Applied custom handlers for socket ${socket.id} in /${namespaceParam}`
171
- );
172
- } catch (error) {
173
- console.error(
174
- `❌ Error applying custom handlers for socket ${socket.id}:`,
175
- error
176
- );
177
- }
178
- }
179
-
180
- // Apply registered namespace handlers
181
- if (namespaceHandlers[namespaceParam]) {
182
- try {
183
- namespaceHandlers[namespaceParam](socket, namespace);
184
- console.log(
185
- `📋 Applied registered handlers for socket ${socket.id} in /${namespaceParam}`
186
- );
187
- } catch (error) {
188
- console.error(
189
- `❌ Error applying registered handlers for socket ${socket.id}:`,
190
- error
191
- );
192
- }
193
- }
194
-
195
- // Default event handlers for backward compatibility
196
- setupDefaultHandlers(socket, namespace, namespaceParam);
105
+ // Handle custom events from clients
106
+ socket.on("client_event", (data) => {
107
+ console.log(`📥 Client event from ${socket.id}:`, data);
108
+ // You can process client events here
109
+ });
197
110
 
198
111
  // Handle disconnect
199
112
  socket.on("disconnect", (reason) => {
200
113
  console.log(
201
- `❌ Client disconnected: ${socket.id} (Workspace: ${
202
- workspaceId || "N/A"
203
- }, Reason: ${reason}) from /${namespaceParam}`
114
+ `❌ Client disconnected: ${socket.id} (Workspace: ${workspaceId}, Reason: ${reason})`
204
115
  );
205
-
206
- // Leave all rooms
207
- if (workspaceId) socket.leave(workspaceId);
208
- if (callId) socket.leave(`call:${callId}`);
209
- if (agentId) socket.leave(`agent:${agentId}`);
210
-
211
- // Emit disconnect event to namespace for cleanup
212
- namespace.emit("client_disconnected", {
213
- socketId: socket.id,
214
- metadata: socket.metadata,
215
- reason,
216
- });
116
+ socket.leave(workspaceId);
217
117
  });
218
118
 
219
119
  // Handle errors
220
120
  socket.on("error", (error) => {
221
- console.error(
222
- `❌ Socket error for ${socket.id} in /${namespaceParam}:`,
223
- error
224
- );
121
+ console.error(`❌ Socket error for ${socket.id}:`, error);
225
122
  });
226
123
  });
227
124
 
@@ -231,64 +128,15 @@ const initializeSocket = async (
231
128
  return namespace;
232
129
  };
233
130
 
234
- /**
235
- * Setup default event handlers for backward compatibility
236
- * @param {Socket} socket - Socket.IO socket instance
237
- * @param {Namespace} namespace - Socket.IO namespace instance
238
- * @param {string} namespaceParam - Namespace name
239
- */
240
- const setupDefaultHandlers = (socket, namespace, namespaceParam) => {
241
- // Handle custom events from clients (backward compatibility)
242
- socket.on("client_event", (data) => {
243
- console.log(
244
- `📥 Client event from ${socket.id} in /${namespaceParam}:`,
245
- data
246
- );
247
-
248
- // Emit to Redis for other instances to handle
249
- sendEventToServer({
250
- workspaceId: socket.metadata.workspaceId,
251
- event: "client_event_received",
252
- data: {
253
- socketId: socket.id,
254
- originalData: data,
255
- metadata: socket.metadata,
256
- },
257
- namespace: namespaceParam,
258
- }).catch((err) => {
259
- console.error(`❌ Error publishing client_event to Redis:`, err);
260
- });
261
- });
262
-
263
- // Handle room join requests
264
- socket.on("join_room", (roomName) => {
265
- if (roomName && typeof roomName === "string") {
266
- socket.join(roomName);
267
- socket.emit("room_joined", { room: roomName, success: true });
268
- console.log(`📥 Socket ${socket.id} joined room: ${roomName}`);
269
- }
270
- });
271
-
272
- // Handle room leave requests
273
- socket.on("leave_room", (roomName) => {
274
- if (roomName && typeof roomName === "string") {
275
- socket.leave(roomName);
276
- socket.emit("room_left", { room: roomName, success: true });
277
- console.log(`📤 Socket ${socket.id} left room: ${roomName}`);
278
- }
279
- });
280
- };
281
-
282
131
  /**
283
132
  * Send event to clients via Redis pub/sub
284
133
  * @param {object} params - Event parameters
285
- * @param {string} [params.workspaceId] - Target workspace ID
134
+ * @param {string} params.workspaceId - Target workspace ID
286
135
  * @param {string} params.event - Event name
287
136
  * @param {any} params.data - Event data payload
288
137
  * @param {string} [params.namespace="conversation"] - Target namespace
289
138
  * @param {string} [params.redisChannel="socket_events"] - Base Redis channel
290
- * @param {string} [params.target] - Specific target (broadcast, socket:socketId, etc.)
291
- * @returns {Promise<boolean>}
139
+ * @returns {Promise<void>}
292
140
  */
293
141
  const sendEventToServer = async ({
294
142
  workspaceId,
@@ -296,7 +144,6 @@ const sendEventToServer = async ({
296
144
  data,
297
145
  namespace = "conversation",
298
146
  redisChannel = "socket_events",
299
- target,
300
147
  }) => {
301
148
  try {
302
149
  // Create namespace-specific Redis channel
@@ -306,21 +153,13 @@ const sendEventToServer = async ({
306
153
  await connectRedis();
307
154
 
308
155
  // Create message payload
309
- const message = JSON.stringify({
310
- workspaceId,
311
- event,
312
- data,
313
- target,
314
- timestamp: new Date().toISOString(),
315
- });
156
+ const message = JSON.stringify({ workspaceId, event, data });
316
157
 
317
158
  // Publish to Redis
318
159
  await publisher.publish(nsRedisChannel, message);
319
160
 
320
161
  console.log(
321
- `📢 Event published to Redis channel ${nsRedisChannel}: ${event} (Workspace: ${
322
- workspaceId || "N/A"
323
- }, Target: ${target || "default"})`
162
+ `📢 Event published to Redis channel ${nsRedisChannel}: ${event} (Workspace: ${workspaceId})`
324
163
  );
325
164
 
326
165
  return true;
@@ -330,62 +169,4 @@ const sendEventToServer = async ({
330
169
  }
331
170
  };
332
171
 
333
- /**
334
- * Get a specific namespace instance
335
- * @param {string} namespace - Namespace name
336
- * @returns {SocketIO.Namespace|null} - Namespace instance or null if not found
337
- */
338
- const getNamespace = (namespace) => {
339
- return namespaceSockets[namespace] || null;
340
- };
341
-
342
- /**
343
- * Get all active namespaces
344
- * @returns {Object} - Object containing all active namespaces
345
- */
346
- const getAllNamespaces = () => {
347
- return { ...namespaceSockets };
348
- };
349
-
350
- /**
351
- * Get Socket.IO server instance
352
- * @returns {SocketIO.Server|null} - Server instance or null if not initialized
353
- */
354
- const getSocketServer = () => {
355
- return io || null;
356
- };
357
-
358
- /**
359
- * Emit event to specific room in a namespace
360
- * @param {string} namespace - Namespace name
361
- * @param {string} room - Room name
362
- * @param {string} event - Event name
363
- * @param {any} data - Event data
364
- * @returns {boolean} - Success status
365
- */
366
- const emitToRoom = (namespace, room, event, data) => {
367
- try {
368
- const ns = namespaceSockets[namespace];
369
- if (!ns) {
370
- console.warn(`❌ Namespace /${namespace} not found`);
371
- return false;
372
- }
373
-
374
- ns.to(room).emit(event, data);
375
- console.log(`📢 Event ${event} emitted to room ${room} in /${namespace}`);
376
- return true;
377
- } catch (error) {
378
- console.error(`❌ Error emitting to room ${room} in /${namespace}:`, error);
379
- return false;
380
- }
381
- };
382
-
383
- module.exports = {
384
- initializeSocket,
385
- sendEventToServer,
386
- registerNamespaceHandlers,
387
- getNamespace,
388
- getAllNamespaces,
389
- getSocketServer,
390
- emitToRoom,
391
- };
172
+ module.exports = { initializeSocket, sendEventToServer };
@@ -10,6 +10,11 @@ const AttributeSchema = new Schema(
10
10
  ref: "Workspace",
11
11
  default: null,
12
12
  },
13
+ websiteId: {
14
+ type: Schema.Types.ObjectId,
15
+ ref: "Website",
16
+ default: null,
17
+ },
13
18
  oldId: { type: String, default: "" },
14
19
  createdBy: { type: Schema.Types.ObjectId, ref: "User", default: null },
15
20
  updatedBy: { type: Schema.Types.ObjectId, ref: "User", default: null },
@@ -61,6 +61,9 @@ const AutomationAction = new Schema({
61
61
  deleteComment: {
62
62
  enabled: { type: Boolean, default: false },
63
63
  },
64
+ hideComment: {
65
+ enabled: { type: Boolean, default: false },
66
+ },
64
67
  closeChat: {
65
68
  enabled: { type: Boolean, default: false },
66
69
  },
@@ -96,6 +99,11 @@ const AutomationAction = new Schema({
96
99
  default: null,
97
100
  },
98
101
  members: [userJoin],
102
+ templateFormId: {
103
+ type: mongoose.Schema.Types.ObjectId,
104
+ ref: "FormTemplate",
105
+ default: null,
106
+ },
99
107
  },
100
108
  escalation: {
101
109
  enabled: { type: Boolean, default: false },
@@ -160,6 +168,7 @@ const AutomationCondition = new Schema({
160
168
  "post",
161
169
  "shift",
162
170
  "newCommentPost",
171
+ "profile",
163
172
  ],
164
173
  },
165
174
  keyValue: {
@@ -183,6 +192,7 @@ const AutomationCondition = new Schema({
183
192
  "orderConfirmation",
184
193
  "orderPublish",
185
194
  "newCommentPost",
195
+ "profile",
186
196
  ],
187
197
  },
188
198
  subKeyValue: {
package/models/Card.js CHANGED
@@ -156,6 +156,11 @@ const cardSchema = new mongoose.Schema(
156
156
  type: mongoose.Schema.Types.Mixed,
157
157
  default: {},
158
158
  },
159
+ templateFormId: {
160
+ type: mongoose.Schema.Types.ObjectId,
161
+ ref: "FormTemplate",
162
+ default: null,
163
+ },
159
164
  },
160
165
  { timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true } }
161
166
  );
@@ -11,6 +11,11 @@ const CategorySchema = new Schema(
11
11
  ref: "Workspace",
12
12
  default: null,
13
13
  },
14
+ websiteId: {
15
+ type: Schema.Types.ObjectId,
16
+ ref: "Website",
17
+ default: null,
18
+ },
14
19
  oldId: { type: String, default: "" },
15
20
  webCategoryId: { type: Number, default: null },
16
21
  parentId: { type: Schema.Types.ObjectId, ref: "Category", default: null },
package/models/Chatbot.js CHANGED
@@ -30,6 +30,7 @@ const ChatbotSchema = new Schema(
30
30
  },
31
31
  events: [
32
32
  {
33
+ type: { type: String, default: "" },
33
34
  value: { type: String, default: "" },
34
35
  },
35
36
  ],
@@ -14,6 +14,11 @@ const CheckpointSchema = new Schema(
14
14
  ref: "Workspace",
15
15
  default: null,
16
16
  },
17
+ websiteId: {
18
+ type: Schema.Types.ObjectId,
19
+ ref: "Website",
20
+ default: null,
21
+ },
17
22
  },
18
23
  { timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true } }
19
24
  );
@@ -18,6 +18,11 @@ const CustomerSchema = new Schema(
18
18
  ref: "Workspace",
19
19
  default: null,
20
20
  },
21
+ websiteId: {
22
+ type: Schema.Types.ObjectId,
23
+ ref: "Website",
24
+ default: null,
25
+ },
21
26
  isBlackListed: { type: Boolean, default: false },
22
27
  createdBy: {
23
28
  type: Schema.Types.ObjectId,