shuttlepro-shared 1.1.73 → 1.1.75

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 CHANGED
@@ -3,16 +3,19 @@ require("dotenv").config();
3
3
 
4
4
  const REDIS_URL = process.env.REDIS_URL || "redis://localhost:6379";
5
5
 
6
- // ✅ Create a single Redis client instance
6
+ // ✅ Create main Redis client
7
7
  const client = createClient({
8
8
  url: REDIS_URL,
9
- socket: { reconnectStrategy: (retries) => Math.min(retries * 50, 1000) }, // Exponential backoff for reconnections
9
+ socket: { reconnectStrategy: (retries) => Math.min(retries * 50, 1000) }, // Exponential backoff
10
10
  });
11
11
 
12
+ // ✅ Create separate Pub/Sub clients
13
+ const publisher = client.duplicate();
14
+ const subscriber = client.duplicate();
15
+
12
16
  // ✅ Handle Redis Connection Events
13
17
  client.on("error", (err) => console.error("❌ Redis Error:", err));
14
18
  client.on("connect", () => console.log("✅ Redis Connected"));
15
- client.on("reconnecting", () => console.warn("⚠️ Redis Reconnecting..."));
16
19
  client.on("ready", () => console.log("🚀 Redis Ready to use"));
17
20
  client.on("end", () => console.log("❗ Redis Connection Closed"));
18
21
 
@@ -21,12 +24,46 @@ const connectRedis = async () => {
21
24
  if (!client.isOpen) {
22
25
  try {
23
26
  await client.connect();
27
+ await publisher.connect();
28
+ await subscriber.connect();
24
29
  } catch (err) {
25
30
  console.error("❌ Failed to connect to Redis:", err);
26
31
  }
27
32
  }
28
33
  };
29
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
+
30
67
  /**
31
68
  * ✅ Set Data in Redis (Supports String & Hash)
32
69
  * @param {string} key - The Redis key
@@ -91,29 +128,33 @@ const deleteRedisData = async (key, field = null) => {
91
128
  /**
92
129
  * ✅ Graceful Shutdown Handling
93
130
  */
94
- const closeClient = async () => {
95
- if (client.isOpen) {
96
- await client.quit();
97
- console.log("❗ Redis Disconnected Gracefully");
98
- }
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");
99
136
  };
100
137
 
101
138
  // ✅ Ensure Redis disconnects properly on process exit
102
- process.on("exit", closeClient);
139
+ process.on("exit", closeClients);
103
140
  process.on("SIGINT", async () => {
104
- await closeClient();
141
+ await closeClients();
105
142
  process.exit();
106
143
  });
107
144
  process.on("SIGTERM", async () => {
108
- await closeClient();
145
+ await closeClients();
109
146
  process.exit();
110
147
  });
111
148
 
112
149
  // ✅ Export Functions
113
150
  module.exports = {
114
- client, // Export Redis client for reuse
151
+ client,
152
+ publisher,
153
+ subscriber,
115
154
  setRedisData,
116
155
  getRedisData,
117
156
  deleteRedisData,
157
+ publishToChannel,
158
+ subscribeToChannel,
118
159
  connectRedis,
119
160
  };
package/config/socket.js CHANGED
@@ -1,50 +1,88 @@
1
1
  const { Server } = require("socket.io");
2
- const { client: redisClient } = require("./redis"); // Import shared Redis client
2
+ const { publisher, subscriber, connectRedis } = require("./redis");
3
3
  require("dotenv").config();
4
4
 
5
5
  let io;
6
6
  const namespaceSockets = {}; // Store different namespace sockets
