diff --git a/client/src/App.css b/client/src/App.css index 45cc428..ee94161 100644 --- a/client/src/App.css +++ b/client/src/App.css @@ -26,6 +26,11 @@ height: 603px !important; } +video { + max-width: 200px !important; + max-height: 200px !important; +} + @keyframes App-logo-spin { from { transform: rotate(0deg); diff --git a/client/src/pages/CodeUI.js b/client/src/pages/CodeUI.js index 053e1ff..228cdbc 100644 --- a/client/src/pages/CodeUI.js +++ b/client/src/pages/CodeUI.js @@ -19,10 +19,13 @@ import "codemirror/lib/codemirror.css"; import "codemirror/theme/material.css"; import { theme } from "../themes/theme"; import socket from "../socket/socket"; +import Video from 'twilio-video'; +import ParticipantVideo from "../video-chat/ParticipantVideo"; require("codemirror/mode/xml/xml"); require("codemirror/mode/python/python"); require("codemirror/mode/clike/clike"); require("codemirror/mode/javascript/javascript"); + const codeUIStyle = (theme) => ({ root: { flexGrow: 1, @@ -65,6 +68,14 @@ const codeUIStyle = (theme) => ({ fill: "black", }, }, + remoteParticipant: { + width: 200, + height: 200, + zIndex: 9, + position: 'absolute', + right: 0, + margin: "0 auto", + } }); const qtitle = "Diagonal Difference"; const qdesc = @@ -84,6 +95,9 @@ function CodeUI(props) { const [runResult, setrunResult] = useState(null); const [inRoom, setInRoom] = useState(false); const history = useHistory(); + const [videoRoom, setVideoRoom] = useState(null); + const [videoParticipants, setVideoParticipants] = useState([]); + const [token, setToken] = useState(null); const [language, setLanguage] = useState("text/x-python"); useEffect(() => { @@ -118,6 +132,60 @@ function CodeUI(props) { }); }, []); + //For video chat room creation + useEffect(() => { + fetch(`/video/token/${props.match.params.id}`, { + method: 'POST', + body: JSON.stringify({}), + headers: { 'Content-Type': 'application/json' } + }) + .then(res => res.json()) + .then(data => setToken(data.token)); + },[]); + + //For video chat connection + useEffect(() => { + if (token) { + const participantConnected = participant => { + setVideoParticipants(prevParticipants => [...prevParticipants, participant]); + }; + + const participantDisconnected = participant => { + setVideoParticipants(prevParticipants => + prevParticipants.filter(p => p !== participant) + ); + }; + + Video.connect(token, { + name: props.match.params.id + }).then(videoRoom => { + setVideoRoom(videoRoom); + videoRoom.on('participantConnected', participantConnected); + videoRoom.on('participantDisconnected', participantDisconnected); + videoRoom.participants.forEach(participantConnected); + }); + + return () => { + setVideoRoom(currentRoom => { + if (currentRoom && currentRoom.localParticipant.state === 'connected') { + currentRoom.localParticipant.tracks.forEach(function(trackPublication) { + trackPublication.track.stop(); + }); + currentRoom.disconnect(); + return null; + } else { + return currentRoom; + } + }); + }; + } + }, [props.match.params.id, token]); + + //For video chat other participant video + const remoteParticipants = videoParticipants.map(participant => ( + + )); + const handleInRoom = () => { inRoom ? setInRoom(false) : setInRoom(true); }; @@ -142,6 +210,7 @@ function CodeUI(props) { fetch(`/interviews/endInterview/${props.match.params.id}`) .then((res) => socket.emit("endInterview", { id: props.match.params.id })) .catch((err) => console.log(err)); + setToken(null);//For video-chat logout } const changeLanguage = (lang) => { @@ -203,6 +272,8 @@ function CodeUI(props) { C C++ +
{remoteParticipants}
+ {/*
I want this above
*/} {}} + style={{position: 'absolute'}} /> + {/*
{remoteParticipants}
*/} diff --git a/client/src/video-chat/ParticipantVideo.js b/client/src/video-chat/ParticipantVideo.js new file mode 100644 index 0000000..f21b9bb --- /dev/null +++ b/client/src/video-chat/ParticipantVideo.js @@ -0,0 +1,83 @@ +import React, { useState, useEffect, useRef } from "react"; +import { makeStyles } from "@material-ui/core/styles"; + +const useStyles = makeStyles((theme) => ({ + screen: { + maxWidth: "200px", + maxHeight: "200px" + } +})); + +const ParticipantVideo = ({ participant }) => { + const classes = useStyles(); + const [videoTracks, setVideoTracks] = useState([]); + const [audioTracks, setAudioTracks] = useState([]); + + const videoRef = useRef(); + const audioRef = useRef(); + + const trackpubsToTracks = (trackMap) => + Array.from(trackMap.values()) + .map((publication) => publication.track) + .filter((track) => track !== null); + + useEffect(() => { + setVideoTracks(trackpubsToTracks(participant.videoTracks)); + setAudioTracks(trackpubsToTracks(participant.audioTracks)); + + const trackSubscribed = (track) => { + if (track.kind === "video") { + setVideoTracks((videoTracks) => [...videoTracks, track]); + } else if (track.kind === "audio") { + setAudioTracks((audioTracks) => [...audioTracks, track]); + } + }; + + const trackUnsubscribed = (track) => { + if (track.kind === "video") { + setVideoTracks((videoTracks) => videoTracks.filter((v) => v !== track)); + } else if (track.kind === "audio") { + setAudioTracks((audioTracks) => audioTracks.filter((a) => a !== track)); + } + }; + + participant.on("trackSubscribed", trackSubscribed); + participant.on("trackUnsubscribed", trackUnsubscribed); + + return () => { + setVideoTracks([]); + setAudioTracks([]); + participant.removeAllListeners(); + }; + }, [participant]); + + useEffect(() => { + const videoTrack = videoTracks[0]; + if (videoTrack) { + videoTrack.attach(videoRef.current); + return () => { + videoTrack.detach(); + }; + } + }, [videoTracks]); + + useEffect(() => { + const audioTrack = audioTracks[0]; + if (audioTrack) { + audioTrack.attach(audioRef.current); + return () => { + audioTrack.detach(); + }; + } + }, [audioTracks]); + + return ( +
+

{participant.identity}

+
+ ); +}; + +export default ParticipantVideo; diff --git a/server/app.js b/server/app.js index b437247..c3be4fc 100644 --- a/server/app.js +++ b/server/app.js @@ -8,6 +8,7 @@ const interviewRouter = require("./interview/interview.controller"); const accountRouter = require("./users/users.controller"); const questionRouter = require("./question/question.controller"); const runCode = require("./helpers/runcode"); +const videoRouter = require("./video-chat/videoChatApis"); const { json, urlencoded } = express; @@ -23,6 +24,7 @@ app.use("/", accountRouter); app.use("/users", require("./users/users.controller")); app.use("/interviews", interviewRouter); app.use("/questions", questionRouter); +app.use("/video", videoRouter); // catch 404 and forward to error handler app.use(function (req, res, next) { diff --git a/server/package-lock.json b/server/package-lock.json index 5e6b217..9838a14 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -4,10 +4,14 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "@twilio/webrtc": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@twilio/webrtc/-/webrtc-4.3.1.tgz", - "integrity": "sha512-T9Slk8TyKrKqjc4PH4aPknjmEozny/M1TzVyfRRyEQSt548hXXhLYrSp3nUcK49bfzVrgbYuRj6ojExYh2qLqw==" + "@types/body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } }, "@types/chai": { "version": "4.1.7", @@ -15,17 +19,69 @@ "integrity": "sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==", "dev": true }, + "@types/connect": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", + "requires": { + "@types/node": "*" + } + }, "@types/cookiejar": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.1.tgz", "integrity": "sha512-aRnpPa7ysx3aNW60hTiCtLHlQaIFsXFCgQlpakNgDNVFzbtusSY8PwjAQgRWfSk0ekNoBjO51eQRB6upA9uuyw==", "dev": true }, + "@types/express": { + "version": "4.17.7", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.7.tgz", + "integrity": "sha512-dCOT5lcmV/uC2J9k0rPafATeeyz+99xTt54ReX11/LObZgfzJqZNcW27zGhYyX+9iSEGXGt5qLPwRSvBZcLvtQ==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.9.tgz", + "integrity": "sha512-DG0BYg6yO+ePW+XoDENYz8zhNGC3jDDEpComMYn7WJc4mY1Us8Rw9ax2YhJXxpyk2SF47PQAoQ0YyVT1a0bEkA==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/mime": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.2.tgz", + "integrity": "sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q==" + }, "@types/node": { "version": "12.6.8", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.6.8.tgz", - "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==", - "dev": true + "integrity": "sha512-aX+gFgA5GHcDi89KG5keey2zf0WfZk/HAQotEamsK2kbey+8yGKcson0hbK8E+v0NArlCJQCqMP161YhV6ZXLg==" + }, + "@types/qs": { + "version": "6.9.3", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.3.tgz", + "integrity": "sha512-7s9EQWupR1fTc2pSMtXRQ9w9gLOcrJn+h7HOXw4evxyvVqMi4f+q7d2tnFe3ng3SNHjtK+0EzGMGFUQX4/AQRA==" + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" + }, + "@types/serve-static": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.4.tgz", + "integrity": "sha512-jTDt0o/YbpNwZbQmE/+2e+lfjJEJJR0I3OFaKQKPWkASkCoW3i6fsUnqudSMcNAfbtmADGu8f4MV4q+GqULmug==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } }, "@types/superagent": { "version": "3.8.7", @@ -141,6 +197,11 @@ "resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz", "integrity": "sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==" }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -173,19 +234,19 @@ "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" }, + "axios": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.2.tgz", + "integrity": "sha512-fjgm5MvRHLhx+osE2xoekY70AhARk3a6hkN+3Io1jc00jtquGvxYlKlsFUhmUET0V5te6CcZI7lcv2Ym61mjHA==", + "requires": { + "follow-redirects": "1.5.10" + } + }, "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, - "backoff": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", - "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", - "requires": { - "precond": "0.2" - } - }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -664,6 +725,11 @@ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz", "integrity": "sha1-ojD2T1aDEOFJgAmUB5DsmVRbyn4=" }, + "dayjs": { + "version": "1.8.30", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.30.tgz", + "integrity": "sha512-5s5IGuP5bVvIbOWkEDcfmXsUj24fZW1NMHVVSdSFF/kW8d+alZcI9SpBKC+baEyBe+z3fUp17y75ulstv5swUw==" + }, "debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -1185,6 +1251,24 @@ } } }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + } + } + }, "for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", @@ -3010,16 +3094,16 @@ "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" }, + "pop-iterate": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pop-iterate/-/pop-iterate-1.0.1.tgz", + "integrity": "sha1-zqz9q0q/NT16DyqqLB/Hs/lBO6M=" + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" }, - "precond": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", - "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" - }, "prepend-http": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", @@ -3059,11 +3143,26 @@ "once": "^1.3.1" } }, + "q": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/q/-/q-2.0.3.tgz", + "integrity": "sha1-dbjbAlWhpa+C9Yw/Oqoe/sfQ0TQ=", + "requires": { + "asap": "^2.0.0", + "pop-iterate": "^1.0.1", + "weak-map": "^1.0.5" + } + }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" }, + "querystringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", + "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==" + }, "range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -3182,6 +3281,11 @@ "semver": "^5.1.0" } }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=" + }, "resolve-from": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", @@ -3197,6 +3301,11 @@ "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" }, + "rootpath": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/rootpath/-/rootpath-0.1.2.tgz", + "integrity": "sha1-Wzeah9ypBum5HWkKWZQ5vvJn6ms=" + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -3224,6 +3333,11 @@ "sparse-bitfield": "^3.0.3" } }, + "scmp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz", + "integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==" + }, "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", @@ -3775,26 +3889,33 @@ "nopt": "~1.0.10" } }, - "twilio-video": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/twilio-video/-/twilio-video-2.7.0.tgz", - "integrity": "sha512-juAMupRjBGh1pqVvE9CwpMGoGyJOlmJbzG2C3TstK1bAcHDLFUvFkBlNwL+ubIUTipX0nrrxzVfcQrEI3wWHqA==", - "requires": { - "@twilio/webrtc": "4.3.1", - "backoff": "^2.5.0", - "ws": "^3.3.1", - "xmlhttprequest": "^1.8.0" + "twilio": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/twilio/-/twilio-3.48.0.tgz", + "integrity": "sha512-FDPCze9PxPF88pULND7ZfaVkZtAGh8i0Xo1ZpuW6aOzd4SRGplJBZSrx4IiH1q56di+yKQ6ABw2uXb5rRH+rbQ==", + "requires": { + "@types/express": "^4.17.7", + "axios": "^0.19.2", + "dayjs": "^1.8.29", + "jsonwebtoken": "^8.5.1", + "lodash": "^4.17.19", + "q": "2.0.x", + "qs": "^6.9.4", + "rootpath": "^0.1.2", + "scmp": "^2.1.0", + "url-parse": "^1.4.7", + "xmlbuilder": "^13.0.2" }, "dependencies": { - "ws": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", - "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", - "requires": { - "async-limiter": "~1.0.0", - "safe-buffer": "~5.1.0", - "ultron": "~1.1.0" - } + "lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" + }, + "qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ==" } } }, @@ -3813,11 +3934,6 @@ "mime-types": "~2.1.24" } }, - "ultron": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", - "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" - }, "undefsafe": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.2.tgz", @@ -3918,6 +4034,15 @@ "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" }, + "url-parse": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", + "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "url-parse-lax": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", @@ -3951,6 +4076,11 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "weak-map": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/weak-map/-/weak-map-1.0.5.tgz", + "integrity": "sha1-eWkVhNmGB/UHC9O3CkDmuyLkAes=" + }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -4055,10 +4185,10 @@ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-3.0.0.tgz", "integrity": "sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=" }, - "xmlhttprequest": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", - "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + "xmlbuilder": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz", + "integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==" }, "xmlhttprequest-ssl": { "version": "1.5.5", diff --git a/server/package.json b/server/package.json index d015d69..0991dd1 100644 --- a/server/package.json +++ b/server/package.json @@ -22,7 +22,7 @@ "morgan": "~1.9.1", "nodemon": "^1.19.1", "socket.io": "^2.3.0", - "twilio-video": "^2.7.0" + "twilio": "^3.48.0" }, "devDependencies": { "chai": "^4.2.0", diff --git a/server/video-chat/twilioTokens.js b/server/video-chat/twilioTokens.js new file mode 100644 index 0000000..4c70ca8 --- /dev/null +++ b/server/video-chat/twilioTokens.js @@ -0,0 +1,26 @@ +const twilio = require('twilio'); +const AccessToken = twilio.jwt.AccessToken; +const { VideoGrant } = AccessToken; + +const generateToken = () => { + return new AccessToken( + process.env.TWILIO_ACCOUNT_SID, + process.env.TWILIO_API_KEY, + process.env.TWILIO_API_SECRET + ); +}; + +const videoToken = (identity, room) => { + let videoGrant; + if (typeof room !== 'undefined') { + videoGrant = new VideoGrant({ room }); + } else { + videoGrant = new VideoGrant(); + } + const token = generateToken(); + token.addGrant(videoGrant); + token.identity = identity; + return token; +}; + +module.exports = { videoToken }; diff --git a/server/video-chat/videoChatApis.js b/server/video-chat/videoChatApis.js new file mode 100644 index 0000000..1b47b76 --- /dev/null +++ b/server/video-chat/videoChatApis.js @@ -0,0 +1,33 @@ +const express = require("express"); +const router = express.Router(); +const { videoToken } = require('./twilioTokens'); +const verifyToken = require("../helpers/verifyToken"); + +const sendTokenResponse = (token, res) => { + res.set('Content-Type', 'application/json'); + res.send( + JSON.stringify({ + token: token.toJwt() + }) + ); + }; + +router.get('/token/:interviewId', verifyToken, (req, res) => { + const user = req.user; + const interviewId = req.params.interviewId; + const identity = user.id; + const room = interviewId; + const token = videoToken(identity, room); + sendTokenResponse(token, res); + }); + +router.post('/token/:interviewId', verifyToken, (req, res) => { + const user = req.user; + const interviewId = req.params.interviewId; + const identity = user.id; + const room = interviewId; + const token = videoToken(identity, room); + sendTokenResponse(token, res); + }); + + module.exports = router; \ No newline at end of file