├── README.md ├── backend ├── .gitignore ├── package-lock.json ├── package.json ├── src │ ├── index.ts │ └── managers │ │ ├── RoomManager.ts │ │ └── UserManger.ts └── tsconfig.json └── frontend ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── public └── vite.svg ├── src ├── App.css ├── App.tsx ├── assets │ └── react.svg ├── components │ ├── Landing.tsx │ └── Room.tsx ├── index.css ├── main.tsx └── vite-env.d.ts ├── tsconfig.json ├── tsconfig.node.json └── vite.config.ts /README.md: -------------------------------------------------------------------------------- 1 | ## Omegle clone using WebRTC (p2p) 2 | -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /backend/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "backend", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@types/express": "^4.17.21", 13 | "socket.io": "^4.7.3" 14 | } 15 | }, 16 | "node_modules/@socket.io/component-emitter": { 17 | "version": "3.1.0", 18 | "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", 19 | "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" 20 | }, 21 | "node_modules/@types/body-parser": { 22 | "version": "1.19.5", 23 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", 24 | "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", 25 | "dependencies": { 26 | "@types/connect": "*", 27 | "@types/node": "*" 28 | } 29 | }, 30 | "node_modules/@types/connect": { 31 | "version": "3.4.38", 32 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", 33 | "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", 34 | "dependencies": { 35 | "@types/node": "*" 36 | } 37 | }, 38 | "node_modules/@types/cookie": { 39 | "version": "0.4.1", 40 | "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", 41 | "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" 42 | }, 43 | "node_modules/@types/cors": { 44 | "version": "2.8.17", 45 | "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", 46 | "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", 47 | "dependencies": { 48 | "@types/node": "*" 49 | } 50 | }, 51 | "node_modules/@types/express": { 52 | "version": "4.17.21", 53 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", 54 | "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", 55 | "dependencies": { 56 | "@types/body-parser": "*", 57 | "@types/express-serve-static-core": "^4.17.33", 58 | "@types/qs": "*", 59 | "@types/serve-static": "*" 60 | } 61 | }, 62 | "node_modules/@types/express-serve-static-core": { 63 | "version": "4.17.41", 64 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", 65 | "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", 66 | "dependencies": { 67 | "@types/node": "*", 68 | "@types/qs": "*", 69 | "@types/range-parser": "*", 70 | "@types/send": "*" 71 | } 72 | }, 73 | "node_modules/@types/http-errors": { 74 | "version": "2.0.4", 75 | "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", 76 | "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" 77 | }, 78 | "node_modules/@types/mime": { 79 | "version": "1.3.5", 80 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", 81 | "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" 82 | }, 83 | "node_modules/@types/node": { 84 | "version": "20.10.8", 85 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.8.tgz", 86 | "integrity": "sha512-f8nQs3cLxbAFc00vEU59yf9UyGUftkPaLGfvbVOIDdx2i1b8epBqj2aNGyP19fiyXWvlmZ7qC1XLjAzw/OKIeA==", 87 | "dependencies": { 88 | "undici-types": "~5.26.4" 89 | } 90 | }, 91 | "node_modules/@types/qs": { 92 | "version": "6.9.11", 93 | "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", 94 | "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==" 95 | }, 96 | "node_modules/@types/range-parser": { 97 | "version": "1.2.7", 98 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", 99 | "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" 100 | }, 101 | "node_modules/@types/send": { 102 | "version": "0.17.4", 103 | "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", 104 | "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", 105 | "dependencies": { 106 | "@types/mime": "^1", 107 | "@types/node": "*" 108 | } 109 | }, 110 | "node_modules/@types/serve-static": { 111 | "version": "1.15.5", 112 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", 113 | "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", 114 | "dependencies": { 115 | "@types/http-errors": "*", 116 | "@types/mime": "*", 117 | "@types/node": "*" 118 | } 119 | }, 120 | "node_modules/accepts": { 121 | "version": "1.3.8", 122 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 123 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 124 | "dependencies": { 125 | "mime-types": "~2.1.34", 126 | "negotiator": "0.6.3" 127 | }, 128 | "engines": { 129 | "node": ">= 0.6" 130 | } 131 | }, 132 | "node_modules/base64id": { 133 | "version": "2.0.0", 134 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", 135 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", 136 | "engines": { 137 | "node": "^4.5.0 || >= 5.9" 138 | } 139 | }, 140 | "node_modules/cookie": { 141 | "version": "0.4.2", 142 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 143 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", 144 | "engines": { 145 | "node": ">= 0.6" 146 | } 147 | }, 148 | "node_modules/cors": { 149 | "version": "2.8.5", 150 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 151 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 152 | "dependencies": { 153 | "object-assign": "^4", 154 | "vary": "^1" 155 | }, 156 | "engines": { 157 | "node": ">= 0.10" 158 | } 159 | }, 160 | "node_modules/debug": { 161 | "version": "4.3.4", 162 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 163 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 164 | "dependencies": { 165 | "ms": "2.1.2" 166 | }, 167 | "engines": { 168 | "node": ">=6.0" 169 | }, 170 | "peerDependenciesMeta": { 171 | "supports-color": { 172 | "optional": true 173 | } 174 | } 175 | }, 176 | "node_modules/engine.io": { 177 | "version": "6.5.4", 178 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", 179 | "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", 180 | "dependencies": { 181 | "@types/cookie": "^0.4.1", 182 | "@types/cors": "^2.8.12", 183 | "@types/node": ">=10.0.0", 184 | "accepts": "~1.3.4", 185 | "base64id": "2.0.0", 186 | "cookie": "~0.4.1", 187 | "cors": "~2.8.5", 188 | "debug": "~4.3.1", 189 | "engine.io-parser": "~5.2.1", 190 | "ws": "~8.11.0" 191 | }, 192 | "engines": { 193 | "node": ">=10.2.0" 194 | } 195 | }, 196 | "node_modules/engine.io-parser": { 197 | "version": "5.2.1", 198 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", 199 | "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==", 200 | "engines": { 201 | "node": ">=10.0.0" 202 | } 203 | }, 204 | "node_modules/mime-db": { 205 | "version": "1.52.0", 206 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 207 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 208 | "engines": { 209 | "node": ">= 0.6" 210 | } 211 | }, 212 | "node_modules/mime-types": { 213 | "version": "2.1.35", 214 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 215 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 216 | "dependencies": { 217 | "mime-db": "1.52.0" 218 | }, 219 | "engines": { 220 | "node": ">= 0.6" 221 | } 222 | }, 223 | "node_modules/ms": { 224 | "version": "2.1.2", 225 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 226 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 227 | }, 228 | "node_modules/negotiator": { 229 | "version": "0.6.3", 230 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 231 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", 232 | "engines": { 233 | "node": ">= 0.6" 234 | } 235 | }, 236 | "node_modules/object-assign": { 237 | "version": "4.1.1", 238 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 239 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 240 | "engines": { 241 | "node": ">=0.10.0" 242 | } 243 | }, 244 | "node_modules/socket.io": { 245 | "version": "4.7.3", 246 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.3.tgz", 247 | "integrity": "sha512-SE+UIQXBQE+GPG2oszWMlsEmWtHVqw/h1VrYJGK5/MC7CH5p58N448HwIrtREcvR4jfdOJAY4ieQfxMr55qbbw==", 248 | "dependencies": { 249 | "accepts": "~1.3.4", 250 | "base64id": "~2.0.0", 251 | "cors": "~2.8.5", 252 | "debug": "~4.3.2", 253 | "engine.io": "~6.5.2", 254 | "socket.io-adapter": "~2.5.2", 255 | "socket.io-parser": "~4.2.4" 256 | }, 257 | "engines": { 258 | "node": ">=10.2.0" 259 | } 260 | }, 261 | "node_modules/socket.io-adapter": { 262 | "version": "2.5.2", 263 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", 264 | "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", 265 | "dependencies": { 266 | "ws": "~8.11.0" 267 | } 268 | }, 269 | "node_modules/socket.io-parser": { 270 | "version": "4.2.4", 271 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", 272 | "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", 273 | "dependencies": { 274 | "@socket.io/component-emitter": "~3.1.0", 275 | "debug": "~4.3.1" 276 | }, 277 | "engines": { 278 | "node": ">=10.0.0" 279 | } 280 | }, 281 | "node_modules/undici-types": { 282 | "version": "5.26.5", 283 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 284 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 285 | }, 286 | "node_modules/vary": { 287 | "version": "1.1.2", 288 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 289 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 290 | "engines": { 291 | "node": ">= 0.8" 292 | } 293 | }, 294 | "node_modules/ws": { 295 | "version": "8.11.0", 296 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", 297 | "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", 298 | "engines": { 299 | "node": ">=10.0.0" 300 | }, 301 | "peerDependencies": { 302 | "bufferutil": "^4.0.1", 303 | "utf-8-validate": "^5.0.2" 304 | }, 305 | "peerDependenciesMeta": { 306 | "bufferutil": { 307 | "optional": true 308 | }, 309 | "utf-8-validate": { 310 | "optional": true 311 | } 312 | } 313 | } 314 | }, 315 | "dependencies": { 316 | "@socket.io/component-emitter": { 317 | "version": "3.1.0", 318 | "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", 319 | "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==" 320 | }, 321 | "@types/body-parser": { 322 | "version": "1.19.5", 323 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", 324 | "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", 325 | "requires": { 326 | "@types/connect": "*", 327 | "@types/node": "*" 328 | } 329 | }, 330 | "@types/connect": { 331 | "version": "3.4.38", 332 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", 333 | "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", 334 | "requires": { 335 | "@types/node": "*" 336 | } 337 | }, 338 | "@types/cookie": { 339 | "version": "0.4.1", 340 | "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", 341 | "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" 342 | }, 343 | "@types/cors": { 344 | "version": "2.8.17", 345 | "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz", 346 | "integrity": "sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==", 347 | "requires": { 348 | "@types/node": "*" 349 | } 350 | }, 351 | "@types/express": { 352 | "version": "4.17.21", 353 | "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", 354 | "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", 355 | "requires": { 356 | "@types/body-parser": "*", 357 | "@types/express-serve-static-core": "^4.17.33", 358 | "@types/qs": "*", 359 | "@types/serve-static": "*" 360 | } 361 | }, 362 | "@types/express-serve-static-core": { 363 | "version": "4.17.41", 364 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz", 365 | "integrity": "sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA==", 366 | "requires": { 367 | "@types/node": "*", 368 | "@types/qs": "*", 369 | "@types/range-parser": "*", 370 | "@types/send": "*" 371 | } 372 | }, 373 | "@types/http-errors": { 374 | "version": "2.0.4", 375 | "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", 376 | "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" 377 | }, 378 | "@types/mime": { 379 | "version": "1.3.5", 380 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", 381 | "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==" 382 | }, 383 | "@types/node": { 384 | "version": "20.10.8", 385 | "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.8.tgz", 386 | "integrity": "sha512-f8nQs3cLxbAFc00vEU59yf9UyGUftkPaLGfvbVOIDdx2i1b8epBqj2aNGyP19fiyXWvlmZ7qC1XLjAzw/OKIeA==", 387 | "requires": { 388 | "undici-types": "~5.26.4" 389 | } 390 | }, 391 | "@types/qs": { 392 | "version": "6.9.11", 393 | "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", 394 | "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==" 395 | }, 396 | "@types/range-parser": { 397 | "version": "1.2.7", 398 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", 399 | "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==" 400 | }, 401 | "@types/send": { 402 | "version": "0.17.4", 403 | "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", 404 | "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", 405 | "requires": { 406 | "@types/mime": "^1", 407 | "@types/node": "*" 408 | } 409 | }, 410 | "@types/serve-static": { 411 | "version": "1.15.5", 412 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", 413 | "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", 414 | "requires": { 415 | "@types/http-errors": "*", 416 | "@types/mime": "*", 417 | "@types/node": "*" 418 | } 419 | }, 420 | "accepts": { 421 | "version": "1.3.8", 422 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", 423 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", 424 | "requires": { 425 | "mime-types": "~2.1.34", 426 | "negotiator": "0.6.3" 427 | } 428 | }, 429 | "base64id": { 430 | "version": "2.0.0", 431 | "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", 432 | "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" 433 | }, 434 | "cookie": { 435 | "version": "0.4.2", 436 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", 437 | "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" 438 | }, 439 | "cors": { 440 | "version": "2.8.5", 441 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 442 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 443 | "requires": { 444 | "object-assign": "^4", 445 | "vary": "^1" 446 | } 447 | }, 448 | "debug": { 449 | "version": "4.3.4", 450 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", 451 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", 452 | "requires": { 453 | "ms": "2.1.2" 454 | } 455 | }, 456 | "engine.io": { 457 | "version": "6.5.4", 458 | "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", 459 | "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", 460 | "requires": { 461 | "@types/cookie": "^0.4.1", 462 | "@types/cors": "^2.8.12", 463 | "@types/node": ">=10.0.0", 464 | "accepts": "~1.3.4", 465 | "base64id": "2.0.0", 466 | "cookie": "~0.4.1", 467 | "cors": "~2.8.5", 468 | "debug": "~4.3.1", 469 | "engine.io-parser": "~5.2.1", 470 | "ws": "~8.11.0" 471 | } 472 | }, 473 | "engine.io-parser": { 474 | "version": "5.2.1", 475 | "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.1.tgz", 476 | "integrity": "sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ==" 477 | }, 478 | "mime-db": { 479 | "version": "1.52.0", 480 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 481 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" 482 | }, 483 | "mime-types": { 484 | "version": "2.1.35", 485 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 486 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 487 | "requires": { 488 | "mime-db": "1.52.0" 489 | } 490 | }, 491 | "ms": { 492 | "version": "2.1.2", 493 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", 494 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" 495 | }, 496 | "negotiator": { 497 | "version": "0.6.3", 498 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", 499 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" 500 | }, 501 | "object-assign": { 502 | "version": "4.1.1", 503 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 504 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" 505 | }, 506 | "socket.io": { 507 | "version": "4.7.3", 508 | "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.7.3.tgz", 509 | "integrity": "sha512-SE+UIQXBQE+GPG2oszWMlsEmWtHVqw/h1VrYJGK5/MC7CH5p58N448HwIrtREcvR4jfdOJAY4ieQfxMr55qbbw==", 510 | "requires": { 511 | "accepts": "~1.3.4", 512 | "base64id": "~2.0.0", 513 | "cors": "~2.8.5", 514 | "debug": "~4.3.2", 515 | "engine.io": "~6.5.2", 516 | "socket.io-adapter": "~2.5.2", 517 | "socket.io-parser": "~4.2.4" 518 | } 519 | }, 520 | "socket.io-adapter": { 521 | "version": "2.5.2", 522 | "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", 523 | "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", 524 | "requires": { 525 | "ws": "~8.11.0" 526 | } 527 | }, 528 | "socket.io-parser": { 529 | "version": "4.2.4", 530 | "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", 531 | "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", 532 | "requires": { 533 | "@socket.io/component-emitter": "~3.1.0", 534 | "debug": "~4.3.1" 535 | } 536 | }, 537 | "undici-types": { 538 | "version": "5.26.5", 539 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", 540 | "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" 541 | }, 542 | "vary": { 543 | "version": "1.1.2", 544 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 545 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==" 546 | }, 547 | "ws": { 548 | "version": "8.11.0", 549 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", 550 | "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", 551 | "requires": {} 552 | } 553 | } 554 | } 555 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "npx tsc -b", 8 | "start": "node dist/index.js", 9 | "dev": "npm run build && npm run start" 10 | }, 11 | "keywords": [], 12 | "author": "", 13 | "license": "ISC", 14 | "dependencies": { 15 | "@types/express": "^4.17.21", 16 | "socket.io": "^4.7.3" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /backend/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Socket } from "socket.io"; 2 | import http from "http"; 3 | 4 | import express from 'express'; 5 | import { Server } from 'socket.io'; 6 | import { UserManager } from "./managers/UserManger"; 7 | 8 | const app = express(); 9 | const server = http.createServer(http); 10 | 11 | const io = new Server(server, { 12 | cors: { 13 | origin: "*" 14 | } 15 | }); 16 | 17 | const userManager = new UserManager(); 18 | 19 | io.on('connection', (socket: Socket) => { 20 | console.log('a user connected'); 21 | userManager.addUser("randomName", socket); 22 | socket.on("disconnect", () => { 23 | console.log("user disconnected"); 24 | userManager.removeUser(socket.id); 25 | }) 26 | }); 27 | 28 | server.listen(3000, () => { 29 | console.log('listening on *:3000'); 30 | }); -------------------------------------------------------------------------------- /backend/src/managers/RoomManager.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./UserManger"; 2 | 3 | let GLOBAL_ROOM_ID = 1; 4 | 5 | interface Room { 6 | user1: User, 7 | user2: User, 8 | } 9 | 10 | export class RoomManager { 11 | private rooms: Map 12 | constructor() { 13 | this.rooms = new Map() 14 | } 15 | 16 | createRoom(user1: User, user2: User) { 17 | const roomId = this.generate().toString(); 18 | this.rooms.set(roomId.toString(), { 19 | user1, 20 | user2, 21 | }) 22 | 23 | user1.socket.emit("send-offer", { 24 | roomId 25 | }) 26 | 27 | user2.socket.emit("send-offer", { 28 | roomId 29 | }) 30 | } 31 | 32 | onOffer(roomId: string, sdp: string, senderSocketid: string) { 33 | const room = this.rooms.get(roomId); 34 | if (!room) { 35 | return; 36 | } 37 | const receivingUser = room.user1.socket.id === senderSocketid ? room.user2: room.user1; 38 | receivingUser?.socket.emit("offer", { 39 | sdp, 40 | roomId 41 | }) 42 | } 43 | 44 | onAnswer(roomId: string, sdp: string, senderSocketid: string) { 45 | const room = this.rooms.get(roomId); 46 | if (!room) { 47 | return; 48 | } 49 | const receivingUser = room.user1.socket.id === senderSocketid ? room.user2: room.user1; 50 | 51 | receivingUser?.socket.emit("answer", { 52 | sdp, 53 | roomId 54 | }); 55 | } 56 | 57 | onIceCandidates(roomId: string, senderSocketid: string, candidate: any, type: "sender" | "receiver") { 58 | const room = this.rooms.get(roomId); 59 | if (!room) { 60 | return; 61 | } 62 | const receivingUser = room.user1.socket.id === senderSocketid ? room.user2: room.user1; 63 | receivingUser.socket.emit("add-ice-candidate", ({candidate, type})); 64 | } 65 | 66 | generate() { 67 | return GLOBAL_ROOM_ID++; 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /backend/src/managers/UserManger.ts: -------------------------------------------------------------------------------- 1 | import { Socket } from "socket.io"; 2 | import { RoomManager } from "./RoomManager"; 3 | 4 | export interface User { 5 | socket: Socket; 6 | name: string; 7 | } 8 | 9 | export class UserManager { 10 | private users: User[]; 11 | private queue: string[]; 12 | private roomManager: RoomManager; 13 | 14 | constructor() { 15 | this.users = []; 16 | this.queue = []; 17 | this.roomManager = new RoomManager(); 18 | } 19 | 20 | addUser(name: string, socket: Socket) { 21 | this.users.push({ 22 | name, socket 23 | }) 24 | this.queue.push(socket.id); 25 | socket.emit("lobby"); 26 | this.clearQueue() 27 | this.initHandlers(socket); 28 | } 29 | 30 | removeUser(socketId: string) { 31 | const user = this.users.find(x => x.socket.id === socketId); 32 | 33 | this.users = this.users.filter(x => x.socket.id !== socketId); 34 | this.queue = this.queue.filter(x => x === socketId); 35 | } 36 | 37 | clearQueue() { 38 | console.log("inside clear queues") 39 | console.log(this.queue.length); 40 | if (this.queue.length < 2) { 41 | return; 42 | } 43 | 44 | const id1 = this.queue.pop(); 45 | const id2 = this.queue.pop(); 46 | console.log("id is " + id1 + " " + id2); 47 | const user1 = this.users.find(x => x.socket.id === id1); 48 | const user2 = this.users.find(x => x.socket.id === id2); 49 | 50 | if (!user1 || !user2) { 51 | return; 52 | } 53 | console.log("creating roonm"); 54 | 55 | const room = this.roomManager.createRoom(user1, user2); 56 | this.clearQueue(); 57 | } 58 | 59 | initHandlers(socket: Socket) { 60 | socket.on("offer", ({sdp, roomId}: {sdp: string, roomId: string}) => { 61 | this.roomManager.onOffer(roomId, sdp, socket.id); 62 | }) 63 | 64 | socket.on("answer",({sdp, roomId}: {sdp: string, roomId: string}) => { 65 | this.roomManager.onAnswer(roomId, sdp, socket.id); 66 | }) 67 | 68 | socket.on("add-ice-candidate", ({candidate, roomId, type}) => { 69 | this.roomManager.onIceCandidates(roomId, socket.id, candidate, type); 70 | }); 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /backend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Visit https://aka.ms/tsconfig to read more about this file */ 4 | 5 | /* Projects */ 6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */ 7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */ 8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */ 9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */ 10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ 11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ 12 | 13 | /* Language and Environment */ 14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ 15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ 16 | // "jsx": "preserve", /* Specify what JSX code is generated. */ 17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ 18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */ 19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */ 20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */ 21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */ 22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */ 23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */ 24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */ 25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ 26 | 27 | /* Modules */ 28 | "module": "commonjs", /* Specify what module code is generated. */ 29 | "rootDir": "./src", /* Specify the root folder within your source files. */ 30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ 31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ 32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ 33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ 34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ 35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */ 36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ 38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ 39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */ 40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */ 41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */ 42 | // "resolveJsonModule": true, /* Enable importing .json files. */ 43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */ 44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */ 45 | 46 | /* JavaScript Support */ 47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */ 48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */ 49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */ 50 | 51 | /* Emit */ 52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */ 53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */ 54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */ 55 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ 56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ 57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ 58 | "outDir": "./dist", /* Specify an output folder for all emitted files. */ 59 | // "removeComments": true, /* Disable emitting comments. */ 60 | // "noEmit": true, /* Disable emitting files from a compilation. */ 61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ 62 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */ 63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */ 64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */ 65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 66 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */ 67 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */ 68 | // "newLine": "crlf", /* Set the newline character for emitting files. */ 69 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */ 70 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */ 71 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */ 72 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */ 73 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */ 74 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */ 75 | 76 | /* Interop Constraints */ 77 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ 78 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ 79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ 80 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ 81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ 82 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ 83 | 84 | /* Type Checking */ 85 | "strict": true, /* Enable all strict type-checking options. */ 86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ 87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ 88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ 89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ 90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ 91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ 92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */ 93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ 94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ 95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ 96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */ 97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */ 98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */ 99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */ 100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */ 101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */ 102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */ 103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */ 104 | 105 | /* Completeness */ 106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ 107 | "skipLibCheck": true /* Skip type checking all .d.ts files. */ 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /frontend/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'plugin:react-hooks/recommended', 8 | ], 9 | ignorePatterns: ['dist', '.eslintrc.cjs'], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['react-refresh'], 12 | rules: { 13 | 'react-refresh/only-export-components': [ 14 | 'warn', 15 | { allowConstantExport: true }, 16 | ], 17 | }, 18 | } 19 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # React + TypeScript + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | 10 | ## Expanding the ESLint configuration 11 | 12 | If you are developing a production application, we recommend updating the configuration to enable type aware lint rules: 13 | 14 | - Configure the top-level `parserOptions` property like this: 15 | 16 | ```js 17 | export default { 18 | // other rules... 19 | parserOptions: { 20 | ecmaVersion: 'latest', 21 | sourceType: 'module', 22 | project: ['./tsconfig.json', './tsconfig.node.json'], 23 | tsconfigRootDir: __dirname, 24 | }, 25 | } 26 | ``` 27 | 28 | - Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked` 29 | - Optionally add `plugin:@typescript-eslint/stylistic-type-checked` 30 | - Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list 31 | -------------------------------------------------------------------------------- /frontend/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React + TS 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "tsc && vite build", 9 | "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "socket.io-client": "^4.7.3" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.2.43", 19 | "@types/react-dom": "^18.2.17", 20 | "@typescript-eslint/eslint-plugin": "^6.14.0", 21 | "@typescript-eslint/parser": "^6.14.0", 22 | "@vitejs/plugin-react": "^4.2.1", 23 | "eslint": "^8.55.0", 24 | "eslint-plugin-react-hooks": "^4.6.0", 25 | "eslint-plugin-react-refresh": "^0.4.5", 26 | "react-router-dom": "^6.21.1", 27 | "typescript": "^5.2.2", 28 | "vite": "^5.0.8" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /frontend/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkirat/omegle/dcf96943388b0330e4d1b75ac46c35f9de6fe9a2/frontend/src/App.css -------------------------------------------------------------------------------- /frontend/src/App.tsx: -------------------------------------------------------------------------------- 1 | 2 | import { BrowserRouter, Routes, Route } from "react-router-dom"; 3 | import { Landing } from './components/Landing'; 4 | import { Room } from './components/Room'; 5 | 6 | function App() { 7 | 8 | return ( 9 | 10 | 11 | } /> 12 | 13 | 14 | ) 15 | } 16 | 17 | export default App 18 | -------------------------------------------------------------------------------- /frontend/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/src/components/Landing.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react" 2 | import { Link } from "react-router-dom"; 3 | import { Room } from "./Room"; 4 | 5 | export const Landing = () => { 6 | const [name, setName] = useState(""); 7 | const [localAudioTrack, setLocalAudioTrack] = useState(null); 8 | const [localVideoTrack, setlocalVideoTrack] = useState(null); 9 | const videoRef = useRef(null); 10 | 11 | const [joined, setJoined] = useState(false); 12 | 13 | const getCam = async () => { 14 | const stream = await window.navigator.mediaDevices.getUserMedia({ 15 | video: true, 16 | audio: true 17 | }) 18 | // MediaStream 19 | const audioTrack = stream.getAudioTracks()[0] 20 | const videoTrack = stream.getVideoTracks()[0] 21 | setLocalAudioTrack(audioTrack); 22 | setlocalVideoTrack(videoTrack); 23 | if (!videoRef.current) { 24 | return; 25 | } 26 | videoRef.current.srcObject = new MediaStream([videoTrack]) 27 | videoRef.current.play(); 28 | // MediaStream 29 | } 30 | 31 | useEffect(() => { 32 | if (videoRef && videoRef.current) { 33 | getCam() 34 | } 35 | }, [videoRef]); 36 | 37 | if (!joined) { 38 | 39 | return
40 | 41 | { 42 | setName(e.target.value); 43 | }}> 44 | 45 | 48 |
49 | } 50 | 51 | return 52 | } -------------------------------------------------------------------------------- /frontend/src/components/Room.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef, useState } from "react"; 2 | import { useSearchParams } from "react-router-dom"; 3 | import { Socket, io } from "socket.io-client"; 4 | 5 | const URL = "http://localhost:3000"; 6 | 7 | export const Room = ({ 8 | name, 9 | localAudioTrack, 10 | localVideoTrack 11 | }: { 12 | name: string, 13 | localAudioTrack: MediaStreamTrack | null, 14 | localVideoTrack: MediaStreamTrack | null, 15 | }) => { 16 | const [searchParams, setSearchParams] = useSearchParams(); 17 | const [lobby, setLobby] = useState(true); 18 | const [socket, setSocket] = useState(null); 19 | const [sendingPc, setSendingPc] = useState(null); 20 | const [receivingPc, setReceivingPc] = useState(null); 21 | const [remoteVideoTrack, setRemoteVideoTrack] = useState(null); 22 | const [remoteAudioTrack, setRemoteAudioTrack] = useState(null); 23 | const [remoteMediaStream, setRemoteMediaStream] = useState(null); 24 | const remoteVideoRef = useRef(); 25 | const localVideoRef = useRef(); 26 | 27 | useEffect(() => { 28 | const socket = io(URL); 29 | socket.on('send-offer', async ({roomId}) => { 30 | console.log("sending offer"); 31 | setLobby(false); 32 | const pc = new RTCPeerConnection(); 33 | 34 | setSendingPc(pc); 35 | if (localVideoTrack) { 36 | console.error("added tack"); 37 | console.log(localVideoTrack) 38 | pc.addTrack(localVideoTrack) 39 | } 40 | if (localAudioTrack) { 41 | console.error("added tack"); 42 | console.log(localAudioTrack) 43 | pc.addTrack(localAudioTrack) 44 | } 45 | 46 | pc.onicecandidate = async (e) => { 47 | console.log("receiving ice candidate locally"); 48 | if (e.candidate) { 49 | socket.emit("add-ice-candidate", { 50 | candidate: e.candidate, 51 | type: "sender", 52 | roomId 53 | }) 54 | } 55 | } 56 | 57 | pc.onnegotiationneeded = async () => { 58 | console.log("on negotiation neeeded, sending offer"); 59 | const sdp = await pc.createOffer(); 60 | //@ts-ignore 61 | pc.setLocalDescription(sdp) 62 | socket.emit("offer", { 63 | sdp, 64 | roomId 65 | }) 66 | } 67 | }); 68 | 69 | socket.on("offer", async ({roomId, sdp: remoteSdp}) => { 70 | console.log("received offer"); 71 | setLobby(false); 72 | const pc = new RTCPeerConnection(); 73 | pc.setRemoteDescription(remoteSdp) 74 | const sdp = await pc.createAnswer(); 75 | //@ts-ignore 76 | pc.setLocalDescription(sdp) 77 | const stream = new MediaStream(); 78 | if (remoteVideoRef.current) { 79 | remoteVideoRef.current.srcObject = stream; 80 | } 81 | 82 | setRemoteMediaStream(stream); 83 | // trickle ice 84 | setReceivingPc(pc); 85 | window.pcr = pc; 86 | pc.ontrack = (e) => { 87 | alert("ontrack"); 88 | // console.error("inside ontrack"); 89 | // const {track, type} = e; 90 | // if (type == 'audio') { 91 | // // setRemoteAudioTrack(track); 92 | // // @ts-ignore 93 | // remoteVideoRef.current.srcObject.addTrack(track) 94 | // } else { 95 | // // setRemoteVideoTrack(track); 96 | // // @ts-ignore 97 | // remoteVideoRef.current.srcObject.addTrack(track) 98 | // } 99 | // //@ts-ignore 100 | // remoteVideoRef.current.play(); 101 | } 102 | 103 | pc.onicecandidate = async (e) => { 104 | if (!e.candidate) { 105 | return; 106 | } 107 | console.log("omn ice candidate on receiving seide"); 108 | if (e.candidate) { 109 | socket.emit("add-ice-candidate", { 110 | candidate: e.candidate, 111 | type: "receiver", 112 | roomId 113 | }) 114 | } 115 | } 116 | 117 | socket.emit("answer", { 118 | roomId, 119 | sdp: sdp 120 | }); 121 | setTimeout(() => { 122 | const track1 = pc.getTransceivers()[0].receiver.track 123 | const track2 = pc.getTransceivers()[1].receiver.track 124 | console.log(track1); 125 | if (track1.kind === "video") { 126 | setRemoteAudioTrack(track2) 127 | setRemoteVideoTrack(track1) 128 | } else { 129 | setRemoteAudioTrack(track1) 130 | setRemoteVideoTrack(track2) 131 | } 132 | //@ts-ignore 133 | remoteVideoRef.current.srcObject.addTrack(track1) 134 | //@ts-ignore 135 | remoteVideoRef.current.srcObject.addTrack(track2) 136 | //@ts-ignore 137 | remoteVideoRef.current.play(); 138 | // if (type == 'audio') { 139 | // // setRemoteAudioTrack(track); 140 | // // @ts-ignore 141 | // remoteVideoRef.current.srcObject.addTrack(track) 142 | // } else { 143 | // // setRemoteVideoTrack(track); 144 | // // @ts-ignore 145 | // remoteVideoRef.current.srcObject.addTrack(track) 146 | // } 147 | // //@ts-ignore 148 | }, 5000) 149 | }); 150 | 151 | socket.on("answer", ({roomId, sdp: remoteSdp}) => { 152 | setLobby(false); 153 | setSendingPc(pc => { 154 | pc?.setRemoteDescription(remoteSdp) 155 | return pc; 156 | }); 157 | console.log("loop closed"); 158 | }) 159 | 160 | socket.on("lobby", () => { 161 | setLobby(true); 162 | }) 163 | 164 | socket.on("add-ice-candidate", ({candidate, type}) => { 165 | console.log("add ice candidate from remote"); 166 | console.log({candidate, type}) 167 | if (type == "sender") { 168 | setReceivingPc(pc => { 169 | if (!pc) { 170 | console.error("receicng pc nout found") 171 | } else { 172 | console.error(pc.ontrack) 173 | } 174 | pc?.addIceCandidate(candidate) 175 | return pc; 176 | }); 177 | } else { 178 | setSendingPc(pc => { 179 | if (!pc) { 180 | console.error("sending pc nout found") 181 | } else { 182 | // console.error(pc.ontrack) 183 | } 184 | pc?.addIceCandidate(candidate) 185 | return pc; 186 | }); 187 | } 188 | }) 189 | 190 | setSocket(socket) 191 | }, [name]) 192 | 193 | useEffect(() => { 194 | if (localVideoRef.current) { 195 | if (localVideoTrack) { 196 | localVideoRef.current.srcObject = new MediaStream([localVideoTrack]); 197 | localVideoRef.current.play(); 198 | } 199 | } 200 | }, [localVideoRef]) 201 | 202 | return
203 | Hi {name} 204 |
208 | } 209 | 210 | -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hkirat/omegle/dcf96943388b0330e4d1b75ac46c35f9de6fe9a2/frontend/src/index.css -------------------------------------------------------------------------------- /frontend/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.tsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')!).render( 7 | , 8 | ) 9 | -------------------------------------------------------------------------------- /frontend/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"], 24 | "references": [{ "path": "./tsconfig.node.json" }] 25 | } 26 | -------------------------------------------------------------------------------- /frontend/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /frontend/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | --------------------------------------------------------------------------------