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.
- package/common/repositories/customerProfile.repository.js +32 -0
- package/common/repositories/index.js +2 -3
- package/common/repositories/interactive.repository.js +124 -0
- package/config/socket.js +30 -249
- package/models/Attribute.js +5 -0
- package/models/Automation.js +10 -0
- package/models/Card.js +5 -0
- package/models/Category.js +5 -0
- package/models/Chatbot.js +1 -0
- package/models/Checkpoint.js +5 -0
- package/models/Customer.js +5 -0
- package/models/Interactive.js +70 -0
- package/models/Location.js +5 -0
- package/models/Order.js +6 -0
- package/models/OrderPdf.js +5 -0
- package/models/OrderProduct.js +5 -0
- package/models/Product.js +5 -0
- package/models/ProductAttachment.js +5 -0
- package/models/ProductAttribute.js +5 -0
- package/models/ProductCategory.js +5 -0
- package/models/ProductTag.js +5 -0
- package/models/ProductVariant.js +5 -0
- package/models/Tag.js +5 -0
- package/models/VariantLocation.js +5 -0
- package/models.js +2 -2
- package/package.json +1 -1
- package/common/repositories/call.repository.js +0 -480
- package/models/Call.js +0 -188
|
@@ -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
|
|
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
|
-
|
|
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, {
|
|
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
|
|
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
|
-
//
|
|
80
|
-
if (
|
|
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
|
-
//
|
|
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
|
|
82
|
+
const { workspaceId } = socket.handshake.query;
|
|
117
83
|
|
|
118
|
-
|
|
119
|
-
|
|
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
|
|
134
|
-
|
|
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
|
-
//
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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}
|
|
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
|
-
* @
|
|
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 };
|
package/models/Attribute.js
CHANGED
|
@@ -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 },
|
package/models/Automation.js
CHANGED
|
@@ -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
|
);
|
package/models/Category.js
CHANGED
|
@@ -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
package/models/Checkpoint.js
CHANGED
|
@@ -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
|
);
|
package/models/Customer.js
CHANGED
|
@@ -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,
|