diff --git a/.env b/.env new file mode 100644 index 0000000..e69de29 diff --git a/Dockerfile b/Dockerfile index d0642b9..5851d4c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,10 @@ COPY package.json /videochat/ RUN npm install COPY ./ /videochat -RUN npm run build + +ARG VUE_APP_SOCKET_HOST=NOT_SET +ARG VUE_APP_SOCKET_PORT=NOT_SET + +RUN export VUE_APP_SOCKET_HOST=${VUE_APP_SOCKET_HOST} VUE_APP_SOCKET_PORT=${VUE_APP_SOCKET_PORT} && npm run build CMD ["npm", "run", "run:server"] \ No newline at end of file diff --git a/README.md b/README.md index acf707c..632a826 100644 --- a/README.md +++ b/README.md @@ -1 +1,45 @@ -# video-chat \ No newline at end of file +# video-chat +> Video chat application using [VueJS](https://vuejs.org), [Vuex](https://vuex.vuejs.org), [WebRTC](https://webrtc.org/start/), [SocketIO](https://socket.io), NodeJS and [Redis](https://github.com/NodeRedis/node_redis) + +## Quick start +First of all, you need to install and run the redis in your PC. Here there is an [article](https://medium.com/@petehouston/install-and-config-redis-on-mac-os-x-via-homebrew-eb8df9a4f298) for Mac OS X. Once Redis is up and running: + +```bash +# Clone the repo +git clone https://github.com/adrigardi90/video-chat + +# Change into the repo directory +cd video-chat + +# install +npm install + +# Start the FE in dev mode +npm run serve + +# Start the server +npm run run:server + +``` +Then visit http://localhost:8080 in your browser + +## Horizontal scaling +To test the horizontal scaling we need to run two different instances. Each one will run a nodeJS process serving the FE and +exposing the API + +
+ +
+ + +```bash +# Build the images +docker-compose build + +# Create and run the two instances +docker-compose up + +``` + +Then you'll find on http://localhost:3000 and http://localhost:3001 both FE applications, each one with a different socket connection + diff --git a/docker-compose.yml b/docker-compose.yml index d3f5461..cba6b54 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,24 +1,53 @@ -version: '2' +version: '3' services: + redis: + image: redis:4.0.5-alpine + networks: + - video-chat + ports: + - 6379:6379 + expose: + - "6379" + restart: always + command: ["redis-server", "--appendonly", "yes"] + + # Copy 1 chat1: - build: . + build: + context: . + args: + VUE_APP_SOCKET_HOST: localhost + VUE_APP_SOCKET_PORT: 3000 ports: - 3000:3000 networks: - video-chat + depends_on: + - redis environment: PORT: 3000 + REDIS_HOST: redis + REDIS_PORT: 6379 + # Copy 2 chat2: - build: . + build: + context: . + args: + VUE_APP_SOCKET_HOST: localhost + VUE_APP_SOCKET_PORT: 3001 ports: - 3001:3001 networks: - video-chat + depends_on: + - redis environment: PORT: 3001 - + REDIS_HOST: redis + REDIS_PORT: 6379 + networks: video-chat: \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 79228c7..177784c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -832,6 +832,11 @@ "integrity": "sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==", "dev": true }, + "@sindresorhus/is": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", + "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==" + }, "@types/q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.1.tgz", @@ -2383,6 +2388,42 @@ "schema-utils": "^0.4.2" } }, + "cacheable-request": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz", + "integrity": "sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=", + "requires": { + "clone-response": "1.0.2", + "get-stream": "3.0.0", + "http-cache-semantics": "3.8.1", + "keyv": "3.0.0", + "lowercase-keys": "1.0.0", + "normalize-url": "2.0.1", + "responselike": "1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "lowercase-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz", + "integrity": "sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=" + }, + "normalize-url": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz", + "integrity": "sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==", + "requires": { + "prepend-http": "^2.0.0", + "query-string": "^5.0.1", + "sort-keys": "^2.0.0" + } + } + } + }, "call-me-maybe": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", @@ -2717,6 +2758,14 @@ "shallow-clone": "^1.0.0" } }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -3473,8 +3522,15 @@ "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } }, "deep-equal": { "version": "1.0.1", @@ -3781,12 +3837,22 @@ "is-obj": "^1.0.0" } }, + "double-ended-queue": { + "version": "2.1.0-0", + "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", + "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" + }, "duplexer": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, "duplexify": { "version": "3.6.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.6.1.tgz", @@ -4936,7 +5002,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, "requires": { "inherits": "^2.0.1", "readable-stream": "^2.0.0" @@ -5748,6 +5813,37 @@ "minimatch": "~3.0.2" } }, + "got": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/got/-/got-8.3.2.tgz", + "integrity": "sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==", + "requires": { + "@sindresorhus/is": "^0.7.0", + "cacheable-request": "^2.1.1", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "into-stream": "^3.1.0", + "is-retry-allowed": "^1.1.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "mimic-response": "^1.0.0", + "p-cancelable": "^0.4.0", + "p-timeout": "^2.0.1", + "pify": "^3.0.0", + "safe-buffer": "^5.1.1", + "timed-out": "^4.0.1", + "url-parse-lax": "^3.0.0", + "url-to-options": "^1.0.1" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + } + } + }, "graceful-fs": { "version": "4.1.15", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", @@ -5833,12 +5929,25 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" + }, "has-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -6092,6 +6201,11 @@ "resolved": "https://registry.npmjs.org/http/-/http-0.0.0.tgz", "integrity": "sha1-huYybSnF0Dnen6xYSkVon5KfT3I=" }, + "http-cache-semantics": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz", + "integrity": "sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==" + }, "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -6354,6 +6468,15 @@ "ipaddr.js": "^1.5.2" } }, + "into-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz", + "integrity": "sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=", + "requires": { + "from2": "^2.1.1", + "p-is-promise": "^1.1.0" + } + }, "invariant": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", @@ -6579,6 +6702,11 @@ "integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=", "dev": true }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" + }, "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -6603,6 +6731,11 @@ "path-is-inside": "^1.0.1" } }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, "is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -6632,6 +6765,11 @@ "integrity": "sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==", "dev": true }, + "is-retry-allowed": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz", + "integrity": "sha1-EaBgVotnM5REAz0BJaYaINVk+zQ=" + }, "is-stream": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", @@ -6707,6 +6845,15 @@ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, "javascript-stringify": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-1.6.0.tgz", @@ -6777,6 +6924,11 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -6845,6 +6997,14 @@ "verror": "1.10.0" } }, + "keyv": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz", + "integrity": "sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==", + "requires": { + "json-buffer": "3.0.0" + } + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -7114,6 +7274,11 @@ "integrity": "sha1-miyr0bno4K6ZOkv31YdcOcQujqw=", "dev": true }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, "lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -7317,6 +7482,11 @@ "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", "dev": true }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, "mini-css-extract-plugin": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz", @@ -7767,6 +7937,11 @@ "integrity": "sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg==", "dev": true }, + "notepack.io": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-2.1.3.tgz", + "integrity": "sha512-AgSt+cP5XMooho1Ppn8NB3FFaVWefV+qZoZncYTUSch2GAEwlYLcIIbT5YVkMlFeNHnfwOvc4HDlbvrB5BRxXA==" + }, "npm-run-path": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", @@ -8042,6 +8217,11 @@ "os-tmpdir": "^1.0.0" } }, + "p-cancelable": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz", + "integrity": "sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==" + }, "p-defer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", @@ -8051,14 +8231,12 @@ "p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" }, "p-is-promise": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz", - "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=", - "dev": true + "integrity": "sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=" }, "p-limit": { "version": "2.1.0", @@ -8084,6 +8262,14 @@ "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", "dev": true }, + "p-timeout": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz", + "integrity": "sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==", + "requires": { + "p-finally": "^1.0.0" + } + }, "p-try": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", @@ -8184,6 +8370,25 @@ "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", "dev": true }, + "path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", + "requires": { + "process": "^0.11.1", + "util": "^0.10.3" + }, + "dependencies": { + "util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "requires": { + "inherits": "2.0.3" + } + } + } + }, "path-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", @@ -8873,6 +9078,11 @@ "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", "dev": true }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, "prettier": { "version": "1.13.7", "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.13.7.tgz", @@ -8898,8 +9108,7 @@ "process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true + "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" }, "process-nextick-args": { "version": "2.0.0", @@ -9006,6 +9215,16 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, "querystring": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", @@ -9177,6 +9396,26 @@ } } }, + "redis": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", + "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", + "requires": { + "double-ended-queue": "^2.1.0-0", + "redis-commands": "^1.2.0", + "redis-parser": "^2.6.0" + } + }, + "redis-commands": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.4.0.tgz", + "integrity": "sha512-cu8EF+MtkwI4DLIT0x9P8qNTLFhQD4jLfxLR0cCNkeGzs87FN6879JOJwNQR/1zD7aSYNbU0hgsV9zGY71Itvw==" + }, + "redis-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", + "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" + }, "regenerate": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", @@ -9461,6 +9700,14 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -10193,6 +10440,33 @@ } } }, + "socket.io-redis": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/socket.io-redis/-/socket.io-redis-5.2.0.tgz", + "integrity": "sha1-j+KtlEX8UIhvtwq8dZ1nQD1Ymd8=", + "requires": { + "debug": "~2.6.8", + "notepack.io": "~2.1.2", + "redis": "~2.8.0", + "socket.io-adapter": "~1.1.0", + "uid2": "0.0.3" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "sockjs": { "version": "0.3.19", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.19.tgz", @@ -10237,6 +10511,14 @@ } } }, + "sort-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz", + "integrity": "sha1-ZYU1WEhh7JfXMNbPQYIuH1ZoQSg=", + "requires": { + "is-plain-obj": "^1.0.0" + } + }, "source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -10485,6 +10767,11 @@ "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=", "dev": true }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" + }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -10825,6 +11112,11 @@ "integrity": "sha512-YwT8pjmNcAXBZqrubu22P4FYsh2D4dxRmnWBOL8Jk8bUcRUtc5326kx32tuTmFDAZtLOGEVNl8POAR8j896Iow==", "dev": true }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + }, "timers-browserify": { "version": "2.0.10", "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz", @@ -11031,6 +11323,11 @@ } } }, + "uid2": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz", + "integrity": "sha1-SDEm4Rd03y9xuLY53NeZw3YWK4I=" + }, "unicode-canonical-property-names-ecmascript": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", @@ -11265,6 +11562,19 @@ "requires-port": "^1.0.0" } }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -11428,6 +11738,14 @@ "resolved": "https://registry.npmjs.org/vue-material/-/vue-material-1.0.0-beta-10.2.tgz", "integrity": "sha512-DXOUXm6M8zbeJ6LPvP1h/0EhXmCkxNGz2dM/0q4w+lsT9uTaos+YDrwQvJBJcxil3fEw6oHCfhG/peWks+8v7Q==" }, + "vue-resource": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/vue-resource/-/vue-resource-1.5.1.tgz", + "integrity": "sha512-o6V4wNgeqP+9v9b2bPXrr20CGNQPEXjpbUWdZWq9GJhqVeAGcYoeTtn/D4q059ZiyN0DIrDv/ADrQUmlUQcsmg==", + "requires": { + "got": "^8.0.3" + } + }, "vue-router": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.0.2.tgz", diff --git a/package.json b/package.json index 0305cca..3fb737e 100644 --- a/package.json +++ b/package.json @@ -17,9 +17,11 @@ "path": "^0.12.7", "socket.io": "^2.2.0", "socket.io-client": "^2.2.0", + "socket.io-redis": "^5.2.0", "vue": "^2.5.21", "vue-loading-spinner": "^1.0.11", "vue-material": "^1.0.0-beta-10.2", + "vue-resource": "^1.5.1", "vue-router": "^3.0.1", "vue-socket.io": "^3.0.5", "vuex": "^3.0.1" diff --git a/server/chat_namespace/events.js b/server/chat_namespace/events.js index 23d8350..b2a9a97 100644 --- a/server/chat_namespace/events.js +++ b/server/chat_namespace/events.js @@ -1,12 +1,5 @@ -const users = { - general: [ - - ], - sports: [ - - ] -}; +const ChatRedis = require('../redis') const joinRoom = (socket, namespace) => ({ username, room }) => { console.log(`user ${username} wants to join the room ${room}`); @@ -15,11 +8,15 @@ const joinRoom = (socket, namespace) => ({ username, room }) => { socket.join(room, () => { console.log(`user ${username} joined the room ${room}`); - // push user for the suitable ROOM!!! - users[room].push({ username: username, privateChat: false }) + // add user for the suitable ROOM + ChatRedis.addUser(room, socket.id, { username: username, privateChat: false }) + + ChatRedis.getUsers(room).then(users => { + if (users === null) return - // Notify all the users in the same room - namespace.sockets.in(room).emit('newUser', users[room]); + // Notify all the users in the same room + namespace.sockets.in(room).emit('newUser', users); + }) }); } @@ -37,12 +34,19 @@ const leaveRoom = (socket, namespace) => ({ room, username }) => { socket.leave(room, () => { console.log(`user ${username} left the room ${room}`); - let usersRoom = users[room] - // delete user from the suitable array - usersRoom = usersRoom.filter((user) => (user.username !== username)) - // Notify all the users in the same room - namespace.sockets.in(room).emit('newUser', usersRoom); + ChatRedis.delUser(room, socket.id) + .then(data => { + if (data === null) return null + return ChatRedis.getUsers(room); + }) + .then(users => { + if (users === null) return + + // Notify all the users in the same room + namespace.sockets.in(room).emit('newUser', users); + }) + }) } @@ -54,33 +58,43 @@ const joinPrivateRoom = (socket, namespace) => ({ username, room, to }) => { if (room !== null) { - let usersRoom = users[room]; - let userToTalk = usersRoom.find(user => user.username === to) - - // If he is already talking - if (userToTalk.privateChat) { - - namespace.to(to).emit('leavePrivateRoom', { - to, - privateMessage: `${to} is already talking`, - from: username, - room - }) - - socket.leave(to, () => { - console.log(`user ${username} force to left the room ${to}`); - }) + ChatRedis.getUser(room, socket.id) + .then(user => { + + if (user === null) return null + + // If he is already talking + if (user.privateChat) { + + namespace.to(to).emit('leavePrivateRoom', { + to, + privateMessage: `${to} is already talking`, + from: username, + room + }) - return; - } + socket.leave(to, () => { + console.log(`user ${username} force to left the room ${to}`); + }) - userToTalk.privateChat = true + return null; + } - // Notify the user to talk with (in the same main room) - namespace.sockets.in(room).emit('privateChat', { - username, - to - }); + return ChatRedis.setUser(room, socket.id, { + ...user, + privateChat: true + }) + }) + .then(res => { + if (res === null) return + + // Notify the user to talk with (in the same main room) + namespace.sockets.in(room).emit('privateChat', { + username, + to + }); + + }) } }); } @@ -88,20 +102,29 @@ const joinPrivateRoom = (socket, namespace) => ({ username, room, to }) => { const leavePrivateRoom = (socket, namespace) => ({ room, from, to }) => { console.log(`user ${from} wants to leave the private chat with ${to}`); - let usersRoom = users[room]; - let userToTalk = usersRoom.find(user => user.username === to) - - userToTalk.privateChat = false - - namespace.to(to).emit('leavePrivateRoom', { - to, - privateMessage: `${to} has closed the chat`, - from - }) - - socket.leave(to, () => { - console.log(`user ${from} left the private chat with ${to}`); - }) + ChatRedis.getUser(room, socket.id) + .then(user => { + if (user === null) return + + return ChatRedis.setUser(room, socket.id, { + ...user, + privateChat: false + }) + }) + .then(res => { + if (res === null) return + + namespace.to(to).emit('leavePrivateRoom', { + to, + privateMessage: `${to} has closed the chat`, + from + }) + + socket.leave(to, () => { + console.log(`user ${from} left the private chat with ${to}`); + }) + + }) } const privateMessage = (namespace) => ({ privateMessage, to, from, room }) => { diff --git a/server/chat_namespace/index.js b/server/chat_namespace/index.js index fc116ec..d62b03b 100644 --- a/server/chat_namespace/index.js +++ b/server/chat_namespace/index.js @@ -1,5 +1,5 @@ -const config = require('./../config') const events = require('./events.js') +const config = require('./../config') // Socket namespace let namespace; @@ -7,6 +7,8 @@ let namespace; // When connecting const onConnection = (socket) => { + console.log(`Socket connected to port ${config.PORT}`) + // Listening for joining a room socket.on('joinRoom', events.joinRoom(socket, namespace)); diff --git a/server/config/index.js b/server/config/index.js index eb59159..c6517c9 100644 --- a/server/config/index.js +++ b/server/config/index.js @@ -1,7 +1,9 @@ const CONFIG = { PORT: process.env.PORT || 3000, - CHAT_NAMESPACE: '/chat' + CHAT_NAMESPACE: '/chat', + REDIS_HOST: process.env.REDIS_HOST || 'localhost', + REDIS_PORT: process.env.REDIS_PORT || 6379 } module.exports = CONFIG \ No newline at end of file diff --git a/server/index.js b/server/index.js index e7fc75d..59e0f82 100644 --- a/server/index.js +++ b/server/index.js @@ -1,5 +1,7 @@ -const app = require('./app') const http = require('http'); +const redis = require('socket.io-redis'); + +const app = require('./app') const config = require('./config') // Server @@ -8,7 +10,9 @@ const server = http.createServer(app); // Atach server to the socket app.io.attach(server) +// Using the adapter to pass event between nodes +app.io.adapter(redis({ host: config.REDIS_HOST, port: config.REDIS_PORT })); + server.listen(config.PORT, () => { console.log(`Server Listening on port ${config.PORT}`) }); - diff --git a/server/redis/index.js b/server/redis/index.js new file mode 100644 index 0000000..e9472db --- /dev/null +++ b/server/redis/index.js @@ -0,0 +1,93 @@ +const redis = require('redis') +const bluebird = require('bluebird') + +const config = require('./../config/') + +// Using promises +bluebird.promisifyAll(redis); + +function ChatRedis() { + this.client = redis.createClient({ + host: config.REDIS_HOST + }); +} + +/** + * Add user with hash + * @param {} room + * @param {} socketId + * @param {} userObject + */ +ChatRedis.prototype.addUser = function (room, socketId, userObject) { + this.client.hsetAsync(room, socketId, JSON.stringify(userObject)).then( + () => console.debug('addUser', userObject.username + 'added to the room ' + room), + err => console.log('addUser', err) + ); +} + +/** + * Get all users by room + * @param {} room + */ +ChatRedis.prototype.getUsers = function (room) { + return this.client.hgetallAsync(room).then(users => { + const userList = [] + + for (let user in users) { + userList.push(JSON.parse(users[user])) + } + + return userList + }, error => { + console.log('getUsers ', error) + return null + }) +} + +/** + * Deleter a user in a room with socketId + * @param {} room + * @param {} socketId + */ +ChatRedis.prototype.delUser = function(room, socketId){ + return this.client.hdelAsync(room, socketId).then( + res => (res), + err => { + console.log('delUser ', err) + return null + } + ) +} + +/** + * Get user by room and socketId + * @param {} room + * @param {} socketId + */ +ChatRedis.prototype.getUser = function(room, socketId){ + return this.client.hgetAsync(room, socketId).then( + res => JSON.parse(res), + err => { + console.log('getUser ', err) + return null + } + ) +} + +/** + * Set user + * @param {} room + * @param {} socketId + * @param {} newValue + */ +ChatRedis.prototype.setUser = function(room, socketId, newValue){ + return this.client.hsetAsync(room, socketId, JSON.stringify(newValue)).then( + res => res, + err => { + console.log('setUser', err) + return null + } + ) +} + +module.exports = new ChatRedis() diff --git a/server/routes/room.js b/server/routes/room.js index 09d7e1d..bfd2442 100644 --- a/server/routes/room.js +++ b/server/routes/room.js @@ -17,8 +17,8 @@ const rooms = [ ] // route for get rooms -roomRouter.get('/', () => { - console.log('ok') +roomRouter.get('/', (req,res) => { + res.send(rooms) }) module.exports = roomRouter \ No newline at end of file diff --git a/src/assets/scaling.png b/src/assets/scaling.png new file mode 100644 index 0000000..9db2e2c Binary files /dev/null and b/src/assets/scaling.png differ diff --git a/src/main.js b/src/main.js index 231c55a..9863cbb 100644 --- a/src/main.js +++ b/src/main.js @@ -3,18 +3,22 @@ import App from './App.vue' import router from './router' import store from './store' import VueSocketIO from 'vue-socket.io' +import VueResource from 'vue-resource'; import './styles/app.scss' +import { url } from './utils/config' // Socket config Vue.use(new VueSocketIO({ debug: true, - connection: 'http://localhost:3000', + connection: url, vuex: { - store, - actionPrefix: 'SOCKET_', - mutationPrefix: 'SOCKET_' + store, + actionPrefix: 'SOCKET_', + mutationPrefix: 'SOCKET_' } })) +// Vue resource for http +Vue.use(VueResource) Vue.config.productionTip = false diff --git a/src/store.js b/src/store.js index bd8ad27..738e777 100644 --- a/src/store.js +++ b/src/store.js @@ -6,7 +6,8 @@ Vue.use(Vuex) export default new Vuex.Store({ state: { room: undefined, - username: undefined + username: undefined, + rooms: [] }, mutations: { joinRoom(state, { room, username }) { @@ -15,6 +16,9 @@ export default new Vuex.Store({ }, changeRoom(state, room) { state.room = room + }, + setRooms(state, rooms) { + state.rooms = rooms } }, actions: { @@ -23,6 +27,9 @@ export default new Vuex.Store({ }, changeRoom({ commit }, room) { commit('changeRoom', room) + }, + setRooms({ commit }, rooms) { + commit('setRooms', rooms) } } }) diff --git a/src/utils/config.js b/src/utils/config.js new file mode 100644 index 0000000..1cc7c90 --- /dev/null +++ b/src/utils/config.js @@ -0,0 +1 @@ +export const url = `${process.env.VUE_APP_SOCKET_HOST || 'localhost'}:${process.env.VUE_APP_SOCKET_PORT || '3000'}` \ No newline at end of file diff --git a/src/views/Chat.vue b/src/views/Chat.vue index 14e2f1a..82faafa 100644 --- a/src/views/Chat.vue +++ b/src/views/Chat.vue @@ -4,8 +4,7 @@