152 Commits

Author SHA1 Message Date
Manuel Romero
69b92a64bc awscli 2021-07-09 12:32:29 +02:00
Manuel Romero
f84ecd7212 new barracuda api version 2021-07-09 09:44:04 +02:00
Manuel Romero
5b6bb90119 simple barracuda 2021-06-25 12:00:02 +02:00
Manuel Romero
d5637065c2 fixes for barracuda 2021-06-25 11:30:57 +02:00
Manuel Romero
6d322c3cc2 fix 2021-06-10 10:55:58 +02:00
Manuel Romero
e0ba340ca1 info times 2021-06-10 10:39:01 +02:00
Manuel Romero
79ec7af773 fixed readme 2021-06-09 16:13:01 +02:00
Manuel Romero
752a3329be periods per scenario 2021-06-09 15:46:22 +02:00
Manuel Romero
36a246504f scenario model allowedInnactiveHours 2021-06-09 13:43:34 +02:00
Manuel Romero
b2a4b41023 n/a for non vm provisions 2021-06-04 09:39:40 +02:00
Manuel Romero
852c943f99 Logevent 2021-06-01 15:12:17 +02:00
Manuel Romero
96e33ca765 azurewebhook 2021-06-01 11:36:08 +02:00
Manuel Romero
bae80ec847 azurewebhook 2021-06-01 10:49:52 +02:00
Manuel Romero
ce55788a26 Delete BarracudaApp first 2021-05-27 11:54:14 +02:00
Manuel Romero
17e128099e new barracuda api 0.0.8 2021-05-26 17:40:57 +02:00
Manuel Romero
17f391ffbd new barracuda api version 2021-05-26 17:35:17 +02:00
Manuel Romero
23bf5f58f6 Only cli for provision with VMs 2021-05-07 13:43:03 +02:00
Manuel Romero
b36616f28a fix 2021-04-30 16:51:58 +02:00
Manuel Romero
5e266a4461 ui fixes 2021-04-30 16:23:04 +02:00
Manuel Romero
0dd199d5f8 no vmImage 2021-04-30 15:53:19 +02:00
Manuel Romero
0f020e96a7 fix 2021-04-29 13:31:37 +02:00
Manuel Romero
0dcba31636 fix 2021-04-28 14:06:48 +02:00
Manuel Romero
04221eb03a using lock icon 2021-04-28 10:06:35 +02:00
Manuel Romero
c12039d354 info class 2021-04-23 13:51:06 +02:00
Manuel Romero
c5dcc547a9 QDC barracuda 2021-04-23 13:38:12 +02:00
Manuel Romero
5c3e10f9df Some buttons 2021-04-20 16:22:37 +02:00
Manuel Romero
e1848da829 Adding user dashbooard 2021-04-20 14:40:16 +02:00
Manuel Romero
a4ae065d8b ttl to 60 for cnames 2021-04-19 13:00:06 +02:00
Manuel Romero
8e0ffd72fa create barracuda app at provision 2021-04-16 15:23:08 +02:00
Manuel Romero
fafe697af5 create barracuda app at provision 2021-04-16 15:21:58 +02:00
Manuel Romero
2cf319175d fix 2021-04-16 14:39:50 +02:00
Manuel Romero
643cb9775d change region barracuda 2021-04-16 13:21:16 +02:00
Manuel Romero
66085e49c5 getting barracuda detauls in one 2021-04-16 11:33:50 +02:00
Manuel Romero
fd85d65aeb barracuda app status 2021-04-16 11:12:02 +02:00
Manuel Romero
24a3573f73 logs 2021-04-16 10:09:48 +02:00
Manuel Romero
048fc6d89d gitbranch for scenarios 2021-04-16 09:38:56 +02:00
Manuel Romero
bf20f4efd4 gitbranch for scenarios 2021-04-16 09:32:47 +02:00
Manuel Romero
2fd05a2b54 adding git 2021-04-15 14:43:05 +02:00
Manuel Romero
03ce50c255 Some logs 2021-04-15 14:03:53 +02:00
Manuel Romero
171f552571 Some logs 2021-04-15 14:01:12 +02:00
Manuel Romero
1843907b42 Testing delete barracuda at destroy provision 2021-04-15 13:06:32 +02:00
Manuel Romero
d88a6f2359 fix 2021-04-14 13:05:03 +02:00
Manuel Romero
d273bc937e delete barracuda endpoint 2021-04-13 15:58:30 +02:00
Manuel Romero
6777d2f0d8 delete barracuda endpoint 2021-04-13 15:56:40 +02:00
Manuel Romero
3d2506639d Adding creation of DNS record 2021-04-13 15:41:09 +02:00
Manuel Romero
a9806bd94e Changed dockerfile to install git 2021-04-13 14:50:20 +02:00
Manuel Romero
783642f083 barracuda email password 2021-04-13 13:37:16 +02:00
Manuel Romero
2a7fec03d7 Barrcuda API endpoint 2021-04-13 13:32:13 +02:00
Manuel Romero
aed18cccfa No logs tf output 2021-04-08 10:46:06 +02:00
Manuel Romero
0f12ee6649 testing tags are the same 2021-04-07 11:07:02 +02:00
Manuel Romero
fbc727a66e allow userId as 'me' 2021-03-31 15:29:37 +02:00
Manuel Romero
897fa2a631 trycatch error setting tags 2021-03-31 10:33:41 +02:00
Manuel Romero
43a428592e Merge branch 'dev' 2021-03-25 15:53:32 +01:00
Manuel Romero
c3796b478f Sort updated on Scenarios 2021-03-25 15:53:23 +01:00
Manuel Romero
e12f60516b sort first by updated 2021-03-25 15:31:28 +01:00
Manuel Romero
84fe322c96 sort updated 2021-03-25 15:00:57 +01:00
Manuel Romero
84263b08dd Merge branch 'dev' 2021-03-25 12:21:31 +01:00
Manuel Romero
895e069326 Using new node version 15.12 2021-03-25 12:21:07 +01:00
Manuel Romero
da4c940055 fix azurecli module 2021-03-25 12:02:30 +01:00
Manuel Romero
f9e608c06c fix 2021-03-17 15:31:00 +01:00
Manuel Romero
15e8c12508 readme 2021-03-17 12:45:42 +01:00
Manuel Romero
1e543b1e6f Destroy on aborted for user 2021-03-17 12:34:40 +01:00
Manuel Romero
00739082f2 movedestroyed with date 2021-03-17 12:04:10 +01:00
Manuel Romero
aacf23f57b movedestroyed 2021-03-17 11:31:42 +01:00
Manuel Romero
ca037b42ef updatemany 2021-03-17 09:55:56 +01:00
Manuel Romero
96300e1e99 fix axios final 2021-03-16 17:49:35 +01:00
Manuel Romero
721fbe24b8 fix axios 2021-03-16 17:45:34 +01:00
Manuel Romero
b20a6fe858 fix axios 2021-03-16 17:30:53 +01:00
Manuel Romero
658bd6a131 abort from UI 2021-03-16 17:20:33 +01:00
Manuel Romero
be5c9ef85e fix 2021-03-16 14:51:17 +01:00
Manuel Romero
5cc6f7a3c6 Test queue stop container 2021-03-16 13:30:24 +01:00
Manuel Romero
5fdd26306a abort job 2021-03-16 11:24:06 +01:00
Manuel Romero
0e74d8873f disable cache stats 2021-03-10 09:18:33 +01:00
Manuel Romero
3f8e9290de cached stats 2021-03-09 09:32:33 +01:00
Manuel Romero
034dde5e1d fix 2021-03-05 18:06:36 +01:00
Manuel Romero
d0e274cd5f fix 2021-03-05 18:00:11 +01:00
Manuel Romero
b3de18bbda fix 2021-03-05 17:55:47 +01:00
Manuel Romero
e02ac30cde fix 2021-03-05 17:44:00 +01:00
Manuel Romero
49b444cfc8 fix 2021-03-05 17:32:50 +01:00
Manuel Romero
1ab9055fc5 request up to 6 2021-03-05 17:30:00 +01:00
Manuel Romero
fd8e542467 adding page vms 2021-03-05 17:08:10 +01:00
Manuel Romero
7699978f39 Added nextlink 2021-03-05 16:48:32 +01:00
Manuel Romero
902db15c17 using azurerm new lib 2021-03-05 15:14:14 +01:00
Manuel Romero
49bd59df62 vms stats 2021-03-05 13:50:13 +01:00
Manuel Romero
b7e44f436c separate vms endpoints 2021-03-05 12:25:16 +01:00
Manuel Romero
aa26928716 stats vms 2021-03-05 12:22:41 +01:00
Manuel Romero
8509a520cf Adding stats endpoint 2021-03-05 11:36:37 +01:00
Manuel Romero
0037634b70 New topbar logo 2021-03-03 16:56:23 +01:00
Manuel Romero
6dfab50871 fix assign new users on events 2021-03-03 14:54:51 +01:00
Manuel Romero
fcc46cdb1a fix check 2021-03-01 19:41:14 +01:00
Manuel Romero
06c4fae9c2 fix check 2021-03-01 19:35:36 +01:00
Manuel Romero
8b18a65303 ffix 2021-02-28 20:28:28 +01:00
Manuel Romero
e4ad1dde54 Better events logs 2021-02-28 20:26:48 +01:00
Manuel Romero
a2b82501aa Changed onschedule timer algorithm 2021-02-28 18:14:08 +01:00
Manuel Romero
ad0c4cbbd1 schedule log event 2021-02-26 12:35:51 +01:00
Manuel Romero
1a638c16bf fix events 2021-02-26 11:52:24 +01:00
Manuel Romero
7dd9fc79c2 show events 2021-02-25 20:03:00 +01:00
Manuel Romero
d492c5e4a8 more events 2021-02-25 18:18:24 +01:00
Manuel Romero
c2a14e5f79 Event collection 2021-02-25 18:10:25 +01:00
Manuel Romero
c888c068b4 fix nootifi 2021-02-25 17:23:05 +01:00
Manuel Romero
7b0bacc990 notificates from divvy and user api 2021-02-25 16:49:47 +01:00
Manuel Romero
4d66c40c07 onscheduled running calculation 2021-02-25 12:39:20 +01:00
Manuel Romero
8a86130fe6 fix 2021-02-25 09:37:18 +01:00
Manuel Romero
b7db4a5f00 fix 2021-02-24 15:00:51 +01:00
Manuel Romero
f3b9869d4f fix 2021-02-24 13:00:23 +01:00
Manuel Romero
0801f84b10 queues logs 2021-02-09 13:56:20 +01:00
Manuel Romero
9dd091f982 pipeline status 2021-02-09 12:05:26 +01:00
Manuel Romero
190967a889 Using qlik smpt sender 2021-02-09 10:42:33 +01:00
Manuel Romero
510254922b adding axios 2021-02-05 17:43:03 +01:00
Manuel Romero
91da6199b3 Merge branch 'dev' 2021-02-05 17:16:32 +01:00
Manuel Romero
9374a60e2a send message with BAM 2021-02-05 17:16:25 +01:00
Manuel Romero
0e1817947e fix 2021-02-05 17:01:23 +01:00
Manuel Romero
870ad47cbc fix 2021-02-05 16:57:24 +01:00
Manuel Romero
4304c56685 fix 2021-02-05 16:36:09 +01:00
Manuel Romero
b7dfee506f Send with BAM 2021-02-05 16:31:49 +01:00
Manuel Romero
022b414766 fix 2021-02-05 14:44:08 +01:00
Manuel Romero
05143c78c0 fix 2021-02-05 13:15:13 +01:00
Manuel Romero
04ed3a22ef test email 2021-02-05 13:13:48 +01:00
Manuel Romero
d3f0513224 worker 2021-02-05 11:28:49 +01:00
Manuel Romero
d5efa608bc warning error cli 2020-12-04 13:11:03 +01:00
Manuel Romero
040d2187cb try catch start stop vms 2020-12-04 10:46:00 +01:00
Manuel Romero
2d254ae238 first tags then start 2020-12-03 12:18:05 +01:00
Manuel Romero
82861f2130 wait for updating tags azure 2020-12-03 11:43:21 +01:00
Manuel Romero
e0270e6925 wait for updating tags azure 2020-12-03 11:39:35 +01:00
Manuel Romero
dc2180af03 Using avatar-ui for avatarars 2020-12-01 12:40:06 +01:00
Manuel Romero
d240b06c5c support emails 2020-11-30 16:03:50 +01:00
Manuel Romero
2434d2d157 cosmetics 2020-11-26 14:12:16 +01:00
Manuel Romero
c631fb141e Adding user_email to qdi scenario 2020-11-24 09:34:45 +01:00
Manuel Romero
4e27a35e4e Adding scenario titles 2020-11-18 10:40:28 +01:00
Manuel Romero
1b458ab2df New image 2020-11-13 14:13:40 +01:00
Manuel Romero
082e98c01a Adding init password 2020-11-11 14:19:59 +01:00
Manuel Romero
c6d1f84c57 new versions 2020-11-03 13:51:03 +01:00
Manuel Romero
c3b9e8532f fix2 2020-11-03 13:36:03 +01:00
Manuel Romero
4194973884 fix 2020-11-03 12:56:00 +01:00
Manuel Romero
57a47d7794 fix 2020-11-03 12:52:04 +01:00
Manuel Romero
953930bc42 Synapse databases and others 2020-11-03 12:36:50 +01:00
Manuel Romero
35d78b4db0 Do not display error 2020-10-23 14:05:27 +02:00
Manuel Romero
b8a8b8bace error passport image 2020-10-23 12:08:37 +02:00
Manuel Romero
e09d03f1ef Photos 2020-10-23 10:46:07 +02:00
Manuel Romero
dd2f328f45 fix sort by region 2020-10-22 15:51:24 +02:00
Manuel Romero
b3895be44a Set url for sent emails 2020-10-22 15:39:21 +02:00
Manuel Romero
7516f46903 fix filter scenarioos 2020-10-20 21:22:46 +02:00
Manuel Romero
3c7327ed57 Allowed users 2020-10-20 11:34:20 +02:00
Manuel Romero
8e1c027038 No logs 2020-10-19 18:55:51 +02:00
Manuel Romero
f8fe33039c better ui 2020-10-19 18:51:05 +02:00
Manuel Romero
bf6ea3cd51 better ui 2020-10-19 18:43:45 +02:00
Manuel Romero
e3aeeea51f Allowed users for a scenario 2020-10-19 18:37:27 +02:00
Manuel Romero
1fba40289e Default enable if optional server 2020-10-19 11:17:04 +02:00
Manuel Romero
b32c18a349 Set default terraform image 2020-10-16 09:57:48 +02:00
Manuel Romero
9fc6bdd5a3 fix 2020-10-16 09:12:31 +02:00
Manuel Romero
755fd11f1a logs terraform version 2020-10-15 18:31:19 +02:00
Manuel Romero
82984673aa New package versions 2020-10-15 17:42:29 +02:00
125 changed files with 8769 additions and 3021 deletions

2
.gitignore vendored
View File

@@ -48,4 +48,4 @@ secrets.json
qmi-cloud-tf-modules/
*.pfx
/photos/*

View File

@@ -1,7 +1,7 @@
# Stage 1:
FROM node:13.8-alpine AS sources
FROM node:15.12.0-alpine AS sources
RUN apk --no-cache add yarn
RUN apk --no-cache add yarn git
WORKDIR /var/www/app
@@ -12,7 +12,7 @@ ADD ./qmi-cloud-common ./qmi-cloud-common
RUN yarn install --production
# Stage 2:
FROM node:13.8-alpine AS production
FROM node:15.12.0-alpine AS production
WORKDIR /var/www/app
COPY --from=sources /var/www/app/node_modules ./node_modules
COPY --from=sources /var/www/app/package.json ./package.json

View File

@@ -1,5 +1,7 @@
# QMI Cloud
[![pipeline status](https://gitlab.com/qmi/qmi-cloud/badges/master/pipeline.svg)](https://gitlab.com/qmi/qmi-cloud/-/commits/master)
## Pre-requisites
- Docker
- Docker-Compose

BIN
dist/qmi-cloud/assets/user1.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 713 KiB

After

Width:  |  Height:  |  Size: 699 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -1,12 +1,12 @@
<?xml version="1.0" standalone="no"?>
<!--
Font Awesome Free 5.14.0 by @fontawesome - https://fontawesome.com
Font Awesome Free 5.13.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 20200314 at Wed Jul 15 11:59:40 2020
Created by FontForge 20190801 at Mon Mar 23 10:45:51 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.004 448.1"
bbox="-0.0663408 -64.0662 640.01 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="&#xf024;"
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.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
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
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="&#xf02e;" 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="&#xf06e;" horiz-adv-x="576"
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" />
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" />
<glyph glyph-name="eye-slash" unicode="&#xf070;" horiz-adv-x="640"
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" />
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" />
<glyph glyph-name="calendar-alt" unicode="&#xf073;" 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="&#xf0a0;" 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 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" />
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" />
<glyph glyph-name="hand-point-right" unicode="&#xf0a4;"
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
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
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
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
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
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="&#xf0a5;"
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-3.49414 6.85938 -6.21582 14.3584 -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-2.96094 5.8125 -6.51367 15.6973 -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="&#xf0a6;" 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.6465c7.70605 -1.71387 15.2051 -4.43555 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.6465c6.36719 -1.41602 16.252 -4.96875 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 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" />
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" />
<glyph glyph-name="hand-point-down" unicode="&#xf0a7;" 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-6.85938 -3.49414 -14.3584 -6.21582 -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-5.8125 -2.96094 -15.6973 -6.51367 -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="&#xf0c5;" horiz-adv-x="448"
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" />
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" />
<glyph glyph-name="save" unicode="&#xf0c7;" horiz-adv-x="448"
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" />
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" />
<glyph glyph-name="square" unicode="&#xf0c8;" 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="&#xf0e0;"
@@ -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="&#xf0eb;" 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.00488281 4.98145 0.790039 9.78809 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.00390625 4.04199 0.999023 10.4482 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="&#xf14d;" 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.5068c7.4375 2.125 14.5508 5.30566 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.5068c6.14844 1.75684 15.5449 5.92383 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.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
" />
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" />
<glyph glyph-name="compass" unicode="&#xf14e;" horiz-adv-x="496"
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
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
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="&#xf150;" 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="&#xf186;"
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
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
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="&#xf191;" 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="&#xf1c7;" horiz-adv-x="384"
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
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
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="&#xf1c8;" horiz-adv-x="384"
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
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
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="&#xf1c9;" 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="&#xf1f9;"
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.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" />
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" />
<glyph glyph-name="closed-captioning" unicode="&#xf20a;"
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="&#xf249;" horiz-adv-x="448"
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
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
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="&#xf24d;"
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.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" />
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" />
<glyph glyph-name="hand-paper" unicode="&#xf256;" horiz-adv-x="448"
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
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
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,46 +424,45 @@ c2.63086 11.417 3.96484 23.1553 3.96484 34.8857z" />
<glyph glyph-name="hand-scissors" unicode="&#xf257;"
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.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
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
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="&#xf258;" 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.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" />
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" />
<glyph glyph-name="hand-spock" unicode="&#xf259;"
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" />
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" />
<glyph glyph-name="hand-pointer" unicode="&#xf25a;" horiz-adv-x="448"
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" />
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" />
<glyph glyph-name="hand-peace" unicode="&#xf25b;" 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 -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
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
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.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-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-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="&#xf25d;"
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.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" />
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" />
<glyph glyph-name="calendar-plus" unicode="&#xf271;" 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
@@ -482,9 +481,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="&#xf279;" horiz-adv-x="576"
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
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
M528 65.0801v288.97l-127.36 -47.0693l-0.639648 -0.240234v-286.19z" />
<glyph glyph-name="comment-alt" unicode="&#xf27a;"
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
@@ -498,16 +497,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="&#xf2b5;" 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.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
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
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="&#xf2b6;"
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
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
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
@@ -556,11 +555,10 @@ 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="&#xf2ed;" horiz-adv-x="448"
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" />
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" />
<glyph glyph-name="images" unicode="&#xf302;" 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
@@ -586,9 +584,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="&#xf3d1;" 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.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
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
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="&#xf410;"
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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 893 KiB

After

Width:  |  Height:  |  Size: 876 KiB

View File

@@ -6,8 +6,8 @@
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="assets/favicon.ico">
<link rel="stylesheet" href="styles.b2e9059fb6aa834811ea.css"></head>
<link rel="stylesheet" href="styles.d596c43fc1f81eecfae1.css"></head>
<body>
<app-root></app-root>
<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.5c4c2f7e4461741ce9f1.js" defer></script></body>
<script src="runtime.689ba4fd6cadb82c1ac2.js" defer></script><script src="polyfills-es5.2f1b30b563fe6f309b2d.js" nomodule defer></script><script src="polyfills.60117177d3b4f4827ace.js" defer></script><script src="scripts.73c34722d75b092f2620.js" defer></script><script src="main.ece894ac50d4edc475b5.js" defer></script></body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -77,6 +77,7 @@ services:
- ../qmi-cloud-provisions:/provisions
- ./logs:/logs
- ./costexport:/var/www/app/costexport
- ./photos:/var/www/app/photos
#- ./certs:/var/www/app/server/certs
depends_on:
- mongo
@@ -95,6 +96,7 @@ services:
container_name: qmi-cloud-worker
restart: on-failure
environment:
- HOSTNAME_URL=http://localhost:3000
- REDIS_URL=redis://redis
- MONGO_URI=mongodb://root:example@mongo/qmicloud?authSource=admin
- PROJECT_PATH=%PWD%
@@ -106,7 +108,7 @@ services:
command: "sh -c 'npm run start:dev'"
volumes:
# -- Dev only volumes
- ./worker:/app/worker
- ./qmi-cloud-worker:/app/worker
# -------------------
- ./logs:/logs
- /var/run/docker.sock:/home/docker.sock

470
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "qmi-cloud-app",
"version": "1.1.3",
"version": "1.2.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -440,6 +440,206 @@
"integrity": "sha512-IZG1kvw48JyFRy7bfMHqBixWrEHZmXmkP5DWsi5Tw6KusaczkMghI20BevCkodPcajXWHAUHNKyp1tlE3OnH0w==",
"dev": true
},
"@angular/localize": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/@angular/localize/-/localize-9.0.7.tgz",
"integrity": "sha512-nlmcxEVZmz4ty1MXE1OFnHMYndedQUunZY4MB3lm7c8MJrRnI5x7zAOvkwI+sUF0Hy3rfjkO7ddOzGBfDtjPPA==",
"requires": {
"@babel/core": "7.8.3",
"glob": "7.1.2",
"yargs": "13.1.0"
},
"dependencies": {
"@babel/core": {
"version": "7.8.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.8.3.tgz",
"integrity": "sha512-4XFkf8AwyrEG7Ziu3L2L0Cv+WyY47Tcsp70JFmpftbAA1K7YL/sgE9jh9HyNj08Y/U50ItUchpN0w6HxAoX1rA==",
"requires": {
"@babel/code-frame": "^7.8.3",
"@babel/generator": "^7.8.3",
"@babel/helpers": "^7.8.3",
"@babel/parser": "^7.8.3",
"@babel/template": "^7.8.3",
"@babel/traverse": "^7.8.3",
"@babel/types": "^7.8.3",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.1",
"json5": "^2.1.0",
"lodash": "^4.17.13",
"resolve": "^1.3.2",
"semver": "^5.4.1",
"source-map": "^0.5.0"
}
},
"@babel/generator": {
"version": "7.13.9",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.13.9.tgz",
"integrity": "sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw==",
"requires": {
"@babel/types": "^7.13.0",
"jsesc": "^2.5.1",
"source-map": "^0.5.0"
},
"dependencies": {
"@babel/types": {
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.13.0.tgz",
"integrity": "sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA==",
"requires": {
"@babel/helper-validator-identifier": "^7.12.11",
"lodash": "^4.17.19",
"to-fast-properties": "^2.0.0"
}
},
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
}
}
},
"@babel/helper-validator-identifier": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
"integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw=="
},
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg=="
},
"emoji-regex": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
"integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA=="
},
"find-up": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
"requires": {
"locate-path": "^3.0.0"
}
},
"get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="
},
"glob": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
"integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"json5": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.0.tgz",
"integrity": "sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA==",
"requires": {
"minimist": "^1.2.5"
}
},
"locate-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
"requires": {
"p-locate": "^3.0.0",
"path-exists": "^3.0.0"
}
},
"p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
"requires": {
"p-try": "^2.0.0"
}
},
"p-locate": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
"requires": {
"p-limit": "^2.0.0"
}
},
"p-try": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
},
"require-main-filename": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
},
"source-map": {
"version": "0.5.7",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
},
"string-width": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
"integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
"requires": {
"emoji-regex": "^7.0.1",
"is-fullwidth-code-point": "^2.0.0",
"strip-ansi": "^5.1.0"
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
"integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
"requires": {
"ansi-regex": "^4.1.0"
}
},
"yargs": {
"version": "13.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.1.0.tgz",
"integrity": "sha512-1UhJbXfzHiPqkfXNHYhiz79qM/kZqjTE8yGlEjZa85Q+3+OwcV6NRkV7XOV1W2Eom2bzILeUn55pQYffjVOLAg==",
"requires": {
"cliui": "^4.0.0",
"find-up": "^3.0.0",
"get-caller-file": "^2.0.1",
"os-locale": "^3.1.0",
"require-directory": "^2.1.1",
"require-main-filename": "^2.0.0",
"set-blocking": "^2.0.0",
"string-width": "^3.0.0",
"which-module": "^2.0.0",
"y18n": "^4.0.0",
"yargs-parser": "^13.0.0"
}
},
"yargs-parser": {
"version": "13.1.2",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
"integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
}
}
},
"@angular/platform-browser": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-9.0.7.tgz",
@@ -455,6 +655,120 @@
"resolved": "https://registry.npmjs.org/@angular/router/-/router-9.0.7.tgz",
"integrity": "sha512-uKru9F/Zju//gg6INl54abnlpLdEUUO/GpCfMk4zqu8LCZGNFta6OY7VT+9DK9Vdrh/XUD70oE9WoelcRwwTYA=="
},
"@azure/abort-controller": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.4.tgz",
"integrity": "sha512-lNUmDRVGpanCsiUN3NWxFTdwmdFI53xwhkTFfHDGTYk46ca7Ind3nanJc+U6Zj9Tv+9nTCWRBscWEW1DyKOpTw==",
"requires": {
"tslib": "^2.0.0"
},
"dependencies": {
"tslib": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
}
}
},
"@azure/arm-compute": {
"version": "15.0.0",
"resolved": "https://registry.npmjs.org/@azure/arm-compute/-/arm-compute-15.0.0.tgz",
"integrity": "sha512-ElV2MuYZ+B2+ygMx2iiM/u3C7WDRruZjkXzfk5p2O+UbWxjG6j/686OH3T+VSDqmzg+47AnIOTLu2v0u0H8FOw==",
"requires": {
"@azure/ms-rest-azure-js": "^2.0.1",
"@azure/ms-rest-js": "^2.0.4",
"tslib": "^1.10.0"
}
},
"@azure/arm-dns": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@azure/arm-dns/-/arm-dns-4.0.0.tgz",
"integrity": "sha512-VhI8NRd6hyHKxMSTqUWpozQ//D4S1CuxFMRDao/Bzs0ETUIUem4DNOJAk5Zn5+IWfDDpJnRlLqLCspfMD5/V8A==",
"requires": {
"@azure/ms-rest-azure-js": "^2.0.1",
"@azure/ms-rest-js": "^2.0.4",
"tslib": "^1.10.0"
}
},
"@azure/core-auth": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.3.0.tgz",
"integrity": "sha512-kSDSZBL6c0CYdhb+7KuutnKGf2geeT+bCJAgccB0DD7wmNJSsQPcF7TcuoZX83B7VK4tLz/u+8sOO/CnCsYp8A==",
"requires": {
"@azure/abort-controller": "^1.0.0",
"tslib": "^2.0.0"
},
"dependencies": {
"tslib": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
"integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
}
}
},
"@azure/ms-rest-azure-env": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@azure/ms-rest-azure-env/-/ms-rest-azure-env-2.0.0.tgz",
"integrity": "sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw=="
},
"@azure/ms-rest-azure-js": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@azure/ms-rest-azure-js/-/ms-rest-azure-js-2.1.0.tgz",
"integrity": "sha512-CjZjB8apvXl5h97Ck6SbeeCmU0sk56YPozPtTyGudPp1RGoHXNjFNtoOvwOG76EdpmMpxbK10DqcygI16Lu60Q==",
"requires": {
"@azure/core-auth": "^1.1.4",
"@azure/ms-rest-js": "^2.2.0",
"tslib": "^1.10.0"
}
},
"@azure/ms-rest-js": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/@azure/ms-rest-js/-/ms-rest-js-2.3.0.tgz",
"integrity": "sha512-8NOnHgovi61NpcUld53zRkY/IcQJBBO48VeMntNTUtaPo8yYYTnu1hWRvp6b6vpBnur7HGmuj692J9li5Kx6/Q==",
"requires": {
"@azure/core-auth": "^1.1.4",
"abort-controller": "^3.0.0",
"form-data": "^2.5.0",
"node-fetch": "^2.6.0",
"tough-cookie": "^3.0.1",
"tslib": "^1.10.0",
"tunnel": "0.0.6",
"uuid": "^3.3.2",
"xml2js": "^0.4.19"
},
"dependencies": {
"form-data": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
}
},
"tough-cookie": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
"integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
"requires": {
"ip-regex": "^2.1.0",
"psl": "^1.1.28",
"punycode": "^2.1.1"
}
}
}
},
"@azure/ms-rest-nodeauth": {
"version": "3.0.9",
"resolved": "https://registry.npmjs.org/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-3.0.9.tgz",
"integrity": "sha512-+GdDHUJlWtIDanRZemFooLy68NsBDhN/Oni9DSFeoXIFNGlSe1IOes8/IRkQdrNXyUvBanuzzR7I5WYYzYQsmA==",
"requires": {
"@azure/ms-rest-azure-env": "^2.0.0",
"@azure/ms-rest-js": "^2.0.4",
"adal-node": "^0.1.28"
}
},
"@babel/code-frame": {
"version": "7.10.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz",
@@ -1337,6 +1651,11 @@
"resolved": "https://registry.npmjs.org/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz",
"integrity": "sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q=="
},
"@ng-bootstrap/ng-bootstrap": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-6.2.0.tgz",
"integrity": "sha512-wqwhnJFyEwvzWQJjXrEt+7oBTSvu2qPbdYvUFYhDVzOJLWB5M7YEhDAkMrfHQJ0pZNBMGr580FqYue+XiURY0Q=="
},
"@ngtools/webpack": {
"version": "9.0.7",
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-9.0.7.tgz",
@@ -1488,8 +1807,7 @@
"@types/node": {
"version": "8.9.5",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.9.5.tgz",
"integrity": "sha512-jRHfWsvyMtXdbhnz5CVHxaBgnV6duZnPlQuRSo/dm/GnmikNcmZhxIES4E9OZjUmQ8C+HCl4KJux+cXN/ErGDQ==",
"dev": true
"integrity": "sha512-jRHfWsvyMtXdbhnz5CVHxaBgnV6duZnPlQuRSo/dm/GnmikNcmZhxIES4E9OZjUmQ8C+HCl4KJux+cXN/ErGDQ=="
},
"@types/prop-types": {
"version": "15.7.3",
@@ -1753,6 +2071,14 @@
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"requires": {
"event-target-shim": "^5.0.0"
}
},
"accepts": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
@@ -1794,13 +2120,6 @@
"uuid": "^3.1.0",
"xmldom": ">= 0.1.x",
"xpath.js": "~1.1.0"
},
"dependencies": {
"@types/node": {
"version": "8.10.61",
"resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.61.tgz",
"integrity": "sha512-l+zSbvT8TPRaCxL1l9cwHCb0tSqGAGcjPJFItGGYat5oCTiq1uQQKYg5m7AF1mgnEBzFXGLJ2LRmNjtreRX76Q=="
}
}
},
"adm-zip": {
@@ -2370,36 +2689,12 @@
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz",
"integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA=="
},
"azure-arm-compute": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/azure-arm-compute/-/azure-arm-compute-10.0.0.tgz",
"integrity": "sha512-ehafNtcMKI6c00FT+xhPWPtzhYgHCZ675TUsaJ1FJ2bSpznih5EUrpir/4w519T4zbFBigszhnRK6eBkl78I9g==",
"axios": {
"version": "0.21.1",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz",
"integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==",
"requires": {
"ms-rest": "^2.5.0",
"ms-rest-azure": "^2.5.5"
},
"dependencies": {
"async": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
"integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
"requires": {
"lodash": "^4.14.0"
}
},
"ms-rest-azure": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/ms-rest-azure/-/ms-rest-azure-2.6.0.tgz",
"integrity": "sha512-J6386a9krZ4VtU7CRt+Ypgo9RGf8+d3gjMBkH7zbkM4zzkhbbMOYiPRaZ+bHZcfihkKLlktTgA6rjshTjF329A==",
"requires": {
"adal-node": "^0.1.28",
"async": "2.6.0",
"moment": "^2.22.2",
"ms-rest": "^2.3.2",
"request": "^2.88.0",
"uuid": "^3.2.1"
}
}
"follow-redirects": "^1.10.0"
}
},
"babel-code-frame": {
@@ -2488,6 +2783,13 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"barracuda-api": {
"version": "git+https://gitlab.com/qlik_gear/barracuda-api-node.git#a4a15a6766f3c66196ed1854cc301575c218323f",
"from": "git+https://gitlab.com/qlik_gear/barracuda-api-node.git#0.0.3",
"requires": {
"axios": "^0.21.1"
}
},
"base": {
"version": "0.11.2",
"resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
@@ -4938,11 +5240,6 @@
"nan": "^2.14.0"
}
},
"duplexer": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
"integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E="
},
"duplexer3": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
@@ -5311,6 +5608,11 @@
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc="
},
"event-target-shim": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
},
"eventemitter3": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.4.tgz",
@@ -6052,6 +6354,11 @@
"integrity": "sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA==",
"dev": true
},
"gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="
},
"geojson-vt": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-3.2.1.tgz",
@@ -9059,44 +9366,6 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"ms-rest": {
"version": "2.5.4",
"resolved": "https://registry.npmjs.org/ms-rest/-/ms-rest-2.5.4.tgz",
"integrity": "sha512-VeqCbawxRM6nhw0RKNfj7TWL7SL8PB6MypqwgylXCi+u412uvYoyY/kSmO8n06wyd8nIcnTbYToCmSKFMI1mCg==",
"requires": {
"duplexer": "^0.1.1",
"is-buffer": "^1.1.6",
"is-stream": "^1.1.0",
"moment": "^2.21.0",
"request": "^2.88.0",
"through": "^2.3.8",
"tunnel": "0.0.5",
"uuid": "^3.2.1"
}
},
"ms-rest-azure": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/ms-rest-azure/-/ms-rest-azure-3.0.0.tgz",
"integrity": "sha512-cttN01/TtMDB4v3rt/WQ/slgffB6jcUYxcPzcL0VNSB+WFPE1j4y5ICNHMuD1RaNNekCYMI4Pv51BDQ/BXNq7Q==",
"requires": {
"adal-node": "^0.1.28",
"async": "2.6.0",
"moment": "^2.22.2",
"ms-rest": "^2.3.2",
"request": "^2.88.0",
"uuid": "^3.2.1"
},
"dependencies": {
"async": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
"integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
"requires": {
"lodash": "^4.14.0"
}
}
}
},
"multicast-dns": {
"version": "6.2.3",
"resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-6.2.3.tgz",
@@ -9214,6 +9483,11 @@
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
},
"node-fetch": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"node-fetch-npm": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz",
@@ -11353,11 +11627,14 @@
"qmi-cloud-common": {
"version": "file:qmi-cloud-common",
"requires": {
"@azure/arm-compute": "^15.0.0",
"@azure/arm-dns": "^4.0.0",
"@azure/ms-rest-nodeauth": "^3.0.7",
"@hapi/boom": "^9.1.0",
"azure-arm-compute": "^10.0.0",
"axios": "^0.21.1",
"barracuda-api": "git+https://gitlab.com/qlik_gear/barracuda-api-node.git#0.0.3",
"bull": "^3.11.0",
"mongoose": "^5.7.4",
"ms-rest-azure": "^3.0.0",
"nodemailer": "^6.4.2"
}
},
@@ -13594,7 +13871,8 @@
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
},
"through2": {
"version": "2.0.5",
@@ -13802,9 +14080,9 @@
"integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY="
},
"tunnel": {
"version": "0.0.5",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.5.tgz",
"integrity": "sha512-gj5sdqherx4VZKMcBA4vewER7zdK25Td+z1npBqpbDys4eJrLx+SlYjJvq1bDXs2irkuJM5pf8ktaEQVipkrbA=="
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
"integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
},
"tunnel-agent": {
"version": "0.6.0",
@@ -13990,9 +14268,9 @@
}
},
"underscore": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.10.2.tgz",
"integrity": "sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg=="
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.0.tgz",
"integrity": "sha512-sCs4H3pCytsb5K7i072FAEC9YlSYFIbosvM0tAKAlpSSUgD7yC1iXSEGdl5XrDKQ1YUB+p/HDzYrSG2H2Vl36g=="
},
"unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
@@ -15066,7 +15344,6 @@
"version": "0.4.23",
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz",
"integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==",
"dev": true,
"requires": {
"sax": ">=0.6.0",
"xmlbuilder": "~11.0.0"
@@ -15075,13 +15352,12 @@
"xmlbuilder": {
"version": "11.0.1",
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
"dev": true
"integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA=="
},
"xmldom": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.3.0.tgz",
"integrity": "sha512-z9s6k3wxE+aZHgXYxSTpGDo7BYOUfJsIRyoZiX6HTjwpwfS2wpQBQKa2fD+ShLyPkqDYo5ud7KitmLZ2Cd6r0g=="
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.5.0.tgz",
"integrity": "sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA=="
},
"xmlhttprequest-ssl": {
"version": "1.5.5",

View File

@@ -1,6 +1,6 @@
{
"name": "qmi-cloud-app",
"version": "1.1.8",
"version": "1.3.0",
"scripts": {
"start": "node -r esm server/server.js",
"start:dev": "nodemon -r esm server/server.js",
@@ -27,6 +27,7 @@
"adal-angular4": "^4.0.12",
"angular-bootstrap-md": "9.0.0",
"animate.css": "^3.7.2",
"axios": "^0.21.1",
"body-parser": "^1.19.0",
"bootstrap": "^4.3.1",
"bull-arena": "^2.6.4",

0
photos/.keep Normal file
View File

View File

@@ -1,7 +1,7 @@
# Stage 1: NOTE: context is actually ../
FROM node:13.8-alpine AS sources
FROM node:15.12.0-alpine AS sources
RUN apk --no-cache add yarn
RUN apk --no-cache add yarn git
WORKDIR /app
@@ -11,7 +11,7 @@ ADD ./qmi-cloud-common ../qmi-cloud-common
RUN yarn install --production
# Stage 2:
FROM node:13.8-alpine AS production
FROM node:15.12.0-alpine AS production
WORKDIR /app
COPY --from=sources /app ./

View File

@@ -1,22 +1,36 @@
# Examples
## Check Destroy
```
docker run --net=host \
-e MONGO_URI="mongodb://root:example@localhost:27017/qmicloud?authSource=admin" \
-e API_KEY="c229219ccdd72d11e8ea253fd3876d247e5f489c9c84922cabdfb0cc194d8ff398a8d8d6528d8241efc99add2207e0ec75122a1b2c5598cc340cbe6b7c3c0dbf" \
qlikgear/qmi-cloud-cli:latest -s checkdestroy -t warning -p 4 -r test
qlikgear/qmi-cloud-cli:latest -s checkdestroy -t warning -r test
```
## Init DB
```
docker run --net=host \
-e MONGO_URI="mongodb://root:example@localhost:27017/qmicloud?authSource=admin" \
qlikgear/qmi-cloud-cli:latest -s initdb
```
## Check Destroy
```
docker run --net=host -e MONGO_URI=$MONGO_URI -e API_KEY=$API_KEY qlikgear/qmi-cloud-cli:latest -s checkdestroy -t warning -p 20 -r test
docker run --net=host -e MONGO_URI=$MONGO_URI -e API_KEY=$API_KEY qlikgear/qmi-cloud-cli:latest -s checkdestroy -t warning -r test
```
## Check Stop
```
docker run --net=host -e MONGO_URI=$MONGO_URI -e API_KEY=$API_KEY qlikgear/qmi-cloud-cli:latest -s checkstop -t warning -p 4 -r test
docker run --net=host -e SMTP_EMAIL_SENDER=http://172.20.16.4:9200/sendemail -e MONGO_URI=$MONGO_URI -e API_KEY=$API_KEY qlikgear/qmi-cloud-cli:latest -s checkstop -t warning -r test
```
## Check Errors
```
docker run --net=host -e MONGO_URI=$MONGO_URI qlikgear/qmi-cloud-cli:latest -s checkerror -r test
```
## Move Destroyed
```
docker run --net=host -e MONGO_URI=$MONGO_URI -v $PWD/qmi-cloud-provisions:/provisions qlikgear/qmi-cloud-cli:latest -s movedestroyed -r "2020-31-12T00:00:00.000Z"
```

View File

@@ -1,7 +1,7 @@
var myArgs = process.argv.slice(2);
if ( myArgs.length < 3 ) {
if ( myArgs.length < 2 ) {
console.log("Missing args", myArgs);
process.exit(0);
}
@@ -12,9 +12,7 @@ const moment = require('moment');
const fetch = require('node-fetch');
const IS_REAL = myArgs[2] !== 'test';
const STOPPED_PERIOD = myArgs[1]; //20 Days
const STOPPED_PERIOD_IS_EXTERNAL = Math.ceil(myArgs[1]/2); //10 Days
const IS_REAL = myArgs[1] !== 'test';
const WARNING_DAYS = 2;
//---
@@ -63,7 +61,8 @@ async function init(type) {
var filter = {
"isDestroyed":false,
"isDeleted": false,
"statusVms": "Stopped"
"statusVms": "Stopped",
"vmImage": {"$exists": true}
};
if ( type === "warning" ) {
filter.pendingNextAction = {$ne: "destroy"};
@@ -84,11 +83,15 @@ async function init(type) {
typeSchedule = 'OnSchedule';
}
timeRunning(p);
let stoppedPeriod = p._scenarioDoc.allowedInnactiveDays || 20;
let stoppedPeriodExternal = Math.ceil(stoppedPeriod/2);
var limit;
if ( type === "warning" ) {
limit = p.isExternalAccess? 24*(STOPPED_PERIOD_IS_EXTERNAL-WARNING_DAYS) : 24*(STOPPED_PERIOD-WARNING_DAYS);
limit = p.isExternalAccess? 24*(stoppedPeriodExternal-WARNING_DAYS) : 24*(stoppedPeriod-WARNING_DAYS);
} else if ( type === "exec" ) {
limit = p.isExternalAccess? (24*STOPPED_PERIOD_IS_EXTERNAL) : (24*STOPPED_PERIOD);
limit = p.isExternalAccess? (24*stoppedPeriodExternal) : (24*stoppedPeriod);
}
if ( !IS_REAL ) {
console.log(`${p._id} (${typeSchedule}) - limit: ${limit} hs - actual duration: ${p.duration.hours} hs`);
@@ -106,9 +109,11 @@ 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' });
await db.provision.update(p._id, {"pendingNextAction": "destroy"});
await sendEmail.sendWillDestroyIn24(p, p._scenarioDoc, Math.floor(limit/24), WARNING_DAYS);
await db.notification.add({ provision: p._id.toString(), type: 'warningDestroy', message: msg });
await sendEmail.sendWillDestroyIn24(p, p._scenarioDoc, Math.floor(limit/24), WARNING_DAYS);
}
}
};
@@ -118,6 +123,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' });
await postDestroy(p);
await db.notification.add({ provision: p._id.toString(), type: 'destroy', message: msg });
}

View File

@@ -0,0 +1,59 @@
var myArgs = process.argv.slice(2);
if ( myArgs.length < 1 ) {
console.log("Missing args", myArgs);
process.exit(0);
}
const IS_REAL = myArgs[0] !== 'test';
// ---
const db = require('qmi-cloud-common/mongo');
const sendEmail = require("qmi-cloud-common/send-email");
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
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' });
await db.notification.add({ provision: p._id.toString(), type: 'warningError', message: msg });
await sendEmail.sendProvisionError(p, p._scenarioDoc);
}
};
async function init() {
let provisions = await db.provision.get({
"isDestroyed":false,
"isDeleted": false,
"status": "error"
});
await asyncForEach(provisions.results, async function(p) {
await doSendEmailErrorProvision(p);
});
}
function check() {
init().then(function(){
db.mongoose.connection.close()
process.exit(0);
}).catch(function(e){
db.mongoose.connection.close()
console.log("Error", e);
process.exit(0);
});
}
// --------------------------------
check();

View File

@@ -0,0 +1,61 @@
var myArgs = process.argv.slice(2);
if ( myArgs.length < 1 ) {
console.log("Missing args", myArgs);
process.exit(0);
}
console.log("myArgs", myArgs);
const ISODATE = new Date(myArgs[0]).toISOString();
const db = require('qmi-cloud-common/mongo');
const fs = require('fs-extra');
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
}
}
async function init() {
let provisions = await db.provision.get({
"isDestroyed":true,
"isDeleted": false,
"created": { "$lt" : ISODATE }
});
await asyncForEach(provisions.results, async function(p) {
await doMoveDestroyed(p);
});
}
const doMoveDestroyed = async function(provision) {
var scenarioFolder = `${provision.scenario}_${provision._id}`;
console.log(`Moving scenario: /provisions/${scenarioFolder}`);
if (fs.existsSync(`/provisions/${scenarioFolder}`)) {
fs.moveSync(`/provisions/${scenarioFolder}`, `/provisions/deleted/${scenarioFolder}`, { overwrite: true })
console.log(`OK.`);
} else {
console.log(`NOK: It does not exist.`);
}
};
function check() {
init().then(function(){
db.mongoose.connection.close()
process.exit(0);
}).catch(function(e){
db.mongoose.connection.close()
console.log("Error", e);
process.exit(0);
});
}
// --------------------------------
check();

View File

@@ -1,15 +1,12 @@
var myArgs = process.argv.slice(2);
if ( myArgs.length < 3 ) {
if ( myArgs.length < 2 ) {
console.log("Missing args", myArgs);
process.exit(0);
}
const IS_REAL = myArgs[2] !== 'test';
const RUNNING_PERIOD = myArgs[1]; // 7 Days
const RUNNING_PERIOD_ON_SCHEDULE = Math.ceil(myArgs[1]/2); // ~ 4 Days
const IS_REAL = myArgs[1] !== 'test';
const WARNING_DAYS = 1;
// ---
@@ -17,7 +14,7 @@ const WARNING_DAYS = 1;
const db = require('qmi-cloud-common/mongo');
const sendEmail = require("qmi-cloud-common/send-email");
const moment = require('moment');
const azurecli = require('qmi-cloud-common/azurecli');
const cli = require('qmi-cloud-common/cli');
function timeRunningIs24x7(p) {
let runningFromTime = p.runningFrom? new Date(p.runningFrom).getTime() : new Date(p.created).getTime();
@@ -41,6 +38,18 @@ function timeRunningOnSchedule(p) {
};
}
function timeRunningOnSchedule2(p) {
let startTimestamp = p.startDateOnSchedule? new Date(p.startDateOnSchedule).getTime() : 0;
let endTimestamp = p.endDateOnSchedule? new Date(p.endDateOnSchedule).getTime() : 0;
let totalTimeOnschedule = Math.abs(endTimestamp - startTimestamp);
let duration = moment.duration(totalTimeOnschedule);
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);
@@ -52,7 +61,8 @@ async function init(type) {
var filter = {
"isDestroyed": false,
"isDeleted": false,
"statusVms": "Running"
"statusVms": "Running",
"vmImage": { "$exists": true }
};
if ( type === "warning" ) {
filter.pendingNextAction = { $ne: "stopVms" };
@@ -69,18 +79,19 @@ async function init(type) {
await asyncForEach(provisions.results, async function(p) {
//Only if 24x7
var typeSchedule = "24x7";
var periodDays = RUNNING_PERIOD;
let typeSchedule = "24x7";
let periodDays = p._scenarioDoc.allowed24x7RunningDays;
if ( !p.schedule || p.schedule.is24x7 ) {
// 24x7
timeRunningIs24x7(p);
periodDays = RUNNING_PERIOD;
periodDays = p._scenarioDoc.allowed24x7RunningDays;
} else if ( p.schedule && !p.schedule.is24x7 ) {
timeRunningOnSchedule(p);
// OnSchedule
timeRunningOnSchedule2(p);
typeSchedule = 'OnSchedule';
let onScheduleRenewed = p.schedule.onScheduleRenewed? p.schedule.onScheduleRenewed : 1;
periodDays = RUNNING_PERIOD_ON_SCHEDULE * onScheduleRenewed;
periodDays = p._scenarioDoc.allowedOnScheduleRunningDays;
}
var limit;
let limit;
if ( type === "warning" ) {
limit = 24*(periodDays-WARNING_DAYS);
} else if ( type === "exec" ) {
@@ -102,9 +113,10 @@ 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' });
await db.provision.update(p._id, {"pendingNextAction": "stopVms"});
await sendEmail.sendWillStopIn24(p, p._scenarioDoc, Math.floor(limit/24), WARNING_DAYS);
await db.notification.add({ provision: p._id.toString(), type: 'warningStop', message: msg });
await sendEmail.sendWillStopIn24(p, p._scenarioDoc, Math.floor(limit/24), WARNING_DAYS);
}
}
};
@@ -118,14 +130,15 @@ const doStop = async function(p, limit, typeSchedule) {
if (p.schedule){
//Disable Divvy
await db.schedule.update(p.schedule._id, {"isStartupTimeEnable": false});
await azurecli.updateVmsTags(p._id, {
await cli.updateVmsTags(p._id, {
"24x7": false,
"StartupTime": false,
"ShutdownTime": false
});
}
//Stop VMs indefinitely
await azurecli.deallocate(p._id, true);
db.event.add({ user: p.user._id, provision: p._id, type: 'vms.exec-stop' });
await cli.deallocate(p._id, true);
await db.notification.add({ provision: p._id.toString(), type: 'stop', message: msg });
}

View File

@@ -1,13 +1,15 @@
{
"name": "qmi-cloud-cli",
"version": "1.1.5",
"version": "1.3.0",
"scripts": {
},
"private": true,
"dependencies": {
"qmi-cloud-common": "../qmi-cloud-common",
"node-fetch": "^2.6.0",
"moment": "^2.24.0"
"moment": "^2.24.0",
"axios": "^0.21.1",
"fs-extra": "^8.1.0"
}
}

View File

@@ -5,18 +5,33 @@ helpFunction()
{
echo ""
echo "Usage: $0 -s script"
echo -e "\t-s Type of script ( checkstop | checkdestroy | initdb )"
echo -e "\t-s Type of script ( checkstop | checkdestroy | checkerror | movedestroyed | initdb )"
exit 1 # Exit script after printing help
}
helpFunctionCheck()
{
echo ""
echo "Usage: $0 -s script -t type -p period -r realortest"
echo -e "\t-s Type of script ( checkstop | checkdestroy | initdb )"
echo "Usage: $0 -s script [-t type -r realortest]"
echo -e "\t-s Type of script ( checkstop | checkdestroy | checkerror )"
echo -e "\t-t Type of script ( warning | exec )"
echo -e "\t-p Period (number of days)"
echo -e "\t-r Type of script (test | real)"
echo -e "\t-r Indicate whether is for test or real execution ( test | real )"
exit 1 # Exit script after printing help
}
helpFunctionCheck2()
{
echo ""
echo "Usage: $0 -s checkerror [-r realortest]"
echo -e "\t-r Indicate whether is for test or real execution ( test | real )"
exit 1 # Exit script after printing help
}
helpFunctionCheck3()
{
echo ""
echo "Usage: $0 -s movedestroyed [-r isodate]"
echo -e "\t-r An ISODATE (ie: '2020-31-12T00:00:00.000Z') to movedestroyed backwards"
exit 1 # Exit script after printing help
}
@@ -25,7 +40,6 @@ do
case "$opt" in
s ) script="$OPTARG" ;;
t ) type="$OPTARG" ;;
p ) period="$OPTARG" ;;
r ) realortest="$OPTARG" ;;
? ) helpFunction ;; # Print helpFunction in case parameter is non-existent
esac
@@ -33,25 +47,25 @@ done
if [ -z "$script" ]
then
echo "Some or all of the parameters are empty";
echo "Script not recognised";
helpFunction
fi
if [ "$script" = "checkstop" ] || [ "$script" = "checkdestroy" ]
then
if [ -z "$type" ] || [ -z "$period" ] || [ -z "$realortest" ]
if [ -z "$type" ] || [ -z "$realortest" ]
then
echo "Some or all of the parameters are empty";
helpFunctionCheck
else
if [ "$script" = "checkstop" ]
then
$BASEDIR/checkstop.sh $type $period $realortest
$BASEDIR/checkstop.sh $type $realortest
fi
if [ "$script" = "checkdestroy" ]
then
$BASEDIR/checkdestroy.sh $type $period $realortest
$BASEDIR/checkdestroy.sh $type $realortest
fi
fi
elif [ "$script" = "initdb" ]
@@ -64,5 +78,24 @@ then
echo "\$MONGO_URI=$MONGO_URI"
node $BASEDIR/../jobs/db-init.js
fi
elif [ "$script" = "checkerror" ]
then
if [ -z "$realortest" ]
then
echo "Some or all of the parameters are empty";
helpFunctionCheck2
else
node $BASEDIR/../jobs/error5.js $realortest
fi
elif [ "$script" = "movedestroyed" ]
then
if [ -z "$realortest" ]
then
echo "Some or all of the parameters are empty";
helpFunctionCheck3
else
node $BASEDIR/../jobs/move_destroyed.js $realortest
fi
fi

View File

@@ -3,8 +3,7 @@ d=`date`
echo "------ $d"
echo "------ SCRIPT: $0"
echo "------ TYPE: $1"
echo "------ PERIOD: $2"
echo "------ TEST/REAL: $3"
echo "------ TEST/REAL: $2"
if [ -z "$MONGO_URI" ]
then
@@ -22,4 +21,4 @@ else
echo "\$API_KEY=$API_KEY"
fi
node $BASEDIR/../jobs/destroy5.js $1 $2 $3
node $BASEDIR/../jobs/destroy5.js $1 $2

View File

@@ -0,0 +1,14 @@
BASEDIR=$(dirname "$0")
d=`date`
echo "------ $d"
echo "------ SCRIPT: $0"
if [ -z "$MONGO_URI" ]
then
echo "\$MONGO_URI is empty"
exit
else
echo "\$MONGO_URI=$MONGO_URI"
fi
node $BASEDIR/../jobs/error5.js $1

View File

@@ -3,8 +3,7 @@ d=`date`
echo "------ $d"
echo "------ SCRIPT: $0"
echo "------ TYPE: $1"
echo "------ PERIOD: $2"
echo "------ TEST/REAL: $3"
echo "------ TEST/REAL: $2"
if [ -z "$MONGO_URI" ]
then
@@ -22,4 +21,4 @@ else
echo "\$API_KEY=$API_KEY"
fi
node $BASEDIR/../jobs/stop5.js $1 $2 $3
node $BASEDIR/../jobs/stop5.js $1 $2

View File

@@ -2,6 +2,70 @@
# yarn lockfile v1
"@azure/abort-controller@^1.0.0":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.0.4.tgz#fd3c4d46c8ed67aace42498c8e2270960250eafd"
integrity sha512-lNUmDRVGpanCsiUN3NWxFTdwmdFI53xwhkTFfHDGTYk46ca7Ind3nanJc+U6Zj9Tv+9nTCWRBscWEW1DyKOpTw==
dependencies:
tslib "^2.0.0"
"@azure/arm-compute@^15.0.0":
version "15.0.0"
resolved "https://registry.yarnpkg.com/@azure/arm-compute/-/arm-compute-15.0.0.tgz#5d0d0c1db16adbb6db3d33ca376b120c68e6ae23"
integrity sha512-ElV2MuYZ+B2+ygMx2iiM/u3C7WDRruZjkXzfk5p2O+UbWxjG6j/686OH3T+VSDqmzg+47AnIOTLu2v0u0H8FOw==
dependencies:
"@azure/ms-rest-azure-js" "^2.0.1"
"@azure/ms-rest-js" "^2.0.4"
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"
integrity sha512-KUl+Nwn/Sm6Lw5d3U90m1jZfNSL087SPcqHLxwn2T6PupNKmcgsEbDjHB25gDvHO4h7pBsTlrdJAY7dz+Qk8GA==
dependencies:
"@azure/abort-controller" "^1.0.0"
tslib "^2.0.0"
"@azure/ms-rest-azure-env@^2.0.0":
version "2.0.0"
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":
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==
dependencies:
"@azure/core-auth" "^1.1.4"
"@azure/ms-rest-js" "^2.2.0"
tslib "^1.10.0"
"@azure/ms-rest-js@^2.0.4", "@azure/ms-rest-js@^2.2.0":
version "2.2.3"
resolved "https://registry.yarnpkg.com/@azure/ms-rest-js/-/ms-rest-js-2.2.3.tgz#8f0085f7768c69f17b3cdb20ce95728b452dc304"
integrity sha512-sXOhOu/37Tr8428f32Jwuwga975Xw64pYg1UeUwOBMhkNgtn5vUuNRa3fhmem+I6f8EKoi6hOsYDFlaHeZ52jA==
dependencies:
"@azure/core-auth" "^1.1.4"
"@types/node-fetch" "^2.3.7"
"@types/tunnel" "0.0.1"
abort-controller "^3.0.0"
form-data "^2.5.0"
node-fetch "^2.6.0"
tough-cookie "^3.0.1"
tslib "^1.10.0"
tunnel "0.0.6"
uuid "^3.3.2"
xml2js "^0.4.19"
"@azure/ms-rest-nodeauth@^3.0.7":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-3.0.7.tgz#73c399b0aef45c75104324b6617aa4e0a6c27875"
integrity sha512-7Q1MyMB+eqUQy8JO+virSIzAjqR2UbKXE/YQZe+53gC8yakm8WOQ5OzGfPP+eyHqeRs6bQESyw2IC5feLWlT2A==
dependencies:
"@azure/ms-rest-azure-env" "^2.0.0"
"@azure/ms-rest-js" "^2.0.4"
adal-node "^0.1.28"
"@hapi/boom@^9.1.0":
version "9.1.0"
resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.0.tgz#0d9517657a56ff1e0b42d0aca9da1b37706fec56"
@@ -14,11 +78,38 @@
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.0.4.tgz#e80ad4e8e8d2adc6c77d985f698447e8628b6010"
integrity sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==
"@types/node-fetch@^2.3.7":
version "2.5.8"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb"
integrity sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==
dependencies:
"@types/node" "*"
form-data "^3.0.0"
"@types/node@*":
version "14.14.35"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.35.tgz#42c953a4e2b18ab931f72477e7012172f4ffa313"
integrity sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag==
"@types/node@^8.0.47":
version "8.10.61"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.61.tgz#d299136ce54bcaf1abaa4a487f9e4bedf6b0d393"
integrity sha512-l+zSbvT8TPRaCxL1l9cwHCb0tSqGAGcjPJFItGGYat5oCTiq1uQQKYg5m7AF1mgnEBzFXGLJ2LRmNjtreRX76Q==
"@types/tunnel@0.0.1":
version "0.0.1"
resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.1.tgz#0d72774768b73df26f25df9184273a42da72b19c"
integrity sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A==
dependencies:
"@types/node" "*"
abort-controller@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
dependencies:
event-target-shim "^5.0.0"
adal-node@^0.1.28:
version "0.1.28"
resolved "https://registry.yarnpkg.com/adal-node/-/adal-node-0.1.28.tgz#468c4bb3ebbd96b1270669f4b9cba4e0065ea485"
@@ -56,13 +147,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0:
resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525"
integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=
async@2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4"
integrity sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==
dependencies:
lodash "^4.14.0"
async@>=0.6.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
@@ -83,13 +167,12 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2"
integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==
azure-arm-compute@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/azure-arm-compute/-/azure-arm-compute-10.0.0.tgz#ce9ba2e4d6dd6b1174c34da2219700a8080f389b"
integrity sha512-ehafNtcMKI6c00FT+xhPWPtzhYgHCZ675TUsaJ1FJ2bSpznih5EUrpir/4w519T4zbFBigszhnRK6eBkl78I9g==
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
ms-rest "^2.5.0"
ms-rest-azure "^2.5.5"
follow-redirects "^1.10.0"
bcrypt-pbkdf@^1.0.0:
version "1.0.2"
@@ -147,7 +230,7 @@ cluster-key-slot@^1.1.0:
resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.0.tgz#30474b2a981fb12172695833052bc0d01336d10d"
integrity sha512-2Nii8p3RwAPiFwsnZvukotvow2rIHM+yQ6ZcBXGHdniadkYGZYiGmkHJIbZPIV9nfv7m/U1IPMVVcAhoWFeklw==
combined-stream@^1.0.6, combined-stream@~1.0.6:
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
@@ -215,11 +298,6 @@ denque@^1.1.0, denque@^1.4.1:
resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf"
integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==
duplexer@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
@@ -261,6 +339,11 @@ es-to-primitive@^1.2.1:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
event-target-shim@^5.0.0:
version "5.0.1"
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==
extend@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa"
@@ -286,11 +369,34 @@ fast-json-stable-stringify@^2.0.0:
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
follow-redirects@^1.10.0:
version "1.13.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
form-data@^2.5.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.6"
mime-types "^2.1.12"
form-data@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@@ -300,6 +406,15 @@ form-data@~2.3.2:
combined-stream "^1.0.6"
mime-types "^2.1.12"
fs-extra@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
dependencies:
graceful-fs "^4.2.0"
jsonfile "^4.0.0"
universalify "^0.1.0"
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@@ -317,6 +432,11 @@ getpass@^0.1.1:
dependencies:
assert-plus "^1.0.0"
graceful-fs@^4.1.6, graceful-fs@^4.2.0:
version "4.2.6"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
har-schema@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92"
@@ -371,10 +491,10 @@ ioredis@^4.14.1:
redis-parser "^3.0.0"
standard-as-callback "^2.0.1"
is-buffer@^1.1.6:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
ip-regex@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
is-callable@^1.1.4, is-callable@^1.1.5:
version "1.2.0"
@@ -400,11 +520,6 @@ is-regex@^1.0.5:
dependencies:
has-symbols "^1.0.1"
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
is-symbol@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
@@ -447,6 +562,13 @@ json-stringify-safe@~5.0.1:
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
jsonfile@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
optionalDependencies:
graceful-fs "^4.1.6"
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
@@ -489,7 +611,7 @@ lodash.flatten@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
lodash@^4.14.0, lodash@^4.17.15:
lodash@^4.17.15:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@@ -518,7 +640,7 @@ moment-timezone@^0.5.31:
dependencies:
moment ">= 2.9.0"
"moment@>= 2.9.0", moment@^2.21.0, moment@^2.22.2, moment@^2.24.0:
"moment@>= 2.9.0", moment@^2.24.0:
version "2.26.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a"
integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==
@@ -574,44 +696,6 @@ mquery@3.2.2:
safe-buffer "5.1.2"
sliced "1.0.1"
ms-rest-azure@^2.5.5:
version "2.6.0"
resolved "https://registry.yarnpkg.com/ms-rest-azure/-/ms-rest-azure-2.6.0.tgz#2098efec529eecfa0c6e215b69143abcaba12140"
integrity sha512-J6386a9krZ4VtU7CRt+Ypgo9RGf8+d3gjMBkH7zbkM4zzkhbbMOYiPRaZ+bHZcfihkKLlktTgA6rjshTjF329A==
dependencies:
adal-node "^0.1.28"
async "2.6.0"
moment "^2.22.2"
ms-rest "^2.3.2"
request "^2.88.0"
uuid "^3.2.1"
ms-rest-azure@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ms-rest-azure/-/ms-rest-azure-3.0.0.tgz#54d0341c2aeef7b9f17f2a46258788740b2f0ec5"
integrity sha512-cttN01/TtMDB4v3rt/WQ/slgffB6jcUYxcPzcL0VNSB+WFPE1j4y5ICNHMuD1RaNNekCYMI4Pv51BDQ/BXNq7Q==
dependencies:
adal-node "^0.1.28"
async "2.6.0"
moment "^2.22.2"
ms-rest "^2.3.2"
request "^2.88.0"
uuid "^3.2.1"
ms-rest@^2.3.2, ms-rest@^2.5.0:
version "2.5.4"
resolved "https://registry.yarnpkg.com/ms-rest/-/ms-rest-2.5.4.tgz#57b42299cf302e45d5e1a734220bf7d4a110167a"
integrity sha512-VeqCbawxRM6nhw0RKNfj7TWL7SL8PB6MypqwgylXCi+u412uvYoyY/kSmO8n06wyd8nIcnTbYToCmSKFMI1mCg==
dependencies:
duplexer "^0.1.1"
is-buffer "^1.1.6"
is-stream "^1.1.0"
moment "^2.21.0"
request "^2.88.0"
through "^2.3.8"
tunnel "0.0.5"
uuid "^3.2.1"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -707,13 +791,14 @@ punycode@^2.1.0, punycode@^2.1.1:
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qmi-cloud-common@../qmi-cloud-common:
version "1.1.2"
version "1.1.6"
dependencies:
"@azure/arm-compute" "^15.0.0"
"@azure/ms-rest-nodeauth" "^3.0.7"
"@hapi/boom" "^9.1.0"
azure-arm-compute "^10.0.0"
axios "^0.21.1"
bull "^3.11.0"
mongoose "^5.7.4"
ms-rest-azure "^3.0.0"
nodemailer "^6.4.2"
qs@~6.5.2:
@@ -756,7 +841,7 @@ regexp-clone@1.0.0, regexp-clone@^1.0.0:
resolved "https://registry.yarnpkg.com/regexp-clone/-/regexp-clone-1.0.0.tgz#222db967623277056260b992626354a04ce9bf63"
integrity sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==
"request@>= 2.52.0", request@^2.88.0:
"request@>= 2.52.0":
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
@@ -817,6 +902,11 @@ saslprep@^1.0.0:
dependencies:
sparse-bitfield "^3.0.3"
sax@>=0.6.0:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
semver@^5.1.0:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
@@ -905,10 +995,14 @@ string_decoder@~1.1.1:
dependencies:
safe-buffer "~5.1.0"
through@^2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
tough-cookie@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2"
integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==
dependencies:
ip-regex "^2.1.0"
psl "^1.1.28"
punycode "^2.1.1"
tough-cookie@~2.5.0:
version "2.5.0"
@@ -918,6 +1012,16 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
tslib@^1.10.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
@@ -925,10 +1029,10 @@ tunnel-agent@^0.6.0:
dependencies:
safe-buffer "^5.0.1"
tunnel@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.5.tgz#d1532254749ed36620fcd1010865495a1fa9d0ae"
integrity sha512-gj5sdqherx4VZKMcBA4vewER7zdK25Td+z1npBqpbDys4eJrLx+SlYjJvq1bDXs2irkuJM5pf8ktaEQVipkrbA==
tunnel@0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
@@ -940,6 +1044,11 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.10.2.tgz#73d6aa3668f3188e4adb0f1943bd12cfd7efaaaf"
integrity sha512-N4P+Q/BuyuEKFJ43B9gYuOj4TQUHXX+j2FqguVOpjkssLUUrnJofCcBccJSCoeturDoZU6GorDTHSvUDlSQbTg==
universalify@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
uri-js@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0"
@@ -962,7 +1071,7 @@ util.promisify@^1.0.1:
has-symbols "^1.0.1"
object.getownpropertydescriptors "^2.1.0"
uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2, uuid@^3.4.0:
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==
@@ -976,6 +1085,19 @@ verror@1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"
xml2js@^0.4.19:
version "0.4.23"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
dependencies:
sax ">=0.6.0"
xmlbuilder "~11.0.0"
xmlbuilder@~11.0.0:
version "11.0.1"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
"xmldom@>= 0.1.x":
version "0.3.0"
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.3.0.tgz#e625457f4300b5df9c2e1ecb776147ece47f3e5a"

108
qmi-cloud-common/awscli.js Normal file
View File

@@ -0,0 +1,108 @@
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');
rgName = rgName + "-" + provision._id.toString();
return rgName;
}
function _getRegion(provision) {
let region = "eu-west-1";
if ( provision.deployOpts.location === 'East US') {
region = "us-east-1";
} else if ( provision.deployOpts.location === 'Southeast Asia' ) {
region = "ap-southeast-1";
}
return region;
}
async function deallocate(provision, isSendEmailAfter) {
let rgName = _getRgName(provision);
let region = _getRegion(provision);
console.log("AWSCLI# Stopping EC2s for resource group: "+rgName);
var ec2 = new AWS.EC2({apiVersion: '2016-11-15', region: region});
var params = {
DryRun: false,
Filters: [
{
Name: 'tag:Name',
Values: [ `fort-${provision._id}` ]
},
]
};
ec2.describeInstances(params, async function (err, data) {
if (err) {
console.log("AWSCLI# ERROR describing EC2s: "+rgName, err.stack);
} else {
var ec2Ids = data.Reservations[0].Instances.map(ec2=> ec2.InstanceId);
console.log("AWSCLI# Stopping EC2s ids", ec2Ids);
params = {
DryRun: false,
InstanceIds: ec2Ids
};
ec2.stopInstances(params, async function(err, data) {
if (err) {
console.log("AWSCLI# ERROR stopping EC2s: "+rgName, err.stack);
} else {
console.log("AWSCLI# EC2s stopped!");
utils.afterStopVms( provision, isSendEmailAfter );
}
});
}
});
}
async function start(provision) {
let rgName = _getRgName(provision);
let region = _getRegion(provision);
console.log("AWSCLI# Stopping EC2s for resource group: "+rgName);
var ec2 = new AWS.EC2({apiVersion: '2016-11-15', region: region});
var params = {
DryRun: false,
Filters: [
{
Name: 'tag:Name',
Values: [ `fort-${provision._id}` ]
},
]
};
ec2.describeInstances(params, async function (err, data) {
if (err) {
console.log("AWSCLI# ERROR describing EC2s: "+rgName, err.stack);
} else {
var ec2Ids = data.Reservations[0].Instances.map(ec2=> ec2.InstanceId);
console.log("AWSCLI# Starting EC2s ids", ec2Ids);
params = {
DryRun: false,
InstanceIds: ec2Ids
};
ec2.startInstances(params, async function(err, data) {
if (err) {
console.log("AWSCLI# ERROR starting EC2s: "+rgName, err.stack);
} else if (data) {
console.log("AWSCLI# EC2s started!");
utils.afterStartVms( provision );
}
});
}
});
}
module.exports.deallocate = deallocate;
module.exports.start = start;

View File

@@ -1,7 +1,8 @@
const MsRest = require('ms-rest-azure');
const computeManagementClient = require('azure-arm-compute');
const loginWithVmMSI = require("@azure/ms-rest-nodeauth").loginWithVmMSI;
const computeManagementClient = require("@azure/arm-compute").ComputeManagementClient;
const dnsManagementClient = require("@azure/arm-dns").DnsManagementClient;
const db = require("./mongo");
const sendEmail = require("./send-email");
const utils = require("./utils");
const SUBSCRIPTION_ID = "62ebff8f-c40b-41be-9239-252d6c0c8ad9";
@@ -18,10 +19,22 @@ async function _getClient(scenarioName) {
var scenario = await db.scenario.getOne({"name": scenarioName});
id = scenario.subscription? scenario.subscription.subsId : SUBSCRIPTION_ID;
}
var credentials = await MsRest.loginWithMSI({ port: 50342 });
var credentials = await loginWithVmMSI({ port: 50342 });
//console.log("AzureCLI# authenticated", credentials);
return new computeManagementClient(credentials, id);
}
async function _getDNSClient(scenarioName) {
var id = SUBSCRIPTION_ID;
if (scenarioName){
var scenario = await db.scenario.getOne({"name": scenarioName});
id = scenario.subscription? scenario.subscription.subsId : SUBSCRIPTION_ID;
}
var credentials = await loginWithVmMSI({ port: 50342 });
//console.log("AzureCLI# authenticated", credentials);
return new dnsManagementClient(credentials, id);
}
async function asyncForEach(array, callback) {
for (let index = 0; index < array.length; index++) {
@@ -29,34 +42,32 @@ async function asyncForEach(array, callback) {
}
}
async function deallocate(provId, isSendEmailAfter ) {
let provision = await db.provision.getById(provId);
if ( !provision ) return;
async function deallocate(provision, isSendEmailAfter ) {
let rgName = _getRgName(provision);
console.log("AzureCLI# Deallocating VMs for resource group: "+rgName);
var computeClient = await _getClient(provision.scenario);
let finalResult = await computeClient.virtualMachines.list(rgName);
if ( finalResult && finalResult.length > 0 ) {
db.provision.update(provision._id, {"statusVms": "Stopping"});
}
try {
await asyncForEach(finalResult, async function(vm) {
await computeClient.virtualMachines.deallocate(rgName, vm.name);
});
let timeRunning = db.utils.getNewTimeRunning(provision);
await db.provision.update(provision._id.toString(), {"statusVms": "Stopped", "timeRunning": timeRunning, "stoppedFrom": new Date(), "pendingNextAction": undefined});
if ( isSendEmailAfter && provision._scenarioDoc ) {
await sendEmail.sendVMsStopped(provision, provision._scenarioDoc);
}
await utils.afterStopVms(provision, isSendEmailAfter);
console.log("AzureCLI# All VMs DEALLOCATED for resource group: "+rgName);
} catch ( error ) {
console.log("AzureCLI# ERROR stopping VMs: "+rgName, error);
await db.provision.update(provision._id.toString(), {"statusVms": "Running" });
}
}
async function start(provId){
let provision = await db.provision.getById(provId);
if ( !provision ) return;
async function start(provision){
let rgName = _getRgName(provision);
console.log("AzureCLI# Starting VMs for resource group: "+rgName);
@@ -67,22 +78,36 @@ async function start(provId){
if ( finalResult && finalResult.length > 0 ) {
db.provision.update(provision._id, {"statusVms": "Starting"});
}
try {
await asyncForEach(finalResult, async function(vm) {
await computeClient.virtualMachines.start(rgName, vm.name);
});
let countExtend = db.utils.getNewCountExtend(provision);
await db.provision.update(provision._id.toString(), {"statusVms": "Running", "runningFrom": new Date(), "countExtend": countExtend, "pendingNextAction": undefined});
console.log("AzureCLI# All VMs RUNNING for resource group: "+rgName);
await utils.afterStartVms( provision );
} catch ( error ) {
console.log("AzureCLI# ERROR starting VMs: "+rgName, error);
await db.provision.update(provision._id.toString(), {"statusVms": "Stopped" });
}
}
async function getResourceGroupVms(rgName){
var computeClient = await _getClient();
let computeClient = await _getClient();
return await computeClient.virtualMachines.list(rgName);
}
async function getAllVms(){
var computeClient = await _getClient();
return await computeClient.virtualMachines.listAll(rgName);
let computeClient = await _getClient();
console.log("AzureCLI# Retrieving all VMS from subscription");
return await computeClient.virtualMachines.listAll();
}
async function getAllVmsNext(nextLink){
let computeClient = await _getClient();
console.log("AzureCLI# Retrieving all VMS from subscription (next): " +nextLink);
return await computeClient.virtualMachines.listAllNext(nextLink)
}
async function updateVmsTags(provId, tagsEdit) {
@@ -94,8 +119,16 @@ async function updateVmsTags(provId, tagsEdit) {
let rgName = _getRgName(provision);
console.log("AzureCLI# Updating TAGS in VMs for resource group: "+rgName);
var computeClient = await _getClient(provision.scenario);
let finalResult = await computeClient.virtualMachines.list(rgName);
var computeClient;
var finalResult;
try {
computeClient = await _getClient(provision.scenario);
finalResult = await computeClient.virtualMachines.list(rgName);
} catch (e2) {
console.log(`AzureCLI# ERROR: Resource group '${rgName}' could not be found`, e2);
return {};
}
if (finalResult && finalResult.length > 0) {
var toDelete = [];
@@ -111,6 +144,8 @@ async function updateVmsTags(provId, tagsEdit) {
if ( toDelete.length > 0 || toAdd.length > 0 ) {
await asyncForEach(finalResult, async function(vm) {
var vmActualTags = JSON.stringify(vm.tags);
var tags = vm.tags;
toDelete.forEach(t=>{
delete tags[t];
@@ -120,11 +155,23 @@ async function updateVmsTags(provId, tagsEdit) {
});
tags["ProvId"] = provision._id.toString();
result[vm.name] = tags;
console.log(`AzureCLI# VM ${vm.name} new tags: `, tags );
computeClient.virtualMachines.update(rgName, vm.name, {"tags": tags} );
if ( JSON.stringify(tags) !== vmActualTags ) {
try {
console.log(`AzureCLI# setting new tags to VM ${vm.name} (${rgName})`);
await computeClient.virtualMachines.update(rgName, vm.name, {"tags": tags} );
} catch (e1) {
console.log(`AzureCLI# ERROR setting tags to VM ${vm.name} (${rgName})`, e1);
}
} else {
console.log(`AzureCLI# Same tags, no need to update tags for VM ${vm.name} (${rgName})`);
}
});
}
//db.event.add({ user: provision.user._id, provision: provision._id, type: 'provision.vms-tags-update' });
return result;
} else {
@@ -132,8 +179,53 @@ async function updateVmsTags(provId, tagsEdit) {
}
};
/**
* DNS Records
*/
const DNS_RESOURCE_GROUP = "QMI-Infra-DNS";
const DNS_ZONE_NAME = "qmi.qlik-poc.com";
async function getDNSRecords(type = "CNAME") {
let dnsClient = await _getDNSClient();
console.log(`AzureCLI# Retrieving '${type}' DNS records`);
return await dnsClient.recordSets.listByType(DNS_RESOURCE_GROUP, DNS_ZONE_NAME, type);
}
async function createDNSRecord(provision, cname, type = "CNAME"){
if ( !provision ) return;
let rgName = _getRgName(provision).toLowerCase();
let dnsClient = await _getDNSClient();
console.log(`AzureCLI# Creating '${type}' DNS record: ${rgName}.${DNS_ZONE_NAME} --> ${cname}`);
let parameters = {
"cnameRecord": {
"cname": cname
},
"tTL": 60
};
return await dnsClient.recordSets.createOrUpdate(DNS_RESOURCE_GROUP, DNS_ZONE_NAME, rgName, type, parameters );
}
async function deleteDNSRecord(provision, type = "CNAME"){
if ( !provision ) return;
let rgName = _getRgName(provision).toLowerCase();
let dnsClient = await _getDNSClient();
console.log(`AzureCLI# Deleting '${type}' DNS record: ${rgName}.${DNS_ZONE_NAME}`);
return await dnsClient.recordSets.deleteMethod(DNS_RESOURCE_GROUP, DNS_ZONE_NAME, rgName, type );
}
module.exports.start = start;
module.exports.deallocate = deallocate;
module.exports.getResourceGroupVms = getResourceGroupVms;
module.exports.getAllVms = getAllVms;
module.exports.getAllVmsNext = getAllVmsNext;
module.exports.updateVmsTags = updateVmsTags;
module.exports.getDNSRecords = getDNSRecords;
module.exports.createDNSRecord = createDNSRecord;
module.exports.deleteDNSRecord = deleteDNSRecord

View File

@@ -0,0 +1,85 @@
const BarracudaAPI = require('barracuda-api');
const azurecli = require('./azurecli');
const db = require('./mongo');
const DOMAIN = "qmi.qlik-poc.com";
async function createApp(prov) {
if ( !prov._scenarioDoc.barracudaTemplate || prov._scenarioDoc.barracudaTemplate === 'qs-only-443' ) {
_createApp(prov, "443", "HTTPS");
} else if ( prov._scenarioDoc.barracudaTemplate === 'qdc-only-8443' ) {
_createApp(prov, "8443", "HTTPS");
} else if (prov._scenarioDoc.barracudaTemplate) {
var spl = prov._scenarioDoc.barracudaTemplate.split("-");
_createApp(prov, spl[0], spl[1]);
}
}
async function _createApp(provision, backendPort, backendType) {
if ( !provision || !provision.barracudaAzureFqdn ) {
console.log(`Barracuda# Provision does not exist or does not have a barracudaAzureFqdn value. Do nothing.`);
return;
}
let appName = provision.scenario.toLowerCase();
appName = appName.replace(/azqmi/g, 'qmi');
appName = `${appName}-${provision._id}`
console.log(`Barracuda# Creating Barracuda App for provision (${appName})`);
const client = new BarracudaAPI.QlikSenseNPrintingApp();
client.create(appName, `${appName}.${DOMAIN}`, provision.barracudaAzureFqdn, backendPort, backendType).then(function(result) {
let bApp = result.app;
console.log("Barracuda# Barracuda App created! ID: "+ bApp.id);
let cname = bApp.waas_services && bApp.waas_services.length? bApp.waas_services[0].cname : null;
db.provision.update(provision._id, {"barracudaAppId": bApp.id, "barracudaAppCname":cname});
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' });
}).catch(function(err){
console.log("Barracuda# Error creating Barracuda App", err);
});
}
async function deleteApp(provision) {
if ( !provision || !provision.barracudaAppId ) {
console.log(`Barracuda# Provision does not exist or does not have a barracudaAppId value. Do nothing.`);
return;
}
console.log(`Barracuda# Deleting DNS record in Azure and Barracuda App ID (${provision.barracudaAppId}) for provision (${provision._id})`);
azurecli.deleteDNSRecord(provision);
const client = new BarracudaAPI.QlikSenseNPrintingApp();
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' });
}).catch(function(err){
console.log("Barracuda# Error deleting Barracuda App", err);
});
}
async function getApp(provision) {
if ( !provision || !provision.barracudaAppId ) {
console.log(`Barracuda# Provision does not exist or does not have a barracudaAppId value. Do nothing.`);
return;
}
const client = new BarracudaAPI.QlikSenseNPrintingApp();
return await client.getDetails(provision.barracudaAppId);
}
module.exports.getApp = getApp;
module.exports.createApp = createApp;
module.exports.deleteApp = deleteApp;

