shuttlepro-shared 1.1.81 → 1.1.82

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/config/redis.js DELETED
@@ -1,160 +0,0 @@
1
- const { createClient } = require("redis");
2
- require("dotenv").config();
3
-
4
- const REDIS_URL = process.env.REDIS_URL || "redis://localhost:6379";
5
-
6
- // ✅ Create main Redis client
7
- const client = createClient({
8
- url: REDIS_URL,
9
- socket: { reconnectStrategy: (retries) => Math.min(retries * 50, 1000) }, // Exponential backoff
10
- });
11
-
12
- // ✅ Create separate Pub/Sub clients
13
- const publisher = client.duplicate();
14
- const subscriber = client.duplicate();
15
-
16
- // ✅ Handle Redis Connection Events
17
- client.on("error", (err) => console.error("❌ Redis Error:", err));
18
- client.on("connect", () => console.log("✅ Redis Connected"));
19
- client.on("ready", () => console.log("🚀 Redis Ready to use"));
20
- client.on("end", () => console.log("❗ Redis Connection Closed"));
21
-
22
- // ✅ Ensure Redis is connected before performing operations
23
- const connectRedis = async () => {
24
- if (!client.isOpen) {
25
- try {
26
- await client.connect();
27
- await publisher.connect();
28
- await subscriber.connect();
29
- } catch (err) {
30
- console.error("❌ Failed to connect to Redis:", err);
31
- }
32
- }
33
- };
34
-
35
- /**
36
- * ✅ Publish Data to a Channel
37
- * @param {string} channel - The Redis Pub/Sub channel
38
- * @param {any} message - The message to send
39
- */
40
- const publishToChannel = async (channel, message) => {
41
- try {
42
- await connectRedis();
43
- await publisher.publish(channel, JSON.stringify(message));
44
- console.log(`📢 Message published to channel: ${channel}`);
45
- } catch (err) {
46
- console.error("❌ Error publishing message:", err);
47
- }
48
- };
49
-
50
- /**
51
- * ✅ Subscribe & Listen for Messages
52
- * @param {string} channel - The Redis Pub/Sub channel
53
- * @param {function} callback - Callback function to handle messages
54
- */
55
- const subscribeToChannel = async (channel, callback) => {
56
- try {
57
- await connectRedis();
58
- await subscriber.subscribe(channel, (message) => {
59
- console.log(`📥 Message received on channel: ${channel}`);
60
- callback(JSON.parse(message));
61
- });
62
- } catch (err) {
63
- console.error("❌ Error subscribing to channel:", err);
64
- }
65
- };
66
-
67
- /**
68
- * ✅ Set Data in Redis (Supports String & Hash)
69
- * @param {string} key - The Redis key
70
- * @param {any} value - The value to store
71
- * @param {string|null} field - Optional field for Hash storage
72
- * @param {number} expiryInSeconds - Expiry time in seconds (default: 3600)
73
- */
74
- const setRedisData = async (
75
- key,
76
- value,
77
- field = null,
78
- expiryInSeconds = 3600
79
- ) => {
80
- try {
81
- await connectRedis();
82
- let result;
83
- if (field) {
84
- result = await client.hSet(key, field, JSON.stringify(value));
85
- } else {
86
- result = await client.set(key, JSON.stringify(value), {
87
- EX: expiryInSeconds,
88
- });
89
- }
90
- return result;
91
- } catch (err) {
92
- console.error("❌ Error setting Redis data:", err);
93
- return null;
94
- }
95
- };
96
-
97
- /**
98
- * ✅ Get Data from Redis (Supports String & Hash)
99
- * @param {string} key - The Redis key
100
- * @param {string|null} field - Optional field for Hash retrieval
101
- */
102
- const getRedisData = async (key, field = null) => {
103
- try {
104
- await connectRedis();
105
- let result = field ? await client.hGet(key, field) : await client.get(key);
106
- return result ? JSON.parse(result) : null;
107
- } catch (err) {
108
- console.error("❌ Error getting Redis data:", err);
109
- return null;
110
- }
111
- };
112
-
113
- /**
114
- * ✅ Delete Data from Redis (Supports String & Hash)
115
- * @param {string} key - The Redis key
116
- * @param {string|null} field - Optional field for Hash deletion
117
- */
118
- const deleteRedisData = async (key, field = null) => {
119
- try {
120
- await connectRedis();
121
- return field ? await client.hDel(key, field) : await client.del(key);
122
- } catch (err) {
123
- console.error("❌ Error deleting Redis data:", err);
124
- return null;
125
- }
126
- };
127
-
128
- /**
129
- * ✅ Graceful Shutdown Handling
130
- */
131
- const closeClients = async () => {
132
- if (client.isOpen) await client.quit();
133
- if (publisher.isOpen) await publisher.quit();
134
- if (subscriber.isOpen) await subscriber.quit();
135
- console.log("❗ Redis Clients Disconnected Gracefully");
136
- };
137
-
138
- // ✅ Ensure Redis disconnects properly on process exit
139
- process.on("exit", closeClients);
140
- process.on("SIGINT", async () => {
141
- await closeClients();
142
- process.exit();
143
- });
144
- process.on("SIGTERM", async () => {
145
- await closeClients();
146
- process.exit();
147
- });
148
-
149
- // ✅ Export Functions
150
- module.exports = {
151
- client,
152
- publisher,
153
- subscriber,
154
- setRedisData,
155
- getRedisData,
156
- deleteRedisData,
157
- publishToChannel,
158
- subscribeToChannel,
159
- connectRedis,
160
- };
package/config/socket.js DELETED
@@ -1,172 +0,0 @@
1
- const { Server } = require("socket.io");
2
- const { publisher, subscriber, connectRedis } = require("./redis");
3
- require("dotenv").config();
4
-
5
- let io;
6
- const namespaceSockets = {}; // Store different namespace sockets
7
-
8
- /**
9
- * Initialize Socket.IO server with namespaces
10
- * @param {http.Server} server - HTTP server instance
11
- * @param {string} namespaceParam - Socket namespace name (default: "conversation")
12
- * @param {string} redisChannel - Redis channel for this namespace (default: "socket_events")
13
- * @returns {SocketIO.Namespace} - The initialized namespace
14
- */
15
- const initializeSocket = async (
16
- server,
17
- namespaceParam = "conversation",
18
- redisChannel = "socket_events"
19
- ) => {
20
- // Initialize Socket.IO server if not already done
21
- if (!io) {
22
- io = new Server(server, {
23
- cors: {
24
- origin: "*",
25
- methods: ["GET", "POST"],
26
- },
27
- });
28
- console.log("✅ Socket.IO server initialized");
29
- }
30
-
31
- // Return existing namespace if already initialized
32
- if (namespaceSockets[namespaceParam]) {
33
- console.log(`📢 Using existing namespace: /${namespaceParam}`);
34
- return namespaceSockets[namespaceParam];
35
- }
36
-
37
- // Create and store new namespace
38
- const namespace = io.of(`/${namespaceParam}`);
39
- namespaceSockets[namespaceParam] = namespace;
40
- console.log(`🚀 Created new namespace: /${namespaceParam}`);
41
-
42
- // Ensure Redis is connected
43
- await connectRedis();
44
-
45
- // Create a unique Redis channel for this namespace
46
- const nsRedisChannel = `${redisChannel}_${namespaceParam}`;
47
-
48
- // Subscribe to Redis events for this namespace
49
- await subscriber.subscribe(nsRedisChannel, (messageStr) => {
50
- try {
51
- const message = JSON.parse(messageStr);
52
- const { workspaceId, event, data } = message;
53
-
54
- console.log(`📥 Redis message received on ${nsRedisChannel}:`, {
55
- workspaceId,
56
- event,
57
- });
58
-
59
- // Emit to specific workspace room in this namespace
60
- if (workspaceId) {
61
- namespace.to(workspaceId).emit(event, data);
62
- console.log(
63
- `📢 Event ${event} emitted to workspace ${workspaceId} in /${namespaceParam}`
64
- );
65
- } else {
66
- // Broadcast to all clients in this namespace if no workspaceId specified
67
- namespace.emit(event, data);
68
- console.log(
69
- `📢 Event ${event} broadcasted to all clients in /${namespaceParam}`
70
- );
71
- }
72
- } catch (err) {
73
- console.error(
74
- `❌ Error handling Redis message on ${nsRedisChannel}:`,
75
- err
76
- );
77
- }
78
- });
79
-
80
- // Handle new socket connections to this namespace
81
- namespace.on("connection", (socket) => {
82
- const { workspaceId } = socket.handshake.query;
83
-
84
- if (!workspaceId) {
85
- console.warn(`⚠️ Connection rejected: No workspaceId provided`);
86
- socket.disconnect(true);
87
- return;
88
- }
89
-
90
- console.log(
91
- `✅ Client connected: ${socket.id} (Workspace: ${workspaceId}) in /${namespaceParam}`
92
- );
93
-
94
- // Join workspace room
95
- socket.join(workspaceId);
96
-
97
- // Send connection confirmation to client
98
- socket.emit("connected", {
99
- status: "connected",
100
- socketId: socket.id,
101
- namespace: namespaceParam,
102
- workspaceId,
103
- });
104
-
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
- });
110
-
111
- // Handle disconnect
112
- socket.on("disconnect", (reason) => {
113
- console.log(
114
- `❌ Client disconnected: ${socket.id} (Workspace: ${workspaceId}, Reason: ${reason})`
115
- );
116
- socket.leave(workspaceId);
117
- });
118
-
119
- // Handle errors
120
- socket.on("error", (error) => {
121
- console.error(`❌ Socket error for ${socket.id}:`, error);
122
- });
123
- });
124
-
125
- console.log(
126
- `✅ Namespace /${namespaceParam} initialized and listening on Redis channel: ${nsRedisChannel}`
127
- );
128
- return namespace;
129
- };
130
-
131
- /**
132
- * Send event to clients via Redis pub/sub
133
- * @param {object} params - Event parameters
134
- * @param {string} params.workspaceId - Target workspace ID
135
- * @param {string} params.event - Event name
136
- * @param {any} params.data - Event data payload
137
- * @param {string} [params.namespace="conversation"] - Target namespace
138
- * @param {string} [params.redisChannel="socket_events"] - Base Redis channel
139
- * @returns {Promise<void>}
140
- */
141
- const sendEventToServer = async ({
142
- workspaceId,
143
- event,
144
- data,
145
- namespace = "conversation",
146
- redisChannel = "socket_events",
147
- }) => {
148
- try {
149
- // Create namespace-specific Redis channel
150
- const nsRedisChannel = `${redisChannel}_${namespace}`;
151
-
152
- // Ensure Redis is connected
153
- await connectRedis();
154
-
155
- // Create message payload
156
- const message = JSON.stringify({ workspaceId, event, data });
157
-
158
- // Publish to Redis
159
- await publisher.publish(nsRedisChannel, message);
160
-
161
- console.log(
162
- `📢 Event published to Redis channel ${nsRedisChannel}: ${event} (Workspace: ${workspaceId})`
163
- );
164
-
165
- return true;
166
- } catch (err) {
167
- console.error("❌ Error publishing to Redis:", err);
168
- throw err;
169
- }
170
- };
171
-
172
- module.exports = { initializeSocket, sendEventToServer };
@@ -1,189 +0,0 @@
1
- const mongoose = require("mongoose");
2
- const { Schema } = mongoose;
3
-
4
- const breakSchema = new mongoose.Schema({
5
- refId: { type: String, required: true },
6
- breakId: { type: String, required: false },
7
- startTime: { type: Date, required: true },
8
- endTime: { type: Date, required: false },
9
- });
10
-
11
- const responseSchema = new mongoose.Schema({
12
- queryMessageId: { type: [String], default: [] },
13
- responseMessageId: { type: [String], default: [] },
14
- responseTime: { type: Number, required: false },
15
- accuracy: { type: Number, required: false, default: 10 },
16
- });
17
-
18
- const activitySchema = new mongoose.Schema({
19
- refId: { type: String, required: true },
20
- type: {
21
- type: String,
22
- required: true,
23
- default: "other",
24
- enum: ["conversation", "ticket", "order", "other"],
25
- },
26
- platformName: { type: String, required: false },
27
- platformType: { type: String, required: false },
28
- customerName: { type: String, default: "", required: false },
29
- conversationId: { type: String, default: "", required: false },
30
- profileId: { type: String, required: false },
31
- conversationType: {
32
- type: String,
33
- enum: ["email", "comment", "message", ""],
34
- default: "",
35
- required: false,
36
- },
37
- status: { type: String, required: false, default: "unassigned" },
38
- labels: [{ type: Schema.Types.ObjectId, ref: "Label" }],
39
- platformId: { type: String, required: false },
40
- assignTime: { type: Date, required: false },
41
- qaScore: { type: Number, required: false, default: 10 },
42
- startTime: { type: String, required: false },
43
- endTime: { type: String, required: false },
44
- resolutionTime: { type: String, required: false },
45
- responses: { type: [responseSchema], default: [] },
46
- remarks: { type: String, required: false },
47
- avgAccuracy: { type: Number, required: false, default: 10 },
48
- });
49
-
50
- const totalActivitySchema = new mongoose.Schema({
51
- type: { type: String, required: true },
52
- platformType: { type: String, required: false },
53
- count: { type: Number, required: true },
54
- });
55
-
56
- const agentActivitySchema = new mongoose.Schema(
57
- {
58
- _id: {
59
- type: mongoose.Schema.Types.ObjectId,
60
- default: mongoose.Types.ObjectId,
61
- },
62
- agentId: { type: String, required: true },
63
- agentRole: { type: String, default: "" },
64
- agentName: { type: String, required: false },
65
- agentShift: { type: Object, default: {}, required: false },
66
- workspaceId: {
67
- type: mongoose.Schema.Types.ObjectId,
68
- required: true,
69
- ref: "Workspace",
70
- },
71
- date: { type: String, required: true },
72
- lateMinutes: {
73
- type: Number,
74
- default: 0,
75
- },
76
- earlyLeaveMinutes: {
77
- type: Number,
78
- default: 0,
79
- },
80
- shiftStartTime: { type: [Date], default: [] },
81
- shiftEndTime: { type: [Date], default: [] },
82
- shifts: [
83
- {
84
- shiftStartTime: { type: Date, required: true, default: null },
85
- shiftEndTime: { type: Date, required: false, default: null },
86
- },
87
- ],
88
- isLate: { type: Boolean, required: true, default: false },
89
- isEarlyLeave: { type: Boolean, required: true, default: false },
90
- breaks: { type: [breakSchema], default: [] },
91
- activities: { type: [activitySchema], default: [] },
92
- totalPsc: { type: Number, required: false, default: 0 },
93
- totalQa: { type: Number, required: false, default: 0 },
94
- weightedQa: { type: Number, required: false, default: 0 },
95
- weightedPsc: { type: Number, required: false, default: 0 },
96
- kpiScore: { type: Number, required: false, default: 0 },
97
- totalActivities: { type: [totalActivitySchema], default: [] },
98
- totalWorkTime: { type: Number, required: false, default: 0 },
99
- totalBreakTime: { type: Number, required: false, default: 0 },
100
- totalIdleTime: { type: Number, required: false, default: 0 },
101
- },
102
- { timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true } }
103
- );
104
-
105
- agentActivitySchema.index({ workspaceId: 1 });
106
- agentActivitySchema.index({ date: 1 });
107
- agentActivitySchema.index({ agentId: 1 });
108
- agentActivitySchema.index({ "activities.platformId": 1 });
109
- agentActivitySchema.index({ "activities.labels": 1 });
110
- agentActivitySchema.index({ workspaceId: 1, date: 1, agentId: 1 });
111
- agentActivitySchema.index({
112
- workspaceId: 1,
113
- date: 1,
114
- agentId: 1,
115
- "activities.platformId": 1,
116
- "activities.labels": 1,
117
- });
118
- agentActivitySchema.index({
119
- workspaceId: 1,
120
- date: 1,
121
- agentId: 1,
122
- "activities.platformId": 1,
123
- });
124
- agentActivitySchema.index({
125
- workspaceId: 1,
126
- date: 1,
127
- agentId: 1,
128
- "activities.labels": 1,
129
- });
130
- agentActivitySchema.index(
131
- { "activities.platformId": 1, "activities.labels": 1 },
132
- {
133
- partialFilterExpression: {
134
- "activities.platformId": { $exists: true },
135
- "activities.labels": { $exists: true },
136
- },
137
- }
138
- );
139
- const AgentActivity = mongoose.model("AgentActivity", agentActivitySchema);
140
-
141
- const findOneAndUpdateAgentActivity = async (
142
- findQuery = {},
143
- updateQuery = null
144
- ) => {
145
- try {
146
- if (!findQuery || Object.values(findQuery).length === 0) {
147
- console.log("Find Query is required.");
148
- return null;
149
- }
150
-
151
- const agentActivityForActivitiesUpdated = await AgentActivity.findOne(
152
- findQuery
153
- ).sort({ createdAt: -1 });
154
-
155
- if (!agentActivityForActivitiesUpdated) {
156
- console.log("No matching record found.");
157
- return null;
158
- }
159
-
160
- const bsonSize = require("bson").calculateObjectSize(
161
- agentActivityForActivitiesUpdated
162
- );
163
-
164
- if (bsonSize <= 1000000) {
165
- return await AgentActivity.findOneAndUpdate(findQuery, updateQuery);
166
- } else {
167
- const { breaks, activities, totalActivities, ...restData } =
168
- agentActivityForActivitiesUpdated.toObject();
169
-
170
- const newAgentActivity = await AgentActivity.create({
171
- ...restData,
172
- });
173
-
174
- const agentActivityRecord = await AgentActivity.findOneAndUpdate(
175
- { ...findQuery, _id: newAgentActivity._id },
176
- updateQuery
177
- );
178
-
179
- return agentActivityRecord;
180
- }
181
- } catch (err) {
182
- console.error("Error:", err);
183
- return null;
184
- }
185
- };
186
- module.exports = {
187
- AgentActivity,
188
- findOneAndUpdateAgentActivity,
189
- };
@@ -1,23 +0,0 @@
1
- const mongoose = require("mongoose");
2
-
3
- const assignmentSchema = new mongoose.Schema({
4
- cardId: {
5
- type: mongoose.Schema.Types.ObjectId,
6
- ref: "Card",
7
- required: true,
8
- },
9
- id: {
10
- type: String,
11
- required: true,
12
- },
13
- name: {
14
- type: String,
15
- },
16
- email: {
17
- type: String,
18
- },
19
- });
20
-
21
- const Assignment = mongoose.model("Assignment", assignmentSchema);
22
-
23
- module.exports = Assignment;
@@ -1,23 +0,0 @@
1
- const mongoose = require("mongoose");
2
- const { Schema } = mongoose;
3
-
4
- const businessDistributionSchema = new mongoose.Schema(
5
- {
6
- name: { type: String, default: "" },
7
- deliveryCharges: { type: String, default: "" },
8
- shippers: [],
9
- type: {
10
- type: String,
11
- enum: ["default", "cities"],
12
- required: true,
13
- },
14
- index: { type: String, default: "" },
15
- workspaceId: { type: String, default: "" },
16
- },
17
- { timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true } }
18
- );
19
-
20
- module.exports = mongoose.model(
21
- "BusinessDistribution",
22
- businessDistributionSchema
23
- );
package/models/Card.js DELETED
@@ -1,144 +0,0 @@
1
- const mongoose = require("mongoose");
2
- const { DYNAMIC_FIELD_TYPES } = require("../constants");
3
-
4
- const cardSchema = new mongoose.Schema(
5
- {
6
- cardId: { type: String, required: true },
7
- columnId: {
8
- type: mongoose.Schema.Types.ObjectId,
9
- ref: "Column",
10
- required: false,
11
- default: null,
12
- set: (v) => (v === "" ? null : v),
13
- },
14
- title: {
15
- type: String,
16
- default: "",
17
- // required: true,
18
- },
19
- description: {
20
- type: String,
21
- },
22
- priority: {
23
- type: String,
24
- enum: ["high", "medium", "low"],
25
- },
26
- typeId: [
27
- {
28
- type: mongoose.Schema.Types.ObjectId,
29
- ref: "Label",
30
- },
31
- ],
32
- type: {
33
- type: String,
34
- required: true,
35
- },
36
- assignedBy: {
37
- type: Object,
38
- },
39
- attachmentId: {
40
- type: mongoose.Schema.Types.ObjectId,
41
- ref: "Attachment",
42
- },
43
- attachments: {
44
- type: [String],
45
- default: [],
46
- },
47
- product: {
48
- type: Object,
49
- },
50
- courierId: {
51
- type: String,
52
- },
53
- orderId: {
54
- type: Object,
55
- },
56
- conversation: {
57
- type: Object,
58
- },
59
- deleted: {
60
- type: Boolean,
61
- default: false,
62
- },
63
- isExpired: {
64
- type: Boolean,
65
- default: false,
66
- },
67
- isUpdated: {
68
- type: Boolean,
69
- default: false,
70
- },
71
- completionDate: {
72
- type: String,
73
- },
74
- updatedDate: {
75
- type: Date,
76
- default: Date.now,
77
- },
78
- ticketId: {
79
- type: Number,
80
- },
81
- category: {
82
- type: String,
83
- default: "ticket",
84
- enum: ["ticket", "conversation"],
85
- },
86
- dynamicFieldValue: [
87
- {
88
- dynamicFieldId: {
89
- type: String,
90
- default: "",
91
- },
92
- fieldType: {
93
- type: String,
94
- enum: DYNAMIC_FIELD_TYPES,
95
- },
96
- fieldValueId: {
97
- type: mongoose.Schema.Types.Mixed,
98
- },
99
- },
100
- ],
101
- workspaceId: { type: String, default: "" },
102
- activities: [
103
- {
104
- data: {
105
- type: mongoose.Schema.Types.Mixed,
106
- default: {},
107
- },
108
- },
109
- ],
110
- },
111
- { timestamps: true, toJSON: { virtuals: true }, toObject: { virtuals: true } }
112
- );
113
-
114
- cardSchema.index({ workspaceId: 1, createdAt: 1 }); // Common filter
115
-
116
- // Virtual for the assignedTo user
117
- cardSchema.virtual("assignedToUser", {
118
- ref: "User",
119
- localField: "assignedTo",
120
- foreignField: "_id",
121
- });
122
-
123
- // Virtual for the attachment
124
- cardSchema.virtual("attachment", {
125
- ref: "Attachment",
126
- localField: "attachmentId",
127
- foreignField: "_id",
128
- });
129
-
130
- // Virtual for the comment
131
- cardSchema.virtual("comments", {
132
- ref: "CardComment",
133
- localField: "_id",
134
- foreignField: "cardId",
135
- });
136
-
137
- // Virtual for the assignmentList
138
- cardSchema.virtual("assignedTo", {
139
- ref: "Assignment",
140
- localField: "_id", // Change to 'cardId' to match the 'cardId' field in the Assignment model
141
- foreignField: "cardId",
142
- });
143
-
144
- module.exports = mongoose.model("Card", cardSchema);