shuttlepro-shared 1.1.92 → 1.1.93
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/constants/index.js +0 -15
- package/index.js +0 -8
- package/models/Role.js +40 -0
- package/models/UserPermission.js +5 -0
- package/models/UserRole.js +10 -3
- package/models.js +3 -50
- package/package.json +2 -13
- package/common/repositories/descriptionTemplates.repository.js +0 -186
- package/common/repositories/index.js +0 -12
- package/common/repositories/integration.repository.js +0 -197
- package/common/repositories/label.repository.js +0 -85
- package/common/repositories/shipper.repository.js +0 -77
- package/common/repositories/workspace.repository.js +0 -95
- package/config/bull.js +0 -78
- package/config/config.js +0 -14
- package/config/database.js +0 -7
- package/config/index.js +0 -13
- package/config/redis.js +0 -160
- package/config/socket.js +0 -172
- package/models/AgentActivity.js +0 -189
- package/models/Assignment.js +0 -23
- package/models/BusinessDistribution.js +0 -23
- package/models/Card.js +0 -144
- package/models/CardComments.js +0 -33
- package/models/Chatbot.js +0 -16
- package/models/Checkpoint.js +0 -50
- package/models/City.js +0 -17
- package/models/Column.js +0 -28
- package/models/Conversation.js +0 -84
- package/models/Customer.js +0 -35
- package/models/DescriptionTemplate.js +0 -22
- package/models/Integration.js +0 -53
- package/models/Label.js +0 -42
- package/models/Message.js +0 -47
- package/models/Order.js +0 -131
- package/models/OrderProduct.js +0 -37
- package/models/Profile.js +0 -127
- package/models/Report.js +0 -27
- package/models/Shipper.js +0 -52
- package/models/Status.js +0 -58
- package/models/StatusType.js +0 -10
- package/models/Step.js +0 -50
- package/models/Type.js +0 -25
- package/models/Workspace.js +0 -190
- package/utils/decorator-factory.js +0 -264
- package/utils/logger.js +0 -41
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
const { getRedisData, setRedisData } = require("../../config/redis");
|
|
2
|
-
const Shipper = require("../../models/Shipper");
|
|
3
|
-
|
|
4
|
-
const CACHE_KEY_ALL = "shippers_all";
|
|
5
|
-
|
|
6
|
-
const getCachedShippers = async () => {
|
|
7
|
-
let shippers = await getRedisData(CACHE_KEY_ALL);
|
|
8
|
-
if (!shippers) {
|
|
9
|
-
shippers = await Shipper.find().lean().exec();
|
|
10
|
-
await setRedisData(CACHE_KEY_ALL, JSON.stringify(shippers));
|
|
11
|
-
} else {
|
|
12
|
-
shippers = JSON.parse(shippers);
|
|
13
|
-
}
|
|
14
|
-
return shippers;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const updateCachedShippers = async () => {
|
|
18
|
-
const shippers = await Shipper.find().lean().exec();
|
|
19
|
-
await setRedisData(CACHE_KEY_ALL, JSON.stringify(shippers));
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const createShipper = async (data) => {
|
|
23
|
-
const newShipper = new Shipper(data);
|
|
24
|
-
const savedShipper = await newShipper.save();
|
|
25
|
-
await updateCachedShippers();
|
|
26
|
-
return savedShipper;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const findShipperById = async (id) => {
|
|
30
|
-
const shippers = await getCachedShippers();
|
|
31
|
-
const shipper = shippers.find((s) => s._id.toString() === id) || null;
|
|
32
|
-
|
|
33
|
-
return shipper;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const findShipper = async (filter) => {
|
|
37
|
-
const shippers = await getCachedShippers();
|
|
38
|
-
return (
|
|
39
|
-
shippers.find((s) =>
|
|
40
|
-
Object.entries(filter).every(([key, value]) => s[key] === value)
|
|
41
|
-
) || null
|
|
42
|
-
);
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const findAllShippers = async (filter = {}) => {
|
|
46
|
-
const shippers = await getCachedShippers();
|
|
47
|
-
return shippers.filter((s) =>
|
|
48
|
-
Object.entries(filter).every(([key, value]) => s[key] === value)
|
|
49
|
-
);
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
const updateShipper = async (id, data) => {
|
|
53
|
-
const updatedShipper = await Shipper.findByIdAndUpdate(id, data, {
|
|
54
|
-
new: true,
|
|
55
|
-
}).exec();
|
|
56
|
-
if (updatedShipper) {
|
|
57
|
-
await updateCachedShippers();
|
|
58
|
-
}
|
|
59
|
-
return updatedShipper;
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const deleteShipper = async (id) => {
|
|
63
|
-
const deletedShipper = await Shipper.findByIdAndDelete(id).exec();
|
|
64
|
-
if (deletedShipper) {
|
|
65
|
-
await updateCachedShippers();
|
|
66
|
-
}
|
|
67
|
-
return deletedShipper;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
module.exports = {
|
|
71
|
-
createShipper,
|
|
72
|
-
findShipperById,
|
|
73
|
-
findShipper,
|
|
74
|
-
findAllShippers,
|
|
75
|
-
updateShipper,
|
|
76
|
-
deleteShipper,
|
|
77
|
-
};
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
const { getRedisData, setRedisData } = require("../../config/redis");
|
|
2
|
-
const Workspace = require("../../models/Workspace");
|
|
3
|
-
|
|
4
|
-
const CACHE_KEY_ALL = "workspaces_all";
|
|
5
|
-
|
|
6
|
-
const getCachedWorkspaces = async () => {
|
|
7
|
-
let workspaces = await getRedisData(CACHE_KEY_ALL);
|
|
8
|
-
if (!workspaces) {
|
|
9
|
-
workspaces = await Workspace.find().exec();
|
|
10
|
-
await setRedisData(CACHE_KEY_ALL, JSON.stringify(workspaces));
|
|
11
|
-
} else {
|
|
12
|
-
workspaces = JSON.parse(workspaces);
|
|
13
|
-
}
|
|
14
|
-
return workspaces;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
const updateCachedWorkspaces = async () => {
|
|
18
|
-
const workspaces = await Workspace.find().exec();
|
|
19
|
-
await setRedisData(CACHE_KEY_ALL, JSON.stringify(workspaces));
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const createWorkspace = async (data) => {
|
|
23
|
-
const newWorkspace = new Workspace(data);
|
|
24
|
-
const savedWorkspace = await newWorkspace.save();
|
|
25
|
-
await updateCachedWorkspaces();
|
|
26
|
-
return savedWorkspace;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const findWorkspaceById = async (id, select = []) => {
|
|
30
|
-
const workspaces = await getCachedWorkspaces();
|
|
31
|
-
const workspace = workspaces.find((ws) => ws._id.toString() === id) || null;
|
|
32
|
-
|
|
33
|
-
if (workspace && select.length > 0) {
|
|
34
|
-
return select.reduce((result, field) => {
|
|
35
|
-
if (workspace[field] !== undefined) {
|
|
36
|
-
result[field] = workspace[field];
|
|
37
|
-
}
|
|
38
|
-
return result;
|
|
39
|
-
}, {});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return workspace;
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const findWorkspace = async (filter) => {
|
|
46
|
-
const workspaces = await getCachedWorkspaces();
|
|
47
|
-
return (
|
|
48
|
-
workspaces.find((ws) =>
|
|
49
|
-
Object.entries(filter).every(([key, value]) => ws[key] === value)
|
|
50
|
-
) || null
|
|
51
|
-
);
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const findWorkspaceByIdAndQueueTracking = async (id) => {
|
|
55
|
-
const workspaces = await getCachedWorkspaces();
|
|
56
|
-
return (
|
|
57
|
-
workspaces.find((ws) => ws._id.toString() === id && ws.queueTracking) ||
|
|
58
|
-
null
|
|
59
|
-
);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const findAllWorkspaces = async (filter = {}) => {
|
|
63
|
-
const workspaces = await getCachedWorkspaces();
|
|
64
|
-
return workspaces.filter((ws) =>
|
|
65
|
-
Object.entries(filter).every(([key, value]) => ws[key] === value)
|
|
66
|
-
);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const updateWorkspace = async (id, data) => {
|
|
70
|
-
const updatedWorkspace = await Workspace.findByIdAndUpdate(id, data, {
|
|
71
|
-
new: true,
|
|
72
|
-
}).exec();
|
|
73
|
-
if (updatedWorkspace) {
|
|
74
|
-
await updateCachedWorkspaces();
|
|
75
|
-
}
|
|
76
|
-
return updatedWorkspace;
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
const deleteWorkspace = async (id) => {
|
|
80
|
-
const deletedWorkspace = await Workspace.findByIdAndDelete(id).exec();
|
|
81
|
-
if (deletedWorkspace) {
|
|
82
|
-
await updateCachedWorkspaces();
|
|
83
|
-
}
|
|
84
|
-
return deletedWorkspace;
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
module.exports = {
|
|
88
|
-
createWorkspace,
|
|
89
|
-
findWorkspaceById,
|
|
90
|
-
findWorkspace,
|
|
91
|
-
findWorkspaceByIdAndQueueTracking,
|
|
92
|
-
findAllWorkspaces,
|
|
93
|
-
updateWorkspace,
|
|
94
|
-
deleteWorkspace,
|
|
95
|
-
};
|
package/config/bull.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
const Queue = require("bull");
|
|
2
|
-
const config = require("./config");
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Create a new queue.
|
|
6
|
-
* @param {string} queueName - The name of the queue.
|
|
7
|
-
* @param {Object} [redisConfig=config.redis] - Redis configuration.
|
|
8
|
-
* @returns {Object} - A collection of queue-related functions.
|
|
9
|
-
*/
|
|
10
|
-
const createQueue = (queueName, redisConfig = config.redis) => {
|
|
11
|
-
const queue = new Queue(queueName, { redis: redisConfig });
|
|
12
|
-
|
|
13
|
-
// Attach event listeners for logging
|
|
14
|
-
queue.on("completed", (job) => console.log(`Job ${job.id} completed.`));
|
|
15
|
-
queue.on("failed", (job, err) => console.error(`Job ${job.id} failed:`, err));
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Add a job to the queue.
|
|
19
|
-
* @param {Object} data - Data to be processed by the job.
|
|
20
|
-
* @param {Object} [options] - Bull job options (e.g., delay, attempts).
|
|
21
|
-
* @returns {Promise<Job>} - The created job.
|
|
22
|
-
*/
|
|
23
|
-
const addJob = async (
|
|
24
|
-
data,
|
|
25
|
-
options = {
|
|
26
|
-
attempts: 3,
|
|
27
|
-
removeOnComplete: true,
|
|
28
|
-
}
|
|
29
|
-
) => {
|
|
30
|
-
return await queue.add(data, options);
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Process jobs in the queue.
|
|
35
|
-
* @param {Function} processor - The function to process each job.
|
|
36
|
-
*/
|
|
37
|
-
const processJobs = (processor) => {
|
|
38
|
-
queue.process(processor);
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Close the queue connection.
|
|
43
|
-
* @returns {Promise<void>}
|
|
44
|
-
*/
|
|
45
|
-
const closeConnection = async () => {
|
|
46
|
-
await queue.close();
|
|
47
|
-
console.log(`Queue "${queue.name}" connection closed.`);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Pause the queue.
|
|
52
|
-
* @returns {Promise<void>}
|
|
53
|
-
*/
|
|
54
|
-
const pauseQueue = async () => {
|
|
55
|
-
await queue.pause();
|
|
56
|
-
console.log(`Queue "${queue.name}" paused.`);
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Resume the queue.
|
|
61
|
-
* @returns {Promise<void>}
|
|
62
|
-
*/
|
|
63
|
-
const resumeQueue = async () => {
|
|
64
|
-
await queue.resume();
|
|
65
|
-
console.log(`Queue "${queue.name}" resumed.`);
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// Return the functional interface
|
|
69
|
-
return {
|
|
70
|
-
addJob,
|
|
71
|
-
processJobs,
|
|
72
|
-
closeConnection,
|
|
73
|
-
pauseQueue,
|
|
74
|
-
resumeQueue,
|
|
75
|
-
};
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
module.exports = { createQueue };
|
package/config/config.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
const ALLOWED_URLS = process.env.ALLOWED_ORIGIN.split(",");
|
|
2
|
-
const WEBHOOK_API_KEY = process.env.API_KEY || process.env.WEBHOOK_API_KEY;
|
|
3
|
-
const mode = process.env.MODE || "production";
|
|
4
|
-
|
|
5
|
-
const redis = {
|
|
6
|
-
url: process.env.REDIS_URI,
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
module.exports = {
|
|
10
|
-
WEBHOOK_API_KEY,
|
|
11
|
-
ALLOWED_URLS,
|
|
12
|
-
mode,
|
|
13
|
-
redis,
|
|
14
|
-
};
|
package/config/database.js
DELETED
package/config/index.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
const bullConfig = require("./bull");
|
|
2
|
-
const redisConfig = require("./redis");
|
|
3
|
-
const config = require("./config");
|
|
4
|
-
const databaseConfig = require("./database");
|
|
5
|
-
const socketConfig = require("./socket");
|
|
6
|
-
|
|
7
|
-
module.exports = {
|
|
8
|
-
bullConfig,
|
|
9
|
-
redisConfig,
|
|
10
|
-
config,
|
|
11
|
-
databaseConfig,
|
|
12
|
-
socketConfig,
|
|
13
|
-
};
|
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 };
|