7
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
+ */
8
15
  const initializeSocket = async (
9
16
  server,
10
17
  namespaceParam = "conversation",
11
18
  redisChannel = "socket_events"
12
19
  ) => {
20
+ // Initialize Socket.IO server if not already done
13
21
  if (!io) {
14
22
  io = new Server(server, {
15
- cors: { origin: "*" },
23
+ cors: {
24
+ origin: "*",
25
+ methods: ["GET", "POST"],
26
+ },
16
27
  });
28
+ console.log("✅ Socket.IO server initialized");
17
29
  }
18
30
 
19
31
  // Return existing namespace if already initialized
20
32
  if (namespaceSockets[namespaceParam]) {
33
+ console.log(`📢 Using existing namespace: /${namespaceParam}`);
21
34
  return namespaceSockets[namespaceParam];
22
35
  }
23
36
 
24
- // Create and store namespace
37
+ // Create and store new namespace
25
38
  const namespace = io.of(`/${namespaceParam}`);
26
39
  namespaceSockets[namespaceParam] = namespace;
40
+ console.log(`🚀 Created new namespace: /${namespaceParam}`);
27
41
 
28
- // Ensure Redis client is connected
29
- if (!redisClient.isOpen) {
30
- await redisClient.connect();
31
- }
42
+ // Ensure Redis is connected
43
+ await connectRedis();
44
+
45
+ // Create a unique Redis channel for this namespace
46
+ const nsRedisChannel = `${redisChannel}_${namespaceParam}`;
32
47
 
33
48
  // Subscribe to Redis events for this namespace
34
- await redisClient.subscribe(redisChannel, (message) => {
49
+ await subscriber.subscribe(nsRedisChannel, (messageStr) => {
35
50
  try {
36
- const { workspaceId, event, data } = JSON.parse(message);
37
- namespace.to(workspaceId).emit(event, data);
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
+ }
38
72
  } catch (err) {
39
- console.error("Error parsing Redis message:", err);
73
+ console.error(
74
+ `❌ Error handling Redis message on ${nsRedisChannel}:`,
75
+ err
76
+ );
40
77
  }
41
78
  });
42
79
 
43
- // Handle new socket connections
80
+ // Handle new socket connections to this namespace
44
81
  namespace.on("connection", (socket) => {
45
82
  const { workspaceId } = socket.handshake.query;
46
83
 
47
84
  if (!workspaceId) {
85
+ console.warn(`⚠️ Connection rejected: No workspaceId provided`);
48
86
  socket.disconnect(true);
49
87
  return;
50
88
  }
@@ -53,29 +91,81 @@ const initializeSocket = async (
53
91
  `✅ Client connected: ${socket.id} (Workspace: ${workspaceId}) in /${namespaceParam}`
54
92
  );
55
93
 
56
- socket.join(workspaceId); // Join workspace room
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
+ });
57
110
 
58
- socket.on("disconnect", () => {
111
+ // Handle disconnect
112
+ socket.on("disconnect", (reason) => {
113
+ console.log(
114
+ `❌ Client disconnected: ${socket.id} (Workspace: ${workspaceId}, Reason: ${reason})`
115
+ );
59
116
  socket.leave(workspaceId);
60
117
  });
118
+
119
+ // Handle errors
120
+ socket.on("error", (error) => {
121
+ console.error(`❌ Socket error for ${socket.id}:`, error);
122
+ });
61
123
  });
62
124
 
125
+ console.log(
126
+ `✅ Namespace /${namespaceParam} initialized and listening on Redis channel: ${nsRedisChannel}`
127
+ );
63
128
  return namespace;
64
129
  };
65
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
+ */
66
141
  const sendEventToServer = async ({
67
142
  workspaceId,
68
143
  event,
69
144
  data,
145
+ namespace = "conversation",
70
146
  redisChannel = "socket_events",
71
147
  }) => {
72
148
  try {
73
- await redisClient.publish(
74
- redisChannel,
75
- JSON.stringify({ workspaceId, event, data })
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})`
76
163
  );
164
+
165
+ return true;
77
166
  } catch (err) {
78
- console.error("Error publishing to Redis:", err);
167
+ console.error("Error publishing to Redis:", err);
168
+ throw err;
79
169
  }
80
170
  };
81
171
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shuttlepro-shared",
3
- "version": "1.1.73",
3
+ "version": "1.1.75",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {