diff --git a/src/fireedge/package-lock.json b/src/fireedge/package-lock.json
index 916f89acbb..d2d67ff4d1 100644
--- a/src/fireedge/package-lock.json
+++ b/src/fireedge/package-lock.json
@@ -47,6 +47,7 @@
"fast-xml-parser": "3.19.0",
"fs-extra": "9.0.1",
"fuse.js": "6.4.1",
+ "guacamole-common-js": "1.3.1",
"helmet": "4.1.1",
"http": "0.0.1-security",
"http-proxy-middleware": "1.0.5",
@@ -220,9 +221,9 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz",
- "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==",
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.3.tgz",
+ "integrity": "sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==",
"dependencies": {
"@babel/types": "^7.17.0",
"jsesc": "^2.5.1",
@@ -273,9 +274,9 @@
}
},
"node_modules/@babel/helper-create-class-features-plugin": {
- "version": "7.17.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz",
- "integrity": "sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ==",
+ "version": "7.17.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz",
+ "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.16.7",
"@babel/helper-environment-visitor": "^7.16.7",
@@ -405,9 +406,9 @@
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.16.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz",
- "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==",
+ "version": "7.17.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.6.tgz",
+ "integrity": "sha512-2ULmRdqoOMpdvkbT8jONrZML/XALfzxlb052bldftkicAUy8AxSCkD5trDPQcwHNmolcl7wP6ehNqMlyUw6AaA==",
"dependencies": {
"@babel/helper-environment-visitor": "^7.16.7",
"@babel/helper-module-imports": "^7.16.7",
@@ -415,8 +416,8 @@
"@babel/helper-split-export-declaration": "^7.16.7",
"@babel/helper-validator-identifier": "^7.16.7",
"@babel/template": "^7.16.7",
- "@babel/traverse": "^7.16.7",
- "@babel/types": "^7.16.7"
+ "@babel/traverse": "^7.17.3",
+ "@babel/types": "^7.17.0"
},
"engines": {
"node": ">=6.9.0"
@@ -533,9 +534,9 @@
}
},
"node_modules/@babel/helpers": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.0.tgz",
- "integrity": "sha512-Xe/9NFxjPwELUvW2dsukcMZIp6XwPSbI4ojFBJuX5ramHuVE22SVcZIwqzdWo5uCgeTXW8qV97lMvSOjq+1+nQ==",
+ "version": "7.17.2",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.2.tgz",
+ "integrity": "sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==",
"dependencies": {
"@babel/template": "^7.16.7",
"@babel/traverse": "^7.17.0",
@@ -581,9 +582,9 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz",
- "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw==",
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.3.tgz",
+ "integrity": "sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA==",
"bin": {
"parser": "bin/babel-parser.js"
},
@@ -655,11 +656,11 @@
}
},
"node_modules/@babel/plugin-proposal-class-static-block": {
- "version": "7.16.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz",
- "integrity": "sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==",
+ "version": "7.17.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz",
+ "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==",
"dependencies": {
- "@babel/helper-create-class-features-plugin": "^7.16.7",
+ "@babel/helper-create-class-features-plugin": "^7.17.6",
"@babel/helper-plugin-utils": "^7.16.7",
"@babel/plugin-syntax-class-static-block": "^7.14.5"
},
@@ -1127,9 +1128,9 @@
}
},
"node_modules/@babel/plugin-transform-destructuring": {
- "version": "7.16.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz",
- "integrity": "sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A==",
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.3.tgz",
+ "integrity": "sha512-dDFzegDYKlPqa72xIlbmSkly5MluLoaC1JswABGktyt6NTXSBcUuse/kWE/wvKFWJHPETpi158qJZFS3JmykJg==",
"dependencies": {
"@babel/helper-plugin-utils": "^7.16.7"
},
@@ -1394,15 +1395,15 @@
}
},
"node_modules/@babel/plugin-transform-react-jsx": {
- "version": "7.16.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz",
- "integrity": "sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag==",
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz",
+ "integrity": "sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.16.7",
"@babel/helper-module-imports": "^7.16.7",
"@babel/helper-plugin-utils": "^7.16.7",
"@babel/plugin-syntax-jsx": "^7.16.7",
- "@babel/types": "^7.16.7"
+ "@babel/types": "^7.17.0"
},
"engines": {
"node": ">=6.9.0"
@@ -1707,9 +1708,9 @@
}
},
"node_modules/@babel/runtime": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.0.tgz",
- "integrity": "sha512-etcO/ohMNaNA2UBdaXBBSX/3aEzFMRrVfaPv8Ptc0k+cWpWW0QFiGZ2XnVqQZI1Cf734LbPGmqBKWESfW4x/dQ==",
+ "version": "7.17.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
+ "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
"dependencies": {
"regenerator-runtime": "^0.13.4"
},
@@ -1718,9 +1719,9 @@
}
},
"node_modules/@babel/runtime-corejs3": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.0.tgz",
- "integrity": "sha512-qeydncU80ravKzovVncW3EYaC1ji3GpntdPgNcJy9g7hHSY6KX+ne1cbV3ov7Zzm4F1z0+QreZPCuw1ynkmYNg==",
+ "version": "7.17.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz",
+ "integrity": "sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==",
"dev": true,
"dependencies": {
"core-js-pure": "^3.20.2",
@@ -1744,17 +1745,17 @@
}
},
"node_modules/@babel/traverse": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz",
- "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==",
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz",
+ "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==",
"dependencies": {
"@babel/code-frame": "^7.16.7",
- "@babel/generator": "^7.17.0",
+ "@babel/generator": "^7.17.3",
"@babel/helper-environment-visitor": "^7.16.7",
"@babel/helper-function-name": "^7.16.7",
"@babel/helper-hoist-variables": "^7.16.7",
"@babel/helper-split-export-declaration": "^7.16.7",
- "@babel/parser": "^7.17.0",
+ "@babel/parser": "^7.17.3",
"@babel/types": "^7.17.0",
"debug": "^4.1.0",
"globals": "^11.1.0"
@@ -1775,10 +1776,18 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
"node_modules/@dabh/diagnostics": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz",
- "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
+ "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
"dependencies": {
"colorspace": "1.1.x",
"enabled": "2.0.x",
@@ -1855,9 +1864,9 @@
}
},
"node_modules/@discoveryjs/json-ext": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz",
- "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==",
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
+ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==",
"engines": {
"node": ">=10.0.0"
}
@@ -1902,9 +1911,9 @@
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
},
"node_modules/@emotion/is-prop-valid": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.1.tgz",
- "integrity": "sha512-bW1Tos67CZkOURLc0OalnfxtSXQJMrAMV0jZTVGJUPSOd4qgjF3+tTD5CwJM13PHA8cltGW1WGbbvV9NpvUZPw==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz",
+ "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==",
"dependencies": {
"@emotion/memoize": "^0.7.4"
}
@@ -1988,9 +1997,9 @@
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
},
"node_modules/@emotion/utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz",
- "integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA=="
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
+ "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
},
"node_modules/@emotion/weak-memoize": {
"version": "0.2.5",
@@ -2212,6 +2221,7 @@
"version": "5.0.0-alpha.54",
"resolved": "https://registry.npmjs.org/@mui/core/-/core-5.0.0-alpha.54.tgz",
"integrity": "sha512-8TxdHqDdSb6wjhsnpE5n7qtkFKDG3PUSlVY0gR3VcdsHXscUY13l3VbMQW1brI4D/R9zx5VYmxIHWaHFgw4RtA==",
+ "deprecated": "You can now upgrade to @mui/base. See https://github.com/mui/material-ui/releases/tag/v5.1.1",
"dependencies": {
"@babel/runtime": "^7.16.0",
"@emotion/is-prop-valid": "^1.1.0",
@@ -2295,17 +2305,17 @@
}
},
"node_modules/@mui/lab/node_modules/@mui/system": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.4.0.tgz",
- "integrity": "sha512-LX7g5gK5yCwiueSUVG73uVNc0yeHjsWUIFLrnPjP3m+J7O38RkPqyao5nZahhaSL1PGNbR9+zfkxljXthO9QqA==",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.5.0.tgz",
+ "integrity": "sha512-zFOfERv3Y4m5ehwTRR9cGaPuMvlD2qVXmFKC60P0Gte3aD6vYObyNriZv+mDVGlhDxZTZhxBrNPH3ns25xSFtQ==",
"dependencies": {
- "@babel/runtime": "^7.16.7",
- "@mui/private-theming": "^5.3.0",
- "@mui/styled-engine": "^5.3.0",
- "@mui/types": "^7.1.0",
- "@mui/utils": "^5.3.0",
+ "@babel/runtime": "^7.17.2",
+ "@mui/private-theming": "^5.4.4",
+ "@mui/styled-engine": "^5.4.4",
+ "@mui/types": "^7.1.2",
+ "@mui/utils": "^5.4.4",
"clsx": "^1.1.1",
- "csstype": "^3.0.10",
+ "csstype": "^3.0.11",
"prop-types": "^15.7.2"
},
"engines": {
@@ -2418,12 +2428,12 @@
}
},
"node_modules/@mui/private-theming": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.3.0.tgz",
- "integrity": "sha512-EBobUEyM9fMnteKrVPp8pTMUh81xXakyfdpkoh7Y19q9JpD2eh7QGAQVJVj0JBFlcUJD60NIE4K8rdokrRmLwg==",
+ "version": "5.4.4",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.4.4.tgz",
+ "integrity": "sha512-V/gxttr6736yJoU9q+4xxXsa0K/w9Hn9pg99zsOHt7i/O904w2CX5NHh5WqDXtoUzVcayLF0RB17yr6l79CE+A==",
"dependencies": {
- "@babel/runtime": "^7.16.7",
- "@mui/utils": "^5.3.0",
+ "@babel/runtime": "^7.17.2",
+ "@mui/utils": "^5.4.4",
"prop-types": "^15.7.2"
},
"engines": {
@@ -2444,11 +2454,11 @@
}
},
"node_modules/@mui/styled-engine": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.3.0.tgz",
- "integrity": "sha512-I4YemFy9WnCLUdZ5T+6egpzc8e7Jq/uh9AJ3QInZHbyNu/9I2SWvNn7vHjWOT/D8Y8LMzIOhu5WwZbzjez7YRw==",
+ "version": "5.4.4",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.4.4.tgz",
+ "integrity": "sha512-AKx3rSgB6dmt5f7iP4K18mLFlE5/9EfJe/5EH9Pyqez8J/CPkTgYhJ/Va6qtlrcunzpui+uG/vfuf04yAZekSg==",
"dependencies": {
- "@babel/runtime": "^7.16.7",
+ "@babel/runtime": "^7.17.2",
"@emotion/cache": "^11.7.1",
"prop-types": "^15.7.2"
},
@@ -2553,9 +2563,9 @@
}
},
"node_modules/@mui/types": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.1.0.tgz",
- "integrity": "sha512-Hh7ALdq/GjfIwLvqH3XftuY3bcKhupktTm+S6qRIDGOtPtRuq2L21VWzOK4p7kblirK0XgGVH5BLwa6u8z/6QQ==",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.1.2.tgz",
+ "integrity": "sha512-SD7O1nVzqG+ckQpFjDhXPZjRceB8HQFHEvdLLrPhlJy4lLbwEBbxK74Tj4t6Jgk0fTvLJisuwOutrtYe9P/xBQ==",
"peerDependencies": {
"@types/react": "*"
},
@@ -2566,11 +2576,11 @@
}
},
"node_modules/@mui/utils": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.3.0.tgz",
- "integrity": "sha512-O/E9IQKPMg0OrN7+gkn7Ga5o5WA2iXQGdyqNBFPNrYzxOvwzsEtM5K7MtTCGGYKFe8mhTRM0ZOjh5OM0dglw+Q==",
+ "version": "5.4.4",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.4.4.tgz",
+ "integrity": "sha512-hfYIXEuhc2mXMGN5nUPis8beH6uE/zl3uMWJcyHX0/LN/+QxO9zhYuV6l8AsAaphHFyS/fBv0SW3Nid7jw5hKQ==",
"dependencies": {
- "@babel/runtime": "^7.16.7",
+ "@babel/runtime": "^7.17.2",
"@types/prop-types": "^15.7.4",
"@types/react-is": "^16.7.1 || ^17.0.0",
"prop-types": "^15.7.2",
@@ -2690,9 +2700,9 @@
}
},
"node_modules/@popperjs/core": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz",
- "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==",
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.4.tgz",
+ "integrity": "sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/popperjs"
@@ -2915,9 +2925,9 @@
}
},
"node_modules/@types/d3-path": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-2.0.1.tgz",
- "integrity": "sha512-6K8LaFlztlhZO7mwsZg7ClRsdLg3FJRzIIi6SZXDWmmSJc2x8dd2VkESbLXdk3p8cuvz71f36S0y8Zv2AxqvQw=="
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-2.0.2.tgz",
+ "integrity": "sha512-3YHpvDw9LzONaJzejXLOwZ3LqwwkoXb9LI2YN7Hbd6pkGo5nIlJ09ul4bQhBN4hQZJKmUpX8HkVqbzgUKY48cg=="
},
"node_modules/@types/d3-polygon": {
"version": "2.0.1",
@@ -3049,14 +3059,14 @@
"dev": true
},
"node_modules/@types/lodash": {
- "version": "4.14.178",
- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz",
- "integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw=="
+ "version": "4.14.179",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
+ "integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w=="
},
"node_modules/@types/node": {
- "version": "17.0.15",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.15.tgz",
- "integrity": "sha512-zWt4SDDv1S9WRBNxLFxFRHxdD9tvH8f5/kg5/IaLFdnSNXsDY4eL3Q3XXN+VxUnWIhyVFDwcsmAprvwXoM/ClA=="
+ "version": "17.0.21",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz",
+ "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ=="
},
"node_modules/@types/parse-json": {
"version": "4.0.0",
@@ -3069,9 +3079,9 @@
"integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
},
"node_modules/@types/react": {
- "version": "17.0.39",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz",
- "integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==",
+ "version": "17.0.40",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.40.tgz",
+ "integrity": "sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==",
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@@ -3087,9 +3097,9 @@
}
},
"node_modules/@types/react-redux": {
- "version": "7.1.22",
- "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.22.tgz",
- "integrity": "sha512-GxIA1kM7ClU73I6wg9IRTVwSO9GS+SAKZKe0Enj+82HMU6aoESFU2HNAdNi3+J53IaOHPiUfT3kSG4L828joDQ==",
+ "version": "7.1.23",
+ "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.23.tgz",
+ "integrity": "sha512-D02o3FPfqQlfu2WeEYwh3x2otYd2Dk1o8wAfsA0B1C2AJEFxE663Ozu7JzuWbznGgW248NaOF6wsqCGNq9d3qw==",
"dependencies": {
"@types/hoist-non-react-statics": "^3.3.0",
"@types/react": "*",
@@ -4107,14 +4117,14 @@
}
},
"node_modules/browserslist": {
- "version": "4.19.1",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz",
- "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==",
+ "version": "4.20.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.0.tgz",
+ "integrity": "sha512-bnpOoa+DownbciXj0jVGENf8VYQnE2LNWomhYuCsMmmx9Jd9lwq0WXODuwpSsp8AVdKM2/HorrzxAfbKvWTByQ==",
"dependencies": {
- "caniuse-lite": "^1.0.30001286",
- "electron-to-chromium": "^1.4.17",
+ "caniuse-lite": "^1.0.30001313",
+ "electron-to-chromium": "^1.4.76",
"escalade": "^3.1.1",
- "node-releases": "^2.0.1",
+ "node-releases": "^2.0.2",
"picocolors": "^1.0.0"
},
"bin": {
@@ -4240,9 +4250,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001307",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001307.tgz",
- "integrity": "sha512-+MXEMczJ4FuxJAUp0jvAl6Df0NI/OfW1RWEE61eSmzS7hw6lz4IKutbhbXendwq8BljfFuHtu26VWsg4afQ7Ng==",
+ "version": "1.0.30001316",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001316.tgz",
+ "integrity": "sha512-JgUdNoZKxPZFzbzJwy4hDSyGuH/gXz2rN51QmoR8cBQsVo58llD3A0vlRKKRt8FGf5u69P9eQyIH8/z9vN/S0Q==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/browserslist"
@@ -4404,14 +4414,6 @@
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
"integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="
},
- "node_modules/colors": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
- "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==",
- "engines": {
- "node": ">=0.1.90"
- }
- },
"node_modules/colorspace": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
@@ -4650,9 +4652,9 @@
}
},
"node_modules/core-js-compat": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.0.tgz",
- "integrity": "sha512-OSXseNPSK2OPJa6GdtkMz/XxeXx8/CJvfhQWTqd6neuUraujcL4jVsjkLQz1OWnax8xVQJnRPe0V2jqNWORA+A==",
+ "version": "3.21.1",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz",
+ "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==",
"dependencies": {
"browserslist": "^4.19.1",
"semver": "7.0.0"
@@ -4671,9 +4673,9 @@
}
},
"node_modules/core-js-pure": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.0.tgz",
- "integrity": "sha512-VaJUunCZLnxuDbo1rNOzwbet9E1K9joiXS5+DQMPtgxd24wfsZbJZMMfQLGYMlCUvSxLfsRUUhoOR2x28mFfeg==",
+ "version": "3.21.1",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz",
+ "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==",
"dev": true,
"hasInstallScript": true,
"funding": {
@@ -4877,9 +4879,9 @@
}
},
"node_modules/csstype": {
- "version": "3.0.10",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
- "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
+ "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
},
"node_modules/d3-color": {
"version": "2.0.0",
@@ -5192,9 +5194,9 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"node_modules/electron-to-chromium": {
- "version": "1.4.65",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.65.tgz",
- "integrity": "sha512-0/d8Skk8sW3FxXP0Dd6MnBlrwx7Qo9cqQec3BlIAlvKnrmS3pHsIbaroEi+nd0kZkGpQ6apMEre7xndzjlEnLw=="
+ "version": "1.4.82",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.82.tgz",
+ "integrity": "sha512-Ks+ANzLoIrFDUOJdjxYMH6CMKB8UQo5modAwvSZTxgF+vEs/U7G5IbWFUp6dS4klPkTDVdxbORuk8xAXXhMsWw=="
},
"node_modules/elliptic": {
"version": "6.5.4",
@@ -5253,9 +5255,9 @@
}
},
"node_modules/engine.io": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz",
- "integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==",
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz",
+ "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==",
"dependencies": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
@@ -5265,7 +5267,7 @@
"cookie": "~0.4.1",
"cors": "~2.8.5",
"debug": "~4.3.1",
- "engine.io-parser": "~5.0.0",
+ "engine.io-parser": "~5.0.3",
"ws": "~8.2.3"
},
"engines": {
@@ -5393,9 +5395,9 @@
}
},
"node_modules/error-stack-parser": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz",
- "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==",
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.7.tgz",
+ "integrity": "sha512-chLOW0ZGRf4s8raLrDxa5sdkvPec5YdvwbFnqJme4rk0rFajP8mPtrDL1+I+CwrQDCjswDA5sREX7jYQDQs9vA==",
"dev": true,
"dependencies": {
"stackframe": "^1.1.1"
@@ -6694,9 +6696,9 @@
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
},
"node_modules/follow-redirects": {
- "version": "1.14.7",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
- "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ==",
+ "version": "1.14.9",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
+ "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==",
"funding": [
{
"type": "individual",
@@ -7033,6 +7035,11 @@
"lodash": "^4.17.15"
}
},
+ "node_modules/guacamole-common-js": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/guacamole-common-js/-/guacamole-common-js-1.3.1.tgz",
+ "integrity": "sha512-PrB+Z4DKERXyrTt20JKkLteR7ZgesD/HihwnnHdo0cwgsgv1i0898is6IUTo0iBHCEcH9vLJDULax18EUJSgvg=="
+ },
"node_modules/har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -7087,9 +7094,9 @@
}
},
"node_modules/has-symbols": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
- "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==",
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
"engines": {
"node": ">= 0.4"
},
@@ -7822,9 +7829,9 @@
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"node_modules/jest-worker": {
- "version": "27.5.0",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.0.tgz",
- "integrity": "sha512-8OEHiPNOPTfaWnJ2SUHM8fmgeGq37uuGsQBvGKQJl1f+6WIy6g7G3fE2ruI5294bUKUI9FaCWt5hDvO8HSwsSg==",
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
"dependencies": {
"@types/node": "*",
"merge-stream": "^2.0.0",
@@ -7880,9 +7887,9 @@
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
},
"node_modules/jsdoc-type-pratt-parser": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-2.2.2.tgz",
- "integrity": "sha512-zRokSWcPLSWkoNzsWn9pq7YYSwDhKyEe+cJYT2qaPqLOOJb5sFSi46BPj81vP+e8chvCNdQL9RG86Bi9EI6MDw==",
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-2.2.5.tgz",
+ "integrity": "sha512-2a6eRxSxp1BW040hFvaJxhsCMI9lT8QB8t14t+NY5tC5rckIR0U9cr2tjOeaFirmEOy6MHvmJnY7zTBHq431Lw==",
"dev": true,
"engines": {
"node": ">=12.0.0"
@@ -8299,14 +8306,14 @@
"dev": true
},
"node_modules/logform": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.2.tgz",
- "integrity": "sha512-V6JiPThZzTsbVRspNO6TmHkR99oqYTs8fivMBYQkjZj6rxW92KxtDCPE6IkAk1DNBnYKNkjm4jYBm6JDUcyhOA==",
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.0.tgz",
+ "integrity": "sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==",
"dependencies": {
- "colors": "1.4.0",
+ "@colors/colors": "1.5.0",
"fecha": "^4.2.0",
"ms": "^2.1.1",
- "safe-stable-stringify": "^1.1.0",
+ "safe-stable-stringify": "^2.3.1",
"triple-beam": "^1.3.0"
}
},
@@ -8482,19 +8489,19 @@
}
},
"node_modules/mime-db": {
- "version": "1.51.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
- "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==",
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
- "version": "2.1.34",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
- "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dependencies": {
- "mime-db": "1.51.0"
+ "mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
@@ -8545,9 +8552,9 @@
"dev": true
},
"node_modules/minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -8646,9 +8653,9 @@
"integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA=="
},
"node_modules/nanoid": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
- "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA==",
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
+ "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
@@ -8788,9 +8795,9 @@
}
},
"node_modules/node-releases": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz",
- "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA=="
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz",
+ "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg=="
},
"node_modules/node-zendesk": {
"version": "2.1.0",
@@ -9494,11 +9501,11 @@
}
},
"node_modules/postcss": {
- "version": "8.4.6",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz",
- "integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==",
+ "version": "8.4.8",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.8.tgz",
+ "integrity": "sha512-2tXEqGxrjvAO6U+CJzDL2Fk2kPHTv1jQsYkSoMeOis2SsYaXRO2COxTdQp99cYvif9JTXaAk9lYGc3VhJt7JPQ==",
"dependencies": {
- "nanoid": "^3.2.0",
+ "nanoid": "^3.3.1",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
},
@@ -9753,9 +9760,9 @@
}
},
"node_modules/qrcode/node_modules/ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
"engines": {
"node": ">=6"
}
@@ -10669,9 +10676,12 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"node_modules/safe-stable-stringify": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz",
- "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw=="
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz",
+ "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==",
+ "engines": {
+ "node": ">=10"
+ }
},
"node_modules/safer-buffer": {
"version": "2.1.2",
@@ -11000,9 +11010,9 @@
}
},
"node_modules/socket.io-client/node_modules/socket.io-parser": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.1.tgz",
- "integrity": "sha512-USQVLSkDWE5nbcY760ExdKaJxCE65kcsG/8k5FDGZVVxpD1pA7hABYXYkCUvxUuYYh/+uQw0N/fvBzfT8o07KA==",
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
+ "integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
"dependencies": {
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1"
@@ -11133,9 +11143,9 @@
}
},
"node_modules/stackframe": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz",
- "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.1.tgz",
+ "integrity": "sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg==",
"dev": true
},
"node_modules/statuses": {
@@ -11481,6 +11491,23 @@
"node": ">= 6"
}
},
+ "node_modules/terser": {
+ "version": "5.12.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.0.tgz",
+ "integrity": "sha512-R3AUhNBGWiFc77HXag+1fXpAxTAFRQTJemlJKjAgD9r8xXTpjNKqIXwHM/o7Rh+O0kUJtS3WQVdBeMKFk5sw9A==",
+ "dependencies": {
+ "acorn": "^8.5.0",
+ "commander": "^2.20.0",
+ "source-map": "~0.7.2",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/terser-webpack-plugin": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz",
@@ -11504,24 +11531,6 @@
"webpack": "^5.1.0"
}
},
- "node_modules/terser-webpack-plugin/node_modules/acorn": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
- "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
- "optional": true,
- "peer": true,
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/terser-webpack-plugin/node_modules/commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
- },
"node_modules/terser-webpack-plugin/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -11530,31 +11539,23 @@
"node": ">=0.10.0"
}
},
- "node_modules/terser-webpack-plugin/node_modules/terser": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
- "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==",
- "dependencies": {
- "commander": "^2.20.0",
- "source-map": "~0.7.2",
- "source-map-support": "~0.5.20"
- },
+ "node_modules/terser/node_modules/acorn": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+ "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
"bin": {
- "terser": "bin/terser"
+ "acorn": "bin/acorn"
},
"engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "acorn": "^8.5.0"
- },
- "peerDependenciesMeta": {
- "acorn": {
- "optional": true
- }
+ "node": ">=0.4.0"
}
},
- "node_modules/terser-webpack-plugin/node_modules/terser/node_modules/source-map": {
+ "node_modules/terser/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ },
+ "node_modules/terser/node_modules/source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
@@ -11675,9 +11676,9 @@
"integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
},
"node_modules/tsconfig-paths": {
- "version": "3.12.0",
- "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz",
- "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==",
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz",
+ "integrity": "sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g==",
"dev": true,
"dependencies": {
"@types/json5": "^0.0.29",
@@ -12185,9 +12186,9 @@
}
},
"node_modules/webpack/node_modules/enhanced-resolve": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz",
- "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==",
+ "version": "5.9.2",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz",
+ "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -12239,9 +12240,12 @@
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"node_modules/which-pm-runs": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
- "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz",
+ "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==",
+ "engines": {
+ "node": ">=4"
+ }
},
"node_modules/wide-align": {
"version": "1.1.5",
@@ -12601,9 +12605,9 @@
}
},
"@babel/generator": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.0.tgz",
- "integrity": "sha512-I3Omiv6FGOC29dtlZhkfXO6pgkmukJSlT26QjVvS1DGZe/NzSVCPG41X0tS21oZkJYlovfj9qDWgKP+Cn4bXxw==",
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.17.3.tgz",
+ "integrity": "sha512-+R6Dctil/MgUsZsZAkYgK+ADNSZzJRRy0TvY65T71z/CR854xHQ1EweBYXdfT+HNeN7w0cSJJEzgxZMv40pxsg==",
"requires": {
"@babel/types": "^7.17.0",
"jsesc": "^2.5.1",
@@ -12639,9 +12643,9 @@
}
},
"@babel/helper-create-class-features-plugin": {
- "version": "7.17.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.1.tgz",
- "integrity": "sha512-JBdSr/LtyYIno/pNnJ75lBcqc3Z1XXujzPanHqjvvrhOA+DTceTFuJi8XjmWTZh4r3fsdfqaCMN0iZemdkxZHQ==",
+ "version": "7.17.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.17.6.tgz",
+ "integrity": "sha512-SogLLSxXm2OkBbSsHZMM4tUi8fUzjs63AT/d0YQIzr6GSd8Hxsbk2KYDX0k0DweAzGMj/YWeiCsorIdtdcW8Eg==",
"requires": {
"@babel/helper-annotate-as-pure": "^7.16.7",
"@babel/helper-environment-visitor": "^7.16.7",
@@ -12735,9 +12739,9 @@
}
},
"@babel/helper-module-transforms": {
- "version": "7.16.7",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.16.7.tgz",
- "integrity": "sha512-gaqtLDxJEFCeQbYp9aLAefjhkKdjKcdh6DB7jniIGU3Pz52WAmP268zK0VgPz9hUNkMSYeH976K2/Y6yPadpng==",
+ "version": "7.17.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.17.6.tgz",
+ "integrity": "sha512-2ULmRdqoOMpdvkbT8jONrZML/XALfzxlb052bldftkicAUy8AxSCkD5trDPQcwHNmolcl7wP6ehNqMlyUw6AaA==",
"requires": {
"@babel/helper-environment-visitor": "^7.16.7",
"@babel/helper-module-imports": "^7.16.7",
@@ -12745,8 +12749,8 @@
"@babel/helper-split-export-declaration": "^7.16.7",
"@babel/helper-validator-identifier": "^7.16.7",
"@babel/template": "^7.16.7",
- "@babel/traverse": "^7.16.7",
- "@babel/types": "^7.16.7"
+ "@babel/traverse": "^7.17.3",
+ "@babel/types": "^7.17.0"
}
},
"@babel/helper-optimise-call-expression": {
@@ -12830,9 +12834,9 @@
}
},
"@babel/helpers": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.0.tgz",
- "integrity": "sha512-Xe/9NFxjPwELUvW2dsukcMZIp6XwPSbI4ojFBJuX5ramHuVE22SVcZIwqzdWo5uCgeTXW8qV97lMvSOjq+1+nQ==",
+ "version": "7.17.2",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.17.2.tgz",
+ "integrity": "sha512-0Qu7RLR1dILozr/6M0xgj+DFPmi6Bnulgm9M8BVa9ZCWxDqlSnqt3cf8IDPB5m45sVXUZ0kuQAgUrdSFFH79fQ==",
"requires": {
"@babel/template": "^7.16.7",
"@babel/traverse": "^7.17.0",
@@ -12863,9 +12867,9 @@
}
},
"@babel/parser": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.0.tgz",
- "integrity": "sha512-VKXSCQx5D8S04ej+Dqsr1CzYvvWgf20jIw2D+YhQCrIlr2UZGaDds23Y0xg75/skOxpLCRpUZvk/1EAVkGoDOw=="
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.17.3.tgz",
+ "integrity": "sha512-7yJPvPV+ESz2IUTPbOL+YkIGyCqOyNIzdguKQuJGnH7bg1WTIifuM21YqokFt/THWh1AkCRn9IgoykTRCBVpzA=="
},
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
"version": "7.16.7",
@@ -12909,11 +12913,11 @@
}
},
"@babel/plugin-proposal-class-static-block": {
- "version": "7.16.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.16.7.tgz",
- "integrity": "sha512-dgqJJrcZoG/4CkMopzhPJjGxsIe9A8RlkQLnL/Vhhx8AA9ZuaRwGSlscSh42hazc7WSrya/IK7mTeoF0DP9tEw==",
+ "version": "7.17.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.17.6.tgz",
+ "integrity": "sha512-X/tididvL2zbs7jZCeeRJ8167U/+Ac135AM6jCAx6gYXDUviZV5Ku9UDvWS2NCuWlFjIRXklYhwo6HhAC7ETnA==",
"requires": {
- "@babel/helper-create-class-features-plugin": "^7.16.7",
+ "@babel/helper-create-class-features-plugin": "^7.17.6",
"@babel/helper-plugin-utils": "^7.16.7",
"@babel/plugin-syntax-class-static-block": "^7.14.5"
}
@@ -13210,9 +13214,9 @@
}
},
"@babel/plugin-transform-destructuring": {
- "version": "7.16.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.16.7.tgz",
- "integrity": "sha512-VqAwhTHBnu5xBVDCvrvqJbtLUa++qZaWC0Fgr2mqokBlulZARGyIvZDoqbPlPaKImQ9dKAcCzbv+ul//uqu70A==",
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.17.3.tgz",
+ "integrity": "sha512-dDFzegDYKlPqa72xIlbmSkly5MluLoaC1JswABGktyt6NTXSBcUuse/kWE/wvKFWJHPETpi158qJZFS3JmykJg==",
"requires": {
"@babel/helper-plugin-utils": "^7.16.7"
}
@@ -13369,15 +13373,15 @@
}
},
"@babel/plugin-transform-react-jsx": {
- "version": "7.16.7",
- "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.16.7.tgz",
- "integrity": "sha512-8D16ye66fxiE8m890w0BpPpngG9o9OVBBy0gH2E+2AR7qMR2ZpTYJEqLxAsoroenMId0p/wMW+Blc0meDgu0Ag==",
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.17.3.tgz",
+ "integrity": "sha512-9tjBm4O07f7mzKSIlEmPdiE6ub7kfIe6Cd+w+oQebpATfTQMAgW+YOuWxogbKVTulA+MEO7byMeIUtQ1z+z+ZQ==",
"requires": {
"@babel/helper-annotate-as-pure": "^7.16.7",
"@babel/helper-module-imports": "^7.16.7",
"@babel/helper-plugin-utils": "^7.16.7",
"@babel/plugin-syntax-jsx": "^7.16.7",
- "@babel/types": "^7.16.7"
+ "@babel/types": "^7.17.0"
}
},
"@babel/plugin-transform-react-jsx-development": {
@@ -13589,17 +13593,17 @@
}
},
"@babel/runtime": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.0.tgz",
- "integrity": "sha512-etcO/ohMNaNA2UBdaXBBSX/3aEzFMRrVfaPv8Ptc0k+cWpWW0QFiGZ2XnVqQZI1Cf734LbPGmqBKWESfW4x/dQ==",
+ "version": "7.17.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz",
+ "integrity": "sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==",
"requires": {
"regenerator-runtime": "^0.13.4"
}
},
"@babel/runtime-corejs3": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.0.tgz",
- "integrity": "sha512-qeydncU80ravKzovVncW3EYaC1ji3GpntdPgNcJy9g7hHSY6KX+ne1cbV3ov7Zzm4F1z0+QreZPCuw1ynkmYNg==",
+ "version": "7.17.2",
+ "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.17.2.tgz",
+ "integrity": "sha512-NcKtr2epxfIrNM4VOmPKO46TvDMCBhgi2CrSHaEarrz+Plk2K5r9QemmOFTGpZaoKnWoGH5MO+CzeRsih/Fcgg==",
"dev": true,
"requires": {
"core-js-pure": "^3.20.2",
@@ -13617,17 +13621,17 @@
}
},
"@babel/traverse": {
- "version": "7.17.0",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.0.tgz",
- "integrity": "sha512-fpFIXvqD6kC7c7PUNnZ0Z8cQXlarCLtCUpt2S1Dx7PjoRtCFffvOkHHSom+m5HIxMZn5bIBVb71lhabcmjEsqg==",
+ "version": "7.17.3",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.17.3.tgz",
+ "integrity": "sha512-5irClVky7TxRWIRtxlh2WPUUOLhcPN06AGgaQSB8AEwuyEBgJVuJ5imdHm5zxk8w0QS5T+tDfnDxAlhWjpb7cw==",
"requires": {
"@babel/code-frame": "^7.16.7",
- "@babel/generator": "^7.17.0",
+ "@babel/generator": "^7.17.3",
"@babel/helper-environment-visitor": "^7.16.7",
"@babel/helper-function-name": "^7.16.7",
"@babel/helper-hoist-variables": "^7.16.7",
"@babel/helper-split-export-declaration": "^7.16.7",
- "@babel/parser": "^7.17.0",
+ "@babel/parser": "^7.17.3",
"@babel/types": "^7.17.0",
"debug": "^4.1.0",
"globals": "^11.1.0"
@@ -13642,10 +13646,15 @@
"to-fast-properties": "^2.0.0"
}
},
+ "@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ=="
+ },
"@dabh/diagnostics": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.2.tgz",
- "integrity": "sha512-+A1YivoVDNNVCdfozHSR8v/jyuuLTMXwjWuxPFlFlUapXoGc+Gj9mDlTDDfrwl7rXCl2tNZ0kE8sIBO6YOn96Q==",
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
+ "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==",
"requires": {
"colorspace": "1.1.x",
"enabled": "2.0.x",
@@ -13690,9 +13699,9 @@
}
},
"@discoveryjs/json-ext": {
- "version": "0.5.6",
- "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz",
- "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA=="
+ "version": "0.5.7",
+ "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz",
+ "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw=="
},
"@emotion/babel-plugin": {
"version": "11.7.2",
@@ -13731,9 +13740,9 @@
"integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow=="
},
"@emotion/is-prop-valid": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.1.tgz",
- "integrity": "sha512-bW1Tos67CZkOURLc0OalnfxtSXQJMrAMV0jZTVGJUPSOd4qgjF3+tTD5CwJM13PHA8cltGW1WGbbvV9NpvUZPw==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz",
+ "integrity": "sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==",
"requires": {
"@emotion/memoize": "^0.7.4"
}
@@ -13792,9 +13801,9 @@
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
},
"@emotion/utils": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.0.0.tgz",
- "integrity": "sha512-mQC2b3XLDs6QCW+pDQDiyO/EdGZYOygE8s5N5rrzjSI4M3IejPE/JPndCBwRT9z982aqQNi6beWs1UeayrQxxA=="
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz",
+ "integrity": "sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ=="
},
"@emotion/weak-memoize": {
"version": "0.2.5",
@@ -13978,17 +13987,17 @@
},
"dependencies": {
"@mui/system": {
- "version": "5.4.0",
- "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.4.0.tgz",
- "integrity": "sha512-LX7g5gK5yCwiueSUVG73uVNc0yeHjsWUIFLrnPjP3m+J7O38RkPqyao5nZahhaSL1PGNbR9+zfkxljXthO9QqA==",
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.5.0.tgz",
+ "integrity": "sha512-zFOfERv3Y4m5ehwTRR9cGaPuMvlD2qVXmFKC60P0Gte3aD6vYObyNriZv+mDVGlhDxZTZhxBrNPH3ns25xSFtQ==",
"requires": {
- "@babel/runtime": "^7.16.7",
- "@mui/private-theming": "^5.3.0",
- "@mui/styled-engine": "^5.3.0",
- "@mui/types": "^7.1.0",
- "@mui/utils": "^5.3.0",
+ "@babel/runtime": "^7.17.2",
+ "@mui/private-theming": "^5.4.4",
+ "@mui/styled-engine": "^5.4.4",
+ "@mui/types": "^7.1.2",
+ "@mui/utils": "^5.4.4",
"clsx": "^1.1.1",
- "csstype": "^3.0.10",
+ "csstype": "^3.0.11",
"prop-types": "^15.7.2"
}
},
@@ -14048,21 +14057,21 @@
}
},
"@mui/private-theming": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.3.0.tgz",
- "integrity": "sha512-EBobUEyM9fMnteKrVPp8pTMUh81xXakyfdpkoh7Y19q9JpD2eh7QGAQVJVj0JBFlcUJD60NIE4K8rdokrRmLwg==",
+ "version": "5.4.4",
+ "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.4.4.tgz",
+ "integrity": "sha512-V/gxttr6736yJoU9q+4xxXsa0K/w9Hn9pg99zsOHt7i/O904w2CX5NHh5WqDXtoUzVcayLF0RB17yr6l79CE+A==",
"requires": {
- "@babel/runtime": "^7.16.7",
- "@mui/utils": "^5.3.0",
+ "@babel/runtime": "^7.17.2",
+ "@mui/utils": "^5.4.4",
"prop-types": "^15.7.2"
}
},
"@mui/styled-engine": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.3.0.tgz",
- "integrity": "sha512-I4YemFy9WnCLUdZ5T+6egpzc8e7Jq/uh9AJ3QInZHbyNu/9I2SWvNn7vHjWOT/D8Y8LMzIOhu5WwZbzjez7YRw==",
+ "version": "5.4.4",
+ "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.4.4.tgz",
+ "integrity": "sha512-AKx3rSgB6dmt5f7iP4K18mLFlE5/9EfJe/5EH9Pyqez8J/CPkTgYhJ/Va6qtlrcunzpui+uG/vfuf04yAZekSg==",
"requires": {
- "@babel/runtime": "^7.16.7",
+ "@babel/runtime": "^7.17.2",
"@emotion/cache": "^11.7.1",
"prop-types": "^15.7.2"
}
@@ -14107,17 +14116,17 @@
}
},
"@mui/types": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.1.0.tgz",
- "integrity": "sha512-Hh7ALdq/GjfIwLvqH3XftuY3bcKhupktTm+S6qRIDGOtPtRuq2L21VWzOK4p7kblirK0XgGVH5BLwa6u8z/6QQ==",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.1.2.tgz",
+ "integrity": "sha512-SD7O1nVzqG+ckQpFjDhXPZjRceB8HQFHEvdLLrPhlJy4lLbwEBbxK74Tj4t6Jgk0fTvLJisuwOutrtYe9P/xBQ==",
"requires": {}
},
"@mui/utils": {
- "version": "5.3.0",
- "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.3.0.tgz",
- "integrity": "sha512-O/E9IQKPMg0OrN7+gkn7Ga5o5WA2iXQGdyqNBFPNrYzxOvwzsEtM5K7MtTCGGYKFe8mhTRM0ZOjh5OM0dglw+Q==",
+ "version": "5.4.4",
+ "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.4.4.tgz",
+ "integrity": "sha512-hfYIXEuhc2mXMGN5nUPis8beH6uE/zl3uMWJcyHX0/LN/+QxO9zhYuV6l8AsAaphHFyS/fBv0SW3Nid7jw5hKQ==",
"requires": {
- "@babel/runtime": "^7.16.7",
+ "@babel/runtime": "^7.17.2",
"@types/prop-types": "^15.7.4",
"@types/react-is": "^16.7.1 || ^17.0.0",
"prop-types": "^15.7.2",
@@ -14186,9 +14195,9 @@
}
},
"@popperjs/core": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz",
- "integrity": "sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA=="
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.4.tgz",
+ "integrity": "sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg=="
},
"@reach/observe-rect": {
"version": "1.2.0",
@@ -14392,9 +14401,9 @@
}
},
"@types/d3-path": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-2.0.1.tgz",
- "integrity": "sha512-6K8LaFlztlhZO7mwsZg7ClRsdLg3FJRzIIi6SZXDWmmSJc2x8dd2VkESbLXdk3p8cuvz71f36S0y8Zv2AxqvQw=="
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-2.0.2.tgz",
+ "integrity": "sha512-3YHpvDw9LzONaJzejXLOwZ3LqwwkoXb9LI2YN7Hbd6pkGo5nIlJ09ul4bQhBN4hQZJKmUpX8HkVqbzgUKY48cg=="
},
"@types/d3-polygon": {
"version": "2.0.1",
@@ -14526,14 +14535,14 @@
"dev": true
},
"@types/lodash": {
- "version": "4.14.178",
- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.178.tgz",
- "integrity": "sha512-0d5Wd09ItQWH1qFbEyQ7oTQ3GZrMfth5JkbN3EvTKLXcHLRDSXeLnlvlOn0wvxVIwK5o2M8JzP/OWz7T3NRsbw=="
+ "version": "4.14.179",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.179.tgz",
+ "integrity": "sha512-uwc1x90yCKqGcIOAT6DwOSuxnrAbpkdPsUOZtwrXb4D/6wZs+6qG7QnIawDuZWg0sWpxl+ltIKCaLoMlna678w=="
},
"@types/node": {
- "version": "17.0.15",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.15.tgz",
- "integrity": "sha512-zWt4SDDv1S9WRBNxLFxFRHxdD9tvH8f5/kg5/IaLFdnSNXsDY4eL3Q3XXN+VxUnWIhyVFDwcsmAprvwXoM/ClA=="
+ "version": "17.0.21",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.21.tgz",
+ "integrity": "sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ=="
},
"@types/parse-json": {
"version": "4.0.0",
@@ -14546,9 +14555,9 @@
"integrity": "sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ=="
},
"@types/react": {
- "version": "17.0.39",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.39.tgz",
- "integrity": "sha512-UVavlfAxDd/AgAacMa60Azl7ygyQNRwC/DsHZmKgNvPmRR5p70AJ5Q9EAmL2NWOJmeV+vVUI4IAP7GZrN8h8Ug==",
+ "version": "17.0.40",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.40.tgz",
+ "integrity": "sha512-UrXhD/JyLH+W70nNSufXqMZNuUD2cXHu6UjCllC6pmOQgBX4SGXOH8fjRka0O0Ee0HrFxapDD8Bwn81Kmiz6jQ==",
"requires": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@@ -14564,9 +14573,9 @@
}
},
"@types/react-redux": {
- "version": "7.1.22",
- "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.22.tgz",
- "integrity": "sha512-GxIA1kM7ClU73I6wg9IRTVwSO9GS+SAKZKe0Enj+82HMU6aoESFU2HNAdNi3+J53IaOHPiUfT3kSG4L828joDQ==",
+ "version": "7.1.23",
+ "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.23.tgz",
+ "integrity": "sha512-D02o3FPfqQlfu2WeEYwh3x2otYd2Dk1o8wAfsA0B1C2AJEFxE663Ozu7JzuWbznGgW248NaOF6wsqCGNq9d3qw==",
"requires": {
"@types/hoist-non-react-statics": "^3.3.0",
"@types/react": "*",
@@ -15406,14 +15415,14 @@
}
},
"browserslist": {
- "version": "4.19.1",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.19.1.tgz",
- "integrity": "sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==",
+ "version": "4.20.0",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.20.0.tgz",
+ "integrity": "sha512-bnpOoa+DownbciXj0jVGENf8VYQnE2LNWomhYuCsMmmx9Jd9lwq0WXODuwpSsp8AVdKM2/HorrzxAfbKvWTByQ==",
"requires": {
- "caniuse-lite": "^1.0.30001286",
- "electron-to-chromium": "^1.4.17",
+ "caniuse-lite": "^1.0.30001313",
+ "electron-to-chromium": "^1.4.76",
"escalade": "^3.1.1",
- "node-releases": "^2.0.1",
+ "node-releases": "^2.0.2",
"picocolors": "^1.0.0"
}
},
@@ -15508,9 +15517,9 @@
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
},
"caniuse-lite": {
- "version": "1.0.30001307",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001307.tgz",
- "integrity": "sha512-+MXEMczJ4FuxJAUp0jvAl6Df0NI/OfW1RWEE61eSmzS7hw6lz4IKutbhbXendwq8BljfFuHtu26VWsg4afQ7Ng=="
+ "version": "1.0.30001316",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001316.tgz",
+ "integrity": "sha512-JgUdNoZKxPZFzbzJwy4hDSyGuH/gXz2rN51QmoR8cBQsVo58llD3A0vlRKKRt8FGf5u69P9eQyIH8/z9vN/S0Q=="
},
"caseless": {
"version": "0.12.0",
@@ -15641,11 +15650,6 @@
"resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz",
"integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g=="
},
- "colors": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
- "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
- },
"colorspace": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz",
@@ -15845,9 +15849,9 @@
"integrity": "sha512-vJlUi/7YdlCZeL6fXvWNaLUPh/id12WXj3MbkMw5uOyF0PfWPBNOCNbs53YqgrvtujLNlt9JQpruyIKkUZ+PKA=="
},
"core-js-compat": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.0.tgz",
- "integrity": "sha512-OSXseNPSK2OPJa6GdtkMz/XxeXx8/CJvfhQWTqd6neuUraujcL4jVsjkLQz1OWnax8xVQJnRPe0V2jqNWORA+A==",
+ "version": "3.21.1",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.21.1.tgz",
+ "integrity": "sha512-gbgX5AUvMb8gwxC7FLVWYT7Kkgu/y7+h/h1X43yJkNqhlK2fuYyQimqvKGNZFAY6CKii/GFKJ2cp/1/42TN36g==",
"requires": {
"browserslist": "^4.19.1",
"semver": "7.0.0"
@@ -15861,9 +15865,9 @@
}
},
"core-js-pure": {
- "version": "3.21.0",
- "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.0.tgz",
- "integrity": "sha512-VaJUunCZLnxuDbo1rNOzwbet9E1K9joiXS5+DQMPtgxd24wfsZbJZMMfQLGYMlCUvSxLfsRUUhoOR2x28mFfeg==",
+ "version": "3.21.1",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.21.1.tgz",
+ "integrity": "sha512-12VZfFIu+wyVbBebyHmRTuEE/tZrB4tJToWcwAMcsp3h4+sHR+fMJWbKpYiCRWlhFBq+KNyO8rIV9rTkeVmznQ==",
"dev": true
},
"core-util-is": {
@@ -16023,9 +16027,9 @@
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg=="
},
"csstype": {
- "version": "3.0.10",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz",
- "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA=="
+ "version": "3.0.11",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
+ "integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw=="
},
"d3-color": {
"version": "2.0.0",
@@ -16289,9 +16293,9 @@
"integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0="
},
"electron-to-chromium": {
- "version": "1.4.65",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.65.tgz",
- "integrity": "sha512-0/d8Skk8sW3FxXP0Dd6MnBlrwx7Qo9cqQec3BlIAlvKnrmS3pHsIbaroEi+nd0kZkGpQ6apMEre7xndzjlEnLw=="
+ "version": "1.4.82",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.82.tgz",
+ "integrity": "sha512-Ks+ANzLoIrFDUOJdjxYMH6CMKB8UQo5modAwvSZTxgF+vEs/U7G5IbWFUp6dS4klPkTDVdxbORuk8xAXXhMsWw=="
},
"elliptic": {
"version": "6.5.4",
@@ -16346,9 +16350,9 @@
}
},
"engine.io": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.2.tgz",
- "integrity": "sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ==",
+ "version": "6.1.3",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.1.3.tgz",
+ "integrity": "sha512-rqs60YwkvWTLLnfazqgZqLa/aKo+9cueVfEi/dZ8PyGyaf8TLOxj++4QMIgeG3Gn0AhrWiFXvghsoY9L9h25GA==",
"requires": {
"@types/cookie": "^0.4.1",
"@types/cors": "^2.8.12",
@@ -16358,7 +16362,7 @@
"cookie": "~0.4.1",
"cors": "~2.8.5",
"debug": "~4.3.1",
- "engine.io-parser": "~5.0.0",
+ "engine.io-parser": "~5.0.3",
"ws": "~8.2.3"
},
"dependencies": {
@@ -16441,9 +16445,9 @@
}
},
"error-stack-parser": {
- "version": "2.0.6",
- "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.6.tgz",
- "integrity": "sha512-d51brTeqC+BHlwF0BhPtcYgF5nlzf9ZZ0ZIUQNZpc9ZB9qw5IJ2diTrBY9jlCJkTLITYPjmiX6OWCwH+fuyNgQ==",
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.7.tgz",
+ "integrity": "sha512-chLOW0ZGRf4s8raLrDxa5sdkvPec5YdvwbFnqJme4rk0rFajP8mPtrDL1+I+CwrQDCjswDA5sREX7jYQDQs9vA==",
"dev": true,
"requires": {
"stackframe": "^1.1.1"
@@ -17447,9 +17451,9 @@
"integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw=="
},
"follow-redirects": {
- "version": "1.14.7",
- "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz",
- "integrity": "sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ=="
+ "version": "1.14.9",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz",
+ "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w=="
},
"forever-agent": {
"version": "0.6.1",
@@ -17694,6 +17698,11 @@
"lodash": "^4.17.15"
}
},
+ "guacamole-common-js": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/guacamole-common-js/-/guacamole-common-js-1.3.1.tgz",
+ "integrity": "sha512-PrB+Z4DKERXyrTt20JKkLteR7ZgesD/HihwnnHdo0cwgsgv1i0898is6IUTo0iBHCEcH9vLJDULax18EUJSgvg=="
+ },
"har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -17732,9 +17741,9 @@
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"has-symbols": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz",
- "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw=="
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
+ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A=="
},
"has-tostringtag": {
"version": "1.0.0",
@@ -18250,9 +18259,9 @@
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
},
"jest-worker": {
- "version": "27.5.0",
- "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.0.tgz",
- "integrity": "sha512-8OEHiPNOPTfaWnJ2SUHM8fmgeGq37uuGsQBvGKQJl1f+6WIy6g7G3fE2ruI5294bUKUI9FaCWt5hDvO8HSwsSg==",
+ "version": "27.5.1",
+ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz",
+ "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==",
"requires": {
"@types/node": "*",
"merge-stream": "^2.0.0",
@@ -18295,9 +18304,9 @@
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
},
"jsdoc-type-pratt-parser": {
- "version": "2.2.2",
- "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-2.2.2.tgz",
- "integrity": "sha512-zRokSWcPLSWkoNzsWn9pq7YYSwDhKyEe+cJYT2qaPqLOOJb5sFSi46BPj81vP+e8chvCNdQL9RG86Bi9EI6MDw==",
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-2.2.5.tgz",
+ "integrity": "sha512-2a6eRxSxp1BW040hFvaJxhsCMI9lT8QB8t14t+NY5tC5rckIR0U9cr2tjOeaFirmEOy6MHvmJnY7zTBHq431Lw==",
"dev": true
},
"jsesc": {
@@ -18657,14 +18666,14 @@
"dev": true
},
"logform": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/logform/-/logform-2.3.2.tgz",
- "integrity": "sha512-V6JiPThZzTsbVRspNO6TmHkR99oqYTs8fivMBYQkjZj6rxW92KxtDCPE6IkAk1DNBnYKNkjm4jYBm6JDUcyhOA==",
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.0.tgz",
+ "integrity": "sha512-CPSJw4ftjf517EhXZGGvTHHkYobo7ZCc0kvwUoOYcjfR2UVrI66RHj8MCrfAdEitdmFqbu2BYdYs8FHHZSb6iw==",
"requires": {
- "colors": "1.4.0",
+ "@colors/colors": "1.5.0",
"fecha": "^4.2.0",
"ms": "^2.1.1",
- "safe-stable-stringify": "^1.1.0",
+ "safe-stable-stringify": "^2.3.1",
"triple-beam": "^1.3.0"
}
},
@@ -18799,16 +18808,16 @@
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="
},
"mime-db": {
- "version": "1.51.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz",
- "integrity": "sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g=="
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
},
"mime-types": {
- "version": "2.1.34",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.34.tgz",
- "integrity": "sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==",
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"requires": {
- "mime-db": "1.51.0"
+ "mime-db": "1.52.0"
}
},
"mimic-fn": {
@@ -18843,9 +18852,9 @@
"dev": true
},
"minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"requires": {
"brace-expansion": "^1.1.7"
}
@@ -18931,9 +18940,9 @@
"integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA=="
},
"nanoid": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz",
- "integrity": "sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA=="
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz",
+ "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw=="
},
"napi-build-utils": {
"version": "1.0.2",
@@ -19063,9 +19072,9 @@
}
},
"node-releases": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.1.tgz",
- "integrity": "sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA=="
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.2.tgz",
+ "integrity": "sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg=="
},
"node-zendesk": {
"version": "2.1.0",
@@ -19581,11 +19590,11 @@
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w=="
},
"postcss": {
- "version": "8.4.6",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz",
- "integrity": "sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==",
+ "version": "8.4.8",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.8.tgz",
+ "integrity": "sha512-2tXEqGxrjvAO6U+CJzDL2Fk2kPHTv1jQsYkSoMeOis2SsYaXRO2COxTdQp99cYvif9JTXaAk9lYGc3VhJt7JPQ==",
"requires": {
- "nanoid": "^3.2.0",
+ "nanoid": "^3.3.1",
"picocolors": "^1.0.0",
"source-map-js": "^1.0.2"
}
@@ -19776,9 +19785,9 @@
},
"dependencies": {
"ansi-regex": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
- "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
+ "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g=="
},
"buffer": {
"version": "5.7.1",
@@ -20460,9 +20469,9 @@
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safe-stable-stringify": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-1.1.1.tgz",
- "integrity": "sha512-ERq4hUjKDbJfE4+XtZLFPCDi8Vb1JqaxAPTxWFLBx8XcAlf9Bda/ZJdVezs/NAfsMQScyIlUMx+Yeu7P7rx5jw=="
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz",
+ "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg=="
},
"safer-buffer": {
"version": "2.1.2",
@@ -20730,9 +20739,9 @@
},
"dependencies": {
"socket.io-parser": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.1.tgz",
- "integrity": "sha512-USQVLSkDWE5nbcY760ExdKaJxCE65kcsG/8k5FDGZVVxpD1pA7hABYXYkCUvxUuYYh/+uQw0N/fvBzfT8o07KA==",
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.1.2.tgz",
+ "integrity": "sha512-j3kk71QLJuyQ/hh5F/L2t1goqzdTL0gvDzuhTuNSwihfuFUrcSji0qFZmJJPtG6Rmug153eOPsUizeirf1IIog==",
"requires": {
"@socket.io/component-emitter": "~3.0.0",
"debug": "~4.3.1"
@@ -20838,9 +20847,9 @@
"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
},
"stackframe": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.0.tgz",
- "integrity": "sha512-GrdeshiRmS1YLMYgzF16olf2jJ/IzxXY9lhKOskuVziubpTYcYqyOwYeJKzQkwy7uN0fYSsbsC4RQaXf9LCrYA==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.1.tgz",
+ "integrity": "sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg==",
"dev": true
},
"statuses": {
@@ -21117,6 +21126,34 @@
}
}
},
+ "terser": {
+ "version": "5.12.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.12.0.tgz",
+ "integrity": "sha512-R3AUhNBGWiFc77HXag+1fXpAxTAFRQTJemlJKjAgD9r8xXTpjNKqIXwHM/o7Rh+O0kUJtS3WQVdBeMKFk5sw9A==",
+ "requires": {
+ "acorn": "^8.5.0",
+ "commander": "^2.20.0",
+ "source-map": "~0.7.2",
+ "source-map-support": "~0.5.20"
+ },
+ "dependencies": {
+ "acorn": {
+ "version": "8.7.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
+ "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="
+ },
+ "commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+ },
+ "source-map": {
+ "version": "0.7.3",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
+ "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
+ }
+ }
+ },
"terser-webpack-plugin": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.1.4.tgz",
@@ -21130,39 +21167,10 @@
"terser": "^5.7.0"
},
"dependencies": {
- "acorn": {
- "version": "8.7.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.0.tgz",
- "integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==",
- "optional": true,
- "peer": true
- },
- "commander": {
- "version": "2.20.3",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
- "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
- },
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
- },
- "terser": {
- "version": "5.10.0",
- "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz",
- "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==",
- "requires": {
- "commander": "^2.20.0",
- "source-map": "~0.7.2",
- "source-map-support": "~0.5.20"
- },
- "dependencies": {
- "source-map": {
- "version": "0.7.3",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
- "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
- }
- }
}
}
},
@@ -21261,9 +21269,9 @@
"integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw=="
},
"tsconfig-paths": {
- "version": "3.12.0",
- "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz",
- "integrity": "sha512-e5adrnOYT6zqVnWqZu7i/BQ3BnhzvGbjEjejFXO20lKIKpwTaupkCPgEfv4GZK1IBciJUEhYs3J3p75FdaTFVg==",
+ "version": "3.14.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.0.tgz",
+ "integrity": "sha512-cg/1jAZoL57R39+wiw4u/SCC6Ic9Q5NqjBOb+9xISedOYurfog9ZNmKJSxAnb2m/5Bq4lE9lhUcau33Ml8DM0g==",
"dev": true,
"requires": {
"@types/json5": "^0.0.29",
@@ -21554,9 +21562,9 @@
"integrity": "sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ=="
},
"enhanced-resolve": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.3.tgz",
- "integrity": "sha512-EGAbGvH7j7Xt2nc0E7D99La1OiEs8LnyimkRgwExpUMScN6O+3x9tIWs7PLQZVNx4YD+00skHXPXi1yQHpAmZA==",
+ "version": "5.9.2",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.9.2.tgz",
+ "integrity": "sha512-GIm3fQfwLJ8YZx2smuHpBKkXC1yOk+OBEmKckVyL0i/ea8mqDEykK3ld5dgH1QYPNyT/lIllxV2LULnxCHaHkA==",
"requires": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
@@ -21690,9 +21698,9 @@
"integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho="
},
"which-pm-runs": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.0.0.tgz",
- "integrity": "sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs="
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz",
+ "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="
},
"wide-align": {
"version": "1.1.5",
diff --git a/src/fireedge/package.json b/src/fireedge/package.json
index 493f801f2d..d444069661 100644
--- a/src/fireedge/package.json
+++ b/src/fireedge/package.json
@@ -81,6 +81,7 @@
"fast-xml-parser": "3.19.0",
"fs-extra": "9.0.1",
"fuse.js": "6.4.1",
+ "guacamole-common-js": "1.3.1",
"helmet": "4.1.1",
"http": "0.0.1-security",
"http-proxy-middleware": "1.0.5",
diff --git a/src/fireedge/src/client/apps/sunstone/index.js b/src/fireedge/src/client/apps/sunstone/index.js
index c66b1f08c5..d90e40467c 100644
--- a/src/fireedge/src/client/apps/sunstone/index.js
+++ b/src/fireedge/src/client/apps/sunstone/index.js
@@ -62,8 +62,8 @@ const Sunstone = ({ store = {}, location = '' }) => (
Sunstone.propTypes = {
location: PropTypes.string,
- context: PropTypes.shape({}),
- store: PropTypes.shape({}),
+ context: PropTypes.object,
+ store: PropTypes.object,
}
Sunstone.displayName = 'SunstoneApp'
diff --git a/src/fireedge/src/client/apps/sunstone/routes.js b/src/fireedge/src/client/apps/sunstone/routes.js
index 738ff21278..740b986f4d 100644
--- a/src/fireedge/src/client/apps/sunstone/routes.js
+++ b/src/fireedge/src/client/apps/sunstone/routes.js
@@ -27,10 +27,14 @@ const Dashboard = loadable(
const Settings = loadable(() => import('client/containers/Settings'), {
ssr: false,
})
+const Guacamole = loadable(() => import('client/containers/Guacamole'), {
+ ssr: false,
+})
export const PATH = {
DASHBOARD: '/dashboard',
SETTINGS: '/settings',
+ GUACAMOLE: '/guacamole/:id/:type',
}
export const ENDPOINTS = [
@@ -50,6 +54,12 @@ export const ENDPOINTS = [
position: -1,
Component: Settings,
},
+ {
+ label: 'Guacamole',
+ disabledSidebar: true,
+ path: PATH.GUACAMOLE,
+ Component: Guacamole,
+ },
]
/**
diff --git a/src/fireedge/src/client/components/Buttons/ConsoleAction.js b/src/fireedge/src/client/components/Buttons/ConsoleAction.js
new file mode 100644
index 0000000000..7571a619ec
--- /dev/null
+++ b/src/fireedge/src/client/components/Buttons/ConsoleAction.js
@@ -0,0 +1,108 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+import { memo, useMemo, useCallback, ReactElement } from 'react'
+import PropTypes from 'prop-types'
+import { useHistory, generatePath } from 'react-router-dom'
+
+import {
+ AppleImac2021 as VncIcon,
+ TerminalOutline as SshIcon,
+ Windows as RdpIcon,
+} from 'iconoir-react'
+import { SubmitButton } from 'client/components/FormControl'
+
+import { useViews } from 'client/features/Auth'
+import { useLazyGetGuacamoleSessionQuery } from 'client/features/OneApi/vm'
+import {
+ nicsIncludesTheConnectionType,
+ isAvailableAction,
+} from 'client/models/VirtualMachine'
+import { Translate } from 'client/components/HOC'
+import { T, VM, RESOURCE_NAMES, VM_ACTIONS } from 'client/constants'
+import { PATH } from 'client/apps/sunstone/routes'
+
+const GUACAMOLE_BUTTON = {
+ vnc: { tooltip: T.Vnc, icon: },
+ ssh: { tooltip: T.Ssh, icon: },
+ rdp: { tooltip: T.Rdp, icon: },
+}
+
+const GuacamoleButton = memo(
+ /**
+ * @param {object} options - Options
+ * @param {VM} options.vm - Virtual machine
+ * @param {'vnc'|'ssh'|'rdp'} options.connectionType - Connection type
+ * @param {Function} [options.onClick] - Handle click for button
+ * @returns {ReactElement} - Guacamole button
+ */
+ ({ vm, connectionType, onClick }) => {
+ const history = useHistory()
+ const { view, [RESOURCE_NAMES.VM]: vmView } = useViews()
+ const [getSession, { isLoading }] = useLazyGetGuacamoleSessionQuery()
+
+ const isDisabled = useMemo(() => {
+ const noAction = vmView?.actions?.[connectionType] !== true
+ const noAvailable = isAvailableAction(connectionType)(vm)
+
+ return noAction || noAvailable
+ }, [view, vm])
+
+ const { tooltip, icon } = GUACAMOLE_BUTTON[connectionType]
+
+ const goToConsole =
+ onClick ??
+ useCallback(
+ async (evt) => {
+ try {
+ evt.stopPropagation()
+
+ const params = { id: vm?.ID, type: connectionType }
+ await getSession(params)
+ history.push(generatePath(PATH.GUACAMOLE, params))
+ } catch {}
+ },
+ [vm?.ID, connectionType, history]
+ )
+
+ if (
+ isDisabled ||
+ (connectionType !== VM_ACTIONS.VNC &&
+ !nicsIncludesTheConnectionType(vm, connectionType))
+ ) {
+ return null
+ }
+
+ return (
+ }
+ isSubmitting={isLoading}
+ onClick={goToConsole}
+ />
+ )
+ }
+)
+
+GuacamoleButton.propTypes = {
+ vm: PropTypes.object,
+ connectionType: PropTypes.string,
+ onClick: PropTypes.func,
+}
+
+GuacamoleButton.displayName = 'GuacamoleButton'
+
+export { GuacamoleButton }
diff --git a/src/fireedge/src/client/components/Buttons/index.js b/src/fireedge/src/client/components/Buttons/index.js
index 8c7ccdd35d..c4b46ca3ab 100644
--- a/src/fireedge/src/client/components/Buttons/index.js
+++ b/src/fireedge/src/client/components/Buttons/index.js
@@ -15,3 +15,4 @@
* ------------------------------------------------------------------------- */
export * from 'client/components/Buttons/ScheduleAction'
+export * from 'client/components/Buttons/ConsoleAction'
diff --git a/src/fireedge/src/client/components/Cards/VirtualMachineCard.js b/src/fireedge/src/client/components/Cards/VirtualMachineCard.js
index f1fc495cbd..dc02b1bcc8 100644
--- a/src/fireedge/src/client/components/Cards/VirtualMachineCard.js
+++ b/src/fireedge/src/client/components/Cards/VirtualMachineCard.js
@@ -36,9 +36,10 @@ const VirtualMachineCard = memo(
* @param {object} props - Props
* @param {VM} props.vm - Virtual machine resource
* @param {object} props.rootProps - Props to root component
+ * @param {ReactElement} [props.actions] - Actions
* @returns {ReactElement} - Card
*/
- ({ vm, rootProps }) => {
+ ({ vm, rootProps, actions }) => {
const classes = rowStyles()
const { ID, NAME, UNAME, GNAME, IPS, STIME, ETIME, LOCK } = vm
@@ -89,6 +90,7 @@ const VirtualMachineCard = memo(
)}
+ {actions &&
{actions}
}
)
}
@@ -99,6 +101,7 @@ VirtualMachineCard.propTypes = {
rootProps: PropTypes.shape({
className: PropTypes.string,
}),
+ actions: PropTypes.any,
}
VirtualMachineCard.displayName = 'VirtualMachineCard'
diff --git a/src/fireedge/src/client/components/Consoles/Guacamole/buttons.js b/src/fireedge/src/client/components/Consoles/Guacamole/buttons.js
new file mode 100644
index 0000000000..db99f2013c
--- /dev/null
+++ b/src/fireedge/src/client/components/Consoles/Guacamole/buttons.js
@@ -0,0 +1,213 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+import { memo, useCallback, useState, ReactElement } from 'react'
+import PropTypes from 'prop-types'
+import { Refresh, Maximize, Camera } from 'iconoir-react'
+import {
+ Tooltip,
+ Typography,
+ Button,
+ IconButton,
+ CircularProgress,
+} from '@mui/material'
+
+import { Translate } from 'client/components/HOC'
+import { downloadFile } from 'client/utils'
+import { T, GuacamoleSession } from 'client/constants'
+
+const GuacamoleCtrlAltDelButton = memo(
+ /**
+ * @param {GuacamoleSession} session - Guacamole session
+ * @returns {ReactElement} Guacamole mouse plugin
+ */
+ (session) => {
+ const { id, client } = session
+
+ const handleClick = useCallback(() => {
+ if (!client) return
+
+ const ctrlKey = 65507
+ const altKey = 65513
+ const delKey = 65535
+
+ client?.sendKeyEvent(1, ctrlKey)
+ client?.sendKeyEvent(1, altKey)
+ client?.sendKeyEvent(1, delKey)
+ client?.sendKeyEvent(0, delKey)
+ client?.sendKeyEvent(0, altKey)
+ client?.sendKeyEvent(0, ctrlKey)
+ }, [client])
+
+ return (
+
+ )
+ }
+)
+
+/**
+ * @param {GuacamoleSession} session - Guacamole session
+ * @returns {ReactElement} Guacamole mouse plugin
+ */
+const GuacamoleReconnectButton = (session) => {
+ const { id, isLoading, handleReconnect } = session
+ const [reconnecting, setReconnecting] = useState(false)
+
+ const handleReconnectSession = async () => {
+ if (isLoading) return
+
+ setReconnecting(true)
+ await handleReconnect()
+ setReconnecting(false)
+ }
+
+ return (
+
+
+
+ }
+ >
+
+ {reconnecting || isLoading ? (
+
+ ) : (
+
+ )}
+
+
+ )
+}
+
+const GuacamoleFullScreenButton = memo(
+ /**
+ * @param {GuacamoleSession} session - Guacamole session
+ * @returns {ReactElement} Guacamole mouse plugin
+ */
+ (session) => {
+ const { id, viewport } = session
+
+ const handleClick = useCallback(() => {
+ // If the document is not in full screen mode make the video full screen
+ if (!document.fullscreenElement && document.fullscreenEnabled) {
+ viewport?.requestFullscreen?.()
+ } else if (document.exitFullscreen) {
+ document.exitFullscreen()
+ }
+ }, [viewport])
+
+ return (
+
+
+
+ }
+ >
+
+
+
+
+ )
+ }
+)
+
+const GuacamoleScreenshotButton = memo(
+ /**
+ * @param {GuacamoleSession} session - Guacamole session
+ * @returns {ReactElement} Guacamole mouse plugin
+ */
+ (session) => {
+ const { id, client } = session
+
+ const handleClick = useCallback(() => {
+ if (!client) return
+
+ const canvas = client.getDisplay().getDefaultLayer().getCanvas()
+
+ canvas.toBlob((blob) => {
+ downloadFile(new File([blob], 'screenshot.png'))
+ }, 'image/png')
+ }, [client])
+
+ return (
+
+
+
+ }
+ >
+
+
+
+
+ )
+ }
+)
+
+const ButtonPropTypes = {
+ client: PropTypes.object,
+ viewport: PropTypes.object,
+}
+
+GuacamoleCtrlAltDelButton.displayName = 'GuacamoleCtrlAltDelButton'
+GuacamoleCtrlAltDelButton.propTypes = ButtonPropTypes
+GuacamoleReconnectButton.displayName = 'GuacamoleReconnectButton'
+GuacamoleReconnectButton.propTypes = ButtonPropTypes
+GuacamoleFullScreenButton.displayName = 'GuacamoleFullScreenButton'
+GuacamoleFullScreenButton.propTypes = ButtonPropTypes
+GuacamoleScreenshotButton.displayName = 'GuacamoleScreenshotButton'
+GuacamoleScreenshotButton.propTypes = ButtonPropTypes
+
+export {
+ GuacamoleCtrlAltDelButton,
+ GuacamoleReconnectButton,
+ GuacamoleFullScreenButton,
+ GuacamoleScreenshotButton,
+}
diff --git a/src/fireedge/src/client/components/Consoles/Guacamole/client.js b/src/fireedge/src/client/components/Consoles/Guacamole/client.js
new file mode 100644
index 0000000000..ff96c76615
--- /dev/null
+++ b/src/fireedge/src/client/components/Consoles/Guacamole/client.js
@@ -0,0 +1,217 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+import { useRef, useEffect, RefObject } from 'react'
+import { WebSocketTunnel, Tunnel, Client } from 'guacamole-common-js'
+
+import { useGeneralApi } from 'client/features/General'
+import { useGuacamole, useGuacamoleApi } from 'client/features/Guacamole'
+import { getConnectString, clientStateToString } from 'client/models/Guacamole'
+import { fakeDelay, isDevelopment } from 'client/utils'
+import {
+ GuacamoleSession, // eslint-disable-line no-unused-vars
+ SOCKETS,
+ GUACAMOLE_STATES_STR,
+ THUMBNAIL_UPDATE_FREQUENCY,
+} from 'client/constants'
+
+const {
+ CONNECTING,
+ CONNECTED,
+ DISCONNECTING,
+ DISCONNECTED,
+ CLIENT_ERROR,
+ TUNNEL_ERROR,
+} = GUACAMOLE_STATES_STR
+
+// eslint-disable-next-line jsdoc/valid-types
+/**
+ * @typedef {GuacamoleSession & {
+ * handleConnect: Function,
+ * handleDisconnect: Function,
+ * handleReconnect: function():Promise,
+ * }} GuacamoleClientType
+ */
+
+/**
+ * @param {object} options - Client options
+ * @param {string} options.id - Session includes type and VM id. Eg: '6-vnc'
+ * @param {RefObject} options.display - Session display. Only exists if display plugins is enabled
+ * @returns {GuacamoleClientType} Guacamole client props
+ */
+const GuacamoleClient = ({ id, display }) => {
+ const guac = useRef(createGuacamoleClient()).current
+
+ // Automatically update the client thumbnail
+ guac.client.onsync = () => handleUpdateThumbnail()
+
+ const { enqueueError, enqueueInfo, enqueueSuccess } = useGeneralApi()
+ const { token, ...session } = useGuacamole(id)
+ const {
+ setConnectionState,
+ setTunnelUnstable,
+ setMultiTouchSupport,
+ updateThumbnail,
+ } = useGuacamoleApi(id)
+
+ const handleConnect = (width, height, force = false) => {
+ if (!session?.isUninitialized && !session.isDisconnected && !force) return
+
+ isDevelopment() && console.log(`connect ${id} 🔵`)
+
+ const options = { token, display, width, height }
+ const connectString = getConnectString(options)
+
+ guac.client.connect(connectString)
+ }
+
+ const handleDisconnect = () => {
+ try {
+ isDevelopment() && console.log(`disconnect ${id} 🔴`)
+ guac.client?.disconnect()
+ } catch {}
+ }
+
+ const handleReconnect = async (width, height) => {
+ session?.isConnected && handleDisconnect()
+
+ // sleep to avoid quick reconnection
+ await fakeDelay(1500)
+ handleConnect(width, height, true)
+ }
+
+ /**
+ * Store the thumbnail of the given managed client within the connection
+ * history under its associated ID. If the client is not connected, this
+ * function has no effect.
+ */
+ const handleUpdateThumbnail = () => {
+ const nowTimestamp = new Date().getTime()
+ const lastTimestamp = session?.thumbnail?.timestamp
+
+ if (
+ lastTimestamp &&
+ nowTimestamp - lastTimestamp < THUMBNAIL_UPDATE_FREQUENCY
+ )
+ return
+
+ const clientDisplay = guac.client?.getDisplay()
+
+ if (clientDisplay?.getWidth() <= 0 || clientDisplay?.getHeight() <= 0)
+ return
+
+ // Get screenshot
+ const canvas = clientDisplay.flatten()
+
+ // Calculate scale of thumbnail (max 320x240, max zoom 100%)
+ const scale = Math.min(320 / canvas.width, 240 / canvas.height, 1)
+
+ // Create thumbnail canvas
+ const thumbnail = document.createElement('canvas')
+ thumbnail.width = canvas.width * scale
+ thumbnail.height = canvas.height * scale
+
+ // Scale screenshot to thumbnail
+ const context = thumbnail.getContext('2d')
+ context.drawImage(
+ canvas,
+ 0,
+ 0,
+ canvas.width,
+ canvas.height,
+ 0,
+ 0,
+ thumbnail.width,
+ thumbnail.height
+ )
+
+ thumbnail.toBlob((blob) => {
+ const url = URL.createObjectURL(blob)
+ const newThumbnail = { timestamp: nowTimestamp, canvas: url }
+ updateThumbnail({ thumbnail: newThumbnail })
+ }, 'image/webp')
+ }
+
+ useEffect(() => {
+ guac.tunnel.onerror = (status) => {
+ setConnectionState({ state: TUNNEL_ERROR, statusCode: status.code })
+ }
+
+ guac.tunnel.onstatechange = (state) => {
+ ;({
+ [Tunnel.State.CONNECTING]: () => {
+ setConnectionState({ state: CONNECTING })
+ },
+ [Tunnel.State.OPEN]: () => {
+ setTunnelUnstable({ unstable: false })
+ },
+ [Tunnel.State.UNSTABLE]: () => {
+ setTunnelUnstable({ unstable: true })
+ },
+ [Tunnel.State.CLOSED]: () => {
+ setConnectionState({ state: DISCONNECTED })
+ },
+ }[state]?.())
+ }
+
+ guac.client.onstatechange = (state) => {
+ const stateString = clientStateToString(state)
+ const isDisconnect = [DISCONNECTING, DISCONNECTED].includes(stateString)
+ const isDisconnected = DISCONNECTED === stateString
+ const isConnected = CONNECTED === stateString
+
+ isConnected && enqueueSuccess('Connection established')
+ isDisconnected && enqueueInfo('Disconnected')
+
+ !isDisconnect && setConnectionState({ state: stateString })
+ }
+
+ guac.client.onerror = (status) => {
+ enqueueError(status.message)
+ setConnectionState({ state: CLIENT_ERROR, statusCode: status.code })
+ }
+
+ guac.client.onmultitouch = (layer, touches) => {
+ setMultiTouchSupport({ touches })
+ }
+
+ return () => {
+ handleDisconnect()
+ }
+ }, [id])
+
+ useEffect(() => {
+ session?.isError && handleDisconnect()
+ }, [session?.isError])
+
+ useEffect(() => {
+ !session.isConnected && handleConnect()
+ }, [token])
+
+ return { token, ...session, ...guac, handleReconnect }
+}
+
+const createGuacamoleClient = () => {
+ const { protocol, host } = window.location
+ const websocketProtocol = protocol === 'https:' ? 'wss:' : 'ws:'
+ const guacamoleWs = `${websocketProtocol}//${host}/fireedge/${SOCKETS.GUACAMOLE}`
+
+ const tunnel = new WebSocketTunnel(guacamoleWs)
+ const client = new Client(tunnel)
+
+ return { client, tunnel }
+}
+
+export default GuacamoleClient
diff --git a/src/fireedge/src/client/components/Consoles/Guacamole/index.js b/src/fireedge/src/client/components/Consoles/Guacamole/index.js
new file mode 100644
index 0000000000..e77301bf07
--- /dev/null
+++ b/src/fireedge/src/client/components/Consoles/Guacamole/index.js
@@ -0,0 +1,63 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+/* eslint-disable no-unused-vars */
+/* eslint-disable jsdoc/valid-types */
+import { useRef } from 'react'
+
+import {
+ useGetLatest,
+ reducePlugin,
+} from 'client/components/Consoles/Guacamole/utils'
+import GuacamoleClient, {
+ GuacamoleClientType,
+} from 'client/components/Consoles/Guacamole/client'
+import {
+ GuacamoleDisplayPlugin,
+ GuacamoleKeyboardPlugin,
+ GuacamoleMousePlugin,
+} from 'client/components/Consoles/Guacamole/plugins'
+
+/**
+ * Creates guacamole session.
+ *
+ * @param {object} options - Options
+ * @param {string} options.id - Session includes type and VM id. Eg: '6-vnc'
+ * @param {...any} [plugins] - Plugins
+ * @returns {GuacamoleClientType &
+ * GuacamoleDisplayPlugin &
+ * GuacamoleKeyboardPlugin &
+ * GuacamoleMousePlugin} session
+ */
+const useGuacamoleSession = (options, ...plugins) => {
+ // Create the guacamole instance
+ const instanceRef = useRef({})
+ const getInstance = useGetLatest(instanceRef.current)
+
+ // Assign the options to the instance
+ Object.assign(getInstance(), { ...options })
+
+ // Assign the session and plugins to the instance
+ Object.assign(
+ getInstance(),
+ [GuacamoleClient, ...plugins].reduce(reducePlugin, getInstance())
+ )
+
+ return getInstance()
+}
+
+export * from 'client/components/Consoles/Guacamole/plugins'
+export * from 'client/components/Consoles/Guacamole/buttons'
+export { useGuacamoleSession }
diff --git a/src/fireedge/src/client/components/Consoles/Guacamole/plugins/clipboard.js b/src/fireedge/src/client/components/Consoles/Guacamole/plugins/clipboard.js
new file mode 100644
index 0000000000..26cea72be1
--- /dev/null
+++ b/src/fireedge/src/client/components/Consoles/Guacamole/plugins/clipboard.js
@@ -0,0 +1,158 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+import { useEffect, useState } from 'react'
+import {
+ StringReader,
+ StringWriter,
+ BlobReader,
+ BlobWriter,
+} from 'guacamole-common-js'
+
+import { GuacamoleSession } from 'client/constants'
+import { isDevelopment } from 'client/utils'
+
+const createClipboardData = ({ id, type, data } = {}) => ({
+ source: id,
+ /**
+ * The mimetype of the data currently stored within the clipboard.
+ *
+ * @type {string}
+ */
+ type: type || 'text/plain',
+ /**
+ * The data currently stored within the clipboard.
+ *
+ * @type {string|Blob|File}
+ */
+ data: data ?? '',
+})
+
+/**
+ * @param {GuacamoleSession} session - Current session
+ * @returns {null} null
+ */
+const GuacamoleClipboard = (session) => {
+ const { id, client, isConnected } = session ?? {}
+
+ const [pendingRead, setPendingRead] = useState(() => false)
+ const [storedClipboard, storeClipboard] = useState(() =>
+ createClipboardData({ id })
+ )
+
+ const getLocalClipboard = async () => {
+ try {
+ if (pendingRead) return
+
+ const text = await navigator.clipboard.readText()
+ storeClipboard((prev) => ({ ...prev, data: text, type: 'text/plain' }))
+
+ return text
+ } finally {
+ setPendingRead(false)
+ }
+ }
+
+ const setLocalClipboard = async ({ data, type }) => {
+ if (type !== 'text/plain') return
+
+ await navigator.clipboard.writeText(data)
+ storeClipboard((prev) => ({ ...prev, data, type }))
+ }
+
+ const setClientClipboard = ({ data, type = 'text/plain' } = {}) => {
+ // Create stream with proper mimetype
+ const stream = client.createClipboardStream(type)
+
+ // Send data as a string if it is stored as a string
+ if (typeof data === 'string') {
+ const writer = new StringWriter(stream)
+ writer.sendText(data)
+ writer.sendEnd()
+ }
+ // Otherwise, assume the data is a File/Blob
+ else {
+ // Write File/Blob asynchronously
+ const writer = new BlobWriter(stream)
+ writer.oncomplete = () => {
+ writer.sendEnd()
+ }
+
+ // Begin sending data
+ writer.sendBlob(data)
+ }
+ }
+
+ const resyncClipboard = async () => {
+ try {
+ const localClipboard = await getLocalClipboard()
+ setClientClipboard({ data: localClipboard })
+ } catch (e) {
+ isDevelopment() && console.log(e)
+ }
+ }
+
+ const focusGained = (evt) => {
+ // Only recheck clipboard if it's the window itself that gained focus
+ evt.target === window && resyncClipboard()
+ }
+
+ useEffect(() => {
+ if (!isConnected) return
+ ;(async () => await resyncClipboard())()
+
+ window.addEventListener('load', resyncClipboard, true)
+ window.addEventListener('copy', resyncClipboard)
+ window.addEventListener('cut', resyncClipboard)
+ window.addEventListener('focus', focusGained, true)
+
+ client.onclipboard = (stream, mimetype) => {
+ // If the received data is text, read it as a simple string
+ if (/^text\//.exec(mimetype)) {
+ const reader = new StringReader(stream)
+
+ // Assemble received data into a single string
+ let data = ''
+ reader.ontext = (text) => {
+ data += text
+ }
+
+ // Set clipboard contents once stream is finished
+ reader.onend = () => {
+ setLocalClipboard({ data, type: mimetype })
+ }
+ }
+ // Otherwise read the clipboard data as a Blob
+ else {
+ const reader = new BlobReader(stream, mimetype)
+
+ reader.onend = () => {
+ setLocalClipboard({ data: reader.getBlob(), type: mimetype })
+ }
+ }
+ }
+
+ return () => {
+ window.removeEventListener('load', resyncClipboard, true)
+ window.removeEventListener('copy', resyncClipboard)
+ window.removeEventListener('cut', resyncClipboard)
+ window.removeEventListener('focus', focusGained, true)
+ }
+ }, [isConnected])
+
+ return { storedClipboard }
+}
+
+export { GuacamoleClipboard }
diff --git a/src/fireedge/src/client/components/Consoles/Guacamole/plugins/display.js b/src/fireedge/src/client/components/Consoles/Guacamole/plugins/display.js
new file mode 100644
index 0000000000..6d8f82cfbe
--- /dev/null
+++ b/src/fireedge/src/client/components/Consoles/Guacamole/plugins/display.js
@@ -0,0 +1,147 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+import {
+ useEffect,
+ useMemo,
+ useRef,
+ MutableRefObject, // eslint-disable-line no-unused-vars
+ ReactElement, // eslint-disable-line no-unused-vars
+} from 'react'
+import { styled } from '@mui/material'
+
+import { GuacamoleSession } from 'client/constants'
+
+/**
+ * @typedef GuacamoleDisplayPlugin
+ * @property {MutableRefObject} [display] - Display object
+ * @property {MutableRefObject} [viewport] - Viewport object is the wrapper of display
+ * @property {ReactElement} [displayElement] - Display element
+ */
+
+const Viewport = styled('div')({
+ backgroundColor: '#222431',
+ width: '100%',
+ height: '100%',
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ placeContent: 'center',
+})
+
+const Display = styled('div')({
+ zIndex: 1,
+ overflow: 'hidden',
+ '& > *': { cursor: 'none' },
+})
+
+/**
+ * @param {GuacamoleSession} session - Guacamole session
+ * @returns {GuacamoleDisplayPlugin} Guacamole display plugin
+ */
+const GuacamoleDisplay = (session) => {
+ const { id, container, header, client, isConnected } = session ?? {}
+ const isSSH = useMemo(() => id.includes('ssh'), [id])
+
+ const viewportRef = useRef(null)
+ const displayRef = useRef(null)
+
+ const containerResized = () => {
+ if (!client || !container) return
+
+ const clientDisplay = client.getDisplay()
+ const pixelDensity = window.devicePixelRatio || 1
+ const headerHeight = header?.offsetHeight ?? 0
+
+ const width = document.fullscreenElement
+ ? window.innerWidth * pixelDensity
+ : container.offsetWidth * pixelDensity
+
+ const height = document.fullscreenElement
+ ? window.innerHeight * pixelDensity
+ : (container.offsetHeight - headerHeight) * pixelDensity
+
+ if (
+ clientDisplay.getWidth() !== width ||
+ clientDisplay.getHeight() !== height
+ ) {
+ client.sendSize(width, height)
+ }
+
+ // when type connection is SSH, display doesn't need scale
+ id.includes('vnc') && updateDisplayScale()
+ }
+
+ const updateDisplayScale = () => {
+ if (!client) return
+
+ const clientDisplay = client.getDisplay()
+ // Get screen resolution.
+ const origHeight = Math.max(clientDisplay.getHeight(), 1)
+ const origWidth = Math.max(clientDisplay.getWidth(), 1)
+
+ const headerHeight = header?.offsetHeight ?? 0
+
+ const containerWidth = document.fullscreenElement
+ ? window.innerWidth
+ : container.offsetWidth
+
+ const containerHeight = document.fullscreenElement
+ ? window.innerHeight
+ : container.offsetHeight - headerHeight
+
+ const xScale = containerWidth / origWidth
+ const yScale = containerHeight / origHeight
+
+ // This is done to handle both X and Y axis
+ let scale = Math.min(yScale, xScale)
+
+ // Limit to 1
+ scale = Math.min(scale, 1)
+
+ scale !== 0 && clientDisplay.scale(scale)
+ }
+
+ useEffect(() => {
+ if (!isConnected) return
+
+ const display = displayRef.current
+ const clientDisplay = client.getDisplay()
+
+ display?.appendChild(clientDisplay.getElement())
+
+ const pollResize = setInterval(containerResized, 10)
+
+ return () => {
+ display?.childNodes.forEach((node) => display?.removeChild(node))
+ clearInterval(pollResize)
+ }
+ }, [isConnected])
+
+ return {
+ display: displayRef.current,
+ viewport: viewportRef.current,
+ displayElement: (
+
+
+
+ ),
+ }
+}
+
+export { GuacamoleDisplay }
diff --git a/src/fireedge/src/client/components/Consoles/Guacamole/plugins/index.js b/src/fireedge/src/client/components/Consoles/Guacamole/plugins/index.js
new file mode 100644
index 0000000000..bb723bf043
--- /dev/null
+++ b/src/fireedge/src/client/components/Consoles/Guacamole/plugins/index.js
@@ -0,0 +1,20 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+
+export * from 'client/components/Consoles/Guacamole/plugins/clipboard'
+export * from 'client/components/Consoles/Guacamole/plugins/display'
+export * from 'client/components/Consoles/Guacamole/plugins/keyboard'
+export * from 'client/components/Consoles/Guacamole/plugins/mouse'
diff --git a/src/fireedge/src/client/components/Consoles/Guacamole/plugins/keyboard.js b/src/fireedge/src/client/components/Consoles/Guacamole/plugins/keyboard.js
new file mode 100644
index 0000000000..0869ca9201
--- /dev/null
+++ b/src/fireedge/src/client/components/Consoles/Guacamole/plugins/keyboard.js
@@ -0,0 +1,55 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+// eslint-disable-next-line no-unused-vars
+import { useCallback, useEffect, useRef, useState } from 'react'
+import { Keyboard } from 'guacamole-common-js'
+
+import { GuacamoleSession } from 'client/constants'
+
+/**
+ * @typedef GuacamoleKeyboardPlugin
+ * @property {Keyboard} [keyboard] - Guacamole keyboard
+ */
+
+/**
+ * @param {GuacamoleSession} session - Guacamole session
+ * @returns {GuacamoleKeyboardPlugin} Guacamole keyboard plugin
+ */
+const GuacamoleKeyboard = (session) => {
+ const { client, isConnected } = session ?? {}
+
+ const keyboardRef = useRef(null)
+
+ useEffect(() => {
+ if (!isConnected) return
+
+ keyboardRef.current = new Keyboard(document)
+
+ keyboardRef.current.onkeydown = (keySym) => client?.sendKeyEvent(1, keySym)
+ keyboardRef.current.onkeyup = (keySym) => client?.sendKeyEvent(0, keySym)
+
+ // Release all keys when window loses focus
+ window.addEventListener('blur', keyboardRef.current?.reset)
+
+ return () => {
+ window.removeEventListener('blur', keyboardRef.current?.reset)
+ }
+ }, [isConnected])
+
+ return { keyboard: keyboardRef.current }
+}
+
+export { GuacamoleKeyboard }
diff --git a/src/fireedge/src/client/components/Consoles/Guacamole/plugins/mouse.js b/src/fireedge/src/client/components/Consoles/Guacamole/plugins/mouse.js
new file mode 100644
index 0000000000..bec42d548a
--- /dev/null
+++ b/src/fireedge/src/client/components/Consoles/Guacamole/plugins/mouse.js
@@ -0,0 +1,78 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+// eslint-disable-next-line no-unused-vars
+import { useEffect, useRef } from 'react'
+import { Mouse } from 'guacamole-common-js'
+
+import { GuacamoleSession } from 'client/constants'
+
+/**
+ * @typedef GuacamoleMousePlugin
+ * @property {Mouse} [mouse] - Guacamole mouse
+ */
+
+/**
+ * @param {GuacamoleSession} session - Guacamole session
+ * @returns {GuacamoleMousePlugin} Guacamole mouse plugin
+ */
+const GuacamoleMouse = (session) => {
+ const { client, display, isConnected } = session ?? {}
+
+ const mouseRef = useRef(null)
+
+ const handleMouseState = (mouseState, scaleMouse = false) => {
+ // Do not attempt to handle mouse state changes if the client
+ // or display are not yet available
+ if (!client) return
+
+ if (scaleMouse) {
+ const clientScale = client.getDisplay().getScale()
+ mouseState.y = mouseState.y / clientScale
+ mouseState.x = mouseState.x / clientScale
+ }
+
+ // Send mouse state, show cursor if necessary
+ // client.display?.showCursor(!localCursor)
+ client.sendMouseState(mouseState)
+ }
+
+ useEffect(() => {
+ if (!isConnected) return
+
+ const mouse = new Mouse(client.getDisplay().getElement())
+ mouseRef.current = mouse
+
+ mouse.onmousedown = mouse.onmouseup = (mouseState) => {
+ // Ensure focus is regained via mousedown before forwarding event
+ display?.focus()
+ handleMouseState(mouseState)
+ }
+
+ // Forward mousemove events untouched
+ mouse.onmousemove = (mouseState) => {
+ handleMouseState(mouseState, true)
+ }
+
+ // Hide software cursor when mouse leaves display
+ mouse.onmouseout = () => {
+ // client?.display?.showCursor(false)
+ }
+ }, [isConnected])
+
+ return { mouse: mouseRef.current }
+}
+
+export { GuacamoleMouse }
diff --git a/src/fireedge/src/client/components/Consoles/Guacamole/utils.js b/src/fireedge/src/client/components/Consoles/Guacamole/utils.js
new file mode 100644
index 0000000000..7682365cc4
--- /dev/null
+++ b/src/fireedge/src/client/components/Consoles/Guacamole/utils.js
@@ -0,0 +1,41 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+import { useCallback, useRef } from 'react'
+
+/**
+ * Helps avoid a lot of potential memory leaks.
+ *
+ * @param {object} obj - Instance object
+ * @returns {function():object} - Returns the last object
+ */
+export const useGetLatest = (obj) => {
+ const ref = useRef()
+ ref.current = obj
+
+ return useCallback(() => ref.current, [])
+}
+
+/**
+ * Assign the plugin state to the previous state.
+ *
+ * @param {object} prevState - Previous state
+ * @param {function():object} plugin - Plugin
+ * @returns {object} Returns the new state
+ */
+export const reducePlugin = (prevState, plugin) => ({
+ ...prevState,
+ ...plugin(prevState),
+})
diff --git a/src/fireedge/src/client/components/Consoles/index.js b/src/fireedge/src/client/components/Consoles/index.js
new file mode 100644
index 0000000000..50fe5a58f6
--- /dev/null
+++ b/src/fireedge/src/client/components/Consoles/index.js
@@ -0,0 +1,16 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+export * from 'client/components/Consoles/Guacamole'
diff --git a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/graphicsSchema.js b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/graphicsSchema.js
index 8e6b606124..216b551406 100644
--- a/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/graphicsSchema.js
+++ b/src/fireedge/src/client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration/inputOutput/graphicsSchema.js
@@ -32,9 +32,9 @@ const TYPE = {
dependOf: '$general.HYPERVISOR',
values: (hypervisor = kvm) => {
const types = {
- [vcenter]: [T.VMRC],
- [lxc]: [T.VNC],
- }[hypervisor] ?? [T.VNC, T.SDL, T.SPICE]
+ [vcenter]: [T.Vmrc],
+ [lxc]: [T.Vnc],
+ }[hypervisor] ?? [T.Vnc, T.Sdl, T.Spice]
return arrayToOptions(types)
},
diff --git a/src/fireedge/src/client/components/HOC/AuthLayout.js b/src/fireedge/src/client/components/HOC/AuthLayout.js
index ebbb6ed0eb..e9868ad79f 100644
--- a/src/fireedge/src/client/components/HOC/AuthLayout.js
+++ b/src/fireedge/src/client/components/HOC/AuthLayout.js
@@ -19,6 +19,7 @@ import PropTypes from 'prop-types'
import { useAuth, useAuthApi } from 'client/features/Auth'
import { authApi } from 'client/features/AuthApi'
+import { oneApi } from 'client/features/OneApi'
import groupApi from 'client/features/OneApi/group'
import FullscreenProgress from 'client/components/LoadingScreen'
import { findStorageData } from 'client/utils'
@@ -46,6 +47,8 @@ const AuthLayout = ({ subscriptions = [], children }) => {
return () => {
authSubscription.unsubscribe()
+ dispatch(authApi.util.resetApiState())
+ dispatch(oneApi.util.resetApiState())
}
}, [dispatch, jwt])
diff --git a/src/fireedge/src/client/components/HOC/InternalLayout/index.js b/src/fireedge/src/client/components/HOC/InternalLayout/index.js
index 9156cafa37..32f0a55a98 100644
--- a/src/fireedge/src/client/components/HOC/InternalLayout/index.js
+++ b/src/fireedge/src/client/components/HOC/InternalLayout/index.js
@@ -14,10 +14,9 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
/* eslint-disable jsdoc/require-jsdoc */
-import { useRef, useEffect } from 'react'
+import { useRef, useEffect, useMemo } from 'react'
import PropTypes from 'prop-types'
import { useParams } from 'react-router-dom'
-import clsx from 'clsx'
import { Box, Container } from '@mui/material'
import { CSSTransition } from 'react-transition-group'
@@ -25,8 +24,9 @@ import { useGeneral, useGeneralApi } from 'client/features/General'
import Header from 'client/components/Header'
import Footer from 'client/components/Footer'
import internalStyles from 'client/components/HOC/InternalLayout/styles'
+import { sidebar } from 'client/theme/defaults'
-const InternalLayout = ({ title, children }) => {
+const InternalLayout = ({ title, customHeader, disabledSidebar, children }) => {
const classes = internalStyles()
const container = useRef()
const { isFixMenu } = useGeneral()
@@ -40,9 +40,27 @@ const InternalLayout = ({ title, children }) => {
return (
+ disabledSidebar
+ ? {}
+ : {
+ marginLeft: {
+ lg: isFixMenu
+ ? `${sidebar.fixed}px`
+ : `${sidebar.minified}px`,
+ },
+ },
+ [isFixMenu, disabledSidebar]
+ )}
>
-
+ {customHeader ?? (
+
+ )}
{
InternalLayout.propTypes = {
title: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
+ customHeader: PropTypes.node,
+ disabledSidebar: PropTypes.bool,
children: PropTypes.any,
}
diff --git a/src/fireedge/src/client/components/HOC/InternalLayout/styles.js b/src/fireedge/src/client/components/HOC/InternalLayout/styles.js
index 680089a8f4..0d2e1f87fd 100644
--- a/src/fireedge/src/client/components/HOC/InternalLayout/styles.js
+++ b/src/fireedge/src/client/components/HOC/InternalLayout/styles.js
@@ -14,7 +14,7 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
import makeStyles from '@mui/styles/makeStyles'
-import { sidebar, toolbar, footer } from 'client/theme/defaults'
+import { toolbar, footer } from 'client/theme/defaults'
export default makeStyles((theme) => ({
root: {
@@ -27,14 +27,6 @@ export default makeStyles((theme) => ({
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
- [theme.breakpoints.up('lg')]: {
- marginLeft: sidebar.minified,
- },
- },
- isDrawerFixed: {
- [theme.breakpoints.up('lg')]: {
- marginLeft: sidebar.fixed,
- },
},
main: {
height: '100vh',
@@ -49,7 +41,6 @@ export default makeStyles((theme) => ({
},
},
scrollable: {
- backgroundColor: theme.palette.background.default,
paddingTop: theme.spacing(2),
paddingBottom: theme.spacing(2),
height: '100%',
diff --git a/src/fireedge/src/client/components/Header/Popover.js b/src/fireedge/src/client/components/Header/Popover.js
index 7cda82f23e..ab41835105 100644
--- a/src/fireedge/src/client/components/Header/Popover.js
+++ b/src/fireedge/src/client/components/Header/Popover.js
@@ -38,6 +38,7 @@ const HeaderPopover = memo(
icon,
buttonLabel,
buttonProps,
+ onMouseHover,
headerTitle,
popperProps,
children,
@@ -78,7 +79,9 @@ const HeaderPopover = memo(
aria-haspopup
aria-describedby={hasId}
aria-expanded={open ? 'true' : 'false'}
- onClick={handleClick}
+ {...(onMouseHover
+ ? { onMouseEnter: handleClick, onMouseLeave: handleClose }
+ : { onClick: handleClick })}
size="small"
endIcon={}
startIcon={icon}
@@ -147,6 +150,7 @@ HeaderPopover.propTypes = {
buttonProps: PropTypes.object,
tooltip: PropTypes.any,
headerTitle: PropTypes.any,
+ onMouseHover: PropTypes.bool,
disablePadding: PropTypes.bool,
popperProps: PropTypes.object,
children: PropTypes.func,
@@ -160,6 +164,7 @@ HeaderPopover.defaultProps = {
buttonProps: {},
headerTitle: undefined,
disablePadding: false,
+ onMouseHover: false,
popperProps: {},
children: () => undefined,
}
diff --git a/src/fireedge/src/client/components/Header/index.js b/src/fireedge/src/client/components/Header/index.js
index 82c97119ed..b797a07b17 100644
--- a/src/fireedge/src/client/components/Header/index.js
+++ b/src/fireedge/src/client/components/Header/index.js
@@ -36,7 +36,7 @@ import Group from 'client/components/Header/Group'
import Zone from 'client/components/Header/Zone'
import { sentenceCase } from 'client/utils'
-const Header = () => {
+const Header = ({ disabledSidebar = false }) => {
const { isOneAdmin } = useAuth()
const { fixMenu } = useGeneralApi()
const { appTitle, title, isBeta, withGroupSwitcher } = useGeneral()
@@ -45,15 +45,17 @@ const Header = () => {
return (
- fixMenu(true)}
- edge="start"
- size="small"
- variant="outlined"
- sx={{ display: { lg: 'none' } }}
- >
-
-
+ {!disabledSidebar && (
+ fixMenu(true)}
+ edge="start"
+ size="small"
+ variant="outlined"
+ sx={{ display: { lg: 'none' } }}
+ >
+
+
+ )}
{
)}
-
- {title}
-
+ {title && (
+
+ {title}
+
+ )}
{
}
Header.propTypes = {
+ disabledSidebar: PropTypes.bool,
scrollContainer: PropTypes.object,
}
-Header.defaultProps = {
- scrollContainer: null,
-}
-
export default Header
diff --git a/src/fireedge/src/client/components/Sidebar/index.js b/src/fireedge/src/client/components/Sidebar/index.js
index a9380ab224..1c90ed5885 100644
--- a/src/fireedge/src/client/components/Sidebar/index.js
+++ b/src/fireedge/src/client/components/Sidebar/index.js
@@ -16,6 +16,7 @@
/* eslint-disable jsdoc/require-jsdoc */
import { useMemo } from 'react'
import PropTypes from 'prop-types'
+import { useLocation, matchPath } from 'react-router'
import clsx from 'clsx'
import {
@@ -41,6 +42,7 @@ import SidebarLink from 'client/components/Sidebar/SidebarLink'
import SidebarCollapseItem from 'client/components/Sidebar/SidebarCollapseItem'
const Sidebar = ({ endpoints }) => {
+ const { pathname } = useLocation()
const classes = sidebarStyles()
const isUpLg = useMediaQuery((theme) => theme.breakpoints.up('lg'), {
noSsr: true,
@@ -66,6 +68,18 @@ const Sidebar = ({ endpoints }) => {
[endpoints]
)
+ const isDisabledSidebar = useMemo(() => {
+ const endpoint = endpoints.find(({ path }) =>
+ matchPath(pathname, { path, exact: true })
+ )
+
+ return endpoint?.disabledSidebar
+ }, [pathname])
+
+ if (isDisabledSidebar) {
+ return null
+ }
+
return (
{
)
const { view, getResourceView } = useViews()
- const [totalData, setTotalData] = useState(() => [])
- const [args, setArgs] = useState(() => INITIAL_ARGS)
- const { data, isSuccess, refetch, isFetching } = useGetVmsQuery(args, {
- refetchOnMountOrArgChange: true,
+ const { data, refetch, isFetching } = useGetVmsQuery(undefined, {
+ selectFromResult: (result) => ({
+ ...result,
+ data: result?.data?.filter(({ STATE }) => STATE !== '6') ?? [],
+ }),
})
const columns = useMemo(
@@ -69,41 +61,13 @@ const VmsTable = (props) => {
[view]
)
- useEffect(() => {
- if (!isFetching && isSuccess && data?.length >= +INTERVAL_ON_FIRST_RENDER) {
- setArgs((prev) => ({
- ...prev,
- start: prev.start + INTERVAL_ON_FIRST_RENDER,
- }))
- }
- }, [isFetching])
-
- useEffect(() => {
- isSuccess &&
- data &&
- setTotalData((prev) => {
- const notDuplicatedData = data.filter(
- ({ ID }) => !prev.find((vm) => vm.ID === ID)
- )
-
- return prev.concat(notDuplicatedData).sort((a, b) => b.ID - a.ID)
- })
- }, [isSuccess])
-
return (
totalData?.filter(({ STATE }) => STATE !== '6'),
- [totalData]
- )}
+ data={useMemo(() => data, [data])}
rootProps={rootProps}
searchProps={searchProps}
- refetch={() => {
- totalData?.length >= +INTERVAL_ON_FIRST_RENDER
- ? setArgs(INITIAL_ARGS)
- : refetch()
- }}
+ refetch={refetch}
isLoading={isFetching}
getRowId={(row) => String(row.ID)}
RowComponent={VmRow}
diff --git a/src/fireedge/src/client/components/Tables/Vms/row.js b/src/fireedge/src/client/components/Tables/Vms/row.js
index 792a660b20..36716199a0 100644
--- a/src/fireedge/src/client/components/Tables/Vms/row.js
+++ b/src/fireedge/src/client/components/Tables/Vms/row.js
@@ -13,18 +13,42 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
-import { memo } from 'react'
+import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
+
import vmApi from 'client/features/OneApi/vm'
import { VirtualMachineCard } from 'client/components/Cards'
+import { GuacamoleButton } from 'client/components/Buttons'
+import { VM_ACTIONS } from 'client/constants'
+
+const { VNC, RDP, SSH } = VM_ACTIONS
const Row = memo(
({ original, ...props }) => {
- const detail = vmApi.endpoints.getVm.useQueryState(original.ID, {
- selectFromResult: ({ data }) => data,
+ const state = vmApi.endpoints.getVms.useQueryState(undefined, {
+ selectFromResult: ({ data = [] }) =>
+ data.find((vm) => +vm.ID === +original.ID),
})
- return
+ const memoVm = useMemo(() => state ?? original, [state, original])
+
+ return (
+
+ {[VNC, RDP, SSH].map((connectionType) => (
+
+ ))}
+ >
+ }
+ />
+ )
},
(prev, next) => prev.className === next.className
)
diff --git a/src/fireedge/src/client/components/Tabs/Cluster/index.js b/src/fireedge/src/client/components/Tabs/Cluster/index.js
index 3a1973a057..ccd8555f0b 100644
--- a/src/fireedge/src/client/components/Tabs/Cluster/index.js
+++ b/src/fireedge/src/client/components/Tabs/Cluster/index.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { LinearProgress } from '@mui/material'
+import { Alert, LinearProgress } from '@mui/material'
import { useViews } from 'client/features/Auth'
import { useGetClusterQuery } from 'client/features/OneApi/cluster'
@@ -32,7 +32,7 @@ const getTabComponent = (tabName) =>
const ClusterTabs = memo(({ id }) => {
const { view, getResourceView } = useViews()
- const { isLoading } = useGetClusterQuery({ id })
+ const { isLoading, isError, error } = useGetClusterQuery({ id })
const tabsAvailable = useMemo(() => {
const resource = RESOURCE_NAMES.CLUSTER
@@ -41,6 +41,14 @@ const ClusterTabs = memo(({ id }) => {
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
}, [view])
+ if (isError) {
+ return (
+
+ {error.data}
+
+ )
+ }
+
return isLoading ? (
) : (
diff --git a/src/fireedge/src/client/components/Tabs/Datastore/index.js b/src/fireedge/src/client/components/Tabs/Datastore/index.js
index 612801fdf1..32465ebcee 100644
--- a/src/fireedge/src/client/components/Tabs/Datastore/index.js
+++ b/src/fireedge/src/client/components/Tabs/Datastore/index.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { LinearProgress } from '@mui/material'
+import { Alert, LinearProgress } from '@mui/material'
import { useViews } from 'client/features/Auth'
import { useGetDatastoreQuery } from 'client/features/OneApi/datastore'
@@ -32,7 +32,7 @@ const getTabComponent = (tabName) =>
const DatastoreTabs = memo(({ id }) => {
const { view, getResourceView } = useViews()
- const { isLoading } = useGetDatastoreQuery({ id })
+ const { isLoading, isError, error } = useGetDatastoreQuery({ id })
const tabsAvailable = useMemo(() => {
const resource = RESOURCE_NAMES.DATASTORE
@@ -41,6 +41,14 @@ const DatastoreTabs = memo(({ id }) => {
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
}, [view])
+ if (isError) {
+ return (
+
+ {error.data}
+
+ )
+ }
+
return isLoading ? (
) : (
diff --git a/src/fireedge/src/client/components/Tabs/Group/index.js b/src/fireedge/src/client/components/Tabs/Group/index.js
index 704513778b..ada865f8a6 100644
--- a/src/fireedge/src/client/components/Tabs/Group/index.js
+++ b/src/fireedge/src/client/components/Tabs/Group/index.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { LinearProgress } from '@mui/material'
+import { Alert, LinearProgress } from '@mui/material'
import { useViews } from 'client/features/Auth'
import { useGetGroupQuery } from 'client/features/OneApi/group'
@@ -32,7 +32,7 @@ const getTabComponent = (tabName) =>
const GroupTabs = memo(({ id }) => {
const { view, getResourceView } = useViews()
- const { isLoading } = useGetGroupQuery(id)
+ const { isLoading, isError, error } = useGetGroupQuery(id)
const tabsAvailable = useMemo(() => {
const resource = RESOURCE_NAMES.GROUP
@@ -41,6 +41,14 @@ const GroupTabs = memo(({ id }) => {
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
}, [view])
+ if (isError) {
+ return (
+
+ {error.data}
+
+ )
+ }
+
return isLoading ? (
) : (
diff --git a/src/fireedge/src/client/components/Tabs/Host/index.js b/src/fireedge/src/client/components/Tabs/Host/index.js
index ec4f081682..aeb316ec83 100644
--- a/src/fireedge/src/client/components/Tabs/Host/index.js
+++ b/src/fireedge/src/client/components/Tabs/Host/index.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { LinearProgress } from '@mui/material'
+import { Alert, LinearProgress } from '@mui/material'
import { useAuth } from 'client/features/Auth'
import { useGetHostQuery } from 'client/features/OneApi/host'
@@ -32,7 +32,7 @@ const getTabComponent = (tabName) =>
const HostTabs = memo(({ id }) => {
const { view, getResourceView } = useAuth()
- const { isLoading } = useGetHostQuery(id)
+ const { isLoading, isError, error } = useGetHostQuery(id)
const tabsAvailable = useMemo(() => {
const resource = RESOURCE_NAMES.HOST
@@ -41,6 +41,14 @@ const HostTabs = memo(({ id }) => {
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
}, [view])
+ if (isError) {
+ return (
+
+ {error.data}
+
+ )
+ }
+
return isLoading ? (
) : (
diff --git a/src/fireedge/src/client/components/Tabs/Image/index.js b/src/fireedge/src/client/components/Tabs/Image/index.js
index 0f2e741ebb..5e9ab82cb5 100644
--- a/src/fireedge/src/client/components/Tabs/Image/index.js
+++ b/src/fireedge/src/client/components/Tabs/Image/index.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { LinearProgress } from '@mui/material'
+import { Alert, LinearProgress } from '@mui/material'
import { useViews } from 'client/features/Auth'
import { useGetImageQuery } from 'client/features/OneApi/image'
@@ -32,7 +32,7 @@ const getTabComponent = (tabName) =>
const ImageTabs = memo(({ id }) => {
const { view, getResourceView } = useViews()
- const { isLoading } = useGetImageQuery({ id })
+ const { isLoading, isError, error } = useGetImageQuery({ id })
const tabsAvailable = useMemo(() => {
const resource = RESOURCE_NAMES.IMAGE
@@ -41,6 +41,14 @@ const ImageTabs = memo(({ id }) => {
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
}, [view])
+ if (isError) {
+ return (
+
+ {error.data}
+
+ )
+ }
+
return isLoading ? (
) : (
diff --git a/src/fireedge/src/client/components/Tabs/Marketplace/index.js b/src/fireedge/src/client/components/Tabs/Marketplace/index.js
index 49b60ea7ce..07d6d07a2d 100644
--- a/src/fireedge/src/client/components/Tabs/Marketplace/index.js
+++ b/src/fireedge/src/client/components/Tabs/Marketplace/index.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { LinearProgress } from '@mui/material'
+import { Alert, LinearProgress } from '@mui/material'
import { useViews } from 'client/features/Auth'
import { useGetMarketplaceQuery } from 'client/features/OneApi/marketplace'
@@ -32,7 +32,7 @@ const getTabComponent = (tabName) =>
const MarketplaceTabs = memo(({ id }) => {
const { view, getResourceView } = useViews()
- const { isLoading } = useGetMarketplaceQuery({ id })
+ const { isLoading, isError, error } = useGetMarketplaceQuery({ id })
const tabsAvailable = useMemo(() => {
const resource = RESOURCE_NAMES.MARKETPLACE
@@ -41,6 +41,14 @@ const MarketplaceTabs = memo(({ id }) => {
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
}, [view])
+ if (isError) {
+ return (
+
+ {error.data}
+
+ )
+ }
+
return isLoading ? (
) : (
diff --git a/src/fireedge/src/client/components/Tabs/MarketplaceApp/index.js b/src/fireedge/src/client/components/Tabs/MarketplaceApp/index.js
index 0dc5ee59ac..50281361f4 100644
--- a/src/fireedge/src/client/components/Tabs/MarketplaceApp/index.js
+++ b/src/fireedge/src/client/components/Tabs/MarketplaceApp/index.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { LinearProgress } from '@mui/material'
+import { Alert, LinearProgress } from '@mui/material'
import { useViews } from 'client/features/Auth'
import { useGetMarketplaceAppQuery } from 'client/features/OneApi/marketplaceApp'
@@ -34,7 +34,7 @@ const getTabComponent = (tabName) =>
const MarketplaceAppTabs = memo(({ id }) => {
const { view, getResourceView } = useViews()
- const { isLoading } = useGetMarketplaceAppQuery(id)
+ const { isLoading, isError, error } = useGetMarketplaceAppQuery(id)
const tabsAvailable = useMemo(() => {
const resource = RESOURCE_NAMES.APP
@@ -43,6 +43,14 @@ const MarketplaceAppTabs = memo(({ id }) => {
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
}, [view])
+ if (isError) {
+ return (
+
+ {error.data}
+
+ )
+ }
+
return isLoading ? (
) : (
diff --git a/src/fireedge/src/client/components/Tabs/User/index.js b/src/fireedge/src/client/components/Tabs/User/index.js
index 0291dd9623..c49e59c176 100644
--- a/src/fireedge/src/client/components/Tabs/User/index.js
+++ b/src/fireedge/src/client/components/Tabs/User/index.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { LinearProgress } from '@mui/material'
+import { Alert, LinearProgress } from '@mui/material'
import { useViews } from 'client/features/Auth'
import { useGetUserQuery } from 'client/features/OneApi/user'
@@ -32,7 +32,7 @@ const getTabComponent = (tabName) =>
const UserTabs = memo(({ id }) => {
const { view, getResourceView } = useViews()
- const { isLoading } = useGetUserQuery(id)
+ const { isLoading, isError, error } = useGetUserQuery(id)
const tabsAvailable = useMemo(() => {
const resource = RESOURCE_NAMES.USER
@@ -41,6 +41,14 @@ const UserTabs = memo(({ id }) => {
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
}, [view])
+ if (isError) {
+ return (
+
+ {error.data}
+
+ )
+ }
+
return isLoading ? (
) : (
diff --git a/src/fireedge/src/client/components/Tabs/VNetwork/index.js b/src/fireedge/src/client/components/Tabs/VNetwork/index.js
index e845e91105..6f9bdee320 100644
--- a/src/fireedge/src/client/components/Tabs/VNetwork/index.js
+++ b/src/fireedge/src/client/components/Tabs/VNetwork/index.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { LinearProgress } from '@mui/material'
+import { Alert, LinearProgress } from '@mui/material'
import { useViews } from 'client/features/Auth'
import { useGetVNetworkQuery } from 'client/features/OneApi/network'
@@ -32,7 +32,7 @@ const getTabComponent = (tabName) =>
const VNetworkTabs = memo(({ id }) => {
const { view, getResourceView } = useViews()
- const { isLoading } = useGetVNetworkQuery({ id })
+ const { isLoading, isError, error } = useGetVNetworkQuery({ id })
const tabsAvailable = useMemo(() => {
const resource = RESOURCE_NAMES.VNET
@@ -41,6 +41,14 @@ const VNetworkTabs = memo(({ id }) => {
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
}, [view])
+ if (isError) {
+ return (
+
+ {error.data}
+
+ )
+ }
+
return isLoading ? (
) : (
diff --git a/src/fireedge/src/client/components/Tabs/VNetworkTemplate/index.js b/src/fireedge/src/client/components/Tabs/VNetworkTemplate/index.js
index 5b74f12d43..346b7c320d 100644
--- a/src/fireedge/src/client/components/Tabs/VNetworkTemplate/index.js
+++ b/src/fireedge/src/client/components/Tabs/VNetworkTemplate/index.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { LinearProgress } from '@mui/material'
+import { Alert, LinearProgress } from '@mui/material'
import { useViews } from 'client/features/Auth'
import { useGetVNTemplateQuery } from 'client/features/OneApi/networkTemplate'
@@ -32,7 +32,7 @@ const getTabComponent = (tabName) =>
const VNetTemplateTabs = memo(({ id }) => {
const { view, getResourceView } = useViews()
- const { isLoading } = useGetVNTemplateQuery({ id })
+ const { isLoading, isError, error } = useGetVNTemplateQuery({ id })
const tabsAvailable = useMemo(() => {
const resource = RESOURCE_NAMES.VN_TEMPLATE
@@ -41,6 +41,14 @@ const VNetTemplateTabs = memo(({ id }) => {
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
}, [view])
+ if (isError) {
+ return (
+
+ {error.data}
+
+ )
+ }
+
return isLoading ? (
) : (
diff --git a/src/fireedge/src/client/components/Tabs/Vm/index.js b/src/fireedge/src/client/components/Tabs/Vm/index.js
index 42a7a4f9e6..9b8847a63f 100644
--- a/src/fireedge/src/client/components/Tabs/Vm/index.js
+++ b/src/fireedge/src/client/components/Tabs/Vm/index.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { LinearProgress } from '@mui/material'
+import { Alert, LinearProgress } from '@mui/material'
import { useViews } from 'client/features/Auth'
import { useGetVmQuery } from 'client/features/OneApi/vm'
@@ -46,7 +46,7 @@ const getTabComponent = (tabName) =>
const VmTabs = memo(({ id }) => {
const { view, getResourceView } = useViews()
- const { isLoading } = useGetVmQuery(id, {
+ const { isLoading, isError, error } = useGetVmQuery(id, {
refetchOnMountOrArgChange: 10,
})
@@ -57,6 +57,14 @@ const VmTabs = memo(({ id }) => {
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
}, [view])
+ if (isError) {
+ return (
+
+ {error.data}
+
+ )
+ }
+
return isLoading ? (
) : (
diff --git a/src/fireedge/src/client/components/Tabs/VmTemplate/index.js b/src/fireedge/src/client/components/Tabs/VmTemplate/index.js
index b4c3b0e959..94ed0cb3e1 100644
--- a/src/fireedge/src/client/components/Tabs/VmTemplate/index.js
+++ b/src/fireedge/src/client/components/Tabs/VmTemplate/index.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { LinearProgress } from '@mui/material'
+import { Alert, LinearProgress } from '@mui/material'
import { useViews } from 'client/features/Auth'
import { useGetTemplateQuery } from 'client/features/OneApi/vmTemplate'
@@ -34,7 +34,7 @@ const getTabComponent = (tabName) =>
const VmTemplateTabs = memo(({ id }) => {
const { view, getResourceView } = useViews()
- const { isLoading } = useGetTemplateQuery({ id })
+ const { isLoading, isError, error } = useGetTemplateQuery({ id })
const tabsAvailable = useMemo(() => {
const resource = RESOURCE_NAMES.VM_TEMPLATE
@@ -43,6 +43,14 @@ const VmTemplateTabs = memo(({ id }) => {
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
}, [view])
+ if (isError) {
+ return (
+
+ {error.data}
+
+ )
+ }
+
return isLoading ? (
) : (
diff --git a/src/fireedge/src/client/components/Tabs/Zone/index.js b/src/fireedge/src/client/components/Tabs/Zone/index.js
index 58fc377c36..b26d913322 100644
--- a/src/fireedge/src/client/components/Tabs/Zone/index.js
+++ b/src/fireedge/src/client/components/Tabs/Zone/index.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { memo, useMemo } from 'react'
import PropTypes from 'prop-types'
-import { LinearProgress } from '@mui/material'
+import { Alert, LinearProgress } from '@mui/material'
import { useViews } from 'client/features/Auth'
import { useGetZoneQuery } from 'client/features/OneApi/zone'
@@ -32,7 +32,7 @@ const getTabComponent = (tabName) =>
const ZoneTabs = memo(({ id }) => {
const { view, getResourceView } = useViews()
- const { isLoading } = useGetZoneQuery(id)
+ const { isLoading, isError, error } = useGetZoneQuery(id)
const tabsAvailable = useMemo(() => {
const resource = RESOURCE_NAMES.ZONE
@@ -41,6 +41,14 @@ const ZoneTabs = memo(({ id }) => {
return getAvailableInfoTabs(infoTabs, getTabComponent, id)
}, [view])
+ if (isError) {
+ return (
+
+ {error.data}
+
+ )
+ }
+
return isLoading ? (
) : (
diff --git a/src/fireedge/src/client/constants/guacamole.js b/src/fireedge/src/client/constants/guacamole.js
new file mode 100644
index 0000000000..7b48c6ccbc
--- /dev/null
+++ b/src/fireedge/src/client/constants/guacamole.js
@@ -0,0 +1,101 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+
+// eslint-disable-next-line no-unused-vars
+import { Client, WebSocketTunnel, Status } from 'guacamole-common-js'
+
+/**
+ * @typedef GuacamoleSessionThumbnail
+ * @property {number} timestamp - The time that this thumbnail was generated
+ * @property {HTMLCanvasElement} canvas - The thumbnail of the Guacamole client display
+ */
+
+/**
+ * @typedef GuacamoleSessionProperties
+ * @property {boolean} autoFit - Whether the display should be scaled automatically
+ * @property {number} scale - The current scale.
+ * If autoFit is true, the effect of setting this value is undefined
+ * @property {number} minScale - The minimum scale value
+ * @property {number} maxScale - The maximum scale value
+ * @property {boolean} keyboardEnabled - Whether or not the client should listen to keyboard events
+ * @property {number} emulateAbsoluteMouse - Whether translation of touch to mouse events should
+ * emulate an absolute pointer device, or a relative pointer device
+ * @property {number} scrollTop - The relative Y coordinate of the scroll offset of the display
+ * @property {number} scrollLeft - The relative X coordinate of the scroll offset of the display
+ */
+
+/**
+ * @typedef GuacamoleSessionState
+ * @property {GUACAMOLE_CLIENT_STATES} connectionState - The current connection state
+ * @property {Status.Code} statusCode - The status code of the current error condition
+ * @property {boolean} tunnelUnstable - Whether the network connection used by the tunnel seems unstable
+ */
+
+/**
+ * @typedef GuacamoleSession
+ * @property {string} token - The token of the connection associated with this client
+ * @property {string} name - The name returned associated with the connection or connection group in use
+ * @property {string} title - The title which should be displayed as the page title for this client
+ * @property {Client} client - The actual underlying Guacamole client
+ * @property {WebSocketTunnel} tunnel - The tunnel being used by the underlying Guacamole client
+ * @property {GuacamoleSessionState} clientState - The current state of the Guacamole client
+ * @property {boolean} isUninitialized - When true, indicates that the session hasn't been fired yet
+ * @property {boolean} isLoading - When true, indicates that the session is awaiting a response
+ * @property {boolean} isConnected - When true, indicates that the last session was connected successfully
+ * @property {boolean} isDisconnected - When true, indicates that the last session was disconnected
+ * @property {boolean} isError - When true, indicates that the last session has an error state
+ * @property {GuacamoleSessionProperties} clientProperties - The current state of the Guacamole client
+ * @property {GuacamoleSessionThumbnail} thumbnail - The most recently-generated thumbnail for this connection
+ * @property {number} multiTouchSupport - The number of simultaneous touch contacts supported
+ */
+
+/** @enum {string} Guacamole client state strings */
+export const GUACAMOLE_STATES_STR = {
+ IDLE: 'IDLE',
+ CONNECTING: 'CONNECTING',
+ WAITING: 'WAITING',
+ CONNECTED: 'CONNECTED',
+ DISCONNECTING: 'DISCONNECTING',
+ DISCONNECTED: 'DISCONNECTED',
+ CLIENT_ERROR: 'CLIENT_ERROR',
+ TUNNEL_ERROR: 'TUNNEL_ERROR',
+}
+
+/** @enum {string} Guacamole client states */
+export const GUACAMOLE_CLIENT_STATES = [
+ GUACAMOLE_STATES_STR.IDLE,
+ GUACAMOLE_STATES_STR.CONNECTING,
+ GUACAMOLE_STATES_STR.WAITING,
+ GUACAMOLE_STATES_STR.CONNECTED,
+ GUACAMOLE_STATES_STR.DISCONNECTING,
+ GUACAMOLE_STATES_STR.DISCONNECTED,
+]
+
+/**
+ * The mimetype of audio data to be sent along the Guacamole
+ * connection if audio input is supported.
+ *
+ * @type {string}
+ */
+export const AUDIO_INPUT_MIMETYPE = 'audio/L16;rate=44100,channels=2'
+
+/**
+ * The minimum amount of time to wait between updates to
+ * the client thumbnail, in milliseconds.
+ *
+ * @type {number}
+ */
+export const THUMBNAIL_UPDATE_FREQUENCY = 5000
diff --git a/src/fireedge/src/client/constants/index.js b/src/fireedge/src/client/constants/index.js
index 0e696b9879..03306c9434 100644
--- a/src/fireedge/src/client/constants/index.js
+++ b/src/fireedge/src/client/constants/index.js
@@ -27,7 +27,7 @@ export const BY = {
url: 'https://opennebula.io/',
}
-export const _APPS = defaultApps
+export const _APPS = { ...defaultApps }
export const APPS = Object.keys(defaultApps)
export const APPS_IN_BETA = [_APPS.sunstone.name]
export const APPS_WITH_SWITCHER = [_APPS.sunstone.name]
@@ -90,6 +90,7 @@ export const SOCKETS = {
DISCONNECT: 'disconnect',
HOOKS: 'hooks',
PROVISION: 'provision',
+ GUACAMOLE: 'guacamole',
}
/** @enum {string} Names of resource */
@@ -114,23 +115,24 @@ export const RESOURCE_NAMES = {
export * as T from 'client/constants/translates'
export * as ACTIONS from 'client/constants/actions'
export * as STATES from 'client/constants/states'
-export * from 'client/constants/common'
-export * from 'client/constants/quota'
-export * from 'client/constants/scheduler'
-export * from 'client/constants/userInput'
-export * from 'client/constants/flow'
-export * from 'client/constants/provision'
-export * from 'client/constants/user'
-export * from 'client/constants/group'
export * from 'client/constants/cluster'
-export * from 'client/constants/vm'
-export * from 'client/constants/vmTemplate'
-export * from 'client/constants/network'
-export * from 'client/constants/networkTemplate'
+export * from 'client/constants/common'
+export * from 'client/constants/datastore'
+export * from 'client/constants/flow'
+export * from 'client/constants/group'
+export * from 'client/constants/guacamole'
export * from 'client/constants/host'
export * from 'client/constants/image'
export * from 'client/constants/marketplace'
export * from 'client/constants/marketplaceApp'
-export * from 'client/constants/datastore'
+export * from 'client/constants/network'
+export * from 'client/constants/networkTemplate'
+export * from 'client/constants/provision'
+export * from 'client/constants/quota'
+export * from 'client/constants/scheduler'
export * from 'client/constants/securityGroup'
+export * from 'client/constants/user'
+export * from 'client/constants/userInput'
+export * from 'client/constants/vm'
+export * from 'client/constants/vmTemplate'
export * from 'client/constants/zone'
diff --git a/src/fireedge/src/client/constants/translates.js b/src/fireedge/src/client/constants/translates.js
index 12a0118bb8..d1aa495806 100644
--- a/src/fireedge/src/client/constants/translates.js
+++ b/src/fireedge/src/client/constants/translates.js
@@ -361,6 +361,19 @@ module.exports = {
ReadyStatusGate: 'Ready status gate',
/* VM schema */
+ /* VM schema - remote access */
+ Vnc: 'VNC',
+ Ssh: 'SSH',
+ Rdp: 'RDP',
+ SshConnection: 'SSH connection',
+ RdpConnection: 'RDP connection',
+ Vmrc: 'VMRC',
+ Sdl: 'SDL',
+ Spice: 'SPICE',
+ SendCtrlAltDel: 'Send Ctrl-Alt-Del',
+ Reconnect: 'Reconnect',
+ FullScreen: 'Full screen',
+ Screenshot: 'Screenshot',
/* VM schema - info */
UserTemplate: 'User Template',
Template: 'Template',
@@ -389,8 +402,6 @@ module.exports = {
NIC: 'NIC',
Alias: 'Alias',
AsAnAlias: 'Attach as an alias',
- RdpConnection: 'RDP connection',
- SshConnection: 'SSH connection',
External: 'External',
ExternalConcept: 'The NIC will be attached as an external alias of the VM',
OverrideNetworkValuesIPv4: 'Override Network Values IPv4',
@@ -568,10 +579,6 @@ module.exports = {
Class: 'Class',
/* VM Template schema - Input/Output - graphics */
Graphics: 'Graphics',
- VMRC: 'VMRC',
- VNC: 'VNC',
- SDL: 'SDL',
- SPICE: 'SPICE',
ListenOnIp: 'Listen on IP',
ServerPort: 'Server port',
ServerPortConcept: 'Port for the VNC/SPICE server',
diff --git a/src/fireedge/src/client/constants/vm.js b/src/fireedge/src/client/constants/vm.js
index 749c907e4b..baffe06b5e 100644
--- a/src/fireedge/src/client/constants/vm.js
+++ b/src/fireedge/src/client/constants/vm.js
@@ -873,13 +873,12 @@ export const VM_ACTIONS_BY_STATE = {
[VM_ACTIONS.UNRESCHED]: [STATES.RUNNING, STATES.UNKNOWN],
// REMOTE
- [VM_ACTIONS.VMRC]: [],
- [VM_ACTIONS.SPICE]: [],
- [VM_ACTIONS.VNC]: [],
- [VM_ACTIONS.SSH]: [],
- [VM_ACTIONS.RDP]: [],
- [VM_ACTIONS.FILE_RDP]: [],
- [VM_ACTIONS.FILE_VIRT_VIEWER]: [],
+ [VM_ACTIONS.VMRC]: [STATES.RUNNING],
+ [VM_ACTIONS.VNC]: [STATES.RUNNING],
+ [VM_ACTIONS.SSH]: [STATES.RUNNING],
+ [VM_ACTIONS.RDP]: [STATES.RUNNING],
+ [VM_ACTIONS.FILE_RDP]: [STATES.RUNNING],
+ [VM_ACTIONS.FILE_VIRT_VIEWER]: [STATES.RUNNING],
// INFORMATION
[VM_ACTIONS.RENAME]: [],
diff --git a/src/fireedge/src/client/containers/Guacamole/index.js b/src/fireedge/src/client/containers/Guacamole/index.js
new file mode 100644
index 0000000000..208ee332af
--- /dev/null
+++ b/src/fireedge/src/client/containers/Guacamole/index.js
@@ -0,0 +1,152 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+import { ReactElement, useMemo, useRef, useEffect } from 'react'
+import { useParams, useHistory } from 'react-router'
+import { Box, Stack, Typography, Divider, Skeleton } from '@mui/material'
+
+import { useGetVmQuery } from 'client/features/OneApi/vm'
+import {
+ useGuacamoleSession,
+ GuacamoleDisplay,
+ GuacamoleKeyboard,
+ GuacamoleMouse,
+ GuacamoleClipboard,
+ GuacamoleCtrlAltDelButton,
+ GuacamoleReconnectButton,
+ GuacamoleFullScreenButton,
+ GuacamoleScreenshotButton,
+} from 'client/components/Consoles'
+import { useViews } from 'client/features/Auth'
+import { StatusCircle } from 'client/components/Status'
+import MultipleTags from 'client/components/MultipleTags'
+import { getIps, getState } from 'client/models/VirtualMachine'
+import { timeFromMilliseconds } from 'client/models/Helper'
+import { PATH } from 'client/apps/sunstone/routes'
+import { RESOURCE_NAMES } from 'client/constants'
+
+/** @returns {ReactElement} Guacamole container */
+const Guacamole = () => {
+ const { id, type = '' } = useParams()
+ const { push: redirectTo } = useHistory()
+ const { view, [RESOURCE_NAMES.VM]: vmView } = useViews()
+
+ const containerRef = useRef(null)
+ const headerRef = useRef(null)
+
+ const { data: vm, isLoading, isError } = useGetVmQuery(id)
+
+ const ips = getIps(vm)
+ const { color: stateColor, name: stateName } = getState(vm) ?? {}
+ const time = timeFromMilliseconds(+vm?.ETIME || +vm?.STIME)
+
+ const { token, clientState, displayElement, ...session } =
+ useGuacamoleSession(
+ useMemo(
+ () => ({
+ id: `${id}-${type}`,
+ container: containerRef.current,
+ header: headerRef.current,
+ }),
+ [
+ containerRef.current?.offsetWidth,
+ containerRef.current?.offsetHeight,
+ headerRef.current?.offsetWidth,
+ headerRef.current?.offsetHeight,
+ ]
+ ),
+ GuacamoleDisplay,
+ GuacamoleMouse,
+ GuacamoleKeyboard,
+ GuacamoleClipboard
+ )
+
+ useEffect(() => {
+ const noAction = vmView?.actions?.[type] !== true
+
+ // token should be saved after click on console button from datatable
+ if (noAction || !token || isError) {
+ redirectTo(PATH.DASHBOARD)
+ }
+ }, [view, token])
+
+ return (
+
+
+
+
+ {isLoading ? (
+ <>
+
+
+ >
+ ) : (
+ <>
+
+ {`# ${vm?.ID} - ${vm?.NAME}`}
+ >
+ )}
+
+ }
+ gap="1em"
+ >
+ {isLoading ? (
+
+ ) : (
+ {`Started on: ${time.toFormat('ff')}`}
+ )}
+ {isLoading ? (
+
+ ) : (
+ !!ips?.length && (
+
+
+
+ )
+ )}
+
+
+
+
+
+
+
+ {clientState?.connectionState && (
+ {`State: ${clientState?.connectionState}`}
+ )}
+
+
+ {displayElement}
+
+ )
+}
+
+export default Guacamole
diff --git a/src/fireedge/src/client/features/Auth/hooks.js b/src/fireedge/src/client/features/Auth/hooks.js
index 55b08a3481..d019d16624 100644
--- a/src/fireedge/src/client/features/Auth/hooks.js
+++ b/src/fireedge/src/client/features/Auth/hooks.js
@@ -112,5 +112,19 @@ export const useViews = () => {
[view]
)
- return useMemo(() => ({ getResourceView, views, view }), [views, view])
+ return useMemo(
+ () => ({
+ ...Object.values(RESOURCE_NAMES).reduce(
+ (listOfResourceViews, resourceName) => ({
+ ...listOfResourceViews,
+ [resourceName]: getResourceView(resourceName),
+ }),
+ {}
+ ),
+ getResourceView,
+ views,
+ view,
+ }),
+ [views, view]
+ )
}
diff --git a/src/fireedge/src/client/features/Guacamole/hooks.js b/src/fireedge/src/client/features/Guacamole/hooks.js
new file mode 100644
index 0000000000..e04ee135d5
--- /dev/null
+++ b/src/fireedge/src/client/features/Guacamole/hooks.js
@@ -0,0 +1,73 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+
+import { useCallback, useMemo } from 'react'
+import { useDispatch, useSelector, shallowEqual } from 'react-redux'
+
+import { name as guacSlice, actions } from 'client/features/Guacamole/slice'
+import { GuacamoleSession } from 'client/constants'
+
+const {
+ addGuacamoleSession,
+ removeGuacamoleSession,
+ updateThumbnail,
+ setConnectionState,
+ setTunnelUnstable,
+ setMultiTouchSupport,
+} = actions
+
+// --------------------------------------------------------------
+// Guacamole Hooks
+// --------------------------------------------------------------
+
+/**
+ * Hook to get the state of Guacamole sessions.
+ *
+ * @param {string} [id] - Session id to subscribe
+ * @returns {object|GuacamoleSession} Return Guacamole session by id or global state
+ */
+export const useGuacamole = (id) => {
+ const guac = useSelector(
+ (state) => (id ? state[guacSlice][id] : state[guacSlice]),
+ shallowEqual
+ )
+
+ return useMemo(() => ({ ...guac }), [guac])
+}
+
+/**
+ * Hook to manage Guacamole sessions.
+ *
+ * @param {string} [id] - Session id to operate
+ * @returns {object} Return management actions
+ */
+export const useGuacamoleApi = (id) => {
+ const dispatch = useDispatch()
+
+ const commonDispatch = useCallback(
+ (action) => (data) => dispatch(action({ id, ...data })),
+ [dispatch, id]
+ )
+
+ return {
+ addSession: commonDispatch(addGuacamoleSession),
+ removeSession: commonDispatch(removeGuacamoleSession),
+ updateThumbnail: commonDispatch(updateThumbnail),
+ setConnectionState: commonDispatch(setConnectionState),
+ setTunnelUnstable: commonDispatch(setTunnelUnstable),
+ setMultiTouchSupport: commonDispatch(setMultiTouchSupport),
+ }
+}
diff --git a/src/fireedge/src/client/features/Guacamole/index.js b/src/fireedge/src/client/features/Guacamole/index.js
new file mode 100644
index 0000000000..100b0e15bf
--- /dev/null
+++ b/src/fireedge/src/client/features/Guacamole/index.js
@@ -0,0 +1,17 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+export * from 'client/features/Guacamole/slice'
+export * from 'client/features/Guacamole/hooks'
diff --git a/src/fireedge/src/client/features/Guacamole/slice.js b/src/fireedge/src/client/features/Guacamole/slice.js
new file mode 100644
index 0000000000..61185e5d15
--- /dev/null
+++ b/src/fireedge/src/client/features/Guacamole/slice.js
@@ -0,0 +1,126 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+import { createSlice } from '@reduxjs/toolkit'
+import { Status } from 'guacamole-common-js'
+
+import { actions as authActions } from 'client/features/Auth/slice'
+import { GUACAMOLE_STATES_STR } from 'client/constants'
+
+const {
+ IDLE,
+ CONNECTING,
+ WAITING,
+ CONNECTED,
+ DISCONNECTING,
+ DISCONNECTED,
+ CLIENT_ERROR,
+ TUNNEL_ERROR,
+} = GUACAMOLE_STATES_STR
+
+const getIdentifiedFromPayload = ({ id, type } = {}) =>
+ id?.includes('-') ? id : `${id}-${type}`
+
+const INITIAL_SESSION = {
+ thumbnail: null,
+ multiTouchSupport: 0,
+ clientState: {
+ connectionState: IDLE,
+ tunnelUnstable: false,
+ statusCode: Status.Code.SUCCESS,
+ },
+ isUninitialized: true,
+ isLoading: false,
+ isConnected: false,
+ isDisconnected: false,
+ isError: false,
+ clientProperties: {
+ autoFit: true,
+ scale: 1,
+ minScale: 1,
+ maxScale: 3,
+ focused: false,
+ scrollTop: 0,
+ scrollLeft: 0,
+ },
+}
+
+const slice = createSlice({
+ name: 'guacamole',
+ initialState: {},
+ reducers: {
+ addGuacamoleSession: (state, { payload }) => {
+ const id = getIdentifiedFromPayload(payload)
+ state[id] = { ...INITIAL_SESSION, token: payload?.token }
+ },
+ removeGuacamoleSession: (state, { payload }) => {
+ const id = getIdentifiedFromPayload(payload)
+ const { [id]: _, ...rest } = state
+
+ return { ...rest }
+ },
+ updateGuacamoleSession: (state, { payload }) => {
+ const id = getIdentifiedFromPayload(payload)
+ const { [id]: session = {} } = state
+
+ state[id] = { ...session, ...payload?.session }
+ },
+ setConnectionState: (state, { payload = {} }) => {
+ const { state: cState, statusCode } = payload
+ const id = getIdentifiedFromPayload(payload)
+ const { [id]: session = {} } = state
+
+ if (
+ !session ||
+ session?.clientState.connectionState === TUNNEL_ERROR ||
+ session?.clientState.connectionState === CLIENT_ERROR
+ )
+ return state
+
+ statusCode && (session.clientState.statusCode = statusCode)
+ session.clientState.connectionState = cState
+ session.clientState.tunnelUnstable = false
+
+ session.isUninitialized = cState === IDLE
+ session.isLoading = [WAITING, CONNECTING, DISCONNECTING].includes(cState)
+ session.isConnected = cState === CONNECTED
+ session.isDisconnected = cState === DISCONNECTED
+ session.isError = [CLIENT_ERROR, TUNNEL_ERROR].includes(cState)
+ },
+ setTunnelUnstable: (state, { payload = {} }) => {
+ const id = getIdentifiedFromPayload(payload)
+ const { [id]: session = {} } = state
+
+ session.clientState.tunnelUnstable = payload.unstable
+ },
+ setMultiTouchSupport: (state, { payload = {} }) => {
+ const id = getIdentifiedFromPayload(payload)
+ const { [id]: session = {} } = state
+
+ state[id] = { ...session, multiTouchSupport: payload?.touches }
+ },
+ updateThumbnail: (state, { payload = {} }) => {
+ const id = getIdentifiedFromPayload(payload)
+ const { [id]: session = {} } = state
+
+ state[id] = { ...session, thumbnail: payload?.thumbnail }
+ },
+ },
+ extraReducers: (builder) => {
+ builder.addCase(authActions.logout, () => ({}))
+ },
+})
+
+export const { name, reducer, actions } = slice
diff --git a/src/fireedge/src/client/features/OneApi/vm.js b/src/fireedge/src/client/features/OneApi/vm.js
index 68e387cb9e..1fd5ff0ca8 100644
--- a/src/fireedge/src/client/features/OneApi/vm.js
+++ b/src/fireedge/src/client/features/OneApi/vm.js
@@ -14,11 +14,16 @@
* limitations under the License. *
* ------------------------------------------------------------------------- */
import { Actions, Commands } from 'server/utils/constants/commands/vm'
+import {
+ Actions as ExtraActions,
+ Commands as ExtraCommands,
+} from 'server/routes/api/vm/routes'
import {
oneApi,
ONE_RESOURCES,
ONE_RESOURCES_POOL,
} from 'client/features/OneApi'
+import { actions as guacamoleActions } from 'client/features/Guacamole/slice'
import { UpdateFromSocket } from 'client/features/OneApi/socket'
import http from 'client/utils/rest'
import {
@@ -101,7 +106,13 @@ const vmApi = oneApi.injectEndpoints({
index !== -1 && (draft[index] = queryVm)
})
)
- } catch {}
+ } catch {
+ dispatch(
+ vmApi.util.updateQueryData('getVms', undefined, (draft) =>
+ draft.filter(({ ID }) => +ID !== +id)
+ )
+ )
+ }
},
onCacheEntryAdded: UpdateFromSocket({
updateQueryData: (updateFn) =>
@@ -109,6 +120,29 @@ const vmApi = oneApi.injectEndpoints({
resource: VM.toLowerCase(),
}),
}),
+ getGuacamoleSession: builder.query({
+ /**
+ * Returns a Guacamole session.
+ *
+ * @param {object} params - Request parameters
+ * @param {string} params.id - Virtual machine id
+ * @param {'vnc'|'ssh'|'rdp'} params.type - Connection type
+ * @returns {string} The session token
+ * @throws Fails when response isn't code 200
+ */
+ query: (params) => {
+ const name = ExtraActions.GUACAMOLE
+ const command = { name, ...ExtraCommands[name] }
+
+ return { params, command }
+ },
+ async onQueryStarted({ id, type }, { dispatch, queryFulfilled }) {
+ try {
+ const { data: token } = await queryFulfilled
+ dispatch(guacamoleActions.addGuacamoleSession({ id, type, token }))
+ } catch {}
+ },
+ }),
getMonitoring: builder.query({
/**
* Returns the virtual machine monitoring records.
@@ -535,6 +569,34 @@ const vmApi = oneApi.injectEndpoints({
return { params, command }
},
invalidatesTags: (_, __, { id }) => [{ type: VM, id }],
+ async onQueryStarted(
+ { id, ...permissions },
+ { dispatch, queryFulfilled }
+ ) {
+ const patchResult = dispatch(
+ vmApi.util.updateQueryData('getVm', id, (draft) => {
+ Object.entries(permissions)
+ .filter(([_, value]) => value !== '-1')
+ .forEach(([name, value]) => {
+ const ensuredName = {
+ ownerUse: 'OWNER_U',
+ ownerManage: 'OWNER_M',
+ ownerAdmin: 'OWNER_A',
+ groupUse: 'GROUP_U',
+ groupManage: 'GROUP_M',
+ groupAdmin: 'GROUP_A',
+ otherUse: 'OTHER_U',
+ otherManage: 'OTHER_M',
+ otherAdmin: 'OTHER_A',
+ }[name]
+
+ draft.PERMISSIONS[ensuredName] = value
+ })
+ })
+ )
+
+ queryFulfilled.catch(patchResult.undo)
+ },
}),
changeVmOwnership: builder.mutation({
/**
@@ -844,6 +906,8 @@ export const {
useLazyGetVmsQuery,
useGetVmQuery,
useLazyGetVmQuery,
+ useGetGuacamoleSessionQuery,
+ useLazyGetGuacamoleSessionQuery,
useGetMonitoringQuery,
useLazyGetMonitoringQuery,
useGetMonitoringPoolQuery,
diff --git a/src/fireedge/src/client/features/middleware.js b/src/fireedge/src/client/features/middleware.js
index 1c2fd202a5..12a5bee131 100644
--- a/src/fireedge/src/client/features/middleware.js
+++ b/src/fireedge/src/client/features/middleware.js
@@ -15,7 +15,7 @@
* ------------------------------------------------------------------------- */
import { isRejectedWithValue, Middleware, Dispatch } from '@reduxjs/toolkit'
-import * as Auth from 'client/features/Auth/slice'
+import { name as authName, logout } from 'client/features/Auth/slice'
import { T, ONEADMIN_GROUP_ID } from 'client/constants'
/**
@@ -27,7 +27,7 @@ export const unauthenticatedMiddleware =
(next) =>
(action) => {
if (isRejectedWithValue(action) && action.payload.status === 401) {
- dispatch(Auth.actions.logout(T.SessionExpired))
+ dispatch(logout(T.SessionExpired))
}
return next(action)
@@ -41,13 +41,13 @@ export const onlyForOneadminMiddleware =
({ dispatch, getState }) =>
(next) =>
(action) => {
- const groups = getState()?.[Auth.name]?.user?.GROUPS?.ID
+ const groups = getState()?.[authName]?.user?.GROUPS?.ID
- if (!Auth.actions.logout.match(action) && groups) {
+ if (!logout.match(action) && !!groups?.length) {
const ensuredGroups = [groups].flat()
!ensuredGroups.includes(ONEADMIN_GROUP_ID) &&
- dispatch(Auth.actions.logout(T.OnlyForOneadminGroup))
+ dispatch(logout(T.OnlyForOneadminGroup))
}
return next(action)
diff --git a/src/fireedge/src/client/models/Guacamole.js b/src/fireedge/src/client/models/Guacamole.js
new file mode 100644
index 0000000000..1cc93809d8
--- /dev/null
+++ b/src/fireedge/src/client/models/Guacamole.js
@@ -0,0 +1,56 @@
+/* ------------------------------------------------------------------------- *
+ * Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
+ * *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may *
+ * not use this file except in compliance with the License. You may obtain *
+ * a copy of the License at *
+ * *
+ * http://www.apache.org/licenses/LICENSE-2.0 *
+ * *
+ * Unless required by applicable law or agreed to in writing, software *
+ * distributed under the License is distributed on an "AS IS" BASIS, *
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
+ * See the License for the specific language governing permissions and *
+ * limitations under the License. *
+ * ------------------------------------------------------------------------- */
+import { GUACAMOLE_CLIENT_STATES } from 'client/constants'
+
+const isWindow = (display) => display instanceof Window
+
+/**
+ * @param {number} clientState - Guacamole client state
+ * @returns {string} State information from resource
+ */
+export const clientStateToString = (clientState) =>
+ GUACAMOLE_CLIENT_STATES[+clientState]
+
+/**
+ * Returns the string of connection parameters
+ * to be passed to the Guacamole client.
+ *
+ * @param {object} options - Connection parameters
+ * @param {HTMLElement} options.display - Element where the connection will be displayed
+ * @param {number} options.width - Forced width connection
+ * @param {number} options.height - Forced height connection
+ * @returns {string} A string of connection parameters
+ */
+export const getConnectString = (options = {}) => {
+ const { token, display = window, dpi, width, height } = options
+
+ // Calculate optimal width/height for display
+ const pixelDensity = window.devicePixelRatio || 1
+ const optimalDpi = dpi || pixelDensity * 96
+
+ const displayWidth =
+ width || (isWindow(display) ? display?.innerWidth : display?.offsetWidth)
+
+ const displayHeight =
+ height || (isWindow(display) ? display?.innerHeight : display?.offsetHeight)
+
+ return [
+ `token=${encodeURIComponent(token)}`,
+ `width=${Math.floor(displayWidth * pixelDensity)}`,
+ `height=${Math.floor(displayHeight * pixelDensity)}`,
+ `dpi=${Math.floor(optimalDpi)}`,
+ ].join('&')
+}
diff --git a/src/fireedge/src/client/models/VirtualMachine.js b/src/fireedge/src/client/models/VirtualMachine.js
index 0a7c72becd..e572ff5109 100644
--- a/src/fireedge/src/client/models/VirtualMachine.js
+++ b/src/fireedge/src/client/models/VirtualMachine.js
@@ -18,6 +18,7 @@ import {
prettySecurityGroup,
} from 'client/models/SecurityGroup'
import { isRelative } from 'client/models/Scheduler'
+import { stringToBoolean } from 'client/models/Helper'
import {
STATES,
@@ -286,3 +287,16 @@ export const isAvailableAction =
(state) => !VM_ACTIONS_BY_STATE[action]?.includes(state)
)
}
+
+/**
+ * @param {VM} vm - Virtual machine
+ * @param {'ssh'|'rdp'} type - Connection type
+ * @returns {boolean} - Returns connection type is available
+ */
+export const nicsIncludesTheConnectionType = (vm, type) => {
+ const ensuredConnection = String(type).toUpperCase()
+
+ if (!['SSH', 'RDP'].includes(ensuredConnection)) return false
+
+ return getNics(vm).some((nic) => stringToBoolean(nic[ensuredConnection]))
+}
diff --git a/src/fireedge/src/client/router/index.js b/src/fireedge/src/client/router/index.js
index c60ee61a3b..a74982c998 100644
--- a/src/fireedge/src/client/router/index.js
+++ b/src/fireedge/src/client/router/index.js
@@ -28,9 +28,9 @@ import {
import { ProtectedRoute, NoAuthRoute } from 'client/components/Route'
import { InternalLayout } from 'client/components/HOC'
-const renderRoute = ({ Component, label, ...rest }, index) => (
+const renderRoute = ({ Component, label, disabledSidebar, ...rest }, index) => (
-
+
} />
@@ -72,7 +72,7 @@ Router.propTypes = {
PropTypes.shape({
Component: PropTypes.object,
icon: PropTypes.object,
- label: PropTypes.string.isRequired,
+ label: PropTypes.string,
path: PropTypes.string,
sidebar: PropTypes.bool,
routes: PropTypes.array,
diff --git a/src/fireedge/src/client/store/index.js b/src/fireedge/src/client/store/index.js
index becbe1df83..5067fd1c8b 100644
--- a/src/fireedge/src/client/store/index.js
+++ b/src/fireedge/src/client/store/index.js
@@ -20,6 +20,7 @@ import { isDevelopment } from 'client/utils'
import * as Auth from 'client/features/Auth/slice'
import * as General from 'client/features/General/slice'
+import * as Guacamole from 'client/features/Guacamole/slice'
import { authApi } from 'client/features/AuthApi'
import { oneApi } from 'client/features/OneApi'
import { unauthenticatedMiddleware } from 'client/features/middleware'
@@ -35,6 +36,7 @@ export const createStore = ({ initState = {}, extraMiddleware = [] }) => {
reducer: {
[Auth.name]: Auth.reducer,
[General.name]: General.reducer,
+ [Guacamole.name]: Guacamole.reducer,
[authApi.reducerPath]: authApi.reducer,
[oneApi.reducerPath]: oneApi.reducer,
},
diff --git a/src/fireedge/src/client/utils/helpers.js b/src/fireedge/src/client/utils/helpers.js
index 601217b0d7..f6cee39718 100644
--- a/src/fireedge/src/client/utils/helpers.js
+++ b/src/fireedge/src/client/utils/helpers.js
@@ -90,6 +90,32 @@ export const encodeBase64 = (string, defaultValue = '') => {
}
}
+/**
+ * Generates a link to download the file, then remove it.
+ *
+ * @param {File} file - File
+ */
+export const downloadFile = (file) => {
+ try {
+ // Create a link and set the URL using `createObjectURL`
+ const link = document.createElement('a')
+ link.style.display = 'none'
+ link.href = URL.createObjectURL(file)
+ link.download = file.name
+
+ // It needs to be added to the DOM so it can be clicked
+ document.body.appendChild(link)
+ link.click()
+
+ // To make this work on Firefox we need to wait
+ // a little while before removing it
+ setTimeout(() => {
+ URL.revokeObjectURL(link.href)
+ link.parentNode.removeChild(link)
+ }, 0)
+ } catch (e) {}
+}
+
/**
* Converts a long string of units into a readable format e.g KB, MB, GB, TB, YB.
*
diff --git a/src/fireedge/src/server/index.js b/src/fireedge/src/server/index.js
index 5f9b9c20cd..d00c9719d5 100644
--- a/src/fireedge/src/server/index.js
+++ b/src/fireedge/src/server/index.js
@@ -129,7 +129,7 @@ frontApps.forEach((frontApp) => {
app.get(`${basename}/${frontApp}`, entrypointApp)
app.get(`${basename}/${frontApp}/*`, entrypointApp)
})
-app.get('/*', (req, res) => res.redirect(`/${defaultAppName}/provision`))
+app.get('/*', (req, res) => res.redirect(`/${defaultAppName}/sunstone`))
// 404 - public
app.get('*', entrypoint404)
diff --git a/src/fireedge/src/server/routes/api/auth/utils.js b/src/fireedge/src/server/routes/api/auth/utils.js
index 5696a6df96..a3ee3c54f0 100644
--- a/src/fireedge/src/server/routes/api/auth/utils.js
+++ b/src/fireedge/src/server/routes/api/auth/utils.js
@@ -406,14 +406,19 @@ const setZones = () => {
* Create token server admin.
*
* @param {object} config - config create token serveradmin
- * @param {string} config.serverAdmin - serverAdmin username
* @param {string} config.username - user name
* @param {string} config.key - serverAdmin key
* @param {string} config.iv - serverAdmin iv
+ * @param {string} config.serverAdmin - serverAdmin username
* @returns {object|undefined} data encrypted serveradmin
*/
-const createTokenServerAdmin = ({ serverAdmin, username, key, iv }) => {
- if (serverAdmin && username && key && iv) {
+const createTokenServerAdmin = ({
+ username,
+ key,
+ iv,
+ serverAdmin = username,
+}) => {
+ if (username && key && iv) {
!(expireTime && typeof expireTime.toSeconds === 'function') && setDates()
const expire = parseInt(expireTime.toSeconds(), 10)
diff --git a/src/fireedge/src/server/routes/api/vm/functions.js b/src/fireedge/src/server/routes/api/vm/functions.js
index 4ac4a9947c..9a60b95c76 100644
--- a/src/fireedge/src/server/routes/api/vm/functions.js
+++ b/src/fireedge/src/server/routes/api/vm/functions.js
@@ -13,14 +13,25 @@
* See the License for the specific language governing permissions and *
* limitations under the License. *
* ------------------------------------------------------------------------- */
+const { randomBytes, createCipheriv } = require('crypto')
+
const { defaults, httpCodes } = require('server/utils/constants')
-const { httpResponse, executeCommand } = require('server/utils/server')
+const {
+ httpResponse,
+ executeCommand,
+ getSunstoneAuth,
+} = require('server/utils/server')
const { getSunstoneConfig } = require('server/utils/yml')
+const { Actions: userActions } = require('server/utils/constants/commands/user')
+const { Actions: vmActions } = require('server/utils/constants/commands/vm')
+const { createTokenServerAdmin } = require('server/routes/api/auth/utils')
-const { defaultEmptyFunction, defaultCommandVM } = defaults
+const { USER_INFO } = userActions
+const { VM_INFO } = vmActions
+
+const { ok, unauthorized, internalServerError, badRequest } = httpCodes
+const { defaultEmptyFunction, defaultCommandVM, defaultTypeCrypto } = defaults
-const { ok, internalServerError, badRequest } = httpCodes
-const httpBadRequest = httpResponse(badRequest, '', '')
const appConfig = getSunstoneConfig()
const prependCommand = appConfig.sunstone_prepend || ''
const regexpSplitLine = /\r|\n/
@@ -39,7 +50,7 @@ const saveAsTemplate = (
params = {},
userData = {}
) => {
- let rtn = httpBadRequest
+ let rtn = httpResponse(badRequest, '', '')
const { id, name, persistent } = params
if (id && name) {
let message = ''
@@ -66,7 +77,216 @@ const saveAsTemplate = (
next()
}
+/**
+ * Generates a session to connect a VM by id through Guacamole.
+ *
+ * @param {object} res - http response
+ * @param {Function} next - express stepper
+ * @param {object} params - params of http request
+ * @param {string} params.id - VM id
+ * @param {'vnc'|'ssh'|'rdp'} params.type - Type connection
+ * @param {object} userData - user of http request
+ * @param {Function} xmlrpc - XML-RPC function
+ */
+const generateGuacamoleSession = (
+ res = {},
+ next = defaultEmptyFunction,
+ params = {},
+ userData = {},
+ xmlrpc = defaultEmptyFunction
+) => {
+ const { id: userAuthId } = userData
+ const { id: vmId, type } = params
+ const ensuredType = `${type}`.toLowerCase()
+
+ if (!['vnc', 'ssh', 'rdp'].includes(ensuredType)) {
+ const messageError = "Type connection isn't supported by Guacamole"
+ res.locals.httpCode = httpResponse(badRequest, messageError)
+ next()
+ }
+
+ const serverAdmin = getSunstoneAuth() ?? {}
+ const { token: authToken } = createTokenServerAdmin(serverAdmin) ?? {}
+
+ if (!authToken) {
+ res.locals.httpCode = httpResponse(badRequest, '')
+ next()
+ }
+
+ const { username } = serverAdmin
+ const oneClient = xmlrpc(`${username}:${username}`, authToken)
+
+ // get authenticated user
+ oneClient({
+ action: USER_INFO,
+ parameters: [parseInt(userAuthId, 10), true],
+ callback: (userInfoErr, { USER } = {}) => {
+ if (userInfoErr || !USER) {
+ res.locals.httpCode = httpResponse(badRequest, userInfoErr)
+ next()
+ }
+
+ // get VM information by id
+ oneClient({
+ action: VM_INFO,
+ parameters: [parseInt(vmId, 10), true],
+ callback: (vmInfoErr, { VM } = {}) => {
+ if (vmInfoErr || !VM) {
+ res.locals.httpCode = httpResponse(unauthorized, vmInfoErr)
+ next()
+ }
+
+ const settings = {
+ vnc: () => getVncSettings(VM),
+ ssh: () => getSshSettings(VM, USER),
+ rdp: () => getRdpSettings(VM),
+ }[ensuredType]?.() ?? { error: '' }
+
+ if (settings.error) {
+ res.locals.httpCode = httpResponse(badRequest, settings.error)
+ next()
+ }
+
+ // const minutesToAdd = 1
+ // const currentDate = new Date()
+ // const expiration = currentDate.getTime() + minutesToAdd * 60000
+
+ const connection = {
+ // expiration,
+ connection: {
+ type: ensuredType,
+ settings: {
+ security: 'any',
+ 'ignore-cert': 'true',
+ 'enable-drive': 'true',
+ 'create-drive-path': 'true',
+ ...settings,
+ },
+ },
+ }
+
+ const wsToken = JSON.stringify(encryptConnection(connection))
+ const encodedWsToken = Buffer.from(wsToken).toString('base64')
+
+ res.locals.httpCode = httpResponse(ok, encodedWsToken)
+ next()
+ },
+ })
+ },
+ })
+}
+
+const getVncSettings = (vmInfo) => {
+ const config = {}
+
+ if (`${vmInfo.USER_TEMPLATE?.HYPERVISOR}`.toLowerCase() === 'vcenter') {
+ const esxHost = vmInfo?.MONITORING?.VCENTER_ESX_HOST
+
+ if (!esxHost) {
+ return {
+ error: `Could not determine the vCenter ESX host where
+ the VM is running. Wait till the VCENTER_ESX_HOST attribute is
+ retrieved once the host has been monitored`,
+ }
+ }
+
+ config.hostname = esxHost
+ }
+
+ if (!config.hostname) {
+ const lastHistory = [vmInfo.HISTORY_RECORDS?.HISTORY ?? []].flat().at(-1)
+ config.hostname = lastHistory?.HOSTNAME ?? 'localhost'
+ }
+
+ config.port = vmInfo.TEMPLATE?.GRAPHICS?.PORT ?? '5900'
+ config.password = vmInfo.TEMPLATE?.GRAPHICS?.PASSWD ?? null
+
+ return config
+}
+
+const getSshSettings = (vmInfo, authUser) => {
+ const config = {}
+
+ const nics = [
+ vmInfo.TEMPLATE?.NIC ?? [],
+ vmInfo.TEMPLATE?.NIC_ALIAS ?? [],
+ ].flat()
+
+ const nicWithExternalPortRange = nics.find((nic) => !nic.EXTERNAL_PORT_RANGE)
+ const { EXTERNAL_PORT_RANGE } = nicWithExternalPortRange ?? {}
+
+ if (EXTERNAL_PORT_RANGE) {
+ const lastHistory = [vmInfo.HISTORY_RECORDS?.HISTORY ?? []].flat().at(-1)
+ const lastHostname = lastHistory?.HOSTNAME
+
+ if (lastHostname) {
+ config.hostname = lastHostname
+ config.port = parseInt(EXTERNAL_PORT_RANGE.split(':')[0], 10) + 21
+ }
+ }
+
+ if (!config.hostname) {
+ const nicWithSsh = nics.find(({ SSH }) => `${SSH}`.toLowerCase() === 'yes')
+ config.hostname = nicWithSsh?.EXTERNAL_IP ?? nicWithSsh?.IP
+ }
+
+ if (!config.hostname) {
+ return { error: 'Wrong configuration. Cannot find a NIC with SSH' }
+ }
+
+ config.port ??= vmInfo.TEMPLATE?.CONTEXT?.SSH_PORT ?? '22'
+
+ if (vmInfo.TEMPLATE?.CONTEXT?.SSH_PUBLIC_KEY) {
+ config['private-key'] = authUser?.TEMPLATE?.SSH_PRIVATE_KEY
+ config.passphrase = authUser?.TEMPLATE?.SSH_PASSPHRASE
+ } else {
+ config.username = vmInfo.TEMPLATE?.CONTEXT?.USERNAME
+ config.password = vmInfo.TEMPLATE?.CONTEXT?.PASSWORD
+ }
+
+ return config
+}
+
+const getRdpSettings = (vmInfo) => {
+ const config = {}
+
+ const nics = [
+ vmInfo.TEMPLATE?.NIC ?? [],
+ vmInfo.TEMPLATE?.NIC_ALIAS ?? [],
+ ].flat()
+
+ const nicWithRdp = nics.find(({ RDP }) => `${RDP}`.toLowerCase() === 'yes')
+ config.hostname = nicWithRdp?.EXTERNAL_IP ?? nicWithRdp?.IP
+
+ if (!config.hostname) {
+ return { error: 'Wrong configuration. Cannot find a NIC with RDP' }
+ }
+
+ config.port = vmInfo.TEMPLATE?.CONTEXT?.RDP_PORT ?? '3389'
+ config.username = vmInfo.TEMPLATE?.CONTEXT?.USERNAME
+ config.password = vmInfo.TEMPLATE?.CONTEXT?.PASSWORD
+ config['resize-method'] = 'display-update'
+
+ if (config.username && config.password) config.security = 'nla'
+
+ return config
+}
+
+const encryptConnection = (data) => {
+ const iv = randomBytes(16)
+ const key = global.paths.FIREEDGE_KEY
+ const cipher = createCipheriv(defaultTypeCrypto, key, iv)
+
+ const ensuredData = typeof data === 'string' ? data : JSON.stringify(data)
+ let value = cipher.update(ensuredData, 'utf-8', 'base64')
+ value += cipher.final('base64')
+
+ return { iv: iv.toString('base64'), value }
+}
+
const functionRoutes = {
saveAsTemplate,
+ generateGuacamoleSession,
}
+
module.exports = functionRoutes
diff --git a/src/fireedge/src/server/routes/api/vm/index.js b/src/fireedge/src/server/routes/api/vm/index.js
index 09cfb6f3f5..6ec511f76b 100644
--- a/src/fireedge/src/server/routes/api/vm/index.js
+++ b/src/fireedge/src/server/routes/api/vm/index.js
@@ -15,13 +15,20 @@
* ------------------------------------------------------------------------- */
const { Actions, Commands } = require('server/routes/api/vm/routes')
-const { saveAsTemplate } = require('server/routes/api/vm/functions')
+const {
+ saveAsTemplate,
+ generateGuacamoleSession,
+} = require('server/routes/api/vm/functions')
-const { VM_SAVEASTEMPLATE } = Actions
+const { VM_SAVEASTEMPLATE, GUACAMOLE } = Actions
module.exports = [
{
...Commands[VM_SAVEASTEMPLATE],
action: saveAsTemplate,
},
+ {
+ ...Commands[GUACAMOLE],
+ action: generateGuacamoleSession,
+ },
]
diff --git a/src/fireedge/src/server/routes/api/vm/routes.js b/src/fireedge/src/server/routes/api/vm/routes.js
index ae8b3f1977..5ee4a6f9fd 100644
--- a/src/fireedge/src/server/routes/api/vm/routes.js
+++ b/src/fireedge/src/server/routes/api/vm/routes.js
@@ -17,15 +17,18 @@
const {
httpMethod,
from: fromData,
-} = require('server/utils/constants/defaults')
+} = require('../../../utils/constants/defaults')
const basepath = '/vm'
-const { POST } = httpMethod
+const { POST, GET } = httpMethod
const { resource, postBody } = fromData
const VM_SAVEASTEMPLATE = 'vm.saveastemplate'
+const GUACAMOLE = 'vm.guacamole'
+
const Actions = {
VM_SAVEASTEMPLATE,
+ GUACAMOLE,
}
module.exports = {
@@ -47,5 +50,18 @@ module.exports = {
},
},
},
+ [GUACAMOLE]: {
+ path: `${basepath}/:id/guacamole/:type`,
+ httpMethod: GET,
+ auth: true,
+ params: {
+ id: {
+ from: resource,
+ },
+ type: {
+ from: resource,
+ },
+ },
+ },
},
}