From 844e8e7fdd30f282e7956d3034405aefd7e2d646 Mon Sep 17 00:00:00 2001 From: Luke Berndt Date: Thu, 12 Jan 2023 22:16:18 -0500 Subject: [PATCH] websocket atleast connects! --- backend/controllers/uploads.js | 422 ++++++++++++++++---------------- backend/index.js | 9 +- backend/package.json | 2 +- frontend/package.json | 3 +- frontend/src/Call/CallPlayer.js | 150 +++++++----- frontend/yarn.lock | 51 +++- 6 files changed, 355 insertions(+), 282 deletions(-) diff --git a/backend/controllers/uploads.js b/backend/controllers/uploads.js index a4d12aa..c77c413 100644 --- a/backend/controllers/uploads.js +++ b/backend/controllers/uploads.js @@ -13,9 +13,9 @@ var secrets = require("../config/secrets"); var util = require("util") var config = require('../config/config.json'); -var s3_endpoint = process.env['S3_ENDPOINT'] != null ? process.env['S3_ENDPOINT'] : 's3.us-west-1.wasabisys.com'; -var s3_bucket = process.env['S3_BUCKET'] != null ? process.env['S3_BUCKET'] : 'openmhz-west'; -var s3_profile = process.env['S3_PROFILE'] != null ? process.env['S3_PROFILE'] : 'wasabi-account'; +var s3_endpoint = process.env['S3_ENDPOINT'] != null ? process.env['S3_ENDPOINT'] : 's3.us-west-1.wasabisys.com'; +var s3_bucket = process.env['S3_BUCKET'] != null ? process.env['S3_BUCKET'] : 'openmhz-west'; +var s3_profile = process.env['S3_PROFILE'] != null ? process.env['S3_PROFILE'] : 'wasabi-account'; const wasabi = require('aws-sdk'); @@ -31,19 +31,19 @@ const wasabiS3 = new wasabi.S3({ async function callLength(file) { - return new Promise((resolve, reject) => { - probe(file, function(err, probeData) { - if (err) { - reject(err); - } else { - resolve(Math.round(probeData.format.duration)); - } - }); - }) + return new Promise((resolve, reject) => { + probe(file, function (err, probeData) { + if (err) { + reject(err); + } else { + resolve(Math.round(probeData.format.duration)); + } + }); + }) } -exports.upload = function(req, res, next) { -process.nextTick(function() { +exports.upload = function (req, res, next) { + process.nextTick(function () { @@ -61,235 +61,235 @@ process.nextTick(function() { res.send("Error, invalid filename:\n"); return; } - var shortName = req.params.shortName.toLowerCase(); - var apiKey = req.body.api_key; + var shortName = req.params.shortName.toLowerCase(); + var apiKey = req.body.api_key; - System.findOne({ - 'shortName': shortName - },["key", "ignoreUnknownTalkgroup"], async function(err, item) { + System.findOne({ + 'shortName': shortName + }, ["key", "ignoreUnknownTalkgroup"], async function (err, item) { - if (err) { - console.warn("[" + req.params.shortName + "] Error /:shortName/upload - Error: " + err); - res.status(500); - res.send("Error, invalid shotName:\n" + err); - return; - } - if (item === null) { - console.warn("[" + req.params.shortName + "] Error /:shortName/upload ShortName does not exist"); - res.status(500); - res.send("ShortName does not exist: " + shortName + "\n"); - return; - } - if (apiKey != item.key) { - console.warn("[" + req.params.shortName + "] Error /:shortName/upload API Key Mismatch - Provided key: " + apiKey); - res.status(500); - res.send("API Keys do not match!\n"); - return; - } + if (err) { + console.warn("[" + req.params.shortName + "] Error /:shortName/upload - Error: " + err); + res.status(500); + res.send("Error, invalid shotName:\n" + err); + return; + } + if (item === null) { + console.warn("[" + req.params.shortName + "] Error /:shortName/upload ShortName does not exist"); + res.status(500); + res.send("ShortName does not exist: " + shortName + "\n"); + return; + } + if (apiKey != item.key) { + console.warn("[" + req.params.shortName + "] Error /:shortName/upload API Key Mismatch - Provided key: " + apiKey); + res.status(500); + res.send("API Keys do not match!\n"); + return; + } - var talkgroupNum = parseInt(req.body.talkgroup_num); - var freq = parseFloat(req.body.freq); - var time = new Date(parseInt(req.body.start_time) * 1000); - var stopTime = new Date(); - if (req.body.stop_time) { - var stopTime = new Date(parseInt(req.body.stop_time) * 1000); - } - var startTime = req.body.start_time; - var emergency = parseInt(req.body.emergency); - + var talkgroupNum = parseInt(req.body.talkgroup_num); + var freq = parseFloat(req.body.freq); + var time = new Date(parseInt(req.body.start_time) * 1000); + var stopTime = new Date(); + if (req.body.stop_time) { + var stopTime = new Date(parseInt(req.body.stop_time) * 1000); + } + var startTime = req.body.start_time; + var emergency = parseInt(req.body.emergency); - if (item.ignoreUnknownTalkgroup == true) { - //console.log("ignore unknown talkgroup "); - //await Talkgroup.findOne({ answer: 42 }).select({ _id: 1 }).lean().then(doc => !!doc) - talkgroupExists = await Talkgroup.exists({ - 'shortName': shortName, - 'num': talkgroupNum - }); - //console.log("In talkgroup: " + talkgroupExists); - //talkgroupExists = await Talkgroup.findOne({ 'shortName': shortName, 'num': talkgroupNum }).select({ _id: 1 }).lean().then(doc => !!doc).exec(); - if (!talkgroupExists) { - try { - fs.unlinkSync(req.file.path) - //file removed - } catch(err) { - console.log("[" + call.shortName + "] error deleting: " + req.file.path); - } - - res.status(500); - res.send("Talkgroup does not exist, skipping.\n"); - return; - + + if (item.ignoreUnknownTalkgroup == true) { + //console.log("ignore unknown talkgroup "); + //await Talkgroup.findOne({ answer: 42 }).select({ _id: 1 }).lean().then(doc => !!doc) + talkgroupExists = await Talkgroup.exists({ + 'shortName': shortName, + 'num': talkgroupNum + }); + //console.log("In talkgroup: " + talkgroupExists); + //talkgroupExists = await Talkgroup.findOne({ 'shortName': shortName, 'num': talkgroupNum }).select({ _id: 1 }).lean().then(doc => !!doc).exec(); + if (!talkgroupExists) { + try { + fs.unlinkSync(req.file.path) + //file removed + } catch (err) { + console.log("[" + call.shortName + "] error deleting: " + req.file.path); } - } - // Add in an API Key check - - try { - - var srcList = JSON.parse(req.body.source_list); - - // Mongoose does not allow 'errors' to be used in the Schema for call. need to rename to error. - var badFreqList = JSON.parse(req.body.freq_list); - var freqList = badFreqList.map(obj =>{ - obj.error = obj.errors; - delete obj.errors; - return obj; - }); - - } catch (err) { - var srcList = []; - var freqList = []; - console.warn("[" + req.params.shortName + "] Error /:shortName/upload Parsing Source/Freq List - Error: " + err); res.status(500); - res.send("Error parsing sourcelist " + err); + res.send("Talkgroup does not exist, skipping.\n"); + return; + } + } + // Add in an API Key check - var base_path = config.mediaDirectory; - var local_path = "/" + shortName + "/" + time.getFullYear() + "/" + (time.getMonth() + 1) + "/" + time.getDate() + "/"; - var target_file = base_path + local_path + path.basename(req.file.originalname); - var object_key = "media/" + shortName + "-" + talkgroupNum + "-" + startTime + ".m4a"; - - var endpoint = s3_endpoint; - var bucket = s3_bucket; - + try { - // If not a pro plan, use S3 storage - var url = 'https://' + s3_endpoint + "/" + s3_bucket + "/" + object_key; - var objectStore = true; - + var srcList = JSON.parse(req.body.source_list); - var call = new Call({ - shortName: shortName, - talkgroupNum: talkgroupNum, - objectKey: object_key, - endpoint: endpoint, - bucket: bucket, - time: time, - name: talkgroupNum + "-" + startTime + ".m4a", - freq: freq, - url: url, - emergency: emergency, - path: local_path, - srcList: srcList, - freqList: freqList, - len: -1 + // Mongoose does not allow 'errors' to be used in the Schema for call. need to rename to error. + var badFreqList = JSON.parse(req.body.freq_list); + var freqList = badFreqList.map(obj => { + obj.error = obj.errors; + delete obj.errors; + return obj; }); - try { - if (req.body.call_length) { - call.len = parseInt(req.body.call_length); - } else { - call.len = (stopTime - time)/1000; //await callLength(req.file.path); - } - res.status(200).end(); + } catch (err) { + var srcList = []; + var freqList = []; + console.warn("[" + req.params.shortName + "] Error /:shortName/upload Parsing Source/Freq List - Error: " + err); + + res.status(500); + res.send("Error parsing sourcelist " + err); + } + + var base_path = config.mediaDirectory; + var local_path = "/" + shortName + "/" + time.getFullYear() + "/" + (time.getMonth() + 1) + "/" + time.getDate() + "/"; + var target_file = base_path + local_path + path.basename(req.file.originalname); + var object_key = "media/" + shortName + "-" + talkgroupNum + "-" + startTime + ".m4a"; + + var endpoint = s3_endpoint; + var bucket = s3_bucket; - if (!objectStore) { - mkdirp.sync(base_path + local_path, function(err) { - if (err) console.error(err); - }); - var src = fs.createReadStream(req.file.path); - var dest = fs.createWriteStream(target_file); - dest.on('unpipe', (src) => { - src.end(); - dest.end(); - }); - src.pipe(dest); - src.unpipe() - src.on('error', function(err) { - console.warn("[" + call.shortName + "] Error /:shortName/upload copying to location - Error: " + err + " Read: " + req.file.path + " Target: " + target_file); - fs.unlink(req.file.path, (err) => { - if (err) - console.log("[" + call.shortName + "] error deleting: " + req.file.path); - }); - res.contentType('json'); - res.send(JSON.stringify({ - success: false, - error: "Upload failed" - })); - }); - src.on('end', function() { - fs.unlink(req.file.path); - }); - } else { - //var spacesSrc = fs.createReadStream(req.file.path); - //var awsSrc = fs.createReadStream(req.file.path); - var wasabiSrc = fs.createReadStream(req.file.path); + // If not a pro plan, use S3 storage + var url = 'https://' + s3_endpoint + "/" + s3_bucket + "/" + object_key; + var objectStore = true; + + var call = new Call({ + shortName: shortName, + talkgroupNum: talkgroupNum, + objectKey: object_key, + endpoint: endpoint, + bucket: bucket, + time: time, + name: talkgroupNum + "-" + startTime + ".m4a", + freq: freq, + url: url, + emergency: emergency, + path: local_path, + srcList: srcList, + freqList: freqList, + len: -1 + }); + + try { + if (req.body.call_length) { + call.len = parseInt(req.body.call_length); + } else { + call.len = (stopTime - time) / 1000; //await callLength(req.file.path); + } + res.status(200).end(); + + + if (!objectStore) { + mkdirp.sync(base_path + local_path, function (err) { + if (err) console.error(err); + }); + var src = fs.createReadStream(req.file.path); + var dest = fs.createWriteStream(target_file); + dest.on('unpipe', (src) => { + src.end(); + dest.end(); + }); + src.pipe(dest); + src.unpipe() + src.on('error', function (err) { + console.warn("[" + call.shortName + "] Error /:shortName/upload copying to location - Error: " + err + " Read: " + req.file.path + " Target: " + target_file); + fs.unlink(req.file.path, (err) => { + if (err) + console.log("[" + call.shortName + "] error deleting: " + req.file.path); + }); + res.contentType('json'); + res.send(JSON.stringify({ + success: false, + error: "Upload failed" + })); + }); + src.on('end', function () { + fs.unlink(req.file.path); + }); + } else { + //var spacesSrc = fs.createReadStream(req.file.path); + //var awsSrc = fs.createReadStream(req.file.path); + var wasabiSrc = fs.createReadStream(req.file.path); + + // call S3 to retrieve upload file to specified bucket + + /* var spacesParams = {Bucket: 'openmhz', Key: object_key, Body: spacesSrc, ACL: 'public-read'}; // call S3 to retrieve upload file to specified bucket - - /* var spacesParams = {Bucket: 'openmhz', Key: object_key, Body: spacesSrc, ACL: 'public-read'}; - // call S3 to retrieve upload file to specified bucket - spacesS3.upload (spacesParams, function (err, data) { - //fs.unlink(req.file.path); - if (err) { - console.log("Spaces Error", err); - } - });*/ - /* - var awsParams = {Bucket: 'openmhz', Key: object_key, Body: awsSrc, ACL: 'public-read'}; - - awsS3.upload (awsParams, function (err, data) { - fs.unlink(req.file.path); + spacesS3.upload (spacesParams, function (err, data) { + //fs.unlink(req.file.path); if (err) { - console.log("AWS Error", err); + console.log("Spaces Error", err); } });*/ + /* + var awsParams = {Bucket: 'openmhz', Key: object_key, Body: awsSrc, ACL: 'public-read'}; - var wasabiParams = { - Bucket: s3_bucket, - Key: object_key, - Body: wasabiSrc, - ACL: 'public-read' - }; + awsS3.upload (awsParams, function (err, data) { + fs.unlink(req.file.path); + if (err) { + console.log("AWS Error", err); + } + });*/ + + var wasabiParams = { + Bucket: s3_bucket, + Key: object_key, + Body: wasabiSrc, + ACL: 'public-read' + }; - //var options = {partSize: 10 * 1024 * 1024, queueSize: 1}; - /*var upload = new wasabiS3.ManagedUpload({ - leavePartsOnError: true, - partSize: 10 * 1024 * 1024, queueSize: 1, - params: wasabiParams - });*/ - //wasabiS3.upload(wasabiParams, options, function(err, data) { - wasabiS3.upload(wasabiParams, function(err, data) { - wasabiSrc.destroy(); - fs.unlink(req.file.path, (err) => { - if (err) { - console.log("[" + call.shortName + "]error deleting: " + req.file.path); - } - call.save(); - sysStats.addCall(call.toObject()); - - // we only want to notify clients if the clip is longer than 1 second. - if (call.len >= 1) { - req.call = call.toObject(); - next(); - } - }); + //var options = {partSize: 10 * 1024 * 1024, queueSize: 1}; + /*var upload = new wasabiS3.ManagedUpload({ + leavePartsOnError: true, + partSize: 10 * 1024 * 1024, queueSize: 1, + params: wasabiParams + });*/ + //wasabiS3.upload(wasabiParams, options, function(err, data) { + wasabiS3.upload(wasabiParams, function (err, data) { + wasabiSrc.destroy(); + fs.unlink(req.file.path, (err) => { if (err) { - console.log("[" + call.shortName + "] " + call.name + " - content-length: " + req.headers['content-length'] + " Wasabi Error", err); + console.log("[" + call.shortName + "]error deleting: " + req.file.path); + } + call.save(); + sysStats.addCall(call.toObject()); + + // we only want to notify clients if the clip is longer than 1 second. + if (call.len >= 1) { + req.call = call.toObject(); + next(); } }); - } - } catch (err) { - console.warn("[" + call.shortName + "] Upload Error: " + err + " Filename: " + call.name + " content-length: " + req.headers['content-length']); - res.status(500); - sysStats.addError(call.toObject()); - res.contentType('json'); - res.send(JSON.stringify({ - success: false, - error: "FFProbe" - })); - fs.unlink(req.file.path, (err) => { - if (err) - console.log("error deleting: " + req.file.path); + if (err) { + console.log("[" + call.shortName + "] " + call.name + " - content-length: " + req.headers['content-length'] + " Wasabi Error", err); + } }); } + } catch (err) { + console.warn("[" + call.shortName + "] Upload Error: " + err + " Filename: " + call.name + " content-length: " + req.headers['content-length']); + res.status(500); + sysStats.addError(call.toObject()); + res.contentType('json'); + res.send(JSON.stringify({ + success: false, + error: "FFProbe" + })); + fs.unlink(req.file.path, (err) => { + if (err) + console.log("error deleting: " + req.file.path); + }); + } - }); }); + }); } diff --git a/backend/index.js b/backend/index.js index 80c0f0e..3783940 100644 --- a/backend/index.js +++ b/backend/index.js @@ -30,12 +30,17 @@ var app = express(); const server = require('http').createServer(app); -const io = require('socket.io')(server, {}); +const io = require('socket.io')(server, { + cors: { + origin: "*", + methods: ["GET", "POST"], + } +}); var Twitter = require('twitter-lite'); -io.origins('*:*'); +//io.origins('*:*'); /* io.origins((origin, callback) => { //if (origin !== 'https://foo.example.com') { diff --git a/backend/package.json b/backend/package.json index b633309..c1a0b6f 100644 --- a/backend/package.json +++ b/backend/package.json @@ -26,7 +26,7 @@ "node-schedule": "1.2.4", "passport": "0.4.0", "passport-local": "1.0.0", - "socket.io": "2.1.1", + "socket.io": "^4.0.0", "twitter-lite": "1.1.0", "websocket": "^1.0.24", "ws": "^3.1.0" diff --git a/frontend/package.json b/frontend/package.json index 8acbda3..ac813ba 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,7 +25,8 @@ "react-scripts": "5.0.1", "redux-thunk": "2.4.2", "semantic-ui-css": "2.5.0", - "semantic-ui-react": "2.1.4" + "semantic-ui-react": "2.1.4", + "socket.io-client": "^4.5.4" }, "scripts": { "start": "react-scripts start", diff --git a/frontend/src/Call/CallPlayer.js b/frontend/src/Call/CallPlayer.js index 3c47ba8..ea44de6 100644 --- a/frontend/src/Call/CallPlayer.js +++ b/frontend/src/Call/CallPlayer.js @@ -26,10 +26,11 @@ import { import "./CallPlayer.css"; //import setupSocket from '../socket/SetupSocket.js' import queryString from '../query-string'; +import io from 'socket.io-client'; +import { setDateFilter } from "./call-actions"; - - +const socket = io(process.env.REACT_APP_BACKEND_SERVER ); // ---------------------------------------------------- @@ -63,7 +64,10 @@ function CallPlayer (props) { const [filterVisible, setFilterVisible] = useState(false); const [groupVisible, setGroupVisible] = useState(false); const [calendarVisible, setCalendarVisible] = useState(false); - + const [isConnected, setIsConnected] = useState(socket.connected); + + + const dispatch = useDispatch(); @@ -86,10 +90,7 @@ function CallPlayer (props) { const handleSidebarToggle = () => setSidebarOpened(!sidebarOpened); const handleFilterToggle = () => setFilterVisible(!filterVisible); const handleCalendarToggle = () => setCalendarVisible(!calendarVisible); - const handleLiveToggle = () => { - dispatch(setLive(!live)); - } - + const audioRef = false; const loadNewerCalls = () => { @@ -121,7 +122,7 @@ function CallPlayer (props) { } -const getFilter = () => { +const getFilterDescription = () => { var filter = { type: 'all', code: "", filterStarred: false }; @@ -141,45 +142,86 @@ const getFilter = () => { } filter.filterStarred = filterStarred return filter; +} + + + const addCall = (data) =>{ + const message = JSON.parse(data) + switch (message.type) { + case 'calls': + /*const refs = this.refs.current; + callActions.addCall(message); + const newCall = callsById[callsAllIds[0]]; + if (!isPlaying && autoPlay) { + this.playCall({ call: newCall }); + if (this.currentCallRef.current) { + this.currentCallRef.current.scrollIntoView({ + behavior: "smooth", + block: "center", + }); + } + }*/ + console.log("Got: " + message); + break + default: + break + } } - /* - const endSocket = () => { - this.socket.removeAllListeners("new message"); - this.socket.removeAllListeners("reconnect"); - } - setupSocket() { - this.socket.on('new message', this.addCall); - this.socket.on('reconnect', (attempts) => { + + useEffect(() => { + socket.on('connect', () => { + setIsConnected(true); + }); + + socket.on('disconnect', () => { + setIsConnected(false); + }); + + socket.on('reconnect', (attempts) => { console.log("Socket Reconnected after attempts: " + attempts); // true + if (live) { - var filter = this.getFilter(); - this.startSocket(shortName, filter.type, filter.code, filter.filterStarred); + this.startSocket(); } }) - } - startSocket(shortName, filterType = "", filterCode = "", filterStarred = false) { - this.socket.emit("start", { - filterCode: filterCode, - filterType: filterType, + + socket.on("new message", addCall); + + return () => { + socket.off('connect'); + socket.off('disconnect'); + socket.off('pong'); + }; + }, []); + + const startSocket = () => { + const filter = getFilterDescription(); + socket.emit("start", { + filterCode: filter.Code, + filterType: filter.Type, filterName: "OpenMHz", - filterStarred: filterStarred, + filterStarred: filter.Starred, shortName: shortName }); } - stopSocket() { - this.socket.emit("stop"); + const stopSocket = () => { + socket.emit("stop"); } - handleLiveToggle() { + const handleLiveToggle = () => { if (!live) { - callActions.setDateFilter(false); - callActions.setLive(false); - this.setState({ callUrl: "", callId: false }); - callActions.fetchCalls(); - var filter = this.getFilter(); - this.startSocket(shortName, filter.type, filter.code, filter.filterStarred); + dispatch(setDateFilter(false)); + dispatch(setLive(true)); + setCallUrl(""); + setCallId(false); + + dispatch(getCalls({})); + + this.startSocket(); } } -*/ + + + /* updateUri(props, state) { var search = "?" @@ -396,11 +438,9 @@ const getFilter = () => { //talkgroupActions.fetchTalkgroups(shortName); //groupActions.fetchGroups(shortName); //systemActions.fetchSystems(); - /*this.setupSocket(); - if (filter.live) { - this.startSocket(shortName, filterType, filterCode, filter.filterStarred); - }*/ + + setStateFromUri(); //console.log("Setting short name to: " + shortName) //dispatch(setShortName(shortName)); @@ -411,9 +451,14 @@ const getFilter = () => { }, []); useEffect(() => { + /* + this.updateUri(nextProps, nextState);*/ dispatch(getCalls({})); + if (live) { + startSocket(); + } //dispatch(getOlderCalls()); - }, [shortName,filterGroupId,filterTalkgroups,filterType,filterStarred]) + }, [shortName,filterGroupId,filterTalkgroups,filterType,filterDate,filterStarred]) @@ -432,33 +477,6 @@ const getFilter = () => { } },[groupsData] ) - useEffect( () => { - /*var typeString = 'all'; - var filterCode = ''; - switch (nextProps.filterType) { - - case 1: - typeString = 'group'; - filterCode = nextProps.filterGroupId; - break; - case 2: - typeString = 'talkgroup'; - filterCode = nextProps.filterTalkgroups; - break; - default: - case 0: - typeString = "all"; - filterCode = ''; - break; - } - - this.updateUri(nextProps, nextState); - if (live) { - this.startSocket(shortName, typeString, filterCode, nextProps.filterStarred); - }*/ - }, [filterType,filterTalkgroups,filterGroupId,filterStarred,filterDate]) - - /* componentWillUpdate(nextProps, nextState) { diff --git a/frontend/yarn.lock b/frontend/yarn.lock index a308473..a6a5ecd 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2430,6 +2430,11 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@socket.io/component-emitter@~3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" + integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== + "@surma/rollup-plugin-off-main-thread@^2.2.3": version "2.2.3" resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz#ee34985952ca21558ab0d952f00298ad2190c053" @@ -4371,7 +4376,7 @@ debug@2.6.9, debug@^2.6.0, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -4691,6 +4696,22 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== +engine.io-client@~6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.2.3.tgz#a8cbdab003162529db85e9de31575097f6d29458" + integrity sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + engine.io-parser "~5.0.3" + ws "~8.2.3" + xmlhttprequest-ssl "~2.0.0" + +engine.io-parser@~5.0.3: + version "5.0.5" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.5.tgz#c6fa52e57d8d2dc68b24754348f779aa6e44f886" + integrity sha512-mjEyaa4zhuuRhaSLOdjEb57X0XPP9JEsnXI4E+ivhwT0GgzUogARx4MqoY1jQyB+4Bkz3BUOmzL7t9RMKmlG3g== + enhanced-resolve@^5.10.0: version "5.12.0" resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.12.0.tgz#300e1c90228f5b570c4d35babf263f6da7155634" @@ -9093,6 +9114,24 @@ smart-buffer@^4.2.0: resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== +socket.io-client@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.4.tgz#d3cde8a06a6250041ba7390f08d2468ccebc5ac9" + integrity sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.2" + engine.io-client "~6.2.3" + socket.io-parser "~4.2.1" + +socket.io-parser@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5" + integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + sockjs@^0.3.24: version "0.3.24" resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" @@ -10304,6 +10343,11 @@ ws@^8.4.2: resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== +ws@~8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" + integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== + xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" @@ -10314,6 +10358,11 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xmlhttprequest-ssl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67" + integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A== + xtend@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"