38
qmi-cloud-common/cli.js Normal file
View File

@@ -0,0 +1,38 @@
const awscli = require("./awscli");
const azurecli = require("./azurecli");
const db = require("./mongo");
async function deallocate(provId, isSendEmailAfter ) {
let provision = await db.provision.getById(provId);
if ( !provision ) return;
azurecli.deallocate(provision, isSendEmailAfter);
if (provision.scenario === 'azqmi-fort'){
awscli.stop(provision, isSendEmailAfter);
} else {
azurecli.deallocate(provision, isSendEmailAfter);
}
}
async function start(provId){
let provision = await db.provision.getById(provId);
if ( !provision ) return;
if (provision.scenario === 'azqmi-fort'){
awscli.start(provision);
} else {
azurecli.start(provision);
}
}
async function updateVmsTags(provId, tagsEdit) {
azurecli.updateVmsTags(provId, tagsEdit);
}
module.exports.deallocate = deallocate;
module.exports.start = start;
module.exports.updateVmsTags = updateVmsTags;

View File

@@ -0,0 +1,4 @@
module.exports = {
DOCKERIMAGE_TERRAFORM: "qlikgear/terraform:0.13.4",
PROVISION_VERSION: 20201124
}

View File

@@ -0,0 +1,31 @@
const mongoose = require('mongoose');
mongoose.set('useFindAndModify', false);
const crypto = require("crypto");
//mongoose.set('debug', true)
const schema = new mongoose.Schema({
created: {
type: Date,
default: Date.now,
index : true
},
user: {
type: mongoose.Types.ObjectId,
ref: 'User',
index : true
},
provision: {
type: mongoose.Types.ObjectId,
ref: 'Provision'
},
type: {
type: String
},
message: {
type: String
}
});
module.exports = mongoose.model('Event', schema)

View File

