Update event handling and improve project and collection management logic

This commit is contained in:
2025-10-25 16:03:36 +05:30
parent e86a6609b0
commit 09c88c3396
10 changed files with 291 additions and 76 deletions

2
.env
View File

@@ -1,4 +1,4 @@
MONGO_URI=mongodb://192.168.0.212/ MONGO_URI=mongodb://192.168.0.213/
MONGO_USER=mydata MONGO_USER=mydata
MONGO_PASSWORD=mongodb@hexr2002 MONGO_PASSWORD=mongodb@hexr2002
MONGO_AUTH_DB=admin MONGO_AUTH_DB=admin

View File

@@ -167,7 +167,7 @@ export const updateCollectionController = async (
break; break;
case "Success": case "Success":
res.status(200).json({ res.status(200).json({
message: "collection name updated", message: "collection updated successfully",
}); });
break; break;
default: default:

View File

@@ -373,11 +373,6 @@ export const updateProjectController = async (
case "Project not found": case "Project not found":
res.status(404).json({ message: "Project not found" }); res.status(404).json({ message: "Project not found" });
break; break;
case "No access granted to delete this project":
res
.status(200)
.json({ message: "No access granted to update this project" });
break;
case "Project update unsuccessfull": case "Project update unsuccessfull":
res.status(200).json({ message: "Project update unsuccessfull" }); res.status(200).json({ message: "Project update unsuccessfull" });
break; break;

View File

@@ -1401,6 +1401,7 @@ export const DelAttributes = async (
data: IAttributesDel data: IAttributesDel
): Promise<Iresponse> => { ): Promise<Iresponse> => {
const { organization, userId, projectId, collectionNodeId, fieldId } = data; const { organization, userId, projectId, collectionNodeId, fieldId } = data;
console.log('data: ', data);
try { try {
const existingUser = await userModel(organization).findOne({ const existingUser = await userModel(organization).findOne({
@@ -1421,6 +1422,7 @@ export const DelAttributes = async (
isArchive: false, isArchive: false,
"attributes._id": new mongoose.Types.ObjectId(fieldId), "attributes._id": new mongoose.Types.ObjectId(fieldId),
}); });
console.log('existingCollection: ', existingCollection);
if (!existingCollection) return { status: "Collection not found" }; if (!existingCollection) return { status: "Collection not found" };
const attribute = existingCollection.attributes.find( const attribute = existingCollection.attributes.find(

View File

@@ -164,7 +164,7 @@ export const projectCreationService = async (
} }
); );
} }
return { status: "Success", data: newProject._id }; return { status: "Success", data: newProject };
} else { } else {
return { return {
status: "Already MVC architecture assigned to this projectId", status: "Already MVC architecture assigned to this projectId",
@@ -181,7 +181,7 @@ export const projectCreationService = async (
description, description,
}); });
if (!newProject) return { status: "Project creation unsuccessfull" }; if (!newProject) return { status: "Project creation unsuccessfull" };
return { status: "Success", data: newProject._id }; return { status: "Success", data: newProject };
} }
} }
} catch (error: unknown) { } catch (error: unknown) {
@@ -466,7 +466,7 @@ export const projectModification = async (
{ new: true } { new: true }
); );
if (!updateProject) return { status: "Project updated unsuccessfull" }; if (!updateProject) return { status: "Project updated unsuccessfull" };
return { status: "Success" }; return { status: "Success" ,data:updateProject};
} catch (error: unknown) { } catch (error: unknown) {
if (error instanceof Error) { if (error instanceof Error) {
return { return {

View File

@@ -13,8 +13,8 @@ import {
delCollection, delCollection,
DuplicateCollection, DuplicateCollection,
Nodecreation, Nodecreation,
SetCollectionName,
UpdateAttributes, UpdateAttributes,
updatecollection,
} from "../../shared/services/collectionService"; } from "../../shared/services/collectionService";
export const CollectionHandleEvent = async ( export const CollectionHandleEvent = async (
@@ -76,7 +76,7 @@ export const CollectionHandleEvent = async (
connectedUsersByOrg connectedUsersByOrg
); );
}; };
export const CollectioNamenHandleEvent = async ( export const CollectionUpdateHandleEvent = async (
event: string, event: string,
socket: Socket, socket: Socket,
io: Server, io: Server,
@@ -85,13 +85,8 @@ export const CollectioNamenHandleEvent = async (
[org: string]: { socketId: string; userId: string; role: string }[]; [org: string]: { socketId: string; userId: string; role: string }[];
} }
) => { ) => {
if (event !== EVENTS.collectionNameSet || !data?.organization) return; if (event !== EVENTS.collectionUpdate || !data?.organization) return;
const requiredFields = [ const requiredFields = ["collectionNodeId", "projectId", "organization"];
"collectionName",
"collectionNodeId",
"projectId",
"organization",
];
const missingFields = validateFields(data, requiredFields); const missingFields = validateFields(data, requiredFields);
if (missingFields.length > 0) { if (missingFields.length > 0) {
@@ -99,23 +94,24 @@ export const CollectioNamenHandleEvent = async (
io, io,
socket, socket,
data.organization, data.organization,
EVENTS.collectionNameSetResponse, EVENTS.collectionUpdateResponse,
ErrorResponse(missingFields, socket, data.organization), ErrorResponse(missingFields, socket, data.organization),
connectedUsersByOrg connectedUsersByOrg
); );
return; return;
} }
const result = await SetCollectionName(data); const result = await updatecollection(data);
console.log("result: ", result); console.log("result: ", result);
const status = typeof result?.status === "string" ? result.status : "unknown"; const status = typeof result?.status === "string" ? result.status : "unknown";
const messages: Record<string, { message: string }> = { const messages: Record<string, { message: string }> = {
Success: { message: "collection name updated" }, Success: { message: "collection updated successfully" },
"Collection not found": { message: "Collection not found" },
"Project not found": { message: "Project not found" }, "Project not found": { message: "Project not found" },
}; "User not found": { message: "User not found" },
"Collection not found": { message: "Collection not found" },
"Invalid ID": { message: "nvalid ID provided" },
const msg = messages[status] || { message: "Internal server error" }; };
const result_Datas = const result_Datas =
status === "Success" && result?.data ? result.data : undefined; status === "Success" && result?.data ? result.data : undefined;
console.log("result_Datas: ", result_Datas); console.log("result_Datas: ", result_Datas);
@@ -133,11 +129,12 @@ export const CollectioNamenHandleEvent = async (
io, io,
socket, socket,
data.organization, data.organization,
EVENTS.collectionNameSetResponse, EVENTS.collectionUpdateResponse,
response, response,
connectedUsersByOrg connectedUsersByOrg
); );
}; };
export const CollectioDeleteHandleEvent = async ( export const CollectioDeleteHandleEvent = async (
event: string, event: string,
socket: Socket, socket: Socket,
@@ -350,7 +347,9 @@ export const AttributesDeleteHandleEvent = async (
const messages: Record<string, { message: string }> = { const messages: Record<string, { message: string }> = {
Success: { message: "Field deleted successfully" }, Success: { message: "Field deleted successfully" },
"User not found": { message: "User not found" },
"Project not found": { message: "Project not found" }, "Project not found": { message: "Project not found" },
"Attribute not found": { message: "field not found" },
"Collection not found": { message: "Collection not found" }, "Collection not found": { message: "Collection not found" },
}; };
@@ -358,7 +357,11 @@ export const AttributesDeleteHandleEvent = async (
const result_Datas = const result_Datas =
status === "Success" && result?.data ? result.data : undefined; status === "Success" && result?.data ? result.data : undefined;
console.log("result_Datas: ", result_Datas); console.log("result_Datas: ", result_Datas);
const result1_Datas =
(status === "Success") && result?.data
? result.data
: undefined;
console.log('result1_Datas: ', result1_Datas);
const response = FinalResponse( const response = FinalResponse(
status, status,
socket, socket,
@@ -388,7 +391,6 @@ export const AttributesUpdateHandleEvent = async (
) => { ) => {
if (event !== EVENTS.collectionAttributeUpdate || !data?.organization) return; if (event !== EVENTS.collectionAttributeUpdate || !data?.organization) return;
const requiredFields = [ const requiredFields = [
"fieldId",
"collectionNodeId", "collectionNodeId",
"projectId", "projectId",
"organization", "organization",
@@ -417,10 +419,11 @@ export const AttributesUpdateHandleEvent = async (
}; };
const msg = messages[status] || { message: "Internal server error" }; const msg = messages[status] || { message: "Internal server error" };
const result_Datas =
status === "Success" && result?.data ? result.data : undefined;
console.log("result_Datas: ", result_Datas);
const result_Datas =
(status === "Success") && result?.data
? result.data
: undefined;
const response = FinalResponse( const response = FinalResponse(
status, status,
socket, socket,

View File

@@ -6,7 +6,13 @@ import {
validateFields, validateFields,
} from "../utils/socketfunctionHelpers"; } from "../utils/socketfunctionHelpers";
import { emitToSenderAndAdmins } from "../utils/emitEventResponse"; import { emitToSenderAndAdmins } from "../utils/emitEventResponse";
import { projectCreationService } from "../../shared/services/projectService"; import {
DeleteProject,
projectClear,
projectCreationService,
projectModification,
} from "../../shared/services/projectService";
import { updateProjectController } from "../../api-server/controller/projectController";
export const projectHandleEvent = async ( export const projectHandleEvent = async (
event: string, event: string,
@@ -72,3 +78,166 @@ export const projectHandleEvent = async (
connectedUsersByOrg connectedUsersByOrg
); );
}; };
export const projectUpdateHandleEvent = async (
event: string,
socket: Socket,
io: Server,
data: any,
connectedUsersByOrg: {
[org: string]: { socketId: string; userId: string; role: string }[];
}
) => {
if (event !== EVENTS.projectUpdate || !data?.organization) return;
const requiredFields = ["projectId", "organization"];
const missingFields = validateFields(data, requiredFields);
if (missingFields.length > 0) {
emitToSenderAndAdmins(
io,
socket,
data.organization,
EVENTS.projectUpdateResponse,
ErrorResponse(missingFields, socket, data.organization),
connectedUsersByOrg
);
return;
}
const result = await projectModification(data);
const status = typeof result?.status === "string" ? result.status : "unknown";
const messages: Record<string, { message: string }> = {
Success: { message: "Project updated successfully" },
"Project not found": { message: "Project not found" },
"Project update unsuccessfull": {
message: "Project update unsuccessfull",
},
"User not found": { message: "User not found" },
};
const result_Datas =
status === "Success" && result?.data ? result.data : undefined;
const response = FinalResponse(
status,
socket,
data.organization,
messages,
result_Datas
);
emitToSenderAndAdmins(
io,
socket,
data.organization,
EVENTS.projectUpdateResponse,
response,
connectedUsersByOrg
);
};
export const projectDeleteHandleEvent = async (
event: string,
socket: Socket,
io: Server,
data: any,
connectedUsersByOrg: {
[org: string]: { socketId: string; userId: string; role: string }[];
}
) => {
if (event !== EVENTS.projectDelete || !data?.organization) return;
const requiredFields = ["projectId", "organization"];
const missingFields = validateFields(data, requiredFields);
if (missingFields.length > 0) {
emitToSenderAndAdmins(
io,
socket,
data.organization,
EVENTS.projectDeleteResponse,
ErrorResponse(missingFields, socket, data.organization),
connectedUsersByOrg
);
return;
}
const result = await DeleteProject(data);
const status = typeof result?.status === "string" ? result.status : "unknown";
const messages: Record<string, { message: string }> = {
Success: { message: "Project deleted successfully" },
"Project not found": { message: "Project not found" },
"User not found": { message: "User not found" },
"No access granted to delete this project": {
message: "No access granted to delete this project",
},
"Project Delete unsuccessfull": {
message: "Project Delete unsuccessfull",
},
};
const result_Datas =
status === "Success" && result?.data ? result.data : undefined;
const response = FinalResponse(
status,
socket,
data.organization,
messages,
result_Datas
);
emitToSenderAndAdmins(
io,
socket,
data.organization,
EVENTS.projectDeleteResponse,
response,
connectedUsersByOrg
);
};
export const projectClearHandleEvent = async (
event: string,
socket: Socket,
io: Server,
data: any,
connectedUsersByOrg: {
[org: string]: { socketId: string; userId: string; role: string }[];
}
) => {
if (event !== EVENTS.projectClear || !data?.organization) return;
const requiredFields = ["projectId", "organization"];
const missingFields = validateFields(data, requiredFields);
if (missingFields.length > 0) {
emitToSenderAndAdmins(
io,
socket,
data.organization,
EVENTS.projectClearResponse,
ErrorResponse(missingFields, socket, data.organization),
connectedUsersByOrg
);
return;
}
const result = await projectClear(data);
const status = typeof result?.status === "string" ? result.status : "unknown";
const messages: Record<string, { message: string }> = {
Success: { message: "Datas cleared successfully" },
"Project not found": { message: "Project not found" },
"User not found": { message: "User not found" },
"Validation Error": { message: "Validation Error" },
};
const result_Datas =
status === "Success" && result?.data ? result.data : undefined;
const response = FinalResponse(
status,
socket,
data.organization,
messages,
result_Datas
);
emitToSenderAndAdmins(
io,
socket,
data.organization,
EVENTS.projectClearResponse,
response,
connectedUsersByOrg
);
};

View File

@@ -1,17 +1,35 @@
import { AttributesDeleteHandleEvent, AttributesUpdateHandleEvent, CollectioDeleteHandleEvent, CollectioDuplicateHandleEvent, CollectioNamenHandleEvent, CollectionHandleEvent, setAttributesHandleEvent } from "../controllers/collectionNodeController"; import {
import { edgeHandleEvent } from "../controllers/edgeController"; AttributesDeleteHandleEvent,
import { projectHandleEvent } from "../controllers/projectController"; AttributesUpdateHandleEvent,
CollectioDeleteHandleEvent,
CollectioDuplicateHandleEvent,
CollectionHandleEvent,
CollectionUpdateHandleEvent,
setAttributesHandleEvent,
} from "../controllers/collectionNodeController";
import { deleteEdgeHandleEvent, edgeHandleEvent } from "../controllers/edgeController";
import {
projectClearHandleEvent,
projectDeleteHandleEvent,
projectHandleEvent,
projectUpdateHandleEvent,
} from "../controllers/projectController";
import { EVENTS } from "./events"; import { EVENTS } from "./events";
export const eventHandlerMap: Record<string, Function> = { export const eventHandlerMap: Record<string, Function> = {
[EVENTS.edgeConnect]: edgeHandleEvent, [EVENTS.projectCreate]: projectHandleEvent,
[EVENTS.projectCreate]: projectHandleEvent, [EVENTS.projectUpdate]: projectUpdateHandleEvent,
[EVENTS.projectDelete]: projectDeleteHandleEvent,
[EVENTS.projectClear]: projectClearHandleEvent,
[EVENTS.collectionCreate]: CollectionHandleEvent, [EVENTS.collectionCreate]: CollectionHandleEvent,
[EVENTS.collectionNameSet]: CollectioNamenHandleEvent, [EVENTS.collectionUpdate]: CollectionUpdateHandleEvent,
[EVENTS.collectionDelete]: CollectioDeleteHandleEvent, [EVENTS.collectionDelete]: CollectioDeleteHandleEvent,
[EVENTS.collectionDuplicate]: CollectioDuplicateHandleEvent, [EVENTS.collectionDuplicate]: CollectioDuplicateHandleEvent,
[EVENTS.collectionAttributeSet]: setAttributesHandleEvent, [EVENTS.collectionAttributeSet]: setAttributesHandleEvent,
[EVENTS.collectionAttributeDelete]: AttributesDeleteHandleEvent, [EVENTS.collectionAttributeDelete]: AttributesDeleteHandleEvent,
[EVENTS.collectionAttributeUpdate]: AttributesUpdateHandleEvent, [EVENTS.collectionAttributeUpdate]: AttributesUpdateHandleEvent,
[EVENTS.edgeConnect]: edgeHandleEvent,
[EVENTS.deleteEdgeConnect]: deleteEdgeHandleEvent,
}; };

View File

@@ -12,21 +12,30 @@ export const EVENTS = {
projectCreate: "v1:project:create", projectCreate: "v1:project:create",
projectCreateResponse: "v1:response:project:create", projectCreateResponse: "v1:response:project:create",
projectUpdate: "v1:project:update",
projectUpdateResponse: "v1:response:project:update",
projectDelete: "v1:project:delete",
projectDeleteResponse: "v1:response:project:delete",
projectClear: "v1:project:clear",
projectClearResponse: "v1:response:project:clear",
collectionCreate: "v1:collection:create", collectionCreate: "v1:collection-node:create",
collectionCreateResponse: "v1:response:collection:create", collectionCreateResponse: "v1:response:collection-node:create",
collectionNameSet: "v1:collection:setName", collectionUpdate: "v1:collection-node:update",
collectionNameSetResponse: "v1:response:collection:setName", collectionUpdateResponse: "v1:response:collection-node:update",
collectionDelete: "v1:collection:delete", collectionNameSet: "v1:collection-node:setName",
collectionDeleteResponse: "v1:response:collection:delete", collectionNameSetResponse: "v1:response:collection-node:setName",
collectionDuplicate: "v1:collection:duplicate", collectionDelete: "v1:collection-node:delete",
collectionDuplicateResponse: "v1:response:collection:duplicate", collectionDeleteResponse: "v1:response:collection-node:delete",
collectionAttributeSet: "v1:collection:attributeSet", collectionDuplicate: "v1:collection-node:duplicate",
collectionAttributeSetResponse: "v1:response:collection:attributeSet", collectionDuplicateResponse: "v1:response:collection-node:duplicate",
collectionAttributeUpdate: "v1:collection:attributeUpdate",
collectionAttributeUpdateResponse: "v1:response:collection:attributeUpdate", collectionAttributeSet: "v1:collection-node:attributeSet",
collectionAttributeDelete: "v1:collection:attributeDelete", collectionAttributeSetResponse: "v1:response:collection-node:attributeSet",
collectionAttributeDeleteResponse: "v1:response:collection:attributeDelete", collectionAttributeUpdate: "v1:collection-node:attributeUpdate",
collectionAttributeUpdateResponse: "v1:response:collection-node:attributeUpdate",
collectionAttributeDelete: "v1:collection-node:attributeDelete",
collectionAttributeDeleteResponse: "v1:response:collection-node:attributeDelete",
edgeConnect: "v1:edge:connect", edgeConnect: "v1:edge:connect",
edgeConnectResponse: "v1:response:edge:connect", edgeConnectResponse: "v1:response:edge:connect",

View File

@@ -18,42 +18,53 @@ interface UserSocketInfo {
const connectedUsersByOrg: { [organization: string]: UserSocketInfo[] } = {}; const connectedUsersByOrg: { [organization: string]: UserSocketInfo[] } = {};
export const SocketServer = async (io: Server) => { export const SocketServer = async (io: Server) => {
const namespaceNames = ["/edge", "/project", "/collection"]; const namespaceNames = ["/edge", "/project", "/collection"];
const onlineUsers: { [organization: string]: Set<string> } = {}; const onlineUsers: { [organization: string]: Set<string> } = {};
namespaceNames.forEach((nspName) => { namespaceNames.forEach((nspName) => {
const namespace = io.of(nspName); const namespace = io.of(nspName);
namespace.use(async (socket: Socket, next) => { namespace.use(async (socket: Socket, next) => {
try { try {
const accessToken = socket.handshake.auth.accessToken as string; const accessToken =
const refreshToken = socket.handshake.auth.refreshToken as string; socket.handshake.auth?.accessToken ||
socket.handshake.headers?.accesstoken;
const refreshToken =
socket.handshake.auth?.refreshToken ||
socket.handshake.headers?.refreshToken;
if (!accessToken) return next(new Error("No access token provided")); if (!accessToken) return next(new Error("No access token provided"));
const jwt_secret = process.env.JWT_SECRET as string; const jwt_secret = process.env.JWT_SECRET as string;
try { try {
const decoded = jwt.verify(accessToken, jwt_secret) as UserPayload; const decoded = jwt.verify(accessToken, jwt_secret) as UserPayload;
const mailExistance = await existingUserData(decoded.email, decoded.organization); const mailExistance = await existingUserData(
decoded.email,
decoded.organization
);
if (!mailExistance) { if (!mailExistance) {
return next(new Error("Unauthorized user - not in system")); return next(new Error("Unauthorized user - not in system"));
} }
(socket as any).user = decoded; (socket as any).user = decoded;
return next(); return next();
} catch (err: any) { } catch (err: any) {
if (err.name === "TokenExpiredError") { if (err.name === "TokenExpiredError") {
if (!refreshToken) { if (!refreshToken) {
return next(new Error("Token expired and no refresh token provided")); return next(
new Error("Token expired and no refresh token provided")
);
} }
try { try {
const refreshSecret = process.env.REFRESH_JWT_SECRET as string; const refreshSecret = process.env.REFRESH_JWT_SECRET as string;
const decodedRefresh = jwt.verify(refreshToken, refreshSecret) as UserPayload; const decodedRefresh = jwt.verify(
const mailExistance = await existingUserData(decodedRefresh.email, decodedRefresh.organization); refreshToken,
refreshSecret
) as UserPayload;
const mailExistance = await existingUserData(
decodedRefresh.email,
decodedRefresh.organization
);
if (!mailExistance) { if (!mailExistance) {
return next(new Error("Unauthorized user - not in system")); return next(new Error("Unauthorized user - not in system"));
@@ -61,9 +72,12 @@ export const SocketServer = async (io: Server) => {
// Generate new access token // Generate new access token
const newAccessToken = jwt.sign( const newAccessToken = jwt.sign(
{ email: decodedRefresh.email, organization: decodedRefresh.organization }, {
email: decodedRefresh.email,
organization: decodedRefresh.organization,
},
jwt_secret, jwt_secret,
{ expiresIn: "3h" } // adjust expiry { expiresIn: "3h" } // adjust expiry
); );
socket.emit("newAccessToken", newAccessToken); socket.emit("newAccessToken", newAccessToken);
@@ -88,8 +102,13 @@ export const SocketServer = async (io: Server) => {
if (!onlineUsers[organization]) onlineUsers[organization] = new Set(); if (!onlineUsers[organization]) onlineUsers[organization] = new Set();
onlineUsers[organization].add(socket.id); onlineUsers[organization].add(socket.id);
if (!connectedUsersByOrg[organization]) connectedUsersByOrg[organization] = []; if (!connectedUsersByOrg[organization])
connectedUsersByOrg[organization].push({ socketId: socket.id, userId, organization }); connectedUsersByOrg[organization] = [];
connectedUsersByOrg[organization].push({
socketId: socket.id,
userId,
organization,
});
console.log(`✅ Connected to namespace ${nspName}: ${socket.id}`); console.log(`✅ Connected to namespace ${nspName}: ${socket.id}`);
// Common event handler // Common event handler
@@ -107,9 +126,9 @@ export const SocketServer = async (io: Server) => {
if (onlineUsers[organization]?.size === 0) { if (onlineUsers[organization]?.size === 0) {
delete onlineUsers[organization]; delete onlineUsers[organization];
} }
connectedUsersByOrg[organization] = connectedUsersByOrg[organization]?.filter( connectedUsersByOrg[organization] = connectedUsersByOrg[
(u) => u.socketId !== socket.id organization
); ]?.filter((u) => u.socketId !== socket.id);
}); });
}); });
}); });