From 45f48903fe05dc8b510d5ea08d1e67d72a7258c1 Mon Sep 17 00:00:00 2001 From: sabarinathan Date: Thu, 28 Aug 2025 18:08:18 +0530 Subject: [PATCH] socket implementation --- .env | 5 +- package-lock.json | 362 +++++++++++++++++- package.json | 10 +- .../controllers/edgeController.ts | 134 +++++++ .../controllers/projectController.ts | 70 ++++ src/socket-server/events/eventHandaler.ts | 8 + src/socket-server/events/events.ts | 22 ++ src/socket-server/index.ts | 32 ++ src/socket-server/manager/manager.ts | 82 ++++ src/socket-server/utils/emitEventResponse.ts | 104 +++++ .../utils/socketfunctionHelpers.ts | 41 ++ 11 files changed, 856 insertions(+), 14 deletions(-) create mode 100644 src/socket-server/controllers/edgeController.ts create mode 100644 src/socket-server/controllers/projectController.ts create mode 100644 src/socket-server/events/eventHandaler.ts create mode 100644 src/socket-server/events/events.ts create mode 100644 src/socket-server/index.ts create mode 100644 src/socket-server/manager/manager.ts create mode 100644 src/socket-server/utils/emitEventResponse.ts create mode 100644 src/socket-server/utils/socketfunctionHelpers.ts diff --git a/.env b/.env index 42a068d..0021ff1 100644 --- a/.env +++ b/.env @@ -1,6 +1,7 @@ -MONGO_URI=mongodb://192.168.0.111/ +MONGO_URI=mongodb://192.168.0.113/ MONGO_USER=mydata MONGO_PASSWORD=mongodb@hexr2002 MONGO_AUTH_DB=admin -API_PORT=9696 \ No newline at end of file +API_PORT=9696 +SOCKET_PORT=8002 \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f89db40..d2a799c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,16 +11,19 @@ "dependencies": { "body-parser": "^2.2.0", "cors": "^2.8.5", - "dotenv": "^17.2.0", + "dotenv": "^16.4.5", "express": "^5.1.0", "fs": "^0.0.1-security", + "jsonwebtoken": "^9.0.2", "mongoose": "^8.16.3", "nodemon": "^3.1.10", - "path": "^0.12.7" + "path": "^0.12.7", + "socket.io": "^4.8.1" }, "devDependencies": { "@types/cors": "^2.8.19", "@types/express": "^5.0.3", + "@types/jsonwebtoken": "^9.0.10", "@types/node": "^24.0.14" } }, @@ -32,6 +35,11 @@ "sparse-bitfield": "^3.0.3" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", @@ -55,7 +63,6 @@ "version": "2.8.19", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "dev": true, "dependencies": { "@types/node": "*" } @@ -89,17 +96,32 @@ "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", + "dev": true, + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true + }, "node_modules/@types/node": { "version": "24.0.14", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.14.tgz", "integrity": "sha512-4zXMWD91vBLGRtHK3YbIoFMia+1nqEz72coM42C5ETjnNCa/heoj7NT1G67iAfOqMmcfhuCZ4uNpyz8EjlAejw==", - "dev": true, "dependencies": { "undici-types": "~7.8.0" } @@ -179,6 +201,14 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -237,6 +267,11 @@ "node": ">=16.20.1" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -372,9 +407,9 @@ } }, "node_modules/dotenv": { - "version": "17.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.0.tgz", - "integrity": "sha512-Q4sgBT60gzd0BB0lSyYD3xM4YxrXA9y4uBDof1JNYGzOXrQdQ6yX+7XIAqoFOGQFOTK1D3Hts5OllpxMDZFONQ==", + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "engines": { "node": ">=12" }, @@ -395,6 +430,14 @@ "node": ">= 0.4" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -408,6 +451,88 @@ "node": ">= 0.8" } }, + "node_modules/engine.io": { + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", + "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", + "dependencies": { + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.7.2", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/engine.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -740,6 +865,46 @@ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==" }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kareem": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", @@ -748,6 +913,41 @@ "node": ">=12.0.0" } }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -1280,6 +1480,131 @@ "node": ">=10" } }, + "node_modules/socket.io": { + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", + "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "cors": "~2.8.5", + "debug": "~4.3.2", + "engine.io": "~6.6.0", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.2.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", + "dependencies": { + "debug": "~4.3.4", + "ws": "~8.17.1" + } + }, + "node_modules/socket.io-adapter/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/socket.io/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/sparse-bitfield": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", @@ -1366,8 +1691,7 @@ "node_modules/undici-types": { "version": "7.8.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", - "dev": true + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==" }, "node_modules/unpipe": { "version": "1.0.0", @@ -1422,6 +1746,26 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } } } } diff --git a/package.json b/package.json index c0eedce..8d30d02 100644 --- a/package.json +++ b/package.json @@ -6,23 +6,27 @@ "type": "module", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", - "start:api": "nodemon --exec tsx src/api-server/main.ts" + "start:api": "nodemon --exec tsx src/api-server/main.ts", + "start:socket": "nodemon --exec tsx src/socket-server/index.ts" }, "author": "", "license": "ISC", "dependencies": { "body-parser": "^2.2.0", "cors": "^2.8.5", - "dotenv": "^17.2.0", + "dotenv": "^16.4.5", "express": "^5.1.0", "fs": "^0.0.1-security", + "jsonwebtoken": "^9.0.2", "mongoose": "^8.16.3", "nodemon": "^3.1.10", - "path": "^0.12.7" + "path": "^0.12.7", + "socket.io": "^4.8.1" }, "devDependencies": { "@types/cors": "^2.8.19", "@types/express": "^5.0.3", + "@types/jsonwebtoken": "^9.0.10", "@types/node": "^24.0.14" } } diff --git a/src/socket-server/controllers/edgeController.ts b/src/socket-server/controllers/edgeController.ts new file mode 100644 index 0000000..550987f --- /dev/null +++ b/src/socket-server/controllers/edgeController.ts @@ -0,0 +1,134 @@ +import { Socket, Server } from "socket.io"; +import { EVENTS } from "../events/events"; +import { ErrorResponse, FinalResponse, validateFields } from "../utils/socketfunctionHelpers"; +import { emitToSenderAndAdmins } from "../utils/emitEventResponse"; +import { deleteEdge, edgecreation } from "../../shared/services/edgeService"; + + + +export const edgeHandleEvent = async ( + event: string, + socket: Socket, + io: Server, + data: any, + connectedUsersByOrg: { + [org: string]: { socketId: string; userId: string; role: string }[]; + } +) => { + if (event !== EVENTS.edgeConnect || !data?.organization) return; + const requiredFields = [ + "from", + "to", + "projectId", + "organization", + ]; + const missingFields = validateFields(data, requiredFields); + + if (missingFields.length > 0) { + emitToSenderAndAdmins( + io, + socket, + data.organization, + EVENTS.edgeConnectResponse, + ErrorResponse(missingFields, socket, data.organization), + connectedUsersByOrg + ); + return; + } + const result = await edgecreation(data); + console.log('result: ', result); + const status = typeof result?.status === "string" ? result.status : "unknown"; + + const messages: Record = { + Success: { message: "edge Created Successfully" }, + "User not found": { message: "User not found" }, + "Project not found": { message: "Project not found" }, + "From collection not found": { + message: "From collection not found", + }, + "To collection not found": { message: "To collection not found" }, + "Field already exists": { message: "Field already exists" }, + + }; + + 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 response = FinalResponse( + status, + socket, + data.organization, + messages, + result_Datas + ); + console.log('response: ', response); + + emitToSenderAndAdmins( + io, + socket, + data.organization, + EVENTS.edgeConnectResponse, + response, + connectedUsersByOrg + ); +}; +export const deleteEdgeHandleEvent = async ( + event: string, + socket: Socket, + io: Server, + data: any, + connectedUsersByOrg: { + [org: string]: { socketId: string; userId: string; role: string }[]; + } +) => { + if (event !== EVENTS.deleteEdgeConnect || !data?.organization) return; + const requiredFields = [ + "edgeId", + "projectId", + "organization", + ]; + const missingFields = validateFields(data, requiredFields); + + if (missingFields.length > 0) { + emitToSenderAndAdmins( + io, + socket, + data.organization, + EVENTS.deleteEdgeConnectResponse, + ErrorResponse(missingFields, socket, data.organization), + connectedUsersByOrg + ); + return; + } + const result = await deleteEdge(data); + console.log('result: ', result); + const status = typeof result?.status === "string" ? result.status : "unknown"; + + const messages: Record = { + Success: { message: "edge deleted Successfully" }, + "edge not found": { message: "edge not found" }, + "Project not found": { message: "Project not found" }, + + }; + + const result_Datas = status === "Success" && result?.data ? result.data : undefined; + const response = FinalResponse( + status, + socket, + data.organization, + messages, + result_Datas + ); + console.log('response: ', response); + + emitToSenderAndAdmins( + io, + socket, + data.organization, + EVENTS.edgeConnectResponse, + response, + connectedUsersByOrg + ); +}; \ No newline at end of file diff --git a/src/socket-server/controllers/projectController.ts b/src/socket-server/controllers/projectController.ts new file mode 100644 index 0000000..4364b7c --- /dev/null +++ b/src/socket-server/controllers/projectController.ts @@ -0,0 +1,70 @@ +import { Socket, Server } from "socket.io"; +import { EVENTS } from "../events/events"; +import { ErrorResponse, FinalResponse, validateFields } from "../utils/socketfunctionHelpers"; +import { emitToSenderAndAdmins } from "../utils/emitEventResponse"; +import { projectCreationService } from "../../shared/services/projectService"; + +export const projectHandleEvent = async ( + event: string, + socket: Socket, + io: Server, + data: any, + connectedUsersByOrg: { + [org: string]: { socketId: string; userId: string; role: string }[]; + } +) => { + if (event !== EVENTS.ProjectCreate || !data?.organization) return; + const requiredFields = [ + "application", + "architecture", + "apiType", + "projectName", + "useableLanguage", + "organization", + ]; + const missingFields = validateFields(data, requiredFields); + + if (missingFields.length > 0) { + emitToSenderAndAdmins( + io, + socket, + data.organization, + EVENTS.ProjectCreateResponse, + ErrorResponse(missingFields, socket, data.organization), + connectedUsersByOrg + ); + return; + } + const result = await projectCreationService(data); + const status = typeof result?.status === "string" ? result.status : "unknown"; + + const messages: Record = { + Success: { message: "Project Created Successfully" }, + "Project Already Exists": { message: "Project Already Exists" }, + "Already MVC architecture assigned to this projectId": { message: "Already MVC architecture assigned to this projectId" }, + "Project creation unsuccessfull": { + message: "Project creation unsuccessfull", + }, + "New architecture": { message: "New architecture" }, + + + }; + + 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.ProjectCreateResponse, + response, + connectedUsersByOrg + ); +}; \ No newline at end of file diff --git a/src/socket-server/events/eventHandaler.ts b/src/socket-server/events/eventHandaler.ts new file mode 100644 index 0000000..ed40348 --- /dev/null +++ b/src/socket-server/events/eventHandaler.ts @@ -0,0 +1,8 @@ +import { edgeHandleEvent } from "../controllers/edgeController"; +import { projectHandleEvent } from "../controllers/projectController"; +import { EVENTS } from "./events"; + +export const eventHandlerMap: Record = { + [EVENTS.edgeConnect]: edgeHandleEvent, + [EVENTS.ProjectCreate]: projectHandleEvent, +}; \ No newline at end of file diff --git a/src/socket-server/events/events.ts b/src/socket-server/events/events.ts new file mode 100644 index 0000000..86c735e --- /dev/null +++ b/src/socket-server/events/events.ts @@ -0,0 +1,22 @@ +export const EVENTS = { + connection: "connection", + disconnect: "disconnect", + userConnect: "userConnectResponse", + userDisConnect: "userDisConnectResponse", + joinRoom: "joinRoom", + createroom: "createRoom", + leaveRoom: "leaveRoom", + roomCreated: "roomCreated", + roomDeleted: "roomDeleted", + + + edgeConnect:"v1:edge:connect", + edgeConnectResponse:"v1:response:edge:connect", + deleteEdgeConnect:"v1:edge:deleteConnect", + deleteEdgeConnectResponse:"v1:response:edge:deleteConnect", + + ProjectCreate:"v1:project:create", + ProjectCreateResponse:"v1:response:project:create", + + +} \ No newline at end of file diff --git a/src/socket-server/index.ts b/src/socket-server/index.ts new file mode 100644 index 0000000..6e3eff0 --- /dev/null +++ b/src/socket-server/index.ts @@ -0,0 +1,32 @@ +import express, { Response, Request } from "express"; +import http from "http"; +import dotenv from "dotenv"; +import { Server } from "socket.io"; +import cors from "cors"; +import { SocketServer } from "./manager/manager"; + +dotenv.config(); + +const app = express(); +const PORT = process.env.SOCKET_PORT; +const server = http.createServer(app); + +app.use(cors()); +app.use(express.json()); + + + + +app.get("/", (req: Request, res: Response) => { + res.send("Hello, I am autoCode RealTime!"); +}); +const io = new Server(server, { + cors: { + origin: "*", + methods: ["GET", "POST"], + }, +}); +SocketServer(io); +server.listen(PORT, () => { + console.log(`socket-Server is running on http://localhost:${PORT}`); +}); diff --git a/src/socket-server/manager/manager.ts b/src/socket-server/manager/manager.ts new file mode 100644 index 0000000..cb0d53d --- /dev/null +++ b/src/socket-server/manager/manager.ts @@ -0,0 +1,82 @@ +import { Server, Socket } from "socket.io"; +import jwt from "jsonwebtoken"; +import { eventHandlerMap } from "../events/eventHandaler"; + +interface UserPayload { + userId: string; + organization: string; + [key: string]: any; +} + +interface UserSocketInfo { + socketId: string; + userId: string; + organization: string; +} + +const connectedUsersByOrg: { [organization: string]: UserSocketInfo[] } = {}; + +export const SocketServer = (io: Server) => { + + // ✅ Declare all namespaces here + const namespaceNames = ["/edge", "/project", "/graph"]; + + const onlineUsers: { [organization: string]: Set } = {}; + + // ✅ Attach common handler to each namespace + namespaceNames.forEach((nspName) => { + const namespace = io.of(nspName); + + + // namespace.use((socket: Socket, next) => { + // try { + // socket.handshake.auth + // console.log('socket.handshake.auth: ', socket.handshake.auth); + // const token = socket.handshake.auth.token as string; + // if (!token) return next(new Error("No token provided")); + + // const jwt_secret = process.env.JWT_SECRET as string; + // const decoded = jwt.verify(token, jwt_secret) as UserPayload; + + // (socket as any).user = decoded; + // next(); + // } catch (err) { + // next(new Error("Authentication failed")); + // } + // }); + + namespace.on("connection", (socket: Socket) => { + console.log(`✅ Connected to namespace ${nspName}: ${socket.id}`); + // const user = (socket as any).user as UserPayload; + // const { organization, userId } = user; + + // if (!onlineUsers[organization]) onlineUsers[organization] = new Set(); + // onlineUsers[organization].add(socket.id); + + // if (!connectedUsersByOrg[organization]) connectedUsersByOrg[organization] = []; + // connectedUsersByOrg[organization].push({ socketId: socket.id, userId, organization }); + + // 🎯 Common event handler + socket.onAny((event: string, data: any, callback: any) => { + const handler = eventHandlerMap[event]; + if (handler) { + handler(event, socket, io, data, connectedUsersByOrg, callback); + } else { + console.warn(`⚠️ No handler found for event: ${event}`); + } + }); + + // socket.on("disconnect", () => { + // onlineUsers[organization]?.delete(socket.id); + // if (onlineUsers[organization]?.size === 0) { + // delete onlineUsers[organization]; + // } + // connectedUsersByOrg[organization] = connectedUsersByOrg[organization]?.filter( + // (u) => u.socketId !== socket.id + // ); + // }); + }); + }); + + return io; +}; diff --git a/src/socket-server/utils/emitEventResponse.ts b/src/socket-server/utils/emitEventResponse.ts new file mode 100644 index 0000000..56e1ed2 --- /dev/null +++ b/src/socket-server/utils/emitEventResponse.ts @@ -0,0 +1,104 @@ +import { Socket, Server } from "socket.io"; + +interface EmitOptions { + success: boolean; + message: string; + data?: any; + error?: any; + organization: string; + socketId: string; + status?: string; +} + +export const emitEventResponse = ( + socket: Socket, + organization: string, + event: string, + result: EmitOptions +) => { + if (!organization) { + console.log(`Organization missing in response for event: ${event}`); + return; + } + + socket.to(organization).emit(event, { + // success: result.success, + message: result.message, + data: result.data, + error: result.error, + socketId: result.socketId, + organization, + }); +}; + +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); + 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, + }); +}; +// export const emitToRoom = ( +// io: Server, +// socket: Socket, +// room: string, +// event: string, +// result: EmitOptions +// ) => { +// const payload = { +// message: result.message, +// data: result.data, +// socketId: result.socketId, +// organization: result.organization, +// }; + +// console.log("event: ", event); +// console.log("payload: ", payload); +// console.log("room: ", room); +// socket.emit(event, payload); // ✅ FIXED: sends to all in room including sender +// }; +export const emitToRoom = ( + io: Server, + socket: Socket, + room: string, + event: string, + result: EmitOptions +) => { + const payload = { + message: result.message, + data: result.data, + socketId: result.socketId, + organization: result.organization, + }; + + console.log("🔥 EMIT TO ROOM:"); + console.log("➡️ Event: ", event); + console.log("📦 Full result input: ", result); // 👉 what you passed from observer + console.log("📤 Final payload being emitted: ", payload); // 👉 actual emitted payload + console.log("🏠 Room: ", room); + + socket.to(room).emit(event, payload); + + // ✅ Optional: Emit to sender too (if needed) + socket.emit(event, payload); +}; diff --git a/src/socket-server/utils/socketfunctionHelpers.ts b/src/socket-server/utils/socketfunctionHelpers.ts new file mode 100644 index 0000000..fd61c91 --- /dev/null +++ b/src/socket-server/utils/socketfunctionHelpers.ts @@ -0,0 +1,41 @@ +import { Socket, Server } from "socket.io"; + +export const validateFields = ( + data: any, + requiredFields: string[] +): string[] => { + return requiredFields.filter( + (field) => data[field] === undefined || data[field] === null + ); +}; + +export const ErrorResponse = ( + missingFields: string[], + socket: Socket, + organization: string +) => ({ + success: false, + message: `Missing required field(s): ${missingFields.join(", ")}`, + status: "MissingFields", + socketId: socket.id, + organization: organization ?? "unknown", +}); + +export const FinalResponse = ( + status: string, + socket: Socket, + organization: string, + messages: Record, + data?: any +) => { + const msg = messages[status] || { message: "Internal server error" }; + const includeData = (status === "Success" || status === "updated") && data; + return { + success: status === "Success" || status === "updated", + message: msg.message, + status, + socketId: socket.id, + organization, +...(includeData ? { data } : {}), + }; +};