diff --git a/src/shared/services/project/project-Services.ts b/src/shared/services/project/project-Services.ts index b477232..7e1be80 100644 --- a/src/shared/services/project/project-Services.ts +++ b/src/shared/services/project/project-Services.ts @@ -99,6 +99,7 @@ export const GetAllProjects = async (data: GetProjectsInterface) => { if (!existingUser) return { status: "User not found" }; const projectDatas = await projectModel(organization) .find({ + createdBy:userId, isArchive: false, }) .select("_id projectName createdBy thumbnail createdAt projectUuid"); diff --git a/src/socket-server/controllers/project/projectController.ts b/src/socket-server/controllers/project/projectController.ts index 96ae5ef..a16f0e4 100644 --- a/src/socket-server/controllers/project/projectController.ts +++ b/src/socket-server/controllers/project/projectController.ts @@ -1,11 +1,14 @@ import { Socket, Server } from "socket.io"; import { createProject, DeleteProject, updateProject } from "../../../shared/services/project/project-Services.ts"; import { EVENTS } from "../../socket/events.ts"; -import { emitEventResponse } from "../../utils/emitEventResponse.ts"; +import { emitEventResponse, emitToSenderAndAdmins, } from "../../utils/emitEventResponse.ts"; -export const projectHandleEvent = async ( event: string,socket: Socket,data: any,) => { +export const projectHandleEvent = async ( event: string,socket: Socket,io:Server,data: any, connectedUsersByOrg: { [org: string]: { socketId: string; userId: string; role: string }[] }, callback?: Function +) => { + console.log('event: ', event); + console.log('data:controller ', data); if (event !== EVENTS.addProject || !data?.organization) return; - const requiredFields = ["projectUuid", "projectName", "userId", "thumbnail", "organization"]; + const requiredFields = ["projectUuid", "userId", "thumbnail", "organization"]; const missingFields = requiredFields.filter(field => !data?.[field]); if (missingFields.length > 0) { @@ -17,7 +20,7 @@ export const projectHandleEvent = async ( event: string,socket: Socket,data: any organization: data?.organization ?? "unknown", }; - emitEventResponse(socket, data?.organization, EVENTS.projectResponse, response); + emitToSenderAndAdmins(io, socket, data.organization, EVENTS.projectResponse, response, connectedUsersByOrg) return; } const result = await createProject(data); @@ -46,17 +49,35 @@ export const projectHandleEvent = async ( event: string,socket: Socket,data: any ...(status === "Success" ? { data: projectDatas, projectId: result.project._id } : {}), }; - emitEventResponse(socket, data.organization, EVENTS.projectResponse, response); + emitToSenderAndAdmins(io, socket, data.organization, EVENTS.projectResponse, response, connectedUsersByOrg) } export const projectDeleteHandleEvent = async ( event: string, socket: Socket, + io:Server, data: any, + connectedUsersByOrg: { [org: string]: { socketId: string; userId: string; role: string }[] }, ) => { + console.log('event: ', event); if (event !== EVENTS.deleteProject || !data?.organization) return; + const requiredFields = ["projectId", "userId", "organization"]; + const missingFields = requiredFields.filter(field => !data?.[field]); + if (missingFields.length > 0) { + const response = { + success: false, + message: `Missing required field(s): ${missingFields.join(", ")}`, + status: "MissingFields", + socketId: socket.id, + organization: data?.organization ?? "unknown", + }; + + emitToSenderAndAdmins(io, socket, data.organization, EVENTS.deleteProjectResponse, response, connectedUsersByOrg) + return; + } const result = await DeleteProject(data); + console.log('result: ', result); const status = typeof result?.status === "string" ? result.status : "unknown"; const messages: Record = { @@ -86,16 +107,32 @@ export const projectDeleteHandleEvent = async ( }; - emitEventResponse(socket, data.organization, EVENTS.deleteProjectResponse, response); -} +emitToSenderAndAdmins(io, socket, data.organization, EVENTS.deleteProjectResponse, response, connectedUsersByOrg)} + export const projecUpdateHandleEvent = async ( event: string, socket: Socket, + io:Server, data: any, + connectedUsersByOrg: { [org: string]: { socketId: string; userId: string; role: string }[] }, ) => { - console.log('data: ', data); + console.log('data:update ', data); if (event !== EVENTS.ProjectUpdate || !data?.organization) return; + const requiredFields = ["projectId", "userId", "organization"]; + const missingFields = requiredFields.filter(field => !data?.[field]); + if (missingFields.length > 0) { + const response = { + success: false, + message: `Missing required field(s): ${missingFields.join(", ")}`, + status: "MissingFields", + socketId: socket.id, + organization: data?.organization ?? "unknown", + }; + + emitToSenderAndAdmins(io, socket, data.organization, EVENTS.projectUpdateResponse, response, connectedUsersByOrg) + return; + } const result = await updateProject(data); const status = typeof result?.status === "string" ? result.status : "unknown"; @@ -107,8 +144,8 @@ export const projecUpdateHandleEvent = async ( const msg = messages[status] || { message: "Internal server error" }; const projectDeleteDatas = - status === "Success" && result?.data - + status === "Success" && result?.data + const response = { success: status === "Success", @@ -120,7 +157,7 @@ export const projecUpdateHandleEvent = async ( }; - emitEventResponse(socket, data.organization, EVENTS.projectUpdateResponse, response); + emitToSenderAndAdmins(io, socket, data.organization, EVENTS.projectUpdateResponse, response, connectedUsersByOrg) console.log('response: ', response); } \ No newline at end of file diff --git a/src/socket-server/index.ts b/src/socket-server/index.ts index 88dbdbd..39d702b 100644 --- a/src/socket-server/index.ts +++ b/src/socket-server/index.ts @@ -16,7 +16,6 @@ app.get('/', (req: Request, res: Response) => { res.send('Hello, I am Major-Dwinzo RealTime!'); }); initSocketServer(server); -// SocketServer(server) server.listen(PORT, () => { console.log(`socket-Server is running on http://localhost:${PORT}`); }); diff --git a/src/socket-server/socket/socketManager.ts b/src/socket-server/socket/socketManager.ts index 069dc49..dbea0b0 100644 --- a/src/socket-server/socket/socketManager.ts +++ b/src/socket-server/socket/socketManager.ts @@ -14,6 +14,7 @@ import { addTemplate, addTemplateZone, TemplateZoneDelete } from '../services/vi import { deleteAssetModel, replaceEventDatas, setAssetModel } from '../services/assets/asset-Controller.ts'; import { add3Dwidget, delete3Dwidget, update3D } from '../services/visualization/3dWidget-Service.ts'; import { projectDeleteHandleEvent, projectHandleEvent, projecUpdateHandleEvent } from '../controllers/project/projectController.ts'; +import { getUserRole } from '../utils/getUsers.ts'; @@ -1065,6 +1066,13 @@ const emitEventResponse = (socket: Socket, organization: string, event: string, console.warn(`Organization missing in response for event: ${event}`); } }; +interface UserSocketInfo { + socketId: string; + userId: string; + role: string; // e.g., "admin" or "user" +} + +const connectedUsersByOrg: { [organization: string]: UserSocketInfo[] } = {}; export const initSocketServer = (httpServer: any) => { const io = new Server(httpServer, { @@ -1075,44 +1083,12 @@ export const initSocketServer = (httpServer: any) => { }); - // Listen for new connections - // io.on(EVENTS.connection, (socket: Socket) => { - // // console.log(`New client connected: ${socket.id}`); - // // console.log(socket.handshake.auth); - // userStatus(EVENTS.connection, socket, socket.handshake.auth, io); - // // console.log('socket.handshake.auth: ', socket.handshake.auth); - - // // Handle all incoming events with the handleEvent function - // socket.onAny((event: string, data: any,) => { - // cameraHandleEvent(event, socket, data, io); - // EnvironmentHandleEvent(event, socket, data, io); - // floorItemsHandleEvent(event, socket, data, io); - // wallItemsHandleEvent(event, socket, data, io); - // lineHandleEvent(event, socket, data, io); - // zoneHandleEvent(event, socket, data, io); - - - // }); - // socket.on(EVENTS.disconnect, (reason: string) => { - // // console.log(`Client disconnected: ${socket.id}, Reason: ${reason}`); - // // console.log(socket.handshake.auth); - // userStatus(EVENTS.disconnect, socket, socket.handshake.auth, io); - // // Perform cleanup or other necessary actions - // }); - // }); // 🔹 Create different namespaces const namespaces = { - // camera: io.of("/camera"), - // environment: io.of("/environment"), - // floorItems: io.of("/floorItems"), - // wallItems: io.of("/wallItems"), - // line: io.of("/line"), - // zone: io.of("/zone"), + Builder: io.of('/Builder'), visualization: io.of('/Visualization'), project: io.of('/project'), - - // widget:io.of('/widget') }; // const onlineUsers = new Map>(); @@ -1121,14 +1097,13 @@ export const initSocketServer = (httpServer: any) => { const handleNamespace = (namespaceName: string, namespace: any, ...eventHandlers: Function[]) => { - namespace.on("connection", (socket: Socket) => { - // console.log(`✅ Client connected to ${namespaceName}: ${socket.id}`); - // Extract organization from query parameters + namespace.on("connection", async (socket: Socket) => { + // const organization = socket.handshake.query.organization as string; // const email = socket.handshake.query.email as string; - const { organization, email } = socket.handshake.auth - // console.log(`🔍 Received organization: ${organization}`); + const { organization, email, userId } = socket.handshake.auth + console.log(' socket.handshake.auth: ', socket.handshake.auth); if (organization) { socket.join(organization); @@ -1140,24 +1115,49 @@ export const initSocketServer = (httpServer: any) => { onlineUsers[organization] = new Set(); } onlineUsers[organization].add(socket.id); - // console.log('onlineUsers: ', onlineUsers); - // console.log(`✅ User ${email} joined ${organization}. Active users:`, onlineUsers[organization]); } - userStatus(EVENTS.connection, socket, socket.handshake.auth, socket); + const role = await getUserRole(userId, organization); + console.log('role: ', role); + if (role === "Admin") { + socket.join(`${organization}_admins`); + } + // Save user info somewhere + if (organization && userId && role) { + if (!connectedUsersByOrg[organization]) { + connectedUsersByOrg[organization] = []; + } + connectedUsersByOrg[organization].push({ + socketId: socket.id, + userId, + role, + }); + + // console.log(`🔗 User connected: ${userId} with role ${role} in ${organization}`); + // console.log("🧩 connectedUsersByOrg: ", connectedUsersByOrg); + } else { + console.warn(`❌ Cannot store user. Missing data:`,); + } + + + + userStatus(EVENTS.connection, socket, socket.handshake.auth, socket); socket.onAny((event: string, data: any, callback: any) => { - eventHandlers.forEach(handler => handler(event, socket, data, namespace, io, callback)); + eventHandlers.forEach(handler => handler(event, socket, io, data, connectedUsersByOrg, callback)); }); + // Handle disconnection socket.on("disconnect", () => { onlineUsers[organization]?.delete(socket.id); if (onlineUsers[organization]?.size === 0) delete onlineUsers[organization]; userStatus(EVENTS.disconnect, socket, socket.handshake.auth, socket); - // console.log(`❌ User ${email} disconnected. Remaining:`, onlineUsers[organization]); - // console.log(`❌ Client disconnected from ${namespaceName}: ${socket.id}, Reason: ${reason}`); + connectedUsersByOrg[organization] = connectedUsersByOrg[organization].filter( + (u) => u.socketId !== socket.id + ); + }); // Handle reconnection (Auto rejoin) @@ -1172,15 +1172,6 @@ export const initSocketServer = (httpServer: any) => { }; - // 🔹 Apply namespace handlers - // handleNamespace("camera", namespaces.camera, cameraHandleEvent); - // handleNamespace("environment", namespaces.environment, EnvironmentHandleEvent); - // handleNamespace("floorItems", namespaces.floorItems, floorItemsHandleEvent); - // handleNamespace("wallItems", namespaces.wallItems, wallItemsHandleEvent); - // handleNamespace("line", namespaces.line, lineHandleEvent); - // handleNamespace("zone", namespaces.zone, zoneHandleEvent); - // handleNamespace("visualization", namespaces.panel, panelHandleEvent); - // handleNamespace("widget", namespaces.visualization, widgetHandleEvent); handleNamespace("Builder", namespaces.Builder, userStatus, modelAssetHandleEvent, cameraHandleEvent, EnvironmentHandleEvent, wallItemsHandleEvent, lineHandleEvent, zoneHandleEvent); handleNamespace("Visualization", namespaces.visualization, panelHandleEvent, widgetHandleEvent, floatHandleEvent, templateHandleEvent, Widget3DHandleEvent); handleNamespace("project", namespaces.project, projectHandleEvent, projectDeleteHandleEvent, projecUpdateHandleEvent) diff --git a/src/socket-server/utils/emitEventResponse.ts b/src/socket-server/utils/emitEventResponse.ts index 6cbdd44..047109b 100644 --- a/src/socket-server/utils/emitEventResponse.ts +++ b/src/socket-server/utils/emitEventResponse.ts @@ -1,4 +1,4 @@ -import { Socket } from "socket.io"; +import { Socket,Server } from "socket.io"; interface EmitOptions { success: boolean; @@ -19,12 +19,13 @@ export const emitEventResponse = ( event: string, result: EmitOptions ) => { + console.log('event: ', event); if (!organization) { console.log(`Organization missing in response for event: ${event}`); return; } - socket.emit(event, { + socket.to(organization).emit(event, { // success: result.success, message: result.message, data: result.data , @@ -33,3 +34,55 @@ export const emitEventResponse = ( organization, }); }; + +// interface EmitOptions { +// success: boolean; +// message: string; +// data?: any; +// error?: any; +// organization: string; +// socketId: string; +// status?: string; +// } + +export const emitToSenderAndAdmins = ( + io: Server, + socket: Socket, + organization: string, + event: string, + result: EmitOptions, + connectedUsersByOrg: { [org: string]: { socketId: string; userId: string; role: string }[] } +) => { + console.log('result.data,: ', result.data,); + // Emit to sender directly + socket.emit(event, { + message: result.message, + data: result.data, + error: result.error, + socketId: result.socketId, + organization, + }); + socket.to(`${organization}_admins`).emit(event, { + message: result.message, + data: result.data, + error: result.error, + socketId: result.socketId, + organization, + }); + // Emit to all admin sockets in organization except sender + // const admins = connectedUsersByOrg[organization]?.filter( + // user => user.role === 'Admin' && user.socketId !== socket.id + // ) || []; + // console.log('admins: ', admins); + + + // admins.forEach(adminUser => { + // io.to(adminUser.socketId).emit(event, { + // message: result.message, + // data: result.data, + // error: result.error, + // socketId: result.socketId, + // organization, + // }); + // }); +}; diff --git a/src/socket-server/utils/getUsers.ts b/src/socket-server/utils/getUsers.ts new file mode 100644 index 0000000..41b7950 --- /dev/null +++ b/src/socket-server/utils/getUsers.ts @@ -0,0 +1,11 @@ +import userModel from "../../shared/model/user-Model.ts"; + +// Example function to get user role from DB + + +export async function getUserRole(userId: string, organization: string): Promise { + console.log('userId: ', userId); + const user = await userModel(organization).findOne({ _id: userId }); + console.log('user: ', user); + return user?.role || "User"; // default to 'user' if no role found +}