Compare commits
294 Commits
share
...
tryingsess
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ee489ef5a | ||
|
|
c2f26ae478 | ||
|
|
19e94b031a | ||
|
|
7843a26962 | ||
|
|
7040d5d030 | ||
|
|
66111af16f | ||
|
|
1eae012704 | ||
|
|
6f7db33e89 | ||
|
|
af323e1f91 | ||
|
|
b85d5d1a2d | ||
|
|
6c6e1c27b1 | ||
|
|
08714427b3 | ||
|
|
1aabf5dd45 | ||
|
|
03ed7b74db | ||
|
|
036ab4077c | ||
|
|
1888a447d7 | ||
|
|
4e99e55373 | ||
|
|
a31d3324ad | ||
|
|
092726c3f9 | ||
|
|
630a35d528 | ||
|
|
f82c844f6b | ||
|
|
24f9c51559 | ||
|
|
3ceffc97fb | ||
|
|
dcc48ae5d3 | ||
|
|
b03fc86168 | ||
|
|
0784f7bdfb | ||
|
|
71d26dbb9a | ||
|
|
dd4487edcd | ||
|
|
f805f78ef3 | ||
|
|
73206b556d | ||
|
|
397f3c1251 | ||
|
|
d5d7a128d2 | ||
|
|
1f1e9f5b21 | ||
|
|
d262735c4e | ||
|
|
ecac686e25 | ||
|
|
f1c9a6e87e | ||
|
|
fcba362cc0 | ||
|
|
c3d23d7415 | ||
|
|
84303d0632 | ||
|
|
44b6a6d658 | ||
|
|
4e90027630 | ||
|
|
b207b9b3c4 | ||
|
|
213867ed77 | ||
|
|
4412216b6c | ||
|
|
70014e9021 | ||
|
|
a3704bd4f0 | ||
|
|
ca0b30f37e | ||
|
|
7e9ec60b8e | ||
|
|
adb4f54c54 | ||
|
|
eacba70919 | ||
|
|
a68143af97 | ||
|
|
a95e4e691d | ||
|
|
d4578bef28 | ||
|
|
ae04d5f62d | ||
|
|
c3ea7ba384 | ||
|
|
bca55b62df | ||
|
|
36741b06ad | ||
|
|
e7cb9b149d | ||
|
|
8129378fc8 | ||
|
|
f3d34321cd | ||
|
|
2a1525670c | ||
|
|
a7e36d0119 | ||
|
|
2ba046310f | ||
|
|
6ed942d954 | ||
|
|
0af1c4d4c9 | ||
|
|
811220f882 | ||
|
|
caeef42000 | ||
|
|
90a3cf176f | ||
|
|
44cec9cc31 | ||
|
|
e04cec01e6 | ||
|
|
9df38c142e | ||
|
|
51bf4c8064 | ||
|
|
cca7714f19 | ||
|
|
fc6d9437a9 | ||
|
|
537123c6aa | ||
|
|
373258f402 | ||
|
|
6f2a3b75d4 | ||
|
|
ed4cd383df | ||
|
|
3cd7e1d4ae | ||
|
|
7264710bb5 | ||
|
|
004c425738 | ||
|
|
cd50c47b4a | ||
|
|
93d240e9f2 | ||
|
|
01640adde7 | ||
|
|
c3c1c76acb | ||
|
|
7e58cbc685 | ||
|
|
5e7ad89fdb | ||
|
|
f1be3b7730 | ||
|
|
08286e3172 | ||
|
|
f16683a421 | ||
|
|
ce7f97839b | ||
|
|
7e39db8dbd | ||
|
|
5c029d8976 | ||
|
|
bc76634bca | ||
|
|
fcb8365fe4 | ||
|
|
b2f4a41c0d | ||
|
|
811ca7ca70 | ||
|
|
1fcec290b6 | ||
|
|
d9d2ac29b9 | ||
|
|
28e3c12b23 | ||
|
|
1eff5089f2 | ||
|
|
b0520064e8 | ||
|
|
b3262d9acb | ||
|
|
272cb32e74 | ||
|
|
577246830d | ||
|
|
7fed166c29 | ||
|
|
294a3a8d2d | ||
|
|
835d162ede | ||
|
|
8f8c18490a | ||
|
|
dbcdd6974b | ||
|
|
61071e1d00 | ||
|
|
1aa83a1d5b | ||
|
|
f9c27e4b3d | ||
|
|
cb10e425d7 | ||
|
|
8827940789 | ||
|
|
30e099ee2e | ||
|
|
834aff2a9d | ||
|
|
975b84a3b2 | ||
|
|
14d1690100 | ||
|
|
4bbd86e82f | ||
|
|
ba30495cc8 | ||
|
|
368d58070c | ||
|
|
54f05b11c4 | ||
|
|
77b599ec83 | ||
|
|
2cbcb9dd5d | ||
|
|
0f0d050599 | ||
|
|
99d9a1a053 | ||
|
|
1c9d5e5ce2 | ||
|
|
56748c4478 | ||
|
|
41cdb46fc2 | ||
|
|
515fd749e6 | ||
|
|
2574278b68 | ||
|
|
395c495130 | ||
|
|
075815a6b5 | ||
|
|
29c6bcad1e | ||
|
|
35408b7b1f | ||
|
|
04d8d41b05 | ||
|
|
7700675a76 | ||
|
|
2b57b2535d | ||
|
|
ddc36dbfcf | ||
|
|
2ab78335a2 | ||
|
|
fed9ad156c | ||
|
|
b8e06f91ef | ||
|
|
de56713e96 | ||
|
|
3515158bb0 | ||
|
|
a5ed3edd84 | ||
|
|
e2d32d954e | ||
|
|
baf2b3776c | ||
|
|
f48e32230b | ||
|
|
3338f66c0b | ||
|
|
8d34b3d4af | ||
|
|
023a3fa89d | ||
|
|
ad6d7a1082 | ||
|
|
8e8a23454a | ||
|
|
2cea8c6c8a | ||
|
|
ce795f99f0 | ||
|
|
fcda1da75b | ||
|
|
199d11d22e | ||
|
|
dd819b17ce | ||
|
|
2b93f3f986 | ||
|
|
1cf448c823 | ||
|
|
6618b80964 | ||
|
|
3215f559d4 | ||
|
|
cb634636f5 | ||
|
|
c9b8ed3e58 | ||
|
|
6e6233232c | ||
|
|
642ac66b9f | ||
|
|
319ab11274 | ||
|
|
5e928f2e67 | ||
|
|
26b65b5752 | ||
|
|
b677044fa6 | ||
|
|
bafcd504f2 | ||
|
|
f41a9f76c1 | ||
|
|
37c8f97493 | ||
|
|
9aac529d70 | ||
|
|
3d59581432 | ||
|
|
469e1244cc | ||
|
|
1735edf494 | ||
|
|
b02b258d77 | ||
|
|
222056574c | ||
|
|
6748b3518f | ||
|
|
29ccb721f5 | ||
|
|
26706a154b | ||
|
|
131be49e5f | ||
|
|
b7e57ef49c | ||
|
|
6f57285db0 | ||
|
|
6694637fc1 | ||
|
|
e84ffa16d0 | ||
|
|
10b492b3d2 | ||
|
|
ab0866d36a | ||
|
|
deb29c0224 | ||
|
|
c8944543cc | ||
|
|
6ae1472d6c | ||
|
|
9c2c0393e1 | ||
|
|
062e3c89fd | ||
|
|
c08db405f4 | ||
|
|
e7ebfa7f4c | ||
|
|
46a908fb3c | ||
|
|
36a9fbed56 | ||
|
|
63965ec26e | ||
|
|
8b09158148 | ||
|
|
fc10564ab5 | ||
|
|
4c5b65aa38 | ||
|
|
94e392d3ef | ||
|
|
ebc7924049 | ||
|
|
689b789864 | ||
|
|
c3b0470c20 | ||
|
|
3501cf72aa | ||
|
|
3464bfa85a | ||
|
|
671b1b79ed | ||
|
|
b823e82411 | ||
|
|
25c1462848 | ||
|
|
fb1308556c | ||
|
|
d85f521b53 | ||
|
|
9105755afe | ||
|
|
b3b157641e | ||
|
|
55678c9e91 | ||
|
|
7e173a2a5f | ||
|
|
41b0001886 | ||
|
|
75efcbc3a1 | ||
|
|
3c3641040c | ||
|
|
fce686972c | ||
|
|
d28a7b5f26 | ||
|
|
9372e47589 | ||
|
|
b5af6c0959 | ||
|
|
7ec3c6b1c5 | ||
|
|
12f0e627a1 | ||
|
|
ec6149c127 | ||
|
|
06050b2796 | ||
|
|
704319b27a | ||
|
|
c868e7000e | ||
|
|
85f9c826cf | ||
|
|
7d65d736bc | ||
|
|
2208e362c2 | ||
|
|
ac1933d881 | ||
|
|
b95570b769 | ||
|
|
b52b5a0df7 | ||
|
|
1cedcb791d | ||
|
|
a20e677954 | ||
|
|
2ea8877a76 | ||
|
|
f272b030fb | ||
|
|
0de582a750 | ||
|
|
df6d28fd37 | ||
|
|
d3149ad1b3 | ||
|
|
e35873ab54 | ||
|
|
7071eb6907 | ||
|
|
852b68eaba | ||
|
|
9063951aa7 | ||
|
|
f69abbcda9 | ||
|
|
45f936ee7c | ||
|
|
39663ba9ad | ||
|
|
58d50b2552 | ||
|
|
32a1abce22 | ||
|
|
30388bf064 | ||
|
|
a4bc5e4e7b | ||
|
|
1e450bf019 | ||
|
|
e65215448a | ||
|
|
fbd2d8d829 | ||
|
|
d531463e67 | ||
|
|
28b792fe76 | ||
|
|
c19e4ca60a | ||
|
|
9749e98aee | ||
|
|
b49895045d | ||
|
|
ca21757199 | ||
|
|
2e98be3fca | ||
|
|
3b86ac7777 | ||
|
|
295d1ffb30 | ||
|
|
26eb86193c | ||
|
|
9e2cf17ab7 | ||
|
|
4600969f16 | ||
|
|
332df80b1d | ||
|
|
0a534eed37 | ||
|
|
900a6abeb7 | ||
|
|
9b81646219 | ||
|
|
ca8d26c119 | ||
|
|
245bd0d90e | ||
|
|
67ec379e1d | ||
|
|
3e73e1a561 | ||
|
|
55ccfab66c | ||
|
|
23ae12bf2e | ||
|
|
e8de86da66 | ||
|
|
13a297d58d | ||
|
|
f2aa774acd | ||
|
|
721b478d00 | ||
|
|
4ee672ea3b | ||
|
|
acd210da93 | ||
|
|
4c853a7b20 | ||
|
|
51f20a9e2d | ||
|
|
18a17ea761 | ||
|
|
60c5bc543a | ||
|
|
0aa1dc0c74 | ||
|
|
f5b85b25ed | ||
|
|
4245694b10 | ||
|
|
24c99df65a |
@@ -9,9 +9,9 @@
|
||||
|
||||
```json
|
||||
{
|
||||
"AZURE_TENANT_ID" : "xxxxxxxx",
|
||||
"AZURE_CLIENT_ID": "yyyyyyyy",
|
||||
"AZURE_CLIENT_SECRET": "zzzzzzzz"
|
||||
"IDENTITY_METADATA" : "xxxxxxxx",
|
||||
"CLIENT_ID": "yyyyyyyy",
|
||||
"CLIENT_SECRET": "zzzzzzzz"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
11
angular.json
@@ -23,8 +23,10 @@
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "src/tsconfig.app.json",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
"src/favicon.svg",
|
||||
"src/assets",
|
||||
"src/env.js",
|
||||
"src/oauth-callback.html"
|
||||
],
|
||||
"styles": [
|
||||
"node_modules/@fortawesome/fontawesome-free/scss/fontawesome.scss",
|
||||
@@ -79,7 +81,8 @@
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "qmi-cloud:build"
|
||||
"browserTarget": "qmi-cloud:build",
|
||||
"proxyConfig": "proxy.conf.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
@@ -105,7 +108,7 @@
|
||||
],
|
||||
"scripts": [],
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/favicon.svg",
|
||||
"src/assets"
|
||||
]
|
||||
}
|
||||
|
||||
3
dist/qmi-cloud/assets/faq.md
vendored
@@ -10,7 +10,7 @@ Just an assorted selection of questions and answers.
|
||||
|
||||
### 2\. Are these QMI scenarios ready to go End-To-End demos?
|
||||
|
||||
- Not at this moment, a global environment is being built for PreSales by GEAR.
|
||||
- Not at this moment, a global environment is being built for PreSales by Innovation & Excellence Team.
|
||||
- Provided scenarios are meant for (See 1). QMI works by installing licensing the products. QMI completes the main setup for the scenario to properly work where possible. You may need to put custom data, Qlik Sense applications or whatever you need for your customer engagement.
|
||||
|
||||
* * *
|
||||
@@ -117,7 +117,6 @@ Just an assorted selection of questions and answers.
|
||||
### 15\. How are we managing Costs?
|
||||
|
||||
- Excessive use for instances, will need to be justified, every instance logged it tagged with your trigram. The usage of QMI Cloud will be made public within a Qlik Sense Application.
|
||||
- GEAR is looking at estimating the price for each server that you initiate.
|
||||
|
||||
* * *
|
||||
|
||||
|
||||
BIN
dist/qmi-cloud/assets/favicon.ico
vendored
|
Before Width: | Height: | Size: 15 KiB |
8
dist/qmi-cloud/assets/favicon.svg
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M23.3002 25.1984C21.3002 26.7984 18.7002 27.6984 16.0002 27.6984C9.5002 27.7984 4.2002 22.4984 4.2002 15.9984C4.2002 13.1984 5.2002 10.5984 6.8002 8.59844L4.7002 6.39844C2.5002 8.99844 1.2002 12.2984 1.2002 15.9984C1.2002 24.1984 7.8002 30.7984 16.0002 30.7984C19.6002 30.7984 22.9002 29.4984 25.5002 27.3984L23.3002 25.1984Z"
|
||||
fill="#54565A" />
|
||||
<path
|
||||
d="M27.9002 30.0992H32.0002L27.6002 25.2992C29.6002 22.7992 30.8002 19.4992 30.8002 15.9992C30.8002 7.79922 24.2002 1.19922 16.0002 1.19922C11.5002 1.19922 7.4002 3.29922 4.7002 6.49922L6.9002 8.69922C9.0002 5.89922 12.3002 4.19922 16.0002 4.19922C22.5002 4.19922 27.8002 9.49922 27.8002 15.9992C27.8002 19.6992 26.1002 23.0992 23.3002 25.1992L25.3002 27.1992L25.4002 27.2992L27.9002 30.0992Z"
|
||||
fill="#009845" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 914 B |
1
dist/qmi-cloud/assets/logo.svg
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="72" height="30" fill="none"><path fill="#54565A" d="M38.188.66h-2.985v28.562h2.985V.66ZM46.298 9.27h-2.974v19.953h2.974V9.27ZM46.842 3.02a2.033 2.033 0 1 0-4.014-.652 2.033 2.033 0 0 0 4.014.651ZM22.408 24.334A11.885 11.885 0 0 1 15 26.907C8.425 26.918 3.093 21.587 3.093 15c0-2.833.984-5.429 2.628-7.473L3.536 5.332A14.912 14.912 0 0 0 0 15c0 8.285 6.716 15 15 15 3.645 0 6.986-1.297 9.582-3.46l-2.174-2.206Z"/><path fill="#009845" d="M27.035 29.232h4.132l-4.456-4.856A14.951 14.951 0 0 0 30 15c0-8.284-6.716-15-15-15A14.974 14.974 0 0 0 3.535 5.332L5.72 7.527a11.878 11.878 0 0 1 9.29-4.445c6.586 0 11.917 5.332 11.917 11.918 0 3.785-1.762 7.16-4.51 9.333l2.045 2.055.086.097.022.022 2.465 2.725Z"/><path fill="#54565A" d="M67.191 9.269H63.05l-8.597 7.527L54.44.66h-2.974v28.562h2.974v-9.214l8.825 9.214h4.13L56.875 18.548l10.317-9.28ZM71.495 27.816v1.417h-.248v-.973l-.422.973h-.13l-.433-.973v.973h-.248v-1.417h.303l.443 1.039.432-1.039h.303ZM69.776 27.816v.206h-.433v1.211h-.248v-1.211h-.433v-.206h1.114Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
5
dist/qmi-cloud/env.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
(function (window) {
|
||||
window.__env = window.__env || {};
|
||||
|
||||
window.__env.disabledProvisions = false;
|
||||
}(this));
|
||||
BIN
dist/qmi-cloud/fa-brands-400.cac68c83114580480838.woff2
vendored
Normal file
|
Before Width: | Height: | Size: 699 KiB After Width: | Height: | Size: 713 KiB |
BIN
dist/qmi-cloud/fa-brands-400.dc0bd022735ed218df54.woff
vendored
Normal file
BIN
dist/qmi-cloud/fa-regular-400.05b53beb21e3ef13d282.woff
vendored
Normal file
BIN
dist/qmi-cloud/fa-regular-400.3a3398a6ef60fc64eacf.woff2
vendored
Normal file
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!--
|
||||
Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com
|
||||
Font Awesome Free 5.14.0 by @fontawesome - https://fontawesome.com
|
||||
License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
-->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
|
||||
<metadata>
|
||||
Created by FontForge 20190801 at Mon Mar 23 10:45:51 2020
|
||||
Created by FontForge 20200314 at Wed Jul 15 11:59:40 2020
|
||||
By Robert Madole
|
||||
Copyright (c) Font Awesome
|
||||
</metadata>
|
||||
@@ -20,7 +20,7 @@ Copyright (c) Font Awesome
|
||||
panose-1="2 0 5 3 0 0 0 0 0 0"
|
||||
ascent="448"
|
||||
descent="-64"
|
||||
bbox="-0.0663408 -64.0662 640.01 448.1"
|
||||
bbox="-0.0663408 -64.0662 640.004 448.1"
|
||||
underline-thickness="25"
|
||||
underline-position="-50"
|
||||
unicode-range="U+0020-F5C8"
|
||||
@@ -50,7 +50,7 @@ s-36 16.1182 -36 36s16.1182 36 36 36s36 -16.1182 36 -36zM164 192c0 -19.8818 -16.
|
||||
<glyph glyph-name="flag" unicode=""
|
||||
d="M336.174 368c35.4668 0 73.0195 12.6914 108.922 28.1797c31.6406 13.6514 66.9043 -9.65723 66.9043 -44.1162v-239.919c0 -16.1953 -8.1543 -31.3057 -21.7129 -40.1631c-26.5762 -17.3643 -70.0693 -39.9814 -128.548 -39.9814c-68.6084 0 -112.781 32 -161.913 32
|
||||
c-56.5674 0 -89.957 -11.2803 -127.826 -28.5566v-83.4434c0 -8.83691 -7.16309 -16 -16 -16h-16c-8.83691 0 -16 7.16309 -16 16v406.438c-14.3428 8.2998 -24 23.7979 -24 41.5615c0 27.5693 23.2422 49.71 51.2012 47.8965
|
||||
c22.9658 -1.49023 41.8662 -19.4717 44.4805 -42.3379c0.177734 -1.52441 0.321289 -4.00781 0.321289 -5.54199c0 -4.30176 -1.10352 -11.1035 -2.46289 -15.1846c22.418 8.68555 49.4199 15.168 80.7207 15.168c68.6084 0 112.781 -32 161.913 -32zM464 112v240
|
||||
c22.9658 -1.49023 41.8662 -19.4717 44.4805 -42.3379c0.213867 -1.83398 0.308594 -3.65918 0.308594 -5.5498c0 -5.30273 -0.860352 -10.4053 -2.4502 -15.1768c22.418 8.68555 49.4199 15.168 80.7207 15.168c68.6084 0 112.781 -32 161.913 -32zM464 112v240
|
||||
c-31.5059 -14.6338 -84.5547 -32 -127.826 -32c-59.9111 0 -101.968 32 -161.913 32c-41.4365 0 -80.4766 -16.5879 -102.261 -32v-232c31.4473 14.5967 84.4648 24 127.826 24c59.9111 0 101.968 -32 161.913 -32c41.4365 0 80.4775 16.5879 102.261 32z" />
|
||||
<glyph glyph-name="bookmark" unicode="" horiz-adv-x="384"
|
||||
d="M336 448c26.5098 0 48 -21.4902 48 -48v-464l-192 112l-192 -112v464c0 26.5098 21.4902 48 48 48h288zM336 19.5703v374.434c0 3.31348 -2.68555 5.99609 -6 5.99609h-276c-3.31152 0 -6 -2.68848 -6 -6v-374.43l144 84z" />
|
||||
@@ -77,17 +77,17 @@ c0 -110.569 89.4678 -200 200 -200zM363.244 247.2c0 -67.0518 -72.4209 -68.084 -72
|
||||
c17.5615 9.84473 28.3242 16.541 28.3242 29.5791c0 17.2461 -21.999 28.6934 -39.7842 28.6934c-23.1885 0 -33.8936 -10.9775 -48.9424 -29.9697c-4.05664 -5.11914 -11.46 -6.07031 -16.666 -2.12402l-27.8232 21.0986
|
||||
c-5.10742 3.87207 -6.25098 11.0654 -2.64453 16.3633c23.627 34.6934 53.7217 54.1846 100.575 54.1846c49.0713 0 101.45 -38.3037 101.45 -88.7998zM298 80c0 -23.1592 -18.8408 -42 -42 -42s-42 18.8408 -42 42s18.8408 42 42 42s42 -18.8408 42 -42z" />
|
||||
<glyph glyph-name="eye" unicode="" horiz-adv-x="576"
|
||||
d="M288 304c0.0927734 0 0.244141 0.000976562 0.336914 0.000976562c61.6641 0 111.71 -50.0469 111.71 -111.711c0 -61.6631 -50.0459 -111.71 -111.71 -111.71s-111.71 50.0469 -111.71 111.71c0 8.71289 1.95898 22.5781 4.37305 30.9502
|
||||
c6.93066 -3.94141 19.0273 -7.18457 27 -7.24023c30.9121 0 56 25.0879 56 56c-0.0556641 7.97266 -3.29883 20.0693 -7.24023 27c8.42383 2.62207 22.4189 4.8623 31.2402 5zM572.52 206.6c1.9209 -3.79883 3.47949 -10.3379 3.47949 -14.5947
|
||||
s-1.55859 -10.7959 -3.47949 -14.5947c-54.1992 -105.771 -161.59 -177.41 -284.52 -177.41s-230.29 71.5898 -284.52 177.4c-1.9209 3.79883 -3.47949 10.3379 -3.47949 14.5947s1.55859 10.7959 3.47949 14.5947c54.1992 105.771 161.59 177.41 284.52 177.41
|
||||
s230.29 -71.5898 284.52 -177.4zM288 48c98.6602 0 189.1 55 237.93 144c-48.8398 89 -139.27 144 -237.93 144s-189.09 -55 -237.93 -144c48.8398 -89 139.279 -144 237.93 -144z" />
|
||||
d="M288 304c0.114258 0 0.240234 -0.0175781 0.354492 -0.0175781c61.6543 0 111.71 -50.0557 111.71 -111.71s-50.0557 -111.71 -111.71 -111.71s-111.71 50.0557 -111.71 111.71c0 10.7422 1.51953 21.1328 4.35547 30.9678
|
||||
c7.95898 -4.52637 17.2129 -7.17188 27 -7.24023c30.9072 0 56 25.0928 56 56c-0.0683594 9.78711 -2.71387 19.041 -7.24023 27c9.88379 3.07617 20.3896 4.83008 31.2402 5zM572.52 206.6c2.21387 -4.37793 3.46094 -9.38965 3.46094 -14.626
|
||||
c0 -5.2373 -1.24707 -10.1855 -3.46094 -14.5635c-54.1992 -105.771 -161.59 -177.41 -284.52 -177.41s-230.29 71.5898 -284.52 177.4c-2.21387 4.37793 -3.46094 9.38965 -3.46094 14.626c0 5.2373 1.24707 10.1855 3.46094 14.5635
|
||||
c54.1992 105.771 161.59 177.41 284.52 177.41s230.29 -71.5898 284.52 -177.4zM288 48c98.6602 0 189.1 55 237.93 144c-48.8398 89 -139.27 144 -237.93 144s-189.09 -55 -237.93 -144c48.8398 -89 139.279 -144 237.93 -144z" />
|
||||
<glyph glyph-name="eye-slash" unicode="" horiz-adv-x="640"
|
||||
d="M634 -23c3.31738 -2.65137 6.00977 -8.25098 6.00977 -12.498c0 -3.10449 -1.57715 -7.58984 -3.51953 -10.0117l-10 -12.4902c-2.65234 -3.31152 -8.24707 -6 -12.4902 -6c-3.09961 0 -7.58008 1.57227 -10 3.50977l-598 467.49
|
||||
c-3.31738 2.65137 -6.00977 8.25098 -6.00977 12.498c0 3.10449 1.57715 7.58984 3.51953 10.0117l10 12.4902c2.65234 3.31152 8.24707 6 12.4902 6c3.09961 0 7.58008 -1.57227 10 -3.50977zM296.79 301.53c6.33496 1.35059 16.7324 2.45801 23.21 2.46973
|
||||
c60.4805 0 109.36 -47.9102 111.58 -107.85zM343.21 82.46c-6.33496 -1.34375 -16.7334 -2.44629 -23.21 -2.45996c-60.4697 0 -109.35 47.9102 -111.58 107.84zM320 336c-19.8799 0 -39.2803 -2.7998 -58.2197 -7.09961l-46.4102 36.29
|
||||
c32.9199 11.8096 67.9297 18.8096 104.63 18.8096c122.93 0 230.29 -71.5898 284.57 -177.4c1.91992 -3.79883 3.47949 -10.3379 3.47949 -14.5947s-1.55957 -10.7959 -3.47949 -14.5947c-11.7197 -22.7598 -35.4189 -56.4092 -52.9004 -75.1104l-37.7402 29.5
|
||||
c14.333 15.0156 34.0449 41.9854 44 60.2002c-48.8398 89 -139.279 144 -237.93 144zM320 48c19.8896 0 39.2803 2.7998 58.2197 7.08984l46.4102 -36.2803c-32.9199 -11.7598 -67.9297 -18.8096 -104.63 -18.8096c-122.92 0 -230.28 71.5898 -284.51 177.4
|
||||
c-1.9209 3.79883 -3.47949 10.3379 -3.47949 14.5947s1.55859 10.7959 3.47949 14.5947c11.7168 22.7568 35.4111 56.4014 52.8896 75.1006l37.7402 -29.5c-14.3467 -15.0107 -34.0811 -41.9756 -44.0498 -60.1904c48.8496 -89 139.279 -144 237.93 -144z" />
|
||||
d="M634 -23c3.66895 -2.93262 6.00391 -7.45117 6.00391 -12.5088c0 -3.7832 -1.31543 -7.26074 -3.51367 -10.001l-10 -12.4902c-2.93359 -3.66309 -7.44824 -5.99414 -12.502 -5.99414c-3.77637 0 -7.25 1.31152 -9.98828 3.50391l-598 467.49
|
||||
c-3.66895 2.93262 -6.00391 7.45117 -6.00391 12.5088c0 3.7832 1.31543 7.26074 3.51367 10.001l10 12.4902c2.93359 3.66309 7.44824 5.99414 12.502 5.99414c3.77637 0 7.25 -1.31152 9.98828 -3.50391zM296.79 301.53c7.51172 1.60254 15.2266 2.45508 23.21 2.46973
|
||||
c60.4805 0 109.36 -47.9102 111.58 -107.85zM343.21 82.46c-7.51367 -1.59375 -15.2285 -2.44336 -23.21 -2.45996c-60.4697 0 -109.35 47.9102 -111.58 107.84zM320 336c-19.8799 0 -39.2803 -2.7998 -58.2197 -7.09961l-46.4102 36.29
|
||||
c32.9199 11.8096 67.9297 18.8096 104.63 18.8096c122.93 0 230.29 -71.5898 284.57 -177.4c2.21289 -4.37793 3.45996 -9.38965 3.45996 -14.626c0 -5.2373 -1.24707 -10.1855 -3.45996 -14.5635c-14.1924 -27.5625 -31.9229 -52.6689 -52.9004 -75.1104l-37.7402 29.5
|
||||
c17.2305 18.0527 31.9385 38.1318 44 60.2002c-48.8398 89 -139.279 144 -237.93 144zM320 48c19.8896 0 39.2803 2.7998 58.2197 7.08984l46.4102 -36.2803c-32.9199 -11.7598 -67.9297 -18.8096 -104.63 -18.8096c-122.92 0 -230.28 71.5898 -284.51 177.4
|
||||
c-2.21387 4.37793 -3.46094 9.38965 -3.46094 14.626c0 5.2373 1.24707 10.1855 3.46094 14.5635c14.1885 27.5586 31.916 52.6621 52.8896 75.1006l37.7402 -29.5c-17.249 -18.0469 -31.9727 -38.1221 -44.0498 -60.1904c48.8496 -89 139.279 -144 237.93 -144z" />
|
||||
<glyph glyph-name="calendar-alt" unicode="" horiz-adv-x="448"
|
||||
d="M148 160h-40c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12zM256 172c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40
|
||||
c6.59961 0 12 -5.40039 12 -12v-40zM352 172c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40zM256 76c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12v40
|
||||
@@ -131,47 +131,47 @@ d="M527.9 416c26.5996 0 48.0996 -21.5 48.0996 -48v-352c0 -26.5 -21.5 -48 -48.099
|
||||
h-467.801zM521.9 16c3.2998 0 6 2.7002 6 6v170h-479.801v-170c0 -3.2998 2.7002 -6 6 -6h467.801zM192 116v-40c0 -6.59961 -5.40039 -12 -12 -12h-72c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h72c6.59961 0 12 -5.40039 12 -12zM384 116v-40
|
||||
c0 -6.59961 -5.40039 -12 -12 -12h-136c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h136c6.59961 0 12 -5.40039 12 -12z" />
|
||||
<glyph glyph-name="hdd" unicode="" horiz-adv-x="576"
|
||||
d="M567.403 212.358c5.59668 -8.04688 8.59668 -17.6113 8.59668 -27.4121v-136.946c0 -26.5098 -21.4902 -48 -48 -48h-480c-26.5098 0 -48 21.4902 -48 48v136.946c0 8.30957 3.85156 20.5898 8.59668 27.4121l105.08 151.053
|
||||
c7.90625 11.3652 25.5596 20.5889 39.4033 20.5889h0.000976562h269.838h0.000976562c13.8438 0 31.4971 -9.22363 39.4033 -20.5889zM153.081 336l-77.9131 -112h425.664l-77.9131 112h-269.838zM528 48v128h-480v-128h480zM496 112c0 -17.6729 -14.3271 -32 -32 -32
|
||||
s-32 14.3271 -32 32s14.3271 32 32 32s32 -14.3271 32 -32zM400 112c0 -17.6729 -14.3271 -32 -32 -32s-32 14.3271 -32 32s14.3271 32 32 32s32 -14.3271 32 -32z" />
|
||||
d="M567.403 212.358c5.59668 -8.04688 8.59668 -17.6113 8.59668 -27.4121v-136.946c0 -26.5098 -21.4902 -48 -48 -48h-480c-26.5098 0 -48 21.4902 -48 48v136.946c0 10.167 3.19531 19.6465 8.59668 27.4121l105.08 151.053
|
||||
c8.67383 12.4678 23.0791 20.5889 39.4043 20.5889h269.838c16.3252 0 30.7305 -8.12109 39.4043 -20.5889zM153.081 336l-77.9131 -112h425.664l-77.9131 112h-269.838zM528 48v128h-480v-128h480zM496 112c0 -17.6729 -14.3271 -32 -32 -32s-32 14.3271 -32 32
|
||||
s14.3271 32 32 32s32 -14.3271 32 -32zM400 112c0 -17.6729 -14.3271 -32 -32 -32s-32 14.3271 -32 32s14.3271 32 32 32s32 -14.3271 32 -32z" />
|
||||
<glyph glyph-name="hand-point-right" unicode=""
|
||||
d="M428.8 310.4c45.0996 0 83.2002 -38.1016 83.2002 -83.2002c0 -45.6162 -37.7646 -83.2002 -83.2002 -83.2002h-35.6475c-1.41602 -6.36719 -4.96875 -16.252 -7.92969 -22.0645c2.50586 -22.0059 -3.50293 -44.9775 -15.9844 -62.791
|
||||
d="M428.8 310.4c45.0996 0 83.2002 -38.1016 83.2002 -83.2002c0 -45.6162 -37.7646 -83.2002 -83.2002 -83.2002h-35.6475c-1.71387 -7.70605 -4.43555 -15.2051 -7.92969 -22.0645c2.50586 -22.0059 -3.50293 -44.9775 -15.9844 -62.791
|
||||
c-1.14062 -52.4863 -37.3984 -91.1445 -99.9404 -91.1445h-21.2988c-60.0635 0 -98.5117 40 -127.2 40h-2.67871c-5.74707 -4.95215 -13.5361 -8 -22.1201 -8h-64c-17.6729 0 -32 12.8936 -32 28.7998v230.4c0 15.9062 14.3271 28.7998 32 28.7998h64.001
|
||||
c8.58398 0 16.373 -3.04785 22.1201 -8h2.67871c6.96387 0 14.8623 6.19336 30.1816 23.6689l0.128906 0.148438l0.130859 0.145508c8.85645 9.93652 18.1162 20.8398 25.8506 33.2529c18.7051 30.2471 30.3936 78.7842 75.707 78.7842c56.9277 0 92 -35.2861 92 -83.2002
|
||||
v-0.0839844c0 -6.21777 -0.974609 -16.2148 -2.17578 -22.3154h86.1768zM428.8 192c18.9756 0 35.2002 16.2246 35.2002 35.2002c0 18.7002 -16.7754 35.2002 -35.2002 35.2002h-158.399c0 17.3242 26.3994 35.1992 26.3994 70.3994c0 26.4004 -20.625 35.2002 -44 35.2002
|
||||
c-8.79395 0 -20.4443 -32.7119 -34.9258 -56.0996c-9.07422 -14.5752 -19.5244 -27.2256 -30.7988 -39.875c-16.1094 -18.374 -33.8359 -36.6328 -59.0752 -39.5967v-176.753c42.79 -3.7627 74.5088 -39.6758 120 -39.6758h21.2988
|
||||
c0 -0.0283203 0 0.0361328 0 0.0078125c0 -7.66602 -0.748047 -15.1582 -2.17578 -22.4072h86.1768zM428.8 192c18.9756 0 35.2002 16.2246 35.2002 35.2002c0 18.7002 -16.7754 35.2002 -35.2002 35.2002h-158.399c0 17.3242 26.3994 35.1992 26.3994 70.3994
|
||||
c0 26.4004 -20.625 35.2002 -44 35.2002c-8.79395 0 -20.4443 -32.7119 -34.9258 -56.0996c-9.07422 -14.5752 -19.5244 -27.2256 -30.7988 -39.875c-16.1094 -18.374 -33.8359 -36.6328 -59.0752 -39.5967v-176.753c42.79 -3.7627 74.5088 -39.6758 120 -39.6758h21.2988
|
||||
c40.5244 0 57.124 22.1973 50.6006 61.3252c14.6113 8.00098 24.1514 33.9785 12.9248 53.625c19.3652 18.2246 17.7871 46.3809 4.9502 61.0498h91.0254zM88 64c0 13.2549 -10.7451 24 -24 24s-24 -10.7451 -24 -24s10.7451 -24 24 -24s24 10.7451 24 24z" />
|
||||
<glyph glyph-name="hand-point-left" unicode=""
|
||||
d="M0 227.2c0 45.0986 38.1006 83.2002 83.2002 83.2002h86.1758c-1.3623 6.91016 -2.17578 14.374 -2.17578 22.3994c0 47.9141 35.0723 83.2002 92 83.2002c45.3135 0 57.002 -48.5371 75.7061 -78.7852c7.73438 -12.4121 16.9951 -23.3154 25.8506 -33.2529
|
||||
l0.130859 -0.145508l0.128906 -0.148438c15.3213 -17.4746 23.2197 -23.668 30.1836 -23.668h2.67871c5.74707 4.95215 13.5361 8 22.1201 8h64c17.6729 0 32 -12.8936 32 -28.7998v-230.4c0 -15.9062 -14.3271 -28.7998 -32 -28.7998h-64
|
||||
c-8.58398 0 -16.373 3.04785 -22.1201 8h-2.67871c-28.6885 0 -67.1367 -40 -127.2 -40h-21.2988c-62.542 0 -98.8008 38.6582 -99.9404 91.1445c-12.4814 17.8135 -18.4922 40.7852 -15.9844 62.791c-2.96094 5.8125 -6.51367 15.6973 -7.92969 22.0645h-35.6465
|
||||
c-8.58398 0 -16.373 3.04785 -22.1201 8h-2.67871c-28.6885 0 -67.1367 -40 -127.2 -40h-21.2988c-62.542 0 -98.8008 38.6582 -99.9404 91.1445c-12.4814 17.8135 -18.4922 40.7852 -15.9844 62.791c-3.49414 6.85938 -6.21582 14.3584 -7.92969 22.0645h-35.6465
|
||||
c-45.4355 0 -83.2002 37.584 -83.2002 83.2002zM48 227.2c0 -18.9756 16.2246 -35.2002 35.2002 -35.2002h91.0244c-12.8369 -14.6689 -14.415 -42.8252 4.9502 -61.0498c-11.2256 -19.6465 -1.68652 -45.624 12.9248 -53.625
|
||||
c-6.52246 -39.1279 10.0771 -61.3252 50.6016 -61.3252h21.2988c45.4912 0 77.21 35.9131 120 39.6768v176.752c-25.2393 2.96289 -42.9658 21.2227 -59.0752 39.5967c-11.2744 12.6494 -21.7246 25.2998 -30.7988 39.875
|
||||
c-14.4814 23.3877 -26.1318 56.0996 -34.9258 56.0996c-23.375 0 -44 -8.7998 -44 -35.2002c0 -35.2002 26.3994 -53.0752 26.3994 -70.3994h-158.399c-18.4248 0 -35.2002 -16.5 -35.2002 -35.2002zM448 88c-13.2549 0 -24 -10.7451 -24 -24s10.7451 -24 24 -24
|
||||
s24 10.7451 24 24s-10.7451 24 -24 24z" />
|
||||
<glyph glyph-name="hand-point-up" unicode="" horiz-adv-x="448"
|
||||
d="M105.6 364.8c0 45.0996 38.1016 83.2002 83.2002 83.2002c45.6162 0 83.2002 -37.7646 83.2002 -83.2002v-35.6465c6.36719 -1.41602 16.252 -4.96875 22.0645 -7.92969c22.0059 2.50684 44.9775 -3.50293 62.791 -15.9844
|
||||
d="M105.6 364.8c0 45.0996 38.1016 83.2002 83.2002 83.2002c45.6162 0 83.2002 -37.7646 83.2002 -83.2002v-35.6465c7.70605 -1.71387 15.2051 -4.43555 22.0645 -7.92969c22.0059 2.50684 44.9775 -3.50293 62.791 -15.9844
|
||||
c52.4863 -1.14062 91.1445 -37.3984 91.1445 -99.9404v-21.2988c0 -60.0635 -40 -98.5117 -40 -127.2v-2.67871c4.95215 -5.74707 8 -13.5361 8 -22.1201v-64c0 -17.6729 -12.8936 -32 -28.7998 -32h-230.4c-15.9062 0 -28.7998 14.3271 -28.7998 32v64
|
||||
c0 8.58398 3.04785 16.373 8 22.1201v2.67871c0 6.96387 -6.19336 14.8623 -23.6689 30.1816l-0.148438 0.128906l-0.145508 0.130859c-9.93652 8.85645 -20.8398 18.1162 -33.2529 25.8506c-30.2471 18.7051 -78.7842 30.3936 -78.7842 75.707
|
||||
c0 56.9277 35.2861 92 83.2002 92h0.0839844c6.21777 0 16.2148 -0.974609 22.3154 -2.17578v86.1768zM224 364.8c0 18.9756 -16.2246 35.2002 -35.2002 35.2002c-18.7002 0 -35.2002 -16.7754 -35.2002 -35.2002v-158.399c-17.3242 0 -35.1992 26.3994 -70.3994 26.3994
|
||||
c-26.4004 0 -35.2002 -20.625 -35.2002 -44c0 -8.79395 32.7119 -20.4443 56.0996 -34.9258c14.5752 -9.07422 27.2256 -19.5244 39.875 -30.7988c18.374 -16.1094 36.6328 -33.8359 39.5967 -59.0752h176.753c3.7627 42.79 39.6758 74.5088 39.6758 120v21.2988
|
||||
c0 40.5244 -22.1973 57.124 -61.3252 50.6006c-8.00098 14.6113 -33.9785 24.1514 -53.625 12.9248c-18.2246 19.3652 -46.3809 17.7871 -61.0498 4.9502v91.0254zM352 24c-13.2549 0 -24 -10.7451 -24 -24s10.7451 -24 24 -24s24 10.7451 24 24s-10.7451 24 -24 24z" />
|
||||
c0 56.9277 35.2861 92 83.2002 92c0.0283203 0 -0.0361328 0 -0.0078125 0c7.66602 0 15.1582 -0.748047 22.4072 -2.17578v86.1768zM224 364.8c0 18.9756 -16.2246 35.2002 -35.2002 35.2002c-18.7002 0 -35.2002 -16.7754 -35.2002 -35.2002v-158.399
|
||||
c-17.3242 0 -35.1992 26.3994 -70.3994 26.3994c-26.4004 0 -35.2002 -20.625 -35.2002 -44c0 -8.79395 32.7119 -20.4443 56.0996 -34.9258c14.5752 -9.07422 27.2256 -19.5244 39.875 -30.7988c18.374 -16.1094 36.6328 -33.8359 39.5967 -59.0752h176.753
|
||||
c3.7627 42.79 39.6758 74.5088 39.6758 120v21.2988c0 40.5244 -22.1973 57.124 -61.3252 50.6006c-8.00098 14.6113 -33.9785 24.1514 -53.625 12.9248c-18.2246 19.3652 -46.3809 17.7871 -61.0498 4.9502v91.0254zM352 24c-13.2549 0 -24 -10.7451 -24 -24
|
||||
s10.7451 -24 24 -24s24 10.7451 24 24s-10.7451 24 -24 24z" />
|
||||
<glyph glyph-name="hand-point-down" unicode="" horiz-adv-x="448"
|
||||
d="M188.8 -64c-45.0986 0 -83.2002 38.1006 -83.2002 83.2002v86.1758c-6.91016 -1.3623 -14.374 -2.17578 -22.3994 -2.17578c-47.9141 0 -83.2002 35.0723 -83.2002 92c0 45.3135 48.5371 57.002 78.7852 75.707c12.4121 7.73438 23.3154 16.9951 33.2529 25.8506
|
||||
l0.145508 0.130859l0.148438 0.128906c17.4746 15.3213 23.668 23.2197 23.668 30.1836v2.67871c-4.95215 5.74707 -8 13.5361 -8 22.1201v64c0 17.6729 12.8936 32 28.7998 32h230.4c15.9062 0 28.7998 -14.3271 28.7998 -32v-64.001
|
||||
c0 -8.58398 -3.04785 -16.373 -8 -22.1201v-2.67871c0 -28.6885 40 -67.1367 40 -127.2v-21.2988c0 -62.542 -38.6582 -98.8008 -91.1445 -99.9404c-17.8135 -12.4814 -40.7852 -18.4922 -62.791 -15.9844c-5.8125 -2.96094 -15.6973 -6.51367 -22.0645 -7.92969v-35.6465
|
||||
c0 -8.58398 -3.04785 -16.373 -8 -22.1201v-2.67871c0 -28.6885 40 -67.1367 40 -127.2v-21.2988c0 -62.542 -38.6582 -98.8008 -91.1445 -99.9404c-17.8135 -12.4814 -40.7852 -18.4922 -62.791 -15.9844c-6.85938 -3.49414 -14.3584 -6.21582 -22.0645 -7.92969v-35.6465
|
||||
c0 -45.4355 -37.584 -83.2002 -83.2002 -83.2002zM188.8 -16c18.9756 0 35.2002 16.2246 35.2002 35.2002v91.0244c14.6689 -12.8369 42.8252 -14.415 61.0498 4.9502c19.6465 -11.2256 45.624 -1.68652 53.625 12.9248c39.1279 -6.52246 61.3252 10.0771 61.3252 50.6016
|
||||
v21.2988c0 45.4912 -35.9131 77.21 -39.6768 120h-176.752c-2.96289 -25.2393 -21.2227 -42.9658 -39.5967 -59.0752c-12.6494 -11.2744 -25.2998 -21.7246 -39.875 -30.7988c-23.3877 -14.4814 -56.0996 -26.1318 -56.0996 -34.9258c0 -23.375 8.7998 -44 35.2002 -44
|
||||
c35.2002 0 53.0752 26.3994 70.3994 26.3994v-158.399c0 -18.4248 16.5 -35.2002 35.2002 -35.2002zM328 384c0 -13.2549 10.7451 -24 24 -24s24 10.7451 24 24s-10.7451 24 -24 24s-24 -10.7451 -24 -24z" />
|
||||
<glyph glyph-name="copy" unicode="" horiz-adv-x="448"
|
||||
d="M433.941 382.059c7.75977 -7.75977 14.0586 -22.9658 14.0586 -33.9404v-268.118c0 -26.5098 -21.4902 -48 -48 -48h-80v-48c0 -26.5098 -21.4902 -48 -48 -48h-224c-26.5098 0 -48 21.4902 -48 48v320c0 26.5098 21.4902 48 48 48h80v48c0 26.5098 21.4902 48 48 48
|
||||
h172.118c10.9746 0 26.1807 -6.29883 33.9404 -14.0586zM266 -16c3.31152 0 6 2.68848 6 6v42h-96c-26.5098 0 -48 21.4902 -48 48v224h-74c-3.31152 0 -6 -2.68848 -6 -6v-308c0 -3.31152 2.68848 -6 6 -6h212zM394 80c3.31152 0 6 2.68848 6 6v202h-88
|
||||
c-13.2549 0 -24 10.7451 -24 24v88h-106c-3.31152 0 -6 -2.68848 -6 -6v-308c0 -3.31152 2.68848 -6 6 -6h212zM400 336v9.63184v0.000976562c0 1.37207 -0.787109 3.27246 -1.75684 4.24219l-48.3682 48.3682c-1.12598 1.125 -2.65234 1.75684 -4.24316 1.75684h-9.63184
|
||||
v-64h64z" />
|
||||
d="M433.941 382.059c8.68848 -8.68848 14.0586 -20.6943 14.0586 -33.9404v-268.118c0 -26.5098 -21.4902 -48 -48 -48h-80v-48c0 -26.5098 -21.4902 -48 -48 -48h-224c-26.5098 0 -48 21.4902 -48 48v320c0 26.5098 21.4902 48 48 48h80v48c0 26.5098 21.4902 48 48 48
|
||||
h172.118c13.2461 0 25.252 -5.37012 33.9404 -14.0586zM266 -16c3.31152 0 6 2.68848 6 6v42h-96c-26.5098 0 -48 21.4902 -48 48v224h-74c-3.31152 0 -6 -2.68848 -6 -6v-308c0 -3.31152 2.68848 -6 6 -6h212zM394 80c3.31152 0 6 2.68848 6 6v202h-88
|
||||
c-13.2549 0 -24 10.7451 -24 24v88h-106c-3.31152 0 -6 -2.68848 -6 -6v-308c0 -3.31152 2.68848 -6 6 -6h212zM400 336v9.63184c0 1.65527 -0.670898 3.15723 -1.75684 4.24316l-48.3682 48.3682c-1.12598 1.125 -2.65234 1.75684 -4.24316 1.75684h-9.63184v-64h64z" />
|
||||
<glyph glyph-name="save" unicode="" horiz-adv-x="448"
|
||||
d="M433.941 318.059c7.75977 -7.75977 14.0586 -22.9658 14.0586 -33.9404v-268.118c0 -26.5098 -21.4902 -48 -48 -48h-352c-26.5098 0 -48 21.4902 -48 48v352c0 26.5098 21.4902 48 48 48h268.118c10.9746 0 26.1807 -6.29883 33.9404 -14.0586zM272 368h-128v-80h128v80
|
||||
zM394 16c3.31152 0 6 2.68848 6 6v259.632v0.000976562c0 1.37207 -0.787109 3.27246 -1.75684 4.24219l-78.2432 78.2432v-100.118c0 -13.2549 -10.7451 -24 -24 -24h-176c-13.2549 0 -24 10.7451 -24 24v104h-42c-3.31152 0 -6 -2.68848 -6 -6v-340
|
||||
c0 -3.31152 2.68848 -6 6 -6h340zM224 216c48.5234 0 88 -39.4766 88 -88s-39.4766 -88 -88 -88s-88 39.4766 -88 88s39.4766 88 88 88zM224 88c22.0557 0 40 17.9443 40 40s-17.9443 40 -40 40s-40 -17.9443 -40 -40s17.9443 -40 40 -40z" />
|
||||
d="M433.941 318.059c8.68848 -8.68848 14.0586 -20.6943 14.0586 -33.9404v-268.118c0 -26.5098 -21.4902 -48 -48 -48h-352c-26.5098 0 -48 21.4902 -48 48v352c0 26.5098 21.4902 48 48 48h268.118c13.2461 0 25.252 -5.37012 33.9404 -14.0586zM272 368h-128v-80h128v80z
|
||||
M394 16c3.31152 0 6 2.68848 6 6v259.632c0 1.65527 -0.670898 3.15723 -1.75684 4.24316l-78.2432 78.2432v-100.118c0 -13.2549 -10.7451 -24 -24 -24h-176c-13.2549 0 -24 10.7451 -24 24v104h-42c-3.31152 0 -6 -2.68848 -6 -6v-340c0 -3.31152 2.68848 -6 6 -6h340z
|
||||
M224 216c48.5234 0 88 -39.4766 88 -88s-39.4766 -88 -88 -88s-88 39.4766 -88 88s39.4766 88 88 88zM224 88c22.0557 0 40 17.9443 40 40s-17.9443 40 -40 40s-40 -17.9443 -40 -40s17.9443 -40 40 -40z" />
|
||||
<glyph glyph-name="square" unicode="" horiz-adv-x="448"
|
||||
d="M400 416c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h352zM394 16c3.2998 0 6 2.7002 6 6v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h340z" />
|
||||
<glyph glyph-name="envelope" unicode=""
|
||||
@@ -181,7 +181,7 @@ c-22.5439 -17.748 -60.3359 -55.1787 -103.053 -54.9473c-42.9277 -0.231445 -81.205
|
||||
<glyph glyph-name="lightbulb" unicode="" horiz-adv-x="352"
|
||||
d="M176 368c8.83984 0 16 -7.16016 16 -16s-7.16016 -16 -16 -16c-35.2803 0 -64 -28.7002 -64 -64c0 -8.83984 -7.16016 -16 -16 -16s-16 7.16016 -16 16c0 52.9404 43.0596 96 96 96zM96.0596 -11.1699l-0.0400391 43.1797h159.961l-0.0507812 -43.1797
|
||||
c-0.00976562 -3.13965 -0.939453 -6.21973 -2.67969 -8.83984l-24.5098 -36.8398c-2.95996 -4.45996 -7.95996 -7.14062 -13.3203 -7.14062h-78.8496c-5.35059 0 -10.3506 2.68066 -13.3203 7.14062l-24.5098 36.8398c-1.75 2.62012 -2.68066 5.68945 -2.68066 8.83984z
|
||||
M176 448c97.2002 0 176 -78.7998 176 -176c0 -44.3701 -16.4502 -84.8496 -43.5498 -115.79c-16.6406 -18.9795 -42.7402 -58.79 -52.4199 -92.1602v-0.0498047h-48v0.0996094c0.00390625 4.04199 0.999023 10.4482 2.21973 14.3008
|
||||
M176 448c97.2002 0 176 -78.7998 176 -176c0 -44.3701 -16.4502 -84.8496 -43.5498 -115.79c-16.6406 -18.9795 -42.7402 -58.79 -52.4199 -92.1602v-0.0498047h-48v0.0996094c0.00488281 4.98145 0.790039 9.78809 2.21973 14.3008
|
||||
c5.67969 17.9893 22.9902 64.8496 62.0996 109.46c20.4102 23.29 31.6504 53.1699 31.6504 84.1396c0 70.5801 -57.4199 128 -128 128c-68.2803 0 -128.15 -54.3604 -127.95 -128c0.0898438 -30.9902 11.0703 -60.71 31.6104 -84.1396
|
||||
c39.3496 -44.9004 56.5801 -91.8604 62.1699 -109.67c1.42969 -4.56055 2.13965 -9.30078 2.15039 -14.0703v-0.120117h-48v0.0595703c-9.68066 33.3604 -35.7803 73.1709 -52.4209 92.1602c-27.1094 30.9307 -43.5596 71.4102 -43.5596 115.78
|
||||
c0 93.0303 73.7197 176 176 176z" />
|
||||
@@ -241,13 +241,13 @@ c4.70508 4.66699 12.3027 4.63672 16.9697 -0.0683594l22.5361 -22.7178c4.66699 -4.
|
||||
<glyph glyph-name="share-square" unicode="" horiz-adv-x="576"
|
||||
d="M561.938 289.94c18.75 -18.7402 18.75 -49.1406 0 -67.8809l-143.998 -144c-29.9727 -29.9727 -81.9404 -9.05273 -81.9404 33.9404v53.7998c-101.266 -7.83691 -99.625 -31.6406 -84.1104 -78.7598c14.2285 -43.0889 -33.4736 -79.248 -71.0195 -55.7402
|
||||
c-51.6924 32.3057 -84.8701 83.0635 -84.8701 144.76c0 39.3408 12.2197 72.7402 36.3301 99.3008c19.8398 21.8398 47.7402 38.4697 82.9102 49.4199c36.7295 11.4395 78.3096 16.1094 120.76 17.9893v57.1982c0 42.9355 51.9258 63.9541 81.9404 33.9404zM384 112l144 144
|
||||
l-144 144v-104.09c-110.86 -0.90332 -240 -10.5166 -240 -119.851c0 -52.1396 32.79 -85.6094 62.3096 -104.06c-39.8174 120.65 48.999 141.918 177.69 143.84v-103.84zM408.74 27.5068c6.14844 1.75684 15.5449 5.92383 20.9736 9.30273
|
||||
l-144 144v-104.09c-110.86 -0.90332 -240 -10.5166 -240 -119.851c0 -52.1396 32.79 -85.6094 62.3096 -104.06c-39.8174 120.65 48.999 141.918 177.69 143.84v-103.84zM408.74 27.5068c7.4375 2.125 14.5508 5.30566 20.9736 9.30273
|
||||
c7.97656 4.95215 18.2861 -0.825195 18.2861 -10.2139v-42.5957c0 -26.5098 -21.4902 -48 -48 -48h-352c-26.5098 0 -48 21.4902 -48 48v352c0 26.5098 21.4902 48 48 48h132c6.62695 0 12 -5.37305 12 -12v-4.48633c0 -4.91699 -2.9873 -9.36914 -7.56934 -11.1514
|
||||
c-13.7021 -5.33105 -26.3955 -11.5371 -38.0498 -18.585c-1.59668 -0.974609 -4.41016 -1.77051 -6.28027 -1.77734h-86.1006c-3.31152 0 -6 -2.68848 -6 -6v-340c0 -3.31152 2.68848 -6 6 -6h340c3.31152 0 6 2.68848 6 6v25.9658c0 5.37012 3.5791 10.0596 8.74023 11.541
|
||||
z" />
|
||||
c-13.7021 -5.33105 -26.3955 -11.5371 -38.0498 -18.585c-1.82715 -1.11523 -3.98633 -1.76953 -6.28027 -1.77734h-86.1006c-3.31152 0 -6 -2.68848 -6 -6v-340c0 -3.31152 2.68848 -6 6 -6h340c3.31152 0 6 2.68848 6 6v25.9658c0 5.37012 3.5791 10.0596 8.74023 11.541z
|
||||
" />
|
||||
<glyph glyph-name="compass" unicode="" horiz-adv-x="496"
|
||||
d="M347.94 318.14c16.6592 7.61035 33.8096 -9.54004 26.1992 -26.1992l-65.9697 -144.341c-2.73047 -5.97363 -9.7959 -13.0391 -15.7695 -15.7695l-144.341 -65.9697c-16.6592 -7.61035 -33.8096 9.5498 -26.1992 26.1992l65.9697 144.341
|
||||
c2.73047 5.97363 9.7959 13.0391 15.7695 15.7695zM270.58 169.42c12.4697 12.4697 12.4697 32.6904 0 45.1602s-32.6904 12.4697 -45.1602 0s-12.4697 -32.6904 0 -45.1602s32.6904 -12.4697 45.1602 0zM248 440c136.97 0 248 -111.03 248 -248s-111.03 -248 -248 -248
|
||||
d="M347.94 318.14c16.6592 7.61035 33.8096 -9.54004 26.1992 -26.1992l-65.9697 -144.341c-3.19238 -6.9834 -8.78613 -12.5771 -15.7695 -15.7695l-144.341 -65.9697c-16.6592 -7.61035 -33.8096 9.5498 -26.1992 26.1992l65.9697 144.341
|
||||
c3.19238 6.9834 8.78613 12.5771 15.7695 15.7695zM270.58 169.42c12.4697 12.4697 12.4697 32.6904 0 45.1602s-32.6904 12.4697 -45.1602 0s-12.4697 -32.6904 0 -45.1602s32.6904 -12.4697 45.1602 0zM248 440c136.97 0 248 -111.03 248 -248s-111.03 -248 -248 -248
|
||||
s-248 111.03 -248 248s111.03 248 248 248zM248 -8c110.28 0 200 89.7197 200 200s-89.7197 200 -200 200s-200 -89.7197 -200 -200s89.7197 -200 200 -200z" />
|
||||
<glyph glyph-name="caret-square-down" unicode="" horiz-adv-x="448"
|
||||
d="M125.1 240h197.801c10.6992 0 16.0996 -13 8.5 -20.5l-98.9004 -98.2998c-4.7002 -4.7002 -12.2002 -4.7002 -16.9004 0l-98.8994 98.2998c-7.7002 7.5 -2.2998 20.5 8.39941 20.5zM448 368v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352
|
||||
@@ -287,7 +287,7 @@ l40.4004 -59.8994l70.8994 13.6992c13 2.60059 26.6006 -1.59961 36.2002 -11.0996c9
|
||||
l-91 17.5996l17.5996 -91.2002l-76.7998 -52l76.7998 -52l-17.5996 -91.1992l90.8994 17.5996l51.9004 -77l51.9004 76.9004l91 -17.6006zM256 296c57.2998 0 104 -46.7002 104 -104s-46.7002 -104 -104 -104s-104 46.7002 -104 104s46.7002 104 104 104zM256 136
|
||||
c30.9004 0 56 25.0996 56 56s-25.0996 56 -56 56s-56 -25.0996 -56 -56s25.0996 -56 56 -56z" />
|
||||
<glyph glyph-name="moon" unicode=""
|
||||
d="M279.135 -64c-141.424 0 -256 114.64 -256 256c0 141.425 114.641 256 256 256c13.0068 -0.00195312 33.9443 -1.91797 46.7354 -4.27734c44.0205 -8.13086 53.7666 -66.8691 15.0215 -88.9189c-41.374 -23.5439 -67.4336 -67.4121 -67.4336 -115.836
|
||||
d="M279.135 -64c-141.424 0 -256 114.64 -256 256c0 141.425 114.641 256 256 256c16.0342 -0.00292969 31.5078 -1.46875 46.7354 -4.27734c44.0205 -8.13086 53.7666 -66.8691 15.0215 -88.9189c-41.374 -23.5439 -67.4336 -67.4121 -67.4336 -115.836
|
||||
c0 -83.5234 75.9238 -146.475 158.272 -130.792c43.6904 8.32129 74.5186 -42.5693 46.248 -77.4004c-47.8613 -58.9717 -120.088 -94.7754 -198.844 -94.7754zM279.135 400c-114.875 0 -208 -93.125 -208 -208s93.125 -208 208 -208
|
||||
c65.2314 0 123.439 30.0361 161.575 77.0244c-111.611 -21.2568 -215.252 64.0957 -215.252 177.943c0 67.5127 36.9326 126.392 91.6934 157.555c-12.3271 2.27637 -25.0312 3.47754 -38.0166 3.47754z" />
|
||||
<glyph glyph-name="caret-square-left" unicode="" horiz-adv-x="448"
|
||||
@@ -334,12 +334,12 @@ c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM256 396.1v-76.0996h76.0996zM336 -1
|
||||
c-33.2002 0 -58 30.4004 -51.4004 62.9004l19.7002 97.0996v32h32v-32h22.1006c5.7998 0 10.6992 -4.09961 11.7998 -9.7002zM160.3 57.9004c17.9004 0 32.4004 12.0996 32.4004 27c0 14.8994 -14.5 27 -32.4004 27c-17.8994 0 -32.3994 -12.1006 -32.3994 -27
|
||||
c0 -14.9004 14.5 -27 32.3994 -27zM192.3 256v-32h-32v32h32z" />
|
||||
<glyph glyph-name="file-audio" unicode="" horiz-adv-x="384"
|
||||
d="M369.941 350.059c7.75977 -7.75977 14.0586 -22.9658 14.0586 -33.9404v-332.118c0 -26.5098 -21.4902 -48 -48 -48h-288c-26.5098 0 -48 21.4902 -48 48v416c0 26.5098 21.4902 48 48 48h204.118c10.9746 0 26.1807 -6.29883 33.9404 -14.0586zM332.118 320
|
||||
d="M369.941 350.059c8.68848 -8.68848 14.0586 -20.6943 14.0586 -33.9404v-332.118c0 -26.5098 -21.4902 -48 -48 -48h-288c-26.5098 0 -48 21.4902 -48 48v416c0 26.5098 21.4902 48 48 48h204.118c13.2461 0 25.252 -5.37012 33.9404 -14.0586zM332.118 320
|
||||
l-76.1182 76.1182v-76.1182h76.1182zM48 -16h288v288h-104c-13.2549 0 -24 10.7451 -24 24v104h-160v-416zM192 60.0244c0 -10.6914 -12.9258 -16.0459 -20.4854 -8.48535l-35.5146 35.9746h-28c-6.62695 0 -12 5.37305 -12 12v56c0 6.62695 5.37305 12 12 12h28
|
||||
l35.5146 36.9473c7.56055 7.56055 20.4854 2.20605 20.4854 -8.48535v-135.951zM233.201 107.154c9.05078 9.29688 9.05957 24.1328 0.000976562 33.4385c-22.1494 22.752 12.2344 56.2461 34.3945 33.4814c27.1982 -27.9404 27.2119 -72.4443 0.000976562 -100.401
|
||||
c-21.793 -22.3857 -56.9463 10.3154 -34.3965 33.4814z" />
|
||||
<glyph glyph-name="file-video" unicode="" horiz-adv-x="384"
|
||||
d="M369.941 350.059c7.75977 -7.75977 14.0586 -22.9658 14.0586 -33.9404v-332.118c0 -26.5098 -21.4902 -48 -48 -48h-288c-26.5098 0 -48 21.4902 -48 48v416c0 26.5098 21.4902 48 48 48h204.118c10.9746 0 26.1807 -6.29883 33.9404 -14.0586zM332.118 320
|
||||
d="M369.941 350.059c8.68848 -8.68848 14.0586 -20.6943 14.0586 -33.9404v-332.118c0 -26.5098 -21.4902 -48 -48 -48h-288c-26.5098 0 -48 21.4902 -48 48v416c0 26.5098 21.4902 48 48 48h204.118c13.2461 0 25.252 -5.37012 33.9404 -14.0586zM332.118 320
|
||||
l-76.1182 76.1182v-76.1182h76.1182zM48 -16h288v288h-104c-13.2549 0 -24 10.7451 -24 24v104h-160v-416zM276.687 195.303c10.0049 10.0049 27.3135 2.99707 27.3135 -11.3135v-111.976c0 -14.2939 -17.2959 -21.332 -27.3135 -11.3135l-52.6865 52.6738v-37.374
|
||||
c0 -11.0459 -8.9541 -20 -20 -20h-104c-11.0459 0 -20 8.9541 -20 20v104c0 11.0459 8.9541 20 20 20h104c11.0459 0 20 -8.9541 20 -20v-37.374z" />
|
||||
<glyph glyph-name="file-code" unicode="" horiz-adv-x="384"
|
||||
@@ -376,9 +376,9 @@ c73.46 -15.2598 127.939 -77.46 127.939 -155.16c0 -41.3604 6.03027 -70.7197 14.33
|
||||
c-35.3203 0 -63.9697 28.6504 -63.9697 64h127.939c0 -35.3496 -28.6494 -64 -63.9697 -64z" />
|
||||
<glyph glyph-name="copyright" unicode=""
|
||||
d="M256 440c136.967 0 248 -111.033 248 -248s-111.033 -248 -248 -248s-248 111.033 -248 248s111.033 248 248 248zM256 -8c110.549 0 200 89.4678 200 200c0 110.549 -89.4678 200 -200 200c-110.549 0 -200 -89.4688 -200 -200c0 -110.549 89.4678 -200 200 -200z
|
||||
M363.351 93.0645c-9.61328 -9.71289 -45.5293 -41.3965 -104.064 -41.3965c-82.4297 0 -140.484 61.4248 -140.484 141.567c0 79.1514 60.2754 139.4 139.763 139.4c55.5303 0 88.7373 -26.6201 97.5928 -34.7783c2.13379 -1.96289 3.86523 -5.9082 3.86523 -8.80762
|
||||
c0 -1.95508 -0.864258 -4.87402 -1.92969 -6.51465l-18.1543 -28.1133c-3.8418 -5.9502 -11.9668 -7.28223 -17.499 -2.9209c-8.5957 6.77637 -31.8145 22.5381 -61.708 22.5381c-48.3037 0 -77.916 -35.3301 -77.916 -80.082c0 -41.5889 26.8877 -83.6924 78.2764 -83.6924
|
||||
c32.6572 0 56.8428 19.0391 65.7266 27.2256c5.26953 4.85645 13.5957 4.03906 17.8193 -1.73828l19.8652 -27.1699c1.28613 -1.74512 2.33008 -4.91992 2.33008 -7.08789c0 -2.72363 -1.56055 -6.5 -3.48242 -8.42969z" />
|
||||
M363.351 93.0645c-9.61328 -9.71289 -45.5293 -41.3965 -104.064 -41.3965c-82.4297 0 -140.484 61.4248 -140.484 141.567c0 79.1514 60.2754 139.4 139.763 139.4c55.5303 0 88.7373 -26.6201 97.5928 -34.7783c2.37793 -2.1875 3.86914 -5.3252 3.86914 -8.80762
|
||||
c0 -2.39746 -0.717773 -4.64258 -1.93359 -6.51465l-18.1543 -28.1133c-3.8418 -5.9502 -11.9668 -7.28223 -17.499 -2.9209c-8.5957 6.77637 -31.8145 22.5381 -61.708 22.5381c-48.3037 0 -77.916 -35.3301 -77.916 -80.082c0 -41.5889 26.8877 -83.6924 78.2764 -83.6924
|
||||
c32.6572 0 56.8428 19.0391 65.7266 27.2256c5.26953 4.85645 13.5957 4.03906 17.8193 -1.73828l19.8652 -27.1699c1.45996 -1.98145 2.32422 -4.42969 2.32422 -7.07715c0 -3.28809 -1.32422 -6.2793 -3.47656 -8.44043z" />
|
||||
<glyph glyph-name="closed-captioning" unicode=""
|
||||
d="M464 384c26.5 0 48 -21.5 48 -48v-288c0 -26.5 -21.5 -48 -48 -48h-416c-26.5 0 -48 21.5 -48 48v288c0 26.5 21.5 48 48 48h416zM458 48c3.2998 0 6 2.7002 6 6v276c0 3.2998 -2.7002 6 -6 6h-404c-3.2998 0 -6 -2.7002 -6 -6v-276c0 -3.2998 2.7002 -6 6 -6h404z
|
||||
M246.9 133.7c1.69922 -2.40039 1.5 -5.60059 -0.5 -7.7002c-53.6006 -56.7998 -172.801 -32.0996 -172.801 67.9004c0 97.2998 121.7 119.5 172.5 70.0996c2.10059 -2 2.5 -3.2002 1 -5.7002l-17.5 -30.5c-1.89941 -3.09961 -6.19922 -4 -9.09961 -1.7002
|
||||
@@ -398,7 +398,7 @@ c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12h-12v-24h88v12c
|
||||
h-32v-32h32zM96 136h224v12c0 6.62695 5.37305 12 12 12h12v160h-12c-6.62695 0 -12 5.37305 -12 12v12h-224v-12c0 -6.62695 -5.37305 -12 -12 -12h-12v-160h12c6.62695 0 12 -5.37305 12 -12v-12zM224 0v32h-32v-32h32zM504 64v160h-12c-6.62695 0 -12 5.37305 -12 12v12
|
||||
h-88v-88h12c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12h-72c-6.62695 0 -12 5.37305 -12 12v12h-88v-24h12c6.62695 0 12 -5.37305 12 -12v-12h224v12c0 6.62695 5.37305 12 12 12h12zM544 0v32h-32v-32h32zM544 256v32h-32v-32h32z" />
|
||||
<glyph glyph-name="sticky-note" unicode="" horiz-adv-x="448"
|
||||
d="M448 99.8936c0 -10.9746 -6.29883 -26.1797 -14.0586 -33.9404l-83.8828 -83.8818c-7.75977 -7.76074 -22.9658 -14.0596 -33.9404 -14.0596h-268.118c-26.5098 0 -48 21.4902 -48 48v351.988c0 26.5098 21.4902 48 48 48h352c26.5098 0 48 -21.4902 48 -48v-268.106z
|
||||
d="M448 99.8936c0 -13.2451 -5.37012 -25.252 -14.0586 -33.9404l-83.8828 -83.8818c-8.68848 -8.68848 -20.6943 -14.0596 -33.9404 -14.0596h-268.118c-26.5098 0 -48 21.4902 -48 48v351.988c0 26.5098 21.4902 48 48 48h352c26.5098 0 48 -21.4902 48 -48v-268.106z
|
||||
M320 19.8936l76.1182 76.1182h-76.1182v-76.1182zM400 368h-352v-351.988h224v104c0 13.2549 10.7451 24 24 24h104v223.988z" />
|
||||
<glyph glyph-name="clone" unicode=""
|
||||
d="M464 448c26.5098 0 48 -21.4902 48 -48v-320c0 -26.5098 -21.4902 -48 -48 -48h-48v-48c0 -26.5098 -21.4902 -48 -48 -48h-320c-26.5098 0 -48 21.4902 -48 48v320c0 26.5098 21.4902 48 48 48h48v48c0 26.5098 21.4902 48 48 48h320zM362 -16c3.31152 0 6 2.68848 6 6
|
||||
@@ -412,11 +412,11 @@ d="M408.864 368.948c48.8213 20.751 103.136 -15.0723 103.136 -67.9111v-114.443c0
|
||||
c-17.6729 0 -32 14.3271 -32 32c0 27.3301 1.1416 29.2012 -3.11035 32.9033l-97.71 85.0811c-24.8994 21.6797 -39.1797 52.8926 -39.1797 85.6338v56.9531c0 47.4277 44.8457 82.0215 91.0459 71.1807c1.96094 55.751 63.5107 87.8262 110.671 60.8057
|
||||
c29.1895 31.0713 78.8604 31.4473 108.334 -0.0214844c32.7051 18.6846 76.4121 10.3096 98.8135 -23.5879zM464 186.594v114.445c0 34.29 -52 33.8232 -52 0.676758c0 -8.83594 -7.16309 -16 -16 -16h-7c-8.83691 0 -16 7.16406 -16 16v26.751
|
||||
c0 34.457 -52 33.707 -52 0.676758v-27.4287c0 -8.83594 -7.16309 -16 -16 -16h-7c-8.83691 0 -16 7.16406 -16 16v40.4658c0 34.3525 -52 33.8115 -52 0.677734v-41.1436c0 -8.83594 -7.16406 -16 -16 -16h-7c-8.83594 0 -16 7.16406 -16 16v26.751
|
||||
c0 34.4023 -52 33.7744 -52 0.676758v-116.571c0 -8.83203 -7.16797 -16 -16 -16c-3.30664 0 -8.01367 1.7627 -10.5068 3.93359l-7 6.09473c-3.03223 2.64062 -5.49316 8.04688 -5.49316 12.0674v0v41.2275c0 34.2148 -52 33.8857 -52 0.677734v-56.9531
|
||||
c0 -18.8555 8.27441 -36.874 22.7002 -49.4365l97.71 -85.0801c12.4502 -10.8398 19.5898 -26.4463 19.5898 -42.8164v-10.2861h220v7.07617c0 13.21 2.65332 26.0791 7.88281 38.25l42.835 99.6553c2.91602 6.75391 5.28223 18.207 5.28223 25.5635v0.0488281z" />
|
||||
c0 34.4023 -52 33.7744 -52 0.676758v-116.571c0 -8.83105 -7.17773 -15.9961 -16.0078 -15.9961c-4.0166 0 -7.68848 1.48242 -10.499 3.92969l-7 6.09473c-3.37012 2.93457 -5.49316 7.25293 -5.49316 12.0674v41.2275c0 34.2148 -52 33.8857 -52 0.677734v-56.9531
|
||||
c0 -18.8555 8.27441 -36.874 22.7002 -49.4365l97.71 -85.0801c12.4502 -10.8398 19.5898 -26.4463 19.5898 -42.8164v-10.2861h220v7.07617c0 13.21 2.65332 26.0791 7.88281 38.25l42.835 99.6553c3.37891 7.82715 5.28223 16.501 5.28223 25.5625v0.0498047z" />
|
||||
<glyph glyph-name="hand-paper" unicode="" horiz-adv-x="448"
|
||||
d="M372.57 335.359c39.9062 5.63281 75.4297 -25.7393 75.4297 -66.3594v-131.564c-0.00195312 -12.7666 -2.33008 -33.2246 -5.19531 -45.666l-30.1836 -130.958c-3.34668 -14.5234 -16.2783 -24.8125 -31.1816 -24.8125h-222.897
|
||||
c-9.10352 0 -20.7793 6.01758 -26.0615 13.4316l-119.97 168.415c-21.2441 29.8203 -14.8047 71.3574 14.5498 93.1533c18.7754 13.9395 42.1309 16.2979 62.083 8.87109v126.13c0 44.0547 41.125 75.5439 82.4053 64.9834c23.8926 48.1963 92.3535 50.2471 117.982 0.74707
|
||||
d="M372.57 335.359c39.9062 5.63281 75.4297 -25.7393 75.4297 -66.3594v-131.564c-0.00292969 -15.7393 -1.80566 -30.9482 -5.19531 -45.666l-30.1836 -130.958c-3.34668 -14.5234 -16.2783 -24.8125 -31.1816 -24.8125h-222.897
|
||||
c-10.7539 0 -20.2588 5.28613 -26.0615 13.4316l-119.97 168.415c-21.2441 29.8203 -14.8047 71.3574 14.5498 93.1533c18.7754 13.9395 42.1309 16.2979 62.083 8.87109v126.13c0 44.0547 41.125 75.5439 82.4053 64.9834c23.8926 48.1963 92.3535 50.2471 117.982 0.74707
|
||||
c42.5186 11.1445 83.0391 -21.9346 83.0391 -65.5469v-10.8242zM399.997 137.437l-0.00195312 131.563c0 24.9492 -36.5703 25.5508 -36.5703 -0.691406v-76.3086c0 -8.83691 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16309 -16 16v154.184
|
||||
c0 25.501 -36.5703 26.3633 -36.5703 0.691406v-154.875c0 -8.83691 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16309 -16 16v188.309c0 25.501 -36.5703 26.3545 -36.5703 0.691406v-189c0 -8.83691 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16309 -16 16
|
||||
v153.309c0 25.501 -36.5713 26.3359 -36.5713 0.691406v-206.494c0 -15.5703 -20.0352 -21.9092 -29.0303 -9.2832l-27.1279 38.0791c-14.3711 20.1709 -43.833 -2.33496 -29.3945 -22.6045l115.196 -161.697h201.92l27.3252 118.551
|
||||
@@ -424,45 +424,46 @@ c2.63086 11.417 3.96484 23.1553 3.96484 34.8857z" />
|
||||
<glyph glyph-name="hand-scissors" unicode=""
|
||||
d="M256 -32c-44.9561 0 -77.3428 43.2627 -64.0244 85.8535c-21.6484 13.71 -34.0156 38.7617 -30.3408 65.0068h-87.6348c-40.8037 0 -74 32.8105 -74 73.1406c0 40.3291 33.1963 73.1396 74 73.1396l94 -9.14062l-78.8496 18.6787
|
||||
c-38.3076 14.7422 -57.04 57.4707 -41.9424 95.1123c15.0303 37.4736 57.7549 55.7803 95.6416 41.2012l144.929 -55.7568c24.9551 30.5566 57.8086 43.9932 92.2178 24.7324l97.999 -54.8525c20.9746 -11.7393 34.0049 -33.8457 34.0049 -57.6904v-205.702
|
||||
c0 -30.7422 -21.4404 -57.5576 -51.7979 -64.5537l-118.999 -27.4268c-4.97168 -1.14648 -10.0889 -1.72949 -15.2031 -1.72949zM256 16.0127l70 -0.000976562c1.23633 0 3.21777 0.225586 4.42285 0.501953l119.001 27.4277
|
||||
c8.58203 1.97754 14.5762 9.29102 14.5762 17.7812v205.701c0 6.4873 -3.62109 12.542 -9.44922 15.8047l-98 54.8545c-8.13965 4.55566 -18.668 2.61914 -24.4873 -4.50781l-21.7646 -26.6475c-2.65039 -3.24512 -8.20215 -5.87891 -12.3926 -5.87891
|
||||
c-1.64062 0 -4.21484 0.477539 -5.74609 1.06738l-166.549 64.0908c-32.6543 12.5664 -50.7744 -34.5771 -19.2227 -46.7168l155.357 -59.7852c5.66016 -2.17773 10.2539 -8.86816 10.2539 -14.9326v0v-11.6328c0 -8.83691 -7.16309 -16 -16 -16h-182
|
||||
c0 -30.7422 -21.4404 -57.5576 -51.7979 -64.5537l-118.999 -27.4268c-4.97168 -1.14648 -10.0889 -1.72949 -15.2031 -1.72949zM256 16.0127l70 -0.000976562c1.52441 0 2.99707 0.174805 4.42285 0.501953l119.001 27.4277
|
||||
c8.58203 1.97754 14.5762 9.29102 14.5762 17.7812v205.701c0 6.4873 -3.62109 12.542 -9.44922 15.8047l-98 54.8545c-8.13965 4.55566 -18.668 2.61914 -24.4873 -4.50781l-21.7646 -26.6475c-2.93457 -3.59375 -7.40332 -5.87305 -12.4004 -5.87305
|
||||
c-2.02246 0 -3.95703 0.375977 -5.73828 1.06152l-166.549 64.0908c-32.6543 12.5664 -50.7744 -34.5771 -19.2227 -46.7168l155.357 -59.7852c6 -2.30859 10.2539 -8.12402 10.2539 -14.9326v-11.6328c0 -8.83691 -7.16309 -16 -16 -16h-182
|
||||
c-34.375 0 -34.4297 -50.2803 0 -50.2803h182c8.83691 0 16 -7.16309 16 -16v-6.85645c0 -8.83691 -7.16309 -16 -16 -16h-28c-25.1221 0 -25.1592 -36.5674 0 -36.5674h28c8.83691 0 16 -7.16211 16 -16v-6.85547c0 -8.83691 -7.16309 -16 -16 -16
|
||||
c-25.1201 0 -25.1602 -36.5674 0 -36.5674z" />
|
||||
<glyph glyph-name="hand-lizard" unicode="" horiz-adv-x="576"
|
||||
d="M556.686 157.458c12.6357 -19.4863 19.3145 -42.0615 19.3145 -65.2871v-124.171h-224v71.582l-99.751 38.7871c-2.7832 1.08203 -5.70996 1.63086 -8.69727 1.63086h-131.552c-30.8789 0 -56 25.1211 -56 56c0 48.5234 39.4766 88 88 88h113.709l18.333 48h-196.042
|
||||
c-44.1123 0 -80 35.8877 -80 80v8c0 30.8779 25.1211 56 56 56h293.917c24.5 0 47.084 -12.2725 60.4111 -32.8291zM528 16v76.1709v0.0478516c0 11.7461 -5.19141 29.2734 -11.5879 39.124l-146.358 225.715c-4.44336 6.85254 -11.9707 10.9424 -20.1367 10.9424h-293.917
|
||||
c-4.41113 0 -8 -3.58887 -8 -8v-8c0 -17.6445 14.3555 -32 32 -32h213.471c25.2021 0 42.626 -25.293 33.6299 -48.8457l-24.5518 -64.2812c-7.05371 -18.4658 -25.0732 -30.873 -44.8398 -30.873h-113.709c-22.0557 0 -40 -17.9443 -40 -40c0 -4.41113 3.58887 -8 8 -8
|
||||
h131.552h0.0517578c7.44141 0 19.1074 -2.19238 26.041 -4.89355l99.752 -38.7881c18.5898 -7.22852 30.6035 -24.7881 30.6035 -44.7363v-23.582h128z" />
|
||||
c-44.1123 0 -80 35.8877 -80 80v8c0 30.8779 25.1211 56 56 56h293.917c24.5 0 47.084 -12.2725 60.4111 -32.8291zM528 16v76.1709c0 0.0166016 -0.0439453 0.106445 -0.0439453 0.12207c0 14.3945 -4.24219 27.8057 -11.5439 39.0498l-146.358 225.715
|
||||
c-4.44336 6.85254 -11.9707 10.9424 -20.1367 10.9424h-293.917c-4.41113 0 -8 -3.58887 -8 -8v-8c0 -17.6445 14.3555 -32 32 -32h213.471c25.2021 0 42.626 -25.293 33.6299 -48.8457l-24.5518 -64.2812c-7.05371 -18.4658 -25.0732 -30.873 -44.8398 -30.873h-113.709
|
||||
c-22.0557 0 -40 -17.9443 -40 -40c0 -4.41113 3.58887 -8 8 -8h131.552c0.0175781 0 0.0712891 -0.0273438 0.0888672 -0.0273438c9.16992 0 17.9404 -1.72461 26.0039 -4.86621l99.752 -38.7881c18.5898 -7.22852 30.6035 -24.7881 30.6035 -44.7363v-23.582h128z" />
|
||||
<glyph glyph-name="hand-spock" unicode=""
|
||||
d="M501.03 331.824c6.05762 -9.77832 10.9746 -27.0498 10.9746 -38.5518c0 -4.80664 -0.915039 -12.499 -2.04297 -17.1709l-57.623 -241.963c-12.748 -54.1729 -68.2627 -98.1387 -123.915 -98.1387h-0.345703h-107.455h-0.224609
|
||||
c-33.8135 0 -81.2148 18.834 -105.807 42.041l-91.3652 85.9766c-12.8213 12.0469 -23.2266 36.1016 -23.2266 53.6943c0 16.1299 8.97266 38.7529 20.0273 50.499c5.31836 5.66406 29.875 29.3926 68.1152 21.8477l-24.3594 82.1973
|
||||
c-1.68164 5.66406 -3.0459 15.0576 -3.0459 20.9668c0 37.5938 30.417 70.502 67.8955 73.4551c-0.204102 2.03125 -0.369141 5.33691 -0.369141 7.37891c0 31.627 24.8594 63.6895 55.4902 71.5684c43.248 10.9785 80.5645 -17.7012 89.6602 -53.0723l13.6836 -53.207
|
||||
l4.64648 22.6602c6.76074 32.417 39.123 58.8115 72.2373 58.916c8.73438 0 56.625 -3.26953 70.7383 -54.0801c15.0664 0.710938 46.9199 -3.50977 66.3105 -35.0176zM463.271 287.219c7.86914 32.9844 -42.1211 45.2695 -50.0859 11.9219l-24.8008 -104.146
|
||||
c-4.38867 -18.4141 -31.7783 -11.8926 -28.0557 6.2168l28.5479 139.166c7.39844 36.0703 -43.3076 45.0703 -50.1182 11.9629l-31.791 -154.971c-3.54883 -17.3086 -28.2832 -18.0469 -32.7109 -0.804688l-47.3262 184.035
|
||||
c-8.43359 32.8105 -58.3691 20.2676 -49.8652 -12.8359l42.4414 -165.039c4.81641 -18.7207 -23.3711 -26.9121 -28.9648 -8.00781l-31.3438 105.779c-9.6875 32.6465 -59.1191 18.2578 -49.3867 -14.625l36.0137 -121.539
|
||||
c5.61816 -18.9521 10.1777 -50.377 10.1777 -70.1436v-0.00878906c0 -6.54297 -8.05664 -10.9355 -13.4824 -5.82617l-51.123 48.1074c-24.7852 23.4082 -60.0527 -14.1875 -35.2793 -37.4902l91.3691 -85.9805c16.9629 -16.0068 49.6592 -28.998 72.9824 -28.998h0.154297
|
||||
h107.455h0.216797c34.7402 0 69.3936 27.4443 77.3525 61.2598z" />
|
||||
d="M501.03 331.824c6.92773 -11.1826 10.9697 -24.4053 10.9697 -38.5146c0 -5.92676 -0.706055 -11.6885 -2.03809 -17.208l-57.623 -241.963c-13.2236 -56.1904 -63.707 -98.1387 -123.908 -98.1387h-0.352539h-107.455
|
||||
c-0.0761719 0 -0.193359 0.00195312 -0.270508 0.00195312c-40.9248 0 -78.1475 15.9814 -105.761 42.0391l-91.3652 85.9766c-14.3076 13.4434 -23.2246 32.5547 -23.2246 53.7168c0 19.5254 7.61035 37.2861 20.0254 50.4766
|
||||
c5.31836 5.66406 29.875 29.3926 68.1152 21.8477l-24.3594 82.1973c-1.97363 6.64844 -2.97656 13.6836 -2.97656 20.9688c0 38.6953 29.8926 70.4639 67.8262 73.4531c-0.246094 2.45117 -0.34082 4.85547 -0.34082 7.37207c0 34.4199 23.585 63.376 55.4619 71.5752
|
||||
c43.248 10.9785 80.5645 -17.7012 89.6602 -53.0723l13.6836 -53.207l4.64648 22.6602c6.99023 33.5186 36.6826 58.8037 72.2373 58.916c8.73438 0 56.625 -3.26953 70.7383 -54.0801c15.0664 0.710938 46.9199 -3.50977 66.3105 -35.0176zM463.271 287.219
|
||||
c7.86914 32.9844 -42.1211 45.2695 -50.0859 11.9219l-24.8008 -104.146c-4.38867 -18.4141 -31.7783 -11.8926 -28.0557 6.2168l28.5479 139.166c7.39844 36.0703 -43.3076 45.0703 -50.1182 11.9629l-31.791 -154.971
|
||||
c-3.54883 -17.3086 -28.2832 -18.0469 -32.7109 -0.804688l-47.3262 184.035c-8.43359 32.8105 -58.3691 20.2676 -49.8652 -12.8359l42.4414 -165.039c4.81641 -18.7207 -23.3711 -26.9121 -28.9648 -8.00781l-31.3438 105.779
|
||||
c-9.6875 32.6465 -59.1191 18.2578 -49.3867 -14.625l36.0137 -121.539c6.59375 -22.2441 10.1777 -45.7803 10.1777 -70.1523c0 -6.54297 -8.05664 -10.9355 -13.4824 -5.82617l-51.123 48.1074c-24.7852 23.4082 -60.0527 -14.1875 -35.2793 -37.4902l91.3691 -85.9805
|
||||
c19.0469 -17.9736 44.75 -28.998 72.9795 -28.998h0.157227h107.455c0.0732422 0 0.138672 0.0429688 0.212891 0.0429688c37.5791 0 69.1016 26.1416 77.3564 61.2168z" />
|
||||
<glyph glyph-name="hand-pointer" unicode="" horiz-adv-x="448"
|
||||
d="M358.182 268.639c43.1934 16.6348 89.8184 -15.7949 89.8184 -62.6387v-84c-0.000976562 -4.25 -0.775391 -11.0615 -1.72754 -15.2041l-27.4297 -118.999c-6.98242 -30.2969 -33.7549 -51.7969 -64.5566 -51.7969h-178.286c-21.2588 0 -41.3682 10.4102 -53.791 27.8457
|
||||
l-109.699 154.001c-21.2432 29.8193 -14.8047 71.3574 14.5498 93.1523c18.8115 13.9658 42.1748 16.2822 62.083 8.87207v161.129c0 36.9443 29.7363 67 66.2861 67s66.2861 -30.0557 66.2861 -67v-73.6338c20.4131 2.85742 41.4678 -3.94238 56.5947 -19.6289
|
||||
c27.1934 12.8467 60.3799 5.66992 79.8721 -19.0986zM80.9854 168.303c-14.4004 20.2119 -43.8008 -2.38281 -29.3945 -22.6055l109.712 -154c3.43457 -4.81934 8.92871 -7.69727 14.6973 -7.69727h178.285c8.49219 0 15.8037 5.99414 17.7822 14.5762l27.4297 119.001
|
||||
c0.333008 1.44629 0.501953 2.93457 0.501953 4.42285v84c0 25.1602 -36.5713 25.1211 -36.5713 0c0 -8.83594 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16406 -16 16v21c0 25.1602 -36.5713 25.1201 -36.5713 0v-21c0 -8.83594 -7.16309 -16 -16 -16h-6.85938
|
||||
c-8.83691 0 -16 7.16406 -16 16v35c0 25.1602 -36.5703 25.1201 -36.5703 0v-35c0 -8.83594 -7.16309 -16 -16 -16h-6.85742c-8.83691 0 -16 7.16406 -16 16v175c0 25.1602 -36.5713 25.1201 -36.5713 0v-241.493c0 -15.5703 -20.0352 -21.9092 -29.0303 -9.2832z
|
||||
M176.143 48v96c0 8.83691 6.26855 16 14 16h6c7.73242 0 14 -7.16309 14 -16v-96c0 -8.83691 -6.26758 -16 -14 -16h-6c-7.73242 0 -14 7.16309 -14 16zM251.571 48v96c0 8.83691 6.26758 16 14 16h6c7.73145 0 14 -7.16309 14 -16v-96c0 -8.83691 -6.26855 -16 -14 -16h-6
|
||||
c-7.73242 0 -14 7.16309 -14 16zM327 48v96c0 8.83691 6.26758 16 14 16h6c7.73242 0 14 -7.16309 14 -16v-96c0 -8.83691 -6.26758 -16 -14 -16h-6c-7.73242 0 -14 7.16309 -14 16z" />
|
||||
d="M358.182 268.639c43.1934 16.6348 89.8184 -15.7949 89.8184 -62.6387v-84c-0.000976562 -5.24023 -0.600586 -10.3037 -1.72754 -15.2041l-27.4297 -118.999c-6.98242 -30.2969 -33.7549 -51.7969 -64.5566 -51.7969h-178.286
|
||||
c-21.2588 0 -41.3682 10.4102 -53.791 27.8457l-109.699 154.001c-21.2432 29.8193 -14.8047 71.3574 14.5498 93.1523c18.8115 13.9658 42.1748 16.2822 62.083 8.87207v161.129c0 36.9443 29.7363 67 66.2861 67s66.2861 -30.0557 66.2861 -67v-73.6338
|
||||
c20.4131 2.85742 41.4678 -3.94238 56.5947 -19.6289c27.1934 12.8467 60.3799 5.66992 79.8721 -19.0986zM80.9854 168.303c-14.4004 20.2119 -43.8008 -2.38281 -29.3945 -22.6055l109.712 -154c3.43457 -4.81934 8.92871 -7.69727 14.6973 -7.69727h178.285
|
||||
c8.49219 0 15.8037 5.99414 17.7822 14.5762l27.4297 119.001c0.333008 1.44629 0.501953 2.93457 0.501953 4.42285v84c0 25.1602 -36.5713 25.1211 -36.5713 0c0 -8.83594 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16406 -16 16v21
|
||||
c0 25.1602 -36.5713 25.1201 -36.5713 0v-21c0 -8.83594 -7.16309 -16 -16 -16h-6.85938c-8.83691 0 -16 7.16406 -16 16v35c0 25.1602 -36.5703 25.1201 -36.5703 0v-35c0 -8.83594 -7.16309 -16 -16 -16h-6.85742c-8.83691 0 -16 7.16406 -16 16v175
|
||||
c0 25.1602 -36.5713 25.1201 -36.5713 0v-241.493c0 -15.5703 -20.0352 -21.9092 -29.0303 -9.2832zM176.143 48v96c0 8.83691 6.26855 16 14 16h6c7.73242 0 14 -7.16309 14 -16v-96c0 -8.83691 -6.26758 -16 -14 -16h-6c-7.73242 0 -14 7.16309 -14 16zM251.571 48v96
|
||||
c0 8.83691 6.26758 16 14 16h6c7.73145 0 14 -7.16309 14 -16v-96c0 -8.83691 -6.26855 -16 -14 -16h-6c-7.73242 0 -14 7.16309 -14 16zM327 48v96c0 8.83691 6.26758 16 14 16h6c7.73242 0 14 -7.16309 14 -16v-96c0 -8.83691 -6.26758 -16 -14 -16h-6
|
||||
c-7.73242 0 -14 7.16309 -14 16z" />
|
||||
<glyph glyph-name="hand-peace" unicode="" horiz-adv-x="448"
|
||||
d="M362.146 256.024c42.5908 13.3184 85.8535 -19.0684 85.8535 -64.0244l-0.0117188 -70.001c-0.000976562 -4.25 -0.775391 -11.0615 -1.72949 -15.2031l-27.4268 -118.999c-6.99707 -30.3564 -33.8105 -51.7969 -64.5547 -51.7969h-205.702
|
||||
d="M362.146 256.024c42.5908 13.3184 85.8535 -19.0684 85.8535 -64.0244l-0.0117188 -70.001c-0.000976562 -5.24023 -0.600586 -10.3027 -1.72949 -15.2031l-27.4268 -118.999c-6.99707 -30.3564 -33.8105 -51.7969 -64.5547 -51.7969h-205.702
|
||||
c-23.8447 0 -45.9502 13.0303 -57.6904 34.0059l-54.8525 97.999c-19.2607 34.4092 -5.82422 67.2617 24.7324 92.2178l-55.7568 144.928c-14.5791 37.8867 3.72754 80.6113 41.2012 95.6416c37.6406 15.0977 80.3691 -3.63477 95.1123 -41.9424l18.6787 -78.8496
|
||||
l-9.14062 94c0 40.8037 32.8096 74 73.1396 74s73.1406 -33.1963 73.1406 -74v-87.6348c26.2451 3.6748 51.2959 -8.69238 65.0068 -30.3408zM399.987 122l-0.000976562 70c0 25.1602 -36.5674 25.1201 -36.5674 0c0 -8.83691 -7.16309 -16 -16 -16h-6.85547
|
||||
c-8.83789 0 -16 7.16309 -16 16v28c0 25.1592 -36.5674 25.1221 -36.5674 0v-28c0 -8.83691 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16309 -16 16v182c0 34.4297 -50.2803 34.375 -50.2803 0v-182c0 -8.83691 -7.16309 -16 -16 -16h-11.6328v0
|
||||
c-6.06445 0 -12.7549 4.59375 -14.9326 10.2539l-59.7842 155.357c-12.1396 31.5518 -59.2842 13.4326 -46.7168 -19.2227l64.0898 -166.549c0.589844 -1.53125 1.06738 -4.10547 1.06738 -5.74609c0 -4.19043 -2.63379 -9.74219 -5.87891 -12.3926l-26.6475 -21.7646
|
||||
c-8.83789 0 -16 7.16309 -16 16v28c0 25.1592 -36.5674 25.1221 -36.5674 0v-28c0 -8.83691 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16309 -16 16v182c0 34.4297 -50.2803 34.375 -50.2803 0v-182c0 -8.83691 -7.16309 -16 -16 -16h-11.6328
|
||||
c-6.80859 0 -12.624 4.25391 -14.9326 10.2539l-59.7842 155.357c-12.1396 31.5518 -59.2842 13.4326 -46.7168 -19.2227l64.0898 -166.549c0.685547 -1.78125 1.07812 -3.71875 1.07812 -5.74121c0 -4.99707 -2.2959 -9.46289 -5.88965 -12.3975l-26.6475 -21.7646
|
||||
c-7.12695 -5.81934 -9.06445 -16.3467 -4.50781 -24.4873l54.8535 -98c3.26367 -5.82812 9.31934 -9.44922 15.8057 -9.44922h205.701c8.49121 0 15.8037 5.99414 17.7812 14.5762l27.4277 119.001c0.333008 1.44629 0.501953 2.93457 0.501953 4.42285z" />
|
||||
<glyph glyph-name="registered" unicode=""
|
||||
d="M256 440c136.967 0 248 -111.033 248 -248s-111.033 -248 -248 -248s-248 111.033 -248 248s111.033 248 248 248zM256 -8c110.549 0 200 89.4678 200 200c0 110.549 -89.4678 200 -200 200c-110.549 0 -200 -89.4688 -200 -200c0 -110.549 89.4678 -200 200 -200z
|
||||
M366.442 73.791c4.40332 -7.99219 -1.37012 -17.791 -10.5107 -17.791h-42.8096h-0.0126953c-3.97559 0 -8.71582 2.84961 -10.5801 6.36035l-47.5156 89.3027h-31.958v-83.6631c0 -6.61719 -5.38281 -12 -12 -12h-38.5674c-6.61719 0 -12 5.38281 -12 12v248.304
|
||||
c0 6.61719 5.38281 12 12 12h78.667c71.251 0 101.498 -32.749 101.498 -85.252c0 -31.6123 -15.2148 -59.2969 -39.4824 -73.1758c3.02148 -4.61719 0.225586 0.199219 53.2715 -96.085zM256.933 208.094c20.9131 0 32.4307 11.5186 32.4316 32.4316
|
||||
c0 19.5752 -6.5127 31.709 -38.9297 31.709h-27.377v-64.1406h33.875z" />
|
||||
M366.442 73.791c4.40332 -7.99219 -1.37012 -17.791 -10.5107 -17.791h-42.8096c-0.00488281 0 -0.000976562 -0.0126953 -0.00585938 -0.0126953c-4.58594 0 -8.57422 2.58301 -10.5869 6.37305l-47.5156 89.3027h-31.958v-83.6631c0 -6.61719 -5.38281 -12 -12 -12
|
||||
h-38.5674c-6.61719 0 -12 5.38281 -12 12v248.304c0 6.61719 5.38281 12 12 12h78.667c71.251 0 101.498 -32.749 101.498 -85.252c0 -31.6123 -15.2148 -59.2969 -39.4824 -73.1758c3.02148 -4.61719 0.225586 0.199219 53.2715 -96.085zM256.933 208.094
|
||||
c20.9131 0 32.4307 11.5186 32.4316 32.4316c0 19.5752 -6.5127 31.709 -38.9297 31.709h-27.377v-64.1406h33.875z" />
|
||||
<glyph glyph-name="calendar-plus" unicode="" horiz-adv-x="448"
|
||||
d="M336 156v-24c0 -6.59961 -5.40039 -12 -12 -12h-76v-76c0 -6.59961 -5.40039 -12 -12 -12h-24c-6.59961 0 -12 5.40039 -12 12v76h-76c-6.59961 0 -12 5.40039 -12 12v24c0 6.59961 5.40039 12 12 12h76v76c0 6.59961 5.40039 12 12 12h24c6.59961 0 12 -5.40039 12 -12
|
||||
v-76h76c6.59961 0 12 -5.40039 12 -12zM448 336v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h48v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h128v52c0 6.59961 5.40039 12 12 12h40
|
||||
@@ -481,9 +482,9 @@ c6.62695 0 12 -5.37305 12 -12v-52h48zM394 -16c3.31152 0 6 2.68848 6 6v298h-352v-
|
||||
c-4.66699 4.70508 -4.6377 12.3027 0.0673828 16.9707l22.7197 22.5361c4.70508 4.66699 12.3027 4.63672 16.9697 -0.0693359l44.1035 -44.4609l111.072 110.182c4.70508 4.66699 12.3027 4.63672 16.9707 -0.0683594l22.5361 -22.7178
|
||||
c4.66699 -4.70508 4.63672 -12.3027 -0.0683594 -16.9697z" />
|
||||
<glyph glyph-name="map" unicode="" horiz-adv-x="576"
|
||||
d="M560.02 416c8.4502 0 15.9805 -6.83008 15.9805 -16.0195v-346.32c0 -11.9609 -9.01367 -25.2705 -20.1201 -29.71l-151.83 -52.8105c-5.32617 -1.7334 -14.1953 -3.13965 -19.7969 -3.13965c-5.7373 0 -14.8105 1.47363 -20.2529 3.29004l-172 60.71l-170.05 -62.8398
|
||||
c-1.99023 -0.790039 -4 -1.16016 -5.95996 -1.16016c-8.45996 0 -15.9902 6.83008 -15.9902 16.0195v346.32c0.00292969 11.959 9.0166 25.2686 20.1201 29.71l151.83 52.8105c6.43945 2.08984 13.1201 3.13965 19.8096 3.13965
|
||||
c5.73242 -0.00195312 14.8008 -1.47168 20.2402 -3.28027l172 -60.7197h0.00976562l170.05 62.8398c1.98047 0.790039 4 1.16016 5.95996 1.16016zM224 357.58v-285.97l128 -45.1904v285.97zM48 29.9502l127.36 47.0801l0.639648 0.229492v286.2l-128 -44.5303v-288.979z
|
||||
d="M560.02 416c8.4502 0 15.9805 -6.83008 15.9805 -16.0195v-346.32c0 -13.4707 -8.32422 -24.9951 -20.1201 -29.71l-151.83 -52.8105c-6.23242 -2.02832 -12.9023 -3.12305 -19.8076 -3.12305c-7.07324 0 -13.8799 1.15039 -20.2422 3.27344l-172 60.71l-170.05 -62.8398
|
||||
c-1.99023 -0.790039 -4 -1.16016 -5.95996 -1.16016c-8.45996 0 -15.9902 6.83008 -15.9902 16.0195v346.32c0.00292969 13.4697 8.32617 24.9932 20.1201 29.71l151.83 52.8105c6.43945 2.08984 13.1201 3.13965 19.8096 3.13965
|
||||
c7.06641 -0.00292969 13.8789 -1.16602 20.2402 -3.28027l172 -60.7197h0.00976562l170.05 62.8398c1.98047 0.790039 4 1.16016 5.95996 1.16016zM224 357.58v-285.97l128 -45.1904v285.97zM48 29.9502l127.36 47.0801l0.639648 0.229492v286.2l-128 -44.5303v-288.979z
|
||||
M528 65.0801v288.97l-127.36 -47.0693l-0.639648 -0.240234v-286.19z" />
|
||||
<glyph glyph-name="comment-alt" unicode=""
|
||||
d="M448 448c35.2998 0 64 -28.7002 64 -64v-288c0 -35.2998 -28.7002 -64 -64 -64h-144l-124.9 -93.5996c-2.19922 -1.7002 -4.69922 -2.40039 -7.09961 -2.40039c-6.2002 0 -12 4.90039 -12 12v84h-96c-35.2998 0 -64 28.7002 -64 64v288c0 35.2998 28.7002 64 64 64h384z
|
||||
@@ -497,16 +498,16 @@ c-8.7998 0 -16 7.2002 -16 16v160c0 8.7998 7.2002 16 16 16h160c8.7998 0 16 -7.200
|
||||
<glyph glyph-name="handshake" unicode="" horiz-adv-x="640"
|
||||
d="M519.2 320.1h120.8v-255.699h-64c-17.5 0 -31.7998 14.1992 -31.9004 31.6992h-57.8994c-1.7998 -8.19922 -5.2998 -16.0996 -10.9004 -23l-26.2002 -32.2998c-15.7998 -19.3994 -41.8994 -25.5 -64 -16.7998c-13.5 -16.5996 -30.5996 -24 -48.7998 -24
|
||||
c-15.0996 0 -28.5996 5.09961 -41.0996 15.9004c-31.7998 -21.9004 -74.7002 -21.3008 -105.601 3.7998l-84.5996 76.3994h-9.09961c-0.100586 -17.5 -14.3008 -31.6992 -31.9004 -31.6992h-64v255.699h118l47.5996 47.6006c10.5 10.3994 24.8008 16.2998 39.6006 16.2998
|
||||
h226.8v0c12.7812 0 30.5225 -7.30273 39.5996 -16.2998zM48 96.4004c8.7998 0 16 7.09961 16 16c0 8.7998 -7.2002 16 -16 16s-16 -7.2002 -16 -16c0 -8.80078 7.2002 -16 16 -16zM438 103.3c2.7002 3.40039 2.2002 8.5 -1.2002 11.2998l-108.2 87.8008l-8.19922 -7.5
|
||||
h226.8c15.4326 0 29.4326 -6.22168 39.5996 -16.2998zM48 96.4004c8.7998 0 16 7.09961 16 16c0 8.7998 -7.2002 16 -16 16s-16 -7.2002 -16 -16c0 -8.80078 7.2002 -16 16 -16zM438 103.3c2.7002 3.40039 2.2002 8.5 -1.2002 11.2998l-108.2 87.8008l-8.19922 -7.5
|
||||
c-40.3008 -36.8008 -86.7002 -11.8008 -101.5 4.39941c-26.7002 29 -25 74.4004 4.39941 101.3l38.7002 35.5h-56.7002c-2 -0.799805 -3.7002 -1.5 -5.7002 -2.2998l-61.6992 -61.5996h-41.9004v-128.101h27.7002l97.2998 -88
|
||||
c16.0996 -13.0996 41.4004 -10.5 55.2998 6.60059l15.6006 19.2002l36.7998 -31.5c3 -2.40039 12 -4.90039 18 2.39941l30 36.5l23.8994 -19.3994c3.5 -2.80078 8.5 -2.2002 11.3008 1.19922zM544 144.1v128h-44.7002l-61.7002 61.6006
|
||||
c-1.39941 1.5 -3.39941 2.2998 -5.5 2.2998l-83.6992 -0.200195c-10 0 -19.6006 -3.7002 -27 -10.5l-65.6006 -60.0996c-9.7002 -8.7998 -10.5 -24 -1.2002 -33.9004c8.90039 -9.39941 25.1006 -8.7002 34.6006 0l55.2002 50.6006c6.5 5.89941 16.5996 5.5 22.5996 -1
|
||||
l10.9004 -11.7002c6 -6.5 5.5 -16.6006 -1 -22.6006l-12.5 -11.3994l102.699 -83.4004c2.80078 -2.2998 5.40039 -4.89941 7.7002 -7.7002h69.2002zM592 96.4004c8.7998 0 16 7.09961 16 16c0 8.7998 -7.2002 16 -16 16s-16 -7.2002 -16 -16c0 -8.80078 7.2002 -16 16 -16z
|
||||
" />
|
||||
<glyph glyph-name="envelope-open" unicode=""
|
||||
d="M494.586 283.484c9.6123 -7.94824 17.4141 -24.5205 17.4141 -36.9932v-262.491c0 -26.5098 -21.4902 -48 -48 -48h-416c-26.5098 0 -48 21.4902 -48 48v262.515c0 12.5166 7.84668 29.1279 17.5146 37.0771c4.08008 3.35449 110.688 89.0996 135.15 108.549
|
||||
c22.6992 18.1426 60.1299 55.8594 103.335 55.8594c43.4365 0 81.2314 -38.1914 103.335 -55.8594c23.5283 -18.707 130.554 -104.773 135.251 -108.656zM464 -10v253.632v0.00488281c0 1.5791 -0.996094 3.66602 -2.22363 4.6582
|
||||
c-15.8633 12.8232 -108.793 87.5752 -132.366 106.316c-17.5527 14.0195 -49.7168 45.3887 -73.4102 45.3887c-23.6016 0 -55.2451 -30.8799 -73.4102 -45.3887c-23.5713 -18.7393 -116.494 -93.4795 -132.364 -106.293
|
||||
d="M494.586 283.484c10.6523 -8.80762 17.4141 -22.1064 17.4141 -36.9932v-262.491c0 -26.5098 -21.4902 -48 -48 -48h-416c-26.5098 0 -48 21.4902 -48 48v262.515c0 14.9355 6.80469 28.2705 17.5146 37.0771c4.08008 3.35449 110.688 89.0996 135.15 108.549
|
||||
c22.6992 18.1426 60.1299 55.8594 103.335 55.8594c43.4365 0 81.2314 -38.1914 103.335 -55.8594c23.5283 -18.707 130.554 -104.773 135.251 -108.656zM464 -10v253.632c0 0.00195312 0.00390625 0.000976562 0.00390625 0.00292969
|
||||
c0 1.88184 -0.869141 3.56152 -2.22754 4.66016c-15.8633 12.8232 -108.793 87.5752 -132.366 106.316c-17.5527 14.0195 -49.7168 45.3887 -73.4102 45.3887c-23.6016 0 -55.2451 -30.8799 -73.4102 -45.3887c-23.5713 -18.7393 -116.494 -93.4795 -132.364 -106.293
|
||||
c-1.40918 -1.13965 -2.22559 -2.85254 -2.22559 -4.66504v-253.653c0 -3.31152 2.68848 -6 6 -6h404c3.31152 0 6 2.68848 6 6zM432.009 177.704c4.24902 -5.15918 3.46484 -12.7949 -1.74512 -16.9814c-28.9746 -23.2822 -59.2734 -47.5967 -70.9287 -56.8623
|
||||
c-22.6992 -18.1436 -60.1299 -55.8604 -103.335 -55.8604c-43.4521 0 -81.2871 38.2373 -103.335 55.8604c-11.2793 8.9668 -41.7441 33.4131 -70.9268 56.8643c-5.20996 4.1875 -5.99316 11.8223 -1.74512 16.9814l15.2578 18.5283
|
||||
c4.17773 5.07227 11.6572 5.84277 16.7793 1.72559c28.6182 -23.001 58.5654 -47.0352 70.5596 -56.5713c17.5527 -14.0195 49.7168 -45.3887 73.4102 -45.3887c23.6016 0 55.2461 30.8799 73.4102 45.3887c11.9941 9.53516 41.9434 33.5703 70.5625 56.5684
|
||||
@@ -555,10 +556,11 @@ c6.09961 -6.2002 6.09961 -16.4004 0 -22.6006l-58.2998 -59.2998v-84.5l71.8994 42.
|
||||
c7.5 4.39941 17.2002 1.7998 21.5 -5.90039l7.90039 -13.9004c4.2998 -7.69922 1.7002 -17.5 -5.7998 -21.8994l-39.2002 -23l34.0996 -9.2998c8.40039 -2.30078 13.3008 -11.1006 11.1006 -19.6006l-4.10059 -15.5c-2.2998 -8.5 -10.8994 -13.5996 -19.2998 -11.2998
|
||||
l-79.7002 21.7002l-71.8994 -42.2002l71.7998 -42.2002l79.7002 21.7002c8.39941 2.2998 17.0996 -2.7998 19.2998 -11.2998l4.09961 -15.5c2.30078 -8.5 -2.69922 -17.2998 -11.0996 -19.6006l-34.0996 -9.2998z" />
|
||||
<glyph glyph-name="trash-alt" unicode="" horiz-adv-x="448"
|
||||
d="M268 32c-6.62402 0 -12 5.37598 -12 12v216c0 6.62402 5.37598 12 12 12h24c6.62402 0 12 -5.37598 12 -12v-216c0 -6.62402 -5.37598 -12 -12 -12h-24zM432 368c8.83203 0 16 -7.16797 16 -16v-16c0 -8.83203 -7.16797 -16 -16 -16h-16v-336
|
||||
c0 -26.4961 -21.5039 -48 -48 -48h-288c-26.4961 0 -48 21.5039 -48 48v336h-16c-8.83203 0 -16 7.16797 -16 16v16c0 8.83203 7.16797 16 16 16h82.4102l34.0195 56.7002c7.71875 12.8613 26.1572 23.2998 41.1572 23.2998h0.00292969h100.82h0.0224609
|
||||
c15 0 33.4385 -10.4385 41.1572 -23.2998l34 -56.7002h82.4102zM171.84 397.09l-17.4502 -29.0898h139.221l-17.46 29.0898c-0.96582 1.60645 -3.26953 2.91016 -5.14355 2.91016h-0.00683594h-94h-0.0166016c-1.87402 0 -4.17871 -1.30371 -5.14355 -2.91016zM368 -16v336
|
||||
h-288v-336h288zM156 32c-6.62402 0 -12 5.37598 -12 12v216c0 6.62402 5.37598 12 12 12h24c6.62402 0 12 -5.37598 12 -12v-216c0 -6.62402 -5.37598 -12 -12 -12h-24z" />
|
||||
d="M268 32c-6.62305 0 -12 5.37695 -12 12v216c0 6.62305 5.37695 12 12 12h24c6.62305 0 12 -5.37695 12 -12v-216c0 -6.62305 -5.37695 -12 -12 -12h-24zM432 368c8.83105 0 16 -7.16895 16 -16v-16c0 -8.83105 -7.16895 -16 -16 -16h-16v-336
|
||||
c0 -26.4922 -21.5078 -48 -48 -48h-288c-26.4922 0 -48 21.5078 -48 48v336h-16c-8.83105 0 -16 7.16895 -16 16v16c0 8.83105 7.16895 16 16 16h82.4102l34.0195 56.7002c8.39258 13.9844 23.6777 23.2998 41.1602 23.2998h100.82
|
||||
c0.0078125 0 -0.015625 0.0517578 -0.0078125 0.0517578c17.4824 0 32.7949 -9.36719 41.1875 -23.3516l34 -56.7002h82.4102zM171.84 397.09l-17.4502 -29.0898h139.221l-17.46 29.0898c-1.0498 1.74707 -2.95898 2.91016 -5.14355 2.91016h-0.00683594h-94
|
||||
c-0.00585938 0 -0.00683594 0.00683594 -0.0126953 0.00683594c-2.18457 0 -4.09766 -1.16992 -5.14746 -2.91699zM368 -16v336h-288v-336h288zM156 32c-6.62305 0 -12 5.37695 -12 12v216c0 6.62305 5.37695 12 12 12h24c6.62305 0 12 -5.37695 12 -12v-216
|
||||
c0 -6.62305 -5.37695 -12 -12 -12h-24z" />
|
||||
<glyph glyph-name="images" unicode="" horiz-adv-x="576"
|
||||
d="M480 32v-16c0 -26.5098 -21.4902 -48 -48 -48h-384c-26.5098 0 -48 21.4902 -48 48v256c0 26.5098 21.4902 48 48 48h16v-48h-10c-3.31152 0 -6 -2.68848 -6 -6v-244c0 -3.31152 2.68848 -6 6 -6h372c3.31152 0 6 2.68848 6 6v10h48zM522 368h-372
|
||||
c-3.31152 0 -6 -2.68848 -6 -6v-244c0 -3.31152 2.68848 -6 6 -6h372c3.31152 0 6 2.68848 6 6v244c0 3.31152 -2.68848 6 -6 6zM528 416c26.5098 0 48 -21.4902 48 -48v-256c0 -26.5098 -21.4902 -48 -48 -48h-384c-26.5098 0 -48 21.4902 -48 48v256
|
||||
@@ -584,9 +586,9 @@ d="M464 448c4.09961 0 7.7998 -2 10.0996 -5.40039l99.9004 -147.199c2.90039 -4.400
|
||||
c2.2002 3.40039 6 5.40039 10 5.40039h352zM444.7 400h-56.7998l51.6992 -96h68.4004zM242.6 400l-51.5996 -96h194l-51.7002 96h-90.7002zM131.3 400l-63.2998 -96h68.4004l51.6992 96h-56.7998zM88.2998 256l119.7 -160l-68.2998 160h-51.4004zM191.2 256l96.7998 -243.3
|
||||
l96.7998 243.3h-193.6zM368 96l119.6 160h-51.3994z" />
|
||||
<glyph glyph-name="money-bill-alt" unicode="" horiz-adv-x="640"
|
||||
d="M320 304c53.0195 0 96 -50.1396 96 -112c0 -61.8701 -43 -112 -96 -112c-53.0195 0 -96 50.1504 -96 112c0 61.8604 42.9805 112 96 112zM360 136v16c0 4.41992 -3.58008 8 -8 8h-16v88c0 4.41992 -3.58008 8 -8 8h-13.5801h-0.000976562
|
||||
c-4.01074 0 -9.97266 -1.80566 -13.3086 -4.03027l-15.3301 -10.2197c-1.96777 -1.30957 -3.56445 -4.29004 -3.56445 -6.65332c0 -1.33691 0.601562 -3.32422 1.34375 -4.43652l8.88086 -13.3105c1.30859 -1.9668 4.29004 -3.56445 6.65332 -3.56445
|
||||
c1.33691 0 3.32422 0.602539 4.43652 1.34473l0.469727 0.310547v-55.4404h-16c-4.41992 0 -8 -3.58008 -8 -8v-16c0 -4.41992 3.58008 -8 8 -8h64c4.41992 0 8 3.58008 8 8zM608 384c17.6699 0 32 -14.3301 32 -32v-320c0 -17.6699 -14.3301 -32 -32 -32h-576
|
||||
d="M320 304c53.0195 0 96 -50.1396 96 -112c0 -61.8701 -43 -112 -96 -112c-53.0195 0 -96 50.1504 -96 112c0 61.8604 42.9805 112 96 112zM360 136v16c0 4.41992 -3.58008 8 -8 8h-16v88c0 4.41992 -3.58008 8 -8 8h-13.5801
|
||||
c-4.91113 0 -9.50586 -1.49316 -13.3096 -4.03027l-15.3301 -10.2197c-2.15332 -1.43262 -3.55957 -3.88379 -3.55957 -6.66113c0 -1.6377 0.493164 -3.16113 1.33887 -4.42871l8.88086 -13.3105c1.43164 -2.15234 3.88379 -3.55957 6.66113 -3.55957
|
||||
c1.6377 0 3.16016 0.494141 4.42871 1.33984l0.469727 0.310547v-55.4404h-16c-4.41992 0 -8 -3.58008 -8 -8v-16c0 -4.41992 3.58008 -8 8 -8h64c4.41992 0 8 3.58008 8 8zM608 384c17.6699 0 32 -14.3301 32 -32v-320c0 -17.6699 -14.3301 -32 -32 -32h-576
|
||||
c-17.6699 0 -32 14.3301 -32 32v320c0 17.6699 14.3301 32 32 32h576zM592 112v160c-35.3496 0 -64 28.6504 -64 64h-416c0 -35.3496 -28.6504 -64 -64 -64v-160c35.3496 0 64 -28.6504 64 -64h416c0 35.3496 28.6504 64 64 64z" />
|
||||
<glyph glyph-name="window-close" unicode=""
|
||||
d="M464 416c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-416c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h416zM464 22v340c0 3.2998 -2.7002 6 -6 6h-404c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h404c3.2998 0 6 2.7002 6 6z
|
||||
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 876 KiB After Width: | Height: | Size: 893 KiB |
BIN
dist/qmi-cloud/fa-solid-900.c500da19d776384ba695.woff2
vendored
Normal file
BIN
dist/qmi-cloud/fa-solid-900.ee09ad7553b8ad3d8115.woff
vendored
Normal file
BIN
dist/qmi-cloud/favicon.ico
vendored
|
Before Width: | Height: | Size: 5.3 KiB |
26
dist/qmi-cloud/index.html
vendored
@@ -5,9 +5,29 @@
|
||||
<title>QMI Cloud</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" href="assets/favicon.ico">
|
||||
<link rel="stylesheet" href="styles.921aafa95031aeb74181.css"></head>
|
||||
<link rel="icon" href="assets/favicon.svg">
|
||||
<!-- Load environment variables -->
|
||||
<script src="env.js"></script>
|
||||
|
||||
<!--
|
||||
<script crossorigin="anonymous"
|
||||
type="application/javascript"
|
||||
src="https://cdn.jsdelivr.net/npm/@qlik/embed-web-components"
|
||||
data-host="https://qmicloud.qliktech.com/qcsproxy"></script>
|
||||
-->
|
||||
|
||||
<script crossorigin="anonymous"
|
||||
type="application/javascript"
|
||||
src="https://cdn.jsdelivr.net/npm/@qlik/embed-web-components"
|
||||
data-host="https://innovation.us.qlikcloud.com"
|
||||
data-auth-type="Oauth2"
|
||||
data-client-id="21be5044bba1072c16a803a3e6e4dca0"
|
||||
data-redirect-uri="https://qmicloud-dev.qliktech.com/oauth-callback.html"
|
||||
data-access-token-storage="session"
|
||||
data-auto-redirect="true"></script>
|
||||
|
||||
<link rel="stylesheet" href="styles.3b2b6672156f20378f8f.css"></head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
<script src="runtime.689ba4fd6cadb82c1ac2.js" defer></script><script src="polyfills-es5.feb8e3dfdd8e1cace860.js" nomodule defer></script><script src="polyfills.60117177d3b4f4827ace.js" defer></script><script src="scripts.73c34722d75b092f2620.js" defer></script><script src="main.eca58c33a1ad840ee769.js" defer></script></body>
|
||||
<script src="runtime.c51bd5b1c616d9ffddc1.js" defer></script><script src="polyfills-es5.6fef7e679f78bcc42760.js" nomodule defer></script><script src="polyfills.51f5cc3d1309de3a873d.js" defer></script><script src="scripts.1af868998801499c8755.js" defer></script><script src="main.0eb476bbc192df0d888a.js" defer></script></body>
|
||||
</html>
|
||||
|
||||
1
dist/qmi-cloud/main.0eb476bbc192df0d888a.js
vendored
Normal file
1
dist/qmi-cloud/main.eca58c33a1ad840ee769.js
vendored
12
dist/qmi-cloud/oauth-callback.html
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script
|
||||
crossorigin="anonymous"
|
||||
type="application/javascript"
|
||||
data-host="https://innovation.us.qlikcloud.com"
|
||||
src="https://cdn.jsdelivr.net/npm/@qlik/embed-web-components@0/dist/oauth-callback.js"
|
||||
></script>
|
||||
</head>
|
||||
</html>
|
||||
1
dist/qmi-cloud/scripts.1af868998801499c8755.js
vendored
Normal file
80
dist/qmi-cloud/styles.3b2b6672156f20378f8f.css
vendored
Normal file
80
dist/qmi-cloud/styles.921aafa95031aeb74181.css
vendored
@@ -104,7 +104,7 @@ services:
|
||||
- GIT_TAG=dev
|
||||
- SSHPATH=/Users/aor/.ssh
|
||||
- DOCKERIMAGE_AZURE_POWERSHELL=mcr.microsoft.com/azure-powershell:4.2.0-ubuntu-18.04
|
||||
- DOCKERIMAGE_TERRAFORM=qlikgear/terraform:1.0.1
|
||||
- DOCKERIMAGE_TERRAFORM=qlikgear/terraform:1.3.5
|
||||
command: "sh -c 'npm run start:dev'"
|
||||
volumes:
|
||||
# -- Dev only volumes
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
|
||||
|
||||
<link rel="shortcut icon" href="favicon.ico" />
|
||||
<link rel="shortcut icon" href="favicon.svg" />
|
||||
<!-- <script type="text/javascript" src="https://qdt-apps.qlik.com/qdt-components/v3/3.0.0/qdt-components.js"></script> -->
|
||||
<script type="text/javascript" src="qdt-components.js"></script>
|
||||
<script type="text/javascript" src="index.js"></script>
|
||||
|
||||
15490
package-lock.json
generated
10
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "qmi-cloud-app",
|
||||
"version": "1.3.1",
|
||||
"version": "5.0.1",
|
||||
"scripts": {
|
||||
"start": "node -r esm server/server.js",
|
||||
"start:dev": "nodemon -r esm server/server.js",
|
||||
@@ -33,6 +33,7 @@
|
||||
"bull-arena": "^2.6.4",
|
||||
"chart.js": "^2.9.3",
|
||||
"connect-mongo": "^3.0.0",
|
||||
"cookie": "^0.6.0",
|
||||
"cookie-parser": "^1.4.4",
|
||||
"core-js": "^2.5.4",
|
||||
"esm": "^3.2.25",
|
||||
@@ -41,20 +42,23 @@
|
||||
"font-awesome": "^4.7.0",
|
||||
"fs-extra": "^8.1.0",
|
||||
"hammerjs": "^2.0.8",
|
||||
"js-sha1": "^0.6.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"leonardo-ui": "^1.7.1",
|
||||
"moment": "^2.24.0",
|
||||
"moment-timezone": "^0.5.31",
|
||||
"mongoose": "^5.7.4",
|
||||
"mongoose": "^6.11.1",
|
||||
"ngx-markdown": "^9.0.0",
|
||||
"nodemon": "^1.19.1",
|
||||
"passport": "^0.4.0",
|
||||
"passport-azure-ad": "^4.1.0",
|
||||
"passport-openidconnect": "^0.1.2",
|
||||
"qdt-components": "^2.5.5",
|
||||
"qmi-cloud-common": "./qmi-cloud-common",
|
||||
"rxjs": "~6.5.4",
|
||||
"swagger-jsdoc": "3.5.0",
|
||||
"swagger-ui-express": "4.1.3",
|
||||
"uuid": "^9.0.1",
|
||||
"ws": "^8.14.2",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
9
proxy.conf.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"/api/*": {
|
||||
"target": "http://localhost:3000/api",
|
||||
"secure": false,
|
||||
"logLevel": "debug",
|
||||
"changeOrigin": true,
|
||||
"pathRewrite": { "^/api": "" }
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,12 @@
|
||||
"isWafPolicyAppGw": true,
|
||||
"isExternal": true,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Qlik Sense",
|
||||
"vmTypeDefault": "Standard_D4s_v3",
|
||||
"index": "vm1",
|
||||
"versions": [
|
||||
"values": [
|
||||
{
|
||||
"name": "Qlik Sense February 2020",
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/images/qliksense-base-feb20-2"
|
||||
@@ -35,12 +35,12 @@
|
||||
"isWafPolicyAppGw": true,
|
||||
"isExternal": true,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Qlik Sense",
|
||||
"vmTypeDefault": "Standard_D4s_v3",
|
||||
"index": "vm1",
|
||||
"versions": [
|
||||
"values": [
|
||||
{
|
||||
"name": "Qlik Sense February 2020",
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/images/qliksense-base-feb20-2"
|
||||
@@ -55,7 +55,7 @@
|
||||
"product": "QDC",
|
||||
"vmTypeDefault": "Standard_D4s_v3",
|
||||
"index": "vm2",
|
||||
"versions": [
|
||||
"values": [
|
||||
{
|
||||
"name": "QDC Single Server December 2019",
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/images/qdc-base-4.4-2"
|
||||
@@ -81,12 +81,12 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Oracle",
|
||||
"vmTypeDefault": "Standard_D4s_v3",
|
||||
"index": "vm1",
|
||||
"versions": []
|
||||
"values": []
|
||||
}
|
||||
],
|
||||
"name": "azqmi-database-lkn",
|
||||
@@ -102,12 +102,12 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "packer",
|
||||
"vmTypeDefault": "Standard_D8s_v3",
|
||||
"index": "vm1",
|
||||
"versions": []
|
||||
"values": []
|
||||
}
|
||||
],
|
||||
"name": "azqmi-box-builder",
|
||||
@@ -123,12 +123,12 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": true,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "QDC",
|
||||
"vmTypeDefault": "Standard_D4s_v3",
|
||||
"index": "vm1",
|
||||
"versions": [
|
||||
"values": [
|
||||
{
|
||||
"name": "QDC Single Server December 2019",
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/images/qdc-base-4.4-2"
|
||||
@@ -154,13 +154,13 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Windows",
|
||||
"vmTypeDefault": "Standard_D2s_v3",
|
||||
"diskSizeGbDefault": 128,
|
||||
"index": "vm1",
|
||||
"versions": []
|
||||
"values": []
|
||||
}
|
||||
],
|
||||
"vmTypeDefault": "Standard_D2s_v3",
|
||||
@@ -177,12 +177,12 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Windows",
|
||||
"vmTypeDefault": "Standard_D2s_v3",
|
||||
"index": "vm1",
|
||||
"versions": []
|
||||
"values": []
|
||||
}
|
||||
],
|
||||
"vmTypeDefault": "Standard_D2s_v3",
|
||||
@@ -198,13 +198,13 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Ubuntu 18.04",
|
||||
"vmTypeDefault": "Standard_B2s",
|
||||
"diskSizeGbDefault": 128,
|
||||
"index": "vm1",
|
||||
"versions": []
|
||||
"values": []
|
||||
}
|
||||
],
|
||||
"vmTypeDefault": "Standard_B2s",
|
||||
@@ -221,13 +221,13 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Centos 7.5",
|
||||
"vmTypeDefault": "Standard_B2s",
|
||||
"diskSizeGbDefault": 128,
|
||||
"index": "vm1",
|
||||
"versions": []
|
||||
"values": []
|
||||
}
|
||||
],
|
||||
"vmTypeDefault": "Standard_B2s",
|
||||
@@ -244,13 +244,13 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Centos 7.5",
|
||||
"vmTypeDefault": "Standard_D4s_v3",
|
||||
"diskSizeGbDefault": 128,
|
||||
"index": "vm1",
|
||||
"versions": []
|
||||
"values": []
|
||||
}
|
||||
],
|
||||
"vmTypeDefault": "Standard_D4s_v3",
|
||||
@@ -268,13 +268,13 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Windows",
|
||||
"vmTypeDefault": "Standard_D8s_v3",
|
||||
"diskSizeGbDefault": 128,
|
||||
"index": "vm1",
|
||||
"versions": []
|
||||
"values": []
|
||||
}
|
||||
],
|
||||
"vmTypeDefault": "Standard_D8s_v3",
|
||||
@@ -292,12 +292,12 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "QlikView",
|
||||
"vmTypeDefault": "Standard_D4s_v3",
|
||||
"index": "vm1",
|
||||
"versions": []
|
||||
"values": []
|
||||
}
|
||||
],
|
||||
"vmTypeDefault": "Standard_D4s_v3",
|
||||
@@ -314,13 +314,13 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "each node",
|
||||
"vmTypeDefault": "Standard_D8s_v3",
|
||||
"diskSizeGbDefault": 250,
|
||||
"index": "vm1",
|
||||
"versions": []
|
||||
"values": []
|
||||
}
|
||||
],
|
||||
"vmTypeDefault": "Standard_D8s_v3",
|
||||
@@ -337,12 +337,12 @@
|
||||
"isWafPolicyAppGw": true,
|
||||
"isExternal": true,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "QIB",
|
||||
"vmTypeDefault": "Standard_D8s_v3",
|
||||
"index": "vm1",
|
||||
"versions": [
|
||||
"values": [
|
||||
{
|
||||
"name": "Qlik Sense Feb 2020 and QIB Feb 2020",
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/images/qliksense-qib-base-feb20-1"
|
||||
@@ -363,12 +363,12 @@
|
||||
"isWafPolicyAppGw": true,
|
||||
"isExternal": true,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Windows VM",
|
||||
"vmTypeDefault": "Standard_D8s_v3",
|
||||
"index": "vm1",
|
||||
"versions": []
|
||||
"values": []
|
||||
}
|
||||
],
|
||||
"vmTypeDefault": "Standard_D8s_v3",
|
||||
@@ -384,13 +384,13 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Ubuntu 18.04",
|
||||
"vmTypeDefault": "Standard_D4s_v3",
|
||||
"diskSizeGbDefault": 128,
|
||||
"index": "vm1",
|
||||
"versions": []
|
||||
"values": []
|
||||
}
|
||||
],
|
||||
"vmTypeDefault": "Standard_D4s_v3",
|
||||
@@ -406,12 +406,12 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Attunity Replicate/Compose DW",
|
||||
"vmTypeDefault": "Standard_D2s_v3",
|
||||
"index": "vm1",
|
||||
"versions": []
|
||||
"values": []
|
||||
}
|
||||
],
|
||||
"vmTypeDefault": "Standard_D2s_v3",
|
||||
|
||||
@@ -4,16 +4,16 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Qlik Sense",
|
||||
"vmTypeDefault": "Standard_D4s_v3",
|
||||
"diskSizeGbDefault": "500",
|
||||
"index": "vm1",
|
||||
"versions": [
|
||||
"values": [
|
||||
{
|
||||
"name": "Qlik Sense April 2020",
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/galleries/QMICloud/images/QlikSenseEnterprise/versions/13.72.3"
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/galleries/QMICloud/images/QlikSenseEnterprise/values/13.72.3"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -22,10 +22,10 @@
|
||||
"vmTypeDefault": "Standard_D4s_v3",
|
||||
"diskSizeGbDefault": "500",
|
||||
"index": "vm2",
|
||||
"versions": [
|
||||
"values": [
|
||||
{
|
||||
"name": "QDC Single Server April 2020",
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/galleries/QMICloud/images/QDC/versions/4.5.0"
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/galleries/QMICloud/images/QDC/values/4.5.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -42,16 +42,16 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "QDC",
|
||||
"vmTypeDefault": "Standard_D8s_v3",
|
||||
"diskSizeGbDefault": 500,
|
||||
"index": "vm1",
|
||||
"versions": [
|
||||
"values": [
|
||||
{
|
||||
"name": "QDC Single Server April 2020",
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/galleries/QMICloud/images/QDC/versions/4.5.0"
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/galleries/QMICloud/images/QDC/values/4.5.0"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -68,20 +68,20 @@
|
||||
"isWafPolicyAppGw": false,
|
||||
"isExternal": false,
|
||||
"isDisabled": false,
|
||||
"availableProductVersions": [
|
||||
"availableProductvalues": [
|
||||
{
|
||||
"product": "Qlik Sense",
|
||||
"vmTypeDefault": "Standard_D8s_v3",
|
||||
"diskSizeGbDefault": 250,
|
||||
"index": "vm1",
|
||||
"versions": [
|
||||
"values": [
|
||||
{
|
||||
"name": "Qlik Sense February 2020",
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/galleries/QMICloud/images/QlikSenseEnterprise/versions/13.62.0"
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/galleries/QMICloud/images/QlikSenseEnterprise/values/13.62.0"
|
||||
},
|
||||
{
|
||||
"name": "Qlik Sense April 2020",
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/galleries/QMICloud/images/QlikSenseEnterprise/versions/13.72.3"
|
||||
"image": "/subscriptions/62ebff8f-c40b-41be-9239-252d6c0c8ad9/resourceGroups/QMI-Machines/providers/Microsoft.Compute/galleries/QMICloud/images/QlikSenseEnterprise/values/13.72.3"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -50,6 +50,15 @@ function timeRunning(p) {
|
||||
};
|
||||
}
|
||||
|
||||
function timeFromCreated(p) {
|
||||
let totalTime = Math.abs(new Date().getTime() - new Date(p.created).getTime());
|
||||
let duration = moment.duration(totalTime);
|
||||
p.duration = {
|
||||
hours: Math.floor(duration.asHours()),
|
||||
complete: Math.floor(duration.asDays()) +"d "+duration.hours()+"h "+duration.minutes()
|
||||
};
|
||||
}
|
||||
|
||||
async function asyncForEach(array, callback) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
@@ -61,9 +70,12 @@ async function init(type) {
|
||||
var filter = {
|
||||
"isDestroyed":false,
|
||||
"isDeleted": false,
|
||||
"$or": [ {"runForever":{ "$exists": false }}, {"runForever": false}],
|
||||
"statusVms": "Stopped",
|
||||
"vmImage": {"$exists": true},
|
||||
"vmImage.vm1": { "$exists": true }
|
||||
"$and": [{"$or": [
|
||||
{ "options": {"$exists": true}, "options.vm1": { "$exists": true } },
|
||||
{ "vmImage": {"$exists": true}, "vmImage.vm1": { "$exists": true } },
|
||||
]}]
|
||||
};
|
||||
if ( type === "warning" ) {
|
||||
filter.pendingNextAction = {$ne: "destroy"};
|
||||
@@ -103,6 +115,55 @@ async function init(type) {
|
||||
});
|
||||
}
|
||||
|
||||
async function initOthers() {
|
||||
const WARN_DAYS = 82;
|
||||
const DEST_DAYS = 91;
|
||||
var dWarning = new Date();
|
||||
var dDestroy = new Date();
|
||||
dWarning.setDate(dWarning.getDate() - WARN_DAYS);
|
||||
dDestroy.setDate(dDestroy.getDate() - DEST_DAYS);
|
||||
|
||||
dWarning = dWarning.getTime();
|
||||
dDestroy = dDestroy.getTime();
|
||||
|
||||
var filter = {
|
||||
"isDestroyed":false,
|
||||
"isDeleted": false,
|
||||
"$or": [ {"runForever":{ "$exists": false }}, {"runForever": false}],
|
||||
"scenario": { "$in": ["awsqmi-s3bucket"] }
|
||||
};
|
||||
|
||||
|
||||
let provisions = await db.provision.get(filter);
|
||||
|
||||
await asyncForEach(provisions.results, async function(p) {
|
||||
var typeSchedule = "24x7";
|
||||
timeFromCreated(p);
|
||||
|
||||
let pCreatedAt = new Date(p.created);
|
||||
pCreatedAt = pCreatedAt.getTime();
|
||||
var limit;
|
||||
|
||||
if ( pCreatedAt <= dDestroy ) {
|
||||
//TODO Destroy
|
||||
limit = DEST_DAYS*24;
|
||||
await doDestroy(p, limit, "24x7");
|
||||
} else if ( pCreatedAt > dDestroy && pCreatedAt <= dWarning && !p.pendingNextAction) {
|
||||
//TODO Warning
|
||||
limit = WARN_DAYS*24;
|
||||
let msg = `Send warning DESTROY email - ${p.user.displayName} (${p.user.upn}) about provision '${p._scenarioDoc.title}' (${p._id} - ${typeSchedule}) being 'Inactive' more than ${limit} hours, (exactly ${p.duration.complete})`;
|
||||
console.log(msg);
|
||||
if ( IS_REAL ) {
|
||||
db.event.add({ provision: p._id, type: 'vms.warning-destroy' });
|
||||
await db.provision.update(p._id, {"pendingNextAction": "destroy"});
|
||||
await db.notification.add({ provision: p._id.toString(), type: 'warningDestroy', message: msg });
|
||||
await sendEmail.sendWillDestroyInDays(p, p._scenarioDoc, WARN_DAYS, 10);
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const doSendEmailDestroyWarning = async function(p, limit, typeSchedule) {
|
||||
if ( p.pendingNextAction === 'destroy') {
|
||||
console.log(`Warning email Destroy already sent. Wait for pending action to complete.`);
|
||||
@@ -110,7 +171,7 @@ const doSendEmailDestroyWarning = async function(p, limit, typeSchedule) {
|
||||
let msg = `Send warning DESTROY email - ${p.user.displayName} (${p.user.upn}) about provision '${p._scenarioDoc.title}' (${p._id} - ${typeSchedule}) being 'Inactive' more than ${limit} hours, (exactly ${p.duration.complete})`;
|
||||
console.log(msg);
|
||||
if ( IS_REAL ) {
|
||||
db.event.add({ user: p.user._id, provision: p._id, type: 'vms.warning-destroy' });
|
||||
db.event.add({ provision: p._id, type: 'vms.warning-destroy' });
|
||||
await db.provision.update(p._id, {"pendingNextAction": "destroy"});
|
||||
await db.notification.add({ provision: p._id.toString(), type: 'warningDestroy', message: msg });
|
||||
await sendEmail.sendWillDestroyIn24(p, p._scenarioDoc, Math.floor(limit/24), WARNING_DAYS);
|
||||
@@ -124,7 +185,7 @@ const doDestroy = async function(p, limit, typeSchedule) {
|
||||
let msg = `Provision destroyed - ${p.user.displayName} (${p.user.upn}) about provision '${p._scenarioDoc.title}' (${p._id} - ${typeSchedule}) being 'Inactive' more than ${limit} hours (exactly ${p.duration.complete})`
|
||||
console.log(msg);
|
||||
if ( IS_REAL ) {
|
||||
db.event.add({ user: p.user._id, provision: p._id, type: 'vms.exec-destroy' });
|
||||
db.event.add({ provision: p._id, type: 'vms.exec-destroy' });
|
||||
await postDestroy(p);
|
||||
await db.notification.add({ provision: p._id.toString(), type: 'destroy', message: msg });
|
||||
}
|
||||
@@ -145,6 +206,18 @@ function check(type) {
|
||||
});
|
||||
}
|
||||
|
||||
function checkOthers() {
|
||||
initOthers().then(function(){
|
||||
db.mongoose.connection.close()
|
||||
process.exit(0);
|
||||
|
||||
}).catch(function(e){
|
||||
db.mongoose.connection.close()
|
||||
console.log("Error", e);
|
||||
process.exit(0);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------
|
||||
switch (myArgs[0]) {
|
||||
@@ -154,6 +227,9 @@ switch (myArgs[0]) {
|
||||
case 'exec':
|
||||
check("exec");
|
||||
break;
|
||||
case 'others':
|
||||
checkOthers();
|
||||
break;
|
||||
default:
|
||||
console.log('Sorry, that is not something I know how to do.');
|
||||
process.exit(0);
|
||||
|
||||
@@ -24,7 +24,7 @@ const doSendEmailErrorProvision = async function(p) {
|
||||
let msg = `Send prrovision ERROR email - ${p.user.displayName} (${p.user.upn}) about provision '${p._scenarioDoc.title}' (${p._id}) provisioned with 'Errors'`;
|
||||
console.log(msg);
|
||||
if ( IS_REAL ) {
|
||||
db.event.add({ user: p.user._id, provision: p._id, type: 'vms.warning-error' });
|
||||
db.event.add({ provision: p._id, type: 'vms.warning-error' });
|
||||
await db.notification.add({ provision: p._id.toString(), type: 'warningError', message: msg });
|
||||
await sendEmail.sendProvisionError(p, p._scenarioDoc);
|
||||
}
|
||||
|
||||
@@ -61,10 +61,14 @@ async function init(type) {
|
||||
var filter = {
|
||||
"isDestroyed": false,
|
||||
"isDeleted": false,
|
||||
"$or": [ {"runForever":{ "$exists": false }}, {"runForever": false}],
|
||||
"statusVms": "Running",
|
||||
"vmImage": { "$exists": true },
|
||||
"vmImage.vm1": { "$exists": true }
|
||||
"$and": [{"$or": [
|
||||
{ "options": {"$exists": true}, "options.vm1": { "$exists": true } },
|
||||
{ "vmImage": {"$exists": true}, "vmImage.vm1": { "$exists": true } },
|
||||
]}]
|
||||
};
|
||||
|
||||
if ( type === "warning" ) {
|
||||
filter.pendingNextAction = { $ne: "stopVms" };
|
||||
cb = doSendEmailWarning;
|
||||
@@ -114,7 +118,7 @@ const doSendEmailWarning = async function(p, limit, typeSchedule) {
|
||||
let msg = `Send warning STOP email - ${p.user.displayName} (${p.user.upn}) about provision '${p._scenarioDoc.title}' (${p._id} - ${typeSchedule}) being 'Running' more than ${limit} hours, (exactly ${p.duration.complete})`;
|
||||
console.log(msg);
|
||||
if ( IS_REAL ) {
|
||||
db.event.add({ user: p.user._id, provision: p._id, type: 'vms.warning-stop' });
|
||||
db.event.add({ provision: p._id, type: 'vms.warning-stop' });
|
||||
await db.provision.update(p._id, {"pendingNextAction": "stopVms"});
|
||||
await db.notification.add({ provision: p._id.toString(), type: 'warningStop', message: msg });
|
||||
await sendEmail.sendWillStopIn24(p, p._scenarioDoc, Math.floor(limit/24), WARNING_DAYS);
|
||||
@@ -138,8 +142,8 @@ const doStop = async function(p, limit, typeSchedule) {
|
||||
});
|
||||
}
|
||||
//Stop VMs indefinitely
|
||||
db.event.add({ user: p.user._id, provision: p._id, type: 'vms.exec-stop' });
|
||||
await cli.deallocate(p._id, true);
|
||||
db.event.add({ provision: p._id, type: 'vms.exec-stop' });
|
||||
await cli.deallocate(p._id, null, true);
|
||||
await db.notification.add({ provision: p._id.toString(), type: 'stop', message: msg });
|
||||
}
|
||||
|
||||
|
||||
@@ -57,8 +57,8 @@ async function getProvisions(filter){
|
||||
console.log(res.results.length);
|
||||
await asyncForEach(res.results, async function(prov){
|
||||
if ( prov.vmType ) {
|
||||
//console.log("vmType", prov.vmType, prov.vmImage)
|
||||
prov["vmImage"] = {
|
||||
//console.log("vmType", prov.vmType, prov.options)
|
||||
prov["options"] = {
|
||||
"vm1": {
|
||||
"vmType": prov.vmType,
|
||||
"diskSizeGb": 128
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "qmi-cloud-cli",
|
||||
"version": "1.3.0",
|
||||
"version": "2.0.5",
|
||||
"scripts": {
|
||||
},
|
||||
"private": true,
|
||||
|
||||
@@ -2,7 +2,6 @@ const AWS = require("aws-sdk");
|
||||
const db = require("./mongo");
|
||||
const utils = require("./utils");
|
||||
|
||||
|
||||
function _getRgName(provision) {
|
||||
let rgName = provision.scenario.toUpperCase();
|
||||
rgName = rgName.replace(/AZQMI/g, 'QMI');
|
||||
@@ -20,7 +19,98 @@ function _getRegion(provision) {
|
||||
return region;
|
||||
}
|
||||
|
||||
function deallocate(provision, isSendEmailAfter) {
|
||||
function _getDbIndentifier(provision) {
|
||||
var out;
|
||||
if ( provision.outputs['db_instance_id'] ) {
|
||||
out = provision.outputs['db_instance_id'];
|
||||
} else if (provision.outputs['db_instance_endpoint']) {
|
||||
out = provision.outputs['db_instance_endpoint'].split(".")[0];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
async function stopDbInstance(provision, triggerUserId, isSendEmailAfter) {
|
||||
if (provision.statusVms === 'Stopped' || provision.statusVms === 'Stopping'){
|
||||
return;
|
||||
}
|
||||
var identifier = _getDbIndentifier(provision);
|
||||
|
||||
if ( !identifier ) {
|
||||
return;
|
||||
}
|
||||
|
||||
await db.provision.update(provision._id.toString(), {"statusVms": "Stopping" });
|
||||
|
||||
db.event.add({ user: triggerUserId, provision: provision._id, type: `db.stopping` });
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let region = _getRegion(provision);
|
||||
var rds = new AWS.RDS({apiVersion: '2014-10-31', region: region});
|
||||
var params = {
|
||||
DBInstanceIdentifier: identifier
|
||||
};
|
||||
console.log(`AWSCLI# DB (${identifier}) stopping...`);
|
||||
rds.stopDBInstance(params, async function(err, data) {
|
||||
if (err) {
|
||||
console.log("AWSCLI# ERROR stopping DB: "+identifier, err.stack);
|
||||
await db.provision.update(provision._id.toString(), {"statusVms": "Running" });
|
||||
reject(err);
|
||||
} else {
|
||||
console.log(`AWSCLI# DB (${identifier}) stopped!`);
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
async function startDbInstance(provision, triggerUserId) {
|
||||
if (provision.statusVms === 'Running' || provision.statusVms === 'Starting'){
|
||||
return;
|
||||
}
|
||||
|
||||
var identifier = _getDbIndentifier(provision);
|
||||
|
||||
if ( !identifier ) {
|
||||
return;
|
||||
}
|
||||
|
||||
await db.provision.update(provision._id.toString(), {"statusVms": "Starting" });
|
||||
db.event.add({ user: triggerUserId, provision: provision._id, type: `db.starting` });
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let region = _getRegion(provision);
|
||||
var rds = new AWS.RDS({apiVersion: '2014-10-31', region: region});
|
||||
var params = {
|
||||
DBInstanceIdentifier: identifier
|
||||
};
|
||||
console.log(`AWSCLI# DB (${identifier}) starting...`);
|
||||
|
||||
rds.startDBInstance(params, async function(err, data) {
|
||||
|
||||
if (err) {
|
||||
console.log("AWSCLI# ERROR starting DB: "+identifier, err.stack);
|
||||
await db.provision.update(provision._id.toString(), {"statusVms": "Stopped" });
|
||||
reject(err);
|
||||
} else {
|
||||
console.log(`AWSCLI# DB (${identifier}) starting (2)...`);
|
||||
rds.waitFor('dBInstanceAvailable', params, async function(err, data) {
|
||||
if (err) {
|
||||
console.log("AWSCLI# ERROR waiting for DB to become available: "+identifier, err.stack);
|
||||
reject(err);
|
||||
} else {
|
||||
console.log(`AWSCLI# DB (${identifier}) started!`);
|
||||
await utils.afterStartVms( provision, triggerUserId, "db" );
|
||||
}
|
||||
});
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function deallocate(provision, triggerUserId, isSendEmailAfter) {
|
||||
|
||||
let rgName = _getRgName(provision);
|
||||
let region = _getRegion(provision);
|
||||
@@ -62,7 +152,7 @@ function deallocate(provision, isSendEmailAfter) {
|
||||
reject(err1);
|
||||
} else {
|
||||
console.log("AWSCLI# Ec2s stopped!");
|
||||
await utils.afterStopVms( provision, isSendEmailAfter );
|
||||
await utils.afterStopVms( provision, triggerUserId, isSendEmailAfter );
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
@@ -74,7 +164,7 @@ function deallocate(provision, isSendEmailAfter) {
|
||||
});
|
||||
}
|
||||
|
||||
function start(provision) {
|
||||
function start(provision, triggerUserId) {
|
||||
|
||||
let rgName = _getRgName(provision);
|
||||
let region = _getRegion(provision);
|
||||
@@ -114,7 +204,7 @@ function start(provision) {
|
||||
reject(err1);
|
||||
} else {
|
||||
console.log("AWSCLI# Ec2s started!");
|
||||
await utils.afterStartVms( provision );
|
||||
await utils.afterStartVms( provision, triggerUserId );
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
@@ -128,3 +218,5 @@ function start(provision) {
|
||||
|
||||
module.exports.deallocate = deallocate;
|
||||
module.exports.start = start;
|
||||
module.exports.stopDbInstance = stopDbInstance;
|
||||
module.exports.startDbInstance = startDbInstance;
|
||||
|
||||
@@ -42,7 +42,7 @@ async function asyncForEach(array, callback) {
|
||||
}
|
||||
}
|
||||
|
||||
async function deallocate(provision, isSendEmailAfter ) {
|
||||
async function deallocate(provision, triggerUserId, isSendEmailAfter ) {
|
||||
|
||||
let rgName = _getRgName(provision);
|
||||
console.log("AzureCLI# Deallocating VMs for resource group: "+rgName);
|
||||
@@ -58,7 +58,7 @@ async function deallocate(provision, isSendEmailAfter ) {
|
||||
await computeClient.virtualMachines.deallocate(rgName, vm.name);
|
||||
});
|
||||
|
||||
await utils.afterStopVms(provision, isSendEmailAfter);
|
||||
await utils.afterStopVms(provision, triggerUserId, isSendEmailAfter);
|
||||
|
||||
console.log("AzureCLI# All VMs DEALLOCATED for resource group: "+rgName);
|
||||
} catch ( error ) {
|
||||
@@ -67,7 +67,7 @@ async function deallocate(provision, isSendEmailAfter ) {
|
||||
}
|
||||
}
|
||||
|
||||
async function start(provision){
|
||||
async function start(provision, triggerUserId){
|
||||
|
||||
let rgName = _getRgName(provision);
|
||||
console.log("AzureCLI# Starting VMs for resource group: "+rgName);
|
||||
@@ -85,7 +85,7 @@ async function start(provision){
|
||||
await computeClient.virtualMachines.start(rgName, vm.name);
|
||||
});
|
||||
|
||||
await utils.afterStartVms( provision );
|
||||
await utils.afterStartVms( provision, triggerUserId );
|
||||
console.log("AzureCLI# All VMs RUNNING for resource group: "+rgName);
|
||||
|
||||
} catch ( error ) {
|
||||
|
||||
@@ -40,7 +40,7 @@ async function _createApp(provision, backendPort, backendType) {
|
||||
console.log("Barracuda# Creating DNS record in Azure: "+cname);
|
||||
azurecli.createDNSRecord(provision, cname);
|
||||
|
||||
db.event.add({ user: provision.user._id, provision: provision._id, type: 'provision.is-public' });
|
||||
db.event.add({ provision: provision._id, type: 'provision.is-public' });
|
||||
|
||||
}).catch(function(err){
|
||||
console.log("Barracuda# Error creating Barracuda App", err);
|
||||
@@ -62,7 +62,7 @@ async function deleteApp(provision) {
|
||||
client.delete(provision.barracudaAppId).then(function() {
|
||||
console.log("Barracuda# Barracuda App deleted! ID: "+ provision.barracudaAppId);
|
||||
|
||||
db.event.add({ user: provision.user._id, provision: provision._id, type: 'provision.is-private' });
|
||||
db.event.add({ provision: provision._id, type: 'provision.is-private' });
|
||||
}).catch(function(err){
|
||||
console.log("Barracuda# Error deleting Barracuda App", err);
|
||||
});
|
||||
|
||||
@@ -3,31 +3,32 @@ const azurecli = require("./azurecli");
|
||||
const db = require("./mongo");
|
||||
|
||||
|
||||
async function deallocate(provId, isSendEmailAfter ) {
|
||||
async function deallocate(provId, userId, isSendEmailAfter ) {
|
||||
try {
|
||||
let provision = await db.provision.getById(provId);
|
||||
if ( !provision ) return;
|
||||
|
||||
if (provision.scenario === 'azqmi-fort'){
|
||||
return awscli.deallocate(provision, isSendEmailAfter);
|
||||
return awscli.deallocate(provision, userId, isSendEmailAfter);
|
||||
} else {
|
||||
return azurecli.deallocate(provision, isSendEmailAfter);
|
||||
return azurecli.deallocate(provision, userId, isSendEmailAfter);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("CLI# ERROR stopping VMs", err);
|
||||
}
|
||||
}
|
||||
|
||||
async function start(provId){
|
||||
async function start(provId, userId){
|
||||
|
||||
try {
|
||||
let provision = await db.provision.getById(provId);
|
||||
|
||||
if ( !provision ) return;
|
||||
|
||||
if (provision.scenario === 'azqmi-fort'){
|
||||
return awscli.start(provision);
|
||||
return awscli.start(provision, userId);
|
||||
} else {
|
||||
return azurecli.start(provision);
|
||||
return azurecli.start(provision, userId);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("CLI# ERROR starting VMs", err);
|
||||
@@ -42,6 +43,30 @@ async function updateVmsTags(provId, tagsEdit) {
|
||||
}
|
||||
}
|
||||
|
||||
async function stopDb(provId, userId, isSendEmailAfter) {
|
||||
try {
|
||||
let provision = await db.provision.getById(provId);
|
||||
if ( !provision ) return;
|
||||
|
||||
return awscli.stopDbInstance(provision, userId, isSendEmailAfter);
|
||||
} catch (err) {
|
||||
console.log("CLI# ERROR stopping DB", err);
|
||||
}
|
||||
}
|
||||
|
||||
async function startDb(provId, userId) {
|
||||
try {
|
||||
let provision = await db.provision.getById(provId);
|
||||
if ( !provision ) return;
|
||||
|
||||
return awscli.startDbInstance(provision, userId);
|
||||
} catch (err) {
|
||||
console.log("CLI# ERROR stopping DB", err);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.deallocate = deallocate;
|
||||
module.exports.start = start;
|
||||
module.exports.updateVmsTags = updateVmsTags;
|
||||
module.exports.updateVmsTags = updateVmsTags;
|
||||
module.exports.stopDb = stopDb;
|
||||
module.exports.startDb = startDb;
|
||||
@@ -1,4 +0,0 @@
|
||||
module.exports = {
|
||||
DOCKERIMAGE_TERRAFORM: "qlikgear/terraform:0.13.4",
|
||||
PROVISION_VERSION: 20201124
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
const mongoose = require('mongoose');
|
||||
mongoose.set('useFindAndModify', false);
|
||||
const crypto = require("crypto");
|
||||
//mongoose.set('debug', true)
|
||||
|
||||
|
||||
const schema = new mongoose.Schema({
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
const mongoose = require('mongoose');
|
||||
mongoose.set('useFindAndModify', false);
|
||||
//mongoose.set('debug', true)
|
||||
|
||||
|
||||
const destroySchema = new mongoose.Schema({
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
const mongoose = require('mongoose');
|
||||
mongoose.set('useFindAndModify', false);
|
||||
const crypto = require("crypto");
|
||||
//mongoose.set('debug', true)
|
||||
|
||||
|
||||
const schema = new mongoose.Schema({
|
||||
created: {
|
||||
@@ -12,12 +8,12 @@ const schema = new mongoose.Schema({
|
||||
},
|
||||
user: {
|
||||
type: mongoose.Types.ObjectId,
|
||||
ref: 'User',
|
||||
index : true
|
||||
ref: 'User'
|
||||
},
|
||||
provision: {
|
||||
type: mongoose.Types.ObjectId,
|
||||
ref: 'Provision'
|
||||
ref: 'Provision',
|
||||
index : true
|
||||
},
|
||||
type: {
|
||||
type: String
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
const mongoose = require('mongoose');
|
||||
mongoose.set('useFindAndModify', false);
|
||||
//mongoose.set('debug', true)
|
||||
|
||||
|
||||
const sc = new mongoose.Schema({
|
||||
created: {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
const mongoose = require('mongoose');
|
||||
mongoose.set('useFindAndModify', false);
|
||||
//mongoose.set('debug', true)
|
||||
|
||||
const provisionSchema = new mongoose.Schema({
|
||||
user: {
|
||||
@@ -27,6 +25,7 @@ const provisionSchema = new mongoose.Schema({
|
||||
},
|
||||
description: String,
|
||||
vmImage: Object,
|
||||
options: Object,
|
||||
status: {
|
||||
type: String,
|
||||
default: "queued"
|
||||
@@ -106,6 +105,13 @@ const provisionSchema = new mongoose.Schema({
|
||||
parent: {
|
||||
type: mongoose.Types.ObjectId,
|
||||
ref: 'Provision'
|
||||
},
|
||||
runForever: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
guacaConnId: {
|
||||
type: String
|
||||
}
|
||||
},{
|
||||
toObject: {virtuals:true},
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
const mongoose = require('mongoose')
|
||||
mongoose.set('useFindAndModify', false);
|
||||
//mongoose.set('debug', true)
|
||||
|
||||
|
||||
const scenarioSchema = new mongoose.Schema({
|
||||
@@ -35,6 +33,10 @@ const scenarioSchema = new mongoose.Schema({
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
forceExternalAccess: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isDivvyEnabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@@ -91,6 +93,9 @@ const scenarioSchema = new mongoose.Schema({
|
||||
allowedOnScheduleRunningDays: {
|
||||
type: Number,
|
||||
default: 4
|
||||
},
|
||||
terraformImage: {
|
||||
type: String
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
const mongoose = require('mongoose');
|
||||
mongoose.set('useFindAndModify', false);
|
||||
//mongoose.set('debug', true)
|
||||
|
||||
|
||||
const sc = new mongoose.Schema({
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
const mongoose = require('mongoose')
|
||||
mongoose.set('useFindAndModify', false);
|
||||
//mongoose.set('debug', true)
|
||||
|
||||
const schema = new mongoose.Schema({
|
||||
created: {
|
||||
type: Date,
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
const mongoose = require('mongoose')
|
||||
mongoose.set('useFindAndModify', false);
|
||||
//mongoose.set('debug', true)
|
||||
|
||||
const subSchema = new mongoose.Schema({
|
||||
created: {
|
||||
|
||||
67
qmi-cloud-common/models/TrainingSession.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const mongoose = require('mongoose');
|
||||
const crypto = require("crypto");
|
||||
|
||||
|
||||
const schema = new mongoose.Schema({
|
||||
description: {
|
||||
type: String
|
||||
},
|
||||
user: {
|
||||
type: mongoose.Types.ObjectId,
|
||||
ref: 'User'
|
||||
},
|
||||
template: {
|
||||
type: mongoose.Types.ObjectId,
|
||||
ref: 'TrainingTemplate'
|
||||
},
|
||||
created: {
|
||||
type: Date,
|
||||
default: Date.now
|
||||
},
|
||||
updated: {
|
||||
type: Date,
|
||||
default: Date.now
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
default: "inactive" //active, inactive, terminated
|
||||
},
|
||||
qcsTenantHost: {
|
||||
type: String
|
||||
},
|
||||
qcsApiKey: {
|
||||
type: String
|
||||
},
|
||||
qaUrl: {
|
||||
type: String
|
||||
},
|
||||
qaToken: {
|
||||
type: String
|
||||
},
|
||||
cloudshareClass: {
|
||||
type: String
|
||||
},
|
||||
qcsSharedSpace: {
|
||||
type: String
|
||||
},
|
||||
qcsDataSpace: {
|
||||
type: String
|
||||
},
|
||||
passwd: {
|
||||
type: String,
|
||||
default: function() {
|
||||
return crypto.randomBytes(4).toString('hex');
|
||||
}
|
||||
},
|
||||
studentsCount: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
studentEmailFilter: {
|
||||
type: String // qlik.com,talend.com,gmail.com
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
module.exports = mongoose.model('TrainingSession', schema);
|
||||
22
qmi-cloud-common/models/TrainingStudent.js
Normal file
@@ -0,0 +1,22 @@
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
|
||||
const schema = new mongoose.Schema({
|
||||
created: {
|
||||
type: Date,
|
||||
default: Date.now
|
||||
},
|
||||
email: {
|
||||
type: String
|
||||
},
|
||||
session: {
|
||||
type: mongoose.Types.ObjectId, ref: 'TrainingSession'
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
default: 'pending'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
module.exports = mongoose.model('TrainingStudent', schema);
|
||||
35
qmi-cloud-common/models/TrainingTemplate.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
const schema = new mongoose.Schema({
|
||||
index: {
|
||||
type: String
|
||||
},
|
||||
created: {
|
||||
type: Date,
|
||||
default: Date.now
|
||||
},
|
||||
title: {
|
||||
type: String
|
||||
},
|
||||
description: {
|
||||
type: String
|
||||
},
|
||||
publicDescription: {
|
||||
type: String
|
||||
},
|
||||
cloudshare: {
|
||||
type: Boolean
|
||||
},
|
||||
qcs: {
|
||||
type: Boolean
|
||||
},
|
||||
needQcsAutomation: {
|
||||
type: Array //['main']
|
||||
},
|
||||
needQcsApiKey: {
|
||||
type: Boolean
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
module.exports = mongoose.model('TrainingTemplate', schema);
|
||||
@@ -1,6 +1,4 @@
|
||||
const mongoose = require('mongoose')
|
||||
mongoose.set('useFindAndModify', false);
|
||||
//mongoose.set('debug', true)
|
||||
|
||||
|
||||
const userSchema = new mongoose.Schema({
|
||||
@@ -14,7 +12,11 @@ const userSchema = new mongoose.Schema({
|
||||
default: Date.now
|
||||
},
|
||||
displayName: String,
|
||||
upn: String,
|
||||
upn: {
|
||||
type: String,
|
||||
index: true
|
||||
},
|
||||
sub: String,
|
||||
oid: {
|
||||
type: String,
|
||||
index: true
|
||||
@@ -31,6 +33,19 @@ const userSchema = new mongoose.Schema({
|
||||
},
|
||||
qcsUserSubject: {
|
||||
type: String
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
jobTitle: {
|
||||
type: String
|
||||
},
|
||||
mail: {
|
||||
type: String
|
||||
},
|
||||
featureFlags: {
|
||||
type: Array
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
const mongoose = require('mongoose')
|
||||
mongoose.set('useFindAndModify', false);
|
||||
//mongoose.set('debug', true)
|
||||
|
||||
|
||||
const userSchema = new mongoose.Schema({
|
||||
type: String,
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
const mongoose = require('mongoose');
|
||||
const boom = require('@hapi/boom');
|
||||
const options = {
|
||||
loggerLevel: 'error',
|
||||
useNewUrlParser: true,
|
||||
//reconnectInterval: 2000,
|
||||
//reconnectTries: 30, // Retry up to 30 times
|
||||
useCreateIndex: true,
|
||||
useUnifiedTopology: true
|
||||
};
|
||||
|
||||
console.log("--- MongoDB connecting... ", process.env.MONGO_URI);
|
||||
|
||||
// Connect to DB
|
||||
mongoose.connect(process.env.MONGO_URI, options);
|
||||
mongoose.connect(process.env.MONGO_URI);
|
||||
|
||||
// When successfully connected
|
||||
mongoose.connection.on('connected', () => {
|
||||
@@ -44,7 +36,9 @@ const Notification = require('./models/Notification');
|
||||
const Subscription = require('./models/Subscription');
|
||||
const Event = require('./models/Event');
|
||||
const SharedProvision = require('./models/SharedProvision');
|
||||
|
||||
const TrainingTemplate = require('./models/TrainingTemplate');
|
||||
const TrainingSession = require('./models/TrainingSession');
|
||||
const TrainingStudent = require('./models/TrainingStudent');
|
||||
|
||||
const getNewCountExtend = function(provision) {
|
||||
return provision.countExtend !== undefined? (provision.countExtend + 1) : 1;
|
||||
@@ -96,16 +90,24 @@ const getPage = async ( model, filter, page, populates, select ) => {
|
||||
exec = exec.populate({ path: 'user', select: 'displayName upn'}).populate({path:'destroy', select: "-user -jobId"}).populate({path:'_scenarioDoc', select: "-availableProductVersions -updated -created"}).populate({path: "schedule"}).populate('deployOpts');
|
||||
}
|
||||
if ( model === SharedProvision ) {
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn oid active'}).populate({path: 'user', select: 'displayName upn'});
|
||||
}
|
||||
|
||||
if ( model === ApiKey ) {
|
||||
exec = exec.populate('user');
|
||||
}
|
||||
|
||||
if ( model = Scenario ) {
|
||||
if ( model === Scenario ) {
|
||||
exec = exec.populate('subscription').populate('deployOpts');
|
||||
}
|
||||
|
||||
if ( model === Event ) {
|
||||
exec = exec.populate({path: 'user', select: 'displayName'});
|
||||
}
|
||||
|
||||
if ( model === TrainingSession ) {
|
||||
exec = exec.populate('user').populate('template');
|
||||
}
|
||||
}
|
||||
|
||||
const entity = await exec;
|
||||
@@ -174,16 +176,23 @@ const get = async (model, filter, select, skip, limit, populates, reply) => {
|
||||
},{
|
||||
path: 'destroy',
|
||||
select: "-user -jobId"
|
||||
}]}).populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||
}]}).populate({path: 'sharedWithUser', select: 'displayName upn oid active'}).populate({path: 'user', select: 'displayName upn'});
|
||||
}
|
||||
|
||||
if ( model === ApiKey ) {
|
||||
exec = exec.populate('user');
|
||||
}
|
||||
|
||||
if ( model = Scenario ) {
|
||||
if ( model === Scenario ) {
|
||||
exec = exec.populate('subscription').populate('deployOpts').populate('allowedUsers');
|
||||
}
|
||||
|
||||
if ( model === Event ) {
|
||||
exec = exec.populate({path: 'user', select: 'displayName'});
|
||||
}
|
||||
if ( model === TrainingSession ) {
|
||||
exec = exec.populate('user').populate('template');
|
||||
}
|
||||
}
|
||||
|
||||
const entity = await exec;
|
||||
@@ -210,14 +219,20 @@ const getById = async (model, id, reply) => {
|
||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
||||
}
|
||||
if ( model === SharedProvision ) {
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn oid active'}).populate({path: 'user', select: 'displayName upn'});
|
||||
}
|
||||
if ( model === ApiKey ) {
|
||||
exec = exec.populate('user');
|
||||
}
|
||||
if ( model = Scenario ) {
|
||||
if ( model === Scenario ) {
|
||||
exec = exec.populate('subscription').populate('deployOpts').populate('allowedUsers');
|
||||
}
|
||||
if ( model === Event ) {
|
||||
exec = exec.populate({path: 'user', select: 'displayName'});
|
||||
}
|
||||
if ( model === TrainingSession ) {
|
||||
exec = exec.populate('user').populate('template');
|
||||
}
|
||||
const entity = await exec;
|
||||
return entity;
|
||||
} catch (err) {
|
||||
@@ -232,14 +247,20 @@ const getOne = async (model, filter, reply) => {
|
||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
||||
}
|
||||
if ( model === SharedProvision ) {
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn oid active'}).populate({path: 'user', select: 'displayName upn'});
|
||||
}
|
||||
if ( model === ApiKey ) {
|
||||
exec = exec.populate('user');
|
||||
}
|
||||
if ( model = Scenario ) {
|
||||
if ( model === Scenario ) {
|
||||
exec = exec.populate('subscription').populate('deployOpts').populate('allowedUsers');
|
||||
}
|
||||
if ( model === Event ) {
|
||||
exec = exec.populate({path: 'user', select: 'displayName'});
|
||||
}
|
||||
if ( model === TrainingSession ) {
|
||||
exec = exec.populate('user').populate('template');
|
||||
}
|
||||
const entity = await exec;
|
||||
return entity;
|
||||
} catch (err) {
|
||||
@@ -266,14 +287,17 @@ const update = async (model, id, body, reply) => {
|
||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
||||
}
|
||||
if ( model === SharedProvision ) {
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn oid active'}).populate({path: 'user', select: 'displayName upn'});
|
||||
}
|
||||
if ( model === ApiKey ) {
|
||||
exec = exec.populate('user');
|
||||
}
|
||||
if ( model = Scenario ) {
|
||||
if ( model === Scenario ) {
|
||||
exec = exec.populate('subscription').populate('deployOpts');
|
||||
}
|
||||
if ( model === TrainingSession ) {
|
||||
exec = exec.populate('user').populate('template');
|
||||
}
|
||||
const update = await exec;
|
||||
return update;
|
||||
} catch (err) {
|
||||
@@ -293,14 +317,17 @@ const updateMany = async (model, filter, body, reply) => {
|
||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
||||
}
|
||||
if ( model === SharedProvision ) {
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn oid active'}).populate({path: 'user', select: 'displayName upn'});
|
||||
}
|
||||
if ( model === ApiKey ) {
|
||||
exec = exec.populate('user');
|
||||
}
|
||||
if ( model = Scenario ) {
|
||||
if ( model === Scenario ) {
|
||||
exec = exec.populate('subscription').populate('deployOpts');
|
||||
}
|
||||
if ( model === TrainingSession ) {
|
||||
exec = exec.populate('user').populate('template');
|
||||
}
|
||||
|
||||
return await exec;
|
||||
|
||||
@@ -382,6 +409,9 @@ module.exports = {
|
||||
event: _m(Event),
|
||||
user: _m(User),
|
||||
sharedProvision: _m(SharedProvision),
|
||||
trainingSession: _m(TrainingSession),
|
||||
trainingTemplate: _m(TrainingTemplate),
|
||||
trainingStudent: _m(TrainingStudent),
|
||||
utils: {
|
||||
getNewTimeRunning: getNewTimeRunning,
|
||||
getNewCountExtend: getNewCountExtend
|
||||
@@ -398,7 +428,10 @@ module.exports = {
|
||||
ApiKey: ApiKey,
|
||||
Subscription: Subscription,
|
||||
Event: Event,
|
||||
SharedProvision: SharedProvision
|
||||
SharedProvision: SharedProvision,
|
||||
TrainingSession: TrainingSession,
|
||||
TrainingTemplate: TrainingTemplate,
|
||||
TrainingStudent: TrainingStudent
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "qmi-cloud-common",
|
||||
"version": "1.1.6",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@azure/arm-compute": "^15.0.0",
|
||||
"@azure/arm-dns": "^4.0.0",
|
||||
@@ -8,9 +8,9 @@
|
||||
"@hapi/boom": "^9.1.0",
|
||||
"aws-sdk": "^2.942.0",
|
||||
"axios": "^0.21.1",
|
||||
"barracuda-api": "https://gitlab.com/qlik_gear/barracuda-api-node.git#0.0.10",
|
||||
"barracuda-api": "https://gitlab.com/qlik_gear/barracuda-api-node.git#1.1.0",
|
||||
"bull": "^3.11.0",
|
||||
"mongoose": "^5.7.4",
|
||||
"mongoose": "^6.11.1",
|
||||
"nodemailer": "^6.4.2",
|
||||
"uuid": "^8.3.2"
|
||||
}
|
||||
|
||||
@@ -5,19 +5,22 @@ export const TF_APPLY_QUEUE = 'TF_APPLY_QUEUE';
|
||||
export const TF_DESTROY_QUEUE = 'TF_DESTROY_QUEUE';
|
||||
export const TF_APPLY_QSEOK_QUEUE = 'TF_APPLY_QSEOK_QUEUE';
|
||||
export const STOP_CONTAINER_QUEUE = 'STOP_CONTAINER_QUEUE';
|
||||
export const SYNAPSE_QUEUE = 'SYNAPSE_QUEUE';
|
||||
|
||||
|
||||
var terraformApplyQueue = new Queue(TF_APPLY_QUEUE, process.env.REDIS_URL);
|
||||
var terraformDestroyQueue = new Queue(TF_DESTROY_QUEUE, process.env.REDIS_URL);
|
||||
var terraformApplyQseokQueue = new Queue(TF_APPLY_QSEOK_QUEUE, process.env.REDIS_URL);
|
||||
var stopContainerQueue = new Queue(STOP_CONTAINER_QUEUE, process.env.REDIS_URL);
|
||||
var synapseQueue = new Queue(SYNAPSE_QUEUE, process.env.REDIS_URL);
|
||||
|
||||
|
||||
export const queues = {
|
||||
[TF_APPLY_QUEUE]: terraformApplyQueue,
|
||||
[TF_DESTROY_QUEUE]: terraformDestroyQueue,
|
||||
[TF_APPLY_QSEOK_QUEUE]: terraformApplyQseokQueue,
|
||||
[STOP_CONTAINER_QUEUE]: stopContainerQueue
|
||||
[STOP_CONTAINER_QUEUE]: stopContainerQueue,
|
||||
[SYNAPSE_QUEUE]: synapseQueue
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ const https = require("https");
|
||||
const SMTP_EMAIL_SENDER = process.env.SMTP_EMAIL_SENDER;
|
||||
|
||||
const HOSTNAME_URL = process.env.HOSTNAME_URL || "https://qmicloud.qliktech.com";
|
||||
const FOOTER = `<div style="color:#404040;font-size:16px;margin:30px 0px">
|
||||
const FOOTER = `<div style="color:#404040;font-size:16px;padding:30px 0px 50px 0px">
|
||||
<p style="margin:0px">Check it out at <a href="${HOSTNAME_URL}">${HOSTNAME_URL}</a></p>
|
||||
</div>`;
|
||||
|
||||
@@ -86,6 +86,25 @@ function getHtmlScenarioDestroyIn24( provision, scenario, period, warningDays) {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function getHtmlScenarioIndays( provision, scenario, period, warningDays) {
|
||||
var common = _getCommonDetails(provision,scenario);
|
||||
return`<div style="width:600px;color:black!important;font-family:'Source Sans Pro',sans-serif;padding:50px">
|
||||
<div style="background-color:white;height:100%;padding:20px 10px">
|
||||
<div style="color:#404040;font-size:34px;text-align:center;margin:20px">
|
||||
<p style="margin:0px">QMI Cloud</p>
|
||||
</div>
|
||||
<div style="color:#404040;font-size:22px;margin:20px 0px 40px 0px">
|
||||
<p style="margin:0px">Provision '${scenario.title}' running more than ${period} days</p>
|
||||
</div>
|
||||
<div style="color:#404040;font-size:18px;margin:10px 0px">
|
||||
<p style="margin:0px;color: #FF2020">This scenario was meant to live active no more than 90 days. It's been 80 days since creation and it will be automatically DESTROYED in ${warningDays} days. Please, consider to backup it up manually if you don't want to lose data.</p>
|
||||
</div>
|
||||
${common}
|
||||
${FOOTER}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
function getHtmlScenarioVMsStopped( provision, scenario) {
|
||||
var common = _getCommonDetails(provision,scenario);
|
||||
@@ -109,6 +128,26 @@ function getHtmlScenarioVMsStopped( provision, scenario) {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function getHtmlSharedProvision( provision, scenario) {
|
||||
var common = _getCommonDetails(provision,scenario);
|
||||
return `<div style="width:600px;color:black!important;font-family:'Source Sans Pro',sans-serif;padding:50px">
|
||||
<div style="background-color:white;height:100%;padding:20px 10px">
|
||||
<div style="color:#404040;font-size:34px;text-align:center;margin:20px">
|
||||
<p style="margin:0px">QMI Cloud</p>
|
||||
</div>
|
||||
<div style="color:#404040;font-size:22px;margin:20px 0px 40px 0px">
|
||||
<p style="margin:0px">Provision '${scenario.title}'</p>
|
||||
</div>
|
||||
<div style="color:#404040;font-size:18px;margin:30px 0px">
|
||||
<p style="margin:0px;color: #FF2020">'${provision.user.displayName}' is sharing with you!</p>
|
||||
</div>
|
||||
|
||||
${common}
|
||||
${FOOTER}
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function getHtmlScenarioWillStopIn24( provision, scenario, period, warningDays ) {
|
||||
var common = _getCommonDetails(provision,scenario);
|
||||
return`<div style="width:600px;color:black!important;font-family:'Source Sans Pro',sans-serif;padding:50px">
|
||||
@@ -136,15 +175,20 @@ function getHtmlScenarioWillStopIn24( provision, scenario, period, warningDays )
|
||||
function getHtmlNewProvision(provision, scenario) {
|
||||
var htmlint;
|
||||
if ( provision && provision.outputs ) {
|
||||
htmlint = `<div style="color:#404040;font-size:18px;padding: 10px 0px;">Connection resources</div>`;
|
||||
htmlint = `<div style="color:#404040;font-size:20px;padding: 10px 0px;">Connection resources</div>`;
|
||||
htmlint += `<table style="width:100%" border="0">`;
|
||||
} else {
|
||||
htmlint = "";
|
||||
}
|
||||
for (let key in provision.outputs) {
|
||||
htmlint += `<div>
|
||||
<span style="color:#404040">${key}</span>
|
||||
<pre style="color:#404040;">${provision.outputs[key]}</pre>
|
||||
</div>`;
|
||||
htmlint += `<tr>
|
||||
<td style="padding-right: 15px"><b style="color:#404040">${key}</b></td>
|
||||
<td><pre style="color:#404040;">${provision.outputs[key]}</pre></td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
if ( provision && provision.outputs ) {
|
||||
htmlint += `</table>`;
|
||||
}
|
||||
|
||||
var common = _getCommonDetails(provision, scenario);
|
||||
@@ -243,17 +287,30 @@ async function sendWillDestroyIn24( provision, scenario, period, warningDays ) {
|
||||
await _doSend(provision.user.upn, `QMI Cloud - Provision will destroy in ${(warningDays*24)} hours`, htmlText);
|
||||
}
|
||||
|
||||
async function sendWillDestroyInDays( provision, scenario, period, warningDays ) {
|
||||
|
||||
const htmlText = getHtmlScenarioIndays( provision, scenario, period, warningDays);
|
||||
await _doSend(provision.user.upn, `QMI Cloud - Provision will destroy in ${warningDays} days`, htmlText);
|
||||
}
|
||||
|
||||
async function sendVMsStopped( provision, scenario ) {
|
||||
const htmlText = getHtmlScenarioVMsStopped( provision, scenario);
|
||||
await _doSend(provision.user.upn, 'QMI Cloud - VMs stopped automatically', htmlText);
|
||||
}
|
||||
|
||||
async function sendSharedProvision(provision, shareWithUser) {
|
||||
const htmlText = getHtmlSharedProvision( provision, provision._scenarioDoc);
|
||||
await _doSend(shareWithUser.upn, `${provision.user.displayName} shared a QMI Cloud provision with you.`, htmlText);
|
||||
}
|
||||
|
||||
module.exports.sendProvisionSuccess = sendProvisionSuccess;
|
||||
module.exports.sendProvisionError = sendProvisionError;
|
||||
module.exports.sendDestroyedSuccess = sendDestroyedSuccess;
|
||||
module.exports.sendWillStopIn24 = sendWillStopIn24;
|
||||
module.exports.sendWillDestroyInDays = sendWillDestroyInDays;
|
||||
module.exports.sendVMsStopped = sendVMsStopped;
|
||||
module.exports.sendWillDestroyIn24 = sendWillDestroyIn24;
|
||||
module.exports.sendSharedProvision= sendSharedProvision;
|
||||
module.exports._doSend = _doSend;
|
||||
|
||||
|
||||
|
||||
@@ -1,82 +1,72 @@
|
||||
const db = require("./mongo");
|
||||
const sendEmail = require("./send-email");
|
||||
|
||||
async function afterStopVms( provision, auto ) {
|
||||
async function afterStopVms( provision, triggerUserId, auto = false, type = 'vms' ) {
|
||||
let timeRunning = db.utils.getNewTimeRunning(provision);
|
||||
const dateNow = new Date();
|
||||
let patch = {
|
||||
"statusVms": "Stopped",
|
||||
"timeRunning": timeRunning,
|
||||
"stoppedFrom": dateNow,
|
||||
"pendingNextAction": undefined
|
||||
"pendingNextAction": null
|
||||
};
|
||||
|
||||
if ( auto && provision._scenarioDoc ) { //From CLI (auto stop)
|
||||
let msg = "";
|
||||
let msg = `[CLI] TotalTimeRunning: ${timeRunning} mins`;
|
||||
// Actual onschedule reset
|
||||
if ( provision.schedule && !provision.schedule.is24x7 ) {
|
||||
patch["startDateOnSchedule"] = dateNow;
|
||||
patch["endDateOnSchedule"] = dateNow;
|
||||
msg = "Schedule time has been reset.";
|
||||
msg += " - (Schedule) accumlated running time has been reset.";
|
||||
} else {
|
||||
msg = "24x7 vms auto-stopped due to limitted running time reached.";
|
||||
msg += " - (24x7) accumlated running time was reached.";
|
||||
}
|
||||
|
||||
msg += `New totalTimeRunning: ${timeRunning} mins`;
|
||||
|
||||
await db.provision.update(provision._id.toString(), patch);
|
||||
await sendEmail.sendVMsStopped(provision, provision._scenarioDoc);
|
||||
db.event.add({ user: provision.user._id, provision: provision._id, type: 'vms.stop-auto', message: msg });
|
||||
db.event.add({ provision: provision._id, type: 'vms.stop', message: msg });
|
||||
|
||||
} else { //On Demand stop
|
||||
let msg = "";
|
||||
|
||||
|
||||
if ( provision.schedule && !provision.schedule.is24x7 ) {
|
||||
patch["endDateOnSchedule"] = dateNow;
|
||||
|
||||
//This is temporary, only to make sure there is some initial value soon
|
||||
if ( !provision["startDateOnSchedule"] ) {
|
||||
patch["startDateOnSchedule"] = dateNow;
|
||||
msg = "startDateOS: " + dateNow.toISOString();
|
||||
} else {
|
||||
msg = "startDateOS: " + new Date(provision.startDateOnSchedule).toISOString();
|
||||
}
|
||||
msg += (" - endDateOS: " + dateNow.toISOString());
|
||||
|
||||
}
|
||||
|
||||
msg += ` - New totalTimeRunning: ${timeRunning} mins`;
|
||||
|
||||
await db.provision.update(provision._id.toString(), patch);
|
||||
|
||||
db.event.add({ user: provision.user._id, provision: provision._id, type: 'vms.stop-ondemand', message: msg });
|
||||
db.event.add({ user: triggerUserId, provision: provision._id, type: `${type}.stop`, message: `[Manual] TotalTimeRunning: ${timeRunning} mins` });
|
||||
}
|
||||
}
|
||||
|
||||
async function afterStartVms( provision ) {
|
||||
async function afterStartVms( provision, triggerUserId, type = 'vms' ) {
|
||||
const dateNow = new Date();
|
||||
let countExtend = db.utils.getNewCountExtend(provision);
|
||||
var patch = {
|
||||
"statusVms": "Running",
|
||||
"runningFrom": dateNow,
|
||||
"countExtend": countExtend,
|
||||
"pendingNextAction": undefined
|
||||
"pendingNextAction": null
|
||||
};
|
||||
|
||||
// Actual onschedule reset
|
||||
let msg = "";
|
||||
|
||||
let msg = `[Manual] TotalTimeRunning: ${provision.timeRunning} mins`;
|
||||
if ( provision.schedule && !provision.schedule.is24x7 ) {
|
||||
msg = "Schedule time has been reset.";
|
||||
msg += " - Schedule time has been reset.";
|
||||
patch["startDateOnSchedule"] = dateNow;
|
||||
patch["endDateOnSchedule"] = dateNow;
|
||||
} else {
|
||||
msg = `24x7, New count extend: ${countExtend}`;
|
||||
}
|
||||
|
||||
msg += `TotalTimeRunning so far: ${provision.timeRunning} mins`;
|
||||
msg += ` - 24x7, New count extend: ${countExtend}`;
|
||||
}
|
||||
|
||||
await db.provision.update(provision._id.toString(), patch);
|
||||
|
||||
db.event.add({ user: provision.user._id, provision: provision._id, type: 'vms.start-ondemand', message: msg });
|
||||
db.event.add({ user: triggerUserId, provision: provision._id, type: `${type}.start`, message: msg });
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,11 @@ const PROJECT_PATH = process.env.PROJECT_PATH;
|
||||
const tf = require("./docker/tf");
|
||||
const azure = require("./docker/azure");
|
||||
const sendEmail = require("qmi-cloud-common/send-email");
|
||||
const barracuda = require("qmi-cloud-common/barracuda");
|
||||
const guaca = require("./guacamole");
|
||||
|
||||
module.exports = async function(job) {
|
||||
|
||||
const triggerUser = job.data.user;
|
||||
|
||||
var prov = await db.provision.update(job.data.id, {
|
||||
"status": "initializing",
|
||||
@@ -22,7 +24,7 @@ module.exports = async function(job) {
|
||||
|
||||
var idProv = prov._id.toString();
|
||||
|
||||
db.event.add({ user: prov.user._id, provision: prov._id, type: 'provision.init' });
|
||||
db.event.add({ user: triggerUser._id, provision: prov._id, type: 'provision.init' });
|
||||
|
||||
// TERRAFORM INIT
|
||||
return tf.init(prov)
|
||||
@@ -32,7 +34,7 @@ module.exports = async function(job) {
|
||||
return Promise.reject({"success": false, "error": "Error at Terraform Init", provStatus: "error_init"});
|
||||
} else {
|
||||
// TERRAFORM PLAN
|
||||
return tf.plan(prov, job.data._scenario);
|
||||
return tf.plan(prov);
|
||||
}
|
||||
} )
|
||||
.then( async function(res) {
|
||||
@@ -43,7 +45,7 @@ module.exports = async function(job) {
|
||||
const dateNow = new Date();
|
||||
let patch = {
|
||||
"status": "provisioning",
|
||||
"statusVms": (prov.vmImage && prov.vmImage.vm1)? "Running" : "N/A",
|
||||
"statusVms": (prov.scenario === 'azqmi-synapse' || prov.scenario === 'awsqmi-rds' || prov.options && prov.options.vm1)? "Running" : "N/A",
|
||||
"runningFrom": dateNow,
|
||||
"runningTime": 0,
|
||||
"countExtend": 0
|
||||
@@ -58,51 +60,63 @@ module.exports = async function(job) {
|
||||
// TERRAFORM APPLY
|
||||
return tf.apply(prov);
|
||||
} ).then( async function(res) {
|
||||
// Save Provision status
|
||||
if ( res.statusCode === 1 ) {
|
||||
console.log(`ProcessorApply# Error at Terraform APPLY for provision (${idProv})`);
|
||||
} else if ( res.statusCode === 137 ){
|
||||
console.log(`ProcessorApply# Apply container must have been killed!`);
|
||||
return Promise.reject({"success": false, "error": "Apply container must have been killed", provStatus: "aborted"});
|
||||
}
|
||||
|
||||
var status = ( res.output.indexOf("Error:") !== -1 )? "error" : "provisioned";
|
||||
return await db.provision.update(prov._id, {"status": status});
|
||||
} ).then( async function(prov) {
|
||||
return tf.outputs(prov).then( async function(outputs){
|
||||
if ( outputs.barracudaAzureFqdn ) {
|
||||
let fqdn = outputs.barracudaAzureFqdn;
|
||||
delete outputs['barracudaAzureFqdn'];
|
||||
return await db.provision.update(prov._id, {"barracudaAzureFqdn": fqdn, "outputs": outputs});
|
||||
} else {
|
||||
return await db.provision.update(prov._id, {"outputs": outputs});
|
||||
}
|
||||
});
|
||||
|
||||
} ).then( async function(prov) {
|
||||
if ( prov.isExternalAccess && prov.barracudaAzureFqdn ) {
|
||||
console.log(`ProcessorApply# Calling Barracuda service to create App and DNS CName for provision (${prov._id})`);
|
||||
barracuda.createApp(prov);
|
||||
}
|
||||
return prov;
|
||||
} ).then( async function(prov) {
|
||||
// Generate Terraform OUTPUTS and store in provision object
|
||||
return tf.outputs(prov).then( async function(outputs){
|
||||
//if ( outputs.barracudaAzureFqdn ) {
|
||||
// let fqdn = outputs.barracudaAzureFqdn;
|
||||
// delete outputs['barracudaAzureFqdn'];
|
||||
// return await db.provision.update(prov._id, {"barracudaAzureFqdn": fqdn, "outputs": outputs});
|
||||
//} else {
|
||||
return await db.provision.update(prov._id, {"outputs": outputs});
|
||||
//}
|
||||
});
|
||||
|
||||
} )
|
||||
//.then( async function(prov) {
|
||||
// if ( prov.isExternalAccess && prov.barracudaAzureFqdn ) {
|
||||
// console.log(`ProcessorApply# Calling Barracuda service to create App and DNS CName for provision (${prov._id})`);
|
||||
// barracuda.createApp(prov);
|
||||
// }
|
||||
// return prov;
|
||||
//} )
|
||||
.then( async function(prov) {
|
||||
// Application Gateway assign policy
|
||||
return azure.appgateway(prov, job.data._scenario);
|
||||
} ).then( async function(prov) {
|
||||
// Create Image
|
||||
return azure.createimage(prov, job.data._scenario);
|
||||
} ).then( async function(prov) {
|
||||
// Guacamole Web access
|
||||
return await guaca.setUserConnection(prov, job.data._scenario);
|
||||
|
||||
} ).then( function(prov) {
|
||||
if (prov.status === "provisioned") {
|
||||
db.event.add({ provision: prov._id, type: 'provision.finished' });
|
||||
sendEmail.sendProvisionSuccess(prov, job.data._scenario);
|
||||
} else {
|
||||
db.event.add({ provision: prov._id, type: 'provision.error' });
|
||||
sendEmail.sendProvisionError(prov, job.data._scenario);
|
||||
}
|
||||
db.event.add({ user: prov.user._id, provision: prov._id, type: 'provision.finished' });
|
||||
|
||||
return Promise.resolve({"success": true, provMongo: prov});
|
||||
} ).catch( function(err) {
|
||||
console.log("ProcessorApply# Provision: error", err);
|
||||
|
||||
var errormsg = err.provStatus? err.provStatus : 'error'
|
||||
db.provision.update(prov._id, {"status": errormsg});
|
||||
db.event.add({ user: prov.user._id, provision: prov._id, type: 'provision.'+errormsg });
|
||||
db.event.add({ user: triggerUser._id, provision: prov._id, type: 'provision.'+errormsg });
|
||||
|
||||
if ( errormsg !== "aborted") {
|
||||
sendEmail.sendProvisionError(prov, job.data._scenario);
|
||||
|
||||
170
qmi-cloud-worker/docker/synapse.js
Normal file
@@ -0,0 +1,170 @@
|
||||
const Docker = require('dockerode');
|
||||
const docker = new Docker({
|
||||
'socketPath': '/home/docker.sock'
|
||||
//'socketPath': '/var/run/docker.sock'
|
||||
});
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const SSHPATH = process.env.SSHPATH;
|
||||
const DOCKERIMAGE = "mcr.microsoft.com/azure-cli";
|
||||
const PROJECT_PATH = process.env.PROJECT_PATH;
|
||||
|
||||
const pause = function(provision) {
|
||||
|
||||
if ( provision.scenario !== 'azqmi-synapse' ) {
|
||||
console.log(`SynapseAzCLI# provision (${provision._id}) is not AZ SYNAPSE`);
|
||||
return {"message": `Won't do anything, provision (${provision._id}) is not AZ SYNAPSE`};
|
||||
}
|
||||
|
||||
if ( !provision.outputs ) {
|
||||
console.log(`SynapseAzCLI# provision (${provision._id}) AZ SYNAPSE has NO outputs`);
|
||||
return {"message": `Won't do anything, provision (${provision._id}) AZ SYNAPSE has NO outputs`};
|
||||
}
|
||||
|
||||
const name = `qmi-synpause-${provision._id}`;
|
||||
console.log(`SynapseAzCLI# Pause Synapse: ${name}`);
|
||||
var processStream = fs.createWriteStream(provision.logFile, {flags:'a'});
|
||||
var exec = ['synpause', provision.outputs.Synapse_WS_ID, provision.outputs.Synapse_Database];
|
||||
console.log('SynapseAzCLI# Pause Synapse: exec: '+exec.join(" "));
|
||||
|
||||
return docker.run(DOCKERIMAGE, exec, processStream, {
|
||||
//"Env": vars.envs,
|
||||
"name": name,
|
||||
//"WorkingDir": "/app",
|
||||
"HostConfig": {
|
||||
"Binds": [
|
||||
`${provision.path}/synpause.sh:/bin/synpause`,
|
||||
`${SSHPATH}:/root/.ssh`
|
||||
]
|
||||
}
|
||||
}).then(function(data) {
|
||||
var container = data[1];
|
||||
console.log(`SynapseAzCLI# Pause Synapse: '${name}' (${container.id}) has finished with code: ${data[0].StatusCode}`);
|
||||
return container.remove().then(function(){
|
||||
console.log(`SynapseAzCLI# Pause Synapse: Container '${name}' removed!`);
|
||||
return data[0].StatusCode;
|
||||
});
|
||||
}).then(async function(statusCode) {
|
||||
|
||||
return {"output": fs.readFileSync(provision.logFile), "statusCode": statusCode};
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const resume = function(provision) {
|
||||
|
||||
if ( provision.scenario !== 'azqmi-synapse' ) {
|
||||
console.log(`SynapseAzCLI# provision (${provision._id}) is not AZ SYNAPSE`);
|
||||
return {"message": `Won't do anything, provision (${provision._id}) is not AZ SYNAPSE`};
|
||||
}
|
||||
|
||||
if ( !provision.outputs ) {
|
||||
console.log(`SynapseAzCLI# provision (${provision._id}) AZ SYNAPSE has NO outputs`);
|
||||
return {"message": `Won't do anything, provision (${provision._id}) AZ SYNAPSE has NO outputs`};
|
||||
}
|
||||
|
||||
const name = `qmi-synresume-${provision._id}`;
|
||||
console.log(`SynapseAzCLI# Resume Synapse: ${name}`);
|
||||
var processStream = fs.createWriteStream(provision.logFile, {flags:'a'});
|
||||
var exec = ['synresume', provision.outputs.Synapse_WS_ID, provision.outputs.Synapse_Database];
|
||||
console.log('SynapseAzCLI# Resume Synapse: exec: '+exec.join(" "));
|
||||
|
||||
return docker.run(DOCKERIMAGE, exec, processStream, {
|
||||
//"Env": vars.envs,
|
||||
"name": name,
|
||||
//"WorkingDir": "/app",
|
||||
"HostConfig": {
|
||||
"Binds": [
|
||||
`${provision.path}/synresume.sh:/bin/synresume`,
|
||||
`${SSHPATH}:/root/.ssh`
|
||||
]
|
||||
}
|
||||
}).then(function(data) {
|
||||
var container = data[1];
|
||||
console.log(`SynapseAzCLI# Resume Synapse: '${name}' (${container.id}) has finished with code: ${data[0].StatusCode}`);
|
||||
return container.remove().then(function(){
|
||||
console.log(`SynapseAzCLI# Resume Synapse: Container '${name}' removed!`);
|
||||
return data[0].StatusCode;
|
||||
});
|
||||
}).then(async function(statusCode) {
|
||||
|
||||
return {"output": fs.readFileSync(provision.logFile), "statusCode": statusCode};
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const copySnapshots = function(data) {
|
||||
|
||||
let snapName = data.snapName;
|
||||
let regions = data.regions;
|
||||
|
||||
const name = `qmi-copysnaps`;
|
||||
console.log(`CopySnapsAzCLI# container: ${name}`);
|
||||
var processStream = fs.createWriteStream("/logs/general/qmi-snapshots.log", {flags:'a'});
|
||||
var exec = ['copy-snapshots-to-sa', snapName, regions];
|
||||
console.log('CopySnapsAzCLI# exec: '+exec.join(" "));
|
||||
let scriptPath = path.join(PROJECT_PATH,'..');
|
||||
return docker.run(DOCKERIMAGE, exec, processStream, {
|
||||
//"Env": vars.envs,
|
||||
"name": name,
|
||||
//"WorkingDir": "/app",
|
||||
"HostConfig": {
|
||||
"Binds": [
|
||||
`${scriptPath}/copy-snapshots-to-sa.sh:/bin/copy-snapshots-to-sa`,
|
||||
`${SSHPATH}:/root/.ssh`
|
||||
]
|
||||
}
|
||||
}).then(function(data) {
|
||||
var container = data[1];
|
||||
console.log(`CopySnapsAzCLI# '${name}' (${container.id}) has finished with code: ${data[0].StatusCode}`);
|
||||
return container.remove().then(function(){
|
||||
console.log(`CopySnapsAzCLI# Container '${name}' removed!`);
|
||||
return data[0].StatusCode;
|
||||
});
|
||||
}).then(async function(statusCode) {
|
||||
|
||||
return {"output": fs.readFileSync("/logs/general/qmi-snapshots.log"), "statusCode": statusCode};
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
const checkCopySnapshots = function(data) {
|
||||
|
||||
let snapName = data.snapName;
|
||||
|
||||
const name = `qmi-checkcopysnaps`;
|
||||
console.log(`CheckCopySnapsAzCLI# container: ${name}`);
|
||||
var processStream = fs.createWriteStream("/logs/general/qmi-snapshots.log", {flags:'a'});
|
||||
var exec = ['checkcopystatus', snapName];
|
||||
console.log('CheckCopySnapsAzCLI# exec: '+exec.join(" "));
|
||||
|
||||
let scriptPath = path.join(PROJECT_PATH,'..');
|
||||
|
||||
return docker.run(DOCKERIMAGE, exec, processStream, {
|
||||
//"Env": vars.envs,
|
||||
"name": name,
|
||||
//"WorkingDir": "/app",
|
||||
"HostConfig": {
|
||||
"Binds": [
|
||||
`${scriptPath}/checkcopystatus.sh:/bin/checkcopystatus`,
|
||||
`${SSHPATH}:/root/.ssh`
|
||||
]
|
||||
}
|
||||
}).then(function(data) {
|
||||
var container = data[1];
|
||||
console.log(`CheckCopySnapsAzCLI# '${name}' (${container.id}) has finished with code: ${data[0].StatusCode}`);
|
||||
return container.remove().then(function(){
|
||||
console.log(`CheckCopySnapsAzCLI# Container '${name}' removed!`);
|
||||
return data[0].StatusCode;
|
||||
});
|
||||
}).then(async function(statusCode) {
|
||||
|
||||
return {"output": fs.readFileSync("/logs/general/qmi-snapshots.log"), "statusCode": statusCode};
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
module.exports.pause = pause;
|
||||
module.exports.resume = resume;
|
||||
module.exports.copySnapshots = copySnapshots;
|
||||
module.exports.checkCopySnapshots = checkCopySnapshots;
|
||||
@@ -3,11 +3,11 @@ const docker = new Docker({
|
||||
'socketPath': '/home/docker.sock'
|
||||
});
|
||||
const fs = require('fs');
|
||||
const config = require('qmi-cloud-common/config');
|
||||
const GIT_SCENARIOS = process.env.GIT_SCENARIOS;
|
||||
const GIT_TAG = process.env.GIT_TAG || "master";
|
||||
const DOCKERIMAGE = process.env.DOCKERIMAGE_TERRAFORM || "qlikgear/terraform:1.0.1";
|
||||
const SSHPATH = process.env.SSHPATH;
|
||||
const AWS_ACCESS_KEY_ID = process.env.AWS_ACCESS_KEY_ID;
|
||||
const AWS_SECRET_ACCESS_KEY = process.env.AWS_SECRET_ACCESS_KEY;
|
||||
|
||||
function hook_stdout(callback) {
|
||||
var old_write = process.stdout.write
|
||||
@@ -24,81 +24,53 @@ function hook_stdout(callback) {
|
||||
}
|
||||
}
|
||||
|
||||
function _buildVarsExec( exec, provision, scenario ) {
|
||||
function _buildVarsExec( exec, provision ) {
|
||||
|
||||
let prefix = provision.scenario.toUpperCase();
|
||||
|
||||
prefix = prefix.replace(/AZQMI/g, 'QMI');
|
||||
exec.push('-var');
|
||||
exec.push(`prefix=${prefix}`);
|
||||
|
||||
if ( provision.deployOpts && provision.deployOpts.subsId ) {
|
||||
exec.push('-var');
|
||||
exec.push(`subscription_id=${provision.deployOpts.subsId}`);
|
||||
}
|
||||
//Deprecated
|
||||
else if ( scenario && scenario.subscription && scenario.subscription.subsId ) {
|
||||
exec.push('-var');
|
||||
exec.push(`subscription_id=${scenario.subscription.subsId}`);
|
||||
let gitBranch = GIT_TAG;
|
||||
if ( provision._scenarioDoc && provision._scenarioDoc.gitBranch && provision._scenarioDoc.gitBranch.trim() !== "") {
|
||||
gitBranch = provision._scenarioDoc.gitBranch;
|
||||
}
|
||||
let prefix = provision.scenario.toUpperCase();
|
||||
prefix = prefix.replace(/AZQMI/g, 'QMI');
|
||||
|
||||
let envs = [
|
||||
`AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}`,
|
||||
`AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}`,
|
||||
`AWS_DEFAULT_REGION=us-east-1`,
|
||||
`TF_VAR_envbranch=${gitBranch}`,
|
||||
`TF_VAR_user_email=${provision.user.upn}`,
|
||||
`TF_VAR_user_oid=${provision.user.oid}`,
|
||||
`TF_VAR_prefix=${prefix}`
|
||||
];
|
||||
|
||||
if ( provision.deployOpts ) {
|
||||
|
||||
if ( provision.deployOpts.subsId ) {
|
||||
exec.push('-var');
|
||||
exec.push(`subscription_id=${provision.deployOpts.subsId}`);
|
||||
}
|
||||
|
||||
if ( provision.deployOpts.location ) {
|
||||
exec.push('-var');
|
||||
exec.push(`location=${provision.deployOpts.location}`);
|
||||
}
|
||||
|
||||
if ( provision.deployOpts.vnetExists ) {
|
||||
exec.push('-var');
|
||||
exec.push(`subnet_id=${provision.deployOpts.subnetId}`);
|
||||
|
||||
envs.push(`TF_VAR_subnet_id=${provision.deployOpts.subnetId}`);
|
||||
if ( provision.isExternalAccess ) {
|
||||
exec.push('-var');
|
||||
exec.push(`app_gw_subnet=${provision.deployOpts.appGwSubnetId}`);
|
||||
envs.push(`TF_VAR_app_gw_subnet=${provision.deployOpts.appGwSubnetId}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//Deprecated
|
||||
else if ( scenario && scenario.subscription && scenario.subscription.vnetExists ) {
|
||||
exec.push('-var');
|
||||
exec.push(`subnet_id=${scenario.subscription.subnetId}`);
|
||||
|
||||
if ( scenario.isExternal || provision.isExternalAccess) {
|
||||
exec.push('-var');
|
||||
exec.push(`app_gw_subnet=${scenario.subscription.appGwSubnetId}`);
|
||||
}
|
||||
}
|
||||
|
||||
exec.push('-var');
|
||||
exec.push(`provision_id=${provision._id}`);
|
||||
exec.push('-var');
|
||||
exec.push(`user_id=${provision.user.displayName}`);
|
||||
|
||||
if ( provision.scenario.indexOf('azqmi-qdi') !== -1 && provision.version && provision.version >= config.PROVISION_VERSION ) {
|
||||
exec.push('-var');
|
||||
exec.push(`user_email=${provision.user.upn}`);
|
||||
}
|
||||
|
||||
if ( provision.scenario.indexOf('azqmi-synapse') !== -1 || provision.scenario.indexOf('azqmi-qdi') !== -1 ) {
|
||||
exec.push('-var');
|
||||
exec.push(`user_oid=${provision.user.oid}`);
|
||||
}
|
||||
|
||||
if (!provision.vmImage) {
|
||||
//Old way
|
||||
if ( provision.vmType ) {
|
||||
exec.push('-var');
|
||||
exec.push(`vm_type=${provision.vmType}`);
|
||||
}
|
||||
|
||||
if ( provision.nodeCount) {
|
||||
exec.push('-var');
|
||||
exec.push(`agent_count=${provision.nodeCount}`);
|
||||
}
|
||||
} else if ( provision.vmImage ) {
|
||||
//New way
|
||||
|
||||
//DEPRECATED VMIMAGE
|
||||
if ( provision.vmImage ) {
|
||||
for ( let key in provision.vmImage ) {
|
||||
|
||||
if ( !provision.vmImage[key].disabled ) {
|
||||
@@ -133,6 +105,50 @@ function _buildVarsExec( exec, provision, scenario ) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NEW ATTRS
|
||||
if ( provision.options ) {
|
||||
for ( let key in provision.options ) {
|
||||
|
||||
if ( !provision.options[key].disabled ) {
|
||||
|
||||
if ( key.indexOf("vm") !== -1 ) {
|
||||
if ( provision.options[key].nodeCount ) {
|
||||
exec.push('-var');
|
||||
exec.push(`agent_count_${key}=${provision[key].nodeCount}`);
|
||||
}
|
||||
if ( provision.options[key].vmType ) {
|
||||
exec.push('-var');
|
||||
exec.push(`vm_type_${key}=${provision.options[key].vmType}`);
|
||||
}else {
|
||||
exec.push('-var');
|
||||
exec.push(`vm_type_${key}=ENABLED`);
|
||||
}
|
||||
if ( provision.options[key].diskSizeGb ) {
|
||||
exec.push('-var');
|
||||
exec.push(`disk_size_gb_${key}=${provision.options[key].diskSizeGb}`);
|
||||
}
|
||||
if ( provision.options[key].selected && provision.options[key].selected.value) {
|
||||
exec.push('-var');
|
||||
exec.push(`image_reference_${key}=${provision.options[key].selected.value}`);
|
||||
}
|
||||
|
||||
if ( provision.options[key].selected && provision.options[key].selected.init_password) {
|
||||
exec.push('-var');
|
||||
exec.push(`image_reference_${key}_init_password=${provision.options[key].selected.init_password}`);
|
||||
}
|
||||
} else {
|
||||
if ( provision.options[key].selected && provision.options[key].selected.value) {
|
||||
exec.push('-var');
|
||||
exec.push(`attr_${key}=${provision.options[key].selected.value}`);
|
||||
} else {
|
||||
exec.push('-var');
|
||||
exec.push(`attr_${key}=ENABLED`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( provision.isExternalAccess ) {
|
||||
exec.push('-var');
|
||||
@@ -157,7 +173,7 @@ function _buildVarsExec( exec, provision, scenario ) {
|
||||
}
|
||||
}
|
||||
|
||||
return exec;
|
||||
return {"exec": exec, "envs": envs};
|
||||
}
|
||||
|
||||
const init = function( provision ) {
|
||||
@@ -171,10 +187,9 @@ const init = function( provision ) {
|
||||
}
|
||||
let exec = ['terraform', 'init', '-no-color', `-from-module=${GIT_SCENARIOS}//${provision.scenario}?ref=${gitBranch}`];
|
||||
console.log('Terraform# Init: exec: '+exec.join(" "));
|
||||
var terraformImage = provision.terraformImage? provision.terraformImage : DOCKERIMAGE;
|
||||
console.log('Terraform# Init: version to use is : '+terraformImage);
|
||||
return docker.run(terraformImage, exec, processStream, {
|
||||
//"Env": ["VAR_ENV=whatever"],
|
||||
console.log('Terraform# Init: version to use is : '+provision.terraformImage);
|
||||
return docker.run(provision.terraformImage, exec, processStream, {
|
||||
//"Env": [],
|
||||
"name": name,
|
||||
"WorkingDir": "/app",
|
||||
"HostConfig": {
|
||||
@@ -195,19 +210,19 @@ const init = function( provision ) {
|
||||
});
|
||||
};
|
||||
|
||||
const plan = function( provision, scenario ) {
|
||||
const plan = function( provision ) {
|
||||
|
||||
const name = `qmi-tf-plan-${provision._id}`;
|
||||
console.log(`Terraform# Plan: will spin up container: ${name}`);
|
||||
var processStream = fs.createWriteStream(provision.logFile, {flags:'a'});
|
||||
//var processStream = process.stdout;
|
||||
var exec = ['terraform', 'plan', '-no-color', '-input=false', '-out=tfplan' ];
|
||||
exec = _buildVarsExec(exec, provision, scenario);
|
||||
var vars = _buildVarsExec(exec, provision);
|
||||
exec = vars.exec;
|
||||
console.log('Terraform# Plan: exec: '+exec.join(" "));
|
||||
var terraformImage = provision.terraformImage? provision.terraformImage : DOCKERIMAGE;
|
||||
console.log('Terraform# Plan: version to use is : '+terraformImage);
|
||||
return docker.run(terraformImage, exec, processStream, {
|
||||
//"Env": ["VAR_ENV=whatever"],
|
||||
console.log('Terraform# Plan: version to use is : '+provision.terraformImage);
|
||||
return docker.run(provision.terraformImage, exec, processStream, {
|
||||
"Env": vars.envs,
|
||||
"name": name,
|
||||
"WorkingDir": "/app",
|
||||
"HostConfig": {
|
||||
@@ -238,10 +253,13 @@ const apply = function( provision ) {
|
||||
|
||||
var exec = ['terraform', 'apply', 'tfplan', '-no-color'];
|
||||
console.log('Terraform# Apply: exec: '+exec.join(" "));
|
||||
var terraformImage = provision.terraformImage? provision.terraformImage : DOCKERIMAGE;
|
||||
console.log('Terraform# Apply: version to use is : '+terraformImage);
|
||||
return docker.run(terraformImage, exec, processStream, {
|
||||
//"Env": ["VAR_ENV=whatever"],
|
||||
console.log('Terraform# Apply: version to use is : '+provision.terraformImage);
|
||||
return docker.run(provision.terraformImage, exec, processStream, {
|
||||
"Env": [
|
||||
`AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}`,
|
||||
`AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}`,
|
||||
`AWS_DEFAULT_REGION=us-east-1`
|
||||
],
|
||||
"name": name,
|
||||
"WorkingDir": "/app",
|
||||
"HostConfig": {
|
||||
@@ -263,20 +281,19 @@ const apply = function( provision ) {
|
||||
})
|
||||
}
|
||||
|
||||
const destroy = function(destroyMongo, provision, scenario) {
|
||||
const destroy = function(destroyMongo, provision) {
|
||||
|
||||
const name = `qmi-tf-destroy-${destroyMongo._id}`;
|
||||
console.log(`Terraform# Destroy: will spin up container: ${name}`);
|
||||
var processStream = fs.createWriteStream(destroyMongo.logFile, {flags:'a'});
|
||||
var exec = ['terraform', 'destroy', '-auto-approve', '-no-color'];
|
||||
exec = _buildVarsExec(exec, provision, scenario);
|
||||
var vars = _buildVarsExec(exec, provision);
|
||||
exec = vars.exec;
|
||||
console.log('Terraform# Destroy: exec: '+exec.join(" "));
|
||||
|
||||
var terraformImage = provision.terraformImage? provision.terraformImage : DOCKERIMAGE;
|
||||
console.log('Terraform# Destroy: version to use is : '+terraformImage);
|
||||
|
||||
return docker.run(terraformImage, exec, processStream, {
|
||||
//"Env": ["VAR_ENV=whatever"],
|
||||
console.log('Terraform# Destroy: version to use is : '+provision.terraformImage);
|
||||
|
||||
return docker.run(provision.terraformImage, exec, processStream, {
|
||||
"Env": vars.envs,
|
||||
"name": name,
|
||||
"WorkingDir": "/app",
|
||||
"HostConfig": {
|
||||
@@ -311,11 +328,8 @@ const outputs = function(provision) {
|
||||
var unhook = hook_stdout(function(string, encoding, fd) {
|
||||
tfout += string.trim();
|
||||
});
|
||||
|
||||
var terraformImage = provision.terraformImage? provision.terraformImage : DOCKERIMAGE;
|
||||
|
||||
return docker.run(terraformImage, exec, process.stdout, {
|
||||
//"Env": ["VAR_ENV=whatever"],
|
||||
return docker.run(provision.terraformImage, exec, process.stdout, {
|
||||
//"Env": [],
|
||||
"name": name,
|
||||
"WorkingDir": "/app",
|
||||
"HostConfig": {
|
||||
|
||||
302
qmi-cloud-worker/guacamole.js
Normal file
@@ -0,0 +1,302 @@
|
||||
'use strict';
|
||||
const axios = require('axios');
|
||||
const https = require("https");
|
||||
const db = require('qmi-cloud-common/mongo');
|
||||
const URL = "https://qmicloud-dev.qliktech.com:8443/api/session/data/postgresql";
|
||||
const GUACA_USERNAME = process.env.GUACA_USERNAME || "guacadmin";
|
||||
const GUACA_PASSWORD = process.env.GUACA_PASSWORD;
|
||||
|
||||
|
||||
const base64urlEncode = function(value) {
|
||||
|
||||
// Translate padded standard base64 to unpadded base64url
|
||||
return Buffer.from(value).toString('base64').replace(/[+/=]/g,
|
||||
(str) => ({
|
||||
'+' : '-',
|
||||
'/' : '_',
|
||||
'=' : ''
|
||||
})[str]
|
||||
);
|
||||
|
||||
};
|
||||
|
||||
const guacamoleClientId = function(id, type, dataSource) {
|
||||
return base64urlEncode([
|
||||
id,
|
||||
type,
|
||||
dataSource
|
||||
].join('\0'));
|
||||
};
|
||||
|
||||
async function _auth() {
|
||||
try {
|
||||
const params = new URLSearchParams();
|
||||
params.append('username', GUACA_USERNAME);
|
||||
params.append('password', GUACA_PASSWORD);
|
||||
|
||||
var res = await axios.post('https://qmicloud-dev.qliktech.com:8443/api/tokens', params, {
|
||||
httpsAgent: new https.Agent({
|
||||
rejectUnauthorized: false
|
||||
})
|
||||
});
|
||||
|
||||
return res.data.authToken;
|
||||
|
||||
|
||||
} catch (err) {
|
||||
// Handle Error Here
|
||||
console.log("Guacamole# Could not auth", err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function _createUser(email, token) {
|
||||
try {
|
||||
var body = {
|
||||
"username": email,
|
||||
"attributes": {
|
||||
"disabled": "",
|
||||
"expired": "",
|
||||
"access-window-start": "",
|
||||
"access-window-end": "",
|
||||
"valid-from": "",
|
||||
"valid-until": "",
|
||||
"timezone": null
|
||||
}
|
||||
};
|
||||
var res = await axios({
|
||||
url: `${URL}/users`,
|
||||
method: "post",
|
||||
data: body,
|
||||
httpsAgent: new https.Agent({
|
||||
rejectUnauthorized: false
|
||||
}),
|
||||
headers: {
|
||||
'Guacamole-Token': token
|
||||
}
|
||||
});
|
||||
|
||||
console.log("Guacamole# User created: ", email);
|
||||
|
||||
return email;
|
||||
|
||||
|
||||
} catch (err) {
|
||||
// Handle Error Here
|
||||
|
||||
console.log("Guacamole# User already existed: ", email);
|
||||
|
||||
return email;
|
||||
}
|
||||
}
|
||||
|
||||
async function _createConnection(type, name, ip, username, password, token) {
|
||||
try {
|
||||
var body = {
|
||||
"parentIdentifier": "ROOT",
|
||||
"name": name,
|
||||
"protocol": type,
|
||||
"parameters": {
|
||||
"port": type === "ssh"? "22" : "3389",
|
||||
"read-only": "",
|
||||
"swap-red-blue": "",
|
||||
"cursor": "",
|
||||
"color-depth": "",
|
||||
"force-lossless": "",
|
||||
"clipboard-encoding": "",
|
||||
"disable-copy": "",
|
||||
"disable-paste": "",
|
||||
"dest-port": "",
|
||||
"recording-exclude-output": "",
|
||||
"recording-exclude-mouse": "",
|
||||
"recording-include-keys": "",
|
||||
"create-recording-path": "",
|
||||
"enable-sftp": "",
|
||||
"sftp-port": "",
|
||||
"sftp-server-alive-interval": "",
|
||||
"sftp-disable-download": "",
|
||||
"sftp-disable-upload": "",
|
||||
"enable-audio": "",
|
||||
"wol-send-packet": "",
|
||||
"wol-udp-port": "",
|
||||
"wol-wait-time": "",
|
||||
"security": "nla",
|
||||
"disable-auth": "",
|
||||
"ignore-cert": "true",
|
||||
"gateway-port": "",
|
||||
"server-layout": "",
|
||||
"timezone": null,
|
||||
"enable-touch": "",
|
||||
"console": "",
|
||||
"width": "",
|
||||
"height": "",
|
||||
"dpi": "",
|
||||
"resize-method": "",
|
||||
"normalize-clipboard": "",
|
||||
"console-audio": "",
|
||||
"disable-audio": "",
|
||||
"enable-audio-input": "",
|
||||
"enable-printing": "",
|
||||
"enable-drive": "",
|
||||
"disable-download": "",
|
||||
"disable-upload": "",
|
||||
"create-drive-path": "",
|
||||
"enable-wallpaper": "",
|
||||
"enable-theming": "",
|
||||
"enable-font-smoothing": "",
|
||||
"enable-full-window-drag": "",
|
||||
"enable-desktop-composition": "",
|
||||
"enable-menu-animations": "",
|
||||
"disable-bitmap-caching": "",
|
||||
"disable-offscreen-caching": "",
|
||||
"disable-glyph-caching": "",
|
||||
"preconnection-id": "",
|
||||
"recording-exclude-touch": "",
|
||||
"hostname": ip,
|
||||
"username": username,
|
||||
"password": password
|
||||
},
|
||||
"attributes": {
|
||||
"max-connections": "2",
|
||||
"max-connections-per-user": "2",
|
||||
"weight": "",
|
||||
"failover-only": "",
|
||||
"guacd-port": "",
|
||||
"guacd-encryption": ""
|
||||
}
|
||||
};
|
||||
var res = await axios({
|
||||
url: `${URL}/connections`,
|
||||
method: "post",
|
||||
data: body,
|
||||
httpsAgent: new https.Agent({
|
||||
rejectUnauthorized: false
|
||||
}),
|
||||
headers: {
|
||||
'Guacamole-Token': token
|
||||
}
|
||||
});
|
||||
|
||||
console.log("Guacamole# Connection created: ", name);
|
||||
|
||||
return res.data;
|
||||
|
||||
|
||||
} catch (err) {
|
||||
// Handle Error Here
|
||||
console.log("Guacamole# Could not create Guaca Connection", err.config.data);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function _addConnectionToUser(email, identifier, token) {
|
||||
try {
|
||||
var body = [
|
||||
{
|
||||
"op": "add",
|
||||
"path": "/connectionPermissions/"+identifier,
|
||||
"value": "READ"
|
||||
}
|
||||
];
|
||||
var res = await axios({
|
||||
url: `${URL}/users/${email}/permissions`,
|
||||
method: "patch",
|
||||
data: body,
|
||||
httpsAgent: new https.Agent({
|
||||
rejectUnauthorized: false
|
||||
}),
|
||||
headers: {
|
||||
'Guacamole-Token': token
|
||||
}
|
||||
});
|
||||
|
||||
console.log("Guacamole# Connection identifier ("+identifier+") added to user: ", email);
|
||||
|
||||
return res.data;
|
||||
|
||||
} catch (err) {
|
||||
// Handle Error Here
|
||||
console.log("Guacamole# Could not add connection "+identifier+" to Guaca User " + email);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function setUserConnection(provision, scenario){
|
||||
|
||||
var resultProvision = provision;
|
||||
|
||||
if ( provision && provision.status === 'provisioned' && provision.options && provision.options.vm1 ) {
|
||||
var token = await _auth();
|
||||
|
||||
if (token) {
|
||||
await _createUser(provision.user.mail, token);
|
||||
|
||||
|
||||
let ip = provision.outputs["RDP-ip"] || provision.outputs["Replicate__RDP_IP"] || provision.outputs["Private_IP"] || provision.outputs["IP"];
|
||||
let credentials = provision.outputs["RDP-credentials"] || provision.outputs["Replicate__RDP-credentials"] || provision.outputs["User-and-Password"] || provision.outputs["All_DBs_User-and-Password"];
|
||||
|
||||
if (ip && credentials){
|
||||
let username = credentials.split(" / ")[0];
|
||||
let password = credentials.split(" / ")[1];
|
||||
|
||||
if (username && password){
|
||||
let connection;
|
||||
let connName = `(${scenario.name}_${provision._id}) - ${provision.description}`;
|
||||
let type;
|
||||
if ( scenario.labels.indexOf("linux") === -1 ) {
|
||||
connection = await _createConnection("rdp",connName, ip, username, password, token);
|
||||
type = "RDP";
|
||||
} else {
|
||||
connection = await _createConnection("ssh", connName, ip, username, password, token);
|
||||
type = "SSH";
|
||||
}
|
||||
if (connection) {
|
||||
let outputs = provision.outputs || {};
|
||||
const guacClient = guacamoleClientId(connection.identifier,"c", "postgresql");
|
||||
outputs[`WEB_${type}_ACCESS_WITH_GUACAMOLE`] = `https://qmicloud-dev.qliktech.com:8443/#/client/${guacClient}`;
|
||||
|
||||
_addConnectionToUser(provision.user.mail, connection.identifier, token);
|
||||
|
||||
resultProvision = await db.provision.update(provision._id, {"guacaConnId": connection.identifier, outputs: outputs});
|
||||
|
||||
} else {
|
||||
console.log("Guacamole# No connection was created for provision: ", provision._id);
|
||||
}
|
||||
} else {
|
||||
console.log("Guacamole# Could not find username or password for provision:", provision._id);
|
||||
}
|
||||
} else {
|
||||
console.log("Guacamole# Could not find ip or credentials for provision:", provision._id);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return resultProvision;
|
||||
|
||||
}
|
||||
|
||||
async function deleteConnection(provision){
|
||||
if ( provision.guacaConnId ) {
|
||||
try {
|
||||
var token = await _auth();
|
||||
await axios({
|
||||
url: `${URL}/connections/${provision.guacaConnId}`,
|
||||
method: "delete",
|
||||
httpsAgent: new https.Agent({
|
||||
rejectUnauthorized: false
|
||||
}),
|
||||
headers: {
|
||||
'Guacamole-Token': token
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
// Handle Error Here
|
||||
console.log("Guacamole# Could not delete connection ("+provision.guacaConnId+")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.setUserConnection = setUserConnection;
|
||||
module.exports.deleteConnection = deleteConnection;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CONTAINER_QUEUE } from 'qmi-cloud-common/queues';
|
||||
import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CONTAINER_QUEUE, SYNAPSE_QUEUE } from 'qmi-cloud-common/queues';
|
||||
|
||||
var path = require("path");
|
||||
|
||||
@@ -7,6 +7,7 @@ queues[TF_APPLY_QUEUE].process("tf_apply_job", 10, path.resolve(__dirname, 'proc
|
||||
queues[TF_APPLY_QSEOK_QUEUE].process("tf_apply_qseok_job", 10, path.resolve(__dirname, 'processor-apply-qseok.js'));
|
||||
queues[TF_DESTROY_QUEUE].process("tf_destroy_job", 10, path.resolve(__dirname, 'processor-destroy.js'));
|
||||
queues[STOP_CONTAINER_QUEUE].process("tf_abort_apply_job", 10, path.resolve(__dirname, 'processor-stop-container.js'));
|
||||
queues[SYNAPSE_QUEUE].process("synapse_job", 10, path.resolve(__dirname, 'processor-synapse.js'));
|
||||
|
||||
|
||||
console.log(`Worker queues started!`);
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "qmi-cloud-worker",
|
||||
"version": "1.3.1",
|
||||
"version": "2.1.0",
|
||||
"scripts": {
|
||||
"start": "node -r esm index.js",
|
||||
"start:dev": "nodemon -r esm index.js",
|
||||
|
||||
@@ -3,9 +3,13 @@ const db = require('qmi-cloud-common/mongo');
|
||||
const path = require('path');
|
||||
const sendEmail = require("qmi-cloud-common/send-email");
|
||||
const barracuda = require("qmi-cloud-common/barracuda");
|
||||
const guaca = require("./guacamole");
|
||||
|
||||
|
||||
module.exports = async function(job){
|
||||
|
||||
var triggerUser = job.data.user;
|
||||
|
||||
var destroyMongo = await db.destroy.update(job.data.id, {
|
||||
"status": "destroying",
|
||||
"jobId": job.id,
|
||||
@@ -27,34 +31,44 @@ module.exports = async function(job){
|
||||
console.log(`ProcessorDestroy# There is no barracuda to be deleted` );
|
||||
}
|
||||
|
||||
db.event.add({ user: provMongo.user._id, provision: provMongo._id, type: 'provision.destroy-init' });
|
||||
db.event.add({ user: triggerUser._id, provision: provMongo._id, type: 'provision.destroy-init' });
|
||||
|
||||
return tf.destroy(destroyMongo, provMongo, job.data._scenario)
|
||||
.then(async function(res) {
|
||||
return tf.destroy(destroyMongo, provMongo).then(async function(res) {
|
||||
let update, update2;
|
||||
if ( res.output.indexOf("Error:") !== -1 ) {
|
||||
update = await db.destroy.update(destroyMongo._id,{"status": "error"});
|
||||
update2 = await db.provision.update(provMongo._id, {"isDestroyed": false});
|
||||
|
||||
} else {
|
||||
update = await db.destroy.update(destroyMongo._id, {"status": "destroyed"});
|
||||
let timeRunning = db.utils.getNewTimeRunning(provMongo);
|
||||
update2 = await db.provision.update(provMongo._id, {"isDestroyed": true, "timeRunning": timeRunning, "pendingNextAction": undefined, "actualDestroyDate": new Date()});
|
||||
update2 = await db.provision.update(provMongo._id, {"isDestroyed": true, "timeRunning": timeRunning, "pendingNextAction": null, "actualDestroyDate": new Date()});
|
||||
sendEmail.sendDestroyedSuccess(update2, job.data._scenario);
|
||||
guaca.deleteConnection(provMongo);
|
||||
|
||||
|
||||
//console.log(`ProcessorDestroy# Deleting shares of this provision` );
|
||||
//db.sharedProvision.delMany({"provision":update2._id});
|
||||
}
|
||||
return { destroy: update, provision: update2 };
|
||||
}).then(async function(res) {
|
||||
console.log(`ProcessorDestroy# Provision destroyed!` );
|
||||
if ( res.provision.scenario === "azqmi-qdi" ) {
|
||||
let tempApiKey = await db.apiKey.getOne({"description": res.provision._id});
|
||||
if (tempApiKey && tempApiKey._id){
|
||||
db.apiKey.del(tempApiKey._id);
|
||||
if ( res.provision.isDestroyed ) {
|
||||
console.log(`ProcessorDestroy# Provision (${res.provision._id}) destroyed!` );
|
||||
db.event.add({ provision: provMongo._id, type: 'provision.destroy-finished' });
|
||||
if ( res.provision.scenario === "azqmi-qdi" ) {
|
||||
let tempApiKey = await db.apiKey.getOne({"description": res.provision._id});
|
||||
if (tempApiKey && tempApiKey._id){
|
||||
db.apiKey.del(tempApiKey._id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(`ProcessorDestroy# Provision (${res.provision._id}) NOT destroyed - Error!` );
|
||||
db.event.add({ provision: provMongo._id, type: 'provision.destroy-error' });
|
||||
}
|
||||
db.event.add({ user: provMongo.user._id, provision: provMongo._id, type: 'provision.destroy-finished' });
|
||||
return Promise.resolve({"success": true, job: res});
|
||||
}).catch(function(err) {
|
||||
console.log("ProcessorDestroy# err", err);
|
||||
db.event.add({ user: provMongo.user._id, provision: provMongo._id, type: 'provision.destroy-error' });
|
||||
db.event.add({ provision: provMongo._id, type: 'provision.destroy-error' });
|
||||
db.destroy.update(destroyMongo._id, {"status": "error", "isDestroyed": false});
|
||||
return Promise.reject({"success": false, "err": err});
|
||||
});
|
||||
|
||||
67
qmi-cloud-worker/processor-synapse.js
Normal file
@@ -0,0 +1,67 @@
|
||||
const db = require('qmi-cloud-common/mongo');
|
||||
const synapse = require("./docker/synapse");
|
||||
|
||||
module.exports = async function(job) {
|
||||
|
||||
console.log(`ProcessorAzCLI# will execute ${job.data.tasktype} task`);
|
||||
|
||||
if (job.data.tasktype === 'pause') {
|
||||
|
||||
var prov = await db.provision.getById(job.data.provId);
|
||||
|
||||
if ( !prov ) {
|
||||
console.log(`ProcessorAzCLI# Error: Not found Provision object in Database (it should exist!), provisionId is: ${job.data.id}` );
|
||||
return Promise.reject({"success": false, "err": "Not found Provision object in Worker"});
|
||||
}
|
||||
|
||||
// Synapse Pause
|
||||
db.provision.update(prov._id, {"statusVms": "Stopping"});
|
||||
return synapse.pause(prov)
|
||||
.then( function(res) {
|
||||
db.provision.update(prov._id, {"statusVms": "Stopped"});
|
||||
return Promise.resolve( { "success": true, "output": res });
|
||||
} ).catch( function(err) {
|
||||
console.log("ProcessorAzCLI# Error:", err);
|
||||
db.provision.update(prov._id, {"statusVms": "Running"});
|
||||
return Promise.reject({"success": false, "error": err});
|
||||
} );
|
||||
} else if (job.data.tasktype === 'resume') {
|
||||
|
||||
var prov = await db.provision.getById(job.data.provId);
|
||||
|
||||
if ( !prov ) {
|
||||
console.log(`ProcessorAzCLI# Error: Not found Provision object in Database (it should exist!), provisionId is: ${job.data.id}` );
|
||||
return Promise.reject({"success": false, "err": "Not found Provision object in Worker"});
|
||||
}
|
||||
|
||||
// Synapse Resume
|
||||
db.provision.update(prov._id, {"statusVms": "Starting"});
|
||||
return synapse.resume(prov)
|
||||
.then( function(res) {
|
||||
db.provision.update(prov._id, {"statusVms": "Running"});
|
||||
return Promise.resolve( { "success": true, "output": res });
|
||||
} ).catch( function(err) {
|
||||
console.log("ProcessorAzCLI# Error:", err);
|
||||
db.provision.update(prov._id, {"statusVms": "Stopped"});
|
||||
return Promise.reject({"success": false, "error": err});
|
||||
} );
|
||||
} else if (job.data.tasktype === 'copy-snapshots') {
|
||||
|
||||
return synapse.copySnapshots(job.data)
|
||||
.then( function(res) {
|
||||
return Promise.resolve( { "success": true, "output": res });
|
||||
} ).catch( function(err) {
|
||||
console.log("ProcessorAzCLI# Error:", err);
|
||||
return Promise.reject({"success": false, "error": err});
|
||||
} );
|
||||
} else if (job.data.tasktype === 'check-copy-snapshots') {
|
||||
|
||||
return synapse.checkCopySnapshots(job.data)
|
||||
.then( function(res) {
|
||||
return Promise.resolve( { "success": true, "output": res });
|
||||
} ).catch( function(err) {
|
||||
console.log("ProcessorAzCLI# Error:", err);
|
||||
return Promise.reject({"success": false, "error": err});
|
||||
} );
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,16 @@
|
||||
"@azure/ms-rest-js" "^2.0.4"
|
||||
tslib "^1.10.0"
|
||||
|
||||
"@azure/arm-dns@^4.0.0":
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@azure/arm-dns/-/arm-dns-4.1.1.tgz#50ad92fd2b292da7753a7f39df2d3c5f3789ffb5"
|
||||
integrity sha512-6AW0gy5v5hugLwtVM8F7Hx1yLmqbs2qmEoTwtbnG7X/6TYQtrWL4CwM7tQ+Ltu0PPYA/swDMH6vtOAUpmd3XFA==
|
||||
dependencies:
|
||||
"@azure/core-auth" "^1.1.4"
|
||||
"@azure/ms-rest-azure-js" "^2.1.0"
|
||||
"@azure/ms-rest-js" "^2.2.0"
|
||||
tslib "^1.10.0"
|
||||
|
||||
"@azure/core-auth@^1.1.4":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.2.0.tgz#a5a181164e99f8446a3ccf9039345ddc9bb63bb9"
|
||||
@@ -31,7 +41,7 @@
|
||||
resolved "https://registry.yarnpkg.com/@azure/ms-rest-azure-env/-/ms-rest-azure-env-2.0.0.tgz#45809f89763a480924e21d3c620cd40866771625"
|
||||
integrity sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw==
|
||||
|
||||
"@azure/ms-rest-azure-js@^2.0.1":
|
||||
"@azure/ms-rest-azure-js@^2.0.1", "@azure/ms-rest-azure-js@^2.1.0":
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@azure/ms-rest-azure-js/-/ms-rest-azure-js-2.1.0.tgz#8c90b31468aeca3146b06c7144b386fd4827f64c"
|
||||
integrity sha512-CjZjB8apvXl5h97Ck6SbeeCmU0sk56YPozPtTyGudPp1RGoHXNjFNtoOvwOG76EdpmMpxbK10DqcygI16Lu60Q==
|
||||
@@ -103,6 +113,19 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/webidl-conversions@*":
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz#2b8e60e33906459219aa587e9d1a612ae994cfe7"
|
||||
integrity sha512-xTE1E+YF4aWPJJeUzaZI5DRntlkY3+BCVJi0axFptnjGmAoWxkyREIh/XMrfxVLejwQxMCfDXdICo0VLxThrog==
|
||||
|
||||
"@types/whatwg-url@^8.2.1":
|
||||
version "8.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/whatwg-url/-/whatwg-url-8.2.2.tgz#749d5b3873e845897ada99be4448041d4cc39e63"
|
||||
integrity sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
"@types/webidl-conversions" "*"
|
||||
|
||||
abbrev@1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||
@@ -224,6 +247,27 @@ atob@^2.1.2:
|
||||
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
|
||||
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
|
||||
|
||||
available-typed-arrays@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
|
||||
integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
|
||||
|
||||
aws-sdk@^2.942.0:
|
||||
version "2.1374.0"
|
||||
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1374.0.tgz#f915dd6a946cab810d1bd46ca14892e985ce69ab"
|
||||
integrity sha512-Q7TExvBskRM23GfE1iy0GRxpUF+d9LY7ggmdHvL+2gX/QTzxSYaHHRPa8MNzFtzZLz+BDcDIgAcMjYKsrm18Rw==
|
||||
dependencies:
|
||||
buffer "4.9.2"
|
||||
events "1.1.1"
|
||||
ieee754 "1.1.13"
|
||||
jmespath "0.16.0"
|
||||
querystring "0.2.0"
|
||||
sax "1.2.1"
|
||||
url "0.10.3"
|
||||
util "^0.12.4"
|
||||
uuid "8.0.0"
|
||||
xml2js "0.5.0"
|
||||
|
||||
aws-sign2@~0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
|
||||
@@ -246,6 +290,12 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||
|
||||
"barracuda-api@https://gitlab.com/qlik_gear/barracuda-api-node.git#1.1.0":
|
||||
version "0.0.10"
|
||||
resolved "https://gitlab.com/qlik_gear/barracuda-api-node.git#ce8be42102398a45bf896cf973290b46d2e18666"
|
||||
dependencies:
|
||||
axios "^0.21.1"
|
||||
|
||||
base64-js@^1.0.2:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
||||
@@ -283,14 +333,6 @@ bindings@^1.5.0:
|
||||
dependencies:
|
||||
file-uri-to-path "1.0.0"
|
||||
|
||||
bl@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-2.2.0.tgz#e1a574cdf528e4053019bb800b041c0ac88da493"
|
||||
integrity sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==
|
||||
dependencies:
|
||||
readable-stream "^2.3.5"
|
||||
safe-buffer "^5.1.1"
|
||||
|
||||
bl@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.2.tgz#52b71e9088515d0606d9dd9cc7aa48dc1f98e73a"
|
||||
@@ -300,11 +342,6 @@ bl@^4.0.1:
|
||||
inherits "^2.0.4"
|
||||
readable-stream "^3.4.0"
|
||||
|
||||
bluebird@3.5.1:
|
||||
version "3.5.1"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9"
|
||||
integrity sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==
|
||||
|
||||
boxen@^1.2.1:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b"
|
||||
@@ -342,10 +379,10 @@ braces@^2.3.1, braces@^2.3.2:
|
||||
split-string "^3.0.2"
|
||||
to-regex "^3.0.1"
|
||||
|
||||
bson@^1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/bson/-/bson-1.1.4.tgz#f76870d799f15b854dffb7ee32f0a874797f7e89"
|
||||
integrity sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q==
|
||||
bson@^5.2.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.yarnpkg.com/bson/-/bson-5.2.0.tgz#c81d35dd30e2798203e5422a639780ea98dd25ba"
|
||||
integrity sha512-HevkSpDbpUfsrHWmWiAsNavANKYIErV2ePXllp1bwq5CDreAaFVj6RVlZpJnxK4WWDCJ/5jMUpaY6G526q3Hjg==
|
||||
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
version "1.0.1"
|
||||
@@ -357,6 +394,15 @@ buffer-from@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
|
||||
buffer@4.9.2:
|
||||
version "4.9.2"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8"
|
||||
integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==
|
||||
dependencies:
|
||||
base64-js "^1.0.2"
|
||||
ieee754 "^1.1.4"
|
||||
isarray "^1.0.0"
|
||||
|
||||
buffer@^5.5.0:
|
||||
version "5.6.0"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.6.0.tgz#a31749dc7d81d84db08abf937b6b8c4033f62786"
|
||||
@@ -396,6 +442,14 @@ cache-base@^1.0.1:
|
||||
union-value "^1.0.0"
|
||||
unset-value "^1.0.0"
|
||||
|
||||
call-bind@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
|
||||
integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
get-intrinsic "^1.0.2"
|
||||
|
||||
camelcase@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
|
||||
@@ -579,12 +633,12 @@ date-utils@*:
|
||||
resolved "https://registry.yarnpkg.com/date-utils/-/date-utils-1.2.21.tgz#61fb16cdc1274b3c9acaaffe9fc69df8720a2b64"
|
||||
integrity sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=
|
||||
|
||||
debug@3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
||||
debug@4.x:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
ms "2.1.2"
|
||||
|
||||
debug@^2.2.0, debug@^2.3.3:
|
||||
version "2.6.9"
|
||||
@@ -656,7 +710,7 @@ delayed-stream@~1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||
integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
|
||||
|
||||
denque@^1.1.0, denque@^1.4.1:
|
||||
denque@^1.1.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf"
|
||||
integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==
|
||||
@@ -755,6 +809,11 @@ event-target-shim@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
||||
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
|
||||
|
||||
events@1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924"
|
||||
integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==
|
||||
|
||||
execa@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
|
||||
@@ -855,6 +914,13 @@ follow-redirects@^1.10.0:
|
||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
|
||||
integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==
|
||||
|
||||
for-each@^0.3.3:
|
||||
version "0.3.3"
|
||||
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
|
||||
integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==
|
||||
dependencies:
|
||||
is-callable "^1.1.3"
|
||||
|
||||
for-in@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||
@@ -917,6 +983,15 @@ function-bind@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
|
||||
get-intrinsic@^1.0.2, get-intrinsic@^1.1.3:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.0.tgz#7ad1dc0535f3a2904bba075772763e5051f6d05f"
|
||||
integrity sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
has-symbols "^1.0.3"
|
||||
|
||||
get-port@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193"
|
||||
@@ -954,6 +1029,13 @@ global-dirs@^0.1.0:
|
||||
dependencies:
|
||||
ini "^1.3.4"
|
||||
|
||||
gopd@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
|
||||
integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.3"
|
||||
|
||||
got@^6.7.1:
|
||||
version "6.7.1"
|
||||
resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0"
|
||||
@@ -999,6 +1081,18 @@ has-symbols@^1.0.0, has-symbols@^1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
|
||||
integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
|
||||
|
||||
has-symbols@^1.0.2, has-symbols@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
|
||||
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
|
||||
|
||||
has-tostringtag@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25"
|
||||
integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==
|
||||
dependencies:
|
||||
has-symbols "^1.0.2"
|
||||
|
||||
has-value@^0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
|
||||
@@ -1046,7 +1140,7 @@ http-signature@~1.2.0:
|
||||
jsprim "^1.2.2"
|
||||
sshpk "^1.7.0"
|
||||
|
||||
ieee754@^1.1.4:
|
||||
ieee754@1.1.13, ieee754@^1.1.4:
|
||||
version "1.1.13"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
|
||||
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
|
||||
@@ -1096,6 +1190,11 @@ ip-regex@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
|
||||
integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
|
||||
|
||||
ip@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da"
|
||||
integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==
|
||||
|
||||
is-accessor-descriptor@^0.1.6:
|
||||
version "0.1.6"
|
||||
resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6"
|
||||
@@ -1110,6 +1209,14 @@ is-accessor-descriptor@^1.0.0:
|
||||
dependencies:
|
||||
kind-of "^6.0.0"
|
||||
|
||||
is-arguments@^1.0.4:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b"
|
||||
integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==
|
||||
dependencies:
|
||||
call-bind "^1.0.2"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-binary-path@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898"
|
||||
@@ -1122,6 +1229,11 @@ is-buffer@^1.1.5:
|
||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
||||
|
||||
is-callable@^1.1.3:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
|
||||
integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
|
||||
|
||||
is-callable@^1.1.4, is-callable@^1.1.5:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb"
|
||||
@@ -1193,6 +1305,13 @@ is-fullwidth-code-point@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
|
||||
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
|
||||
|
||||
is-generator-function@^1.0.7:
|
||||
version "1.0.10"
|
||||
resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72"
|
||||
integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==
|
||||
dependencies:
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-glob@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a"
|
||||
@@ -1282,6 +1401,17 @@ is-symbol@^1.0.2:
|
||||
dependencies:
|
||||
has-symbols "^1.0.1"
|
||||
|
||||
is-typed-array@^1.1.10, is-typed-array@^1.1.3:
|
||||
version "1.1.10"
|
||||
resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f"
|
||||
integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==
|
||||
dependencies:
|
||||
available-typed-arrays "^1.0.5"
|
||||
call-bind "^1.0.2"
|
||||
for-each "^0.3.3"
|
||||
gopd "^1.0.1"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
is-typedarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
@@ -1292,10 +1422,10 @@ is-windows@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d"
|
||||
integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==
|
||||
|
||||
isarray@1.0.0, isarray@~1.0.0:
|
||||
isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||
integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
|
||||
integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==
|
||||
|
||||
isexe@^2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -1319,6 +1449,11 @@ isstream@~0.1.2:
|
||||
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
|
||||
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
|
||||
|
||||
jmespath@0.16.0:
|
||||
version "0.16.0"
|
||||
resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076"
|
||||
integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==
|
||||
|
||||
jsbn@~0.1.0:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||
@@ -1366,10 +1501,10 @@ jws@3.x.x:
|
||||
jwa "^1.4.1"
|
||||
safe-buffer "^5.0.1"
|
||||
|
||||
kareem@2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.3.1.tgz#def12d9c941017fabfb00f873af95e9c99e1be87"
|
||||
integrity sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==
|
||||
kareem@2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/kareem/-/kareem-2.5.1.tgz#7b8203e11819a8e77a34b3517d3ead206764d15d"
|
||||
integrity sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==
|
||||
|
||||
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0:
|
||||
version "3.2.2"
|
||||
@@ -1522,56 +1657,49 @@ moment-timezone@^0.5.31:
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a"
|
||||
integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==
|
||||
|
||||
mongodb@3.5.8:
|
||||
version "3.5.8"
|
||||
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-3.5.8.tgz#34550856449b745d145873734bf922c12d6b9caa"
|
||||
integrity sha512-jz7mR58z66JKL8Px4ZY+FXbgB7d0a0hEGCT7kw8iye46/gsqPrOEpZOswwJ2BQlfzsrCLKdsF9UcaUfGVN2HrQ==
|
||||
mongodb-connection-string-url@^2.6.0:
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz#57901bf352372abdde812c81be47b75c6b2ec5cf"
|
||||
integrity sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==
|
||||
dependencies:
|
||||
bl "^2.2.0"
|
||||
bson "^1.1.4"
|
||||
denque "^1.4.1"
|
||||
require_optional "^1.0.1"
|
||||
safe-buffer "^5.1.2"
|
||||
"@types/whatwg-url" "^8.2.1"
|
||||
whatwg-url "^11.0.0"
|
||||
|
||||
mongodb@5.3.0:
|
||||
version "5.3.0"
|
||||
resolved "https://registry.yarnpkg.com/mongodb/-/mongodb-5.3.0.tgz#9bef3ff35511a66fb7d9aafb7b06112787138db1"
|
||||
integrity sha512-Wy/sbahguL8c3TXQWXmuBabiLD+iVmz+tOgQf+FwkCjhUIorqbAxRbbz00g4ZoN4sXIPwpAlTANMaGRjGGTikQ==
|
||||
dependencies:
|
||||
bson "^5.2.0"
|
||||
mongodb-connection-string-url "^2.6.0"
|
||||
socks "^2.7.1"
|
||||
optionalDependencies:
|
||||
saslprep "^1.0.0"
|
||||
saslprep "^1.0.3"
|
||||
|
||||
mongoose-legacy-pluralize@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz#3ba9f91fa507b5186d399fb40854bff18fb563e4"
|
||||
integrity sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==
|
||||
|
||||
mongoose@^5.7.4:
|
||||
version "5.9.17"
|
||||
resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-5.9.17.tgz#9b74659481807cd9ff5b9c120cdb5087cbbd92bd"
|
||||
integrity sha512-9EDmTiKrOu/41twlPWUA1aOsdxSN6PRIdFwTpLu4MjyNcJ/vuBE+VewKrN1jsD4oXO5rB8bMYtYxVmJQ02SrPg==
|
||||
mongoose@^7.1.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mongoose/-/mongoose-7.1.0.tgz#ad7f23184a0c19eb8efbbad4f1ff2113ede1515d"
|
||||
integrity sha512-shoo9z/7o96Ojx69wpJn65+EC+Mt3q1SWTducW+F2Y4ieCXo0lZwpCZedgC841MIvJ7V8o6gmzoN1NfcnOTOuw==
|
||||
dependencies:
|
||||
bson "^1.1.4"
|
||||
kareem "2.3.1"
|
||||
mongodb "3.5.8"
|
||||
mongoose-legacy-pluralize "1.0.2"
|
||||
mpath "0.7.0"
|
||||
mquery "3.2.2"
|
||||
ms "2.1.2"
|
||||
regexp-clone "1.0.0"
|
||||
safe-buffer "5.1.2"
|
||||
sift "7.0.1"
|
||||
sliced "1.0.1"
|
||||
bson "^5.2.0"
|
||||
kareem "2.5.1"
|
||||
mongodb "5.3.0"
|
||||
mpath "0.9.0"
|
||||
mquery "5.0.0"
|
||||
ms "2.1.3"
|
||||
sift "16.0.1"
|
||||
|
||||
mpath@0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.7.0.tgz#20e8102e276b71709d6e07e9f8d4d0f641afbfb8"
|
||||
integrity sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg==
|
||||
mpath@0.9.0:
|
||||
version "0.9.0"
|
||||
resolved "https://registry.yarnpkg.com/mpath/-/mpath-0.9.0.tgz#0c122fe107846e31fc58c75b09c35514b3871904"
|
||||
integrity sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==
|
||||
|
||||
mquery@3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.yarnpkg.com/mquery/-/mquery-3.2.2.tgz#e1383a3951852ce23e37f619a9b350f1fb3664e7"
|
||||
integrity sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==
|
||||
mquery@5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/mquery/-/mquery-5.0.0.tgz#a95be5dfc610b23862df34a47d3e5d60e110695d"
|
||||
integrity sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==
|
||||
dependencies:
|
||||
bluebird "3.5.1"
|
||||
debug "3.1.0"
|
||||
regexp-clone "^1.0.0"
|
||||
safe-buffer "5.1.2"
|
||||
sliced "1.0.1"
|
||||
debug "4.x"
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -1583,6 +1711,11 @@ ms@2.1.2, ms@^2.1.1:
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||
|
||||
ms@2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
nan@^2.12.1:
|
||||
version "2.14.1"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
|
||||
@@ -1824,27 +1957,41 @@ pump@^3.0.0:
|
||||
end-of-stream "^1.1.0"
|
||||
once "^1.3.1"
|
||||
|
||||
punycode@1.3.2:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d"
|
||||
integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==
|
||||
|
||||
punycode@^2.1.0, punycode@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
|
||||
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
|
||||
|
||||
qmi-cloud-common@../qmi-cloud-common:
|
||||
version "1.1.6"
|
||||
version "2.0.0"
|
||||
dependencies:
|
||||
"@azure/arm-compute" "^15.0.0"
|
||||
"@azure/arm-dns" "^4.0.0"
|
||||
"@azure/ms-rest-nodeauth" "^3.0.7"
|
||||
"@hapi/boom" "^9.1.0"
|
||||
aws-sdk "^2.942.0"
|
||||
axios "^0.21.1"
|
||||
barracuda-api "https://gitlab.com/qlik_gear/barracuda-api-node.git#1.1.0"
|
||||
bull "^3.11.0"
|
||||
mongoose "^5.7.4"
|
||||
mongoose "^7.1.0"
|
||||
nodemailer "^6.4.2"
|
||||
uuid "^8.3.2"
|
||||
|
||||
qs@~6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
|
||||
|
||||
querystring@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620"
|
||||
integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==
|
||||
|
||||
rc@^1.0.1, rc@^1.1.6:
|
||||
version "1.2.8"
|
||||
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
|
||||
@@ -1855,7 +2002,7 @@ rc@^1.0.1, rc@^1.1.6:
|
||||
minimist "^1.2.0"
|
||||
strip-json-comments "~2.0.1"
|
||||
|
||||
readable-stream@^2.0.2, readable-stream@^2.3.5:
|
||||
readable-stream@^2.0.2:
|
||||
version "2.3.7"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
|
||||
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
|
||||
@@ -1911,11 +2058,6 @@ regex-not@^1.0.0, regex-not@^1.0.2:
|
||||
extend-shallow "^3.0.2"
|
||||
safe-regex "^1.1.0"
|
||||
|
||||
regexp-clone@1.0.0, regexp-clone@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/regexp-clone/-/regexp-clone-1.0.0.tgz#222db967623277056260b992626354a04ce9bf63"
|
||||
integrity sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==
|
||||
|
||||
registry-auth-token@^3.0.1:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-3.4.0.tgz#d7446815433f5d5ed6431cd5dca21048f66b397e"
|
||||
@@ -1972,19 +2114,6 @@ repeat-string@^1.6.1:
|
||||
tunnel-agent "^0.6.0"
|
||||
uuid "^3.3.2"
|
||||
|
||||
require_optional@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/require_optional/-/require_optional-1.0.1.tgz#4cf35a4247f64ca3df8c2ef208cc494b1ca8fc2e"
|
||||
integrity sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==
|
||||
dependencies:
|
||||
resolve-from "^2.0.0"
|
||||
semver "^5.1.0"
|
||||
|
||||
resolve-from@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57"
|
||||
integrity sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=
|
||||
|
||||
resolve-url@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
|
||||
@@ -1995,16 +2124,16 @@ ret@~0.1.10:
|
||||
resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
|
||||
integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==
|
||||
|
||||
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
|
||||
safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
|
||||
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
|
||||
|
||||
safe-buffer@~5.1.0, safe-buffer@~5.1.1:
|
||||
version "5.1.2"
|
||||
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
|
||||
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
|
||||
|
||||
safe-regex@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e"
|
||||
@@ -2017,13 +2146,18 @@ safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
|
||||
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
|
||||
|
||||
saslprep@^1.0.0:
|
||||
saslprep@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/saslprep/-/saslprep-1.0.3.tgz#4c02f946b56cf54297e347ba1093e7acac4cf226"
|
||||
integrity sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==
|
||||
dependencies:
|
||||
sparse-bitfield "^3.0.3"
|
||||
|
||||
sax@1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a"
|
||||
integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==
|
||||
|
||||
sax@>=0.6.0:
|
||||
version "1.2.4"
|
||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||
@@ -2068,20 +2202,20 @@ shebang-regex@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
|
||||
integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=
|
||||
|
||||
sift@7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sift/-/sift-7.0.1.tgz#47d62c50b159d316f1372f8b53f9c10cd21a4b08"
|
||||
integrity sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==
|
||||
sift@16.0.1:
|
||||
version "16.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sift/-/sift-16.0.1.tgz#e9c2ccc72191585008cf3e36fc447b2d2633a053"
|
||||
integrity sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ==
|
||||
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
|
||||
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
|
||||
|
||||
sliced@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41"
|
||||
integrity sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=
|
||||
smart-buffer@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
|
||||
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
|
||||
|
||||
snapdragon-node@^2.0.1:
|
||||
version "2.1.1"
|
||||
@@ -2113,6 +2247,14 @@ snapdragon@^0.8.1:
|
||||
source-map-resolve "^0.5.0"
|
||||
use "^3.1.0"
|
||||
|
||||
socks@^2.7.1:
|
||||
version "2.7.1"
|
||||
resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55"
|
||||
integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==
|
||||
dependencies:
|
||||
ip "^2.0.0"
|
||||
smart-buffer "^4.2.0"
|
||||
|
||||
source-map-resolve@^0.5.0:
|
||||
version "0.5.3"
|
||||
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
|
||||
@@ -2364,6 +2506,13 @@ tough-cookie@~2.5.0:
|
||||
psl "^1.1.28"
|
||||
punycode "^2.1.1"
|
||||
|
||||
tr46@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
|
||||
integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
|
||||
dependencies:
|
||||
punycode "^2.1.1"
|
||||
|
||||
tslib@^1.10.0:
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||
@@ -2478,6 +2627,14 @@ url-parse-lax@^1.0.0:
|
||||
dependencies:
|
||||
prepend-http "^1.0.1"
|
||||
|
||||
url@0.10.3:
|
||||
version "0.10.3"
|
||||
resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64"
|
||||
integrity sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==
|
||||
dependencies:
|
||||
punycode "1.3.2"
|
||||
querystring "0.2.0"
|
||||
|
||||
use@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||
@@ -2498,11 +2655,32 @@ util.promisify@^1.0.1:
|
||||
has-symbols "^1.0.1"
|
||||
object.getownpropertydescriptors "^2.1.0"
|
||||
|
||||
util@^0.12.4:
|
||||
version "0.12.5"
|
||||
resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc"
|
||||
integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==
|
||||
dependencies:
|
||||
inherits "^2.0.3"
|
||||
is-arguments "^1.0.4"
|
||||
is-generator-function "^1.0.7"
|
||||
is-typed-array "^1.1.3"
|
||||
which-typed-array "^1.1.2"
|
||||
|
||||
uuid@8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c"
|
||||
integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==
|
||||
|
||||
uuid@^3.1.0, uuid@^3.3.2, uuid@^3.4.0:
|
||||
version "3.4.0"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||
|
||||
uuid@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
|
||||
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
|
||||
|
||||
verror@1.10.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400"
|
||||
@@ -2512,6 +2690,31 @@ verror@1.10.0:
|
||||
core-util-is "1.0.2"
|
||||
extsprintf "^1.2.0"
|
||||
|
||||
webidl-conversions@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
|
||||
integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==
|
||||
|
||||
whatwg-url@^11.0.0:
|
||||
version "11.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018"
|
||||
integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
|
||||
dependencies:
|
||||
tr46 "^3.0.0"
|
||||
webidl-conversions "^7.0.0"
|
||||
|
||||
which-typed-array@^1.1.2:
|
||||
version "1.1.9"
|
||||
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6"
|
||||
integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==
|
||||
dependencies:
|
||||
available-typed-arrays "^1.0.5"
|
||||
call-bind "^1.0.2"
|
||||
for-each "^0.3.3"
|
||||
gopd "^1.0.1"
|
||||
has-tostringtag "^1.0.0"
|
||||
is-typed-array "^1.1.10"
|
||||
|
||||
which@^1.2.9:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
|
||||
@@ -2545,6 +2748,14 @@ xdg-basedir@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
|
||||
integrity sha1-SWsswQnsqNus/i3HK2A8F8WHCtQ=
|
||||
|
||||
xml2js@0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.5.0.tgz#d9440631fbb2ed800203fad106f2724f62c493b7"
|
||||
integrity sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==
|
||||
dependencies:
|
||||
sax ">=0.6.0"
|
||||
xmlbuilder "~11.0.0"
|
||||
|
||||
xml2js@^0.4.19:
|
||||
version "0.4.23"
|
||||
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
|
||||
|
||||
52
server/certs/privatekey.pem
Normal file
@@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIJQQIBADANBgkqhkiG9w0BAQEFAASCCSswggknAgEAAoICAQCzYn0nGRdheWZf
|
||||
X9Gvz5Fc0RH0oeJe1HfC0E7HrDFlogkmErJyvr6vZMwjx7jxSYDbYgNJtUXmalKZ
|
||||
a3fgF4nRmlv+aPG1pNjeCThoTHTof7hMS70iAIyrbubU6v+SKvbmcWM/RkEkEXXa
|
||||
7TCOi/ZbN7S+dFQAA9ObNF6dsYHLWBQO4O8ZoydTcxTc1enc+v1Q6/Bqm7//fBY+
|
||||
VV8tIi4hiIgLTEGnsBCnH34a3Ie8i6JDXEMmD+NiZjeYLrr5YToniDu4drkrpzIp
|
||||
2P9X2wbdwI25WC6RcWLwSIkjiRY8Z/jDgowjieCDNXHVCYv47+8peeOx/Z//YuKy
|
||||
Hul2ob25KjK51k6t55NKNj3kTrzXyRIgGOdI6Q+IBl85lfhIiXPRSCobLoMVbZgG
|
||||
5qTZpEHyp2RN5CPLGlshslrQigunUzuA6sPGR+qnT8ZwGndiV8n5oaWx7fmIoK1k
|
||||
SYBe5zKehYHh4hprThvzvqqPfJwzAFAOCSJJsch7SuZGlz/2xSx0P1e27vRJ9at/
|
||||
zSKL3FIFqHpk3I0FKf/FjNZu29RPmyLIyX8uaB/Iud6cg45THaUs+GXeiLY3mt/u
|
||||
3BRADZF9AXzJZR02Ubtre1gE5PSu0NOnkudoL+9fnCK5PGFlifdtp68R8UiKAnXj
|
||||
OjMljbq+yqn5tf6DB0y82rEk0qXCoQIDAQABAoICAAhFqkF4zz1f+fVmTPGfZUko
|
||||
nPwxJj1ll9uPJpWKSRbc44YdcPGIqUYbpFb2uVPAe3riaLC9SsARjBs7ZkzvrkZ7
|
||||
qJ1b8orZU2Td0PuXoavXuUR6GPqDQvlkL6yG5QqKWlYNyYaxSIS6HLtf77rLFS0S
|
||||
Aw7l/LpUHYMikEW+WfQUwjazbw3kGlv8n8F/8ye3wqG4156FmH4sFzcq/FdvpD1I
|
||||
ogQUsVG4dUl3qCWN9jZ0IU3w6GnOFsLCtZ1EyRDXR8rAkLHKIRzfD274QkInaAgm
|
||||
09ebC5QKbLEURJAJHNI5SzKc3QswgFQcoqdb0tgZHGgcZlXJZ9eWPvT5fEj2Xsd3
|
||||
i0Q46D6y7r+dAMhhE9hdZA/Ke1fkcBgANLRRdoHHFUey1en7uQsliGfNzm1i/OR4
|
||||
AW9XdyNiB5F2LqYfnrRXEsG7FHAnCVkN1k5ur8GsBZpfYtzWof9yKXCaVUQODKqW
|
||||
0gUndI38omx8liZoFruP5VteRJgVYEIkcyAAfeG3OlIaywGmwdTGyViXhWZcihse
|
||||
3pJMsUI7P72SkSjGKAjVxCf08bwCIT6ls8V79epiUzhMnORAZC4hZxayZrBphS8U
|
||||
sVzrMGQeVexlXso3EoqjWCaM7XkbvE/8ZOBbFxyJWAkIMuD4vUuW19Fk8IOTzAL4
|
||||
VKlcq+vfZxWbA7Mq6w7vAoIBAQDeJF82Lv/IUXL3JkgYmSnze7rGg+rwsuUxBXd5
|
||||
CNPjrlwbCEIVEreQHUPOzih2Rvhrd2iCVpyCO8Kdo2AE5B7wnPePAitnEwZ6hFcD
|
||||
xMubuiRqlaILfDi8Ph7juL0mmSaz5D+HkzUdCUfvBUFXxvLyeDsNFjgqBGOahL0b
|
||||
1E1FDct/6Mltc+ATlavWxQxn+UEs8/3ssy+f4gmeLgwgevt+A8cB3ppwSFCOnu4I
|
||||
iX/vdti5MHjdUBjJGV/qjTOsUWioRXY1ay87H5RiQju3GpTQ8zlkyRv9MMll0lqi
|
||||
OQwtVvVpRd6O4qHFmgJOfPt+Z81b6cPrA0StbwAG/t5qYH7/AoIBAQDOucsdyj5k
|
||||
XmlcDo51VyEr95FsJ/SZOPOBh4MpRm4IYUgLsQTacUueTCiY17TUR6CUc+si76gb
|
||||
YYAcF0KV/17lvPosoWkdZNsqq1L8nrVq3+z2WsTkCs8m10FZ0aajf3N8WTT0v6VX
|
||||
DsOrZLsD2xxIU4f3Lf4FnL6PI5BDT83Up5paHSIhE+omAp+x7Bzf0KSpsDu2uZTH
|
||||
r8yJf1ghet+Z923/yUUTHDM8Vpt8hhoEjXWJd3r96OSIoFUwHzLgNFpiPgHkGJUU
|
||||
xRZV4Au+GeHuz9nLWPRaYVG5CxX/IbPyMzGJHchVFywIySqYK2l+VvVM17M7AuCq
|
||||
V4SZ3y7Ky15fAoIBAHahp/MwwEqDLMlOOVxhl2S/Y+yWEIbAkuNODxKlIztJJ0kM
|
||||
bPYCC+O7rTWpJTSdDBegKkDI7kYikflLgYC7LsbCnPZTa0hdga02NZ3+n9mnW8FL
|
||||
7cECcu4corRsOR9+1ItnToIhnFDIXxEHlnDA/4d7q9V+UzolI+gmETPmeelxx4ak
|
||||
k8WPB1COMrm8e7afBy5xkt6whrN0rDw8TR+fbeVLMSEPdxyVkefIekg23grNRkoH
|
||||
19Qg7Uuf8Hg7NihFRYXvqoQ2nH+Pite6lVdgq6625aSsPfVF85gb8WkG3DjuYpr4
|
||||
xDU8VLZJXAf8ePZ1itcWDRnZofiY+cPCopbet5MCggEAGb7l3wXrE1D2yjI957s8
|
||||
NF+WyuOHAPYozX71BNTyqzSCZoJbWmE1y7csbyyeJrns89Aj/qveQdq4u8bh0hCF
|
||||
3xLUDW7kynZfHUdNBI03huHwfxX643O9LNcuGmOT31TmKxxpDfo4O0lpcRUQfYBy
|
||||
W0eb7VrbAhPtX6JMOzXbKprdDFAIihoS1T0Kanw/dFhlyYRbS3x9XQk17gHgFftZ
|
||||
kbFRD8QfSCwA7YjTwIRrBRohA0fQF4NDwwhE08Nu8KFUiFu0nJW7K2UITRWkIL7U
|
||||
douIUlz3wbHRHbyVtrqZ0JYzmyIMaxyBrW5wUZdGgieOUU2j0rufA1f2+brj9vmw
|
||||
/QKCAQBDuCwPOBuZ6qFu1zBAfDyElTsIve1Uh32q+jbL5w8kkBhsiGaxFcmC3gG6
|
||||
WkdtKZKSRaf23wl4LgjvAYD5lPX+p3127pHxG+haWFkXweDMw4WrvGamm/X0H8JD
|
||||
8CX9PNWi2yxVDjNXiJ5tSbsFnhBf2CijHinrZexxlVTId6yuptOWDaq4XeYnMmKt
|
||||
FRgpnwnKAkxZKmoKIXOeSQOUVJzzxufkX3QG3CJqu60DGEDsLxM00HoP/Vxa81un
|
||||
XT+DxB6kgSYLC0a7KsHsXm8CbCoikqVEtANzIvahbsKDLNah/OWmOo9O1UphhYN+
|
||||
HQe3R2QQtfmWZ320cdhukaok4yj3
|
||||
-----END PRIVATE KEY-----
|
||||
@@ -2,16 +2,14 @@
|
||||
const secrets = require("/run/secrets/config.json");
|
||||
|
||||
const HOSTNAME_URL = process.env.HOSTNAME_URL || "http://localhost:3000"
|
||||
const TENANT_ID = secrets.AZURE_TENANT_ID;
|
||||
const CLIENT_ID = secrets.AZURE_CLIENT_ID;
|
||||
const CLIENT_SECRET = secrets.AZURE_CLIENT_SECRET;
|
||||
const CLIENT_ID = secrets.CLIENT_ID;
|
||||
const CLIENT_SECRET = secrets.CLIENT_SECRET;
|
||||
const LOGOUT_URL = HOSTNAME_URL;
|
||||
const REDIRECT_URL = `${HOSTNAME_URL}/auth/openid/return`;
|
||||
|
||||
exports.creds = {
|
||||
// Required
|
||||
identityMetadata: `https://login.microsoftonline.com/${TENANT_ID}/.well-known/openid-configuration`,
|
||||
// or equivalently: 'https://login.microsoftonline.com/${TENANT_ID}/.well-known/openid-configuration'
|
||||
//identityMetadata: IDENTITY_METADATA,
|
||||
//
|
||||
// or you can use the common endpoint
|
||||
// 'https://login.microsoftonline.com/common/.well-known/openid-configuration'
|
||||
@@ -85,12 +83,13 @@ exports.creds = {
|
||||
exports.resourceURL = 'https://graph.microsoft.com';
|
||||
|
||||
// The url you need to go to destroy the session with AAD
|
||||
exports.destroySessionUrl = `https://login.microsoftonline.com/common/oauth2/logout?post_logout_redirect_uri=${LOGOUT_URL}`;
|
||||
|
||||
//exports.destroySessionUrl = `https://login.microsoftonline.com/common/oauth2/logout?post_logout_redirect_uri=${LOGOUT_URL}`;
|
||||
//exports.destroySessionUrl = `https://qlik.okta.com/logout?id_token_hint=${id_token}&post_logout_redirect_uri=${LOGOUT_URL}&state=${state}`
|
||||
exports.destroySessionUrl = `https://qlik.okta.com/logout?post_logout_redirect_uri=${LOGOUT_URL}`
|
||||
// If you want to use the mongoDB session store for session middleware, set to true; otherwise we will use the default
|
||||
// session store provided by express-session.
|
||||
// Note that the default session store is designed for development purpose only.
|
||||
exports.useMongoDBSessionStore = true;
|
||||
exports.useMongoDBSessionStore = false;
|
||||
|
||||
// If you want to use mongoDB, provide the uri here for the database.
|
||||
exports.databaseUri = process.env.MONGO_URI;
|
||||
|
||||
@@ -10,6 +10,14 @@ const axios = require('axios');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
const sessionStore = new MongoStore({
|
||||
mongooseConnection: mongoose.connection,
|
||||
url: process.env.MONGO_URI,
|
||||
autoRemove: 'interval',
|
||||
autoRemoveInterval: 10
|
||||
//clear_interval: config.mongoDBSessionMaxAge
|
||||
});
|
||||
|
||||
// Start QuickStart here
|
||||
|
||||
var OIDCStrategy = require('passport-azure-ad').OIDCStrategy;
|
||||
@@ -80,22 +88,29 @@ passport.use(new OIDCStrategy({
|
||||
cookieEncryptionKeys: config.creds.cookieEncryptionKeys,
|
||||
clockSkew: config.creds.clockSkew,
|
||||
},
|
||||
function(iss, sub, profile, jwtClaims, accessToken, refreshToken, params, done) {
|
||||
async function(iss, sub, profile, jwtClaims, accessToken, refreshToken, params, done) {
|
||||
|
||||
if ( !profile.oid ) {
|
||||
return done(new Error("No oid found"), null);
|
||||
}
|
||||
|
||||
let email = profile.upn;
|
||||
let isPermitterEmail = (email.toLowerCase().includes("@qlik") || email.toLowerCase().includes("@talend"));
|
||||
if ( !email || !isPermitterEmail) {
|
||||
return done(new Error("Not a valid Qlik email"), null);
|
||||
}
|
||||
|
||||
//console.log("accessToken", accessToken);
|
||||
//console.log("iss", iss);
|
||||
//console.log("sub", sub);
|
||||
//console.log("refreshToken", refreshToken);
|
||||
//console.log("jwtClaims", jwtClaims);
|
||||
//console.log("params", params);
|
||||
console.log(`Passport# new login from: ${profile.upn} (${profile.displayName})` );
|
||||
console.log(`Passport# new login from: ${profile.upn} (${profile.displayName}) - sub: ${sub}` );
|
||||
|
||||
//Save user photo
|
||||
axios({
|
||||
method: 'get',
|
||||
method: 'GET',
|
||||
url: 'https://graph.microsoft.com/v1.0/me/photo/$value',
|
||||
responseType: 'stream',
|
||||
headers: { 'Authorization' : 'Bearer '+accessToken }
|
||||
@@ -104,26 +119,54 @@ passport.use(new OIDCStrategy({
|
||||
}).catch(function(err){
|
||||
console.log('Passport# Warning: No picture found');
|
||||
});
|
||||
|
||||
let mail, jobTitle;
|
||||
|
||||
try {
|
||||
let fullProfile = await axios({
|
||||
method: 'GET',
|
||||
url: `https://graph.microsoft.com/v1.0/users/${profile.oid}`,
|
||||
responseType: 'json',
|
||||
headers: { 'Authorization' : 'Bearer '+accessToken }
|
||||
});
|
||||
if ( fullProfile.data ) {
|
||||
mail = fullProfile.data.mail;
|
||||
jobTitle = fullProfile.data.jobTitle;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Passport# Warning: Could not get user entire profile');
|
||||
}
|
||||
|
||||
// asynchronous verification, for effect...
|
||||
process.nextTick(function () {
|
||||
_findByOid(profile.oid, async function(err, user) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
if (!user) {
|
||||
// "Auto-registration"
|
||||
user = await db.user.add({
|
||||
"oid": profile.oid,
|
||||
"upn": profile.upn,
|
||||
"displayName": profile.displayName,
|
||||
"lastLogin": new Date()
|
||||
_findByOid(profile.oid, async function(err, user) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
if (!user) {
|
||||
// "Auto-registration"
|
||||
user = await db.user.add({
|
||||
"oid": profile.oid,
|
||||
"upn": profile.upn,
|
||||
"displayName": profile.displayName,
|
||||
"lastLogin": new Date(),
|
||||
"sub": sub,
|
||||
"active": true,
|
||||
"mail": mail,
|
||||
"jobTitle": jobTitle
|
||||
});
|
||||
return done(null, user);
|
||||
}
|
||||
db.user.update(user._id, {
|
||||
"lastLogin": new Date(),
|
||||
"sub": sub,
|
||||
"active": true,
|
||||
"mail": mail,
|
||||
"jobTitle": jobTitle
|
||||
});
|
||||
return done(null, user);
|
||||
}
|
||||
db.user.update(user._id, {"lastLogin": new Date()});
|
||||
return done(null, user);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
));
|
||||
@@ -136,17 +179,16 @@ module.exports.init = function(app){
|
||||
app.use(expressSession({
|
||||
secret: 'secret',
|
||||
cookie: {maxAge: config.mongoDBSessionMaxAge * 1000},
|
||||
store: new MongoStore({
|
||||
mongooseConnection: mongoose.connection,
|
||||
autoRemove: 'interval',
|
||||
autoRemoveInterval: 10
|
||||
//clear_interval: config.mongoDBSessionMaxAge
|
||||
}),
|
||||
store: sessionStore,
|
||||
resave: true,
|
||||
saveUninitialized: false
|
||||
}));
|
||||
} else {
|
||||
app.use(expressSession({ secret: 'keyboard cat', resave: true, saveUninitialized: false }));
|
||||
app.use(expressSession({
|
||||
secret: 'keyboard cat',
|
||||
resave: true,
|
||||
saveUninitialized: false
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -266,4 +308,6 @@ module.exports.ensureAuthenticatedAndIsMe = async function (req, res, next) {
|
||||
}
|
||||
}
|
||||
return res.status(401).send("Error: Unauthorized");
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.sessionStore = sessionStore;
|
||||
268
server/passport-okta.js
Normal file
@@ -0,0 +1,268 @@
|
||||
const passport = require('passport');
|
||||
const expressSession = require('express-session');
|
||||
const config = require('./config');
|
||||
|
||||
// set up database for express session
|
||||
const MongoStore = require('connect-mongo')(expressSession);
|
||||
const mongoose = require('mongoose');
|
||||
const db = require("qmi-cloud-common/mongo");
|
||||
|
||||
const sessionStore = config.useMongoDBSessionStore? new MongoStore({
|
||||
mongooseConnection: mongoose.connection,
|
||||
url: process.env.MONGO_URI,
|
||||
autoRemove: 'interval',
|
||||
autoRemoveInterval: 10
|
||||
//clear_interval: config.mongoDBSessionMaxAge
|
||||
}) : new expressSession.MemoryStore();
|
||||
|
||||
var OpenIDConnectStrategy = require('passport-openidconnect');
|
||||
|
||||
const OKTA_DOMAIN = "qlik.okta.com";
|
||||
|
||||
passport.serializeUser(function(user, done) {
|
||||
done(null, user);
|
||||
});
|
||||
|
||||
passport.deserializeUser(function(user, done) {
|
||||
_findByUpn(user.upn, function (err, user) {
|
||||
done(err, user);
|
||||
});
|
||||
});
|
||||
|
||||
var _findByUpn = async function(upn, fn) {
|
||||
var mongouser = await db.user.getOne({"upn": { $regex: new RegExp(upn, 'i') } });
|
||||
if (mongouser){
|
||||
return fn(null, mongouser);
|
||||
} else {
|
||||
return fn(null, null);
|
||||
}
|
||||
};
|
||||
|
||||
// set up passport
|
||||
passport.use('oidc', new OpenIDConnectStrategy({
|
||||
issuer: "https://qlik.okta.com",
|
||||
authorizationURL: `https://${OKTA_DOMAIN}/oauth2/v1/authorize`,
|
||||
tokenURL: `https://${OKTA_DOMAIN}/oauth2/v1/token`,
|
||||
clientID: config.creds.clientID,
|
||||
clientSecret: config.creds.clientSecret,
|
||||
callbackURL: config.creds.redirectUrl,
|
||||
scope: config.creds.scope,
|
||||
passReqToCallback: true
|
||||
}, async (req, issuer, profile, context, idToken, accessToken, refreshToken, done) => {
|
||||
|
||||
//console.log("OKTA ISSUER ", issuer)
|
||||
//console.log("OKTA PROFILE ", profile)
|
||||
//console.log("OKTA context ", context)
|
||||
//console.log("OKTA idToken ", idToken)
|
||||
//console.log("OKTA accessToken ", accessToken)
|
||||
//console.log("OKTA refreshToken ", refreshToken)
|
||||
|
||||
|
||||
req.session.oktaAccessToken = accessToken;
|
||||
|
||||
if ( !profile.id ) {
|
||||
return done(new Error("No profile id found"), null);
|
||||
}
|
||||
|
||||
console.log(`Passport# new login from: ${profile.displayName} - sub: ${profile.id}` );
|
||||
|
||||
// asynchronous verification, for effect...
|
||||
process.nextTick(function () {
|
||||
_findByUpn(profile.username, async function(err, user) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
if (!user) {
|
||||
// "Auto-registration"
|
||||
user = await db.user.add({
|
||||
//"oid": profile.id,
|
||||
"upn": profile.username.toLowerCase(),
|
||||
"displayName": profile.displayName,
|
||||
"lastLogin": new Date(),
|
||||
"sub": profile.id,
|
||||
"active": true,
|
||||
"mail": profile.emails[0].value,
|
||||
//"jobTitle": jobTitle
|
||||
});
|
||||
return done(null, user);
|
||||
}
|
||||
db.user.update(user._id, {
|
||||
"upn": profile.username.toLowerCase(),
|
||||
"lastLogin": new Date(),
|
||||
"sub": profile.id,
|
||||
"active": true,
|
||||
"mail": profile.emails[0].value,
|
||||
//"jobTitle": jobTitle
|
||||
});
|
||||
return done(null, user);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
//return done(null, profile);
|
||||
}));
|
||||
|
||||
|
||||
|
||||
module.exports.init = function(app, isSecure){
|
||||
|
||||
const cookieName = 'qmiconnect.sid';
|
||||
|
||||
var cookieConf = {
|
||||
maxAge: config.mongoDBSessionMaxAge * 1000,
|
||||
httpOnly: false,
|
||||
secure: isSecure,
|
||||
sameSite: isSecure? 'none' : undefined
|
||||
};
|
||||
|
||||
|
||||
console.log("--- SESSION", "Cookie Conf = ", cookieConf , "Cookie Name = " + cookieName);
|
||||
|
||||
|
||||
// set up session middleware
|
||||
if (config.useMongoDBSessionStore) {
|
||||
app.use(expressSession({
|
||||
name: 'qmiconnect.sid',
|
||||
secret: 'secret',
|
||||
cookie: cookieConf,
|
||||
store: sessionStore,
|
||||
resave: true,
|
||||
saveUninitialized: false
|
||||
}));
|
||||
} else {
|
||||
app.use(expressSession({
|
||||
name: 'qmiconnect.sid',
|
||||
secret: 'keyboard cat',
|
||||
cookie: cookieConf,
|
||||
store: sessionStore,
|
||||
resave: true,
|
||||
saveUninitialized: false
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
// Initialize Passport! Also use passport.session() middleware, to support
|
||||
// persistent login sessions (recommended).
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
|
||||
app.get('/sessioninfo', function(req, res){
|
||||
|
||||
if (req.session.oktaAccessToken && req.user ) {
|
||||
res.json({
|
||||
"oktaAccessToken": req.session.oktaAccessToken,
|
||||
"user": req.user
|
||||
});
|
||||
} else {
|
||||
res.status(401).json({});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
app.get('/login',
|
||||
function(req, res, next) {
|
||||
passport.authenticate('oidc',
|
||||
{
|
||||
response: res, // required
|
||||
resourceURL: config.resourceURL, // optional. Provide a value if you want to specify the resource.
|
||||
//customState: 'my_state', // optional. Provide a value if you want to provide custom state value.
|
||||
failureRedirect: '/error',
|
||||
session: true,
|
||||
//assignProperty: "weee",
|
||||
authInfo: true
|
||||
}
|
||||
)(req, res, next);
|
||||
},
|
||||
function(req, res) {
|
||||
res.redirect('/home');
|
||||
}
|
||||
);
|
||||
|
||||
app.get('/auth/openid/return',
|
||||
function(req, res, next) {
|
||||
passport.authenticate('oidc', {
|
||||
response: res,
|
||||
failureRedirect: '/error' ,
|
||||
session: true,
|
||||
//assignProperty: "weee",
|
||||
authInfo: true
|
||||
}
|
||||
)(req, res, next);
|
||||
|
||||
},
|
||||
function(req, res) {
|
||||
console.log('Passport# We received a return from OKTA ');
|
||||
res.redirect('/provisions');
|
||||
}
|
||||
);
|
||||
|
||||
/*app.use('/auth/openid/return', passport.authenticate('oidc', { failureRedirect: '/error' }), (req, res) => {
|
||||
|
||||
console.log('Passport# We received a return from OKTA ');
|
||||
res.redirect('/provisions');
|
||||
});*/
|
||||
|
||||
app.get('/logout', function(req, res){
|
||||
req.session.destroy(function(err) {
|
||||
req.logOut();
|
||||
res.redirect("/home");
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
async function isApiKeyAuthenticated(req) {
|
||||
//console.log("oktaToken", req.headers.oktatoken);
|
||||
//console.log("okta session token", req.session.oktaAccessToken);
|
||||
if (req.headers && req.headers.oktatoken !== undefined && req.session && req.headers.oktatoken === req.session.oktaAccessToken) {
|
||||
console.log("OKTA TOKEN PROVIDED and is GOOD!!!!");
|
||||
return true;
|
||||
}
|
||||
let key = req.query.apiKey || req.get('QMI-ApiKey');
|
||||
if ( key ) {
|
||||
var result = await db.apiKey.getOne({"apiKey": key});
|
||||
if ( result ) {
|
||||
req.user = result.user;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.ensureAuthenticatedDoLogin = async function(req, res, next) {
|
||||
if ( await isApiKeyAuthenticated(req) || req.isAuthenticated() ) {
|
||||
return next();
|
||||
}
|
||||
res.redirect('/login');
|
||||
};
|
||||
|
||||
module.exports.ensureAuthenticated = async function(req, res, next) {
|
||||
if ( await isApiKeyAuthenticated(req) || req.isAuthenticated() ) {
|
||||
return next();
|
||||
}
|
||||
res.status(401).send({"error": "Unauthorized"});
|
||||
};
|
||||
|
||||
module.exports.ensureAuthenticatedAndAdmin = async function(req, res, next) {
|
||||
if ( ( await isApiKeyAuthenticated(req) || req.isAuthenticated()) && (req.user.role === 'admin' || req.user.role === 'superadmin') ) {
|
||||
return next();
|
||||
}
|
||||
res.status(401).send({"error": "Unauthorized"});
|
||||
};
|
||||
|
||||
module.exports.ensureAuthenticatedAndIsMe = async function (req, res, next) {
|
||||
if ( await isApiKeyAuthenticated(req) || req.isAuthenticated() ) {
|
||||
var userId = (req.params.userId === 'me')? req.user._id : req.params.userId;
|
||||
if ( req.user._id == userId || req.user.role === 'admin' || req.user.role === 'superadmin' ) {
|
||||
return next();
|
||||
} else {
|
||||
return res.status(401).send("Error: Unauthorized");
|
||||
}
|
||||
}
|
||||
return res.status(401).send("Error: Unauthorized");
|
||||
};
|
||||
|
||||
module.exports.sessionStore = sessionStore;
|
||||
@@ -1,7 +1,7 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const db = require('qmi-cloud-common/mongo');
|
||||
const passport = require('../passport');
|
||||
const passport = require('../passport-okta');
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const db = require('qmi-cloud-common/mongo');
|
||||
const passport = require('../passport');
|
||||
const passport = require('../passport-okta');
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const db = require('qmi-cloud-common/mongo');
|
||||
const passport = require('../passport');
|
||||
const passport = require('../passport-okta');
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const db = require('qmi-cloud-common/mongo');
|
||||
const passport = require('../passport');
|
||||
const passport = require('../passport-okta');
|
||||
|
||||
import { queues, SYNAPSE_QUEUE } from 'qmi-cloud-common/queues';
|
||||
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@@ -42,6 +45,11 @@ router.post('/updates', passport.ensureAuthenticatedAndAdmin, async (req, res, n
|
||||
|
||||
if ( provision ) {
|
||||
|
||||
var type = "vms";
|
||||
if ( logEvent.indexOf("RDS")!== -1 ){
|
||||
type = "db";
|
||||
}
|
||||
|
||||
let id = provision._id.toString();
|
||||
|
||||
console.log(`${logEvent} (${now})# provision (${id}) - scenario is (${provision.scenario} - v${provision.scenarioVersion})`);
|
||||
@@ -50,8 +58,23 @@ router.post('/updates', passport.ensureAuthenticatedAndAdmin, async (req, res, n
|
||||
|
||||
if ( event.instanceState === 'Stopped' ) {
|
||||
|
||||
//Check active children provisions
|
||||
let children = await db.provision.get({ "parent": provision._id, "isDestroyed": false, "isDeleted": false });
|
||||
if (children.results.length > 0 ) {
|
||||
children.results.forEach(function(child) {
|
||||
|
||||
if ( child.scenario === 'azqmi-synapse') {
|
||||
queues[SYNAPSE_QUEUE].add("synapse_job", {
|
||||
provId: child._id,
|
||||
user: req.user,
|
||||
tasktype: 'pause'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if ( provision.statusVms === 'Stopped' ) {
|
||||
console.log(`${logEvent} (${now})# provision (${id}) - VMs were already Stopped!`);
|
||||
console.log(`${logEvent} (${now})# provision (${id}) - were already Stopped!`);
|
||||
} else {
|
||||
|
||||
let timeRunning = db.utils.getNewTimeRunning(provision);
|
||||
@@ -59,39 +82,32 @@ router.post('/updates', passport.ensureAuthenticatedAndAdmin, async (req, res, n
|
||||
"statusVms": "Stopped",
|
||||
"timeRunning": timeRunning,
|
||||
"stoppedFrom": dateNow,
|
||||
"pendingNextAction": undefined
|
||||
"pendingNextAction": null
|
||||
};
|
||||
|
||||
let msg = "";
|
||||
|
||||
if ( provision.schedule && !provision.schedule.is24x7 ) {
|
||||
patch["endDateOnSchedule"] = dateNow;
|
||||
|
||||
//This is temporary, only to make sure there is value
|
||||
if ( !provision["startDateOnSchedule"] ) {
|
||||
patch["startDateOnSchedule"] = dateNow;
|
||||
msg = "startDateOS: " + dateNow.toISOString();
|
||||
} else {
|
||||
msg = "startDateOS: " + new Date(provision.startDateOnSchedule).toISOString();
|
||||
}
|
||||
msg += (" - endDateOS: " + dateNow.toISOString());
|
||||
}
|
||||
msg += ` - New totalTimeRunning: ${timeRunning} mins`;
|
||||
|
||||
await db.provision.update(id, patch);
|
||||
console.log(`${logEvent} (${now})# provision (${id}) - VMs changed to Stopped!`);
|
||||
db.event.add({ user: provision.user._id, provision: provision._id, type: 'vms.stop-schedule', message: msg });
|
||||
console.log(`${logEvent} (${now})# provision (${id}) - changed to Stopped!`);
|
||||
db.event.add({ provision: provision._id, type: `${type}.stop`, message: `[Divvy] TotalTimeRunning: ${timeRunning} mins` });
|
||||
}
|
||||
|
||||
} else if ( event.instanceState === 'Running' ) {
|
||||
|
||||
if ( provision.statusVms === 'Running' ) {
|
||||
console.log(`${logEvent} (${now})# provision (${id}) - VMs were already Running!`);
|
||||
console.log(`${logEvent} (${now})# provision (${id}) - were already Running!`);
|
||||
} else {
|
||||
let patch = {
|
||||
"statusVms": "Running",
|
||||
"runningFrom": dateNow,
|
||||
"pendingNextAction": undefined
|
||||
"pendingNextAction": null
|
||||
};
|
||||
|
||||
// This is temporary, only to make sure there are values soon
|
||||
@@ -103,9 +119,9 @@ router.post('/updates', passport.ensureAuthenticatedAndAdmin, async (req, res, n
|
||||
}
|
||||
|
||||
await db.provision.update(id, patch);
|
||||
console.log(`${logEvent} (${now})# provision (${id}) - VMs changed to Running!`);
|
||||
console.log(`${logEvent} (${now})# provision (${id}) - changed to Running!`);
|
||||
|
||||
db.event.add({ user: provision.user._id, provision: provision._id, type: 'vms.start-schedule', message: `TimeRunning so far: ${provision.timeRunning} mins` });
|
||||
db.event.add({ provision: provision._id, type: `${type}.start`, message: `[Divvy] TotalTimeRunning: ${provision.timeRunning} mins` });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const db = require('qmi-cloud-common/mongo');
|
||||
const passport = require('../passport');
|
||||
const passport = require('../passport-okta');
|
||||
const sendEmail = require('qmi-cloud-common/send-email');
|
||||
|
||||
|
||||
@@ -15,6 +15,15 @@ const sendEmail = require('qmi-cloud-common/send-email');
|
||||
* - admin
|
||||
* produces:
|
||||
* - application/json
|
||||
* parameters:
|
||||
* - name: page
|
||||
* in: query
|
||||
* required: false
|
||||
* type: integer
|
||||
* - name: size
|
||||
* in: query
|
||||
* required: false
|
||||
* type: integer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Notifications
|
||||
@@ -39,8 +48,8 @@ router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) =>
|
||||
* @swagger
|
||||
* /notifications/testsendemail:
|
||||
* post:
|
||||
* description: Get all notifications
|
||||
* summary: Get all notifications
|
||||
* description: Test email service
|
||||
* summary: Test email service
|
||||
* tags:
|
||||
* - admin
|
||||
* produces:
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const db = require('qmi-cloud-common/mongo');
|
||||
const azurecli = require('qmi-cloud-common/azurecli');
|
||||
const passport = require('../passport');
|
||||
const passport = require('../passport-okta');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
|
||||
import { queues, SYNAPSE_QUEUE } from 'qmi-cloud-common/queues';
|
||||
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /provisions:
|
||||
* get:
|
||||
* description: Get all Provisions
|
||||
* summary: Get all Provisions)
|
||||
* summary: Get all Provisions
|
||||
* tags:
|
||||
* - admin
|
||||
* produces:
|
||||
@@ -63,7 +65,8 @@ router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) =>
|
||||
const result = await db.provision.getPage(filter, page, req.query.populates, req.query.select);
|
||||
|
||||
if (result.next){
|
||||
result.nextUrl = new URL(req.protocol + '://' + req.get('Host') + req.baseUrl);
|
||||
|
||||
result.nextUrl = new URL('https://' + req.hostname + req.baseUrl);
|
||||
if ( req.query.filter ) {
|
||||
result.nextUrl.searchParams.append("filter", req.query.filter);
|
||||
}
|
||||
@@ -85,10 +88,121 @@ router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) =>
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /provisions/vmImagesLogs:
|
||||
* /provisions/copysnapshots:
|
||||
* post:
|
||||
* description: Copy snapshots into regions
|
||||
* summary: Copy snapshots into regions
|
||||
* tags:
|
||||
* - admin
|
||||
* produces:
|
||||
* - application/json
|
||||
* parameters:
|
||||
* - name: snap
|
||||
* in: query
|
||||
* required: true
|
||||
* type: string
|
||||
* - name: regions
|
||||
* in: query
|
||||
* required: true
|
||||
* type: string
|
||||
* - name: rg
|
||||
* in: query
|
||||
* required: true
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* 404:
|
||||
* description: Not found
|
||||
*
|
||||
*/
|
||||
router.post('/copysnapshots', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
queues[SYNAPSE_QUEUE].add("synapse_job", {
|
||||
snapName: req.query.snap,
|
||||
rGroup: req.query.rg,
|
||||
regions: req.query.regions,
|
||||
tasktype: "copy-snapshots"
|
||||
});
|
||||
|
||||
return res.json({"msg": "Snapshots are being craeted", "success": true});
|
||||
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /provisions/copysnapshots:
|
||||
* get:
|
||||
* description: Get all vmImages
|
||||
* summary: Get all vmImages
|
||||
* description: Get Status of copy snapshots into regions
|
||||
* summary: Get Status of copy snapshots into regions
|
||||
* tags:
|
||||
* - admin
|
||||
* produces:
|
||||
* - application/json
|
||||
* parameters:
|
||||
* - name: snap
|
||||
* in: query
|
||||
* required: true
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* 404:
|
||||
* description: Not found
|
||||
*
|
||||
*/
|
||||
router.get('/copysnapshots', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
queues[SYNAPSE_QUEUE].add("synapse_job", {
|
||||
snapName: req.query.snap,
|
||||
tasktype: "check-copy-snapshots"
|
||||
});
|
||||
|
||||
return res.json({"msg": "Snapshots are being checked", "success": true});
|
||||
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /provisions/snapshotslogs:
|
||||
* get:
|
||||
* description: Get logs for snapshots
|
||||
* summary: Get logs for snapshots
|
||||
* tags:
|
||||
* - admin
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
* 200:
|
||||
* description: OK
|
||||
* 404:
|
||||
* description: Not found
|
||||
*
|
||||
*/
|
||||
router.get('/snapshotslogs', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
return res.sendFile("/logs/general/qmi-snapshots.log");
|
||||
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /provisions/optionssLogs:
|
||||
* get:
|
||||
* description: Get all optionss
|
||||
* summary: Get all optionss
|
||||
* produces:
|
||||
* - application/json
|
||||
* parameters:
|
||||
@@ -112,25 +226,25 @@ router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) =>
|
||||
* 200:
|
||||
* description: JSON Array
|
||||
*/
|
||||
router.get('/vmImagesLogs', passport.ensureAuthenticated, async (req, res, next) => {
|
||||
router.get('/optionssLogs', passport.ensureAuthenticated, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
let filter = req.query.filter? JSON.parse(req.query.filter) : {};
|
||||
filter.isDeleted = {"$exists": true};
|
||||
let select = "_id vmImage"
|
||||
let select = "_id options"
|
||||
let populates = "[]";
|
||||
|
||||
const result = await db.provision.get(filter, select, req.query.skip, req.query.limit, populates);
|
||||
var final = [];
|
||||
result.results.forEach(p=>{
|
||||
for( let key in p.vmImage ) {
|
||||
for( let key in p.options ) {
|
||||
var o = {
|
||||
"_id": p._id.toString(),
|
||||
"vmIndex": key,
|
||||
"vmType": p.vmImage[key].vmType? p.vmImage[key].vmType : null,
|
||||
"diskSizeGb": p.vmImage[key].diskSizeGb? p.vmImage[key].diskSizeGb : null,
|
||||
"versionProduct": p.vmImage[key].version? p.vmImage[key].version.name : null,
|
||||
"versionImage": p.vmImage[key].version? p.vmImage[key].version.image : null
|
||||
"vmType": p.options[key].vmType? p.options[key].vmType : null,
|
||||
"diskSizeGb": p.options[key].diskSizeGb? p.options[key].diskSizeGb : null,
|
||||
"versionProduct": p.options[key].selected? p.options[key].selected.name : null,
|
||||
"versionImage": p.options[key].selected? p.options[key].selected.value : null
|
||||
}
|
||||
final.push(o);
|
||||
}
|
||||
@@ -140,7 +254,7 @@ router.get('/vmImagesLogs', passport.ensureAuthenticated, async (req, res, next)
|
||||
count: result.count
|
||||
};
|
||||
if ( result.nextSkip && result.nextLimit ) {
|
||||
out.nextUrl = new URL(req.protocol + '://' + req.get('Host') + req.baseUrl);
|
||||
out.nextUrl = new URL('https://' + req.hostname + req.baseUrl);
|
||||
if ( req.query.filter ) {
|
||||
out.nextUrl.searchParams.append("filter", req.query.filter);
|
||||
}
|
||||
@@ -348,4 +462,5 @@ router.post('/:id/updatetagsvms', passport.ensureAuthenticatedAndAdmin, async (r
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,7 +1,7 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const db = require('qmi-cloud-common/mongo');
|
||||
const passport = require('../passport');
|
||||
const passport = require('../passport-okta');
|
||||
|
||||
|
||||
router.get('/all', passport.ensureAuthenticated, async (req, res, next) => {
|
||||
@@ -203,7 +203,7 @@ router.delete('/vmtypes/:id', passport.ensureAuthenticatedAndAdmin, async (req,
|
||||
* type: string
|
||||
* index:
|
||||
* type: string
|
||||
* versions:
|
||||
* values:
|
||||
* type: array
|
||||
* items:
|
||||
* type: object
|
||||
|
||||
446
server/routes/api-training.js
Normal file
@@ -0,0 +1,446 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const db = require('qmi-cloud-common/mongo');
|
||||
const passport = require('../passport-okta');
|
||||
const cloudshare = require('../training/cloudshare');
|
||||
const qa = require('../training/automations');
|
||||
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /training/sessions:
|
||||
* get:
|
||||
* description: Get all training sessions (admin)
|
||||
* summary: Get all training sessions (admin)
|
||||
* tags:
|
||||
* - admin
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
* 200:
|
||||
* description: TrainingTemplate
|
||||
*/
|
||||
router.get('/sessions', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
const filter = req.query.filter? JSON.parse(req.query.filter) : {};
|
||||
const result = await db.trainingSession.get(filter);
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /training/templates:
|
||||
* get:
|
||||
* description: Get all training templates
|
||||
* summary: Get all training templates
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
* 200:
|
||||
* description: TrainingTemplate
|
||||
*/
|
||||
router.get('/templates', passport.ensureAuthenticated, async (req, res, next) => {
|
||||
try {
|
||||
const filter = req.query.filter? JSON.parse(req.query.filter) : {};
|
||||
const result = await db.trainingTemplate.get(filter);
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /training/session/{id}:
|
||||
* get:
|
||||
* description: Get training session by ID
|
||||
* summary: Get training session by ID
|
||||
* produces:
|
||||
* - application/json
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* responses:
|
||||
* 200:
|
||||
* description: TrainingTemplate
|
||||
*/
|
||||
router.get('/session/:id', async (req, res, next) => {
|
||||
try {
|
||||
const result = await db.trainingSession.get({"_id":req.params.id}, 'passwd -user description status created updated studentEmailFilter');
|
||||
if (!result || !result.results || result.results.length === 0 ){
|
||||
return res.status(404).json({"msg": "Not found"});
|
||||
}
|
||||
return res.json(result.results[0]);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /training/session/{id}:
|
||||
* post:
|
||||
* description: Post new student for session by ID
|
||||
* summary: Post new student for session by ID
|
||||
* produces:
|
||||
* - application/json
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* responses:
|
||||
* 200:
|
||||
* description: TrainingTemplate
|
||||
*/
|
||||
router.post('/session/:id', async (req, res, next) => {
|
||||
try {
|
||||
const session = await db.trainingSession.getById(req.params.id);
|
||||
if (!session){
|
||||
return res.status(404).json({"msg": "Not found"});
|
||||
}
|
||||
|
||||
let student = await db.trainingStudent.getOne({"email": req.body.email, "session": session._id});
|
||||
let isNew = false;
|
||||
if ( !student ) {
|
||||
let data = req.body;
|
||||
data.session = session._id;
|
||||
student = await db.trainingStudent.add(data);
|
||||
isNew = true;
|
||||
}
|
||||
|
||||
let result1, result2;
|
||||
if ( session.template.cloudshare && session.cloudshareClass ) {
|
||||
result1 = await cloudshare.addStudentToClass(session, student.email);
|
||||
}
|
||||
if ( session.template.needQcsAutomation && session.qaUrl && session.qaToken ) {
|
||||
result2 = await qa.runQlikAutomation(session, student.email);
|
||||
}
|
||||
|
||||
if (result1 && result1.error || result2 && result2.error) {
|
||||
return res.status(500).json({"msg": "Something went wrong when registering studeent", err: result1.error? result1.error : result2.error});
|
||||
} else {
|
||||
student = await db.trainingStudent.update(student._id, {"status": "sent", "created": new Date()});
|
||||
if ( isNew ) {
|
||||
db.trainingSession.update({ _id: session._id }, { $inc: { studentsCount: 1 } });
|
||||
}
|
||||
return res.json(student);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /training/session/{id}:
|
||||
* delete:
|
||||
* description: Delete session by ID
|
||||
* summary: Delete session by ID
|
||||
* tags:
|
||||
* - admin
|
||||
* produces:
|
||||
* - application/json
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* responses:
|
||||
* 200:
|
||||
* description: TrainingTemplate
|
||||
*/
|
||||
router.delete('/session/:id', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
const result = await db.trainingSession.update(req.params.id, {status: "terminated"});
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /training/templates:
|
||||
* post:
|
||||
* description: Create new training template
|
||||
* summary: Create new training template
|
||||
* tags:
|
||||
* - admin
|
||||
* produces:
|
||||
* - application/json
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* description:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: TrainingTemplate
|
||||
*/
|
||||
router.post('/templates', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
let data = req.body;
|
||||
const result = await db.trainingTemplate.add(data);
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /training/{userId}/sessions:
|
||||
* post:
|
||||
* description: Add new training session
|
||||
* summary: Add new training session
|
||||
* parameters:
|
||||
* - name: userId
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
* 200:
|
||||
* description: TrainingSession
|
||||
*/
|
||||
router.post('/:userId/sessions', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
try {
|
||||
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||
let data = req.body;
|
||||
data.user = userId;
|
||||
let session = await db.trainingSession.add(data);
|
||||
session = await db.trainingSession.getById(session._id);
|
||||
|
||||
let mainAutomation = await qa.getAutomation(session, 'main');
|
||||
if (!mainAutomation) {
|
||||
let result = await qa.createAutomations(session);
|
||||
mainAutomation = result.main;
|
||||
}
|
||||
session = await db.trainingSession.update(session._id, {qaToken: mainAutomation.executionToken, qaUrl: `https://${session.qcsTenantHost}/api/v1/automations/${mainAutomation.id}/actions/execute`})
|
||||
|
||||
return res.json(session);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /training/{userId}/sessions:
|
||||
* get:
|
||||
* description: Get all training session by user
|
||||
* summary: Get all training session by user
|
||||
* parameters:
|
||||
* - name: userId
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
* 200:
|
||||
* description: TrainingSession
|
||||
*/
|
||||
router.get('/:userId/sessions', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
try {
|
||||
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||
const result = await db.trainingSession.get({user: userId, status: {"$ne": "terminated"}});
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /training/{userId}/sessions/{id}:
|
||||
* get:
|
||||
* description: Get training session by Id for a user
|
||||
* summary: Get training session by Id for a user
|
||||
* parameters:
|
||||
* - name: userId
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* - name: id
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
* 200:
|
||||
* description: TrainingSession
|
||||
*/
|
||||
router.get('/:userId/sessions/:id', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
try {
|
||||
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||
const id = req.params.id;
|
||||
const result = await db.trainingSession.getOne({user: userId, _id: id});
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /training/{userId}/sessions/{id}/automation:
|
||||
* get:
|
||||
* description: Test if automation exists in tenant
|
||||
* summary: Test if automation exists in tenant
|
||||
* parameters:
|
||||
* - name: userId
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* - name: id
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
* 200:
|
||||
* description: TrainingSession
|
||||
*/
|
||||
router.get('/:userId/sessions/:id/automation', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
try {
|
||||
const id = req.params.id;
|
||||
let session = await db.trainingSession.getById(id);
|
||||
|
||||
let automation = await qa.getAutomation(session);
|
||||
if (automation) {
|
||||
session = await db.trainingSession.update(session._id, {qaToken: automation.executionToken, qaUrl: `https://${session.qcsTenantHost}/api/v1/automations/${automation.id}/actions/execute`})
|
||||
}
|
||||
return res.json(session);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /training/session/{id}/students:
|
||||
* get:
|
||||
* description: Get studetns for a session
|
||||
* summary: Get studetns for a session
|
||||
* parameters:
|
||||
* - name: userId
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* - name: id
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
* 200:
|
||||
* description: TrainingSession
|
||||
*/
|
||||
router.get('/session/:id/students', passport.ensureAuthenticated, async (req, res, next) => {
|
||||
try {
|
||||
const id = req.params.id;
|
||||
const result = await db.trainingStudent.get({session: id});
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /training/{userId}/sessions/{id}:
|
||||
* put:
|
||||
* description: Update training session
|
||||
* summary: Update training session
|
||||
* parameters:
|
||||
* - name: userId
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* - name: id
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
* 200:
|
||||
* description: TrainingSession
|
||||
*/
|
||||
router.put('/:userId/sessions/:id', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
|
||||
try {
|
||||
const result = await db.trainingSession.update(req.params.id, req.body);
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /training/{userId}/sessions/{id}:
|
||||
* put:
|
||||
* description: Update training session
|
||||
* summary: Update training session
|
||||
* parameters:
|
||||
* - name: userId
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* - name: id
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
* 200:
|
||||
* description: TrainingSession
|
||||
*/
|
||||
router.delete('/:userId/sessions/:id', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
|
||||
try {
|
||||
const result = await db.trainingSession.update(req.params.id, {status: "terminated"});
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -1,11 +1,11 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const db = require('qmi-cloud-common/mongo');
|
||||
const config = require('qmi-cloud-common/config');
|
||||
const passport = require('../passport');
|
||||
const passport = require('../passport-okta');
|
||||
const fs = require('fs-extra');
|
||||
const cli = require('qmi-cloud-common/cli');
|
||||
const barracuda = require('qmi-cloud-common/barracuda');
|
||||
const sendEmail = require("qmi-cloud-common/send-email");
|
||||
|
||||
async function asyncForEach(array, callback) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
@@ -13,7 +13,7 @@ async function asyncForEach(array, callback) {
|
||||
}
|
||||
}
|
||||
|
||||
import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CONTAINER_QUEUE } from 'qmi-cloud-common/queues';
|
||||
import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CONTAINER_QUEUE, SYNAPSE_QUEUE } from 'qmi-cloud-common/queues';
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@@ -21,8 +21,6 @@ import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CO
|
||||
* get:
|
||||
* description: Get all users
|
||||
* summary: Get all users
|
||||
* tags:
|
||||
* - admin
|
||||
* parameters:
|
||||
* - name: filter
|
||||
* in: query
|
||||
@@ -38,7 +36,7 @@ import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CO
|
||||
* 200:
|
||||
* description: User
|
||||
*/
|
||||
router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
router.get('/', passport.ensureAuthenticated, async (req, res, next) => {
|
||||
try {
|
||||
const filter = req.query.filter? JSON.parse(req.query.filter) : {};
|
||||
const result = await db.user.get(filter);
|
||||
@@ -155,7 +153,7 @@ router.put('/:userId', passport.ensureAuthenticatedAndAdmin, async (req, res, ne
|
||||
* type: string
|
||||
* description:
|
||||
* type: string
|
||||
* vmImage:
|
||||
* options:
|
||||
* type: object
|
||||
* properties:
|
||||
* vm1:
|
||||
@@ -176,7 +174,7 @@ router.put('/:userId', passport.ensureAuthenticatedAndAdmin, async (req, res, ne
|
||||
* 404:
|
||||
* description: Scenario not found
|
||||
* 400:
|
||||
* description: Invalid vmImage
|
||||
* description: Invalid options
|
||||
*/
|
||||
router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
try {
|
||||
@@ -196,8 +194,8 @@ router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (r
|
||||
return res.status(400).json({"msg": "Number of simultaneous provisions reached for this scenario: " + scenarioSource.numSimultaneousProvisions});
|
||||
}
|
||||
|
||||
//if (!req.body.vmImage || !req.body.vmImage.vm1 || !req.body.vmImage.vm1.vmType ) {
|
||||
// return res.status(400).json({"msg": "Invalid vmImage"});
|
||||
//if (!req.body.options || !req.body.options.vm1 || !req.body.options.vm1.vmType ) {
|
||||
// return res.status(400).json({"msg": "Invalid options"});
|
||||
//}
|
||||
|
||||
req.body.scenarioVersion = scenarioSource.version;
|
||||
@@ -205,14 +203,72 @@ router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (r
|
||||
if ( req.body.scheduleData && req.body.scheduleData.is24x7 !== undefined ) {
|
||||
const schedule = await db.schedule.add(req.body.scheduleData);
|
||||
req.body.schedule = schedule._id;
|
||||
} else {
|
||||
req.body.schedule = null;
|
||||
}
|
||||
|
||||
req.body.terraformImage = config.DOCKERIMAGE_TERRAFORM;
|
||||
|
||||
if ( req.body.scenario.indexOf('azqmi-qdi') !== -1 ) {
|
||||
req.body.version = config.PROVISION_VERSION;
|
||||
req.body.terraformImage = scenarioSource.terraformImage || process.env.DOCKERIMAGE_TERRAFORM;
|
||||
|
||||
//---Accomodation old QDI Cloud Storage
|
||||
if (req.body.vmImage) {
|
||||
req.body.options = {};
|
||||
if (req.body.vmImage.db) {
|
||||
if (req.body.scenario === 'azqmi-synapse'){
|
||||
req.body.options['sku'] = {
|
||||
selected: {
|
||||
value: req.body.vmImage.db.version.image,
|
||||
name: req.body.vmImage.db.version.name
|
||||
}
|
||||
};
|
||||
} else {
|
||||
req.body.options['db'] = {
|
||||
selected: {
|
||||
value: req.body.vmImage.db.version.image,
|
||||
name: req.body.vmImage.db.version.name
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (req.body.vmImage.qdi_sa_id) {
|
||||
req.body.options['qdi_sa_id'] = {
|
||||
selected: {
|
||||
value: req.body.vmImage.qdi_sa_id.version.image,
|
||||
name: req.body.vmImage.qdi_sa_id.version.name
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (req.body.vmImage.qdi_repl_id) {
|
||||
req.body.options['qdi_repl_id'] = {
|
||||
selected: {
|
||||
value: req.body.vmImage.qdi_repl_id.version.image,
|
||||
name: req.body.vmImage.qdi_repl_id.version.name
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (req.body.vmImage.qdisacc_name) {
|
||||
req.body.options['qdisacc_name'] = {
|
||||
selected: {
|
||||
value: req.body.vmImage.qdisacc_name.version.image,
|
||||
name: req.body.vmImage.qdisacc_name.version.name
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (req.body.vmImage.qdisacc_accesskey) {
|
||||
req.body.options['qdisacc_accesskey'] = {
|
||||
selected: {
|
||||
value: req.body.vmImage.qdisacc_accesskey.version.image,
|
||||
name: req.body.vmImage.qdisacc_accesskey.version.name
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
delete req.body['vmImage'];
|
||||
//---
|
||||
|
||||
const provision = await db.provision.add(req.body);
|
||||
|
||||
@@ -302,6 +358,7 @@ router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (r
|
||||
* description: Provision
|
||||
*/
|
||||
router.put('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
const triggerUser = req.user;
|
||||
let provision = await db.provision.getById(req.params.id);
|
||||
if (!provision){
|
||||
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
|
||||
@@ -311,12 +368,16 @@ router.put('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, async
|
||||
|
||||
let schedule;
|
||||
var msg = "";
|
||||
if ( req.body.scheduleData ) {
|
||||
|
||||
let patch = req.body || {};
|
||||
|
||||
if ( req.body.scheduleData ) {
|
||||
if ( req.body.scheduleData._id ) {
|
||||
schedule = await db.schedule.update(req.body.scheduleData._id, req.body.scheduleData);
|
||||
msg += `Updated running schedule (${schedule._id}). `;
|
||||
} else {
|
||||
schedule = await db.schedule.add(req.body.scheduleData);
|
||||
msg += `New running schedule (${schedule._id}). `;
|
||||
}
|
||||
var tagsEdit = {
|
||||
"24x7": schedule.is24x7? " " : false,
|
||||
@@ -324,25 +385,24 @@ router.put('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, async
|
||||
"ShutdownTime": (!schedule.is24x7 && schedule.utcTagShutdownTime)? schedule.utcTagShutdownTime : false
|
||||
}
|
||||
cli.updateVmsTags(provision._id, tagsEdit);
|
||||
|
||||
patch.schedule = schedule._id;
|
||||
delete patch['scheduleData'];
|
||||
}
|
||||
var resShared = null;
|
||||
var resProvision = await db.provision.update(provision._id, patch);
|
||||
|
||||
let patch = {};
|
||||
if ( req.body.user ) {
|
||||
patch.user = req.body.user;
|
||||
msg += ` - new user: ${req.body.user}`;
|
||||
}
|
||||
if ( schedule ) {
|
||||
patch.schedule = schedule._id;
|
||||
msg += ` - new schedule: ${schedule._id}`;
|
||||
|
||||
if ( patch.user ) {
|
||||
msg += `Changed owner (${patch.user}).`;
|
||||
resShared = await db.sharedProvision.updateMany({"provision": provision._id}, {"user": patch.user});
|
||||
}
|
||||
|
||||
let result = {
|
||||
provision: await db.provision.update(provision._id, patch),
|
||||
event: await db.event.updateMany({"provision": provision._id}, {"user": req.body.user})
|
||||
provision: resProvision,
|
||||
sharedProvision: resShared
|
||||
}
|
||||
|
||||
db.event.add({ user: provision.user._id, provision: provision._id, type: 'provision.update', message: msg });
|
||||
db.event.add({ user: triggerUser._id, provision: provision._id, type: 'provision.update', message: msg });
|
||||
|
||||
return res.json(result);
|
||||
|
||||
@@ -377,7 +437,7 @@ router.put('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, async
|
||||
*/
|
||||
router.delete('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
const triggerUser = req.user;
|
||||
const provision = await db.provision.getById(req.params.id);
|
||||
if (!provision){
|
||||
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
|
||||
@@ -393,7 +453,7 @@ router.delete('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, as
|
||||
fs.moveSync(`/provisions/${provision.scenario}_${req.params.id}`, `/provisions/deleted/${provision.scenario}_${req.params.id}`, { overwrite: true })
|
||||
}
|
||||
|
||||
db.event.add({ user: provision.user._id, provision: provision._id, type: 'provision.delete-history' });
|
||||
db.event.add({ user: triggerUser._id, provision: provision._id, type: 'provision.delete-history' });
|
||||
|
||||
return res.json({"provision": delProv, "destroy": delProv.destroy});
|
||||
} catch (error) {
|
||||
@@ -618,6 +678,8 @@ router.delete('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, as
|
||||
router.post('/:userId/provisions/:id/deallocatevms', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
const triggerUser = req.user;
|
||||
|
||||
let provision = await db.provision.getById(req.params.id);
|
||||
if (!provision){
|
||||
return res.status(404).json({"msg": "Not found provision with id "+req.params.id});
|
||||
@@ -635,9 +697,20 @@ router.post('/:userId/provisions/:id/deallocatevms', passport.ensureAuthenticate
|
||||
cli.updateVmsTags(provision._id, tagsEdit);
|
||||
}
|
||||
|
||||
cli.deallocate(provision._id);
|
||||
if (provision.scenario === 'azqmi-synapse') {
|
||||
queues[SYNAPSE_QUEUE].add("synapse_job", {
|
||||
provId: provision._id,
|
||||
tasktype: 'pause',
|
||||
user: req.user
|
||||
});
|
||||
} else if(provision.scenario === 'awsqmi-rds') {
|
||||
cli.stopDb(provision._id, triggerUser._id);
|
||||
} else {
|
||||
cli.deallocate(provision._id, triggerUser._id);
|
||||
}
|
||||
|
||||
return res.json({"statusVms": "Stopping"});
|
||||
provision.statusVms = "Stopping";
|
||||
return res.json(provision);
|
||||
|
||||
} catch (error) {
|
||||
db.provision.update(req.params.id, {"statusVms": "Error_Stopping"});
|
||||
@@ -671,7 +744,8 @@ router.post('/:userId/provisions/:id/deallocatevms', passport.ensureAuthenticate
|
||||
*/
|
||||
router.post('/:userId/provisions/:id/startvms', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
const triggerUser = req.user;
|
||||
|
||||
let provision = await db.provision.getById(req.params.id);
|
||||
if (!provision){
|
||||
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
|
||||
@@ -691,9 +765,21 @@ router.post('/:userId/provisions/:id/startvms', passport.ensureAuthenticatedAndI
|
||||
cli.updateVmsTags(provision._id, tagsEdit);
|
||||
}
|
||||
|
||||
cli.start(provision._id);
|
||||
if (provision.scenario === 'azqmi-synapse') {
|
||||
queues[SYNAPSE_QUEUE].add("synapse_job", {
|
||||
provId: provision._id,
|
||||
tasktype: 'resume',
|
||||
user: req.user
|
||||
});
|
||||
} else if(provision.scenario === 'awsqmi-rds') {
|
||||
cli.startDb(provision._id, triggerUser._id);
|
||||
} else {
|
||||
cli.start(provision._id, triggerUser._id);
|
||||
}
|
||||
|
||||
|
||||
return res.json({"statusVms": "Starting"});
|
||||
provision.statusVms = "Starting";
|
||||
return res.json(provision);
|
||||
|
||||
} catch (error) {
|
||||
db.provision.update(req.params.id, {"statusVms": "Error_Starting"});
|
||||
@@ -739,7 +825,7 @@ router.post('/:userId/provisions/:id/extend', passport.ensureAuthenticatedAndIsM
|
||||
|
||||
let timeRunning = db.utils.getNewTimeRunning(provision);
|
||||
let countExtend = db.utils.getNewCountExtend(provision);
|
||||
provision = await db.provision.update(req.params.id, {"runningFrom":new Date(), "timeRunning": timeRunning, "countExtend": countExtend, "pendingNextAction": undefined});
|
||||
provision = await db.provision.update(req.params.id, {"runningFrom":new Date(), "timeRunning": timeRunning, "countExtend": countExtend, "pendingNextAction": null});
|
||||
|
||||
console.log(`APIUser# Extending running period fo provision (${provision._id}), new total extends: ${countExtend}`);
|
||||
|
||||
@@ -855,6 +941,8 @@ router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIs
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* - in: body
|
||||
* name: body
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Provision
|
||||
@@ -867,22 +955,28 @@ router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIs
|
||||
try {
|
||||
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||
|
||||
if ( req.params.withUserId === userId ) {
|
||||
return res.status(400).json({"msg": "Can't share with the same user"});
|
||||
}
|
||||
|
||||
let provision = await db.provision.getById(req.params.id);
|
||||
if (!provision){
|
||||
var shareWithUser = await db.user.getById(req.params.withUserId);
|
||||
if (!provision || !shareWithUser){
|
||||
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
|
||||
}
|
||||
|
||||
if ( req.params.withUserId === provision.user._id ) {
|
||||
return res.status(400).json({"msg": "Can't share with the same user"});
|
||||
}
|
||||
|
||||
let found = await db.sharedProvision.getOne({
|
||||
"user": userId,
|
||||
"user": provision.user._id,
|
||||
"provision": provision._id,
|
||||
"sharedWithUser": req.params.withUserId
|
||||
});
|
||||
if ( !found ) {
|
||||
found = await db.sharedProvision.add({"user": userId, "provision": provision._id, "sharedWithUser": req.params.withUserId})
|
||||
let canManage = req.body && req.body.type === "manage";
|
||||
found = await db.sharedProvision.add({"user": provision.user._id, "provision": provision._id, "sharedWithUser": req.params.withUserId, "canManage": canManage})
|
||||
sendEmail.sendSharedProvision(provision, shareWithUser);
|
||||
|
||||
db.event.add({ user: userId, provision: provision._id, type: 'provision.share', 'message': `Start sharing with user (${req.params.withUserId}) with "${canManage? 'manage' : 'view'}" permission` });
|
||||
|
||||
}
|
||||
|
||||
return res.json(found);
|
||||
@@ -928,7 +1022,10 @@ router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIs
|
||||
if ( req.params.withUserId === userId ) {
|
||||
return res.status(400).json({"msg": "Can't share with the same user"});
|
||||
}
|
||||
const result = await db.sharedProvision.delMany({"user": userId, "provision": req.params.id, "sharedWithUser": req.params.withUserId});
|
||||
const result = await db.sharedProvision.delMany({"provision": req.params.id, "sharedWithUser": req.params.withUserId});
|
||||
|
||||
db.event.add({ user: userId, provision: req.params.id, type: 'provision.stop-share', 'message': `Stop sharing with user (${req.params.withUserId})` });
|
||||
|
||||
return res.json(result);
|
||||
|
||||
} catch (error) {
|
||||
@@ -969,7 +1066,7 @@ router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIs
|
||||
return res.status(400).json({"msg": "Can't share with the same user"});
|
||||
}
|
||||
|
||||
const result = await db.sharedProvision.delMany({"user": userId, "provision": req.params.id});
|
||||
const result = await db.sharedProvision.delMany({"provision": req.params.id, "sharedWithUser": req.params.withUserId});
|
||||
return res.json(result);
|
||||
|
||||
} catch (error) {
|
||||
@@ -1001,12 +1098,10 @@ router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIs
|
||||
* description: Not found
|
||||
*
|
||||
*/
|
||||
router.get('/:userId/provisions/:id/share', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
router.get('/:userId/provisions/:id/share', passport.ensureAuthenticated, async (req, res, next) => {
|
||||
|
||||
try {
|
||||
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||
|
||||
const result = await db.sharedProvision.get({"user": userId, "provision": req.params.id});
|
||||
let result = await db.sharedProvision.get({"provision": req.params.id});
|
||||
return res.json(result);
|
||||
|
||||
} catch (error) {
|
||||
@@ -1038,8 +1133,8 @@ router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIs
|
||||
|
||||
try {
|
||||
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||
let result = await db.sharedProvision.get({"sharedWithUser": userId});
|
||||
return res.json(result);
|
||||
let out = await db.sharedProvision.get({"sharedWithUser": userId});
|
||||
return res.json(out);
|
||||
|
||||
} catch (error) {
|
||||
return res.status(error.output.statusCode).json({"err":error});
|
||||
@@ -1092,7 +1187,7 @@ router.get('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (re
|
||||
* 200:
|
||||
* description: JSON Array
|
||||
*/
|
||||
router.get('/:userId/events', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
router.get('/:userId/events', passport.ensureAuthenticated, async (req, res, next) => {
|
||||
|
||||
try {
|
||||
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||
@@ -1124,11 +1219,11 @@ router.get('/:userId/events', passport.ensureAuthenticatedAndIsMe, async (req, r
|
||||
* 200:
|
||||
* description: JSON Array
|
||||
*/
|
||||
router.get('/:userId/provisions/:id/events', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
router.get('/:userId/provisions/:id/events', passport.ensureAuthenticated, async (req, res, next) => {
|
||||
|
||||
try {
|
||||
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||
const result = await db.event.get({"user": userId, "provision": req.params.id});
|
||||
const result = await db.event.get({"provision": req.params.id});
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
|
||||
269
server/routes/qsProxy.js
Normal file
@@ -0,0 +1,269 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
|
||||
const jsonwebtoken = require("jsonwebtoken");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const cookie = require("cookie");
|
||||
const cookieParser = require("cookie-parser");
|
||||
const axios = require("axios");
|
||||
const { v4: uuidv4 } = require("uuid");
|
||||
const ws = require("ws");
|
||||
const WebSocketServer = ws.WebSocketServer;
|
||||
const WebSocket = ws.WebSocket;
|
||||
const passport = require('../passport-okta');
|
||||
|
||||
|
||||
// This is the frontend application uri used for responding to requests.
|
||||
//const frontendUri = "https://outstanding-desert-gatsby.glitch.me";
|
||||
|
||||
|
||||
const privKey = fs.readFileSync(path.resolve(__dirname, '..', 'certs', 'privateKey.pem'));
|
||||
const TENANT_DOMAIN = process.env["TENANT_DOMAIN"] || "innovation.us.qlikcloud.com";
|
||||
const JWT_KEYID = process.env["JWT_KEYID"] || "8889be0d-6cf0-44eb-9fef-24bbc83712c5";
|
||||
const JWT_ISSUER = process.env["JWT_ISSUER"] || TENANT_DOMAIN;
|
||||
const JWT_USER_GROUPS = ["AnonJWTGroup"];
|
||||
|
||||
console.log("QSProxy# - TENANT_DOMAIN", TENANT_DOMAIN);
|
||||
|
||||
const auth = {
|
||||
generateToken: function (user) {
|
||||
// kid and issuer have to match with the IDP config and the audience has to be qlik.api/jwt-login-session
|
||||
|
||||
const signingOptions = {
|
||||
keyid: JWT_KEYID,
|
||||
algorithm: "RS256",
|
||||
issuer: JWT_ISSUER,
|
||||
expiresIn: "60m",
|
||||
notBefore: "0s",
|
||||
audience: "qlik.api/login/jwt-session",
|
||||
};
|
||||
|
||||
// These are the claims that will be accepted and mapped anything else will be ignored. sub, subtype and name are mandatory.
|
||||
const uuid = uuidv4();
|
||||
userEmail = user.mail;
|
||||
const payload = {
|
||||
jti: uuid,
|
||||
sub: `az/${user.sub}`,
|
||||
subType: "user",
|
||||
name: user.displayName,
|
||||
email: userEmail,
|
||||
email_verified: true,
|
||||
groups: JWT_USER_GROUPS,
|
||||
};
|
||||
|
||||
const token = jsonwebtoken.sign(payload, privKey, signingOptions);
|
||||
return token;
|
||||
},
|
||||
getQlikSessionCookie: async function (token) {
|
||||
|
||||
try {
|
||||
const resp = await axios(`https://${TENANT_DOMAIN}/login/jwt-session`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (resp.status === 200) {
|
||||
console.log("QSProxy# - login/jwt-session", resp.headers['set-cookie'])
|
||||
return resp.headers['set-cookie']
|
||||
.map((e) => {
|
||||
return e.split(";")[0];
|
||||
})
|
||||
.join(";");
|
||||
}
|
||||
return "";
|
||||
} catch (e) {
|
||||
console.log("QSProxy# - ERRRR", e);
|
||||
return "";
|
||||
}
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
const getStoreData = function (sidParsed) {
|
||||
const store = passport.sessionStore;
|
||||
return new Promise((resolve) => {
|
||||
store.get(sidParsed, (err, session) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
return resolve(session);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
router.get("/*.js", async (req, res) => {
|
||||
setCors(res);
|
||||
res.redirect(`https://${TENANT_DOMAIN}${req.path}`);
|
||||
res.end();
|
||||
});
|
||||
|
||||
// fetch resource from qlik using a redirect instead of proxy
|
||||
// This endpoint is necessary when your web application uses the capability API.
|
||||
router.get("/resources/*", async (req, res) => {
|
||||
setCors(res);
|
||||
res.redirect(`https://${TENANT_DOMAIN}${req.path}`);
|
||||
res.end();
|
||||
});
|
||||
|
||||
router.get('/assets/*', async (req, res) => {
|
||||
setCors(res);
|
||||
res.redirect(`https://${TENANT_DOMAIN}${req.path}`);
|
||||
res.end();
|
||||
});
|
||||
|
||||
// Issues the necessary pre-flight request to make sure the browser
|
||||
// knows how to work with the web application.
|
||||
router.options("/*", async (req, res) => {
|
||||
setCors(res);
|
||||
res.status(200).end();
|
||||
});
|
||||
|
||||
function setCors(res) {
|
||||
//res.set("Access-Control-Allow-Origin", "http://localhost:3000");
|
||||
//res.set("Access-Control-Allow-Methods", "GET, OPTIONS");
|
||||
//res.set("Access-Control-Allow-Headers", "Content-Type, x-proxy-session-id");
|
||||
//res.set("Access-Control-Allow-Credentials", "true");
|
||||
}
|
||||
|
||||
async function newSession(req){
|
||||
console.log("QSProxy# - New cookie request for user", req.user.mail);
|
||||
const jwtToken = auth.generateToken(req.user);
|
||||
const qlikSession = await auth.getQlikSessionCookie(jwtToken);
|
||||
return encodeURIComponent(qlikSession);
|
||||
}
|
||||
|
||||
// Intercepts a request to one of Qlik's REST APIs and proxies the request to
|
||||
// Qlik Cloud.
|
||||
router.get("/api/v1/*", passport.ensureAuthenticated, async (req, res) => {
|
||||
|
||||
setCors(res);
|
||||
|
||||
var session = req.session;
|
||||
|
||||
var reqHeaders = {};
|
||||
try {
|
||||
if (session && session.id && session.qlikSession) {
|
||||
console.log("QSProxy# - COOKIE FROM session", session.qlikSession);
|
||||
reqHeaders.cookie = decodeURIComponent(session.qlikSession);
|
||||
} else {
|
||||
|
||||
const newS = await newSession(req);
|
||||
session.qlikSession = newS;
|
||||
reqHeaders.cookie = decodeURIComponent(session.qlikSession);
|
||||
}
|
||||
|
||||
try {
|
||||
console.log("QSProxy# - qlikSession", reqHeaders.cookie);
|
||||
const { status, data } = await axios(`https://${TENANT_DOMAIN}${req.path}`, {
|
||||
headers: reqHeaders,
|
||||
});
|
||||
res.status(status).end(data);
|
||||
|
||||
} catch (e2) {
|
||||
console.log("QSProxy# Error: QlikSession expired, requesting a new one.");
|
||||
let newS = await newSession(req);
|
||||
session.qlikSession = newS;
|
||||
reqHeaders.cookie = decodeURIComponent(session.qlikSession);
|
||||
let { status, data } = await axios(`https://${TENANT_DOMAIN}${req.path}`, {
|
||||
headers: reqHeaders,
|
||||
});
|
||||
res.status(status).end(data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
} catch (e) {
|
||||
res.status(500).end("Error obtaining qlik session");
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
router.get('/qlik-embed-iframe/*', async (req, res) => {
|
||||
setCors(res);
|
||||
res.redirect(`https://${TENANT_DOMAIN}${req.path}`);
|
||||
res.end();
|
||||
});
|
||||
|
||||
function init (server) {
|
||||
|
||||
// Websocket section for intercepting websocket requests from the
|
||||
// frontend application. When the front end application communicates
|
||||
// communicates with the backend using websockets, this set of
|
||||
// functions will be invoked.
|
||||
const wss = new WebSocketServer({ server });
|
||||
|
||||
wss.on("connection", async function connection(ws, req) {
|
||||
let isOpened = false;
|
||||
// WebSockets do not have access to session information.
|
||||
// To get the session you need to parse the 1st-party cookie.
|
||||
// This will give you access to the Qlik Cloud cookie in order
|
||||
// to proxy requests.
|
||||
const cookieString = req.headers.cookie;
|
||||
|
||||
let qlikCookie = "";
|
||||
if (cookieString) {
|
||||
const cookieParsed = cookie.parse(cookieString);
|
||||
const appCookie = cookieParsed["connect.sid"];
|
||||
if (appCookie) {
|
||||
const sidParsed = cookieParser.signedCookie(appCookie, "secret");
|
||||
var session = await getStoreData(sidParsed);
|
||||
|
||||
qlikCookie = decodeURIComponent(session.qlikSession);
|
||||
}
|
||||
}
|
||||
|
||||
const appId = req.url.match("/app/(.*)\\?")[1];
|
||||
|
||||
if (!qlikCookie){
|
||||
console.log("QSProxy# - Error in Websocket: NO qlikCookie!");
|
||||
return;
|
||||
}
|
||||
|
||||
const matchCookie = qlikCookie.match("_csrfToken=(.*);");
|
||||
|
||||
if (!matchCookie) {
|
||||
console.log("QSProxy# - Error in Websocket: cant find _csrfToken= in qlikCookie");
|
||||
return;
|
||||
}
|
||||
|
||||
const csrfToken = matchCookie[1];
|
||||
|
||||
var wsConnUrl = `wss://${TENANT_DOMAIN}/app/${appId}?qlik-csrf-token=${csrfToken}`;
|
||||
const qlikWebSocket = new WebSocket(
|
||||
wsConnUrl,
|
||||
{
|
||||
headers: {
|
||||
cookie: qlikCookie,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
qlikWebSocket.on("error", console.error);
|
||||
const openPromise = new Promise((resolve) => {
|
||||
qlikWebSocket.on("open", function open() {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
|
||||
ws.on("message", async function message(data) {
|
||||
if (!isOpened) {
|
||||
await openPromise;
|
||||
isOpened = true;
|
||||
}
|
||||
|
||||
qlikWebSocket.send(data.toString());
|
||||
});
|
||||
|
||||
qlikWebSocket.on("message", function message(data) {
|
||||
ws.send(data.toString());
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
module.exports.router = router;
|
||||
module.exports.init = init;
|
||||
|
||||