@@ -4,7 +4,8 @@ mongoose.set('useFindAndModify', false);
const provisionSchema = new mongoose.Schema({
user: {
type: mongoose.Types.ObjectId, ref: 'User',
type: mongoose.Types.ObjectId,
ref: 'User',
index: true
},
created: {
@@ -38,6 +39,15 @@ const provisionSchema = new mongoose.Schema({
type: Boolean,
default: false
},
barracudaAppId: {
type: String
},
barracudaAppCname: {
type: String
},
barracudaAzureFqdn: {
type: String
},
isDestroyed: {
type: Boolean,
default: false
@@ -66,6 +76,12 @@ const provisionSchema = new mongoose.Schema({
type: Number,
default: 0
},
startDateOnSchedule: {
type: Date
},
endDateOnSchedule: {
type: Date
},
countExtend: {
type: Number,
default: 0
@@ -82,8 +98,10 @@ const provisionSchema = new mongoose.Schema({
ref: "Subscription"
},
terraformImage: {
type: String,
default: "qlikgear/terraform:0.13.4"
type: String
},
version: {
type: Number
}
},{
toObject: {virtuals:true},

View File

@@ -50,6 +50,9 @@ const scenarioSchema = new mongoose.Schema({
type: String,
required: true
},
gitBranch: {
type: String
},
description: String,
availableProductVersions: Array,
productVersionDefault: String,
@@ -65,7 +68,30 @@ const scenarioSchema = new mongoose.Schema({
}],
labels: [{
type: String
}]
}],
allowedUsers: [{
type: mongoose.Types.ObjectId,
ref: 'User',
required: true
}],
support: [{
type: String
}],
barracudaTemplate: {
type: String
},
allowedInnactiveDays: {
type: Number,
default: 20
},
allowed24x7RunningDays: {
type: Number,
default: 7
},
allowedOnScheduleRunningDays: {
type: Number,
default: 4
}
});

View File

@@ -13,10 +13,6 @@ const sc = new mongoose.Schema({
type: Boolean,
default: true
},
onScheduleRenewed: {
type: Number,
default: 1
},
localeShutdownTime: {
type: String
},

View File

@@ -42,6 +42,7 @@ const VmType = require('./models/VmType');
const ApiKey = require('./models/ApiKey');
const Notification = require('./models/Notification');
const Subscription = require('./models/Subscription');
const Event = require('./models/Event');
const getNewCountExtend = function(provision) {
@@ -128,8 +129,15 @@ const getPage = async ( model, filter, page, populates, select ) => {
const get = async (model, filter, select, skip, limit, populates, reply) => {
var sort = {};
var modelAttributes = Object.keys(model.schema.tree);
if ( model === Scenario ) {
if ( modelAttributes.indexOf("updated") !== -1) {
sort.updated = -1;
}
}
if ( modelAttributes.indexOf("created") !== -1) {
sort = {created: -1};
sort.created = -1;
}
try {
var exec = model.find(filter, select).sort(sort);
@@ -158,7 +166,7 @@ const get = async (model, filter, select, skip, limit, populates, reply) => {
}
if ( model = Scenario ) {
exec = exec.populate('subscription').populate('deployOpts');
exec = exec.populate('subscription').populate('deployOpts').populate('allowedUsers');
}
}
@@ -189,7 +197,7 @@ const getById = async (model, id, reply) => {
exec = exec.populate('user');
}
if ( model = Scenario ) {
exec = exec.populate('subscription').populate('deployOpts');
exec = exec.populate('subscription').populate('deployOpts').populate('allowedUsers');
}
const entity = await exec;
return entity;
@@ -208,7 +216,7 @@ const getOne = async (model, filter, reply) => {
exec = exec.populate('user');
}
if ( model = Scenario ) {
exec = exec.populate('subscription').populate('deployOpts');
exec = exec.populate('subscription').populate('deployOpts').populate('allowedUsers');
}
const entity = await exec;
return entity;
@@ -248,6 +256,31 @@ const update = async (model, id, body, reply) => {
}
};
const updateMany = async (model, filter, body, reply) => {
try {
const { ...updateData } = body;
updateData.updated = new Date();
var exec = model.updateMany(filter, updateData);
if ( model === Provision ) {
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
}
if ( model === ApiKey ) {
exec = exec.populate('user');
}
if ( model = Scenario ) {
exec = exec.populate('subscription').populate('deployOpts');
}
return await exec;
} catch (err) {
throw boom.boomify(err)
}
};
const del = async (model, id, reply) => {
try {
const entity = await model.findByIdAndRemove(id)
@@ -257,6 +290,15 @@ const del = async (model, id, reply) => {
}
}
const count = async (model, filter, reply) => {
try {
var totalDocs = await model.countDocuments(filter);
return totalDocs;
} catch (err) {
throw boom.boomify(err)
}
}
function _m(model) {
return {
get: async (filter, select, skip, limit, populates, reply) => {
@@ -277,8 +319,14 @@ function _m(model) {
update: async (id, data, reply) => {
return update(model, id, data, reply);
},
updateMany: async(filter, data, reply) => {
return updateMany(model, filter, data, reply);
},
del: async (id, reply) => {
return del(model, id, reply);
},
count: async (filter, reply) => {
return count(model, filter, reply);
}
}
}
@@ -293,6 +341,7 @@ module.exports = {
apiKey: _m(ApiKey),
notification: _m(Notification),
subscription: _m(Subscription),
event: _m(Event),
user: _m(User),
utils: {
getNewTimeRunning: getNewTimeRunning,
@@ -308,7 +357,8 @@ module.exports = {
VmType: VmType,
Notification: Notification,
ApiKey: ApiKey,
Subscription: Subscription
Subscription: Subscription,
Event: Event
}
};

1315
qmi-cloud-common/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,17 @@
{
"name": "qmi-cloud-common",
"version": "1.1.5",
"version": "1.1.6",
"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#0.0.10",
"bull": "^3.11.0",
"mongoose": "^5.7.4",
"nodemailer": "^6.4.2",
"azure-arm-compute": "^10.0.0",
"ms-rest-azure": "^3.0.0",
"bull": "^3.11.0"
"uuid": "^8.3.2"
}
}

View File

@@ -4,25 +4,27 @@ const Queue = require('bull');
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';
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);
export const queues = {
[TF_APPLY_QUEUE]: terraformApplyQueue,
[TF_DESTROY_QUEUE]: terraformDestroyQueue,
[TF_APPLY_QSEOK_QUEUE]: terraformApplyQseokQueue
[TF_APPLY_QSEOK_QUEUE]: terraformApplyQseokQueue,
[STOP_CONTAINER_QUEUE]: stopContainerQueue
};
for (let key in queues) {
queues[key].on('completed', function(job, result) {
//console.log(`Queues# Job ${job.id} completed! Result`, result);
console.log(`Queues# Job ${job.id} completed!`);
console.log(`Queue ${key}# Job ${job.id} completed!`);
});
@@ -31,13 +33,13 @@ for (let key in queues) {
});
queues[key].on('waiting', function(jobId){
console.log(`Queues# Job ${jobId} is waiting...`);
console.log(`Queue ${key}# Job ${jobId} is waiting...`);
// A Job is waiting to be processed as soon as a worker is idling.
});
queues[key].on('active', function(job, jobPromise){
// A job has started. You can use `jobPromise.cancel()`` to abort it.
console.log(`Queues# Job ${job.id} is now active`);
console.log(`Queue ${key}# Job ${job.id} is now active`);
});
queues[key].on('stalled', function(job){
@@ -47,12 +49,12 @@ for (let key in queues) {
queues[key].on('progress', function(job, progress){
// A job's progress was updated!
console.log(`Queues# Job ${job.id} is ${progress * 100}% ready!`);
console.log(`Queue ${key}# Job ${job.id} is ${progress * 100}% ready!`);
});
queues[key].on('failed', function(job, err){
// A job failed with reason `err`!
console.log(`Queues# Job ${job.id} has failed:`, err);
console.log(`Queue ${key}# Job ${job.id} has failed:`, err);
});
}

View File

@@ -0,0 +1,278 @@
'use strict';
const nodemailer = require('nodemailer');
const FROM = '"Qlik" <no-reply@qlik.com>';
var transporter;
const HOSTNAME_URL = process.env.HOSTNAME_URL || "https://qmicloud.qliktech.com";
const FOOTER = `<div style="color:#404040;font-size:16px;margin:30px 0px">
<p style="margin:0px">Check it out at <a href="${HOSTNAME_URL}">${HOSTNAME_URL}</a></p>
</div>`;
if ( process.env.GMAIL_USERNAME && process.env.GMAIL_PASSWORD ) {
//GMAIL
transporter = nodemailer.createTransport({
service: 'gmail',
port: 587,
secure: false,
auth: {
user: process.env.GMAIL_USERNAME,
pass: process.env.GMAIL_PASSWORD
}
});
} else {
//QLIK
transporter = nodemailer.createTransport({
host: 'smtp.qliktech.com',
port: 587,
secure: false, // true for 465, false for other ports
});
}
async function _doSend(to, subject, htmlText) {
// send mail with defined transport object
try {
let info = await transporter.sendMail( {
from: FROM, // sender address
to: to, // list of receivers
subject: subject, // Subject line
text: subject, // plain text body
html: htmlText // html body
} );
console.log('SendEmail# message id ('+info.messageId+') sent to: ' + to);
return info;
} catch (err) {
console.log('SendEmail# ERROR!! -> could not send email to: ' + to);
console.log(err);
}
}
function _getCommonDetails(provision, scenario){
var description = decodeURI(scenario.description);
var externalAccess = provision.isExternalAccess? 'Yes' : 'No';
var schedule = "";
if ( !provision.schedule || provision.schedule.is24x7 ) {
schedule = "24x7";
} else if ( provision.schedule && !provision.schedule.is24x7 ) {
schedule = `from ${provision.schedule.localeStartupTime}h until ${provision.schedule.localeShutdownTime}h (${provision.schedule.localTimezone})`;
}
return `<div style="color:#404040;font-size:18px;margin:20px 0px">
<p style="margin:0px">Provision information:</p>
</div>
<div>
<span style="color:#404040">ID: </span> ${provision._id}
</div>
<div>
<span style="color:#404040">VMs Running schedule: </span> ${schedule}
</div>
<div>
<span style="color:#404040">Purpose: </span> ${provision.description}
</div>
<div>
<span style="color:#404040">Scenario: </span> ${scenario.title}
</div>
<div>
<span style="color:#404040">With external access: </span> ${externalAccess}
</div>
<div>
<span style="color:#404040">Description: </span> ${description}
</div>`;
}
function getHtmlScenarioDestroyIn24( 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}' inactive more than ${period} days</p>
</div>
<div style="color:#404040;font-size:18px;margin:10px 0px">
<p style="margin:0px;color: #FF2020">This scenario will be automatically DESTROYED in ${(warningDays*24)} hours.</p>
</div>
<div style="color:#404040;font-size:16px;margin:30px 0px">
<p style="margin:0px">If you don't want this to happen, you've got ${(warningDays*24)} hours (from when this email was sent) as a grace period to get back at 'Running' status this provision.</p>
</div>
${common}
${FOOTER}
</div>
</div>`;
}
function getHtmlScenarioVMsStopped( 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:10px 0px">
<p style="margin:0px;color: #FF2020">All VMs for this provision <b>stopped</b> automatically.</p>
</div>
<div style="color:#404040;font-size:16px;margin:30px 0px">
<p style="margin:0px">You can start them up again from <a href="${HOSTNAME_URL}">${HOSTNAME_URL}</a></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">
<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}' - VMs running for ${period} days</p>
</div>
<div style="color:#404040;font-size:18px;margin:10px 0px">
<p style="margin:0px;color: #FF2020">This scenario will automatically stop its VMs in ${warningDays*24} hours.</p>
</div>
<div style="color:#404040;font-size:18px;margin:20px 0px 10px 0px">
<p style="margin:0px;color: #FF2020">Take action and extend the period ${(period+warningDays)} extra days.</p>
</div>
<div style="color:#404040;font-size:16px;margin:30px 0px">
<p style="margin:0px">If you don't want the VMs to automatically stop, you've got ${warningDays*24} hours (from when this email was sent) as a grace period to extend this scenario's <b style="color: #009845">Running</b> VMs for ${(period+warningDays)} extra days.</p>
</div>
${common}
${FOOTER}
</div>
</div>`;
}
function getHtmlNewProvision(provision, scenario) {
var htmlint;
if ( provision && provision.outputs ) {
htmlint = `<div style="color:#404040;font-size:18px;padding: 10px 0px;">Connection resources</div>`;
} 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>`;
}
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">
<p style="margin:0px">Scenario '${scenario.title}' successfully provisioned!</p>
</div>
${common}
<div style="margin: 30px 0px;">
${htmlint}
</div>
${FOOTER}
</div>
</div>`;
}
function getHtmlErrorProvision(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:20px;margin:20px 0px">
<p style="margin:0px;color: #FF2020">Oops! Something didn't work.</p>
</div>
<div style="color:#404040;font-size:20px;margin:20px 0px 50px 0px">
<p style="margin:0px">Scenario '${scenario.title}' failed during provision.</p>
</div>
<div style="color:#404040;font-size:16px;margin:30px 0px">
<p style="margin:0px">Please, follow these steps:</p>
<ul>
<li>Reach out the person responsible for this scenario for support.</li>
<li>As soon as it's possible, consider destroy this provision since it's taking valuable cloud resources which might imply relevant cost.</li>
</ul>
</div>
${common}
${FOOTER}
</div>
</div>`;
}
function getHtmlDestroyProvision(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:20px;margin:40px 0px">
<p style="margin:0px">Scenario '${scenario.title}' successfully destroyed!</p>
</div>
${common}
${FOOTER}
</div>
</div>`;
}
// async..await is not allowed in global scope, must use a wrapper
async function sendProvisionSuccess( provision, scenario ) {
const htmlText = getHtmlNewProvision(provision, scenario);
await _doSend(provision.user.upn, 'QMI Cloud - Provision finished successfully', htmlText);
}
async function sendProvisionError(provision, scenario ) {
const htmlText = getHtmlErrorProvision(provision, scenario);
await _doSend(provision.user.upn, 'QMI Cloud - Provision with errors', htmlText);
}
async function sendDestroyedSuccess(provision, scenario ) {
const htmlText = getHtmlDestroyProvision(provision, scenario);
await _doSend(provision.user.upn, 'QMI Cloud - Provision destroyed successfully', htmlText);
}
async function sendWillStopIn24( provision, scenario, period, warningDays ) {
const htmlText = getHtmlScenarioWillStopIn24( provision, scenario, period, warningDays);
await _doSend(provision.user.upn, `QMI Cloud - VMs will stop in ${warningDays*24} hours`, htmlText);
}
async function sendWillDestroyIn24( provision, scenario, period, warningDays ) {
const htmlText = getHtmlScenarioDestroyIn24( provision, scenario, period, warningDays);
await _doSend(provision.user.upn, `QMI Cloud - Provision will destroy in ${(warningDays*24)} hours`, htmlText);
}
async function sendVMsStopped( provision, scenario ) {
const htmlText = getHtmlScenarioVMsStopped( provision, scenario);
await _doSend(provision.user.upn, 'QMI Cloud - VMs stopped automatically', htmlText);
}
module.exports.sendProvisionSuccess = sendProvisionSuccess;
module.exports.sendProvisionError = sendProvisionError;
module.exports.sendDestroyedSuccess = sendDestroyedSuccess;
module.exports.sendWillStopIn24 = sendWillStopIn24;
module.exports.sendVMsStopped = sendVMsStopped;
module.exports.sendWillDestroyIn24 = sendWillDestroyIn24;
module.exports._doSend = _doSend;

View File

@@ -1,39 +1,35 @@
'use strict';
const nodemailer = require('nodemailer');
const FROM = '"Qlik" <no-reply@qlik.com>';
var transporter;
const axios = require('axios');
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">
<p style="margin:0px">Check it out at <a href="${HOSTNAME_URL}">${HOSTNAME_URL}</a></p>
</div>`;
if ( process.env.GMAIL_USERNAME && process.env.GMAIL_PASSWORD ) {
//GMAIL
transporter = nodemailer.createTransport({
service: 'gmail',
port: 587,
secure: false,
auth: {
user: process.env.GMAIL_USERNAME,
pass: process.env.GMAIL_PASSWORD
}
});
} else {
//QLIK
transporter = nodemailer.createTransport({
host: 'smtp.qliktech.com',
port: 587,
secure: false, // true for 465, false for other ports
});
}
async function _doSend(to, subject, htmlText) {
// send mail with defined transport object
let info = await transporter.sendMail( {
from: FROM, // sender address
to: to, // list of receivers
subject: subject, // Subject line
text: subject, // plain text body
html: htmlText // html body
try {
var body = {
"to": to,
"subject": subject,
"html": htmlText
};
await axios({
url: SMTP_EMAIL_SENDER,
method: "post",
data: body,
httpsAgent: new https.Agent({
rejectUnauthorized: false
})
});
console.log('SendEmail (Qlik SMTP)# message sent to: ' + to);
console.log('SendEmail# message id ('+info.messageId+') sent to: ' + to);
} catch (err) {
// Handle Error Here
console.log("SendEmail (Qlik SMTP) _doSend error: could not send the email to: " +to);
}
}
function _getCommonDetails(provision, scenario){
@@ -85,9 +81,7 @@ function getHtmlScenarioDestroyIn24( provision, scenario, period, warningDays) {
<p style="margin:0px">If you don't want this to happen, you've got ${(warningDays*24)} hours (from when this email was sent) as a grace period to get back at 'Running' status this provision.</p>
</div>
${common}
<div style="color:#404040;font-size:16px;margin:30px 0px">
<p style="margin:0px">Check it out at <a href="https://qmicloud.qliktech.com">https://qmicloud.qliktech.com</a></p>
</div>
${FOOTER}
</div>
</div>`;
}
@@ -107,12 +101,10 @@ function getHtmlScenarioVMsStopped( provision, scenario) {
<p style="margin:0px;color: #FF2020">All VMs for this provision <b>stopped</b> automatically.</p>
</div>
<div style="color:#404040;font-size:16px;margin:30px 0px">
<p style="margin:0px">You can start them up again from <a href="https://qmicloud.qliktech.com">https://qmicloud.qliktech.com</a></p>
<p style="margin:0px">You can start them up again from <a href="${HOSTNAME_URL}">${HOSTNAME_URL}</a></p>
</div>
${common}
<div style="color:#404040;font-size:16px;margin:30px 0px">
<p style="margin:0px">Check it out at <a href="https://qmicloud.qliktech.com">https://qmicloud.qliktech.com</a></p>
</div>
${FOOTER}
</div>
</div>`;
}
@@ -137,9 +129,7 @@ function getHtmlScenarioWillStopIn24( provision, scenario, period, warningDays )
<p style="margin:0px">If you don't want the VMs to automatically stop, you've got ${warningDays*24} hours (from when this email was sent) as a grace period to extend this scenario's <b style="color: #009845">Running</b> VMs for ${(period+warningDays)} extra days.</p>
</div>
${common}
<div style="color:#404040;font-size:16px;margin:30px 0px">
<p style="margin:0px">Check it out at <a href="https://qmicloud.qliktech.com">https://qmicloud.qliktech.com</a></p>
</div>
${FOOTER}
</div>
</div>`;
}
@@ -173,9 +163,7 @@ function getHtmlNewProvision(provision, scenario) {
<div style="margin: 30px 0px;">
${htmlint}
</div>
<div style="color:#404040;font-size:16px;margin:30px 0px">
<p style="margin:0px">Check it out at <a href="https://qmicloud.qliktech.com">https://qmicloud.qliktech.com</a></p>
</div>
${FOOTER}
</div>
</div>`;
@@ -194,10 +182,15 @@ function getHtmlErrorProvision(provision, scenario) {
<div style="color:#404040;font-size:20px;margin:20px 0px 50px 0px">
<p style="margin:0px">Scenario '${scenario.title}' failed during provision.</p>
</div>
${common}
<div style="color:#404040;font-size:16px;margin:30px 0px">
<p style="margin:0px">Check it out at <a href="https://qmicloud.qliktech.com">https://qmicloud.qliktech.com</a></p>
<p style="margin:0px">Please, follow these steps:</p>
<ul>
<li>Reach out the person responsible for this scenario for support.</li>
<li>As soon as it's possible, consider destroy this provision since it's taking valuable cloud resources which might imply relevant cost.</li>
</ul>
</div>
${common}
${FOOTER}
</div>
</div>`;
}
@@ -213,26 +206,24 @@ function getHtmlDestroyProvision(provision, scenario) {
<p style="margin:0px">Scenario '${scenario.title}' successfully destroyed!</p>
</div>
${common}
<div style="color:#404040;font-size:16px;margin:30px 0px">
<p style="margin:0px">Check it out at <a href="https://qmicloud.qliktech.com">https://qmicloud.qliktech.com</a></p>
</div>
${FOOTER}
</div>
</div>`;
}
// async..await is not allowed in global scope, must use a wrapper
async function send( provision, scenario ) {
async function sendProvisionSuccess( provision, scenario ) {
const htmlText = getHtmlNewProvision(provision, scenario);
await _doSend(provision.user.upn, 'QMI Cloud - Provision finished successfully', htmlText);
}
async function sendError(provision, scenario ) {
async function sendProvisionError(provision, scenario ) {
const htmlText = getHtmlErrorProvision(provision, scenario);
await _doSend(provision.user.upn, 'QMI Cloud - Provision with failures', htmlText);
await _doSend(provision.user.upn, 'QMI Cloud - Provision with errors', htmlText);
}
async function sendDestroyed(provision, scenario ) {
async function sendDestroyedSuccess(provision, scenario ) {
const htmlText = getHtmlDestroyProvision(provision, scenario);
await _doSend(provision.user.upn, 'QMI Cloud - Provision destroyed successfully', htmlText);
@@ -257,11 +248,12 @@ async function sendVMsStopped( provision, scenario ) {
await _doSend(provision.user.upn, 'QMI Cloud - VMs stopped automatically', htmlText);
}
module.exports.send = send;
module.exports.sendError = sendError;
module.exports.sendDestroyed = sendDestroyed;
module.exports.sendProvisionSuccess = sendProvisionSuccess;
module.exports.sendProvisionError = sendProvisionError;
module.exports.sendDestroyedSuccess = sendDestroyedSuccess;
module.exports.sendWillStopIn24 = sendWillStopIn24;
module.exports.sendVMsStopped = sendVMsStopped;
module.exports.sendWillDestroyIn24 = sendWillDestroyIn24;
module.exports._doSend = _doSend;

View File

@@ -0,0 +1,7 @@
var sendEmail = require("./send-email");
function test(){
sendEmail._doSend("AOR@qlik.com", "Test subject", "Hi world");
}
test();

85
qmi-cloud-common/utils.js Normal file
View File

@@ -0,0 +1,85 @@
const db = require("./mongo");
const sendEmail = require("./send-email");
async function afterStopVms( provision, auto ) {
let timeRunning = db.utils.getNewTimeRunning(provision);
const dateNow = new Date();
let patch = {
"statusVms": "Stopped",
"timeRunning": timeRunning,
"stoppedFrom": dateNow,
"pendingNextAction": undefined
};
if ( auto && provision._scenarioDoc ) { //From CLI (auto stop)
let msg = "";
// Actual onschedule reset
if ( provision.schedule && !provision.schedule.is24x7 ) {
patch["startDateOnSchedule"] = dateNow;
patch["endDateOnSchedule"] = dateNow;
msg = "Schedule time has been reset.";
} else {
msg = "24x7 vms auto-stopped due to limitted running time 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 });
} 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 });
}
}
async function afterStartVms( provision ) {
const dateNow = new Date();
let countExtend = db.utils.getNewCountExtend(provision);
var patch = {
"statusVms": "Running",
"runningFrom": dateNow,
"countExtend": countExtend,
"pendingNextAction": undefined
};
// Actual onschedule reset
let msg = "";
if ( provision.schedule && !provision.schedule.is24x7 ) {
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`;
await db.provision.update(provision._id.toString(), patch);
console.log("AzureCLI# All VMs RUNNING for resource group: "+rgName);
db.event.add({ user: provision.user._id, provision: provision._id, type: 'vms.start-ondemand', message: msg });
}
module.exports.afterStopVms = afterStopVms;
module.exports.afterStartVms = afterStartVms;

1286
qmi-cloud-common/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
# Stage 1: NOTE: context is actually ../
FROM node:13.8-alpine AS sources
FROM node:15.12.0-alpine AS sources
RUN apk --no-cache add yarn
RUN apk --no-cache add yarn git
WORKDIR /app
@@ -11,7 +11,7 @@ ADD ./qmi-cloud-common ../qmi-cloud-common
RUN yarn install --production
# Stage 2:
FROM node:13.8-alpine AS production
FROM node:15.12.0-alpine AS production
WORKDIR /app
COPY --from=sources /app ./

View File

@@ -4,6 +4,7 @@ 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");
module.exports = async function(job) {
@@ -21,6 +22,8 @@ module.exports = async function(job) {
var idProv = prov._id.toString();
db.event.add({ user: prov.user._id, provision: prov._id, type: 'provision.init' });
// TERRAFORM INIT
return tf.init(prov)
.then(async function(res) {
@@ -37,7 +40,19 @@ module.exports = async function(job) {
console.log(`ProcessorApply# Error at Terraform PLAN for provision (${idProv}) `);
return Promise.reject({"success": false, "error": "Error at Terraform Plan", provStatus: "error_plan"});
} else {
return await db.provision.update(prov._id,{"status": "provisioning", "statusVms": "Running", "runningFrom": new Date(), "runningTime": 0, "countExtend": 0});
const dateNow = new Date();
let patch = {
"status": "provisioning",
"statusVms": prov.vmImage? "Running" : "N/A",
"runningFrom": dateNow,
"runningTime": 0,
"countExtend": 0
};
if ( prov.schedule && !prov.schedule.is24x7 ) {
patch["startDateOnSchedule"] = dateNow;
patch["endDateOnSchedule"] = dateNow;
}
return await db.provision.update(prov._id, patch);
}
} ).then( function(prov) {
// TERRAFORM APPLY
@@ -45,13 +60,29 @@ module.exports = async function(job) {
} ).then( async function(res) {
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) {
// Application Gateway assign policy
return azure.appgateway(prov, job.data._scenario);
@@ -60,15 +91,22 @@ module.exports = async function(job) {
return azure.createimage(prov, job.data._scenario);
} ).then( function(prov) {
if (prov.status === "provisioned") {
sendEmail.send(prov, job.data._scenario);
sendEmail.sendProvisionSuccess(prov, job.data._scenario);
} else {
sendEmail.sendError(prov, job.data._scenario);
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);
db.provision.update(prov._id, {"status": err.provStatus? err.provStatus : 'error'});
sendEmail.sendError(prov, job.data._scenario);
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 });
if ( errormsg !== "aborted") {
sendEmail.sendProvisionError(prov, job.data._scenario);
}
return Promise.reject({"success": false, "error": err});
} );
}

View File

@@ -3,9 +3,10 @@ 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:0.12.18";
const DOCKERIMAGE = process.env.DOCKERIMAGE_TERRAFORM || "qlikgear/terraform:1.0.1";
const SSHPATH = process.env.SSHPATH;
function hook_stdout(callback) {
@@ -25,7 +26,7 @@ function hook_stdout(callback) {
function _buildVarsExec( exec, provision, scenario ) {
let prefix = scenario.name.toUpperCase();
let prefix = provision.scenario.toUpperCase();
prefix = prefix.replace(/AZQMI/g, 'QMI');
exec.push('-var');
@@ -36,7 +37,7 @@ function _buildVarsExec( exec, provision, scenario ) {
exec.push(`subscription_id=${provision.deployOpts.subsId}`);
}
//Deprecated
else if ( scenario.subscription && scenario.subscription.subsId ) {
else if ( scenario && scenario.subscription && scenario.subscription.subsId ) {
exec.push('-var');
exec.push(`subscription_id=${scenario.subscription.subsId}`);
}
@@ -60,7 +61,7 @@ function _buildVarsExec( exec, provision, scenario ) {
}
//Deprecated
else if ( scenario.subscription && scenario.subscription.vnetExists ) {
else if ( scenario && scenario.subscription && scenario.subscription.vnetExists ) {
exec.push('-var');
exec.push(`subnet_id=${scenario.subscription.subnetId}`);
@@ -75,6 +76,11 @@ function _buildVarsExec( exec, provision, scenario ) {
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.vmImage) {
//Old way
if ( provision.vmType ) {
@@ -89,17 +95,20 @@ function _buildVarsExec( exec, provision, scenario ) {
} else if ( provision.vmImage ) {
//New way
for ( let key in provision.vmImage ) {
if ( !provision.vmImage[key].disabled ) {
if ( provision.vmImage[key].nodeCount ) {
exec.push('-var');
exec.push(`agent_count_${key}=${provision[key].nodeCount}`);
}
if ( provision.vmImage[key].vmType ) {
if ( !provision.vmImage[key].disabled ) {
exec.push('-var');
exec.push(`vm_type_${key}=${provision.vmImage[key].vmType}`);
}
} else {
exec.push('-var');
exec.push(`vm_type_${key}=ENABLED`);
}
if ( provision.vmImage[key].diskSizeGb ) {
@@ -107,10 +116,16 @@ function _buildVarsExec( exec, provision, scenario ) {
exec.push(`disk_size_gb_${key}=${provision.vmImage[key].diskSizeGb}`);
}
if ( provision.vmImage[key].version ) {
if ( provision.vmImage[key].version && provision.vmImage[key].version.image) {
exec.push('-var');
exec.push(`image_reference_${key}=${provision.vmImage[key].version.image}`);
}
if ( provision.vmImage[key].version && provision.vmImage[key].version.init_password) {
exec.push('-var');
exec.push(`image_reference_${key}_init_password=${provision.vmImage[key].version.init_password}`);
}
}
}
}
@@ -145,9 +160,14 @@ const init = function( provision ) {
const name = `qmi-tf-init-${provision._id}`;
console.log(`Terraform# Init: will spin up container: ${name}`);
var processStream = fs.createWriteStream(provision.logFile, {flags:'a'});
let exec = ['terraform', 'init', '-no-color', `-from-module=${GIT_SCENARIOS}//${provision.scenario}?ref=${GIT_TAG}`];
let gitBranch = GIT_TAG;
if ( provision._scenarioDoc && provision._scenarioDoc.gitBranch && provision._scenarioDoc.gitBranch.trim() !== "") {
gitBranch = provision._scenarioDoc.gitBranch;
}
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"],
"name": name,
@@ -180,6 +200,7 @@ const plan = function( provision, scenario ) {
exec = _buildVarsExec(exec, provision, scenario);
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"],
"name": name,
@@ -213,6 +234,7 @@ 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"],
"name": name,
@@ -228,7 +250,7 @@ const apply = function( provision ) {
let container = data[1];
console.log(`Terraform# Apply: ${name} (${container.id}) has finished with code: ${data[0].StatusCode}`);
return container.remove().then(function(){
console.log(`Terraform# Apply: ${name} removed!`);
console.log(`Terraform# Apply: Container '${name}' removed!`);
return data[0].StatusCode;
});
}).then(function(statusCode) {
@@ -241,11 +263,12 @@ const destroy = function(destroyMongo, provision, scenario) {
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# terraform', 'destroy', '-auto-approve', '-no-color'];
var exec = ['terraform', 'destroy', '-auto-approve', '-no-color'];
exec = _buildVarsExec(exec, provision, scenario);
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"],
@@ -261,7 +284,7 @@ const destroy = function(destroyMongo, provision, scenario) {
var container = data[1];
console.log(`Terraform# Destroy: '${name}' (${container.id}) has finished with code: ${data[0].StatusCode}`);
return container.remove().then(function(){
console.log(`Terraform# Destroy: '${name}' removed!`);
console.log(`Terraform# Destroy: Container '${name}' removed!`);
return data[0].StatusCode;
});
}).then(async function(statusCode) {
@@ -301,8 +324,8 @@ const outputs = function(provision) {
console.log(`Terraform# Output: '${name}' (${container.id}) has finished with code: ${data[0].StatusCode}`);
return container.remove();
}).then(async function(data) {
console.log(`Terraform# Output: '${name}' removed!`);
console.log("Terraform# Output: tfout: " + tfout);
console.log(`Terraform# Output: Container '${name}' removed!`);
//console.log("Terraform# Output: tfout: " + tfout);
var out = JSON.parse(tfout);
var o = {};
for (var key in out) {
@@ -312,8 +335,48 @@ const outputs = function(provision) {
});
};
const stop = function(provision) {
if ( provision.status !== "provisioning" ) {
console.log(`Terraform# Stop: provision (${provision._id}) is not provisioning`);
return {"message": `Won't stop container: provision (${provision._id}) is not provisioning`};
}
const name = `qmi-tf-apply-${provision._id}`;
console.log(`Terraform# Stop: will try to stop container: ${name}`);
return docker.listContainers().then(function(containers) {
var containerID;
for (let i=0;i<containers.length;i++) {
if ( containers[i].Names && containers[i].Names.length ) {
//console.log(`Terraform# Stop: debug container Names: ${containers[i].Names[0]}`);
if ( containers[i].Names[0] === ("/"+name) ) {
console.log(`Terraform# Stop: container found: ${name}`);
containerID = containers[i].Id;
break;
}
}
}
if ( containerID ) {
console.log(`Terraform# Stop: stopping container: ${name} with ID ${containerID}`);
return docker.getContainer(containerID).kill().then(function(){
console.log(`Terraform# Stop: container stopped!!: ${name}`);
return {"message": `container ${name} stopped`}
});
} else {
console.log(`Terraform# Stop: container ${name} not found!`);
return {"message": `container ${name} not found`};
}
});
}
module.exports.init = init;
module.exports.plan = plan;
module.exports.apply = apply;
module.exports.destroy = destroy;
module.exports.outputs = outputs;
module.exports.stop = stop;

View File

@@ -1,4 +1,4 @@
import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE } from 'qmi-cloud-common/queues';
import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CONTAINER_QUEUE } from 'qmi-cloud-common/queues';
var path = require("path");
@@ -6,6 +6,7 @@ var path = require("path");
queues[TF_APPLY_QUEUE].process("tf_apply_job", 10, path.resolve(__dirname, 'processor-apply.js'));
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'));
console.log(`Worker queues started!`);

View File

@@ -1,6 +1,6 @@
{
"name": "qmi-cloud-worker",
"version": "1.1.6",
"version": "1.3.0",
"scripts": {
"start": "node -r esm index.js",
"start:dev": "nodemon -r esm index.js",
@@ -11,6 +11,7 @@
"qmi-cloud-common": "../qmi-cloud-common",
"dockerode": "^3.0.2",
"esm": "^3.2.25",
"nodemon": "^1.19.1"
"nodemon": "^1.19.1",
"axios": "^0.21.1"
}
}

View File

@@ -2,6 +2,7 @@ const tf = require('./docker/tf');
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");
module.exports = async function(job){
@@ -18,6 +19,16 @@ module.exports = async function(job){
var provMongo = await db.provision.getById(job.data.provId);
// Deleting Barracuda App if existed
if ( provMongo.barracudaAppId ) {
console.log(`ProcessorDestroy# Deleting Barracuda App...` );
barracuda.deleteApp(provMongo);
} else {
console.log(`ProcessorDestroy# There is no barracuda to be deleted` );
}
db.event.add({ user: provMongo.user._id, provision: provMongo._id, type: 'provision.destroy-init' });
return tf.destroy(destroyMongo, provMongo, job.data._scenario)
.then(async function(res) {
let update, update2;
@@ -28,14 +39,16 @@ module.exports = async function(job){
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()});
sendEmail.sendDestroyed(update2, job.data._scenario);
sendEmail.sendDestroyedSuccess(update2, job.data._scenario);
}
return { destroy: update, provision: update2 };
}).then(async function(res) {
console.log(`ProcessorDestroy# Provision destroyed!` );
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.destroy.update(destroyMongo._id, {"status": "error", "isDestroyed": false});
return Promise.reject({"success": false, "err": err});
});

View File

@@ -0,0 +1,21 @@
const db = require('qmi-cloud-common/mongo');
const tf = require("./docker/tf");
module.exports = async function(job) {
var prov = await db.provision.getById(job.data.provId);
if ( !prov ) {
console.log(`ProcessorStopContainer# 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"});
}
// TERRAFORM INIT
return tf.stop(prov)
.then( function(res) {
return Promise.resolve( { "success": true, "output": res });
} ).catch( function(err) {
console.log("ProcessorStopContainer# Error:", err);
return Promise.reject({"success": false, "error": err});
} );
}

View File

@@ -2,6 +2,70 @@
# yarn lockfile v1
"@azure/abort-controller@^1.0.0":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@azure/abort-controller/-/abort-controller-1.0.4.tgz#fd3c4d46c8ed67aace42498c8e2270960250eafd"
integrity sha512-lNUmDRVGpanCsiUN3NWxFTdwmdFI53xwhkTFfHDGTYk46ca7Ind3nanJc+U6Zj9Tv+9nTCWRBscWEW1DyKOpTw==
dependencies:
tslib "^2.0.0"
"@azure/arm-compute@^15.0.0":
version "15.0.0"
resolved "https://registry.yarnpkg.com/@azure/arm-compute/-/arm-compute-15.0.0.tgz#5d0d0c1db16adbb6db3d33ca376b120c68e6ae23"
integrity sha512-ElV2MuYZ+B2+ygMx2iiM/u3C7WDRruZjkXzfk5p2O+UbWxjG6j/686OH3T+VSDqmzg+47AnIOTLu2v0u0H8FOw==
dependencies:
"@azure/ms-rest-azure-js" "^2.0.1"
"@azure/ms-rest-js" "^2.0.4"
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"
integrity sha512-KUl+Nwn/Sm6Lw5d3U90m1jZfNSL087SPcqHLxwn2T6PupNKmcgsEbDjHB25gDvHO4h7pBsTlrdJAY7dz+Qk8GA==
dependencies:
"@azure/abort-controller" "^1.0.0"
tslib "^2.0.0"
"@azure/ms-rest-azure-env@^2.0.0":
version "2.0.0"
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":
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==
dependencies:
"@azure/core-auth" "^1.1.4"
"@azure/ms-rest-js" "^2.2.0"
tslib "^1.10.0"
"@azure/ms-rest-js@^2.0.4", "@azure/ms-rest-js@^2.2.0":
version "2.2.3"
resolved "https://registry.yarnpkg.com/@azure/ms-rest-js/-/ms-rest-js-2.2.3.tgz#8f0085f7768c69f17b3cdb20ce95728b452dc304"
integrity sha512-sXOhOu/37Tr8428f32Jwuwga975Xw64pYg1UeUwOBMhkNgtn5vUuNRa3fhmem+I6f8EKoi6hOsYDFlaHeZ52jA==
dependencies:
"@azure/core-auth" "^1.1.4"
"@types/node-fetch" "^2.3.7"
"@types/tunnel" "0.0.1"
abort-controller "^3.0.0"
form-data "^2.5.0"
node-fetch "^2.6.0"
tough-cookie "^3.0.1"
tslib "^1.10.0"
tunnel "0.0.6"
uuid "^3.3.2"
xml2js "^0.4.19"
"@azure/ms-rest-nodeauth@^3.0.7":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@azure/ms-rest-nodeauth/-/ms-rest-nodeauth-3.0.7.tgz#73c399b0aef45c75104324b6617aa4e0a6c27875"
integrity sha512-7Q1MyMB+eqUQy8JO+virSIzAjqR2UbKXE/YQZe+53gC8yakm8WOQ5OzGfPP+eyHqeRs6bQESyw2IC5feLWlT2A==
dependencies:
"@azure/ms-rest-azure-env" "^2.0.0"
"@azure/ms-rest-js" "^2.0.4"
adal-node "^0.1.28"
"@hapi/boom@^9.1.0":
version "9.1.0"
resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-9.1.0.tgz#0d9517657a56ff1e0b42d0aca9da1b37706fec56"
@@ -14,16 +78,43 @@
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.0.4.tgz#e80ad4e8e8d2adc6c77d985f698447e8628b6010"
integrity sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==
"@types/node-fetch@^2.3.7":
version "2.5.8"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.8.tgz#e199c835d234c7eb0846f6618012e558544ee2fb"
integrity sha512-fbjI6ja0N5ZA8TV53RUqzsKNkl9fv8Oj3T7zxW7FGv1GSH7gwJaNF8dzCjrqKaxKeUpTz4yT1DaJFq/omNpGfw==
dependencies:
"@types/node" "*"
form-data "^3.0.0"
"@types/node@*":
version "14.14.35"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.35.tgz#42c953a4e2b18ab931f72477e7012172f4ffa313"
integrity sha512-Lt+wj8NVPx0zUmUwumiVXapmaLUcAk3yPuHCFVXras9k5VT9TdhJqKqGVUQCD60OTMCl0qxJ57OiTL0Mic3Iag==
"@types/node@^8.0.47":
version "8.10.61"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.61.tgz#d299136ce54bcaf1abaa4a487f9e4bedf6b0d393"
integrity sha512-l+zSbvT8TPRaCxL1l9cwHCb0tSqGAGcjPJFItGGYat5oCTiq1uQQKYg5m7AF1mgnEBzFXGLJ2LRmNjtreRX76Q==
"@types/tunnel@0.0.1":
version "0.0.1"
resolved "https://registry.yarnpkg.com/@types/tunnel/-/tunnel-0.0.1.tgz#0d72774768b73df26f25df9184273a42da72b19c"
integrity sha512-AOqu6bQu5MSWwYvehMXLukFHnupHrpZ8nvgae5Ggie9UwzDR1CCwoXgSSWNZJuyOlCdfdsWMA5F2LlmvyoTv8A==
dependencies:
"@types/node" "*"
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
abort-controller@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392"
integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==
dependencies:
event-target-shim "^5.0.0"
adal-node@^0.1.28:
version "0.1.28"
resolved "https://registry.yarnpkg.com/adal-node/-/adal-node-0.1.28.tgz#468c4bb3ebbd96b1270669f4b9cba4e0065ea485"
@@ -118,13 +209,6 @@ async-each@^1.0.1:
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf"
integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==
async@2.6.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4"
integrity sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==
dependencies:
lodash "^4.14.0"
async@>=0.6.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720"
@@ -150,13 +234,12 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2"
integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==
azure-arm-compute@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/azure-arm-compute/-/azure-arm-compute-10.0.0.tgz#ce9ba2e4d6dd6b1174c34da2219700a8080f389b"
integrity sha512-ehafNtcMKI6c00FT+xhPWPtzhYgHCZ675TUsaJ1FJ2bSpznih5EUrpir/4w519T4zbFBigszhnRK6eBkl78I9g==
axios@^0.21.1:
version "0.21.1"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz#22563481962f4d6bde9a76d516ef0e5d3c09b2b8"
integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==
dependencies:
ms-rest "^2.5.0"
ms-rest-azure "^2.5.5"
follow-redirects "^1.10.0"
balanced-match@^1.0.0:
version "1.0.0"
@@ -406,7 +489,7 @@ color-name@1.1.3:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=
combined-stream@^1.0.6, combined-stream@~1.0.6:
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
@@ -609,11 +692,6 @@ duplexer3@^0.1.4:
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
duplexer@^0.1.1:
version "0.1.1"
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
integrity sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=
ecc-jsbn@~0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
@@ -672,6 +750,11 @@ esm@^3.2.25:
resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10"
integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==
event-target-shim@^5.0.0:
version "5.0.1"
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==
execa@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777"
@@ -767,6 +850,11 @@ fill-range@^4.0.0:
repeat-string "^1.6.1"
to-regex-range "^2.1.0"
follow-redirects@^1.10.0:
version "1.13.3"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267"
integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA==
for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@@ -777,6 +865,24 @@ forever-agent@~0.6.1:
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
form-data@^2.5.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.6"
mime-types "^2.1.12"
form-data@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f"
integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
@@ -985,6 +1091,11 @@ ioredis@^4.14.1:
redis-parser "^3.0.0"
standard-as-callback "^2.0.1"
ip-regex@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
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"
@@ -1006,7 +1117,7 @@ is-binary-path@^1.0.0:
dependencies:
binary-extensions "^1.0.0"
is-buffer@^1.1.5, is-buffer@^1.1.6:
is-buffer@^1.1.5:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
@@ -1301,7 +1412,7 @@ lodash.flatten@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
lodash@^4.14.0, lodash@^4.17.15:
lodash@^4.17.15:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
@@ -1406,7 +1517,7 @@ moment-timezone@^0.5.31:
dependencies:
moment ">= 2.9.0"
"moment@>= 2.9.0", moment@^2.21.0, moment@^2.22.2:
"moment@>= 2.9.0":
version "2.26.0"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a"
integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==
@@ -1462,44 +1573,6 @@ mquery@3.2.2:
safe-buffer "5.1.2"
sliced "1.0.1"
ms-rest-azure@^2.5.5:
version "2.6.0"
resolved "https://registry.yarnpkg.com/ms-rest-azure/-/ms-rest-azure-2.6.0.tgz#2098efec529eecfa0c6e215b69143abcaba12140"
integrity sha512-J6386a9krZ4VtU7CRt+Ypgo9RGf8+d3gjMBkH7zbkM4zzkhbbMOYiPRaZ+bHZcfihkKLlktTgA6rjshTjF329A==
dependencies:
adal-node "^0.1.28"
async "2.6.0"
moment "^2.22.2"
ms-rest "^2.3.2"
request "^2.88.0"
uuid "^3.2.1"
ms-rest-azure@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ms-rest-azure/-/ms-rest-azure-3.0.0.tgz#54d0341c2aeef7b9f17f2a46258788740b2f0ec5"
integrity sha512-cttN01/TtMDB4v3rt/WQ/slgffB6jcUYxcPzcL0VNSB+WFPE1j4y5ICNHMuD1RaNNekCYMI4Pv51BDQ/BXNq7Q==
dependencies:
adal-node "^0.1.28"
async "2.6.0"
moment "^2.22.2"
ms-rest "^2.3.2"
request "^2.88.0"
uuid "^3.2.1"
ms-rest@^2.3.2, ms-rest@^2.5.0:
version "2.5.4"
resolved "https://registry.yarnpkg.com/ms-rest/-/ms-rest-2.5.4.tgz#57b42299cf302e45d5e1a734220bf7d4a110167a"
integrity sha512-VeqCbawxRM6nhw0RKNfj7TWL7SL8PB6MypqwgylXCi+u412uvYoyY/kSmO8n06wyd8nIcnTbYToCmSKFMI1mCg==
dependencies:
duplexer "^0.1.1"
is-buffer "^1.1.6"
is-stream "^1.1.0"
moment "^2.21.0"
request "^2.88.0"
through "^2.3.8"
tunnel "0.0.5"
uuid "^3.2.1"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@@ -1532,6 +1605,11 @@ nanomatch@^1.2.9:
snapdragon "^0.8.1"
to-regex "^3.0.1"
node-fetch@^2.6.0:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
nodemailer@^6.4.2:
version "6.4.8"
resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.4.8.tgz#aca52886e4e56f71f6b8a65f5ca6b767ca751fc7"
@@ -1752,13 +1830,14 @@ punycode@^2.1.0, punycode@^2.1.1:
integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
qmi-cloud-common@../qmi-cloud-common:
version "1.1.2"
version "1.1.6"
dependencies:
"@azure/arm-compute" "^15.0.0"
"@azure/ms-rest-nodeauth" "^3.0.7"
"@hapi/boom" "^9.1.0"
azure-arm-compute "^10.0.0"
axios "^0.21.1"
bull "^3.11.0"
mongoose "^5.7.4"
ms-rest-azure "^3.0.0"
nodemailer "^6.4.2"
qs@~6.5.2:
@@ -1867,7 +1946,7 @@ repeat-string@^1.6.1:
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
"request@>= 2.52.0", request@^2.88.0:
"request@>= 2.52.0":
version "2.88.2"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3"
integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==
@@ -1945,6 +2024,11 @@ saslprep@^1.0.0:
dependencies:
sparse-bitfield "^3.0.3"
sax@>=0.6.0:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
semver-diff@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-2.1.0.tgz#4bbb8437c8d37e4b0cf1a68fd726ec6d645d6d36"
@@ -2226,11 +2310,6 @@ term-size@^1.2.0:
dependencies:
execa "^0.7.0"
through@^2.3.8:
version "2.3.8"
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
timed-out@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
@@ -2268,6 +2347,15 @@ touch@^3.1.0:
dependencies:
nopt "~1.0.10"
tough-cookie@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-3.0.1.tgz#9df4f57e739c26930a018184887f4adb7dca73b2"
integrity sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==
dependencies:
ip-regex "^2.1.0"
psl "^1.1.28"
punycode "^2.1.1"
tough-cookie@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
@@ -2276,6 +2364,16 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
tslib@^1.10.0:
version "1.14.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A==
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
@@ -2283,10 +2381,10 @@ tunnel-agent@^0.6.0:
dependencies:
safe-buffer "^5.0.1"
tunnel@0.0.5:
version "0.0.5"
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.5.tgz#d1532254749ed36620fcd1010865495a1fa9d0ae"
integrity sha512-gj5sdqherx4VZKMcBA4vewER7zdK25Td+z1npBqpbDys4eJrLx+SlYjJvq1bDXs2irkuJM5pf8ktaEQVipkrbA==
tunnel@0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c"
integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==
tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
@@ -2400,7 +2498,7 @@ util.promisify@^1.0.1:
has-symbols "^1.0.1"
object.getownpropertydescriptors "^2.1.0"
uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2, uuid@^3.4.0:
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==
@@ -2447,6 +2545,19 @@ 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.4.19:
version "0.4.23"
resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
dependencies:
sax ">=0.6.0"
xmlbuilder "~11.0.0"
xmlbuilder@~11.0.0:
version "11.0.1"
resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
"xmldom@>= 0.1.x":
version "0.3.0"
resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.3.0.tgz#e625457f4300b5df9c2e1ecb776147ece47f3e5a"

View File

@@ -6,6 +6,9 @@ const config = require('./config');
const MongoStore = require('connect-mongo')(expressSession);
const mongoose = require('mongoose');
const db = require("qmi-cloud-common/mongo");
const axios = require('axios');
const path = require('path');
const fs = require('fs');
// Start QuickStart here
@@ -89,6 +92,19 @@ passport.use(new OIDCStrategy({
//console.log("jwtClaims", jwtClaims);
//console.log("params", params);
console.log(`Passport# new login from: ${profile.upn} (${profile.displayName})` );
//Save user photo
axios({
method: 'get',
url: 'https://graph.microsoft.com/v1.0/me/photo/$value',
responseType: 'stream',
headers: { 'Authorization' : 'Bearer '+accessToken }
}).then(function (response) {
response.data.pipe(fs.createWriteStream(path.resolve(__dirname, '..', 'photos', `${profile.oid}.jpg`)));
}).catch(function(err){
console.log('Passport# Warning: No picture found');
});
// asynchronous verification, for effect...
process.nextTick(function () {
_findByOid(profile.oid, async function(err, user) {
@@ -125,7 +141,9 @@ module.exports.init = function(app){
autoRemove: 'interval',
autoRemoveInterval: 10
//clear_interval: config.mongoDBSessionMaxAge
})
}),
resave: true,
saveUninitialized: false
}));
} else {
app.use(expressSession({ secret: 'keyboard cat', resave: true, saveUninitialized: false }));
@@ -240,7 +258,8 @@ module.exports.ensureAuthenticatedAndAdmin = async function(req, res, next) {
module.exports.ensureAuthenticatedAndIsMe = async function (req, res, next) {
if ( await isApiKeyAuthenticated(req) || req.isAuthenticated() ) {
if ( req.user._id == req.params.userId || req.user.role === 'admin' || req.user.role === 'superadmin' ) {
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");

View File

@@ -23,15 +23,18 @@ const passport = require('../passport');
* description: Notifications
*/
router.post('/updates', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
const now = new Date().toISOString();
const dateNow = new Date();
const now = dateNow.toISOString();
try {
let event = req.body;
const event = req.body;
let logEvent = event.logEvent || 'DivvyCloud';
if ( event.cloudName === 'QMI Automation' ) {
console.log(`DivvyCloud (${now})# event received for subscription (${event.cloudName}) - provision (${event.provID}) -> new status (${event.instanceState})`);
console.log(`${logEvent} (${now})# event received for subscription (${event.cloudName}) - provision (${event.provID}) -> new status (${event.instanceState})`);
if ( event.provID && event.provID !== 'None' ) {
@@ -41,50 +44,91 @@ router.post('/updates', passport.ensureAuthenticatedAndAdmin, async (req, res, n
let id = provision._id.toString();
console.log(`DivvyCloud (${now})# provision (${id}) - scenario is (${provision.scenario} - v${provision.scenarioVersion})`);
console.log(`${logEvent} (${now})# provision (${id}) - scenario is (${provision.scenario} - v${provision.scenarioVersion})`);
if ( provision.status === 'provisioned' ) {
if ( provision.status === 'provisioned' || provision.status === 'error' ) {
if ( event.instanceState === 'Stopped' ) {
if ( provision.statusVms === 'Stopped' ) {
console.log(`DivvyCloud (${now})# provision (${id}) - VMs were already Stopped!`);
console.log(`${logEvent} (${now})# provision (${id}) - VMs were already Stopped!`);
} else {
let timeRunning = db.utils.getNewTimeRunning(provision);
await db.provision.update(id, {"statusVms": "Stopped", "timeRunning": timeRunning, "stoppedFrom": new Date(), "pendingNextAction": undefined});
console.log(`DivvyCloud (${now})# provision (${id}) - VMs changed to Stopped!`);
let patch = {
"statusVms": "Stopped",
"timeRunning": timeRunning,
"stoppedFrom": dateNow,
"pendingNextAction": undefined
};
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 });
}
} else if ( event.instanceState === 'Running' ) {
if ( provision.statusVms === 'Running' ) {
console.log(`DivvyCloud (${now})# provision (${id}) - VMs were already Running!`);
console.log(`${logEvent} (${now})# provision (${id}) - VMs were already Running!`);
} else {
await db.provision.update(id, {"statusVms": "Running", "runningFrom": new Date(), "pendingNextAction": undefined});
console.log(`DivvyCloud (${now})# provision (${id}) - VMs changed to Running!`);
let patch = {
"statusVms": "Running",
"runningFrom": dateNow,
"pendingNextAction": undefined
};
// This is temporary, only to make sure there are values soon
if ( provision.schedule && !provision.schedule.is24x7 ) {
if ( !provision["startDateOnSchedule"] ) {
patch["startDateOnSchedule"] = dateNow;
patch["endDateOnSchedule"] = dateNow;
}
}
await db.provision.update(id, patch);
console.log(`${logEvent} (${now})# provision (${id}) - VMs changed to Running!`);
db.event.add({ user: provision.user._id, provision: provision._id, type: 'vms.start-schedule', message: `TimeRunning so far: ${provision.timeRunning} mins` });
}
}
} else {
console.log(`DivvyCloud (${now})# provision (${event.provID}) - Scenario not yet 'provisioned'`);
console.log(`${logEvent} (${now})# provision (${event.provID}) - Scenario not yet 'provisioned'`);
}
} else {
console.log(`DivvyCloud (${now})# provision (${event.provID}) - Provision not found.`);
console.log(`${logEvent} (${now})# provision (${event.provID}) - Provision not found.`);
}
} else {
console.log(`DivvyCloud (${now})# 'provID' attribute is missing.`);
console.log(`${logEvent} (${now})# 'provID' attribute is missing.`);
}
} else {
//console.log(`DivvyCloud (${now}): event received for subscription (${event.cloudName}) --> won't be processed`);
//console.log(`${logEvent} (${now}): event received for subscription (${event.cloudName}) --> won't be processed`);
}
return res.json(req.body);
} catch (error) {
console.log(`DivvyCloud (${now})# error!!!!`, error);
console.log(`${logEvent} (${now})# error!!!!`, error);
next(error);
}
});

View File

@@ -2,6 +2,7 @@ const express = require('express');
const router = express.Router();
const db = require('qmi-cloud-common/mongo');
const passport = require('../passport');
const sendEmail = require('qmi-cloud-common/send-email');
/**
@@ -34,4 +35,27 @@ router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) =>
}
});
/**
* @swagger
* /notifications/testsendemail:
* post:
* description: Get all notifications
* summary: Get all notifications
* tags:
* - admin
* produces:
* - application/json
* responses:
* 200:
* description: Notifications
*/
router.post('/testsendemail', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
try {
const result = await sendEmail._doSend(req.user.upn, "QMI - Test email", "Hi! This is a test email from QMI Cloud.");
return res.json(result);
} catch (error) {
next(error);
}
});
module.exports = router;

View File

@@ -338,7 +338,7 @@ router.post('/:id/updatetagsvms', passport.ensureAuthenticatedAndAdmin, async (r
if (!provision) {
return res.status(404).json({"msg": "Not found provision with id: "+req.params.id, "success": false});
}
var result = await azurecli.updateVmsTags(provision._id, tagsEdit);
var result = await cli.updateVmsTags(provision._id, tagsEdit);
return res.json({"msg": "Tags are being updated", "result": result, "success": true});
} catch (error) {

227
server/routes/api-stats.js Normal file
View File

@@ -0,0 +1,227 @@
const express = require('express')
const router = express.Router()
const db = require('qmi-cloud-common/mongo');
const moment = require('moment');
const azurecli = require('qmi-cloud-common/azurecli');
const CACHED_PERIOD = 30; //minutes
var cachedTime;
var cachedTimeVms;
var cachedStats;
var cachedVms;
/**
* @swagger
* /stats:
* get:
* description: Get overall stats
* summary: Get overall stats
* produces:
* - application/json
* responses:
* 200:
* description: Stats
* 404:
* description: Not found
*
*/
router.get('/', async (req, res, next) => {
try {
var now = new Date().getTime();
var nowMinus5mins = now - CACHED_PERIOD*60*1000;
if ( (!req.query.disablecache || req.query.disablecache === 'no') && cachedStats && cachedTime && cachedTime > nowMinus5mins ) {
console.log("APIStats# Stats: return cached value");
cachedTime = now;
return res.json(cachedStats);
} else {
console.log("APIStats# Stats: new value");
let filterActiveP = { "isDestroyed": false, "status": "provisioned" };
let filterPRunning = { "isDestroyed": false, "status": "provisioned", "statusVms" : "Running" };
let initCurrentMonth = moment().startOf('month');
let initLastMonth = moment(initCurrentMonth).add(-1, 'months');
let today = moment();
let todayLastMonth = moment().add(-1, "months");
let filterTotalPCurrentMonth = { "status": "provisioned", "created": {
$gte: initCurrentMonth.toISOString(),
$lt: today.toISOString()
} };
let filterTotalPLastMonth = { "status": "provisioned", "created": {
$gte: initLastMonth.toISOString(),
$lt: todayLastMonth.toISOString()
} };
//Counts
let totalActiveP = await db.provision.count(filterActiveP);
let totalCPRunning = await db.provision.count(filterPRunning);
let totalPCurrentmonth = await db.provision.count(filterTotalPCurrentMonth);
let totalPLastmonth = await db.provision.count(filterTotalPLastMonth);
let totalUsers = await db.user.count({});
let totalAuthUsers = await db.user.count({
"lastLogin": {$gte: moment().add(-12, "hours").toISOString()}
});
let totalScenarios = await db.scenario.count({"isDisabled":false, "isAdminOnly": false});
cachedStats = {
provisions: {
active: totalActiveP,
running: totalCPRunning,
totalCurrentMonthPeriod: totalPCurrentmonth,
totalLastMonthPerdiod:totalPLastmonth
},
users: {
total: totalUsers,
activeNow: totalAuthUsers,
active7days: await db.user.count({
"lastLogin": {$gte: moment().add(-7, "days").toISOString()}
}),
active30days: await db.user.count({
"lastLogin": {$gte: moment().add(-30, "days").toISOString()}
})
},
scenarios: {
total: totalScenarios
}
};
cachedTime = now;
return res.json(cachedStats);
}
} catch (error) {
next(error);
}
});
function appendResult(list, output ){
list.forEach(v=>{
if ( v.tags && v.tags["QMI_user"] ) {
if (v.location) {
if ( !output.locations[v.location] ) {
output.locations[v.location] = 0;
}
output.locations[v.location] += 1;
}
if (v.storageProfile && v.storageProfile.osDisk && v.storageProfile.osDisk.osType) {
if ( !output.types[v.storageProfile.osDisk.osType] ) {
output.types[v.storageProfile.osDisk.osType] = 0;
}
output.types[v.storageProfile.osDisk.osType] += 1;
}
output.out.push(v);
}
});
return output;
}
/**
* @swagger
* /stats/vms:
* get:
* description: List azure vms
* summary: List azure vms
* produces:
* - application/json
* responses:
* 200:
* description: Stats
* 404:
* description: Not found
*
*/
router.get('/vms', async (req, res, next) => {
try {
var now = new Date().getTime();
var nowMinus5mins = now - CACHED_PERIOD*60*1000;
if ( (!req.query.disablecache || req.query.disablecache === 'no') && cachedVms && cachedTimeVms && cachedTimeVms > nowMinus5mins ) {
console.log("APIStats# VMs: return cached value");
cachedTimeVms = now;
return res.json(cachedVms);
} else {
console.log("APIStats# VMs: new value");
var output = {
out : [],
locations: {
"eastus": 0,
"westeurope": 0,
"southeastasia": 0
},
types: {
"Windows": 0,
"Linux": 0
}
}
var result = await azurecli.getAllVms();
output = appendResult(result, output);
if ( result.nextLink ) {
console.log("There is a second page");
result = await azurecli.getAllVmsNext(result.nextLink);
output = appendResult(result, output);
if ( result.nextLink ) {
console.log("There is a third page");
result = await azurecli.getAllVmsNext(result.nextLink);
output = appendResult(result, output);
if ( result.nextLink ) {
console.log("There is a forth page");
result = await azurecli.getAllVmsNext(result.nextLink);
output = appendResult(result, output);
if ( result.nextLink ) {
console.log("There is a fifth page");
result = await azurecli.getAllVmsNext(result.nextLink);
output = appendResult(result, output);
if ( result.nextLink ) {
console.log("There is a sixth page");
result = await azurecli.getAllVmsNext(result.nextLink);
output = appendResult(result, output);
if ( result.nextLink ) {
console.log("There is a seventh page");
}
}
}
}
}
}
cachedTimeVms = now;
cachedVms = {
total: output.out.length,
locations: output.locations,
types: output.types
};
return res.json(cachedVms);
}
} catch (error) {
next(error);
}
});
module.exports = router;

View File

@@ -1,26 +1,14 @@
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 moment = require('moment');
const fs = require('fs-extra');
const azurecli = require('qmi-cloud-common/azurecli');
const cli = require('qmi-cloud-common/cli');
const barracuda = require('qmi-cloud-common/barracuda');
import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE } from 'qmi-cloud-common/queues';
const RUNNING_PERIOD_ON_SCHEDULE = 4;
function timeRunningOnSchedule(p) {
let totalRunningTime = p.timeRunning*1000*60;
if ( p.statusVms === 'Running' ) {
let runningFromTime = p.runningFrom? new Date(p.runningFrom).getTime() : new Date(p.created).getTime();
let now = new Date();
totalRunningTime = totalRunningTime + Math.abs(now.getTime() - runningFromTime);
}
let duration = moment.duration(totalRunningTime);
return Math.abs(duration.asHours());
}
import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CONTAINER_QUEUE } from 'qmi-cloud-common/queues';
/**
* @swagger
@@ -85,7 +73,8 @@ router.get('/me', passport.ensureAuthenticated, async (req, res, next) => {
*/
router.get('/:userId', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
const result = await db.user.getById(req.params.userId);
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
const result = await db.user.getById(userId);
return res.json(result);
} catch (error) {
next(error);
@@ -118,7 +107,8 @@ router.get('/:userId', passport.ensureAuthenticatedAndIsMe, async (req, res, nex
router.put('/:userId', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
try {
const result = await db.user.update(req.params.userId, req.body);
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
const result = await db.user.update(userId, req.body);
return res.json(result);
} catch (error) {
next(error);
@@ -176,23 +166,25 @@ router.put('/:userId', passport.ensureAuthenticatedAndAdmin, async (req, res, ne
router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
req.body.user = req.params.userId;
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
req.body.user = userId;
const scenarioSource = await db.scenario.getOne({name: req.body.scenario});
if (!scenarioSource) {
return res.status(404).json({"msg": "Scenario not found "});
}
const filterProvisions = {"user": req.params.userId, "isDestroyed": false, "isDeleted": false, "scenario": scenarioSource.name };
const filterProvisions = {"user": userId, "isDestroyed": false, "isDeleted": false, "scenario": scenarioSource.name };
const result = await db.provision.get(filterProvisions);
if ( scenarioSource.numSimultaneousProvisions && result.total >= scenarioSource.numSimultaneousProvisions ) {
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.vmImage || !req.body.vmImage.vm1 || !req.body.vmImage.vm1.vmType ) {
// return res.status(400).json({"msg": "Invalid vmImage"});
//}
req.body.scenarioVersion = scenarioSource.version;
if ( req.body.scheduleData && req.body.scheduleData.is24x7 !== undefined ) {
@@ -200,13 +192,20 @@ router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (r
req.body.schedule = schedule._id;
}
req.body.terraformImage = config.DOCKERIMAGE_TERRAFORM;
if ( req.body.scenario.indexOf('azqmi-qdi') !== -1 ) {
req.body.version = config.PROVISION_VERSION;
}
const provision = await db.provision.add(req.body);
if ( provision.scenario === "azqmi-qseok" ){
queues[TF_APPLY_QSEOK_QUEUE].add("tf_apply_qseok_job", {
scenario: req.body.scenario,
vmType: req.body.vmType,
vmType: req.body.vmType || null,
nodeCount: req.body.nodeCount,
id: provision._id,
user: req.user,
@@ -230,6 +229,38 @@ router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (r
}
});
/**
* @swagger
* /users/{userId}/provisions/{id}:
* get:
* description: Get provision details
* summary: Get provision details
* parameters:
* - name: userId
* in: path
* type: string
* required: true
* - name: id
* in: path
* type: string
* required: true
* produces:
* - application/json
* responses:
* 200:
* description: Provision
*/
router.get('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
let provision = await db.provision.getById(req.params.id);
return res.json(provision);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /users/{userId}/provisions/{id}:
@@ -264,18 +295,9 @@ router.put('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, async
try {
let schedule;
var msg = "";
if ( req.body.scheduleData ) {
// -- HERE onScheduleRenewed????
var duration = timeRunningOnSchedule(provision); //hours
var onScheduleRenewed = req.body.scheduleData.onScheduleRenewed? req.body.scheduleData.onScheduleRenewed : 1;
if ( duration >= (24 * RUNNING_PERIOD_ON_SCHEDULE * onScheduleRenewed) ) {
onScheduleRenewed = Math.ceil( duration/(24*RUNNING_PERIOD_ON_SCHEDULE) ) + 1;
console.log("APIUser# onScheduleRenewed has been incremented!! -> ", onScheduleRenewed);
}
req.body.scheduleData["onScheduleRenewed"] = onScheduleRenewed;
// --
if ( req.body.scheduleData._id ) {
schedule = await db.schedule.update(req.body.scheduleData._id, req.body.scheduleData);
} else {
@@ -286,19 +308,27 @@ router.put('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, async
"StartupTime": (schedule.isStartupTimeEnable && !schedule.is24x7 && schedule.utcTagStartupTime)? schedule.utcTagStartupTime : false,
"ShutdownTime": (!schedule.is24x7 && schedule.utcTagShutdownTime)? schedule.utcTagShutdownTime : false
}
azurecli.updateVmsTags(provision._id, tagsEdit);
cli.updateVmsTags(provision._id, tagsEdit);
}
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}`;
}
let result = {
provision: await db.provision.update(provision._id, patch)
provision: await db.provision.update(provision._id, patch),
event: await db.event.updateMany({"provision": provision._id}, {"user": req.body.user})
}
db.event.add({ user: provision.user._id, provision: provision._id, type: 'provision.update', message: msg });
return res.json(result);
} catch (error) {
@@ -347,12 +377,205 @@ router.delete('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, as
if (fs.existsSync(`/provisions/${provision.scenario}_${req.params.id}`)) {
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' });
return res.json({"provision": delProv, "destroy": delProv.destroy});
} catch (error) {
next(error);
}
});
/**
* @swagger
* /users/{userId}/provisions/{id}/barracuda:
* get:
* description: Barracuda - get details and provision status
* summary: Barracuda - get details and provision status
* produces:
* - application/json
* parameters:
* - name: userId
* in: path
* type: string
* required: true
* - name: id
* in: path
* type: string
* required: true
* responses:
* 200:
* description: Provision
* 404:
* description: Not found
*
*/
router.get('/:userId/provisions/:id/barracuda', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
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});
}
if ( !provision.barracudaAppId ) {
console.log(`APIUser# Provision (${req.params.id}) does not have a barracudaAppId value!`);
return res.status(404).json({"msg": "Not found Barracuda App for this provision"});
}
var app = await barracuda.getApp(provision);
return res.json(app);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /users/{userId}/provisions/{id}/barracuda:
* post:
* description: Barracuda - give a provision external access
* summary: Barracuda - give a provision external access
* produces:
* - application/json
* parameters:
* - name: userId
* in: path
* type: string
* required: true
* - name: id
* in: path
* type: string
* required: true
* responses:
* 200:
* description: Provision
* 404:
* description: Not found
*
*/
router.post('/:userId/provisions/:id/barracuda', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
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});
}
if ( !provision.barracudaAzureFqdn ) {
console.log(`APIUser# Provision (${req.params.id}) does not have a barracudaAzureFqdn value!`);
return res.json(provision);
}
if ( provision.barracudaAppId ) {
console.log(`APIUser# Provision (${req.params.id}) already have a Barracuda App (${provision.barracudaAppId})!`);
return res.json(provision);
}
console.log(`APIUser# Calling Barracuda service to create App and DNS CName for provision (${provision._id})`);
barracuda.createApp(provision);
return res.json(provision);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /users/{userId}/provisions/{id}/barracuda:
* delete:
* description: Barracuda - delete a provision external access
* summary: Barracuda - delete a provision external access
* produces:
* - application/json
* parameters:
* - name: userId
* in: path
* type: string
* required: true
* - name: id
* in: path
* type: string
* required: true
* responses:
* 200:
* description: Provision
* 404:
* description: Not found
*
*/
router.delete('/:userId/provisions/:id/barracuda', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
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});
}
if ( !provision.barracudaAppId ) {
console.log(`APIUser# Provision (${req.params.id}) does not have a barracudaAppId value!`);
return res.json({});
}
console.log(`APIUser# Calling Barracuda service to delete App and DNS CName for provision (${provision._id})`);
barracuda.deleteApp(provision);
return res.json(provision);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /users/{userId}/provisions/{id}/abort:
* post:
* description: Abort provision
* summary: Abort provision
* produces:
* - application/json
* parameters:
* - name: userId
* in: path
* type: string
* required: true
* - name: id
* in: path
* type: string
* required: true
* responses:
* 200:
* description: Provision
* 404:
* description: Not found
*
*/
router.post('/:userId/provisions/:id/abort', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
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});
}
queues[STOP_CONTAINER_QUEUE].add("tf_abort_apply_job", {
provId: provision._id,
user: req.user
});
return res.json({"status": "aborting"});
} catch (error) {
next(error);
}
});
/**
* @swagger
* /users/{userId}/provisions/{id}/deallocatevms:
@@ -394,10 +617,10 @@ router.post('/:userId/provisions/:id/deallocatevms', passport.ensureAuthenticate
"StartupTime": (schedule.isStartupTimeEnable && !schedule.is24x7 && schedule.utcTagStartupTime)? schedule.utcTagStartupTime : false,
"ShutdownTime": (!schedule.is24x7 && schedule.utcTagShutdownTime)? schedule.utcTagShutdownTime : false
}
azurecli.updateVmsTags(provision._id, tagsEdit);
cli.updateVmsTags(provision._id, tagsEdit);
}
azurecli.deallocate(provision._id);
cli.deallocate(provision._id);
return res.json({"statusVms": "Stopping"});
@@ -439,21 +662,10 @@ router.post('/:userId/provisions/:id/startvms', passport.ensureAuthenticatedAndI
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
}
azurecli.start(provision._id);
//Re-enable DivvyTags according to schedule
if ( provision.schedule ) {
// -- onScheduleRenewed????
var duration = timeRunningOnSchedule(provision); //hours
var onScheduleRenewed = provision.schedule.onScheduleRenewed? provision.schedule.onScheduleRenewed : 1;
if ( duration >= (24 * RUNNING_PERIOD_ON_SCHEDULE * onScheduleRenewed) ) {
onScheduleRenewed = Math.ceil( duration/(24*RUNNING_PERIOD_ON_SCHEDULE) ) + 1;
console.log("APIUser# onScheduleRenewed has been incremented!! -> ", onScheduleRenewed);
}
//let schedule = await db.schedule.update(provision.schedule._id, {"isStartupTimeEnable": true, "onScheduleRenewed": onScheduleRenewed});
let schedule = await db.schedule.update(provision.schedule._id, {"onScheduleRenewed": onScheduleRenewed});
let schedule = await db.schedule.getById(provision.schedule._id);
console.log("APIUser# Re-enabling DivvyTags according to schedule");
var tagsEdit = {
@@ -461,8 +673,11 @@ router.post('/:userId/provisions/:id/startvms', passport.ensureAuthenticatedAndI
"StartupTime": (schedule.isStartupTimeEnable && !schedule.is24x7 && schedule.utcTagStartupTime)? schedule.utcTagStartupTime : false,
"ShutdownTime": (!schedule.is24x7 && schedule.utcTagShutdownTime)? schedule.utcTagShutdownTime : false
}
azurecli.updateVmsTags(provision._id, tagsEdit);
cli.updateVmsTags(provision._id, tagsEdit);
}
cli.start(provision._id);
return res.json({"statusVms": "Starting"});
} catch (error) {
@@ -547,13 +762,13 @@ router.post('/:userId/provisions/:id/extend', passport.ensureAuthenticatedAndIsM
router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
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});
}
const destroyJob = await db.destroy.add({ "user": req.params.userId });
const destroyJob = await db.destroy.add({ "user": userId });
provision = await db.provision.update(req.params.id, {"destroy": destroyJob._id});
const scenarioSource = await db.scenario.getOne({name: provision.scenario});
@@ -592,7 +807,8 @@ router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIs
router.get('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
const filter = {"user": req.params.userId, "isDeleted": false};
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
const filter = {"user": userId, "isDeleted": false};
const result = await db.provision.get(filter);
return res.json(result);
} catch (error) {
@@ -600,6 +816,109 @@ router.get('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (re
}
});
/**
* @swagger
* /users/{userId}/events:
* get:
* description: Get all Events for an User
* summary: Get all Events for an User
* produces:
* - application/json
* parameters:
* - name: userId
* in: path
* type: string
* required: true
* responses:
* 200:
* description: JSON Array
*/
router.get('/:userId/events', passport.ensureAuthenticatedAndIsMe, 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});
return res.json(result);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /users/{userId}/provisions/{id}/events:
* get:
* description: Get all Events for an User and Provision
* summary: Get all Events for an User and Provision
* produces:
* - application/json
* parameters:
* - name: userId
* in: path
* type: string
* required: true
* - name: id
* in: path
* type: string
* required: true
* responses:
* 200:
* description: JSON Array
*/
router.get('/:userId/provisions/:id/events', passport.ensureAuthenticatedAndIsMe, 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});
return res.json(result);
} catch (error) {
next(error);
}
});
/**
* @swagger
* /users/{userId}/scenarios:
* get:
* description: Get all Provisions for an User
* summary: Get all Provisions for an User
* produces:
* - application/json
* parameters:
* - name: userId
* in: path
* type: string
* required: true
* responses:
* 200:
* description: JSON Array
*/
router.get('/:userId/scenarios', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
let filter = {};
if (req.user.role === "user") {
filter.isAdminOnly = false;
}
filter.isDisabled = filter.isDisabled || false;
var result = await db.scenario.get(filter);
if (req.user.role === "user") {
result.results = result.results.filter( scenario => {
let noAllowedUsers = !scenario.allowedUsers || scenario.allowedUsers.length === 0;
if ( noAllowedUsers ) {
return true;
} else {
let allowedUserIds = scenario.allowedUsers.map( u=> u._id.toString());
return allowedUserIds.indexOf(req.user._id.toString()) !== -1;
}
});
}
return res.json(result);
} catch (error) {
next(error);
}
});
/**
* @swagger
@@ -621,7 +940,8 @@ router.get('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (re
router.get('/:userId/destroyprovisions', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
try {
const result = await db.destroy.get({"user": req.params.userId});
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
const result = await db.destroy.get({"user": userId});
return res.json(result);
} catch (error) {
next(error);

View File

@@ -2,7 +2,7 @@ const url = require("url");
const express = require("express");
import Arena from 'bull-arena';
import { TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE } from 'qmi-cloud-common/queues';
import { TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CONTAINER_QUEUE } from 'qmi-cloud-common/queues';
const app = express();
const routesApiScenarios = require('./routes/api-scenarios');
@@ -13,6 +13,7 @@ const routesApiNotifications = require('./routes/api-notifications');
const routesApiDivvy = require('./routes/api-divvy');
const routesApiDeployOpts = require('./routes/api-deployopts')
const routesApiApikeys = require('./routes/api-apikeys')
const routesApiStats = require('./routes/api-stats')
const swaggerUi = require('swagger-ui-express');
const swaggerJsdoc = require('swagger-jsdoc');
const cookieParser = require('cookie-parser');
@@ -51,6 +52,11 @@ app.use('/arena', Arena(
name: TF_DESTROY_QUEUE,
hostId: 'Worker',
redis: _getRedisConfig(process.env.REDIS_URL)
},
{
name: STOP_CONTAINER_QUEUE,
hostId: 'Worker',
redis: _getRedisConfig(process.env.REDIS_URL)
}
]
},
@@ -85,14 +91,29 @@ app.use("/api/v1/notifications", routesApiNotifications);
app.use("/api/v1/divvy", routesApiDivvy);
app.use("/api/v1/deployopts", routesApiDeployOpts);
app.use("/api/v1/apikeys", routesApiApikeys);
app.use("/api/v1/stats", routesApiStats);
function _isAllowedPath(path){
const allowedPaths = [ '/api-docs', '/arena', '/costexport', '/backendlogs', '/photos/user/' ];
let isAllowed = false;
for (let i=0; i<allowedPaths.length; i++) {
if ( path.startsWith( allowedPaths[i]) ) {
isAllowed = true;
break;
}
}
return isAllowed;
}
/* Checking allowedPaths */
app.get('/*',(req, res, next) =>{
if (req.originalUrl.indexOf("/api-docs") !== -1 || req.originalUrl.indexOf("/arena") !== -1 || req.originalUrl.indexOf("/costexport") !== -1 || req.originalUrl.indexOf("/backendlogs") !== -1) {
if ( _isAllowedPath(req.originalUrl) ) {
return next();
} else {
res.sendFile(path.join(__dirname,'/../dist/qmi-cloud/index.html'));
}
});
/* -----------------------*/
app.get('/login', passport.ensureAuthenticatedDoLogin, function(req, res) {
res.redirect("/");
@@ -131,25 +152,7 @@ const options = {
name: "apiKey",
in: "query"
}
},
/*schemas: {
"user": {
"properties": {
"displayName": {
"type": "string"
},
"upn": {
"type": "string"
},
"oid": {
"type": "string"
},
"role": {
"type": "string"
}
}
}
}*/
},
security: [{
ApiKeyAuth: []
@@ -167,21 +170,35 @@ app.use('/costexport*', passport.ensureAuthenticatedAndAdmin, function(req, res)
res.status(404).send("Not found");
} else {
res.header("Content-Type",'application/json');
res.sendFile(path.resolve(__dirname, '..', 'costexport', req.query.file ));
}
} );
app.use('/photos/user/:oid', passport.ensureAuthenticated, function(req, res){
if ( !req.params.oid ) {
res.status(404).send("Not found");
} else {
var pic = path.resolve(__dirname, '..', 'photos', `${req.params.oid}.jpg` );
if (fs.existsSync(pic)){
res.sendFile(pic);
} else {
res.status(404).send();
}
}
} );
const specs = swaggerJsdoc(options);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));
/**
* Create necessary folders
*/
console.log("--- Create necessary folders");
var dirs = ['/logs', '/logs/provision', '/logs/destroy'];
var dirs = ['/logs', '/logs/provision', '/logs/destroy', '/costexports', '/photos'];
dirs.forEach(d => {
if (!fs.existsSync(d)){
console.log(`--- Creating folder '${d}' since it does not exist`);
fs.mkdirSync(d);
}
});

View File

@@ -1,7 +1,7 @@
<p style="margin-top: 70px;text-align: right;">
<a class="lui-text-info" href="/backendlogs" target="blank"><i class="fas fa-long-arrow-alt-right"></i> Backend Logs (internal)</a>
<a class="lui-text-info" href="/backendlogs" target="_blank"><i class="fas fa-long-arrow-alt-right"></i> Backend Logs (internal)</a>
</p>
<ul class="nav nav-pills nav-fill">
@@ -10,32 +10,32 @@
</li>
</ul>
<div *ngIf="tab === 'Scenarios'">
<div *ngIf="tab === 'scenarios'">
<!--<h1>Scenarios</h1>-->
<table-scenarios></table-scenarios>
</div>
<div *ngIf="tab === 'Scenario Deploy Opts'">
<div *ngIf="tab === 'scenario-deploy-opts'">
<!--<h1>Scenarios</h1>-->
<table-subscriptions></table-subscriptions>
</div>
<div *ngIf="tab === 'Provisions'">
<div *ngIf="tab === 'provisions'">
<!--<h1>Provisions</h1>-->
<table-provisions></table-provisions>
</div>
<div *ngIf="tab === 'Users'">
<div *ngIf="tab === 'users'">
<!--<h1>Users</h1>-->
<table-users></table-users>
</div>
<div *ngIf="tab === 'Notifications'">
<div *ngIf="tab === 'notifications'">
<!--<h1>Scenarios</h1>-->
<table-notifications></table-notifications>
</div>
<div *ngIf="tab === 'API keys'">
<div *ngIf="tab === 'api-keys'">
<!--<h1>Scenarios</h1>-->
<table-apikeys></table-apikeys>
</div>
<div *ngIf="tab === 'VM Types'">
<div *ngIf="tab === 'vm-types'">
<!--<h1>Scenarios</h1>-->
<table-vmtypes></table-vmtypes>
</div>

View File

@@ -1,24 +1,35 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
@Component({
selector: 'app-admin',
templateUrl: './admin.component.html',
styleUrls: ['./admin.component.scss']
})
export class AdminComponent implements OnInit {
export class AdminComponent implements OnInit, OnDestroy {
sections = ['Provisions', 'Scenarios', 'Scenario Deploy Opts', 'Users', 'Notifications','API keys', 'VM Types'];
sections = ['provisions', 'scenarios', 'scenario-deploy-opts', 'users', 'notifications','api-keys', 'vm-types'];
tab : string = 'Provisions';
private sub: any;
constructor() { }
constructor( private route: ActivatedRoute, private router: Router ) { }
ngOnInit() {
this.sub = this.route.params.subscribe(params => {
console.log("Params", params);
this.tab = params['tab'] || 'provisions'; // (+) converts string 'id' to a number
});
}
tabSelect($event, tab) {
$event.preventDefault();
this.router.navigate(['/admin', tab]);
/*$event.preventDefault();
$event.stopPropagation();
this.tab = tab;
this.tab = tab;*/
}
ngOnDestroy() {
this.sub.unsubscribe();
}
}

View File

@@ -6,13 +6,16 @@ import { StatsComponent } from './stats/stats.component';
import { HomeComponent } from './home/home.component';
import { AuthGuard } from './services/auth.guard';
import { FaqComponent } from './faq/faq.component';
import { UserDashboardComponent } from './user/user-dashboard.component';
const routes: Routes = [
{ path: 'home', component: HomeComponent},
{ path: 'faq', component: FaqComponent},
{ path: 'provisions', component: ProvisionsComponent, canActivate: [AuthGuard]},
{ path: 'admin', component: AdminComponent, canActivate: [AuthGuard]},
{ path: 'admin/:tab', component: AdminComponent, canActivate: [AuthGuard]},
{ path: 'stats', component: StatsComponent, canActivate: [AuthGuard]},
{ path: 'user/:id', component: UserDashboardComponent, canActivate: [AuthGuard]},
{ path: '',
redirectTo: '/home',
pathMatch: 'full'

View File

@@ -38,12 +38,14 @@ import { TableSubsComponent } from './tables/table-subscriptions.component';
import { TableVmTypesComponent } from './tables/table-vmtypes.component';
import { SubscriptionsService } from './services/deployopts.service';
import { StatsService } from './services/stats.service';
import { TableApiKeysComponent } from './tables/table-apikeys.component';
import { ApikeyModalComponent } from './modals/edit-apikey.component';
import { VmTypeModalComponent } from './modals/edit-vmtype.component';
import { StatsComponent } from './stats/stats.component';
//import { QdtComponentComponent } from './qdt-components/qdt-components.component';
import { ProvisionModalComponent } from './modals/edit-provision.component';
import { UserDashboardComponent } from './user/user-dashboard.component'
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
@@ -88,6 +90,7 @@ export function markedOptions(): MarkedOptions {
StatsComponent,
//QdtComponentComponent,
ProvisionModalComponent,
UserDashboardComponent,
],
imports: [
BrowserModule,
@@ -108,7 +111,8 @@ export function markedOptions(): MarkedOptions {
SubscriptionsService,
UsersService,
AlertService,
AuthGuard
AuthGuard,
StatsService
],
bootstrap: [AppComponent]
})

View File

@@ -1,12 +1,49 @@
<section style="margin-top: 220px; min-height: 600px;" class="text-center">
<div class="container">
<img class="rounded float-right" src="assets/cloud.png" style="width: 300px;">
<h1>QMI Cloud</h1>
<!--<img class="rounded float-right" src="https://www.qlik.com/us/-/media/images/qlik/global/qlik-logo-2x.png" style="width: 300px;">-->
<h1><div>Qlik Machine Images (QMI)</div><div>Cloud</div></h1>
<p class="lead text-muted">Introducing a new way to launch scenarios based on Qlik software in the Cloud.</p>
<p *ngIf="!user">
<a mdbBtn href="/login" color="dark-green" mdbWavesEffect>Login <mdb-icon fas icon="sign-in-alt"></mdb-icon></a> </p>
<p *ngIf="user">
<a mdbBtn color="elegant" routerLink="/provisions">Your Provisions <mdb-icon fas icon="angle-right"></mdb-icon></a>
</p>
<div *ngIf="stats" class="row" style="margin-top: 100px;">
<div class="col-md-4" style="text-align: left;">
<h1># Users</h1>
<p class="kpiline">Registered: <span class="kpilabel">{{stats.users.total}}</span></p>
<p class="kpiline">Active now: <span class="kpilabel">{{stats.users.activeNow}}</span></p>
<p class="kpiline">Active last 7days: <span class="kpilabel">{{stats.users.active7days}}</span></p>
<p class="kpiline">Active last 30days: <span class="kpilabel">{{stats.users.active30days}}</span></p>
</div>
<div class="col-md-4" style="text-align: left;">
<h1># Provisions</h1>
<p class="kpiline">Available scenarios: <span class="kpilabel">{{stats.scenarios.total}}</span></p>
<p class="kpiline">Active now: <span class="kpilabel">{{stats.provisions.active}}</span> ( {{stats.provisions.running}} with running VMS )</p>
<p class="kpiline">Total month-to-date: <span class="kpilabel">{{stats.provisions.totalCurrentMonthPeriod}}</span></p>
<p class="kpiline">Total last month (same period): <span class="kpilabel">{{stats.provisions.totalLastMonthPerdiod}}</span></p>
</div>
<div class="col-md-4" style="text-align: left;">
<h1># Virtual Machines</h1>
<div *ngIf="vms">
<p class="kpiline">Active now: <span class="kpilabel">{{vms.total}}</span></p>
<p class="kpiline">By region:</p>
<ul>
<li style="font-size: 14px;">East US: <span class="kpilabel">{{vms.locations.eastus}}</span></li>
<li style="font-size: 14px;">West Europe: <span class="kpilabel">{{vms.locations.westeurope}}</span></li>
<li style="font-size: 14px;">Southeast Asia: <span class="kpilabel">{{vms.locations.southeastasia}}</span></li>
</ul>
<p class="kpiline">By OS type:</p>
<ul>
<li style="font-size: 14px;">Windows: <span class="kpilabel">{{vms.types.Windows}}</span></li>
<li style="font-size: 14px;">Linux: <span class="kpilabel">{{vms.types.Linux}}</span></li>
</ul>
</div>
</div>
</div>
</div>
</section>

View File

@@ -0,0 +1,7 @@
.kpilabel {
font-size: 1.4em;
}
.kpiline {
margin-bottom: 0px;
}

View File

@@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { AuthGuard } from '../services/auth.guard';
import { StatsService } from '../services/stats.service';
@Component({
@@ -13,8 +14,10 @@ export class HomeComponent implements OnInit {
user;
subs: Subscription;
stats;
vms;
constructor( private _auth: AuthGuard){
constructor( private _auth: AuthGuard, private _stats: StatsService ){
this.subs = this._auth.getUserInfo().subscribe( value => {
this.user = value;
console.log("USER", this.user);
@@ -22,7 +25,14 @@ export class HomeComponent implements OnInit {
}
ngOnInit() {
this._stats.getStats().subscribe(value => {
this.stats = value;
})
this._stats.getVms().subscribe(value => {
console.log("vms", value);
this.vms = value;
})
}
ngOnDestroy() {

View File

@@ -22,9 +22,8 @@ export class ApikeyModalComponent implements OnInit, OnDestroy {
constructor( public modalRef: MDBModalRef, private _usersService: UsersService ) {}
ngOnInit() {
this._usersService.getUsers().subscribe(res=> {
this._usersService.getUsers(true).subscribe(res=> {
this.users = res.results;
this.users = this.users.sort(function(a, b){return a.displayName.localeCompare(b.displayName);});
if (this.apiKey) {
this.sendData = JSON.parse(JSON.stringify(this.apiKey))
}

View File

@@ -45,9 +45,8 @@ export class ProvisionModalComponent implements OnInit, OnDestroy {
if ( this.currentUser.role === 'admin' || this.currentUser.role === 'superadmin' ) {
this._usersService.getUsers().subscribe(res=> {
this._usersService.getUsers(true).subscribe(res=> {
this.users = res.results;
this.users = this.users.sort(function(a, b){return a.displayName.localeCompare(b.displayName);});
if ( this.provision.user ) {
this.selectedUser = this.provision.user._id;
}

View File

@@ -32,6 +32,10 @@
<input mdbInput type="number" name="text" [(ngModel)]="sendData.numSimultaneousProvisions" id="sim1" class="form-control">
<label for="sim1" class="">Num. simultaneus provisions:</label>
</div>
<div class="md-form">
<input mdbInput type="text" name="text" [(ngModel)]="support" id="support1" class="form-control">
<label for="support1" class="">Support (emails comma separated):</label>
</div>
<!--<section style="padding: 20px 0px;">
<label>Deploy options (*):</label>
<select class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedSubscription">
@@ -49,7 +53,16 @@
<pre style="border: 1px solid #ccc;" contenteditable="true" (blur)="updateJson($event, 'availableProductVersions')">{{sendData.availableProductVersions | json}}</pre>
</section>
<section style="padding: 20px 0px; margin: 0px 20px">
<mdb-checkbox (change)="checkOnchange($event, 'isAdminOnly')" [checked]="sendData.isAdminOnly" [default]="false"><mdb-icon fas icon="user-secret" class="grey-text" aria-hidden="true"></mdb-icon> Only for Administrators?</mdb-checkbox>
<mdb-checkbox (change)="checkOnchange($event, 'isAdminOnly')" [checked]="sendData.isAdminOnly" [default]="false"><mdb-icon fas icon="lock" class="grey-text" aria-hidden="true"></mdb-icon> Only for Administrators?</mdb-checkbox>
</section>
<section *ngIf="!sendData.isAdminOnly">
<label>Or, Administrators and selected users:</label>
<select class="custom-select" multiple [(ngModel)]="selectedAllowedUsers">
<option value="{{user._id}}" *ngFor="let user of allUsers">{{user.displayName}}</option>
</select>
<div style="font-size: 10px;">
<i *ngFor="let u of selectedAllowedUsersObjects">{{u.displayName}}; </i>
</div>
</section>
<section style="padding: 20px 0px; margin: 0px 20px">
<mdb-checkbox (change)="checkOnchange($event, 'isExternal')" [checked]="sendData.isExternal" [default]="false"><mdb-icon fas icon="globe-americas" class="grey-text" aria-hidden="true"></mdb-icon> With External Access?</mdb-checkbox>
@@ -64,12 +77,16 @@
<mdb-checkbox (change)="checkOnchange($event, 'isVnetIsolated')" [checked]="sendData.isVnetIsolated" [default]="false"><mdb-icon fas icon="network-wired" class="grey-text" aria-hidden="true"></mdb-icon> Isolated Vnet (secure)?</mdb-checkbox>
</section>-->
<section style="padding: 20px 0px; margin: 0px 20px">
<mdb-checkbox (change)="checkOnchange($event, 'isDisabled')" [checked]="sendData.isDisabled" [default]="false"><mdb-icon fas icon="ban" class="grey-text" aria-hidden="true"></mdb-icon> Is disabled?</mdb-checkbox>
<mdb-checkbox (change)="checkOnchange($event, 'isDisabled')" [checked]="sendData.isDisabled" [default]="false"><mdb-icon fas icon="times-circle" class="grey-text" aria-hidden="true"></mdb-icon> Is disabled?</mdb-checkbox>
</section>
<div class="md-form">
<input mdbInput type="text" name="text" [(ngModel)]="sendData.newImageName" id="image1" class="form-control">
<label for="image1" class="">New Image Name (only for GEN scenarios):</label>
</div>
<div class="md-form">
<input mdbInput type="text" name="text" [(ngModel)]="sendData.gitBranch" id="gitbranch1" class="form-control">
<label for="gitbranch1" class="">Git branch (leave blank for default - master):</label>
</div>
</div>
</div>

View File

@@ -3,6 +3,7 @@ import { MDBModalRef } from 'angular-bootstrap-md';
import { Subject, Subscription } from 'rxjs';
import { ScenariosService } from '../services/scenarios.service';
import { SubscriptionsService } from '../services/deployopts.service';
import { UsersService } from '../services/users.service';
@Component({
selector: 'qmi-new-scenario',
@@ -15,7 +16,11 @@ export class ScenarioModalComponent implements OnInit, OnDestroy {
action: Subject<any> = new Subject();
subscriptions;
selectedDeployOpts;
selectedAllowedUsers;
selectedAllowedUsersObjects = [];
allUsers;
labels : String = "";
support: String = "";
sendData : any = {
availableProductVersions: [{
@@ -27,9 +32,14 @@ export class ScenarioModalComponent implements OnInit, OnDestroy {
}]
};
constructor( public modalRef: MDBModalRef, private _scenariosService: ScenariosService, private _subscriptionsService: SubscriptionsService ) {}
constructor( public modalRef: MDBModalRef, private _usersService: UsersService, private _scenariosService: ScenariosService, private _subscriptionsService: SubscriptionsService ) {}
ngOnInit() {
this._usersService.getUsers(true).subscribe( users => {
this.allUsers = users.results;
});
this._subscriptionsService.getSubscriptions().subscribe ( res => {
this.subscriptions = res.results;
console.log("SCENARIO",this.scenario);
@@ -42,9 +52,20 @@ export class ScenarioModalComponent implements OnInit, OnDestroy {
if ( this.scenario.deployOpts ) {
this.selectedDeployOpts = this.scenario.deployOpts.map( item => item._id);
}
if ( this.scenario.allowedUsers ) {
this.selectedAllowedUsers = this.scenario.allowedUsers.map( item => item._id);
this.selectedAllowedUsersObjects = JSON.parse(JSON.stringify(this.scenario.allowedUsers));
}
console.log("selectedAllowedUsers", this.selectedAllowedUsers);
if ( this.scenario.labels ) {
this.labels = this.scenario.labels.join(",");
}
if ( this.scenario.support ) {
this.support = this.scenario.support.join(",");
}
});
}
@@ -55,12 +76,15 @@ export class ScenarioModalComponent implements OnInit, OnDestroy {
confirm() : void {
let postData = this.sendData;
postData.deployOpts = this.selectedDeployOpts;
postData.allowedUsers = this.selectedAllowedUsers;
if ( this.labels.length ) {
postData.labels = this.labels.split(",");
}
if ( this.support.length ) {
postData.support = this.support.split(",");
}
console.log("sendData", postData);
//this.action.next(postData);
//console.log("Selected Deploy opts", this.selectedDeployOpts);
if ( postData._id) {
var id = postData._id.toString();
postData._id = undefined;

View File

@@ -3,7 +3,10 @@
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
<span aria-hidden="true">×</span>
</button>
<h3 style="text-align: center;" class="modal-title w-100">{{info._scenarioDoc.title}}</h3>
<h3 style="text-align: center;" class="modal-title w-100">
{{info._scenarioDoc.title}}
<div style="font-size: 16px;">{{info.scenario}} (v{{info.scenarioVersion}})</div>
</h3>
</div>
<div class="modal-body">
<div style="text-align: right;">
@@ -27,6 +30,7 @@
<mdb-icon fas icon="server"></mdb-icon> {{info.vmType}} <span *ngIf="info.nodeCount">( {{info.nodeCount}} nodes )</span>
</div>
<div *ngFor="let item of info.vmImage | keyvalue">
<div *ngIf="!item.value.disabled">
<div *ngIf="item.value.version">
<div>
<mdb-icon fas icon="server"></mdb-icon> {{item.value.version.name}}
@@ -41,6 +45,7 @@
</div>
</div>
</div>
</div>
<div *ngIf="info.isExternalAccess">
<mdb-icon fas icon="globe-americas"></mdb-icon> External access enabled
</div>
@@ -49,6 +54,16 @@
<b>{{item.key}}</b>
<div class="mydata">{{item.value}}</div>
</div>
<h5 *ngIf="events" class="info-subtitle">Events</h5>
<table *ngIf="events" mdbTable stickyHeader="true" hover="true" class="table table-sm">
<tbody>
<tr *ngFor="let item of events; let i = index">
<td style="min-width:120px;">{{item.created | date: 'MMM dd, yyyy - H:mm'}}</td>
<td style="min-width:130px;">{{item.type}}</td>
<td>{{item.message}}</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" mdbBtn size="sm" color="grey" class="waves-light" aria-label="Close" (click)="modalRef.hide()" mdbWavesEffect>Close</button>

View File

@@ -16,3 +16,7 @@
.linethrough {
text-decoration: line-through;
}
td {
font-size: 8px !important;
}

View File

@@ -1,5 +1,6 @@
import { Component, OnInit, OnDestroy, Input } from '@angular/core';
import { MDBModalRef } from 'angular-bootstrap-md';
import { ProvisionsService } from '../services/provisions.service';
@Component({
selector: 'qmi-modalinfo',
@@ -10,10 +11,14 @@ export class ModalInfoComponent implements OnInit, OnDestroy {
info;
constructor( public modalRef: MDBModalRef ) {}
events;
constructor( public modalRef: MDBModalRef, private _provisionsService: ProvisionsService ) {}
ngOnInit() {
this._provisionsService.getEvents(this.info.user._id, this.info._id).subscribe(res=> {
this.events = res.results;
});
}
ngOnDestroy() {

View File

@@ -4,21 +4,26 @@
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
<span aria-hidden="true">×</span>
</button>
<h4 class="modal-title w-100 font-weight-bold">New provision</h4>
<h5 class="modal-title w-100 font-weight-bold">
New provision
<div style="font-size: 14px;">{{scenario.title}}</div>
</h5>
</div>
<div class="modal-body" style="max-height: 650px; overflow: auto;">
<div class="modal-body" style="max-height: 550px; overflow: auto;">
<!--<h5>{{scenario.title}}</h5>-->
<div class="needs-validation">
<div class="md-form row" style="padding: 15px;">
<div class="col-sm-6">
<h5>Purpose: (*)</h5>
<textarea [(ngModel)]="sendData.description" type="text" class="md-textarea form-control" rows="1" mdbInput placeholder="Short description or Salesforce opportunity" required></textarea>
<textarea [(ngModel)]="sendData.description" type="text" class="md-textarea form-control" rows="1" mdbInput
placeholder="Short description or Salesforce opportunity" required></textarea>
</div>
<div *ngIf="scenario.deployOpts" class="col-sm-6">
<div>
<label>Deploy into region: (*)</label>
</div>
<select id="region" class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedDeployOpts" required>
<select id="region" class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedDeployOpts"
required>
<option value="" selected disabled>-- Select --</option>
<option *ngFor="let item of scenario.deployOpts" [value]="item._id">{{item.description}}</option>
</select>
@@ -28,42 +33,52 @@
<div *ngIf="scenario.isDivvyEnabled" style="background: #f0f0f0; padding: 15px 15px; margin: 7px 0px;">
<div>
<h5><mdb-icon far icon="clock"></mdb-icon> VMs Running schedule</h5>
<h5>
<mdb-icon far icon="clock"></mdb-icon> VMs Running schedule
</h5>
</div>
<p [ngClass]="{'disabled': schedule.is24x7}" style="font-size: 12px;"><i>Everyday VMs for this scenario will run as scheduled bellow</i></p>
<p [ngClass]="{'disabled': schedule.is24x7}" style="font-size: 12px;"><i>Everyday VMs for this scenario will run
as scheduled bellow</i></p>
<div [ngClass]="{'disabled': schedule.is24x7}">
Timezone is <b>{{zone}}</b> <i>(times relative to this timezone)</i>
</div>
<div class="row" [ngClass]="{'disabled': schedule.is24x7}">
<div class="col-sm-6">
<section style="padding: 10px 0px;">
<mdb-checkbox [default]="true" [checked]="schedule.isStartupTimeEnable" (change)="checkOnchangeStartupTime($event)"><b>Startup time:</b></mdb-checkbox>
<mdb-checkbox [default]="true" [checked]="schedule.isStartupTimeEnable"
(change)="checkOnchangeStartupTime($event)"><b>Startup time:</b> <i [ngClass]="{'disabled': !schedule.isStartupTimeEnable}" class="utc">({{getUTCTimes().startupTime}} UTC)</i></mdb-checkbox>
</section>
<div [ngClass]="{'disabled': !schedule.isStartupTimeEnable}"><ngb-timepicker style="font-size: 10px;" [(ngModel)]="schedule.startupTime" [meridian]="true" [minuteStep]="15" [disabled]="schedule.is24x7 || !schedule.isStartupTimeEnable"></ngb-timepicker></div>
<div [ngClass]="{'disabled': !schedule.isStartupTimeEnable}" class="utc">
<i>UTC time: {{getUTCTimes().startupTime}}</i>
<div [ngClass]="{'disabled': !schedule.isStartupTimeEnable}">
<ngb-timepicker style="font-size: 10px;" [(ngModel)]="schedule.startupTime" [meridian]="true"
[minuteStep]="15" [disabled]="schedule.is24x7 || !schedule.isStartupTimeEnable"></ngb-timepicker>
</div>
</div>
<div class="col-sm-6">
<section style="padding: 10px 0px;">
<mdb-checkbox [default]="true" [checked]="true" [disabled]="true"><b>Shutdown time:</b></mdb-checkbox>
<mdb-checkbox [default]="true" [checked]="true" [disabled]="true"><b>Shutdown time:</b> <i class="utc">({{getUTCTimes().shutdownTime}} UTC)</i></mdb-checkbox>
</section>
<div><ngb-timepicker style="font-size: 10px;" [(ngModel)]="schedule.shutdownTime" [meridian]="true" [minuteStep]="15" [disabled]="schedule.is24x7"></ngb-timepicker></div>
<div class="utc">
<i>UTC time: {{getUTCTimes().shutdownTime}}</i>
<div>
<ngb-timepicker style="font-size: 10px;" [(ngModel)]="schedule.shutdownTime" [meridian]="true"
[minuteStep]="15" [disabled]="schedule.is24x7"></ngb-timepicker>
</div>
</div>
</div>
<hr>
<section style="padding: 0px; margin: 0px 20px">
<mdb-checkbox [default]="false" [checked]="schedule.is24x7" (change)="checkOnchangeSchedule($event)">Run 24x7 <i>(<mdb-icon fas icon="exclamation-triangle"></mdb-icon> requires permission from regional manager)</i></mdb-checkbox>
<mdb-checkbox [default]="false" [checked]="schedule.is24x7" (change)="checkOnchangeSchedule($event)">Run 24x7
<i>(<mdb-icon fas icon="exclamation-triangle"></mdb-icon> requires permission from regional manager)</i>
</mdb-checkbox>
</section>
<div style="padding-left: 25px;font-size: 12px;">
<mdb-icon fas icon="long-arrow-alt-right"></mdb-icon> VMs will run 24 hours during 7 days. Then, if 24x7 is not renewed, VMs will shutdown.
<mdb-icon fas icon="long-arrow-alt-right"></mdb-icon> VMs will run 24 hours during 7 days. Then, if 24x7 is not
renewed, VMs will shutdown.
</div>
</div>
<div style="padding: 15px; margin: 7px 0px; background: #f0f0f0;">
<div *ngIf="scenario.availableProductVersions && scenario.availableProductVersions.length"
style="padding: 15px; margin: 7px 0px; background: #f0f0f0;">
<h5 style="font-weight: 600;">
<mdb-icon fas icon="server"></mdb-icon> Servers
</h5>
@@ -72,22 +87,29 @@
{{server.product}}
</h5>
<div *ngIf="server.optional !== undefined">
<mdb-checkbox style="display: inline-block;padding-left: 20px;vertical-align: top;" [default]="false" [checked]="selectedServers[server.index] || server.optional === undefined" (change)="checkOnchangeServer($event, server)" [disabled]="server.optional === undefined">Enable</mdb-checkbox>
<mdb-checkbox style="display: inline-block;padding-left: 20px;vertical-align: top;" [default]="false"
[checked]="selectedServers[server.index] || server.optional === undefined"
(change)="checkOnchangeServer($event, server)" [disabled]="server.optional === undefined">Enable
</mdb-checkbox>
</div>
<div *ngIf="server.versions && server.versions.length && (selectedServers[server.index] || server.optional === undefined)" >
<div
*ngIf="server.versions && server.versions.length && (selectedServers[server.index] || server.optional === undefined)">
<div>
<div>Product version:</div>
</div>
<select id="pversion" class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedProductVersion[server.index]" >
<select id="pversion" class="browser-default custom-select custom-select-sm"
[(ngModel)]="selectedProductVersion[server.index]">
<option *ngFor="let v of server.versions" [value]="v.name">{{v.name}}</option>
</select>
</div>
<div class="row" *ngIf="selectedServers[server.index] || server.optional === undefined">
<div class="row"
*ngIf="(selectedServers[server.index] || server.optional === undefined) && selectedVmType[server.index]">
<div class="col-sm-6">
<div>
<div>VM type:</div>
</div>
<select id="vmtype" class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedVmType[server.index]">
<select id="vmtype" class="browser-default custom-select custom-select-sm"
[(ngModel)]="selectedVmType[server.index]">
<option *ngFor="let item of vmTypes" [value]="item.type">{{item.type}} ({{item.desc}})</option>
</select>
</div>
@@ -95,34 +117,57 @@
<div>
<div>Disk size (GiB):</div>
</div>
<select class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedDiskSizeGb[server.index]">
<option *ngFor="let item of [128,250,500,750,1000]" [value]="item">{{item}}</option>
<select class="browser-default custom-select custom-select-sm"
[(ngModel)]="selectedDiskSizeGb[server.index]">
<option *ngFor="let item of availableDiskSizes[server.index]" [value]="item">{{item}}</option>
</select>
</div>
</div>
<div *ngIf="server.nodeCount">
<div>Num. nodes:</div>
</div>
<select *ngIf="scenario.nodeCount" class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedNodeCount[server.index]">
<select *ngIf="scenario.nodeCount" class="browser-default custom-select custom-select-sm"
[(ngModel)]="selectedNodeCount[server.index]">
<option *ngFor="let item of [1,2,3,4,5,6]" [value]="item">{{item}}</option>
</select>
</div>
</div>
<div style="background: #f0f0f0; padding: 15px 15px; margin: 7px 0px;">
<div *ngIf="scenario.isExternal && scenario.availableProductVersions && scenario.availableProductVersions.length"
style="background: #f0f0f0; padding: 15px 15px; margin: 7px 0px;">
<div>
<h5><mdb-icon fas icon="globe-americas"></mdb-icon> External access</h5>
<h5>
<mdb-icon fas icon="globe-americas"></mdb-icon> External access
</h5>
</div>
<section *ngIf="scenario.isExternal" style="padding-left: 20px;">
<mdb-checkbox [default]="false" (change)="checkOnchange($event)">Enable (<mdb-icon fas icon="exclamation-triangle"></mdb-icon> only if it's strictly necessary)</mdb-checkbox>
<section style="padding-left: 20px;">
<mdb-checkbox [default]="false" (change)="checkOnchange($event)">Enable (<mdb-icon fas
icon="exclamation-triangle"></mdb-icon> only if it's strictly necessary)</mdb-checkbox>
</section>
<i *ngIf="!scenario.isExternal">This scenario only allows access from VPN</i>
<!--<i *ngIf="!scenario.isExternal">This scenario only allows access from VPN</i>-->
</div>
<div *ngIf="scenario.availableProductVersions && scenario.availableProductVersions.length"
style="padding: 15px 15px; margin: 7px 0px;">
<div>
<h5>
<mdb-icon fas icon="info"></mdb-icon> Important Notes:
</h5>
</div>
<ul>
<li *ngIf="scenario.isDivvyEnabled && !schedule.is24x7">VMs will auto-stop after VMs running for <b>{{scenario.allowedOnScheduleRunningDays}} days</b> (in accumulation) following the schedule. This time is reset at every <b>manual</b> stop.</li>
<li *ngIf="!scenario.isDivvyEnabled || schedule.is24x7">VMs will auto-stop after VMs running for <b>{{scenario.allowed24x7RunningDays}} days</b>. You can extend this running period later.</li>
<li>Provision will auto-destroy after <b *ngIf="!sendData.isExternalAccess">{{scenario.allowedInnactiveDays}} days</b><b *ngIf="sendData.isExternalAccess">{{scenario.allowedInnactiveDays/2}} days</b> of innactivity (VMs stopped
for {{scenario.allowedInnactiveDays}} days).</li>
</ul>
</div>
</div>
<div class="modal-footer d-flex justify-content-center">
<button mdbBtn color="dark-green" size="sm" outline="true" class="waves-effect" mdbWavesEffect (click)="modalRef.hide()">Cancel</button>
<button mdbBtn color="dark-green" size="sm" outline="true" class="waves-effect" mdbWavesEffect
(click)="modalRef.hide()">Cancel</button>
<button mdbBtn color="dark-green" class="waves-light" size="sm" mdbWavesEffect (click)="confirm();">Create</button>
</div>
</div>

View File

@@ -13,7 +13,7 @@ label {
}
.utc {
padding-left: 20px;
padding-left: 10px;
padding-top: 5px;
font-size: 12px
}

Some files were not shown because too many files have changed in this diff Show More