Compare commits
310 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6283dddb22 | ||
|
|
c3b8bd119f | ||
|
|
2e31fe279c | ||
|
|
899fd2fda7 | ||
|
|
dc116c9f7f | ||
|
|
455cc3e75d | ||
|
|
16f574f744 | ||
|
|
1486e2fd21 | ||
|
|
c0349f440e | ||
|
|
1a99589c8b | ||
|
|
b54a590f51 | ||
|
|
eafd78f91c | ||
|
|
243c65c64b | ||
|
|
672ba4194a | ||
|
|
9f539bd96a | ||
|
|
55b9f0c032 | ||
|
|
00dcb33872 | ||
|
|
429838cfc5 | ||
|
|
3a2d6eb0b6 | ||
|
|
4290406c19 | ||
|
|
9057f58342 | ||
|
|
6d03a4f6ed | ||
|
|
5d914c890f | ||
|
|
b1286a1b2b | ||
|
|
a5b5f2a8ca | ||
|
|
8bb7856102 | ||
|
|
8280b32872 | ||
|
|
39ab3fe6dd | ||
|
|
fbe30b740c | ||
|
|
ab5ab80765 | ||
|
|
180a20c8d6 | ||
|
|
91d7017b03 | ||
|
|
5b6cb73b09 | ||
|
|
2003a039f2 | ||
|
|
96b1302520 | ||
|
|
891acb714e | ||
|
|
7cf4e7b5e7 | ||
|
|
0c78992958 | ||
|
|
ed44eb7fef | ||
|
|
8ed6612686 | ||
|
|
793b8f8739 | ||
|
|
a9b3b57414 | ||
|
|
69b92a64bc | ||
|
|
f84ecd7212 | ||
|
|
5b6bb90119 | ||
|
|
d5637065c2 | ||
|
|
6d322c3cc2 | ||
|
|
e0ba340ca1 | ||
|
|
79ec7af773 | ||
|
|
752a3329be | ||
|
|
36a246504f | ||
|
|
b2a4b41023 | ||
|
|
852c943f99 | ||
|
|
96e33ca765 | ||
|
|
bae80ec847 | ||
|
|
ce55788a26 | ||
|
|
17e128099e | ||
|
|
17f391ffbd | ||
|
|
23bf5f58f6 | ||
|
|
b36616f28a | ||
|
|
5e266a4461 | ||
|
|
0dd199d5f8 | ||
|
|
0f020e96a7 | ||
|
|
0dcba31636 | ||
|
|
04221eb03a | ||
|
|
c12039d354 | ||
|
|
c5dcc547a9 | ||
|
|
5c3e10f9df | ||
|
|
e1848da829 | ||
|
|
a4ae065d8b | ||
|
|
8e0ffd72fa | ||
|
|
fafe697af5 | ||
|
|
2cf319175d | ||
|
|
643cb9775d | ||
|
|
66085e49c5 | ||
|
|
fd85d65aeb | ||
|
|
24a3573f73 | ||
|
|
048fc6d89d | ||
|
|
bf20f4efd4 | ||
|
|
2fd05a2b54 | ||
|
|
03ce50c255 | ||
|
|
171f552571 | ||
|
|
1843907b42 | ||
|
|
d88a6f2359 | ||
|
|
d273bc937e | ||
|
|
6777d2f0d8 | ||
|
|
3d2506639d | ||
|
|
a9806bd94e | ||
|
|
783642f083 | ||
|
|
2a7fec03d7 | ||
|
|
aed18cccfa | ||
|
|
0f12ee6649 | ||
|
|
fbc727a66e | ||
|
|
897fa2a631 | ||
|
|
43a428592e | ||
|
|
c3796b478f | ||
|
|
e12f60516b | ||
|
|
84fe322c96 | ||
|
|
84263b08dd | ||
|
|
895e069326 | ||
|
|
da4c940055 | ||
|
|
f9e608c06c | ||
|
|
15e8c12508 | ||
|
|
1e543b1e6f | ||
|
|
00739082f2 | ||
|
|
aacf23f57b | ||
|
|
ca037b42ef | ||
|
|
96300e1e99 | ||
|
|
721fbe24b8 | ||
|
|
b20a6fe858 | ||
|
|
658bd6a131 | ||
|
|
be5c9ef85e | ||
|
|
5cc6f7a3c6 | ||
|
|
5fdd26306a | ||
|
|
0e74d8873f | ||
|
|
3f8e9290de | ||
|
|
034dde5e1d | ||
|
|
d0e274cd5f | ||
|
|
b3de18bbda | ||
|
|
e02ac30cde | ||
|
|
49b444cfc8 | ||
|
|
1ab9055fc5 | ||
|
|
fd8e542467 | ||
|
|
7699978f39 | ||
|
|
902db15c17 | ||
|
|
49bd59df62 | ||
|
|
b7e44f436c | ||
|
|
aa26928716 | ||
|
|
8509a520cf | ||
|
|
0037634b70 | ||
|
|
6dfab50871 | ||
|
|
fcc46cdb1a | ||
|
|
06c4fae9c2 | ||
|
|
8b18a65303 | ||
|
|
e4ad1dde54 | ||
|
|
a2b82501aa | ||
|
|
ad0c4cbbd1 | ||
|
|
1a638c16bf | ||
|
|
7dd9fc79c2 | ||
|
|
d492c5e4a8 | ||
|
|
c2a14e5f79 | ||
|
|
c888c068b4 | ||
|
|
7b0bacc990 | ||
|
|
4d66c40c07 | ||
|
|
8a86130fe6 | ||
|
|
b7db4a5f00 | ||
|
|
f3b9869d4f | ||
|
|
0801f84b10 | ||
|
|
9dd091f982 | ||
|
|
190967a889 | ||
|
|
510254922b | ||
|
|
91da6199b3 | ||
|
|
9374a60e2a | ||
|
|
0e1817947e | ||
|
|
870ad47cbc | ||
|
|
4304c56685 | ||
|
|
b7dfee506f | ||
|
|
022b414766 | ||
|
|
05143c78c0 | ||
|
|
04ed3a22ef | ||
|
|
d3f0513224 | ||
|
|
d5efa608bc | ||
|
|
040d2187cb | ||
|
|
2d254ae238 | ||
|
|
82861f2130 | ||
|
|
e0270e6925 | ||
|
|
dc2180af03 | ||
|
|
d240b06c5c | ||
|
|
2434d2d157 | ||
|
|
c631fb141e | ||
|
|
4e27a35e4e | ||
|
|
1b458ab2df | ||
|
|
082e98c01a | ||
|
|
c6d1f84c57 | ||
|
|
c3b9e8532f | ||
|
|
4194973884 | ||
|
|
57a47d7794 | ||
|
|
953930bc42 | ||
|
|
35d78b4db0 | ||
|
|
b8a8b8bace | ||
|
|
e09d03f1ef | ||
|
|
dd2f328f45 | ||
|
|
b3895be44a | ||
|
|
7516f46903 | ||
|
|
3c7327ed57 | ||
|
|
8e1c027038 | ||
|
|
f8fe33039c | ||
|
|
bf6ea3cd51 | ||
|
|
e3aeeea51f | ||
|
|
1fba40289e | ||
|
|
b32c18a349 | ||
|
|
9fc6bdd5a3 | ||
|
|
755fd11f1a | ||
|
|
82984673aa | ||
|
|
de600d1525 | ||
|
|
c1335fdf25 | ||
|
|
36eced4232 | ||
|
|
3f11e36b42 | ||
|
|
db2a3a4d60 | ||
|
|
d7222c6cf0 | ||
|
|
fca71efb1e | ||
|
|
6943d7eaa4 | ||
|
|
4b343830c9 | ||
|
|
f50d81651b | ||
|
|
7ac86a435a | ||
|
|
e2dd677c7f | ||
|
|
1a8a59fd77 | ||
|
|
80c085ca38 | ||
|
|
6f079a6361 | ||
|
|
b2cf816e2f | ||
|
|
6c176726f2 | ||
|
|
47c6a51c39 | ||
|
|
04f509c76b | ||
|
|
261a3518ee | ||
|
|
eb277aa5e8 | ||
|
|
c4b6ef56e6 | ||
|
|
886d8fa202 | ||
|
|
60ce0dcb3e | ||
|
|
35748fe333 | ||
|
|
aa826eb5f3 | ||
|
|
e25619b601 | ||
|
|
12b2e612aa | ||
|
|
b6f095d504 | ||
|
|
c5a9cbde58 | ||
|
|
dc8cae1853 | ||
|
|
e22168f39c | ||
|
|
3319c400b5 | ||
|
|
6725d91663 | ||
|
|
7a78cb6b21 | ||
|
|
0dc76aaf94 | ||
|
|
58daa3a31e | ||
|
|
beb2330bcd | ||
|
|
f38aef13be | ||
|
|
7920909308 | ||
|
|
d9a2df002c | ||
|
|
5ba87ef2c5 | ||
|
|
b83b599553 | ||
|
|
646d49fa2e | ||
|
|
b1535d74a1 | ||
|
|
8606e31bc5 | ||
|
|
4b87a7584a | ||
|
|
5234295a87 | ||
|
|
aba7919400 | ||
|
|
3fdd58db33 | ||
|
|
6651510249 | ||
|
|
326481521a | ||
|
|
b34895a8ae | ||
|
|
3663462bb4 | ||
|
|
815df437c7 | ||
|
|
cfa47cd47d | ||
|
|
fbfd0b5f4c | ||
|
|
ea9a002e01 | ||
|
|
6f8f7b12ab | ||
|
|
bec0ebc623 | ||
|
|
5f863d6ec4 | ||
|
|
d3345aa685 | ||
|
|
aa48199e58 | ||
|
|
028d9822bb | ||
|
|
08deb29ae8 | ||
|
|
1a7537ef27 | ||
|
|
e4ef581049 | ||
|
|
c642c5a5e9 | ||
|
|
0821049fc1 | ||
|
|
996be45987 | ||
|
|
44f654c085 | ||
|
|
98fe966f32 | ||
|
|
159c9a80c1 | ||
|
|
f407ed5899 | ||
|
|
77ed627312 | ||
|
|
57030be78f | ||
|
|
c8953e46d5 | ||
|
|
e18fbd7e6e | ||
|
|
551ad78345 | ||
|
|
dbabd39967 | ||
|
|
5cf2f37e7c | ||
|
|
fb82a18a5a | ||
|
|
930a9dab4a | ||
|
|
382b9b1576 | ||
|
|
524b16fd75 | ||
|
|
07f29da574 | ||
|
|
ecb6ad4d86 | ||
|
|
0c79d43ba1 | ||
|
|
52f8d098b3 | ||
|
|
e4230d58fa | ||
|
|
8df6915b38 | ||
|
|
2616332b4a | ||
|
|
8218f0469b | ||
|
|
8bfd76981f | ||
|
|
2205a9520d | ||
|
|
3f73c93eb2 | ||
|
|
5dfd363ecd | ||
|
|
c69125c5f2 | ||
|
|
50d68d0078 | ||
|
|
73f37b7567 | ||
|
|
d7b2ae93bb | ||
|
|
1ab433daec | ||
|
|
4fa0f78b42 | ||
|
|
fdea228ed1 | ||
|
|
707f0c6830 | ||
|
|
f71ef166b5 | ||
|
|
89462fd21a | ||
|
|
5f99f553c5 | ||
|
|
dddc1314e7 | ||
|
|
c5f50cb6b1 | ||
|
|
885f2986ef | ||
|
|
e45445f919 | ||
|
|
2040c9f914 | ||
|
|
93884ebeea | ||
|
|
dc9354a36a | ||
|
|
5e1e193688 |
2
.gitignore
vendored
@@ -48,4 +48,4 @@ secrets.json
|
||||
|
||||
qmi-cloud-tf-modules/
|
||||
*.pfx
|
||||
|
||||
/photos/*
|
||||
|
||||
@@ -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,12 +12,13 @@ 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
|
||||
COPY ./server ./server
|
||||
COPY ./dist ./dist
|
||||
COPY ./mystatsmashup ./mystatsmashup
|
||||
|
||||
EXPOSE 3000
|
||||
EXPOSE 3100
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# QMI Cloud
|
||||
|
||||
[](https://gitlab.com/qmi/qmi-cloud/-/commits/master)
|
||||
|
||||
## Pre-requisites
|
||||
- Docker
|
||||
- Docker-Compose
|
||||
|
||||
@@ -38,12 +38,15 @@
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": [
|
||||
|
||||
"node_modules/chart.js/dist/Chart.js",
|
||||
"node_modules/hammerjs/hammer.min.js",
|
||||
"node_modules/marked/lib/marked.js",
|
||||
"node_modules/prismjs/prism.js",
|
||||
"node_modules/prismjs/plugins/line-highlight/prism-line-highlight.js",
|
||||
"node_modules/prismjs/plugins/line-numbers/prism-line-numbers.js"
|
||||
"node_modules/prismjs/plugins/line-numbers/prism-line-numbers.js",
|
||||
"src/assets/js/qdt-components.js",
|
||||
"src/assets/js/qlikMashupQCS.js"
|
||||
]
|
||||
},
|
||||
"configurations": {
|
||||
|
||||
0
costexport/.keep
Normal file
1
costexport/sample.json
Normal file
@@ -0,0 +1 @@
|
||||
{"test": "ok"}
|
||||
BIN
dist/qmi-cloud/3rdpartylicenses.txt
vendored
135423
dist/qmi-cloud/assets/js/qdt-components.js
vendored
Normal file
52
dist/qmi-cloud/assets/js/qlikMashupQCS.js
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
var qlikMashup = (function() {
|
||||
|
||||
const { qdtCapabilityApp, QdtViz } = QdtComponents;
|
||||
|
||||
const initMyQdt = async function(config, returnto) {
|
||||
|
||||
const urlLoggedIn = "/api/v1/users/me";//Use GET request to see if you are authenticated
|
||||
const urlLogin = "/login";
|
||||
|
||||
const response = await fetch(`https://${config.host}${urlLoggedIn}`, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Qlik-Web-Integration-ID': config.webIntegrationId
|
||||
}
|
||||
})
|
||||
|
||||
if( response.status===401 ) {
|
||||
const url = new URL(`https://${config.host}${urlLogin}`);
|
||||
url.searchParams.append('returnto', returnto);
|
||||
url.searchParams.append('qlik-web-integration-id', config.webIntegrationId);
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
const user = await response.json();
|
||||
const capabilityApiAppPromise = qdtCapabilityApp(config);
|
||||
const app = await capabilityApiAppPromise;
|
||||
|
||||
return {app: app, user: user};
|
||||
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
"initMyQdt": initMyQdt,
|
||||
|
||||
"myQdtViz": function(app, divId, qlikObjectId, height, type){
|
||||
|
||||
QdtViz({
|
||||
element: document.getElementById(divId),
|
||||
app,
|
||||
options: {
|
||||
type: type,
|
||||
id: qlikObjectId,
|
||||
height: height
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
})(qlikMashup||{})
|
||||
|
||||
BIN
dist/qmi-cloud/assets/user1.png
vendored
Normal file
|
After Width: | Height: | Size: 30 KiB |
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!--
|
||||
Font Awesome Free 5.12.1 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 20190801 at Tue Feb 4 18:05:39 2020
|
||||
Created by FontForge 20190801 at Mon Mar 23 10:45:51 2020
|
||||
By Robert Madole
|
||||
Copyright (c) Font Awesome
|
||||
</metadata>
|
||||
|
Before Width: | Height: | Size: 699 KiB After Width: | Height: | Size: 699 KiB |
BIN
dist/qmi-cloud/fa-brands-400.a06da7f0950f9dd366fc.woff2
vendored
Normal file
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!--
|
||||
Font Awesome Free 5.12.1 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 20190801 at Tue Feb 4 18:05:39 2020
|
||||
Created by FontForge 20190801 at Mon Mar 23 10:45:51 2020
|
||||
By Robert Madole
|
||||
Copyright (c) Font Awesome
|
||||
</metadata>
|
||||
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
BIN
dist/qmi-cloud/fa-regular-400.c20b5b7362d8d7bb7edd.woff2
vendored
Normal file
BIN
dist/qmi-cloud/fa-solid-900.b15db15f746f29ffa026.woff2
vendored
Normal file
BIN
dist/qmi-cloud/fa-solid-900.bea989e82b07e9687c26.woff
vendored
Normal file
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!--
|
||||
Font Awesome Free 5.12.1 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 20190801 at Tue Feb 4 18:05:39 2020
|
||||
Created by FontForge 20190801 at Mon Mar 23 10:45:51 2020
|
||||
By Robert Madole
|
||||
Copyright (c) Font Awesome
|
||||
</metadata>
|
||||
@@ -23,7 +23,7 @@ Copyright (c) Font Awesome
|
||||
bbox="-0.983398 -64.9834 640.104 448.427"
|
||||
underline-thickness="25"
|
||||
underline-position="-50"
|
||||
unicode-range="U+0020-F941"
|
||||
unicode-range="U+0020-F976"
|
||||
/>
|
||||
<missing-glyph />
|
||||
<glyph glyph-name="glass-martini" unicode=""
|
||||
@@ -2377,6 +2377,10 @@ c0 1.77051 0.74707 4.43359 1.66895 5.94531c0.510742 0.827148 1.51855 2.01953 2.2
|
||||
c-0.112305 3.6416 -3.08203 7.27051 -6.62988 8.10059zM565.27 119.9c5.92383 -5.26953 10.7432 -15.9814 10.7432 -23.9102c0 -8.49121 -5.38184 -19.6865 -12.0127 -24.9902l-151.23 -121c-9.67188 -7.72754 -27.5693 -14 -39.9492 -14h-0.0507812h-356.77
|
||||
c-8.83203 0 -16 7.16797 -16 16v96c0 8.83203 7.16797 16 16 16h55.4004l46.5 37.71c17.8789 14.5059 51.0762 26.2842 74.0996 26.29h160v0c17.6309 0 31.9668 -14.3096 32 -31.9404v-0.120117c0 -1.48438 -0.206055 -3.87695 -0.459961 -5.33984
|
||||
c-2.54004 -15.6992 -17.3496 -26.5996 -33.25 -26.5996h-78.29c-8.83203 0 -16 -7.16797 -16 -16s7.16797 -16 16 -16h118.27h0.176758c12.3496 0 30.1904 6.27148 39.8232 14l92.4004 73.9004c12.4004 10 30.7998 10.6992 42.5996 0z" />
|
||||
<glyph glyph-name="hand-holding-water" unicode="" horiz-adv-x="576"
|
||||
d="M288 192c-53 0 -96 42.0996 -96 94c0 40 57.0996 120.7 83.2002 155.6c6.39941 8.5 19.2002 8.5 25.5996 0c26.1006 -34.8994 83.2002 -115.6 83.2002 -155.6c0 -51.9004 -43 -94 -96 -94zM565.3 119.9c15.1006 -13.6006 13.9004 -36.8008 -1.2998 -48.9004l-151.2 -121
|
||||
c-11.3994 -9.09961 -25.5 -14 -40 -14h-356.8c-8.7998 0 -16 7.2002 -16 16v96c0 8.7998 7.2002 16 16 16h55.4004l46.5 37.7002c21 17 47.0996 26.2998 74.0996 26.2998h160c19.5 0 34.9004 -17.4004 31.5996 -37.4004
|
||||
c-2.59961 -15.6992 -17.3994 -26.5996 -33.2998 -26.5996h-78.2998c-8.7998 0 -16 -7.2002 -16 -16s7.2002 -16 16 -16h118.3c14.6006 0 28.7002 4.90039 40 14l92.4004 73.9004c12.3994 10 30.7998 10.6992 42.5996 0z" />
|
||||
<glyph glyph-name="hands" unicode="" horiz-adv-x="640"
|
||||
d="M204.8 217.6l57.6006 -76.7998c16.5996 -22.2002 25.5996 -49.0996 25.5996 -76.7998v-112c0 -8.7998 -7.2002 -16 -16 -16h-131.7c-7.2002 0 -13.5 4.7002 -15.2998 11.5996c-2 7.80078 -5.40039 15.2002 -10.4004 21.7002l-104.1 134.3
|
||||
c-6.7998 8.5 -10.5 19.1006 -10.5 30v218.4c0 17.7002 14.2998 32 32 32s32 -14.2998 32 -32v-148.4l89.7998 -107.8c6 -7.2998 16.9004 -7.7998 23.6006 -1.09961l12.7998 12.7998c5.59961 5.59961 6.2998 14.5 1.5 20.9004l-38.1006 50.7998
|
||||
@@ -3502,6 +3506,13 @@ d="M12.4102 299.98c-16.5498 7.50977 -16.5498 32.5293 0 40.0391l232.95 105.671c2.
|
||||
c-6.7998 -3.08984 -14.4893 -3.08984 -21.29 0zM499.59 211.7c16.5498 -7.5 16.5498 -32.5 0 -40l-232.95 -105.59c-6.7998 -3.08008 -14.4893 -3.08008 -21.29 0l-232.939 105.59c-16.5498 7.5 -16.5498 32.5 0 40l58.0996 26.3301l161.63 -73.2705
|
||||
c7.57031 -3.42969 15.5908 -5.16992 23.8604 -5.16992s16.2998 1.74023 23.8604 5.16992l161.64 73.2705zM499.59 83.9004c16.5498 -7.5 16.5498 -32.5 0 -40l-232.95 -105.591c-6.7998 -3.0791 -14.4893 -3.0791 -21.29 0l-232.939 105.591
|
||||
c-16.5498 7.5 -16.5498 32.5 0 40l57.8799 26.2295l161.85 -73.3701c7.57031 -3.42969 15.5908 -5.16992 23.8604 -5.16992s16.2998 1.74023 23.8604 5.16992l161.859 73.3701z" />
|
||||
<glyph glyph-name="lungs" unicode="" horiz-adv-x="640"
|
||||
d="M636.11 57.8496c2.58984 -9.68945 3.88965 -19.6396 3.88965 -29.6299c0 -61.2295 -62.4805 -105.439 -125.24 -88.6201l-59.5 15.9502c-42.1797 11.3105 -71.2598 47.4697 -71.2598 88.6201v87.4902l85.8398 -57.2305
|
||||
c1.1123 -0.741211 3.09961 -1.34375 4.43652 -1.34375c2.36328 0 5.34375 1.59668 6.65332 3.56445l8.87988 13.3096c0.742188 1.1123 1.34375 3.09961 1.34375 4.43555c0 2.36328 -1.5957 5.34473 -3.56348 6.6543l-167.59 111.72l-167.59 -111.72
|
||||
c-1.96777 -1.30957 -3.56445 -4.29004 -3.56445 -6.65332c0 -1.33691 0.602539 -3.32422 1.34473 -4.43652l8.87988 -13.3096c1.30859 -1.96777 4.29004 -3.56445 6.65332 -3.56445c1.33691 0 3.32422 0.601562 4.43652 1.34375l85.8398 57.2305v-87.4902
|
||||
c0 -41.1504 -29.0801 -77.3203 -71.2598 -88.6201l-59.5 -15.9502c-62.7598 -16.8193 -125.24 27.3906 -125.24 88.6201c0 9.99023 1.2998 19.9404 3.88965 29.6299c21.6699 81.3008 56.04 159.15 102.011 231.021c22.1191 34.5703 36.0693 63.1299 80.0498 63.1299
|
||||
c38.6895 0 70.0498 -29.4199 70.0498 -65.71v-60.1104l32.8799 21.9199c4.4502 2.9707 7.12012 7.95996 7.12012 13.3105v170.59c0 8.83984 7.16016 16 16 16h16c8.83984 0 16 -7.16016 16 -16v-170.59v-0.00292969c0 -4.72363 3.18945 -10.6855 7.12012 -13.3076
|
||||
l32.8799 -21.9199v60.1104c0 36.29 31.3604 65.71 70.0498 65.71c43.9805 0 57.9307 -28.5596 80.0498 -63.1299c45.9707 -71.8701 80.3408 -149.72 102.011 -231.021z" />
|
||||
<glyph glyph-name="microscope" unicode=""
|
||||
d="M160 128c-17.6699 0 -32 14.3301 -32 32v224c0 17.6699 14.3301 32 32 32v16c0 8.83984 7.16016 16 16 16h64c8.83984 0 16 -7.16016 16 -16v-16c17.6699 0 32 -14.3301 32 -32v-224c0 -17.6699 -14.3301 -32 -32 -32h-12v-16c0 -8.83984 -7.16016 -16 -16 -16h-40
|
||||
c-8.83984 0 -16 7.16016 -16 16v16h-12zM464 0c26.5098 0 48 -21.4902 48 -48c0 -8.83984 -7.16016 -16 -16 -16h-480c-8.83984 0 -16 7.16016 -16 16c0 26.5098 21.4902 48 48 48h272c70.5801 0 128 57.4199 128 128s-57.4199 128 -128 128v64
|
||||
@@ -4503,6 +4514,11 @@ c-2.58789 2.58691 -4.6875 7.65625 -4.6875 11.3154s2.09961 8.72852 4.6875 11.3154
|
||||
c-10.7441 -10.748 -31.4814 -22.2393 -46.29 -25.6494l-120.25 -27.75l-102 -102c-2.58691 -2.58789 -7.65625 -4.6875 -11.3154 -4.6875s-8.72754 2.09961 -11.3154 4.6875l-22.6191 22.6191c-2.58789 2.58789 -4.6875 7.65625 -4.6875 11.3154
|
||||
s2.09961 8.72852 4.6875 11.3154l102 102l27.7393 120.26c3.4248 14.8057 14.9248 35.5439 25.6699 46.29l109.671 109.67l45.25 -45.25l-55.1006 -55.1006zM273.2 141.31l9.30957 9.31055l-67.8896 67.8896l-9.31055 -9.30957
|
||||
c-3.57715 -3.59082 -7.41211 -10.5127 -8.55957 -15.4502l-18.2998 -79.2998l79.2998 18.3193c4.94043 1.13379 11.8623 4.95996 15.4502 8.54004z" />
|
||||
<glyph glyph-name="disease" unicode=""
|
||||
d="M472.29 252.1c48.54 -16.6191 53.8301 -73.8301 8.99023 -96.79l-62 -31.7393c-17.8301 -9.12988 -29.2803 -25.2002 -30.6299 -43l-4.7002 -61.8604c-3.4502 -44.79 -65.1299 -66.7803 -104.45 -37.2197l-54.3203 40.8301
|
||||
c-15.6201 11.7295 -36.96 16.1201 -57.0693 11.7295l-70 -15.2803c-50.6504 -11.0596 -94.1104 32.5605 -73.46 73.8008l28.4297 57c8.17969 16.3799 6.43945 35.1699 -4.63965 50.2393l-38.54 52.4209c-27.9307 37.9492 7 86.9092 59 82.8398l71.8994 -5.62012
|
||||
c20.6602 -1.62012 40.9404 5.59961 54.2002 19.3096l46.0898 47.7207c33.4297 34.5098 98.4199 21.1494 110 -22.6201l16 -60.4502c4.60059 -17.3906 18.8604 -31.71 38.1406 -38.3105zM160 192c17.6641 0 32 14.3359 32 32s-14.3359 32 -32 32s-32 -14.3359 -32 -32
|
||||
s14.3359 -32 32 -32zM288 96c17.6641 0 32 14.3359 32 32s-14.3359 32 -32 32s-32 -14.3359 -32 -32s14.3359 -32 32 -32zM304 224c8.83203 0 16 7.16797 16 16s-7.16797 16 -16 16s-16 -7.16797 -16 -16s7.16797 -16 16 -16z" />
|
||||
<glyph glyph-name="egg" unicode="" horiz-adv-x="384"
|
||||
d="M192 448c106 0 192 -214 192 -320s-86 -192 -192 -192s-192 86 -192 192s86 320 192 320z" />
|
||||
<glyph glyph-name="hamburger" unicode=""
|
||||
@@ -4518,6 +4534,15 @@ l38.3994 -6.40039c13.46 -2.25 23.1504 -12.0996 23.1504 -23.54v-49.5898l35.6504 -
|
||||
<glyph glyph-name="hard-hat" unicode=""
|
||||
d="M480 160v-64h-448v64c0 80.25 49.2803 148.92 119.19 177.62l40.8096 -81.6201v112c0 8.83203 7.16797 16 16 16h96c8.83203 0 16 -7.16797 16 -16v-112l40.8096 81.6201c69.9102 -28.7002 119.19 -97.3701 119.19 -177.62zM496 64c8.83203 0 16 -7.16797 16 -16v-32
|
||||
c0 -8.83203 -7.16797 -16 -16 -16h-480c-8.83203 0 -16 7.16797 -16 16v32c0 8.83203 7.16797 16 16 16h480z" />
|
||||
<glyph glyph-name="hospital-user" unicode="" horiz-adv-x="640"
|
||||
d="M480 128c-52.9922 0 -96 43.0078 -96 96s43.0078 96 96 96s96 -43.0078 96 -96s-43.0078 -96 -96 -96zM528 96c61.8242 0 112.002 -50.1758 112.002 -112c0 -0.170898 -0.000976562 -0.449219 -0.00195312 -0.620117c-0.139648 -26.2598 -21.7305 -47.3799 -48 -47.3799
|
||||
h-224c-26.2695 0 -47.8604 21.1201 -48 47.3799c-0.000976562 0.170898 -0.00195312 0.449219 -0.00195312 0.620117c0 61.8242 50.1758 112 112 112h0.00195312h0.0810547c1.9707 0 5.09277 -0.488281 6.96875 -1.08984
|
||||
c10.9795 -3.81445 29.3223 -6.91016 40.9453 -6.91016s29.9658 3.0957 40.9453 6.91016c1.87891 0.601562 5.00488 1.08984 6.97754 1.08984h0.0820312zM329.91 85.5498c-23.1367 -23.1309 -41.915 -68.4561 -41.915 -101.172
|
||||
c0 -0.322266 0.00195312 -0.845703 0.00488281 -1.16797c0.136719 -14.5381 7.44336 -35.6885 16.3096 -47.21h-288.31c-8.83203 0 -16 7.16797 -16 16v368c0 17.6641 14.3359 32 32 32h32v64c0 17.6641 14.3359 32 32 32h160c17.6641 0 32 -14.3359 32 -32v-64h32
|
||||
c17.6641 0 32 -14.3359 32 -32v-216.62c-6.58008 -4.32227 -16.4766 -12.3096 -22.0898 -17.8301zM144 44v40c0 6.62402 -5.37598 12 -12 12h-40c-6.62402 0 -12 -5.37598 -12 -12v-40c0 -6.62402 5.37598 -12 12 -12h40c6.62402 0 12 5.37598 12 12zM144 172v40
|
||||
c0 6.62402 -5.37598 12 -12 12h-40c-6.62402 0 -12 -5.37598 -12 -12v-40c0 -6.62402 5.37598 -12 12 -12h40c6.62402 0 12 5.37598 12 12zM192 294v26h26c3.31152 0 6 2.68848 6 6v20c0 3.31152 -2.68848 6 -6 6h-26v26c0 3.31152 -2.68848 6 -6 6h-20
|
||||
c-3.31152 0 -6 -2.68848 -6 -6v-26h-26c-3.31152 0 -6 -2.68848 -6 -6v-20c0 -3.31152 2.68848 -6 6 -6h26v-26c0 -3.31152 2.68848 -6 6 -6h20c3.31152 0 6 2.68848 6 6zM272 44v40c0 6.62402 -5.37598 12 -12 12h-40c-6.62402 0 -12 -5.37598 -12 -12v-40
|
||||
c0 -6.62402 5.37598 -12 12 -12h40c6.62402 0 12 5.37598 12 12zM272 172v40c0 6.62402 -5.37598 12 -12 12h-40c-6.62402 0 -12 -5.37598 -12 -12v-40c0 -6.62402 5.37598 -12 12 -12h40c6.62402 0 12 5.37598 12 12z" />
|
||||
<glyph glyph-name="hotdog" unicode=""
|
||||
d="M488.56 424.56c12.9297 -12.9326 23.4238 -38.2715 23.4238 -56.5596s-10.4941 -43.627 -23.4238 -56.5596l-352 -352c-13.0205 -13.4824 -38.7998 -24.4238 -57.543 -24.4238c-44.1592 0 -80 35.8408 -80 80c0 18.7432 10.9414 44.5225 24.4238 57.543l352 352
|
||||
c12.9326 12.9297 38.2715 23.4238 56.5596 23.4238s43.627 -10.4941 56.5596 -23.4238zM438.63 329.37c2.58691 2.58691 4.68652 7.65625 4.68652 11.3145c0 8.83301 -7.16797 16.002 -16.001 16.002c-3.65918 0 -8.72852 -2.09961 -11.3154 -4.68652
|
||||
@@ -4690,11 +4715,224 @@ s-248 111 -248 248s111 248 248 248zM256 64c70.6562 0 128 57.3438 128 128s-57.343
|
||||
d="M416 240c8.83203 0 16 -7.16797 16 -16s-7.16797 -16 -16 -16s-16 7.16797 -16 16s7.16797 16 16 16zM624 128c8.83203 0 16 -7.16797 16 -16v-32c0 -8.83203 -7.16797 -16 -16 -16h-336c0 -52.9922 -43.0078 -96 -96 -96s-96 43.0078 -96 96h-32
|
||||
c-35.3281 0 -64 28.6719 -64 64v256c0 35.3281 28.6719 64 64 64h352c88.3203 0 160 -71.6797 160 -160v-160h48zM192 16c26.4688 0.0273438 47.9727 21.5312 48 48c0 26.4961 -21.5039 48 -48 48s-48 -21.5039 -48 -48s21.5039 -48 48 -48zM256 256v64
|
||||
c0 17.6641 -14.3359 32 -32 32h-128c-17.6641 0 -32 -14.3359 -32 -32v-64c0 -17.6641 14.3359 -32 32 -32h128c17.6641 0 32 14.3359 32 32zM448 128v192c0 17.6641 -14.3359 32 -32 32h-64c-17.6641 0 -32 -14.3359 -32 -32v-192h128z" />
|
||||
<glyph glyph-name="faucet" unicode="串"
|
||||
d="M352 192c88.3203 0 160 -71.6797 160 -160c0 -17.6641 -14.3359 -32 -32 -32h-64c-17.6641 0 -32 14.3359 -32 32s-14.3359 32 -32 32h-12.79c-20.5898 -37.7305 -64.21 -64 -115.21 -64s-94.6201 26.2695 -115.21 64h-92.79c-8.83203 0 -16 7.16797 -16 16v96
|
||||
c0 8.83203 7.16797 16 16 16h118.61c15.71 13.4004 35.46 23 57.3896 28v47.5596l32 3.38086l32 -3.38086v-47.5596c21.9297 -4.92969 41.6797 -14.5596 57.3896 -28h38.6104zM81.5898 288.09c-9.41992 -1 -17.5898 6.81055 -17.5898 16.7998v30.2207
|
||||
c0 9.98926 8.16992 17.7998 17.5898 16.8096l110.41 -11.6602v27.7402c0 8.83203 7.16797 16 16 16h32c8.83203 0 16 -7.16797 16 -16v-27.7402l110.41 11.6602c9.41992 0.990234 17.5898 -6.80957 17.5898 -16.8096v-30.2207
|
||||
c0 -9.98926 -8.16992 -17.7998 -17.5898 -16.7998l-142.41 15z" />
|
||||
<glyph glyph-name="trailer" unicode="論" horiz-adv-x="640"
|
||||
d="M624 128c8.83203 0 16 -7.16797 16 -16v-32c0 -8.83203 -7.16797 -16 -16 -16h-337.61c-7.83008 54.21 -54 96 -110.39 96s-102.56 -41.79 -110.39 -96h-49.6104c-8.83203 0 -16 7.16797 -16 16v288c0 8.83203 7.16797 16 16 16h512c8.83203 0 16 -7.16797 16 -16v-240
|
||||
h80zM96 204.32v107.68c0 4.41602 -3.58398 8 -8 8h-16c-4.41602 0 -8 -3.58398 -8 -8v-128.39c8.20996 6.67578 22.5469 15.9541 32 20.71zM192 222.86v89.1396c0 4.41602 -3.58398 8 -8 8h-16c-4.41602 0 -8 -3.58398 -8 -8v-89.1396
|
||||
c5.30957 0.489258 10.5703 1.13965 16 1.13965s10.6904 -0.650391 16 -1.13965zM288 183.61v128.39c0 4.41602 -3.58398 8 -8 8h-16c-4.41602 0 -8 -3.58398 -8 -8v-107.68c9.45312 -4.75586 23.79 -14.0342 32 -20.71zM384 128v184c0 4.41602 -3.58398 8 -8 8h-16
|
||||
c-4.41602 0 -8 -3.58398 -8 -8v-184h32zM480 128v184c0 4.41602 -3.58398 8 -8 8h-16c-4.41602 0 -8 -3.58398 -8 -8v-184h32zM176 128c44.1602 0 80 -35.8398 80 -80s-35.8398 -80 -80 -80s-80 35.8398 -80 80s35.8398 80 80 80zM176 16c17.6641 0 32 14.3359 32 32
|
||||
s-14.3359 32 -32 32s-32 -14.3359 -32 -32s14.3359 -32 32 -32z" />
|
||||
<glyph glyph-name="box-tissue" unicode="拏"
|
||||
d="M383.88 160.18h-256l-64 288h141.4c27.9277 -0.00195312 57.7646 -21.5059 66.5996 -48c8.83105 -26.4932 38.6641 -47.9971 66.5898 -48h109.41zM-0.120117 -31.8203v64h512v-64c0 -17.6641 -14.3359 -32 -32 -32h-448c-17.6641 0 -32 14.3359 -32 32zM479.88 224.18
|
||||
c17.6582 -0.00488281 31.9902 -14.3408 31.9902 -32v0v-128h-512v128c0 17.6641 14.3359 32 32 32h49l14.2197 -64h-15.21c-8.83203 0 -16 -7.16797 -16 -16s7.16797 -16 16 -16h352c8.83203 0 16 7.16797 16 16s-7.16797 16 -16 16h-14.2695l21.3301 64h40.9395z" />
|
||||
<glyph glyph-name="hand-holding-medical" unicode="樂" horiz-adv-x="576"
|
||||
d="M159.88 272.18c-8.83203 0 -16 7.16797 -16 16v64c0 8.83203 7.16797 16 16 16h64v64c0 8.83203 7.16797 16 16 16h64c8.83203 0 16 -7.16797 16 -16v-64h64c8.83203 0 16 -7.16797 16 -16v-64c0 -8.83203 -7.16797 -16 -16 -16h-64v-64c0 -8.83203 -7.16797 -16 -16 -16
|
||||
h-64c-8.83203 0 -16 7.16797 -16 16v64h-64zM568.07 111.87c4.28906 -5.83496 7.77051 -16.4492 7.77051 -23.6914c0 -11.1436 -7.27637 -25.5596 -16.2412 -32.1787l-135.029 -99.5703c-15.2061 -11.1436 -42.8477 -20.2246 -61.7002 -20.2695h-347
|
||||
c-8.77246 0.0595703 -15.9404 7.22754 -16 16v96c0.0595703 8.77246 7.22754 15.9404 16 16h55.3604l46.5 37.7402c17.8828 14.4893 51.0781 26.25 74.0957 26.25h0.0234375h160h0.00488281c17.6973 0 32.0596 -14.3633 32.0596 -32.0605
|
||||
c0 -1.47852 -0.198242 -3.86133 -0.444336 -5.32031c-2.62012 -15.7393 -17.3701 -26.6094 -33.3701 -26.6094h-78.2393c-8.83203 0 -16 -7.16797 -16 -16s7.16797 -16 16 -16h120.609l119.67 88.1797c5.8418 4.3252 16.4814 7.83496 23.749 7.83496
|
||||
c11.1621 0 25.5791 -7.30469 32.1816 -16.3047z" />
|
||||
<glyph glyph-name="hand-sparkles" unicode="諾" horiz-adv-x="640"
|
||||
d="M106.66 277.36l-20.7402 -49.6201c-1.01074 -2.04297 -3.68066 -3.7002 -5.95996 -3.7002s-4.94922 1.65723 -5.95996 3.7002l-20.6602 49.6602h-0.0703125l-49.5898 20.5996c-1.92383 1.09863 -3.57227 3.78711 -3.67969 6v0c0.106445 2.21973 1.76367 4.9082 3.7002 6
|
||||
l49.6299 20.6904h0.0498047l20.7002 49.6299c1.01465 2.03516 3.68555 3.6875 5.95996 3.6875s4.94434 -1.65234 5.95996 -3.6875l20.6602 -49.6406h0.0703125l49.5693 -20.6699c1.92871 -1.0957 3.57715 -3.78418 3.68066 -6v0
|
||||
c-0.108398 -2.21289 -1.75684 -4.90039 -3.68066 -6l-49.5498 -20.6494h-0.0898438zM471.38 -19.4102l37.4902 -15.6299l0.0703125 -0.169922c-7.59082 -17.0596 -24 -28.79 -43.2402 -28.79h-197.61c-13.4805 0.0224609 -30.8584 8.88867 -38.79 19.79l-125.6 172.61
|
||||
c-4.22852 5.81055 -7.66016 16.3584 -7.66016 23.5449c0 22.0879 17.9268 40.0146 40.0146 40.0146c11.2441 0 25.7393 -7.37891 32.3555 -16.4697l23.5898 -32.4902v241c0 17.6641 14.3359 32 32 32s32 -14.3359 32 -32v-152c0 -4.41602 3.58398 -8 8 -8h16
|
||||
c4.41602 0 8 3.58398 8 8v184c0 17.6641 14.3359 32 32 32s32 -14.3359 32 -32v-184c0 -4.41602 3.58398 -8 8 -8h16c4.41602 0 8 3.58398 8 8v152c0 17.6641 14.3359 32 32 32s32 -14.3359 32 -32v-152c0 -4.41602 3.58398 -8 8 -8h16c4.41602 0 8 3.58398 8 8v72
|
||||
c0 17.6641 14.3359 32 32 32s32 -14.3359 32 -32v-176.03c-0.0195312 -1.30957 -0.269531 -2.66992 -0.269531 -4c-7.77051 -3.70996 -14.5 -9.59961 -18.3506 -17.3398l-0.469727 -0.950195l-0.410156 -1l-15.6299 -37.4795l-37.4902 -15.6299l-1 -0.430664l-1 -0.489258
|
||||
c-11.7803 -5.90527 -21.3408 -21.3926 -21.3408 -34.5703s9.56055 -28.665 21.3408 -34.5703l1 -0.5zM349.79 108.48c1.22266 0.609375 2.21582 2.21289 2.21582 3.5791c0 1.36719 -0.993164 2.9707 -2.21582 3.58008l-29.79 12.4199l-12.4297 29.7803
|
||||
c-0.611328 1.21777 -2.21289 2.20605 -3.5752 2.20605s-2.96387 -0.988281 -3.5752 -2.20605l-12.4199 -29.7803l-29.79 -12.4199c-1.22266 -0.609375 -2.21582 -2.21289 -2.21582 -3.58008c0 -1.36621 0.993164 -2.96973 2.21582 -3.5791l29.79 -12.4102l12.4297 -29.7803
|
||||
c0.611328 -1.21777 2.21289 -2.20605 3.5752 -2.20605s2.96387 0.988281 3.5752 2.20605l12.4199 29.7803zM640 16.0898l-0.0703125 -0.0703125v0c-0.117188 -2.19727 -1.76562 -4.86328 -3.67969 -5.94922l-49.5498 -20.6602h-0.0898438v0l-20.6904 -49.6201
|
||||
c-1.01074 -2.04297 -3.68066 -3.7002 -5.95996 -3.7002s-4.94922 1.65723 -5.95996 3.7002l-20.6602 49.5898h-0.0703125l-49.5693 20.6699c-1.91406 1.08691 -3.5625 3.75293 -3.68066 5.9502v0c0.101562 2.2168 1.75 4.90527 3.68066 6l49.6299 20.7402h0.0498047
|
||||
l20.7002 49.6299c1.01465 2.03516 3.68555 3.6875 5.95996 3.6875s4.94434 -1.65234 5.95996 -3.6875l20.6797 -49.6104h0.0703125l49.5703 -20.6699c1.92969 -1.09473 3.57812 -3.7832 3.67969 -6z" />
|
||||
<glyph glyph-name="hands-wash" unicode="丹" horiz-adv-x="576"
|
||||
d="M496 224c-26.4961 0 -48 21.5039 -48 48s21.5039 48 48 48s48 -21.5039 48 -48s-21.5039 -48 -48 -48zM311.47 269.55l-16.0801 -4.96973l20.9004 66.1699c3.5 11.0703 14.1797 18.8604 25.71 17.5098c11.8564 -1.25195 21.4785 -11.9453 21.4785 -23.8672
|
||||
c0 -2.05566 -0.505859 -5.31348 -1.12891 -7.27246l-15.3496 -48.6104c-5.0752 1.88184 -13.5869 3.44531 -19 3.49023h-0.0322266c-4.6543 0 -12.0449 -1.09766 -16.498 -2.4502zM93.6504 61.6699c-33.4609 19.3945 -61.0801 66.5195 -61.6504 105.19v112.729
|
||||
c0.179688 13.3203 11.6699 23.9102 24.9004 23.8604c13.1709 -0.0771484 23.8604 -10.8281 23.8604 -24c0 -0.0410156 0 -0.108398 -0.000976562 -0.150391l2.06055 -50.0498l60 189.85c3.5 11.0703 14.1797 18.9004 25.71 17.46
|
||||
c11.8398 -1.26465 21.4492 -11.9561 21.4492 -23.8633c0 -2.04785 -0.500977 -5.29395 -1.11914 -7.24609l-38.5605 -122c-0.205078 -0.649414 -0.371094 -1.72949 -0.371094 -2.41016c0 -4.41699 3.58398 -8.00195 8.00098 -8.00195
|
||||
c3.2373 0 6.65527 2.50586 7.62988 5.5918l47.9307 151.71c3.50977 11.0605 14.1797 18.8506 25.71 17.5098c11.8398 -1.26465 21.4502 -11.9561 21.4502 -23.8633c0 -2.04785 -0.501953 -5.29395 -1.12012 -7.24609l-43.3701 -137.79
|
||||
c-0.206055 -0.650391 -0.373047 -1.73242 -0.373047 -2.41504c0 -4.41797 3.58496 -8.00293 8.00293 -8.00293c3.23535 0 6.65332 2.50293 7.62988 5.58789l33.4502 106.42c3.5 11.0703 14.1895 18.8604 25.7197 17.5195
|
||||
c11.8408 -1.26465 21.4502 -11.9561 21.4502 -23.8633c0 -2.04785 -0.501953 -5.29395 -1.12012 -7.24609l-34.1602 -108.12l-73.7002 -22.7598c-59.0469 -19.5098 -107.01 -85.8135 -107.06 -148v-25.6904c-0.80957 -0.169922 -1.5498 -0.519531 -2.34961 -0.709961z
|
||||
M519.1 112c11.6104 0 22.25 -7.83984 24.4404 -19.2402c0.262695 -1.30078 0.476562 -3.43262 0.476562 -4.75977c0 -13.248 -10.752 -24 -24 -24h-0.0166016h-160c-4.41602 0 -8 -3.58398 -8 -8s3.58398 -8 8 -8h127.1c11.6104 0 22.25 -7.83984 24.4404 -19.2402
|
||||
c0.262695 -1.30078 0.476562 -3.43262 0.476562 -4.75977c0 -13.248 -10.752 -24 -24 -24h-0.0166016h-128c-4.41602 0 -8 -3.58398 -8 -8s3.58398 -8 8 -8h95.0996c11.6104 0 22.25 -7.83984 24.4404 -19.2402c0.262695 -1.30078 0.476562 -3.43262 0.476562 -4.75977
|
||||
c0 -13.248 -10.752 -24 -24 -24h-0.0166016h-208c-18.4902 0.0703125 -46.2656 8.00879 -62 17.7197c3.32715 8.06641 6.02734 21.6953 6.02734 30.4209c0 36.0527 -28.6846 71.0908 -64.0273 78.209v25.6504v0.00683594c0 49.501 38.165 102.223 85.1904 117.684
|
||||
l107.72 33.25c1.91211 0.59082 5.08887 1.07031 7.08984 1.07031c13.2539 0 24.0107 -10.7568 24.0107 -24.0107c0 -9.77637 -7.58008 -20.0537 -16.9209 -22.9404l-47.0898 -17.0596h199.1c11.6104 0 22.25 -7.83984 24.4404 -19.2402
|
||||
c0.262695 -1.30078 0.476562 -3.43262 0.476562 -4.75977c0 -13.248 -10.752 -24 -24 -24h-0.0166016h-128c-4.41602 0 -8 -3.58398 -8 -8s3.58398 -8 8 -8h159.1zM416 384c-17.6641 0 -32 14.3359 -32 32s14.3359 32 32 32s32 -14.3359 32 -32s-14.3359 -32 -32 -32z
|
||||
M112 32c26.4961 0 48 -21.5039 48 -48s-21.5039 -48 -48 -48s-48 21.5039 -48 48s21.5039 48 48 48z" />
|
||||
<glyph glyph-name="handshake-alt-slash" unicode="寧" horiz-adv-x="640"
|
||||
d="M358.59 252.4l26.1104 23.8896c2.86914 2.62598 5.19824 7.91504 5.19824 11.8047c0 8.83398 -7.16992 16.0039 -16.0039 16.0039c-3.43164 0 -8.27246 -1.88086 -10.8047 -4.19824l-27 -24.7002l-32.6895 -29.9199l330.43 -255.38
|
||||
c3.41016 -2.65234 6.17773 -8.31055 6.17773 -12.6309c0 -3.0293 -1.50879 -7.42773 -3.36816 -9.81934l-19.6396 -25.2705c-2.65234 -3.41211 -8.31152 -6.18262 -12.6338 -6.18262c-3.03125 0 -7.43359 1.51172 -9.82617 3.37305l-588.35 454.72
|
||||
c-3.41016 2.65234 -6.17773 8.31055 -6.17773 12.6309c0 3.02832 1.50781 7.42773 3.36719 9.81934l19.6201 25.2695c2.65234 3.41602 8.31348 6.1875 12.6377 6.1875c3.03418 0 7.43848 -1.5127 9.83203 -3.37695l116.891 -90.3301l20.3398 20.2998
|
||||
c5.16211 5.17969 15.2871 9.39551 22.5996 9.41016h83.79l-75.5996 -69.2402l25.6895 -19.8496l88.1201 80.6797c5.0625 4.63965 14.7432 8.40723 21.6104 8.41016h85.8896c7.31641 -0.0126953 17.4453 -4.22852 22.6104 -9.41016l54.5898 -54.5898h112v0
|
||||
c8.78223 0 15.9502 -7.12793 16 -15.9102v-191.8c-0.0273438 -8.80469 -7.19531 -15.9727 -16 -16h-97.5898c-2.26465 12.7275 -12.2148 29.7109 -22.21 37.9102zM16 320h7.55957l382.44 -295.59l-8.7998 -10.8203c-6.15723 -7.57617 -19.0762 -13.7246 -28.8389 -13.7246
|
||||
c-7.29004 0 -17.7959 3.73438 -23.4512 8.33496l-17.9102 15.5l-0.200195 -0.200195c-10.6025 -13.0381 -32.8477 -23.6201 -49.6533 -23.6201c-12.5381 0 -30.6133 6.41602 -40.3467 14.3203l-90.5 81.8896h-130.3c-8.83203 0 -16 7.16797 -16 16v191.91
|
||||
c0.0273438 8.80469 7.19531 15.9727 16 16z" />
|
||||
<glyph glyph-name="handshake-slash" unicode="怒" horiz-adv-x="640"
|
||||
d="M0 319.79h23.8301l72.1699 -55.79v-168c0 -17.6641 -14.3359 -32 -32 -32h-64v255.79zM48 127.9c-8.83203 0 -16 -7.16797 -16 -16s7.16797 -16 16 -16s16 7.16797 16 16s-7.16797 16 -16 16zM128 96.0898v143.19l278 -214.87l-8.7998 -10.8203
|
||||
c-6.15723 -7.57617 -19.0762 -13.7246 -28.8389 -13.7246c-7.29004 0 -17.7959 3.73438 -23.4512 8.33496l-17.9102 15.5l-0.200195 -0.200195c-10.6025 -13.0381 -32.8477 -23.6201 -49.6533 -23.6201c-12.5381 0 -30.6133 6.41602 -40.3467 14.3203l-90.5 81.8896
|
||||
h-18.2998zM544 319.79h96v-255.89h-64c-17.6641 0 -32 14.3359 -32 32v223.89zM592 95.9004c8.83203 0 16 7.16797 16 16s-7.16797 16 -16 16s-16 -7.16797 -16 -16s7.16797 -16 16 -16zM303.33 245.33l330.5 -255.43c3.41309 -2.65234 6.18359 -8.3125 6.18359 -12.6357
|
||||
c0 -3.02734 -1.50684 -7.42383 -3.36328 -9.81445l-19.6504 -25.2705c-2.65234 -3.41504 -8.31348 -6.1875 -12.6377 -6.1875c-3.03418 0 -7.43848 1.51367 -9.83203 3.37793l-588.34 454.72c-3.41016 2.65234 -6.17773 8.31055 -6.17773 12.6309
|
||||
c0 3.02832 1.50781 7.42773 3.36719 9.81934l19.6201 25.2695c2.65234 3.41602 8.31348 6.1875 12.6377 6.1875c3.03418 0 7.43848 -1.5127 9.83203 -3.37695l116.891 -90.3398l20.3398 20.3096c5.16211 5.17969 15.2871 9.39551 22.5996 9.41016h83.79l-75.5996 -69.2402
|
||||
l25.6396 -19.8096l88.0703 80.6396c5.05566 4.64258 14.7305 8.41016 21.5947 8.41016h0.00488281h85.9004h0.0351562c7.31055 0 17.4199 -4.21582 22.5645 -9.41016l54.6104 -54.5898v-193.5c-2.02246 2.29102 -5.56641 5.74023 -7.91016 7.7002l-145.59 118.2
|
||||
l26.0898 23.8896c2.73828 2.61035 4.95996 7.79883 4.95996 11.5811c0 8.83203 -7.16797 16 -16 16c-3.32422 0 -8.05078 -1.7793 -10.5498 -3.9707z" />
|
||||
<glyph glyph-name="head-side-cough" unicode="率" horiz-adv-x="640"
|
||||
d="M616 144c-13.248 0 -24 10.752 -24 24s10.752 24 24 24s24 -10.752 24 -24s-10.752 -24 -24 -24zM552 32c13.248 0 24 -10.752 24 -24s-10.752 -24 -24 -24s-24 10.752 -24 24s10.752 24 24 24zM488 88c13.248 0 24 -10.752 24 -24s-10.752 -24 -24 -24
|
||||
s-24 10.752 -24 24s10.752 24 24 24zM616 -16c13.248 0 24 -10.752 24 -24s-10.752 -24 -24 -24s-24 10.752 -24 24s10.752 24 24 24zM616 88c13.248 0 24 -10.752 24 -24s-10.752 -24 -24 -24s-24 10.752 -24 24s10.752 24 24 24zM552 128c13.248 0 24 -10.752 24 -24
|
||||
s-10.752 -24 -24 -24s-24 10.752 -24 24s10.752 24 24 24zM477.22 173c1.52344 -3.42676 2.75977 -9.25 2.75977 -13c0 -17.6523 -14.3271 -31.9883 -31.9795 -32h-32v-32h-96c-17.6641 0 -32 -14.3359 -32 -32s14.3359 -32 32 -32h96c0 -35.3281 -28.6719 -64 -64 -64h-64
|
||||
v-32h-224v177.12c-39.25 35.2598 -64 86.1299 -64 142.88c0 106 86 192 192 192h42.0996c59.5439 -0.0390625 135.704 -39.5752 170 -88.25c24.6201 -35 52.1201 -139.63 73.1201 -186.75zM288 224c17.626 0.0380859 31.9619 14.374 32 32c0 17.6641 -14.3359 32 -32 32
|
||||
s-32 -14.3359 -32 -32s14.3359 -32 32 -32z" />
|
||||
<glyph glyph-name="head-side-cough-slash" unicode="異" horiz-adv-x="640"
|
||||
d="M454.11 128.79l179.72 -138.89c3.41016 -2.65234 6.17773 -8.31055 6.17773 -12.6309c0 -3.0293 -1.50879 -7.42773 -3.36816 -9.81934l-19.6396 -25.2705c-2.65234 -3.41504 -8.31348 -6.1875 -12.6377 -6.1875c-3.03418 0 -7.43848 1.51367 -9.83203 3.37793
|
||||
l-588.351 454.72c-3.41016 2.65234 -6.17773 8.31055 -6.17773 12.6309c0 3.02832 1.50879 7.42773 3.36816 9.81934l19.6299 25.2695c2.65234 3.41309 8.31152 6.18262 12.6338 6.18262c3.03125 0 7.43359 -1.51074 9.82617 -3.37207l38.7197 -29.9199
|
||||
c26.8672 18.3818 75.0928 33.2998 107.646 33.2998h0.173828h42.0996c59.5439 -0.0390625 135.704 -39.5752 170 -88.25c24.6201 -35 52.1201 -139.63 73.1201 -186.75c8.51074 -19.21 -3.5498 -40.4004 -23.1094 -44.21zM313.39 237.55
|
||||
c3.85059 5.28027 6.61035 11.4502 6.58008 18.4502c-0.0322266 17.6309 -14.3691 31.9668 -32 32c-9.92969 0 -18.4795 -4.86035 -24.3594 -12zM616 144c-13.248 0 -24 10.752 -24 24s10.752 24 24 24s24 -10.752 24 -24s-10.752 -24 -24 -24zM552 80
|
||||
c-13.248 0 -24 10.752 -24 24s10.752 24 24 24s24 -10.752 24 -24s-10.752 -24 -24 -24zM288 64c0 -17.6641 14.3359 -32 32 -32h96c0 -35.3281 -28.6719 -64 -64 -64h-64v-32h-224v177.12c-39.25 35.2598 -64 86.1299 -64 142.88
|
||||
c0.0126953 25.2188 9.2998 63.9307 20.7305 86.4102l318.81 -246.41h-19.54c-17.6641 0 -32 -14.3359 -32 -32zM616 88c13.248 0 24 -10.752 24 -24s-10.752 -24 -24 -24s-24 10.752 -24 24s10.752 24 24 24z" />
|
||||
<glyph glyph-name="head-side-mask" unicode="北"
|
||||
d="M0.150391 263.58c0.364258 7.85059 1.94043 20.4707 3.51953 28.1699l220.33 -160.26v-195.49h-160v177.12c-41 36.8203 -66.1699 90.6699 -63.8496 150.46zM509.22 173c1.40625 -3.27148 2.54688 -8.81543 2.54688 -12.376
|
||||
c0 -0.171875 -0.00292969 -0.452148 -0.00683594 -0.624023h-272.55l-225.96 164.35c29.2305 73.0801 103.75 123.65 186.75 123.65h66.1104c59.541 -0.0390625 135.697 -39.5752 169.989 -88.25c24.6201 -35 52.1201 -139.63 73.1201 -186.75zM320 224
|
||||
c17.626 0.0380859 31.9619 14.374 32 32c0 17.6641 -14.3359 32 -32 32s-32 -14.3359 -32 -32s14.3359 -32 32 -32zM336 80c-8.83203 0 -16 -7.16797 -16 -16s7.16797 -16 16 -16h149.34l-10.6699 -32h-138.67c-8.83203 0 -16 -7.16797 -16 -16s7.16797 -16 16 -16h128
|
||||
l-1.41016 -4.24023c-8.05176 -24.1533 -35.25 -43.7578 -60.71 -43.7598h-145.88v192h256l-16 -48h-160z" />
|
||||
<glyph glyph-name="head-side-virus" unicode="磻"
|
||||
d="M272 208c8.83203 0 16 -7.16797 16 -16s-7.16797 -16 -16 -16s-16 7.16797 -16 16s7.16797 16 16 16zM208 272c8.83203 0 16 -7.16797 16 -16s-7.16797 -16 -16 -16s-16 7.16797 -16 16s7.16797 16 16 16zM509.2 173c1.52344 -3.42676 2.75977 -9.25 2.75977 -13
|
||||
c0 -17.6416 -14.3184 -31.9775 -31.96 -32h-32v-64c0 -35.3281 -28.6719 -64 -64 -64h-64v-64h-256v177.19c-35.3281 31.4834 -64 95.4414 -64 142.763v0.046875c0 106 86 192 192 192h74.0898h0.0214844c59.5684 0 135.736 -39.5361 170.019 -88.25
|
||||
c24.6396 -35.0195 52.1396 -139.63 73.0703 -186.75zM368 208c8.83203 0 16 7.16797 16 16s-7.16797 16 -16 16h-12.1201c-28.5098 0 -42.79 34.4697 -22.6299 54.6299l8.58008 8.57031c2.58691 2.58691 4.68652 7.65625 4.68652 11.3145
|
||||
c0 8.83301 -7.16895 16.002 -16.002 16.002c-3.6582 0 -8.72754 -2.09961 -11.3145 -4.68652l-8.57031 -8.58008c-20.1602 -20.1602 -54.6299 -5.87988 -54.6299 22.6299v12.1201c0 8.83203 -7.16797 16 -16 16s-16 -7.16797 -16 -16v-12.1201
|
||||
c0 -28.5098 -34.4697 -42.79 -54.6299 -22.6299l-8.57031 8.58008c-2.58691 2.58691 -7.65625 4.68652 -11.3145 4.68652c-8.83301 0 -16.002 -7.16895 -16.002 -16.002c0 -3.6582 2.09961 -8.72754 4.68652 -11.3145l8.58008 -8.57031
|
||||
c20.1602 -20.1602 5.87988 -54.6299 -22.6299 -54.6299h-12.1201c-8.83203 0 -16 -7.16797 -16 -16s7.16797 -16 16 -16h12.1201c28.5098 0 42.79 -34.4697 22.6299 -54.6299l-8.58008 -8.57031c-2.58691 -2.58691 -4.68652 -7.65625 -4.68652 -11.3145
|
||||
c0 -8.83301 7.16895 -16.002 16.002 -16.002c3.6582 0 8.72754 2.09961 11.3145 4.68652l8.57031 8.58008c20.1602 20.1602 54.6299 5.87988 54.6299 -22.6299v-12.1201c0 -8.83203 7.16797 -16 16 -16s16 7.16797 16 16v12.1201c0 28.5098 34.4697 42.79 54.6299 22.6299
|
||||
l8.57031 -8.58008c2.58691 -2.58691 7.65625 -4.68652 11.3145 -4.68652c8.83301 0 16.002 7.16895 16.002 16.002c0 3.6582 -2.09961 8.72754 -4.68652 11.3145l-8.58008 8.57031c-20.1602 20.1602 -5.87988 54.6299 22.6299 54.6299h12.1201z" />
|
||||
<glyph glyph-name="house-user" unicode="便" horiz-adv-x="576"
|
||||
d="M570.69 211.73c2.54004 -2.81152 4.91895 -8.15137 5.30957 -11.9209c-0.319336 -3.25977 -2.15234 -8.04883 -4.08984 -10.6895l-21.4102 -23.8105c-2.7959 -2.53809 -8.11426 -4.91699 -11.8701 -5.30957c-3.2666 0.334961 -8.06934 2.18066 -10.7197 4.12012
|
||||
l-15.9102 14v-210.12c0 -17.6641 -14.3359 -32 -32 -32h-383.91c-17.6641 0 -32 14.3359 -32 32v210.11l-15.8994 -14c-2.63965 -1.94336 -7.42871 -3.78418 -10.6904 -4.11035c-3.78906 0.381836 -9.16504 2.75586 -12 5.2998l-21.4102 23.79
|
||||
c-2.08398 2.59082 -3.91602 7.38965 -4.08984 10.71c0.200195 3.83789 2.55664 9.16895 5.25977 11.9004l256 226c6.28027 5.68945 18.21 10.2998 26.7402 10.2998s20.5 -4.61035 26.7803 -10.2998l101.22 -89.3701v51.6699c0 8.83203 7.16797 16 16 16h64
|
||||
c8.83203 0 16 -7.16797 16 -16v-136.44zM288 272c-35.3281 0 -64 -28.6719 -64 -64s28.6719 -64 64 -64s64 28.6719 64 64s-28.6719 64 -64 64zM400 0c8.83203 0 16 7.16797 16 16c0 52.9922 -43.0078 96 -96 96h-64c-52.9922 0 -96 -43.0078 -96 -96
|
||||
c0 -8.83203 7.16797 -16 16 -16h224z" />
|
||||
<glyph glyph-name="laptop-house" unicode="復" horiz-adv-x="640"
|
||||
d="M272 160v-128h-176c-17.6641 0 -32 14.3359 -32 32v164.12l-21.6602 -19.1201c-2.27344 -1.77637 -6.45801 -3.33594 -9.33984 -3.48047c-3.45117 0.183594 -8.22754 2.3252 -10.6602 4.78027l-18.79 21.3105c-1.8125 2.27637 -3.40332 6.4834 -3.5498 9.38965
|
||||
c0.194336 3.42871 2.33594 8.16797 4.78027 10.5801l211.8 187.5c5.54004 4.91992 16.0703 8.91992 23.4697 8.91992c7.40039 0 17.9502 -4 23.4502 -8.91992l88.5 -78.3799v39.2998c0 8.83203 7.16797 16 16 16h32c8.83203 0 16 -7.16797 16 -16v-96l59.25 -52.3896
|
||||
c2.42773 -2.42871 4.55566 -7.18164 4.75 -10.6104c-0.15332 -2.93457 -1.77051 -7.17773 -3.61035 -9.46973l-6.64941 -7.53027h-136.94c-17.7998 0 -33.6895 -8.24023 -44.7998 -21.1201v37.1201c0 8.83203 -7.16797 16 -16 16h-64c-8.83203 0 -16 -7.16797 -16 -16v-64
|
||||
c0 -8.83203 7.16797 -16 16 -16h64zM629.33 0c5.88965 0 10.6699 -4.78027 10.6699 -10.6699v-10.6602c-0.0820312 -23.4336 -19.167 -42.5498 -42.5996 -42.6699h-298.801c-23.4326 0.120117 -42.5176 19.2363 -42.5996 42.6699v10.6602
|
||||
c0 5.88965 4.78027 10.6699 10.6699 10.6699v0h37.3301v160c0 17.6699 12.8896 32 28.7998 32h230.4c15.9102 0 28.7998 -14.3301 28.7998 -32v-160h37.3301zM544 0v144h-192v-144h192z" />
|
||||
<glyph glyph-name="lungs-virus" unicode="不" horiz-adv-x="640"
|
||||
d="M344 297.32c-6.11035 3.6875 -16.8623 6.68066 -24 6.68066s-17.8896 -2.99316 -24 -6.68066v134.68c0 8.83203 7.16797 16 16 16h16c8.83203 0 16 -7.16797 16 -16v-134.68zM195.54 3.54004c7.55664 -7.76367 22.4814 -14.0645 33.3154 -14.0645
|
||||
c2.33594 0 6.09668 0.342773 8.39453 0.764648c-11.2559 -14.4775 -34.7754 -30.0459 -52.5 -34.75l-59.5 -15.8701c-62.75 -16.8799 -125.25 27.3799 -125.25 88.6299v0.241211c0 8.25 1.73828 21.4121 3.87988 29.3789c18.2109 68.1455 63.9072 171.634 102 231
|
||||
c22.1201 34.6299 36.1201 63.1299 80.1201 63.1299c38.6201 0 70 -29.3799 70 -65.75v-27.6797c-6.68359 4.46582 -18.6309 8.08984 -26.6689 8.08984c-26.4961 0 -48 -21.5039 -48 -48c0 -11.043 6.36523 -26.3154 14.209 -34.0898l8.58008 -8.57031h-12.1201
|
||||
c-26.4961 0 -48 -21.5039 -48 -48s21.5039 -48 48 -48h12.1201l-8.58008 -8.58008c-7.74609 -7.76562 -14.0332 -22.9707 -14.0332 -33.9395c0 -10.9697 6.28711 -26.1748 14.0332 -33.9404zM421.83 26.1699c-2.58691 -2.58789 -7.65625 -4.6875 -11.3154 -4.6875
|
||||
c-3.6582 0 -8.72754 2.09961 -11.3145 4.6875l-8.57031 8.57031c-20.1602 20.1602 -54.6299 5.87988 -54.6299 -22.6201v-12.1201c0 -8.83203 -7.16797 -16 -16 -16s-16 7.16797 -16 16v12.1201c0 28.5 -34.4697 42.7803 -54.6299 22.6201l-8.57031 -8.57031
|
||||
c-2.60156 -2.67969 -7.74512 -4.85547 -11.4805 -4.85547c-8.83203 0 -16 7.16797 -16 16c0 3.7334 2.17285 8.87402 4.85059 11.4756l8.58008 8.58008c20.1602 20.1602 5.87988 54.6299 -22.6299 54.6299h-12.1201c-8.83203 0 -16 7.16797 -16 16s7.16797 16 16 16h12.1201
|
||||
c28.5098 0 42.79 34.4697 22.6299 54.6201l-8.58008 8.58008c-2.58691 2.58691 -4.68652 7.65625 -4.68652 11.3145c0 8.83301 7.16895 16.002 16.002 16.002c3.6582 0 8.72754 -2.09961 11.3145 -4.68652l8.57031 -8.58008
|
||||
c20.1602 -20.1602 54.6299 -5.87988 54.6299 22.6299v12.1201c0 8.83203 7.16797 16 16 16s16 -7.16797 16 -16v-12.1201c0 -28.5098 34.4697 -42.79 54.6299 -22.6299l8.57031 8.58008c2.58691 2.58691 7.65625 4.68652 11.3145 4.68652
|
||||
c8.83301 0 16.002 -7.16895 16.002 -16.002c0 -3.6582 -2.09961 -8.72754 -4.68652 -11.3145l-8.58008 -8.58008c-20.1602 -20.1504 -5.87988 -54.6201 22.6299 -54.6201h12.1201c8.83203 0 16 -7.16797 16 -16s-7.16797 -16 -16 -16h-12.1201
|
||||
c-28.5098 0 -42.79 -34.4697 -22.6299 -54.6299l8.58008 -8.58008c2.58496 -2.58691 4.68262 -7.65332 4.68262 -11.3096c0 -3.65723 -2.09766 -8.72363 -4.68262 -11.3105zM288 144c8.83203 0 16 7.16797 16 16s-7.16797 16 -16 16s-16 -7.16797 -16 -16
|
||||
s7.16797 -16 16 -16zM352 80c8.83203 0 16 7.16797 16 16s-7.16797 16 -16 16s-16 -7.16797 -16 -16s7.16797 -16 16 -16zM636.12 57.8701c2.1416 -7.9668 3.87988 -21.1289 3.87988 -29.3789v-0.241211c0 -61.25 -62.5 -105.51 -125.25 -88.6299l-59.5 15.8701
|
||||
c-17.7246 4.7041 -41.2441 20.2725 -52.5 34.75c2.32812 -0.421875 6.1377 -0.764648 8.50391 -0.764648c26.3311 0 47.7002 21.3701 47.7002 47.7002c0 11.1445 -6.49316 26.4863 -14.4941 34.2441l-8.58008 8.58008h12.1201c26.4961 0 48 21.5039 48 48
|
||||
s-21.5039 48 -48 48h-12.1201l8.58008 8.53027c7.84375 7.77441 14.209 23.0469 14.209 34.0898c0 26.4961 -21.5039 48 -48 48c-8.03809 0 -19.9854 -3.62402 -26.6689 -8.08984v27.7197c0 36.3701 31.3799 65.75 70 65.75c44 0 58 -28.5 80.1201 -63.1299
|
||||
c38.0928 -59.3662 83.7891 -162.854 102 -231z" />
|
||||
<glyph glyph-name="people-arrows" unicode="泌" horiz-adv-x="576"
|
||||
d="M96 320c-35.3281 0 -64 28.6719 -64 64s28.6719 64 64 64s64 -28.6719 64 -64s-28.6719 -64 -64 -64zM96 143.92v-0.118164c0 -10.4102 6.11035 -24.6934 13.6396 -31.8818l50.3604 -47.5303v-96.3896c0 -17.6641 -14.3359 -32 -32 -32h-64
|
||||
c-17.6641 0 -32 14.3359 -32 32v128c-17.6641 0 -32 14.3359 -32 32v96c0 35.3281 28.6719 64 64 64h64c23.9707 -0.0224609 50.5732 -18.1357 59.3799 -40.4297c-1.83984 -1.26074 -3.95996 -2.02051 -5.61035 -3.57031l-72.1299 -68.0801
|
||||
c-7.5293 -7.19336 -13.6396 -21.4814 -13.6396 -31.8945v-0.105469zM480 320c-35.3281 0 -64 28.6719 -64 64s28.6719 64 64 64s64 -28.6719 64 -64s-28.6719 -64 -64 -64zM512 288c35.3281 0 64 -28.6719 64 -64v-96c0 -17.6641 -14.3359 -32 -32 -32v-128
|
||||
c0 -17.6641 -14.3359 -32 -32 -32h-64c-17.6641 0 -32 14.3359 -32 32v96.3799l50.3604 47.5498c7.52539 7.22949 13.6338 21.5654 13.6338 32c0 10.4355 -6.1084 24.7715 -13.6338 32l-72.1201 68.0605c-1.62012 1.58984 -3.78027 2.31934 -5.62012 3.58984
|
||||
c8.80957 22.291 35.4111 40.3984 59.3799 40.4199h64zM444.4 152.66c1.98633 -2.00195 3.59863 -5.91504 3.59863 -8.73535s-1.6123 -6.7334 -3.59863 -8.73438l-72.1201 -68.0703c-1.91895 -1.83008 -5.62891 -3.31445 -8.28027 -3.31445c-6.62402 0 -12 5.37598 -12 12
|
||||
v0.0546875v36.1396h-128v-36.1396v-0.0546875c0 -6.62402 -5.37598 -12 -12 -12c-2.65137 0 -6.36133 1.48438 -8.28027 3.31445l-72.1201 68.0703c-1.98633 2.00098 -3.59863 5.91406 -3.59863 8.73438s1.6123 6.7334 3.59863 8.73535l72.1201 68.0703
|
||||
c1.91895 1.8291 5.62891 3.31348 8.28027 3.31348c6.62402 0 12 -5.37598 12 -12v-0.0439453v-36h128v36v0.0341797c0 6.62402 5.37598 12 12 12c2.65137 0 6.36133 -1.48438 8.28027 -3.31445z" />
|
||||
<glyph glyph-name="plane-slash" unicode="數" horiz-adv-x="640"
|
||||
d="M32.4805 300.12c-0.21875 0.947266 -0.396484 2.50586 -0.396484 3.47852c0 2.40137 1.01465 6.0127 2.26562 8.06152l324.841 -251.061l-66.6006 -116.54c-2.54297 -4.44824 -8.76562 -8.05957 -13.8896 -8.05957h-65.5
|
||||
c-8.81543 0.0166016 -15.9697 7.18457 -15.9697 16c0 1.24121 0.277344 3.2168 0.619141 4.41016l49 171.59h-102.85l-43.2002 -57.5898c-2.64746 -3.53613 -8.38184 -6.4082 -12.7998 -6.41016h-40c-8.8291 0.00292969 -15.9951 7.1709 -15.9951 16
|
||||
c0 1.08398 0.212891 2.81836 0.475586 3.87012l31.5195 108.13zM633.82 -10.0898c3.41602 -2.65234 6.18848 -8.31445 6.18848 -12.6387c0 -3.03027 -1.50879 -7.42969 -3.36914 -9.82129l-19.6396 -25.2598c-2.65234 -3.41699 -8.31445 -6.18945 -12.6387 -6.18945
|
||||
c-3.03027 0 -7.42969 1.50977 -9.82129 3.36914l-588.36 454.72c-3.41211 2.65234 -6.18262 8.3125 -6.18262 12.6338c0 3.03223 1.51172 7.43359 3.37305 9.82617l19.6299 25.2598c2.65234 3.41309 8.31152 6.18262 12.6338 6.18262
|
||||
c3.03125 0 7.43359 -1.51074 9.82617 -3.37207l189.3 -146.3l-36.9395 129.29c-0.338867 1.1875 -0.614258 3.1543 -0.614258 4.38965c0 8.8291 7.16504 15.9971 15.9941 16h65.5098c5.12988 0 11.3496 -3.61035 13.9004 -8.05957l105.09 -183.94h114.3
|
||||
c35.3398 0 96 -28.6602 96 -64s-60.6602 -64 -96 -64h-56.8604z" />
|
||||
<glyph glyph-name="pump-medical" unicode="索" horiz-adv-x="384"
|
||||
d="M235.51 288.18c32.2471 -0.00195312 60.7979 -26.0664 63.7305 -58.1797l20.3701 -224c0.145508 -1.59766 0.262695 -4.19629 0.262695 -5.7998c0 -35.3242 -28.6689 -63.9961 -63.9932 -64h-192h-0.00292969c-35.3281 0 -64 28.6719 -64 64
|
||||
c0 1.60352 0.117188 4.20215 0.262695 5.7998l20.3701 224c2.93262 32.1133 31.4834 58.1777 63.7305 58.1797h151.27zM239.88 114.85v26.6602c0 7.36426 -5.97656 13.3398 -13.3398 13.3398v0h-40v40c0 7.3584 -5.97168 13.3301 -13.3301 13.3301v0h-26.6699
|
||||
c-7.3584 0 -13.3301 -5.97168 -13.3301 -13.3301v-40h-40c-7.3584 0 -13.3301 -5.97168 -13.3301 -13.3301v-0.00976562v-26.6602c0 -7.35742 5.97168 -13.334 13.3301 -13.3398h40v-40c0 -7.3584 5.97168 -13.3301 13.3301 -13.3301v0h26.6699
|
||||
c7.3584 0 13.3301 5.97168 13.3301 13.3301v40h40c7.3584 0.00585938 13.334 5.98242 13.3398 13.3398zM379.19 354.12c2.58691 -2.58691 4.6875 -7.65625 4.6875 -11.3154c0 -3.6582 -2.10059 -8.72754 -4.6875 -11.3145l-22.6201 -22.6201
|
||||
c-2.58691 -2.58789 -7.65625 -4.6875 -11.3154 -4.6875s-8.72754 2.09961 -11.3145 4.6875l-43.3105 43.3096h-66.75v-32h-128v96c0 17.6641 14.3359 32 32 32h64c17.6641 0 32 -14.3359 32 -32h66.75c14.6279 -0.00195312 34.8955 -8.39746 45.2402 -18.7393z" />
|
||||
<glyph glyph-name="pump-soap" unicode="參" horiz-adv-x="384"
|
||||
d="M235.63 288c32.2637 0 60.8311 -26.0781 63.75 -58.21l20.3604 -224c0.144531 -1.59473 0.262695 -4.18848 0.262695 -5.79004c0 -35.3281 -28.6729 -64 -64 -64h-0.00292969h-192c-35.3242 0.00390625 -63.9922 28.6758 -63.9922 64
|
||||
c0 1.60156 0.117188 4.19531 0.261719 5.79004l20.3604 224c2.91895 32.1318 31.4736 58.21 63.7373 58.21h0.00292969h151.26zM160 32c33.1201 0 60 26.3301 60 58.7305c0 25 -35.6699 75.4697 -52 97.2695c-1.65625 2.21387 -5.24316 4.00977 -8.00781 4.00977
|
||||
c-2.75586 0 -6.33594 -1.78711 -7.99219 -3.99023c-16.2998 -21.7998 -52 -72.2695 -52 -97.2695c0 -32.4199 26.8799 -58.75 60 -58.75zM379.31 353.94c2.58789 -2.58691 4.68848 -7.65625 4.68848 -11.3154s-2.10059 -8.72852 -4.68848 -11.3154l-22.6191 -22.6191
|
||||
c-2.58691 -2.58789 -7.65625 -4.68848 -11.3154 -4.68848s-8.72852 2.10059 -11.3154 4.68848l-43.3096 43.3096h-66.75v-32h-128v96c0 17.6641 14.3359 32 32 32h64c17.6641 0 32 -14.3359 32 -32h66.75v0c14.6309 0 34.9033 -8.39551 45.25 -18.7402z" />
|
||||
<glyph glyph-name="shield-virus" unicode="塞"
|
||||
d="M224 256c8.83203 0 16 -7.16797 16 -16s-7.16797 -16 -16 -16s-16 7.16797 -16 16s7.16797 16 16 16zM466.5 364.32c16.2842 -6.80176 29.5 -26.6445 29.5 -44.292v-0.0283203c0 -221.3 -135.91 -344.61 -221.59 -380.32
|
||||
c-4.89062 -2.03223 -13.1592 -3.68164 -18.4551 -3.68164c-5.29688 0 -13.5645 1.64941 -18.4551 3.68164c-107 44.6006 -221.5 181.82 -221.5 380.32v0.0478516c0 17.6787 13.2559 37.5176 29.5898 44.2822l192 80c4.92676 1.85938 13.1973 3.50391 18.46 3.66992
|
||||
c5.26074 -0.169922 13.5264 -1.81836 18.4502 -3.67969zM384 192c8.83203 0 16 7.16797 16 16s-7.16797 16 -16 16h-12.1201c-28.5098 0 -42.79 34.4697 -22.6299 54.6299l8.58008 8.57031c2.58691 2.58691 4.68652 7.65625 4.68652 11.3145
|
||||
c0 8.83301 -7.16895 16.002 -16.002 16.002c-3.6582 0 -8.72754 -2.09961 -11.3145 -4.68652l-8.57031 -8.58008c-20.1602 -20.1602 -54.6299 -5.87988 -54.6299 22.6299v12.1201c0 8.83203 -7.16797 16 -16 16s-16 -7.16797 -16 -16v-12.1201
|
||||
c0 -28.5098 -34.4697 -42.79 -54.6299 -22.6299l-8.57031 8.58008c-2.58691 2.58691 -7.65625 4.68652 -11.3145 4.68652c-8.83301 0 -16.002 -7.16895 -16.002 -16.002c0 -3.6582 2.09961 -8.72754 4.68652 -11.3145l8.58008 -8.57031
|
||||
c20.1602 -20.1602 5.87988 -54.6299 -22.6299 -54.6299h-12.1201c-8.83203 0 -16 -7.16797 -16 -16s7.16797 -16 16 -16h12.1201c28.5098 0 42.79 -34.4697 22.6299 -54.6299l-8.58008 -8.57031c-2.58691 -2.58691 -4.68652 -7.65625 -4.68652 -11.3145
|
||||
c0 -8.83301 7.16895 -16.002 16.002 -16.002c3.6582 0 8.72754 2.09961 11.3145 4.68652l8.57031 8.58008c20.1602 20.1602 54.6299 5.87988 54.6299 -22.6299v-12.1201c0 -8.83203 7.16797 -16 16 -16s16 7.16797 16 16v12.1201c0 28.5098 34.4697 42.79 54.6299 22.6299
|
||||
l8.57031 -8.58008c2.58691 -2.58691 7.65625 -4.68652 11.3145 -4.68652c8.83301 0 16.002 7.16895 16.002 16.002c0 3.6582 -2.09961 8.72754 -4.68652 11.3145l-8.58008 8.57031c-20.1602 20.1602 -5.87988 54.6299 22.6299 54.6299h12.1201zM288 192
|
||||
c8.83203 0 16 -7.16797 16 -16s-7.16797 -16 -16 -16s-16 7.16797 -16 16s7.16797 16 16 16z" />
|
||||
<glyph glyph-name="soap" unicode="葉"
|
||||
d="M416 256c52.9922 0 96 -43.0078 96 -96v-128c0 -52.9922 -43.0078 -96 -96 -96h-320c-52.9922 0 -96 43.0078 -96 96v128c0 52.9922 43.0078 96 96 96h128c0.0517578 -20.3193 11.2119 -48.9912 24.9102 -64h-88.9102c-52.9922 0 -96 -43.0078 -96 -96
|
||||
s43.0078 -96 96 -96h192h0.206055c52.8809 0 95.7998 42.918 95.7998 95.7998c0 36.9893 -28.2002 77.3047 -62.9463 89.9902c17.0488 15.6279 30.9092 47.082 30.9404 70.21zM320 192c-35.3281 0 -64 28.6719 -64 64s28.6719 64 64 64s64 -28.6719 64 -64
|
||||
s-28.6719 -64 -64 -64zM208 352c-26.4961 0 -48 21.5039 -48 48s21.5039 48 48 48s48 -21.5039 48 -48s-21.5039 -48 -48 -48zM384 384c-17.6641 0 -32 14.3359 -32 32s14.3359 32 32 32s32 -14.3359 32 -32s-14.3359 -32 -32 -32zM160 160h192
|
||||
c35.3281 0 64 -28.6719 64 -64s-28.6719 -64 -64 -64h-192c-35.3281 0 -64 28.6719 -64 64s28.6719 64 64 64z" />
|
||||
<glyph glyph-name="stopwatch-20" unicode="說" horiz-adv-x="448"
|
||||
d="M398.5 257.09c18.4922 -28.3281 33.501 -78.7754 33.501 -112.605c0 -0.133789 -0.000976562 -0.350586 -0.000976562 -0.484375c0 -116 -94.8701 -209.77 -211.28 -208c-113.96 1.78027 -208.08 100.5 -204.63 214.43c2.92773 95.2598 81.7354 184.443 175.91 199.07
|
||||
v34.5h-32c-8.80469 0.0273438 -15.9727 7.19531 -16 16v32c0.0273438 8.80469 7.19531 15.9727 16 16h128c8.83203 0 16 -7.16797 16 -16v-32c0 -8.83203 -7.16797 -16 -16 -16h-32v-34.5c30.8857 -4.76953 75.5469 -24.7461 99.6904 -44.5898l24.6797 24.6797
|
||||
c2.58691 2.58789 7.65625 4.6875 11.3145 4.6875c3.65918 0 8.72852 -2.09961 11.3154 -4.6875l22.6797 -22.6797c2.58789 -2.58691 4.6875 -7.65625 4.6875 -11.3154c0 -3.6582 -2.09961 -8.72754 -4.6875 -11.3145l-26.5898 -26.5801zM204.37 70.4502l-49.1299 0.0400391
|
||||
c1.7998 15.6299 14.8496 36.2002 26.4102 51.2002c21.9092 30.0996 34.3496 45.7295 34.3496 81.3096c0 35.1504 -12.5703 61 -55.5703 61c-47.9492 0 -56.4297 -32.9404 -56.4297 -60.2402v-4.06934c0.0703125 -4.45605 3.74316 -8.07129 8.19922 -8.07129
|
||||
c0.0361328 0 0.0947266 0 0.130859 0.000976562h24.9004c0.0361328 -0.000976562 0.0947266 -0.000976562 0.130859 -0.000976562c4.45508 0 8.12891 3.61523 8.19824 8.07129v5.22949c0 15.2803 3.30078 22.6797 12.6904 22.6797c10.4199 0 12.21 -7.34961 12.21 -24.2695
|
||||
c0 -25.0205 -6.67969 -33.1504 -27.0996 -62.3398c-23.7803 -33.96 -35.6699 -56.1504 -38.4502 -91.3701c-0.0224609 -0.320312 -0.0410156 -0.84082 -0.0410156 -1.16113c0 -9.08594 7.37402 -16.46 16.46 -16.46c0.0527344 0 0.137695 0 0.19043 0.000976562h82.8506
|
||||
c0.0332031 -0.000976562 0.0878906 -0.000976562 0.121094 -0.000976562c4.45508 0 8.12891 3.61621 8.19922 8.07129v22.3096c-0.0703125 4.45508 -3.74414 8.07129 -8.19922 8.07129c-0.0332031 0 -0.0878906 -0.000976562 -0.121094 -0.000976562zM344 95.6797v107.021
|
||||
c0 38.6602 -19 61.2998 -55.7998 61.2998c-36.6201 0 -56.2002 -22.4902 -56.2002 -63.2197v-105.33c0 -33.9307 11.1904 -63.4502 54.7695 -63.4502c44.9307 0 57.2305 28.5195 57.2305 63.6797zM287.87 226.27c10.0098 0 13.0195 -8.05957 13 -19.3291v-115.94
|
||||
c0 -13.2695 -3.36035 -21.2695 -13 -21.2695s-13.2305 7.47949 -13.2305 20.5898v115.949c0 12.5 3.82031 20 13.2305 20z" />
|
||||
<glyph glyph-name="store-alt-slash" unicode="殺" horiz-adv-x="640"
|
||||
d="M17.8896 324.38l88.4707 -68.3799h-74.2607c-25.5898 0 -40.79 28.5 -26.5898 49.7998zM576 34.5801l57.8301 -44.6797c3.41016 -2.65234 6.17773 -8.31055 6.17773 -12.6309c0 -3.0293 -1.50879 -7.42773 -3.36816 -9.81934l-19.6396 -25.2598
|
||||
c-2.65234 -3.41895 -8.31543 -6.19434 -12.6426 -6.19434c-3.03223 0 -7.43457 1.51172 -9.82715 3.37402l-588.351 454.72c-3.41016 2.65234 -6.17773 8.31055 -6.17773 12.6309c0 3.02832 1.50879 7.42773 3.36816 9.81934l19.6299 25.2695
|
||||
c2.65234 3.41309 8.31152 6.18262 12.6338 6.18262c3.03125 0 7.43359 -1.51074 9.82617 -3.37207l34.6904 -26.8203l10.6592 16c5.22266 7.83887 17.1045 14.2002 26.5234 14.2002h0.0869141h405.18h0.0117188c9.45117 0 21.4082 -6.36133 26.6885 -14.2002l85.29 -128
|
||||
c14.1104 -21.2998 -1.08984 -49.7998 -26.5898 -49.7998h-318.48l41.4004 -32h53.0801v-41l128 -99v140h64v-189.42zM320 64v26.8799l64 -49.4697v-73.4102c0 -17.6641 -14.3359 -32 -32 -32h-256c-17.6641 0 -32 14.3359 -32 32v256h64v-160h192z" />
|
||||
<glyph glyph-name="store-slash" unicode="辰" horiz-adv-x="640"
|
||||
d="M121.51 64h226.91l157.33 -128h-414.52c-16.8105 0 -30.4004 14.2998 -30.4004 32v196.8c4.23047 -1.29297 11.2109 -2.90625 15.5801 -3.59961c4.69629 -0.660156 12.3574 -1.19727 17.0996 -1.2002c7.85352 0.12793 20.3975 1.83008 28 3.7998v-99.7998z
|
||||
M93.5098 192.09h-0.21875c-3.54883 0 -9.28418 0.385742 -12.8008 0.860352c-58.9404 8.46973 -87.0098 81.6094 -56.4902 135l133.51 -108.62c-16.71 -16.5205 -38.8994 -27.2402 -64 -27.2402zM602.13 -10.0898c3.24316 -2.74219 5.875 -8.41406 5.875 -12.6611
|
||||
c0 -2.99414 -1.43555 -7.38379 -3.20508 -9.79883l-18.6602 -25.2598c-2.42383 -3.41309 -7.78906 -6.18359 -11.9756 -6.18359c-2.91602 0 -7.10645 1.50684 -9.35449 3.36328l-558.939 454.72c-3.24316 2.74219 -5.875 8.41406 -5.875 12.6611
|
||||
c0 2.99414 1.43555 7.38379 3.20508 9.79883l18.6602 25.2598c2.42188 3.41211 7.78516 6.18164 11.9697 6.18164c2.91797 0 7.11133 -1.51074 9.36035 -3.37109l33.6895 -27.4004l9.38965 15.7803c4.74609 8.18066 16.2734 14.9014 25.7305 15h383.81
|
||||
c9.46289 -0.09375 20.9941 -6.81445 25.7402 -15l61.6602 -103.6c31.9404 -53.6006 3.59961 -127.99 -56.0596 -136.4c-3.57129 -0.5 -9.39453 -0.907227 -13 -0.910156c-28.0303 0 -52.9199 13 -70.1104 33.1104c-17.1104 -20.1104 -42 -33.1104 -70.1104 -33.1104
|
||||
c-7.18164 0.106445 -18.5654 1.96094 -25.4102 4.14062l137.82 -112.11v79.6797c7.59863 -2.00586 20.1426 -3.70898 28 -3.7998c4.79492 0.00585938 12.541 0.542969 17.29 1.2002c4.38281 0.625 11.3584 2.2373 15.5703 3.59961v-130.21z" />
|
||||
<glyph glyph-name="toilet-paper-slash" unicode="沈" horiz-adv-x="640"
|
||||
d="M64 256c0 10.8096 0.530273 21.3398 1.41992 31.6699l316 -244.25c-4.17969 -32.2002 -12.8701 -57.7197 -22.1797 -85.5498c-3.98926 -12.0723 -17.5459 -21.8701 -30.2607 -21.8701h-0.119141h-280.86c-8.78906 0.0429688 -15.9209 7.21094 -15.9209 16
|
||||
c0 1.41504 0.358398 3.65527 0.800781 5c21.3701 64.1201 31.1201 85.75 31.1201 126.87v172.13zM633.82 -10.0898c3.41602 -2.65234 6.18848 -8.31445 6.18848 -12.6387c0 -3.03027 -1.50879 -7.42969 -3.36914 -9.82129l-19.6396 -25.2598
|
||||
c-2.65234 -3.41699 -8.31445 -6.18945 -12.6387 -6.18945c-3.03027 0 -7.42969 1.50977 -9.82129 3.36914l-588.36 454.72c-3.41211 2.65234 -6.18262 8.3125 -6.18262 12.6338c0 3.03223 1.51172 7.43359 3.37305 9.82617l19.6299 25.2598
|
||||
c2.65234 3.41309 8.31152 6.18262 12.6338 6.18262c3.03125 0 7.43359 -1.51074 9.82617 -3.37207l53.2803 -41.1504c16.6299 27.7002 37.9297 44.5303 61.2598 44.5303h284.5c-36.8701 -38.5 -60.5 -108.38 -60.5 -192v-73l50.4297 -39
|
||||
c-11.4297 31.5996 -18.4297 70 -18.4297 112c0 106 43 192 96 192s96 -86 96 -192c0 -92.3203 -32.7197 -168.91 -76.1797 -187.28zM512 192c17.6201 0 32 28.6299 32 64s-14.3701 64 -32 64s-32 -28.6201 -32 -64s14.3701 -64 32 -64z" />
|
||||
<glyph glyph-name="virus" unicode="若"
|
||||
d="M483.55 220.45c0.147461 0.00292969 0.356445 0.00488281 0.503906 0.00488281c15.7041 0 28.4492 -12.7461 28.4492 -28.4502s-12.7451 -28.4502 -28.4492 -28.4502c-0.147461 0 -0.386719 0.00292969 -0.53418 0.00488281h-21.5391
|
||||
c-50.6807 0 -76.0703 -61.2793 -40.2305 -97.1191l15.25 -15.2402c4.15039 -4.50879 7.51855 -13.1406 7.51855 -19.2686c0 -15.7051 -12.7451 -28.4502 -28.4502 -28.4502c-6.12793 0 -14.7598 3.36816 -19.2686 7.51855l-15.2402 15.2305
|
||||
c-35.8398 35.8398 -97.1094 10.4492 -97.1094 -40.2305v-21.5195c0 -15.7051 -12.7461 -28.4502 -28.4502 -28.4502s-28.4502 12.7451 -28.4502 28.4502v21.5391c0 50.6807 -61.2695 76.0703 -97.1094 40.2305l-15.2402 -15.25
|
||||
c-4.50879 -4.15039 -13.1406 -7.51855 -19.2686 -7.51855c-15.7051 0 -28.4502 12.7451 -28.4502 28.4502c0 6.12793 3.36816 14.7598 7.51855 19.2686l15.2305 15.2402c35.8398 35.8398 10.4492 97.1191 -40.2305 97.1191h-21.5498
|
||||
c-0.147461 -0.00195312 -0.386719 -0.00488281 -0.533203 -0.00488281c-15.7051 0 -28.4502 12.7461 -28.4502 28.4502s12.7451 28.4502 28.4502 28.4502c0.146484 0 0.385742 -0.00195312 0.533203 -0.00488281h21.5693c50.6807 0 76.0703 61.2695 40.2305 97.1094
|
||||
l-15.25 15.25c-4.21094 4.52148 -7.62793 13.2051 -7.62793 19.3828c0 15.6992 12.7412 28.4404 28.4404 28.4404c6.17969 0 14.8662 -3.41992 19.3877 -7.63281l15.2402 -15.2305c35.8398 -35.8291 97.1094 -10.4492 97.1094 40.2305v21.5596
|
||||
c0 15.7051 12.7461 28.4502 28.4502 28.4502s28.4502 -12.7451 28.4502 -28.4502v-21.5498c0 -50.6797 61.2695 -76.0596 97.1094 -40.2295l15.2402 15.2197c4.52148 4.21289 13.208 7.63281 19.3877 7.63281c15.6992 0 28.4404 -12.7412 28.4404 -28.4404
|
||||
c0 -6.17773 -3.41699 -14.8613 -7.62793 -19.3828l-15.2305 -15.25c-35.8398 -35.8398 -10.4492 -97.1094 40.2305 -97.1094h21.5498zM224 176c26.4961 0 48 21.5039 48 48s-21.5039 48 -48 48s-48 -21.5039 -48 -48s21.5039 -48 48 -48zM304 120c13.248 0 24 10.752 24 24
|
||||
s-10.752 24 -24 24s-24 -10.752 -24 -24s10.752 -24 24 -24z" />
|
||||
<glyph glyph-name="virus-slash" unicode="掠" horiz-adv-x="640"
|
||||
d="M114 220.44c8.37207 0.0664062 20.9922 3.61914 28.1699 7.92969l244.5 -189c-21.2197 -7.45996 -38.2197 -26.7598 -38.2197 -53.3701v-21.5195c0 -15.7051 -12.7461 -28.4502 -28.4502 -28.4502s-28.4502 12.7451 -28.4502 28.4502v21.5391
|
||||
c0 50.6807 -61.2695 76.0703 -97.1094 40.2305l-15.25 -15.25c-4.66113 -5.03711 -14.0127 -9.125 -20.875 -9.125c-15.6992 0 -28.4404 12.7412 -28.4404 28.4404c0 6.8623 4.08789 16.2139 9.125 20.875l15.2305 15.25c35.8291 35.8398 10.4492 97.1191 -40.2305 97.1191
|
||||
h-21.5596c-15.6992 0 -28.4404 12.7412 -28.4404 28.4404s12.7412 28.4404 28.4404 28.4404h21.5596zM633.82 -10.0898c3.41602 -2.65234 6.18848 -8.31445 6.18848 -12.6387c0 -3.03027 -1.50879 -7.42969 -3.36914 -9.82129l-19.6396 -25.2598
|
||||
c-2.65234 -3.41699 -8.31445 -6.18945 -12.6387 -6.18945c-3.03027 0 -7.42969 1.50977 -9.82129 3.36914l-588.36 454.72c-3.41211 2.65234 -6.18262 8.3125 -6.18262 12.6338c0 3.03223 1.51172 7.43359 3.37305 9.82617l19.6299 25.2598
|
||||
c2.65234 3.41309 8.31152 6.18262 12.6338 6.18262c3.03125 0 7.43359 -1.51074 9.82617 -3.37207l93.2598 -72.0801c0.129883 0.139648 0.150391 0.320312 0.280273 0.459961c4.5957 4.5918 13.5986 8.31934 20.0947 8.31934c6.49707 0 15.5 -3.72754 20.0957 -8.31934
|
||||
l15.25 -15.2305c35.8398 -35.8398 97.1094 -10.46 97.1094 40.2305v21.5498c0 15.7051 12.7461 28.4502 28.4502 28.4502s28.4502 -12.7451 28.4502 -28.4502v-21.54c0 -50.6895 61.2695 -76.0693 97.1094 -40.2295l15.25 15.2197
|
||||
c4.51367 4.17578 13.167 7.56543 19.3154 7.56543c15.6992 0 28.4404 -12.7412 28.4404 -28.4404c0 -6.14844 -3.38965 -14.8018 -7.56543 -19.3154l-15.2305 -15.29c-35.8291 -35.8398 -10.4492 -97.1191 40.2305 -97.1191h21.5596
|
||||
c15.6992 0 28.4404 -12.7412 28.4404 -28.4404s-12.7412 -28.4404 -28.4404 -28.4404v0h-21.5498c-30.4795 0 -51.2197 -22.1299 -55.3896 -47.5195zM335.43 220.52c0.0898438 1.19043 0.570312 2.26074 0.570312 3.48047c0 26.4961 -21.5039 48 -48 48
|
||||
c-4.39844 -0.0683594 -11.3154 -1.36328 -15.4404 -2.88965z" />
|
||||
<glyph glyph-name="viruses" unicode="略" horiz-adv-x="640"
|
||||
d="M624 96c8.83203 0 16 -7.16797 16 -16s-7.16797 -16 -16 -16h-12.1201c-28.5098 0 -42.79 -34.4697 -22.6299 -54.6299l8.58008 -8.57031c2.58691 -2.58691 4.68652 -7.65625 4.68652 -11.3145c0 -8.83301 -7.16895 -16.002 -16.002 -16.002
|
||||
c-3.6582 0 -8.72754 2.09961 -11.3145 4.68652l-8.57031 8.58008c-20.1602 20.1602 -54.6299 5.87988 -54.6299 -22.6299v-12.1201c0 -8.83203 -7.16797 -16 -16 -16s-16 7.16797 -16 16v12.1201c0 28.5098 -34.4697 42.79 -54.6299 22.6299l-8.57031 -8.58008
|
||||
c-2.58691 -2.58691 -7.65625 -4.68652 -11.3145 -4.68652c-8.83301 0 -16.002 7.16895 -16.002 16.002c0 3.6582 2.09961 8.72754 4.68652 11.3145l8.58008 8.57031c20.1602 20.1602 5.87988 54.6299 -22.6299 54.6299h-12.1201c-8.83203 0 -16 7.16797 -16 16
|
||||
s7.16797 16 16 16h12.1201c28.5098 0 42.79 34.4697 22.6299 54.6299l-8.58008 8.57031c-2.58691 2.58691 -4.68652 7.65625 -4.68652 11.3145c0 8.83301 7.16895 16.002 16.002 16.002c3.6582 0 8.72754 -2.09961 11.3145 -4.68652l8.57031 -8.58008
|
||||
c20.1602 -20.1602 54.6299 -5.87988 54.6299 22.6299v12.1201c0 8.83203 7.16797 16 16 16s16 -7.16797 16 -16v-12.1201c0 -28.5098 34.4697 -42.79 54.6299 -22.6299l8.57031 8.58008c2.58691 2.58691 7.65625 4.68652 11.3145 4.68652
|
||||
c8.83301 0 16.002 -7.16895 16.002 -16.002c0 -3.6582 -2.09961 -8.72754 -4.68652 -11.3145l-8.58008 -8.57031c-20.1602 -20.1602 -5.87988 -54.6299 22.6299 -54.6299h12.1201zM480 64c17.6641 0 32 14.3359 32 32s-14.3359 32 -32 32s-32 -14.3359 -32 -32
|
||||
s14.3359 -32 32 -32zM346.51 234.67c-38.0195 0 -57.0498 -45.96 -30.1699 -72.8398l11.4297 -11.4297c3.44922 -3.44922 6.24902 -10.208 6.24902 -15.085c0 -11.7764 -9.55762 -21.334 -21.334 -21.334c-4.87695 0 -11.6357 2.7998 -15.085 6.24902l-11.4297 11.4297
|
||||
c-26.8398 26.8799 -72.8398 7.83008 -72.8398 -30.1699v-16.1602c0 -11.7744 -9.55566 -21.3301 -21.3301 -21.3301s-21.3301 9.55566 -21.3301 21.3301v16.1602c0 38.0195 -45.96 57.0498 -72.8398 30.1699l-11.4297 -11.4297
|
||||
c-3.44922 -3.44922 -10.208 -6.24902 -15.085 -6.24902c-11.7764 0 -21.334 9.55762 -21.334 21.334c0 4.87695 2.7998 11.6357 6.24902 15.085l11.4297 11.4297c26.8799 26.8398 7.83008 72.8398 -30.1699 72.8398h-16.1602c-11.7744 0 -21.3301 9.55566 -21.3301 21.3301
|
||||
s9.55566 21.3301 21.3301 21.3301h16.1602c38.0195 0 57.0498 45.96 30.1699 72.8398l-11.4297 11.4404c-3.41895 3.44336 -6.19434 10.1758 -6.19434 15.0283c0 11.7744 9.55566 21.3301 21.3301 21.3301c4.85449 0 11.5898 -2.77734 15.0342 -6.19922l11.4297 -11.4297
|
||||
c26.8398 -26.8799 72.8398 -7.83008 72.8398 30.1699v16.1602c0 11.7744 9.55566 21.3301 21.3301 21.3301s21.3301 -9.55566 21.3301 -21.3301v-16.1602c0 -38.0195 45.96 -57.0498 72.8398 -30.1699l11.4297 11.4297c3.44434 3.42188 10.1797 6.19922 15.0342 6.19922
|
||||
c11.7744 0 21.3301 -9.55566 21.3301 -21.3301c0 -4.85254 -2.77539 -11.585 -6.19434 -15.0283l-11.4297 -11.4404c-26.8799 -26.8398 -7.83008 -72.8398 30.1699 -72.8398h16.1602c11.7744 0 21.3301 -9.55566 21.3301 -21.3301s-9.55566 -21.3301 -21.3301 -21.3301
|
||||
h-16.1602zM160 256c17.6641 0 32 14.3359 32 32s-14.3359 32 -32 32s-32 -14.3359 -32 -32s14.3359 -32 32 -32zM240 224c8.83203 0 16 7.16797 16 16s-7.16797 16 -16 16s-16 -7.16797 -16 -16s7.16797 -16 16 -16z" />
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
Before Width: | Height: | Size: 829 KiB After Width: | Height: | Size: 876 KiB |
4
dist/qmi-cloud/index.html
vendored
@@ -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.23a05ae088b0f881d0eb.css"></head>
|
||||
<link rel="stylesheet" href="styles.921aafa95031aeb74181.css"></head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
<script src="runtime.e227d1a0e31cbccbf8ec.js" defer></script><script src="polyfills-es5.f1f388528ea207060cbf.js" nomodule defer></script><script src="polyfills.335424f161535f57733f.js" defer></script><script src="scripts.5520a99f673924c17e00.js" defer></script><script src="main.412f22a84714f529779a.js" defer></script></body>
|
||||
<script src="runtime.689ba4fd6cadb82c1ac2.js" defer></script><script src="polyfills-es5.feb8e3dfdd8e1cace860.js" nomodule defer></script><script src="polyfills.60117177d3b4f4827ace.js" defer></script><script src="scripts.73c34722d75b092f2620.js" defer></script><script src="main.eca58c33a1ad840ee769.js" defer></script></body>
|
||||
</html>
|
||||
|
||||
1
dist/qmi-cloud/main.412f22a84714f529779a.js
vendored
1
dist/qmi-cloud/main.eca58c33a1ad840ee769.js
vendored
Normal file
1
dist/qmi-cloud/scripts.73c34722d75b092f2620.js
vendored
Normal file
@@ -56,6 +56,7 @@ services:
|
||||
target: config.json
|
||||
environment:
|
||||
- HOSTNAME_URL=http://localhost:3000
|
||||
- BACKEND_LOGS_URL=http://localhost:8888
|
||||
- REDIS_URL=redis://redis
|
||||
- MONGO_URI=mongodb://root:example@mongo/qmicloud?authSource=admin
|
||||
- CERT_PFX_FILENAME=
|
||||
@@ -75,6 +76,8 @@ 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
|
||||
@@ -93,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%
|
||||
@@ -104,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
|
||||
@@ -127,5 +131,16 @@ services:
|
||||
ME_CONFIG_MONGODB_ADMINPASSWORD: example
|
||||
ME_CONFIG_BASICAUTH_USERNAME: qlik
|
||||
ME_CONFIG_BASICAUTH_PASSWORD: Qlik1234
|
||||
networks:
|
||||
- backend
|
||||
|
||||
dozzle:
|
||||
image: amir20/dozzle:latest
|
||||
volumes:
|
||||
- /var/run/docker.sock:/var/run/docker.sock
|
||||
ports:
|
||||
- 8888:8080
|
||||
#environment:
|
||||
# DOZZLE_BASE: "/dozzle"
|
||||
networks:
|
||||
- backend
|
||||
@@ -22,7 +22,8 @@ echo "--- Building image: qlikgear/qmi-cloud-cli:$TAG_CLI"
|
||||
docker build -f ./qmi-cloud-cli/Dockerfile -t qlikgear/qmi-cloud-cli:$TAG_CLI ./
|
||||
echo "--- Pushing image: qlikgear/qmi-cloud-cli:$TAG_CLI"
|
||||
docker push qlikgear/qmi-cloud-cli:$TAG_CLI
|
||||
docker build -f ./qmi-cloud-cli/Dockerfile -t qlikgear/qmi-cloud-cli:$STABLE_TAG ./
|
||||
docker image tag qlikgear/qmi-cloud-cli:$TAG_CLI qlikgear/qmi-cloud-cli:$STABLE_TAG
|
||||
#docker build -f ./qmi-cloud-cli/Dockerfile -t qlikgear/qmi-cloud-cli:$STABLE_TAG ./
|
||||
docker push qlikgear/qmi-cloud-cli:$STABLE_TAG
|
||||
|
||||
|
||||
@@ -30,12 +31,14 @@ echo "--- Building image: qlikgear/qmi-cloud-worker:$TAG_WORKER"
|
||||
docker build -f ./qmi-cloud-worker/Dockerfile -t qlikgear/qmi-cloud-worker:$TAG_WORKER ./
|
||||
echo "--- Pushing image: qlikgear/qmi-cloud-worker:$TAG_WORKER"
|
||||
docker push qlikgear/qmi-cloud-worker:$TAG_WORKER
|
||||
docker build -f ./qmi-cloud-worker/Dockerfile -t qlikgear/qmi-cloud-worker:$STABLE_TAG ./
|
||||
docker image tag qlikgear/qmi-cloud-worker:$TAG_WORKER qlikgear/qmi-cloud-worker:$STABLE_TAG
|
||||
#docker build -f ./qmi-cloud-worker/Dockerfile -t qlikgear/qmi-cloud-worker:$STABLE_TAG ./
|
||||
docker push qlikgear/qmi-cloud-worker:$STABLE_TAG
|
||||
|
||||
echo "--- Building image: qlikgear/qmi-cloud-app:$TAG_APP"
|
||||
docker build -f ./Dockerfile -t qlikgear/qmi-cloud-app:$TAG_APP ./
|
||||
echo "--- Pushing image: qlikgear/qmi-cloud-app:$TAG_APP"
|
||||
docker push qlikgear/qmi-cloud-app:$TAG_APP
|
||||
docker build -f ./Dockerfile -t qlikgear/qmi-cloud-app:$STABLE_TAG ./
|
||||
docker image tag qlikgear/qmi-cloud-app:$TAG_APP qlikgear/qmi-cloud-app:$STABLE_TAG
|
||||
#docker build -f ./Dockerfile -t qlikgear/qmi-cloud-app:$STABLE_TAG ./
|
||||
docker push qlikgear/qmi-cloud-app:$STABLE_TAG
|
||||
|
||||
89
mystatsmashup/index.html
Normal file
@@ -0,0 +1,89 @@
|
||||
<!doctype html>
|
||||
<!-- <html qva-bootstrap="false" lang="en"> -->
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>Simple html with qdt-components</title>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<meta name="HandheldFriendly" content="True">
|
||||
<meta name="MobileOptimized" content="320">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta http-equiv="cleartype" content="on">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
|
||||
|
||||
<link rel="shortcut icon" href="favicon.ico" />
|
||||
<!-- <script type="text/javascript" src="https://qdt-apps.qlik.com/qdt-components/v3/3.0.0/qdt-components.js"></script> -->
|
||||
<script type="text/javascript" src="qdt-components.js"></script>
|
||||
<script type="text/javascript" src="index.js"></script>
|
||||
|
||||
<style>
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
div.qvobject {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding: 10px 0px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body onload="init()">
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div id="currentselections" style="width: 100%;height: 40px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div id="filterstatus" style="width: 100%;height: 40px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div id="totalprovkpi" class="qvobject"></div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div id="costhismonth" class="qvobject"></div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div id="statusprov" class="qvobject"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div id="qdt1" class="qvobject"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-2">
|
||||
<div id="qdt1a" class="qvobject"></div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div id="qdt3" class="qvobject"></div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div id="qdt4" class="qvobject"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
119
mystatsmashup/index.js
Normal file
@@ -0,0 +1,119 @@
|
||||
// https://help.qlik.com/en-US/sense-developer/June2020/Subsystems/Mashups/Content/Sense_Mashups/mashups-authentication-cloud.htm
|
||||
// https://qlik.dev/tutorials/build-a-simple-web-app
|
||||
const config = {
|
||||
host: "gear-presales.eu.qlikcloud.com",
|
||||
secure: true,
|
||||
port: 443,
|
||||
prefix: "",
|
||||
webIntegrationId: 'n4kMLH62hvXXC84q2vdfW15WUvrUw-HU',
|
||||
appId: "2d03e11f-f3d8-4ba7-a123-be5a282fb9f1"
|
||||
}
|
||||
const urlLoggedIn = "/api/v1/audits";//Use GET request to see if you are authenticated
|
||||
const urlLogin = "/login";
|
||||
|
||||
const init = async () => {
|
||||
const response = await fetch(`https://${config.host}${urlLoggedIn}`, {
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
'Qlik-Web-Integration-ID': config.webIntegrationId
|
||||
}
|
||||
})
|
||||
if(response.status===401){
|
||||
const url = new URL(`https://${config.host}/login`);
|
||||
url.searchParams.append('returnto', 'http://localhost:3000/mystats');
|
||||
url.searchParams.append('qlik-web-integration-id', config.webIntegrationId);
|
||||
window.location.href = url;
|
||||
}
|
||||
const { qdtCapabilityApp, QdtViz } = QdtComponents;
|
||||
const capabilityApiAppPromise = qdtCapabilityApp(config);
|
||||
const app = await capabilityApiAppPromise;
|
||||
|
||||
QdtViz({
|
||||
element: document.getElementById('currentselections'),
|
||||
app,
|
||||
options: {
|
||||
id: 'CurrentSelections',
|
||||
height: "40px"
|
||||
},
|
||||
});
|
||||
|
||||
QdtViz({
|
||||
element: document.getElementById('qdt1'),
|
||||
app,
|
||||
options: {
|
||||
type: 'table',
|
||||
id: 'mCjaU',
|
||||
height: "300px"
|
||||
},
|
||||
});
|
||||
|
||||
QdtViz({
|
||||
element: document.getElementById('qdt1a'),
|
||||
app,
|
||||
options: {
|
||||
type: "piechart",
|
||||
id: 'XrnNdj',
|
||||
height: "300px"
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
QdtViz({
|
||||
element: document.getElementById('qdt3'),
|
||||
app,
|
||||
options: {
|
||||
type: "linechart",
|
||||
id: 'QfYrJj',
|
||||
height: "300px"
|
||||
},
|
||||
});
|
||||
|
||||
QdtViz({
|
||||
element: document.getElementById('qdt4'),
|
||||
app,
|
||||
options: {
|
||||
type: "barchart",
|
||||
id: 'JvSJCW',
|
||||
height: "300px"
|
||||
},
|
||||
});
|
||||
|
||||
QdtViz({
|
||||
element: document.getElementById('totalprovkpi'),
|
||||
app,
|
||||
options: {
|
||||
id: 'zWDmhfq',
|
||||
height: "100px"
|
||||
},
|
||||
});
|
||||
|
||||
QdtViz({
|
||||
element: document.getElementById('filterstatus'),
|
||||
app,
|
||||
options: {
|
||||
id: 'uznaVCC',
|
||||
height: "40px"
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
QdtViz({
|
||||
element: document.getElementById('costhismonth'),
|
||||
app,
|
||||
options: {
|
||||
type: "kpi",
|
||||
id: "GkRz",
|
||||
height: "100px"
|
||||
},
|
||||
});
|
||||
|
||||
QdtViz({
|
||||
element: document.getElementById('statusprov'),
|
||||
app,
|
||||
options: {
|
||||
id: 'kXyQPa',
|
||||
height: "150px"
|
||||
},
|
||||
});
|
||||
}
|
||||
135423
mystatsmashup/qdt-components.js
Normal file
470
package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "qmi-cloud-app",
|
||||
"version": "1.1.6",
|
||||
"version": "1.3.1",
|
||||
"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",
|
||||
@@ -43,6 +44,7 @@
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"leonardo-ui": "^1.7.1",
|
||||
"moment": "^2.24.0",
|
||||
"moment-timezone": "^0.5.31",
|
||||
"mongoose": "^5.7.4",
|
||||
"ngx-markdown": "^9.0.0",
|
||||
"nodemon": "^1.19.1",
|
||||
@@ -75,4 +77,4 @@
|
||||
"tslint": "~5.11.0",
|
||||
"typescript": "~3.7.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
0
photos/.keep
Normal 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 ./
|
||||
|
||||
|
||||
@@ -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 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"
|
||||
```
|
||||
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
|
||||
```
|
||||
@@ -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,10 +12,11 @@ const moment = require('moment');
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
|
||||
const IS_REAL = myArgs[2] !== 'test';
|
||||
const STOPPED_PERIOD = myArgs[1]; //Days
|
||||
const STOPPED_LIMIT_HOURS_WARNING = 24*(STOPPED_PERIOD-2); //Hours
|
||||
const STOPPED_LIMIT_HOURS_STOP = 24*STOPPED_PERIOD; //Hours
|
||||
const IS_REAL = myArgs[1] !== 'test';
|
||||
const WARNING_DAYS = 2;
|
||||
|
||||
//---
|
||||
|
||||
const API_KEY = process.env.API_KEY;
|
||||
const SERVER_URL = "http://localhost:3000";
|
||||
|
||||
@@ -56,18 +57,18 @@ async function asyncForEach(array, callback) {
|
||||
}
|
||||
|
||||
async function init(type) {
|
||||
var limit, cb;
|
||||
var cb;
|
||||
var filter = {
|
||||
"isDestroyed":false,
|
||||
"isDeleted": false,
|
||||
"statusVms": "Stopped"
|
||||
"statusVms": "Stopped",
|
||||
"vmImage": {"$exists": true},
|
||||
"vmImage.vm1": { "$exists": true }
|
||||
};
|
||||
if ( type === "warning" ) {
|
||||
limit = STOPPED_LIMIT_HOURS_WARNING;
|
||||
filter.pendingNextAction = {$ne: "destroy"};
|
||||
cb = doSendEmailDestroyWarning;
|
||||
} else if ( type === "exec" ) {
|
||||
limit = STOPPED_LIMIT_HOURS_STOP;
|
||||
filter.pendingNextAction = "destroy";
|
||||
cb = doDestroy;
|
||||
} else {
|
||||
@@ -76,46 +77,56 @@ async function init(type) {
|
||||
}
|
||||
|
||||
let provisions = await db.provision.get(filter);
|
||||
let scenarios = await db.scenario.get();
|
||||
|
||||
await asyncForEach(provisions.results, async function(p) {
|
||||
var _scenario = scenarios.results.filter(s=>{
|
||||
return s.name === p.scenario
|
||||
});
|
||||
if ( _scenario.length ){
|
||||
p._scenario = _scenario[0];
|
||||
var typeSchedule = "24x7";
|
||||
if ( p.schedule && !p.schedule.is24x7 ) {
|
||||
typeSchedule = 'OnSchedule';
|
||||
}
|
||||
timeRunning(p);
|
||||
if (!IS_REAL) {
|
||||
console.log(`${p._id} - limit: ${limit} hs - actual duration: ${p.duration.hours} hs`);
|
||||
|
||||
let stoppedPeriod = p._scenarioDoc.allowedInnactiveDays || 20;
|
||||
let stoppedPeriodExternal = Math.ceil(stoppedPeriod/2);
|
||||
|
||||
var limit;
|
||||
if ( type === "warning" ) {
|
||||
limit = p.isExternalAccess? 24*(stoppedPeriodExternal-WARNING_DAYS) : 24*(stoppedPeriod-WARNING_DAYS);
|
||||
} else if ( type === "exec" ) {
|
||||
limit = p.isExternalAccess? (24*stoppedPeriodExternal) : (24*stoppedPeriod);
|
||||
}
|
||||
if ( !IS_REAL ) {
|
||||
console.log(`${p._id} (${typeSchedule}) - limit: ${limit} hs - actual duration: ${p.duration.hours} hs`);
|
||||
}
|
||||
if ( p.duration && p.duration.hours >= limit) {
|
||||
await cb(p);
|
||||
await cb(p, limit, typeSchedule);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const doSendEmailDestroyWarning = async function(p) {
|
||||
const doSendEmailDestroyWarning = async function(p, limit, typeSchedule) {
|
||||
if ( p.pendingNextAction === 'destroy') {
|
||||
console.log(`Warning email Destroy already sent. Wait for pending action to complete.`);
|
||||
} else {
|
||||
let msg = `Send warning DESTROY email - ${p.user.displayName} (${p.user.upn}) about provision '${p._scenario.title}' (${p._id}) being 'Inactive' more than ${STOPPED_LIMIT_HOURS_WARNING} hours (exactly ${p.duration.complete})`;
|
||||
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._scenario);
|
||||
await db.notification.add({ provision: p._id.toString(), type: 'warningDestroy', message: msg });
|
||||
await db.notification.add({ provision: p._id.toString(), type: 'warningDestroy', message: msg });
|
||||
await sendEmail.sendWillDestroyIn24(p, p._scenarioDoc, Math.floor(limit/24), WARNING_DAYS);
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const doDestroy = async function(p) {
|
||||
const doDestroy = async function(p, limit, typeSchedule) {
|
||||
try {
|
||||
let msg = `Provision destroyed - ${p.user.displayName} (${p.user.upn}) about provision '${p._scenario.title}' (${p._id}) being 'Inactive' more than ${STOPPED_LIMIT_HOURS_STOP} hours (exactly ${p.duration.complete})`
|
||||
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 ) {
|
||||
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 });
|
||||
await db.notification.add({ provision: p._id.toString(), type: 'destroy', message: msg });
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("doDestroy Error", error);
|
||||
|
||||
59
qmi-cloud-cli/jobs/error5.js
Normal 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();
|
||||
61
qmi-cloud-cli/jobs/move_destroyed.js
Normal 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();
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
|
||||
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]; //Days
|
||||
const RUNNING_LIMIT_HOURS_WARNING = 24*(RUNNING_PERIOD-1);
|
||||
const RUNNING_LIMIT_HOURS_STOP = 24*RUNNING_PERIOD;
|
||||
const IS_REAL = myArgs[1] !== 'test';
|
||||
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 timeRunning(p) {
|
||||
function timeRunningIs24x7(p) {
|
||||
let runningFromTime = p.runningFrom? new Date(p.runningFrom).getTime() : new Date(p.created).getTime();
|
||||
let totalRunningFromTime;
|
||||
if (p.statusVms !== 'Stopped' && p.statusVms !== 'Starting' && !p.isDestroyed) {
|
||||
totalRunningFromTime = Math.abs(new Date().getTime() - runningFromTime);
|
||||
}
|
||||
let totalRunningFromTime = Math.abs(new Date().getTime() - runningFromTime);
|
||||
let duration = moment.duration(totalRunningFromTime);
|
||||
p.duration = {
|
||||
hours: Math.floor(duration.asHours()),
|
||||
@@ -29,6 +26,30 @@ function timeRunning(p) {
|
||||
};
|
||||
}
|
||||
|
||||
function timeRunningOnSchedule(p) {
|
||||
let runningFromTime = p.runningFrom? new Date(p.runningFrom).getTime() : new Date(p.created).getTime();
|
||||
let totalRunningTime = p.timeRunning*1000*60;
|
||||
let now = new Date();
|
||||
totalRunningTime = totalRunningTime + Math.abs(now.getTime() - runningFromTime);
|
||||
let duration = moment.duration(totalRunningTime);
|
||||
p.duration = {
|
||||
hours: Math.floor(duration.asHours()),
|
||||
complete: Math.floor(duration.asDays()) +"d "+duration.hours()+"h "+duration.minutes()
|
||||
};
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -36,18 +57,18 @@ async function asyncForEach(array, callback) {
|
||||
}
|
||||
|
||||
async function init(type) {
|
||||
var limit, cb;
|
||||
var cb;
|
||||
var filter = {
|
||||
"isDestroyed":false,
|
||||
"isDestroyed": false,
|
||||
"isDeleted": false,
|
||||
"statusVms": "Running"
|
||||
"statusVms": "Running",
|
||||
"vmImage": { "$exists": true },
|
||||
"vmImage.vm1": { "$exists": true }
|
||||
};
|
||||
if ( type === "warning" ) {
|
||||
limit = RUNNING_LIMIT_HOURS_WARNING;
|
||||
filter.pendingNextAction = {$ne: "stopVms"};
|
||||
filter.pendingNextAction = { $ne: "stopVms" };
|
||||
cb = doSendEmailWarning;
|
||||
} else if ( type === "exec" ) {
|
||||
limit = RUNNING_LIMIT_HOURS_STOP;
|
||||
filter.pendingNextAction = "stopVms";
|
||||
cb = doStop;
|
||||
} else {
|
||||
@@ -56,46 +77,69 @@ async function init(type) {
|
||||
}
|
||||
|
||||
let provisions = await db.provision.get(filter);
|
||||
let scenarios = await db.scenario.get();
|
||||
|
||||
await asyncForEach(provisions.results, async function(p) {
|
||||
var _scenario = scenarios.results.filter(s=>{
|
||||
return s.name === p.scenario
|
||||
});
|
||||
if ( _scenario.length ){
|
||||
p._scenario = _scenario[0];
|
||||
//Only if 24x7
|
||||
let typeSchedule = "24x7";
|
||||
let periodDays = p._scenarioDoc.allowed24x7RunningDays;
|
||||
if ( !p.schedule || p.schedule.is24x7 ) {
|
||||
// 24x7
|
||||
timeRunningIs24x7(p);
|
||||
periodDays = p._scenarioDoc.allowed24x7RunningDays;
|
||||
} else if ( p.schedule && !p.schedule.is24x7 ) {
|
||||
// OnSchedule
|
||||
timeRunningOnSchedule2(p);
|
||||
typeSchedule = 'OnSchedule';
|
||||
periodDays = p._scenarioDoc.allowedOnScheduleRunningDays;
|
||||
}
|
||||
let limit;
|
||||
if ( type === "warning" ) {
|
||||
limit = 24*(periodDays-WARNING_DAYS);
|
||||
} else if ( type === "exec" ) {
|
||||
limit = 24*periodDays;
|
||||
}
|
||||
timeRunning(p);
|
||||
if (!IS_REAL) {
|
||||
console.log(`${p._id} - limit: ${limit} hs - actual duration: ${p.duration.hours} hs`);
|
||||
console.log(`${p._id} - (${typeSchedule}) - limit: ${limit} hs - actual duration: ${p.duration.hours} hs`);
|
||||
}
|
||||
if ( p.duration && p.duration.hours >= limit) {
|
||||
await cb(p);
|
||||
await cb(p, limit, typeSchedule);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const doSendEmailWarning = async function(p) {
|
||||
const doSendEmailWarning = async function(p, limit, typeSchedule) {
|
||||
if ( p.pendingNextAction === 'stopVms') {
|
||||
console.log(`Warning email Stop already sent. Wait for pending action to complete.`);
|
||||
} else {
|
||||
let msg = `Send warning STOP email - ${p.user.displayName} (${p.user.upn}) about provision '${p._scenario.title}' (${p._id}) being 'Running' more than ${RUNNING_LIMIT_HOURS_WARNING} hours (exactly ${p.duration.complete})`;
|
||||
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._scenario);
|
||||
await db.notification.add({ provision: p._id.toString(), type: 'warningStop', message: msg });
|
||||
await db.notification.add({ provision: p._id.toString(), type: 'warningStop', message: msg });
|
||||
await sendEmail.sendWillStopIn24(p, p._scenarioDoc, Math.floor(limit/24), WARNING_DAYS);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const doStop = async function(p) {
|
||||
const doStop = async function(p, limit, typeSchedule) {
|
||||
try {
|
||||
let msg = `VMs stopped - ${p.user.displayName} (${p.user.upn}) about provision '${p._scenario.title}' (${p._id}) being 'Running' more than ${RUNNING_LIMIT_HOURS_STOP} hours (exactly ${p.duration.complete})`
|
||||
let msg = `VMs stopped - ${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 ) {
|
||||
await azurecli.deallocate(p, true);
|
||||
if ( IS_REAL ) {
|
||||
if (p.schedule){
|
||||
//Disable Divvy
|
||||
await db.schedule.update(p.schedule._id, {"isStartupTimeEnable": false});
|
||||
await cli.updateVmsTags(p._id, {
|
||||
"24x7": false,
|
||||
"StartupTime": false,
|
||||
"ShutdownTime": false
|
||||
});
|
||||
}
|
||||
//Stop VMs indefinitely
|
||||
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 });
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
{
|
||||
"name": "qmi-cloud-cli",
|
||||
"version": "1.1.3",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 -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 "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-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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
14
qmi-cloud-cli/shell-utils/checkerror.sh
Executable 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
|
||||
@@ -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
|
||||
@@ -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"
|
||||
|
||||
130
qmi-cloud-common/awscli.js
Normal file
@@ -0,0 +1,130 @@
|
||||
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;
|
||||
}
|
||||
|
||||
function deallocate(provision, isSendEmailAfter) {
|
||||
|
||||
let rgName = _getRgName(provision);
|
||||
let region = _getRegion(provision);
|
||||
|
||||
console.log("AWSCLI# Stopping EC2s for resource group: "+rgName);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
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);
|
||||
await db.provision.update(provision._id.toString(), {"statusVms": "Running" });
|
||||
reject(err);
|
||||
} else if (data && data.Reservations && data.Reservations.length) {
|
||||
|
||||
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(err1, data) {
|
||||
if (err1) {
|
||||
console.log("AWSCLI# ERROR stopping EC2s: "+rgName, err1.stack);
|
||||
await db.provision.update(provision._id.toString(), {"statusVms": "Running" });
|
||||
reject(err1);
|
||||
} else {
|
||||
console.log("AWSCLI# Ec2s stopped!");
|
||||
await utils.afterStopVms( provision, isSendEmailAfter );
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("AWSCLI# No Ec2 Instances found: "+rgName);
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function start(provision) {
|
||||
|
||||
let rgName = _getRgName(provision);
|
||||
let region = _getRegion(provision);
|
||||
|
||||
console.log("AWSCLI# Stopping EC2s for resource group: "+rgName);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
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);
|
||||
await db.provision.update(provision._id.toString(), {"statusVms": "Stopped" });
|
||||
reject(err);
|
||||
} else if (data && data.Reservations && data.Reservations.length) {
|
||||
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(err1, data) {
|
||||
if (err1) {
|
||||
console.log("AWSCLI# ERROR starting EC2s: "+rgName, err1.stack);
|
||||
await db.provision.update(provision._id.toString(), {"statusVms": "Stopped" });
|
||||
reject(err1);
|
||||
} else {
|
||||
console.log("AWSCLI# Ec2s started!");
|
||||
await utils.afterStartVms( provision );
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log("AWSCLI# No Ec2 Instances found: "+rgName);
|
||||
resolve(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
module.exports.deallocate = deallocate;
|
||||
module.exports.start = start;
|
||||
@@ -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 });
|
||||
console.log("Azure CLI authenticated", credentials);
|
||||
return new computeManagementClient(credentials, id);
|
||||
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++) {
|
||||
@@ -30,65 +43,94 @@ async function asyncForEach(array, callback) {
|
||||
}
|
||||
|
||||
async function deallocate(provision, isSendEmailAfter ) {
|
||||
|
||||
let rgName = _getRgName(provision);
|
||||
console.log("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"});
|
||||
}
|
||||
await asyncForEach(finalResult, async function(vm) {
|
||||
await computeClient.virtualMachines.deallocate(rgName, vm.name);
|
||||
});
|
||||
console.log("AzureCLI# Deallocating VMs for resource group: "+rgName);
|
||||
|
||||
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._scenario) {
|
||||
await sendEmail.sendVMsStopped(provision, provision._scenario);
|
||||
try {
|
||||
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"});
|
||||
}
|
||||
|
||||
await asyncForEach(finalResult, async function(vm) {
|
||||
await computeClient.virtualMachines.deallocate(rgName, vm.name);
|
||||
});
|
||||
|
||||
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" });
|
||||
}
|
||||
|
||||
console.log("All VMs DEALLOCATED for resource group: "+rgName);
|
||||
}
|
||||
|
||||
async function start(provision){
|
||||
|
||||
|
||||
let rgName = _getRgName(provision);
|
||||
console.log("Starting VMs for resource group: "+rgName);
|
||||
console.log("AzureCLI# Starting VMs for resource group: "+rgName);
|
||||
|
||||
var computeClient = await _getClient(provision.scenario);
|
||||
let finalResult = await computeClient.virtualMachines.list(rgName);
|
||||
try {
|
||||
|
||||
if ( finalResult && finalResult.length > 0 ) {
|
||||
db.provision.update(provision._id, {"statusVms": "Starting"});
|
||||
var computeClient = await _getClient(provision.scenario);
|
||||
let finalResult = await computeClient.virtualMachines.list(rgName);
|
||||
|
||||
if ( finalResult && finalResult.length > 0 ) {
|
||||
db.provision.update(provision._id, {"statusVms": "Starting"});
|
||||
}
|
||||
|
||||
await asyncForEach(finalResult, async function(vm) {
|
||||
await computeClient.virtualMachines.start(rgName, vm.name);
|
||||
});
|
||||
|
||||
await utils.afterStartVms( provision );
|
||||
console.log("AzureCLI# All VMs RUNNING for resource group: "+rgName);
|
||||
|
||||
} catch ( error ) {
|
||||
console.log("AzureCLI# ERROR starting VMs: "+rgName, error);
|
||||
await db.provision.update(provision._id.toString(), {"statusVms": "Stopped" });
|
||||
}
|
||||
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("All VMs RUNNING for resource group: "+rgName);
|
||||
|
||||
|
||||
}
|
||||
|
||||
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(provision, tagsEdit) {
|
||||
async function updateVmsTags(provId, tagsEdit) {
|
||||
|
||||
if ( !tagsEdit ){ return {} ; }
|
||||
let provision = await db.provision.getById(provId);
|
||||
if ( !provision ){ return };
|
||||
|
||||
let rgName = _getRgName(provision);
|
||||
console.log("Updating TAGS in VMs for resource group: "+rgName);
|
||||
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 = [];
|
||||
@@ -104,6 +146,8 @@ async function updateVmsTags(provision, 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];
|
||||
@@ -111,11 +155,25 @@ async function updateVmsTags(provision, tagsEdit) {
|
||||
toAdd.forEach(t=>{
|
||||
tags[t.key] = t.value;
|
||||
});
|
||||
result[vm.name] = tags;
|
||||
computeClient.virtualMachines.update(rgName, vm.name, {"tags": tags} );
|
||||
tags["ProvId"] = provision._id.toString();
|
||||
result[vm.name] = 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 {
|
||||
@@ -123,8 +181,53 @@ async function updateVmsTags(provision, 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.updateVmsTags = updateVmsTags;
|
||||
module.exports.getAllVmsNext = getAllVmsNext;
|
||||
module.exports.updateVmsTags = updateVmsTags;
|
||||
module.exports.getDNSRecords = getDNSRecords;
|
||||
module.exports.createDNSRecord = createDNSRecord;
|
||||
module.exports.deleteDNSRecord = deleteDNSRecord
|
||||
|
||||
85
qmi-cloud-common/barracuda.js
Normal 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;
|
||||
47
qmi-cloud-common/cli.js
Normal file
@@ -0,0 +1,47 @@
|
||||
const awscli = require("./awscli");
|
||||
const azurecli = require("./azurecli");
|
||||
const db = require("./mongo");
|
||||
|
||||
|
||||
async function deallocate(provId, isSendEmailAfter ) {
|
||||
try {
|
||||
let provision = await db.provision.getById(provId);
|
||||
if ( !provision ) return;
|
||||
|
||||
if (provision.scenario === 'azqmi-fort'){
|
||||
return awscli.deallocate(provision, isSendEmailAfter);
|
||||
} else {
|
||||
return azurecli.deallocate(provision, isSendEmailAfter);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("CLI# ERROR stopping VMs", err);
|
||||
}
|
||||
}
|
||||
|
||||
async function start(provId){
|
||||
|
||||
try {
|
||||
let provision = await db.provision.getById(provId);
|
||||
if ( !provision ) return;
|
||||
|
||||
if (provision.scenario === 'azqmi-fort'){
|
||||
return awscli.start(provision);
|
||||
} else {
|
||||
return azurecli.start(provision);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log("CLI# ERROR starting VMs", err);
|
||||
}
|
||||
}
|
||||
|
||||
async function updateVmsTags(provId, tagsEdit) {
|
||||
try {
|
||||
return azurecli.updateVmsTags(provId, tagsEdit);
|
||||
} catch (err) {
|
||||
console.log("CLI# ERROR updateVmsTags", err);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.deallocate = deallocate;
|
||||
module.exports.start = start;
|
||||
module.exports.updateVmsTags = updateVmsTags;
|
||||
4
qmi-cloud-common/config.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
DOCKERIMAGE_TERRAFORM: "qlikgear/terraform:0.13.4",
|
||||
PROVISION_VERSION: 20201124
|
||||
}
|
||||
@@ -8,6 +8,9 @@ const schema = new mongoose.Schema({
|
||||
user: {
|
||||
type: mongoose.Types.ObjectId, ref: 'User'
|
||||
},
|
||||
description: {
|
||||
type: String
|
||||
},
|
||||
created: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
|
||||
31
qmi-cloud-common/models/Event.js
Normal 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)
|
||||
@@ -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
|
||||
@@ -73,8 +89,23 @@ const provisionSchema = new mongoose.Schema({
|
||||
pendingNextAction: {
|
||||
type: String
|
||||
},
|
||||
autoShutdown: {
|
||||
type: mongoose.Types.ObjectId, ref: 'ProvisionAutoShutdown'
|
||||
schedule: {
|
||||
type: mongoose.Types.ObjectId,
|
||||
ref: 'Schedule'
|
||||
},
|
||||
deployOpts: {
|
||||
type: mongoose.Types.ObjectId,
|
||||
ref: "Subscription"
|
||||
},
|
||||
terraformImage: {
|
||||
type: String
|
||||
},
|
||||
version: {
|
||||
type: Number
|
||||
},
|
||||
parent: {
|
||||
type: mongoose.Types.ObjectId,
|
||||
ref: 'Provision'
|
||||
}
|
||||
},{
|
||||
toObject: {virtuals:true},
|
||||
|
||||
@@ -31,7 +31,7 @@ const scenarioSchema = new mongoose.Schema({
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isExterrequirednal: {
|
||||
isExternal: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
@@ -39,6 +39,9 @@ const scenarioSchema = new mongoose.Schema({
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
numSimultaneousProvisions: {
|
||||
type: Number
|
||||
},
|
||||
isDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
@@ -47,13 +50,47 @@ const scenarioSchema = new mongoose.Schema({
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
gitBranch: {
|
||||
type: String
|
||||
},
|
||||
description: String,
|
||||
availableProductVersions: Array,
|
||||
productVersionDefault: String,
|
||||
newImageName: String, //For Gen scenarios
|
||||
subscription: {
|
||||
type: mongoose.Types.ObjectId, ref: 'Subscription',
|
||||
type: mongoose.Types.ObjectId,
|
||||
ref: 'Subscription'
|
||||
},
|
||||
deployOpts: [{
|
||||
type: mongoose.Types.ObjectId,
|
||||
ref: 'Subscription',
|
||||
required: true
|
||||
}],
|
||||
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
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -9,12 +9,19 @@ const sc = new mongoose.Schema({
|
||||
required: true,
|
||||
default: true
|
||||
},
|
||||
isStartupTimeEnable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
localeShutdownTime: {
|
||||
type: String
|
||||
},
|
||||
localeStartupTime: {
|
||||
type: String
|
||||
},
|
||||
localTimezone: {
|
||||
type: String
|
||||
},
|
||||
utcTagShutdownTime: {
|
||||
type: String
|
||||
},
|
||||
@@ -31,4 +38,4 @@ const sc = new mongoose.Schema({
|
||||
});
|
||||
|
||||
|
||||
module.exports = mongoose.model('ProvisionAutoShutdown', sc)
|
||||
module.exports = mongoose.model('Schedule', sc)
|
||||
36
qmi-cloud-common/models/SharedProvision.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const mongoose = require('mongoose')
|
||||
mongoose.set('useFindAndModify', false);
|
||||
//mongoose.set('debug', true)
|
||||
const schema = new mongoose.Schema({
|
||||
created: {
|
||||
type: Date,
|
||||
default: Date.now,
|
||||
index : true
|
||||
},
|
||||
updated: {
|
||||
type: Date,
|
||||
default: Date.now
|
||||
},
|
||||
user: {
|
||||
type: mongoose.Types.ObjectId,
|
||||
ref: 'User',
|
||||
index: true
|
||||
},
|
||||
provision: {
|
||||
type: mongoose.Types.ObjectId,
|
||||
ref: 'Provision',
|
||||
index: true
|
||||
},
|
||||
sharedWithUser: {
|
||||
type: mongoose.Types.ObjectId,
|
||||
ref: 'User',
|
||||
index: true
|
||||
},
|
||||
canManage: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
module.exports = mongoose.model('sharedProvision', schema);
|
||||
@@ -30,6 +30,15 @@ const subSchema = new mongoose.Schema({
|
||||
},
|
||||
appGwSubnetId: {
|
||||
type: String
|
||||
},
|
||||
location: {
|
||||
type: String
|
||||
},
|
||||
wafPolicyName: {
|
||||
type: String
|
||||
},
|
||||
wafPolicyRgName: {
|
||||
type: String
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -26,6 +26,12 @@ const userSchema = new mongoose.Schema({
|
||||
lastLogin: {
|
||||
type: Date
|
||||
},
|
||||
qcsUserId: {
|
||||
type: String
|
||||
},
|
||||
qcsUserSubject: {
|
||||
type: String
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const options = {
|
||||
useUnifiedTopology: true
|
||||
};
|
||||
|
||||
console.log("--- MongoDB connecting...");
|
||||
console.log("--- MongoDB connecting... ", process.env.MONGO_URI);
|
||||
|
||||
// Connect to DB
|
||||
mongoose.connect(process.env.MONGO_URI, options);
|
||||
@@ -34,7 +34,7 @@ mongoose.connection.on('error', (err) => {
|
||||
|
||||
// Get Data Models
|
||||
const Scenario = require('./models/Scenario');
|
||||
const ProvisionAutoShutdown = require('./models/ProvisionAutoShutdown');
|
||||
const Schedule = require('./models/Schedule');
|
||||
const Provision = require('./models/Provision');
|
||||
const Destroy = require('./models/Destroy');
|
||||
const User = require('./models/User');
|
||||
@@ -42,6 +42,8 @@ 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 SharedProvision = require('./models/SharedProvision');
|
||||
|
||||
|
||||
const getNewCountExtend = function(provision) {
|
||||
@@ -64,13 +66,83 @@ const getNewTimeRunning = function (provision) {
|
||||
};
|
||||
|
||||
|
||||
|
||||
const get = async (model, filter, select, skip, limit, populates, reply) => {
|
||||
const getPage = async ( model, filter, page, populates, select ) => {
|
||||
var sort = {};
|
||||
var modelAttributes = Object.keys(model.schema.tree);
|
||||
if ( modelAttributes.indexOf("created") !== -1) {
|
||||
sort = {created: -1};
|
||||
}
|
||||
|
||||
try {
|
||||
var exec = model.find(filter, select).sort(sort);
|
||||
var totalDocs = await model.countDocuments(filter);
|
||||
|
||||
var isPage = false;
|
||||
if ( page && page.page > 0 && page.size !== undefined ) {
|
||||
var skip = 0;
|
||||
skip = (page.page - 1) * page.size;
|
||||
exec = exec.skip(skip);
|
||||
exec = exec.limit(page.size);
|
||||
isPage = true;
|
||||
}
|
||||
|
||||
if ( populates ) {
|
||||
populates = JSON.parse(populates);
|
||||
populates.forEach(p=> {
|
||||
exec = exec.populate(p);
|
||||
});
|
||||
} else {
|
||||
if ( model === Provision ) {
|
||||
exec = exec.populate({ path: 'user', select: 'displayName upn'}).populate({path:'destroy', select: "-user -jobId"}).populate({path:'_scenarioDoc', select: "-availableProductVersions -updated -created"}).populate({path: "schedule"}).populate('deployOpts');
|
||||
}
|
||||
if ( model === SharedProvision ) {
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||
}
|
||||
|
||||
if ( model === ApiKey ) {
|
||||
exec = exec.populate('user');
|
||||
}
|
||||
|
||||
if ( model = Scenario ) {
|
||||
exec = exec.populate('subscription').populate('deployOpts');
|
||||
}
|
||||
}
|
||||
|
||||
const entity = await exec;
|
||||
|
||||
var out = {
|
||||
total: totalDocs,
|
||||
count: entity.length,
|
||||
results: entity
|
||||
}
|
||||
if ( isPage && (page.page * page.size < totalDocs)) {
|
||||
out.next = {
|
||||
page: page.page + 1,
|
||||
size: page.size
|
||||
};
|
||||
}
|
||||
|
||||
return out;
|
||||
|
||||
} catch (err) {
|
||||
throw boom.boomify(err)
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
try {
|
||||
var exec = model.find(filter, select).sort(sort);
|
||||
var totalDocs = await model.countDocuments(filter);
|
||||
@@ -90,7 +162,19 @@ const get = async (model, filter, select, skip, limit, populates, reply) => {
|
||||
});
|
||||
} else {
|
||||
if ( model === Provision ) {
|
||||
exec = exec.populate({ path: 'user', select: 'displayName upn'}).populate({path:'destroy', select: "-user -jobId"}).populate({path:'_scenarioDoc', select: "-availableProductVersions -updated -created"}).populate({path: "autoShutdown"});
|
||||
exec = exec.populate({ path: 'user', select: 'displayName upn'}).populate({path:'destroy', select: "-user -jobId"}).populate({path:'_scenarioDoc', select: "-availableProductVersions -updated -created"}).populate({path: "schedule"}).populate('deployOpts');
|
||||
}
|
||||
|
||||
if ( model === SharedProvision ) {
|
||||
exec = exec.populate({path:'provision', populate: [{
|
||||
path: 'schedule',
|
||||
},{
|
||||
path: '_scenarioDoc',
|
||||
select: "-availableProductVersions -updated -created"
|
||||
},{
|
||||
path: 'destroy',
|
||||
select: "-user -jobId"
|
||||
}]}).populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||
}
|
||||
|
||||
if ( model === ApiKey ) {
|
||||
@@ -98,7 +182,7 @@ const get = async (model, filter, select, skip, limit, populates, reply) => {
|
||||
}
|
||||
|
||||
if ( model = Scenario ) {
|
||||
exec = exec.populate('subscription');
|
||||
exec = exec.populate('subscription').populate('deployOpts').populate('allowedUsers');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,13 +207,16 @@ const getById = async (model, id, reply) => {
|
||||
try {
|
||||
var exec = model.findById(id);
|
||||
if ( model === Provision ) {
|
||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("autoShutdown");
|
||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
||||
}
|
||||
if ( model === SharedProvision ) {
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||
}
|
||||
if ( model === ApiKey ) {
|
||||
exec = exec.populate('user');
|
||||
}
|
||||
if ( model = Scenario ) {
|
||||
exec = exec.populate('subscription');
|
||||
exec = exec.populate('subscription').populate('deployOpts').populate('allowedUsers');
|
||||
}
|
||||
const entity = await exec;
|
||||
return entity;
|
||||
@@ -142,13 +229,16 @@ const getOne = async (model, filter, reply) => {
|
||||
try {
|
||||
var exec = model.findOne(filter);
|
||||
if ( model === Provision ) {
|
||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("autoShutdown");
|
||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
||||
}
|
||||
if ( model === SharedProvision ) {
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||
}
|
||||
if ( model === ApiKey ) {
|
||||
exec = exec.populate('user');
|
||||
}
|
||||
if ( model = Scenario ) {
|
||||
exec = exec.populate('subscription');
|
||||
exec = exec.populate('subscription').populate('deployOpts').populate('allowedUsers');
|
||||
}
|
||||
const entity = await exec;
|
||||
return entity;
|
||||
@@ -170,16 +260,19 @@ const update = async (model, id, body, reply) => {
|
||||
try {
|
||||
const { ...updateData } = body;
|
||||
updateData.updated = new Date();
|
||||
console.log("UPDATE", id, updateData);
|
||||
//console.log("UPDATE", id, updateData);
|
||||
var exec = model.findByIdAndUpdate(id, updateData, { new: true });
|
||||
if ( model === Provision ) {
|
||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("autoShutdown");
|
||||
exec = exec.populate('user').populate('destroy').populate('_scenarioDoc').populate("schedule").populate('deployOpts');
|
||||
}
|
||||
if ( model === SharedProvision ) {
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||
}
|
||||
if ( model === ApiKey ) {
|
||||
exec = exec.populate('user');
|
||||
}
|
||||
if ( model = Scenario ) {
|
||||
exec = exec.populate('subscription');
|
||||
exec = exec.populate('subscription').populate('deployOpts');
|
||||
}
|
||||
const update = await exec;
|
||||
return update;
|
||||
@@ -188,10 +281,54 @@ 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 === SharedProvision ) {
|
||||
exec = exec.populate('provision').populate({path: 'sharedWithUser', select: 'displayName upn'}).populate({path: 'user', select: 'displayName upn'});
|
||||
}
|
||||
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)
|
||||
return entity;
|
||||
return await model.findByIdAndRemove(id);
|
||||
} catch (err) {
|
||||
throw boom.boomify(err)
|
||||
}
|
||||
}
|
||||
|
||||
const delMany = async(model, filter, reply) => {
|
||||
try {
|
||||
return await model.deleteMany(filter);
|
||||
} catch (err) {
|
||||
throw boom.boomify(err)
|
||||
}
|
||||
}
|
||||
|
||||
const count = async (model, filter, reply) => {
|
||||
try {
|
||||
var totalDocs = await model.countDocuments(filter);
|
||||
return totalDocs;
|
||||
} catch (err) {
|
||||
throw boom.boomify(err)
|
||||
}
|
||||
@@ -202,6 +339,9 @@ function _m(model) {
|
||||
get: async (filter, select, skip, limit, populates, reply) => {
|
||||
return get(model, filter, select, skip, limit, populates, reply);
|
||||
},
|
||||
getPage: async (filter, page, populates, select, reply) => {
|
||||
return getPage(model, filter, page, populates, select, reply);
|
||||
},
|
||||
getById: async (id, reply) => {
|
||||
return getById(model, id, reply);
|
||||
},
|
||||
@@ -214,15 +354,24 @@ 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);
|
||||
},
|
||||
delMany: async(filter, reply) => {
|
||||
return delMany(model, filter, reply);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
provisionAutoShutdown: _m(ProvisionAutoShutdown),
|
||||
schedule: _m(Schedule),
|
||||
provision: _m(Provision),
|
||||
destroy: _m(Destroy),
|
||||
scenario: _m(Scenario),
|
||||
@@ -230,7 +379,9 @@ module.exports = {
|
||||
apiKey: _m(ApiKey),
|
||||
notification: _m(Notification),
|
||||
subscription: _m(Subscription),
|
||||
event: _m(Event),
|
||||
user: _m(User),
|
||||
sharedProvision: _m(SharedProvision),
|
||||
utils: {
|
||||
getNewTimeRunning: getNewTimeRunning,
|
||||
getNewCountExtend: getNewCountExtend
|
||||
@@ -241,10 +392,13 @@ module.exports = {
|
||||
Destroy: Destroy,
|
||||
Scenario: Scenario,
|
||||
User: User,
|
||||
Schedule: Schedule,
|
||||
VmType: VmType,
|
||||
Notification: Notification,
|
||||
ApiKey: ApiKey,
|
||||
Subscription: Subscription
|
||||
Subscription: Subscription,
|
||||
Event: Event,
|
||||
SharedProvision: SharedProvision
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
1315
qmi-cloud-common/package-lock.json
generated
Normal file
@@ -1,12 +1,17 @@
|
||||
{
|
||||
"name": "qmi-cloud-common",
|
||||
"version": "1.1.4",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,24 +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(`Job ${job.id} completed! Result`, result);
|
||||
//console.log(`Queues# Job ${job.id} completed! Result`, result);
|
||||
console.log(`Queue ${key}# Job ${job.id} completed!`);
|
||||
});
|
||||
|
||||
|
||||
@@ -30,13 +33,13 @@ for (let key in queues) {
|
||||
});
|
||||
|
||||
queues[key].on('waiting', function(jobId){
|
||||
console.log(`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(`Job ${job.id} is in active`);
|
||||
console.log(`Queue ${key}# Job ${job.id} is now active`);
|
||||
});
|
||||
|
||||
queues[key].on('stalled', function(job){
|
||||
@@ -46,12 +49,12 @@ for (let key in queues) {
|
||||
|
||||
queues[key].on('progress', function(job, progress){
|
||||
// A job's progress was updated!
|
||||
console.log(`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(`Job ${job.id} has failed:`, err);
|
||||
console.log(`Queue ${key}# Job ${job.id} has failed:`, err);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
278
qmi-cloud-common/send-email copy.js
Normal 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;
|
||||
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
'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 RUNNING_PERIOD = 4;
|
||||
const RUNNING_PERIOD_WARNING_DAYS = 1;
|
||||
const STOP_PERIOD = 20;
|
||||
const STOP_PERIOD_WARNING_DAYS = 2;
|
||||
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>`;
|
||||
|
||||
|
||||
const RUNNING_PERIOD_WARNING_HOURS = RUNNING_PERIOD_WARNING_DAYS*24;
|
||||
const STOP_PERIOD_WARNING_HOURS = STOP_PERIOD_WARNING_DAYS*24;
|
||||
|
||||
|
||||
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('Provision Email ('+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){
|
||||
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>
|
||||
@@ -57,14 +57,14 @@ function _getCommonDetails(provision, scenario){
|
||||
<span style="color:#404040">Scenario: </span> ${scenario.title}
|
||||
</div>
|
||||
<div>
|
||||
<span style="color:#404040">Description: </span> ${scenario.description}
|
||||
<span style="color:#404040">With external access: </span> ${externalAccess}
|
||||
</div>
|
||||
<div>
|
||||
<span style="color:#404040">ProvisionID: </span> ${provision._id}
|
||||
<span style="color:#404040">Description: </span> ${description}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function getHtmlScenarioDestroyIn24( provision, scenario) {
|
||||
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">
|
||||
@@ -72,18 +72,16 @@ function getHtmlScenarioDestroyIn24( provision, scenario) {
|
||||
<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 ${(STOP_PERIOD - STOP_PERIOD_WARNING_DAYS)} days</p>
|
||||
<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 ${STOP_PERIOD_WARNING_HOURS} hours.</p>
|
||||
<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 ${STOP_PERIOD_WARNING_HOURS} hours (from when this email was sent) as a grace period to get back at 'Running' status this provision.</p>
|
||||
<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>`;
|
||||
}
|
||||
@@ -103,17 +101,15 @@ 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>`;
|
||||
}
|
||||
|
||||
function getHtmlScenarioWillStopIn24( provision, scenario ) {
|
||||
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">
|
||||
@@ -121,21 +117,19 @@ function getHtmlScenarioWillStopIn24( provision, scenario ) {
|
||||
<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 ${(RUNNING_PERIOD - RUNNING_PERIOD_WARNING_DAYS)} days</p>
|
||||
<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 ${RUNNING_PERIOD_WARNING_HOURS} hours.</p>
|
||||
<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 ${RUNNING_PERIOD} extra days.</p>
|
||||
<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 ${RUNNING_PERIOD_WARNING_HOURS} hours (from when this email was sent) as a grace period to extend this scenario's <b style="color: #009845">Running</b> VMs for ${RUNNING_PERIOD} extra days.</p>
|
||||
<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>`;
|
||||
}
|
||||
@@ -169,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>`;
|
||||
@@ -190,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>`;
|
||||
}
|
||||
@@ -209,43 +206,41 @@ 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);
|
||||
|
||||
}
|
||||
|
||||
async function sendWillStopIn24( provision, scenario ) {
|
||||
async function sendWillStopIn24( provision, scenario, period, warningDays ) {
|
||||
|
||||
const htmlText = getHtmlScenarioWillStopIn24( provision, scenario);
|
||||
await _doSend(provision.user.upn, `QMI Cloud - VMs will stop in ${RUNNING_PERIOD_WARNING_HOURS} hours`, htmlText);
|
||||
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 ) {
|
||||
async function sendWillDestroyIn24( provision, scenario, period, warningDays ) {
|
||||
|
||||
const htmlText = getHtmlScenarioDestroyIn24( provision, scenario);
|
||||
await _doSend(provision.user.upn, `QMI Cloud - Provision will destroy in ${STOP_PERIOD_WARNING_HOURS} hours`, htmlText);
|
||||
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 ) {
|
||||
@@ -253,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;
|
||||
|
||||
|
||||
|
||||
7
qmi-cloud-common/test-sendemail.js
Normal file
@@ -0,0 +1,7 @@
|
||||
var sendEmail = require("./send-email");
|
||||
|
||||
function test(){
|
||||
|
||||
sendEmail._doSend("AOR@qlik.com", "Test subject", "Hi world");
|
||||
}
|
||||
test();
|
||||
84
qmi-cloud-common/utils.js
Normal file
@@ -0,0 +1,84 @@
|
||||
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);
|
||||
|
||||
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
@@ -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,8 +11,9 @@ 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 ./
|
||||
|
||||
|
||||
CMD ["node", "-r", "esm", "index.js"]
|
||||
@@ -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) {
|
||||
|
||||
@@ -15,15 +16,19 @@ module.exports = async function(job) {
|
||||
});
|
||||
|
||||
if ( !prov ) {
|
||||
console.log(`Error: Not found Provision object in Database (it should exist!), provisionId is: ${job.data.id}` );
|
||||
console.log(`ProcessorApply# 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"});
|
||||
}
|
||||
|
||||
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) {
|
||||
if ( res.statusCode === 1 ) {
|
||||
console.log("Error at INIT");
|
||||
console.log(`ProcessorApply# Error at Terraform INIT for provision (${idProv})`);
|
||||
return Promise.reject({"success": false, "error": "Error at Terraform Init", provStatus: "error_init"});
|
||||
} else {
|
||||
// TERRAFORM PLAN
|
||||
@@ -32,24 +37,52 @@ module.exports = async function(job) {
|
||||
} )
|
||||
.then( async function(res) {
|
||||
if ( res.statusCode === 1 ) {
|
||||
console.log("Error at PLAN");
|
||||
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 && prov.vmImage.vm1)? "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
|
||||
return tf.apply(prov);
|
||||
} ).then( async function(res) {
|
||||
if ( res.statusCode === 1 ) {
|
||||
console.log("Error at APPLY");
|
||||
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){
|
||||
return await db.provision.update(prov._id, {"outputs": 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);
|
||||
@@ -58,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("Provision: error", err);
|
||||
db.provision.update(prov._id, {"status": err.provStatus? err.provStatus : 'error'});
|
||||
sendEmail.sendError(prov, job.data._scenario);
|
||||
console.log("ProcessorApply# Provision: error", err);
|
||||
|
||||
var errormsg = err.provStatus? err.provStatus : 'error'
|
||||
db.provision.update(prov._id, {"status": errormsg});
|
||||
db.event.add({ user: prov.user._id, provision: prov._id, type: 'provision.'+errormsg });
|
||||
|
||||
if ( errormsg !== "aborted") {
|
||||
sendEmail.sendProvisionError(prov, job.data._scenario);
|
||||
}
|
||||
return Promise.reject({"success": false, "error": err});
|
||||
} );
|
||||
}
|
||||
@@ -13,9 +13,9 @@ const appgateway = function( provision, scenario ) {
|
||||
var provision_id = provision._id.toString();
|
||||
var processStream = fs.createWriteStream(provision.logFile, {flags:'a'});
|
||||
var name = 'qmi-azureps-appgw-'+provision_id;
|
||||
console.log(`AzurePS: will spin up container: ${name}`);
|
||||
console.log(`AzurePS# will spin up container: ${name}`);
|
||||
|
||||
return docker.run(DOCKERIMAGE, ['pwsh', 'appgw.ps1', "-ProvisionId", provision_id ], processStream, {
|
||||
return docker.run(DOCKERIMAGE, ['pwsh', 'appgw.ps1', "-ProvisionId", provision_id, "-Subscription", provision.deployOpts.subsId, "-PolicyName", provision.deployOpts.wafPolicyName , "-PolicyResourceGroup", provision.deployOpts.wafPolicyRgName ], processStream, {
|
||||
"name": name,
|
||||
"WorkingDir": "/myapp",
|
||||
"HostConfig": {
|
||||
@@ -27,10 +27,10 @@ const appgateway = function( provision, scenario ) {
|
||||
}).then(function(data) {
|
||||
var output = data[0];
|
||||
var container = data[1];
|
||||
console.log(`AzurePS: ${name} (${container.id}) has finished with code: ${output.StatusCode}`);
|
||||
console.log(`AzurePS# ${name} (${container.id}) has finished with code: ${output.StatusCode}`);
|
||||
return container.remove();
|
||||
}).then(function() {
|
||||
console.log(`AzurePS: ${name} removed!`);
|
||||
console.log(`AzurePS# ${name} removed!`);
|
||||
return Promise.resolve(provision);
|
||||
});
|
||||
|
||||
@@ -52,7 +52,7 @@ const createimage = function( provision, scenario ) {
|
||||
|
||||
let imageName = scenario.newImageName + "-" + new Date().getTime();
|
||||
|
||||
console.log(`AzurePS: will spin up container: ${name}`);
|
||||
console.log(`AzurePS# will spin up container: ${name}`);
|
||||
|
||||
return docker.run(DOCKERIMAGE, ['pwsh', 'createimage.ps1', "-rgName", rgName, "-imageName", imageName ], processStream, {
|
||||
"name": name,
|
||||
@@ -66,10 +66,10 @@ const createimage = function( provision, scenario ) {
|
||||
}).then(function(data) {
|
||||
var output = data[0];
|
||||
var container = data[1];
|
||||
console.log(`AzurePS: ${name} (${container.id}) has finished with code: ${output.StatusCode}`);
|
||||
console.log(`AzurePS# ${name} (${container.id}) has finished with code: ${output.StatusCode}`);
|
||||
return container.remove();
|
||||
}).then(function() {
|
||||
console.log(`AzurePS: ${name} removed!`);
|
||||
console.log(`AzurePS# ${name} removed!`);
|
||||
return Promise.resolve(provision);
|
||||
});
|
||||
|
||||
|
||||
@@ -12,14 +12,14 @@ const KUBE_PATH = path.join(PROJECT_PATH, 'logs', 'kube');
|
||||
const _done = function(data){
|
||||
let output = data[0];
|
||||
let container = data[1];
|
||||
console.log(`kubectl: (${container.id}) has finished with code: ${output.StatusCode}`);
|
||||
console.log(`kubectl# (${container.id}) has finished with code: ${output.StatusCode}`);
|
||||
return container.remove();
|
||||
};
|
||||
|
||||
const kubeconfig = function( provId ) {
|
||||
|
||||
fs.writeFileSync(`/var/www/app/logs/kube/config_${provId}`, kubeConfigContent);
|
||||
console.log(`kubectl: will spin up container`);
|
||||
console.log(`kubectl# will spin up container`);
|
||||
|
||||
return docker.run(DOCKERIMAGE, ['config', 'current-context', '--kubeconfig', `/app/config_${provId}` ], processStream, {
|
||||
//"name": initContName,
|
||||
@@ -35,7 +35,7 @@ const kubeconfig = function( provId ) {
|
||||
const apply = function( provId, kubeConfigContent ) {
|
||||
|
||||
fs.writeFileSync(`/logs/kube/${provId}.config`, kubeConfigContent);
|
||||
console.log(`kubectl: will spin up container`);
|
||||
console.log(`kubectl# will spin up container`);
|
||||
const yaml = path.join(PROJECT_PATH, 'az-tf-templates', 'azqmi-qseok', 'scripts', 'azure-sc.yaml');
|
||||
return docker.run(DOCKERIMAGE, ['apply','-f', '/yamlfile', '--kubeconfig', `/app/${provId}.config` ], process.stdout, {
|
||||
//"name": initContName,
|
||||
@@ -52,7 +52,7 @@ const apply = function( provId, kubeConfigContent ) {
|
||||
const getpod = function( provId, kubeConfigContent ) {
|
||||
|
||||
fs.writeFileSync(`/logs/kube/${provId}.config`, kubeConfigContent);
|
||||
console.log(`kubectl: will spin up container`);
|
||||
console.log(`kubectl# will spin up container`);
|
||||
|
||||
return docker.run(DOCKERIMAGE, ['get', 'pod', '--kubeconfig', `/app/${provId}.config` ], process.stdout, {
|
||||
//"name": initContName,
|
||||
@@ -68,9 +68,9 @@ const getpod = function( provId, kubeConfigContent ) {
|
||||
const getsvc = function( provId, kubeConfigContent ) {
|
||||
|
||||
fs.writeFileSync(`/logs/kube/config_${provId}`, kubeConfigContent);
|
||||
console.log(`kubectl: will spin up container`);
|
||||
console.log(`kubectl# will spin up container`);
|
||||
|
||||
console.log(`kubectl: will spin up container`);
|
||||
console.log(`kubectl# will spin up container`);
|
||||
return docker.run(DOCKERIMAGE, ['get', 'svc', '--kubeconfig', `/app/config_${provId}` ], process.stdout, {
|
||||
//"name": initContName,
|
||||
"WorkingDir": "/app",
|
||||
|
||||
@@ -3,6 +3,7 @@ const docker = new Docker({
|
||||
'socketPath': '/home/docker.sock'
|
||||
});
|
||||
const fs = require('fs');
|
||||
const config = require('qmi-cloud-common/config');
|
||||
const GIT_SCENARIOS = process.env.GIT_SCENARIOS;
|
||||
const GIT_TAG = process.env.GIT_TAG || "master";
|
||||
const DOCKERIMAGE = process.env.DOCKERIMAGE_TERRAFORM || "qlikgear/terraform:1.0.1";
|
||||
@@ -25,18 +26,42 @@ 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');
|
||||
exec.push(`prefix=${prefix}`);
|
||||
|
||||
if ( scenario.subscription && scenario.subscription.subsId ) {
|
||||
if ( provision.deployOpts && provision.deployOpts.subsId ) {
|
||||
exec.push('-var');
|
||||
exec.push(`subscription_id=${provision.deployOpts.subsId}`);
|
||||
}
|
||||
//Deprecated
|
||||
else if ( scenario && scenario.subscription && scenario.subscription.subsId ) {
|
||||
exec.push('-var');
|
||||
exec.push(`subscription_id=${scenario.subscription.subsId}`);
|
||||
}
|
||||
|
||||
if ( scenario.subscription && scenario.subscription.vnetExists ) {
|
||||
if ( provision.deployOpts ) {
|
||||
|
||||
if ( provision.deployOpts.location ) {
|
||||
exec.push('-var');
|
||||
exec.push(`location=${provision.deployOpts.location}`);
|
||||
}
|
||||
|
||||
if ( provision.deployOpts.vnetExists ) {
|
||||
exec.push('-var');
|
||||
exec.push(`subnet_id=${provision.deployOpts.subnetId}`);
|
||||
|
||||
if ( provision.isExternalAccess ) {
|
||||
exec.push('-var');
|
||||
exec.push(`app_gw_subnet=${provision.deployOpts.appGwSubnetId}`);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
//Deprecated
|
||||
else if ( scenario && scenario.subscription && scenario.subscription.vnetExists ) {
|
||||
exec.push('-var');
|
||||
exec.push(`subnet_id=${scenario.subscription.subnetId}`);
|
||||
|
||||
@@ -50,6 +75,16 @@ function _buildVarsExec( exec, provision, scenario ) {
|
||||
exec.push(`provision_id=${provision._id}`);
|
||||
exec.push('-var');
|
||||
exec.push(`user_id=${provision.user.displayName}`);
|
||||
|
||||
if ( provision.scenario.indexOf('azqmi-qdi') !== -1 && provision.version && provision.version >= config.PROVISION_VERSION ) {
|
||||
exec.push('-var');
|
||||
exec.push(`user_email=${provision.user.upn}`);
|
||||
}
|
||||
|
||||
if ( provision.scenario.indexOf('azqmi-synapse') !== -1 || provision.scenario.indexOf('azqmi-qdi') !== -1 ) {
|
||||
exec.push('-var');
|
||||
exec.push(`user_oid=${provision.user.oid}`);
|
||||
}
|
||||
|
||||
if (!provision.vmImage) {
|
||||
//Old way
|
||||
@@ -65,25 +100,37 @@ function _buildVarsExec( exec, provision, scenario ) {
|
||||
} else if ( provision.vmImage ) {
|
||||
//New way
|
||||
for ( let key in provision.vmImage ) {
|
||||
if ( provision.vmImage[key].nodeCount ) {
|
||||
exec.push('-var');
|
||||
exec.push(`agent_count_${key}=${provision[key].nodeCount}`);
|
||||
}
|
||||
|
||||
if ( !provision.vmImage[key].disabled ) {
|
||||
|
||||
if ( provision.vmImage[key].vmType ) {
|
||||
exec.push('-var');
|
||||
exec.push(`vm_type_${key}=${provision.vmImage[key].vmType}`);
|
||||
}
|
||||
if ( provision.vmImage[key].nodeCount ) {
|
||||
exec.push('-var');
|
||||
exec.push(`agent_count_${key}=${provision[key].nodeCount}`);
|
||||
}
|
||||
|
||||
if ( provision.vmImage[key].diskSizeGb ) {
|
||||
exec.push('-var');
|
||||
exec.push(`disk_size_gb_${key}=${provision.vmImage[key].diskSizeGb}`);
|
||||
}
|
||||
if ( provision.vmImage[key].vmType ) {
|
||||
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].version ) {
|
||||
exec.push('-var');
|
||||
exec.push(`image_reference_${key}=${provision.vmImage[key].version.image}`);
|
||||
}
|
||||
if ( provision.vmImage[key].diskSizeGb ) {
|
||||
exec.push('-var');
|
||||
exec.push(`disk_size_gb_${key}=${provision.vmImage[key].diskSizeGb}`);
|
||||
}
|
||||
|
||||
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}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,20 +139,20 @@ function _buildVarsExec( exec, provision, scenario ) {
|
||||
exec.push(`is_external_access=${provision.isExternalAccess}`);
|
||||
}
|
||||
|
||||
if ( provision.autoShutdown ) {
|
||||
if ( provision.autoShutdown.is24x7 === true ) {
|
||||
if ( provision.schedule ) {
|
||||
if ( provision.schedule.is24x7 === true ) {
|
||||
exec.push('-var');
|
||||
exec.push(`is_24x7=true`);
|
||||
} else if ( provision.autoShutdown.is24x7 === false ) {
|
||||
} else if ( provision.schedule.is24x7 === false ) {
|
||||
exec.push('-var');
|
||||
exec.push(`is_24x7=false`);
|
||||
if ( provision.autoShutdown.utcTagStartupTime ) {
|
||||
if ( provision.schedule.utcTagStartupTime && provision.schedule.isStartupTimeEnable ) {
|
||||
exec.push('-var');
|
||||
exec.push(`startupTime=${provision.autoShutdown.utcTagStartupTime}`);
|
||||
exec.push(`startupTime=${provision.schedule.utcTagStartupTime}`);
|
||||
}
|
||||
if ( provision.autoShutdown.utcTagShutdownTime ) {
|
||||
if ( provision.schedule.utcTagShutdownTime ) {
|
||||
exec.push('-var');
|
||||
exec.push(`shutdownTime=${provision.autoShutdown.utcTagShutdownTime}`);
|
||||
exec.push(`shutdownTime=${provision.schedule.utcTagShutdownTime}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -116,12 +163,17 @@ function _buildVarsExec( exec, provision, scenario ) {
|
||||
const init = function( provision ) {
|
||||
|
||||
const name = `qmi-tf-init-${provision._id}`;
|
||||
console.log(`Init: will spin up container: ${name}`);
|
||||
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}`];
|
||||
console.log('Init: exec: '+exec.join(" "));
|
||||
|
||||
return docker.run(DOCKERIMAGE, exec, processStream, {
|
||||
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,
|
||||
"WorkingDir": "/app",
|
||||
@@ -133,9 +185,9 @@ const init = function( provision ) {
|
||||
}).then(function(data) {
|
||||
var output = data[0];
|
||||
var container = data[1];
|
||||
console.log(`Init: ${name} (${container.id}) has finished with code: ${output.StatusCode}`);
|
||||
console.log(`Terraform# Init: ${name} (${container.id}) has finished with code: ${output.StatusCode}`);
|
||||
return container.remove().then(function(){
|
||||
console.log(`Init: ${name} removed!`);
|
||||
console.log(`Terraform# Init: ${name} removed!`);
|
||||
return output.StatusCode;
|
||||
});
|
||||
}).then(function(statusCode) {
|
||||
@@ -146,14 +198,15 @@ const init = function( provision ) {
|
||||
const plan = function( provision, scenario ) {
|
||||
|
||||
const name = `qmi-tf-plan-${provision._id}`;
|
||||
console.log(`Plan: will spin up container: ${name}`);
|
||||
console.log(`Terraform# Plan: will spin up container: ${name}`);
|
||||
var processStream = fs.createWriteStream(provision.logFile, {flags:'a'});
|
||||
//var processStream = process.stdout;
|
||||
var exec = ['terraform', 'plan', '-no-color', '-input=false', '-out=tfplan' ];
|
||||
exec = _buildVarsExec(exec, provision, scenario);
|
||||
console.log('Plan: exec: '+exec.join(" "));
|
||||
|
||||
return docker.run(DOCKERIMAGE, exec, processStream, {
|
||||
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,
|
||||
"WorkingDir": "/app",
|
||||
@@ -166,9 +219,9 @@ const plan = function( provision, scenario ) {
|
||||
}
|
||||
}).then(function(data){
|
||||
var container = data[1];
|
||||
console.log(`Plan: ${name} (${container.id}) has finished with code: ${data[0].StatusCode}`);
|
||||
console.log(`Terraform# Plan: ${name} (${container.id}) has finished with code: ${data[0].StatusCode}`);
|
||||
return container.remove().then(function(){
|
||||
console.log(`Plan: ${name} removed!`);
|
||||
console.log(`Terraform# Plan: ${name} removed!`);
|
||||
return data[0].StatusCode;
|
||||
});
|
||||
}).then(function(statusCode) {
|
||||
@@ -179,14 +232,15 @@ const plan = function( provision, scenario ) {
|
||||
const apply = function( provision ) {
|
||||
|
||||
const name = `qmi-tf-apply-${provision._id}`;
|
||||
console.log(`Apply: will spin up container: ${name}`);
|
||||
console.log(`Terraform# Apply: will spin up container: ${name}`);
|
||||
var processStream = fs.createWriteStream(provision.logFile, {flags:'a'});
|
||||
//var processStream = process.stdout;
|
||||
|
||||
var exec = ['terraform', 'apply', 'tfplan', '-no-color'];
|
||||
console.log('Apply: exec: '+exec.join(" "));
|
||||
|
||||
return docker.run(DOCKERIMAGE, exec, processStream, {
|
||||
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,
|
||||
"WorkingDir": "/app",
|
||||
@@ -199,9 +253,9 @@ const apply = function( provision ) {
|
||||
}
|
||||
}).then(function(data){
|
||||
let container = data[1];
|
||||
console.log(`Apply: ${name} (${container.id}) has finished with code: ${data[0].StatusCode}`);
|
||||
console.log(`Terraform# Apply: ${name} (${container.id}) has finished with code: ${data[0].StatusCode}`);
|
||||
return container.remove().then(function(){
|
||||
console.log(`Apply: ${name} removed!`);
|
||||
console.log(`Terraform# Apply: Container '${name}' removed!`);
|
||||
return data[0].StatusCode;
|
||||
});
|
||||
}).then(function(statusCode) {
|
||||
@@ -212,13 +266,16 @@ const apply = function( provision ) {
|
||||
const destroy = function(destroyMongo, provision, scenario) {
|
||||
|
||||
const name = `qmi-tf-destroy-${destroyMongo._id}`;
|
||||
console.log(`Destroy: will spin up container: ${name}`);
|
||||
console.log(`Terraform# Destroy: will spin up container: ${name}`);
|
||||
var processStream = fs.createWriteStream(destroyMongo.logFile, {flags:'a'});
|
||||
var exec = ['terraform', 'destroy', '-auto-approve', '-no-color'];
|
||||
exec = _buildVarsExec(exec, provision, scenario);
|
||||
console.log('Destroy: exec: '+exec.join(" "));
|
||||
console.log('Terraform# Destroy: exec: '+exec.join(" "));
|
||||
|
||||
return docker.run(DOCKERIMAGE, exec, processStream, {
|
||||
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"],
|
||||
"name": name,
|
||||
"WorkingDir": "/app",
|
||||
@@ -230,9 +287,9 @@ const destroy = function(destroyMongo, provision, scenario) {
|
||||
}
|
||||
}).then(function(data) {
|
||||
var container = data[1];
|
||||
console.log(`Destroy: '${name}' (${container.id}) has finished with code: ${data[0].StatusCode}`);
|
||||
console.log(`Terraform# Destroy: '${name}' (${container.id}) has finished with code: ${data[0].StatusCode}`);
|
||||
return container.remove().then(function(){
|
||||
console.log(`Destroy: '${name}' removed!`);
|
||||
console.log(`Terraform# Destroy: Container '${name}' removed!`);
|
||||
return data[0].StatusCode;
|
||||
});
|
||||
}).then(async function(statusCode) {
|
||||
@@ -244,18 +301,20 @@ const destroy = function(destroyMongo, provision, scenario) {
|
||||
const outputs = function(provision) {
|
||||
|
||||
const name = `qmi-tf-output-${provision._id}`;
|
||||
console.log(`Output: will spin up container: ${name}`);
|
||||
console.log(`Terraform# Output: will spin up container: ${name}`);
|
||||
|
||||
|
||||
var exec = ['terraform', 'output', '-no-color', '-json'];
|
||||
console.log('Output: exec: '+exec.join(" "));
|
||||
console.log('Terraform# Output: exec: '+exec.join(" "));
|
||||
|
||||
var tfout = "";
|
||||
var unhook = hook_stdout(function(string, encoding, fd) {
|
||||
tfout += string.trim();
|
||||
});
|
||||
|
||||
return docker.run(DOCKERIMAGE, exec, process.stdout, {
|
||||
var terraformImage = provision.terraformImage? provision.terraformImage : DOCKERIMAGE;
|
||||
|
||||
return docker.run(terraformImage, exec, process.stdout, {
|
||||
//"Env": ["VAR_ENV=whatever"],
|
||||
"name": name,
|
||||
"WorkingDir": "/app",
|
||||
@@ -267,11 +326,11 @@ const outputs = function(provision) {
|
||||
}).then(function(data) {
|
||||
unhook();
|
||||
var container = data[1];
|
||||
console.log(`Output: '${name}' (${container.id}) has finished with code: ${data[0].StatusCode}`);
|
||||
console.log(`Terraform# Output: '${name}' (${container.id}) has finished with code: ${data[0].StatusCode}`);
|
||||
return container.remove();
|
||||
}).then(async function(data) {
|
||||
console.log(`Output: '${name}' removed!`);
|
||||
console.log("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) {
|
||||
@@ -281,8 +340,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.outputs = outputs;
|
||||
module.exports.stop = stop;
|
||||
@@ -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!`);
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "qmi-cloud-worker",
|
||||
"version": "1.1.4",
|
||||
"version": "1.3.1",
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -12,11 +13,21 @@ module.exports = async function(job){
|
||||
});
|
||||
|
||||
if ( !destroyMongo ) {
|
||||
console.log(`Error: Not found Destroy object in Database (it should exist!), detroyId is: ${job.data.id}` );
|
||||
console.log(`ProcessorDestroy# Not found Destroy object in Database (it should exist!), detroyId is: ${job.data.id}` );
|
||||
return Promise.reject({"success": false, "err": "Not found Destroy object in Worker"});
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -28,14 +39,22 @@ 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) {
|
||||
|
||||
}).then(async function(res) {
|
||||
console.log(`ProcessorDestroy# Provision destroyed!` );
|
||||
if ( res.provision.scenario === "azqmi-qdi" ) {
|
||||
let tempApiKey = await db.apiKey.getOne({"description": res.provision._id});
|
||||
if (tempApiKey && tempApiKey._id){
|
||||
db.apiKey.del(tempApiKey._id);
|
||||
}
|
||||
}
|
||||
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("Processor Destroy: err", 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});
|
||||
});
|
||||
|
||||
21
qmi-cloud-worker/processor-stop-container.js
Normal 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});
|
||||
} );
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -66,7 +66,7 @@ exports.creds = {
|
||||
scope: ['openid', 'email', 'profile'],
|
||||
|
||||
// Optional, 'error', 'warn' or 'info'
|
||||
loggingLevel: 'info',
|
||||
loggingLevel: 'warn',
|
||||
|
||||
// Optional. The lifetime of nonce in session or cookie, the default value is 3600 (seconds).
|
||||
nonceLifetime: null,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -88,7 +91,20 @@ passport.use(new OIDCStrategy({
|
||||
//console.log("refreshToken", refreshToken);
|
||||
//console.log("jwtClaims", jwtClaims);
|
||||
//console.log("params", params);
|
||||
console.log("New Auth: profile", profile);
|
||||
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 }));
|
||||
@@ -168,7 +186,7 @@ module.exports.init = function(app){
|
||||
)(req, res, next);
|
||||
},
|
||||
function(req, res) {
|
||||
console.log('We received a return from AzureAD.');
|
||||
console.log('Passport# We received a return from AzureAD.');
|
||||
res.redirect('/provisions');
|
||||
}
|
||||
);
|
||||
@@ -187,7 +205,7 @@ module.exports.init = function(app){
|
||||
)(req, res, next);
|
||||
},
|
||||
function(req, res) {
|
||||
console.log('We received a return from AzureAD.');
|
||||
console.log('Passport# We received a return from AzureAD.');
|
||||
res.redirect('/provisions');
|
||||
}
|
||||
);
|
||||
@@ -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");
|
||||
|
||||
@@ -12,6 +12,15 @@ const passport = require('../passport');
|
||||
* summary: Get all API keys
|
||||
* tags:
|
||||
* - admin
|
||||
* parameters:
|
||||
* - name: filter
|
||||
* in: query
|
||||
* required: false
|
||||
* type: object
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
@@ -20,7 +29,8 @@ const passport = require('../passport');
|
||||
*/
|
||||
router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
const result = await db.apiKey.get();
|
||||
const filter = req.query.filter? JSON.parse(req.query.filter) : {};
|
||||
const result = await db.apiKey.get(filter);
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -42,19 +52,27 @@ router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) =>
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* description:
|
||||
* type: string
|
||||
* responses:
|
||||
* 200:
|
||||
* description: API KEY
|
||||
*/
|
||||
router.post('/:userId', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
const user = await db.user.getById(req.params.userId);
|
||||
const user = await db.user.getById(req.params.userId);
|
||||
if ( !user ) {
|
||||
res.status(404).json({"err": "user not found"});
|
||||
}
|
||||
var body = {
|
||||
user: req.params.userId
|
||||
}
|
||||
var body = req.body;
|
||||
body.user = req.params.userId;
|
||||
const result = await db.apiKey.add(body);
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
@@ -64,10 +82,10 @@ router.post('/:userId', passport.ensureAuthenticatedAndAdmin, async (req, res, n
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /apikeys/{id}:
|
||||
* /apikeys/{id}/revoke:
|
||||
* put:
|
||||
* description: Deactivate API Key
|
||||
* summary: Deactivate API Key
|
||||
* description: Revoke API Key
|
||||
* summary: Revoke API Key
|
||||
* tags:
|
||||
* - admin
|
||||
* produces:
|
||||
@@ -81,7 +99,7 @@ router.post('/:userId', passport.ensureAuthenticatedAndAdmin, async (req, res, n
|
||||
* 200:
|
||||
* description: API KEY
|
||||
*/
|
||||
router.put('/:id', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
router.put('/:id/revoke', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
const result = await db.apiKey.update(req.params.id, {"isActive": false});
|
||||
return res.json(result);
|
||||
@@ -90,4 +108,32 @@ router.put('/:id', passport.ensureAuthenticatedAndAdmin, async (req, res, next)
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /apikeys/{id}:
|
||||
* delete:
|
||||
* description: Delete API Key
|
||||
* summary: Delete API Key
|
||||
* tags:
|
||||
* - admin
|
||||
* produces:
|
||||
* - application/json
|
||||
* parameters:
|
||||
* - name: id
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* responses:
|
||||
* 200:
|
||||
* description: API KEY
|
||||
*/
|
||||
router.delete('/:id', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
const result = await db.apiKey.del(req.params.id);
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -55,7 +55,6 @@ router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) =>
|
||||
*/
|
||||
router.post('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
console.log("BODY", req.body);
|
||||
const result = await db.subscription.add(req.body);
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
|
||||
@@ -23,35 +23,114 @@ const passport = require('../passport');
|
||||
* description: Notifications
|
||||
*/
|
||||
router.post('/updates', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
|
||||
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' && event.provID ) {
|
||||
console.log(`DivvyCloud: update received`, event);
|
||||
let provision = await db.provision.getById(event.provID);
|
||||
if ( event.cloudName === 'QMI Automation' ) {
|
||||
|
||||
console.log(`${logEvent} (${now})# event received for subscription (${event.cloudName}) - provision (${event.provID}) -> new status (${event.instanceState})`);
|
||||
|
||||
if ( provision ) {
|
||||
if ( event.instanceState === 'Stopped' && provision.statusVms !== 'Stopped' ) {
|
||||
console.log(`DivvyCloud: VMs for provision are now Stopped!: '${provision._id}'`);
|
||||
let timeRunning = db.utils.getNewTimeRunning(provision);
|
||||
db.provision.update(provision._id.toString(), {"statusVms": "Stopped", "timeRunning": timeRunning, "stoppedFrom": new Date(), "pendingNextAction": undefined});
|
||||
} else if ( event.instanceState === 'Running' && provision.statusVms !== 'Running' ) {
|
||||
console.log(`DivvyCloud: VMs for provision are now Running!: '${provision._id}'`);
|
||||
db.provision.update(provision._id.toString(), {"statusVms": "Running", "runningFrom": new Date(), "pendingNextAction": undefined});
|
||||
if ( event.provID && event.provID !== 'None' ) {
|
||||
|
||||
let provision = await db.provision.getById(event.provID);
|
||||
|
||||
if ( provision ) {
|
||||
|
||||
let id = provision._id.toString();
|
||||
|
||||
console.log(`${logEvent} (${now})# provision (${id}) - scenario is (${provision.scenario} - v${provision.scenarioVersion})`);
|
||||
|
||||
if ( provision.status === 'provisioned' || provision.status === 'error' ) {
|
||||
|
||||
if ( event.instanceState === 'Stopped' ) {
|
||||
|
||||
if ( provision.statusVms === 'Stopped' ) {
|
||||
console.log(`${logEvent} (${now})# provision (${id}) - VMs were already Stopped!`);
|
||||
} else {
|
||||
|
||||
let timeRunning = db.utils.getNewTimeRunning(provision);
|
||||
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(`${logEvent} (${now})# provision (${id}) - VMs were already Running!`);
|
||||
} else {
|
||||
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(`${logEvent} (${now})# provision (${event.provID}) - Scenario not yet 'provisioned'`);
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log(`${logEvent} (${now})# provision (${event.provID}) - Provision not found.`);
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log(`DivvyCloud: provision '${event.provID}' not found.`)
|
||||
console.log(`${logEvent} (${now})# 'provID' attribute is missing.`);
|
||||
}
|
||||
|
||||
} else {
|
||||
console.log(`DivvyCloud: update received for vm (${event.vmName}) for another subcription (${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(`${logEvent} (${now})# error!!!!`, error);
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -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');
|
||||
|
||||
|
||||
/**
|
||||
@@ -20,11 +21,41 @@ const passport = require('../passport');
|
||||
*/
|
||||
router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
const result = await db.notification.get();
|
||||
var page;
|
||||
if ( req.query.page && req.query.size ) {
|
||||
page = {
|
||||
page: parseInt(req.query.page),
|
||||
size: parseInt(req.query.size)
|
||||
}
|
||||
}
|
||||
const result = await db.notification.getPage({}, page);
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @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;
|
||||
@@ -33,11 +33,11 @@ const fs = require('fs-extra');
|
||||
* in: query
|
||||
* required: false
|
||||
* type: string
|
||||
* - name: skip
|
||||
* - name: page
|
||||
* in: query
|
||||
* required: false
|
||||
* type: integer
|
||||
* - name: limit
|
||||
* - name: size
|
||||
* in: query
|
||||
* required: false
|
||||
* type: integer
|
||||
@@ -53,27 +53,31 @@ router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) =>
|
||||
if ( filter.isDeleted === undefined ) {
|
||||
filter.isDeleted = false;
|
||||
}
|
||||
|
||||
const result = await db.provision.get(filter, req.query.select, req.query.skip, req.query.limit, req.query.populates);
|
||||
var page;
|
||||
if ( req.query.page && req.query.size ) {
|
||||
page = {
|
||||
page: parseInt(req.query.page),
|
||||
size: parseInt(req.query.size)
|
||||
}
|
||||
}
|
||||
const result = await db.provision.getPage(filter, page, req.query.populates, req.query.select);
|
||||
|
||||
var out = {
|
||||
total: result.total,
|
||||
count: result.count
|
||||
};
|
||||
if ( result.nextSkip && result.nextLimit ) {
|
||||
out.nextUrl = new URL(req.protocol + '://' + req.get('Host') + req.baseUrl);
|
||||
if (result.next){
|
||||
result.nextUrl = new URL(req.protocol + '://' + req.get('Host') + req.baseUrl);
|
||||
if ( req.query.filter ) {
|
||||
out.nextUrl.searchParams.append("filter", req.query.filter);
|
||||
result.nextUrl.searchParams.append("filter", req.query.filter);
|
||||
}
|
||||
if ( req.query.populates ) {
|
||||
out.nextUrl.searchParams.append("populates", req.query.populates);
|
||||
result.nextUrl.searchParams.append("populates", req.query.populates);
|
||||
}
|
||||
out.nextUrl.searchParams.append("skip", result.nextSkip);
|
||||
out.nextUrl.searchParams.append("limit", result.nextLimit);
|
||||
if ( req.query.select ) {
|
||||
result.nextUrl.searchParams.append("select", req.query.select);
|
||||
}
|
||||
result.nextUrl.searchParams.append("page", result.next.page);
|
||||
result.nextUrl.searchParams.append("size", result.next.size);
|
||||
}
|
||||
out.results = result.results;
|
||||
|
||||
return res.json(out);
|
||||
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
@@ -334,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, 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
@@ -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;
|
||||
@@ -1,11 +1,19 @@
|
||||
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 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';
|
||||
async function asyncForEach(array, callback) {
|
||||
for (let index = 0; index < array.length; index++) {
|
||||
await callback(array[index], index, array);
|
||||
}
|
||||
}
|
||||
|
||||
import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE, STOP_CONTAINER_QUEUE } from 'qmi-cloud-common/queues';
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@@ -15,6 +23,15 @@ import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE } from '
|
||||
* summary: Get all users
|
||||
* tags:
|
||||
* - admin
|
||||
* parameters:
|
||||
* - name: filter
|
||||
* in: query
|
||||
* required: false
|
||||
* type: object
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
@@ -23,7 +40,8 @@ import { queues, TF_APPLY_QUEUE, TF_APPLY_QSEOK_QUEUE, TF_DESTROY_QUEUE } from '
|
||||
*/
|
||||
router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
const result = await db.user.get();
|
||||
const filter = req.query.filter? JSON.parse(req.query.filter) : {};
|
||||
const result = await db.user.get(filter);
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -70,7 +88,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);
|
||||
@@ -103,7 +122,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);
|
||||
@@ -161,32 +181,48 @@ 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 "});
|
||||
}
|
||||
|
||||
if (!req.body.vmImage || !req.body.vmImage.vm1 || !req.body.vmImage.vm1.vmType ) {
|
||||
return res.status(400).json({"msg": "Invalid vmImage"});
|
||||
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"});
|
||||
//}
|
||||
|
||||
req.body.scenarioVersion = scenarioSource.version;
|
||||
|
||||
if ( req.body.autoShutdownData && req.body.autoShutdownData.is24x7 !== undefined ) {
|
||||
const autoShutdown = await db.provisionAutoShutdown.add(req.body.autoShutdownData);
|
||||
req.body.autoShutdown = autoShutdown._id;
|
||||
if ( req.body.scheduleData && req.body.scheduleData.is24x7 !== undefined ) {
|
||||
const schedule = await db.schedule.add(req.body.scheduleData);
|
||||
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 mongoJob = await db.provision.add(req.body);
|
||||
|
||||
const provision = await db.provision.add(req.body);
|
||||
|
||||
|
||||
if ( mongoJob.scenario === "azqmi-qseok" ){
|
||||
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: mongoJob._id,
|
||||
id: provision._id,
|
||||
user: req.user,
|
||||
_scenario: scenarioSource
|
||||
});
|
||||
@@ -195,13 +231,120 @@ router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (r
|
||||
scenario: req.body.scenario,
|
||||
vmType: req.body.vmType,
|
||||
nodeCount: req.body.nodeCount,
|
||||
id: mongoJob._id,
|
||||
id: provision._id,
|
||||
user: req.user,
|
||||
_scenario: scenarioSource
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).json(mongoJob);
|
||||
return res.status(200).json(provision);
|
||||
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @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}:
|
||||
* put:
|
||||
* description: Update Provision by ID
|
||||
* summary: Update Provision by ID
|
||||
* parameters:
|
||||
* - name: userId
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* - name: id
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* - in: body
|
||||
* name: body
|
||||
* description: Provision object
|
||||
* required: true
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Provision
|
||||
*/
|
||||
router.put('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
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});
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
let schedule;
|
||||
var msg = "";
|
||||
if ( req.body.scheduleData ) {
|
||||
|
||||
if ( req.body.scheduleData._id ) {
|
||||
schedule = await db.schedule.update(req.body.scheduleData._id, req.body.scheduleData);
|
||||
} else {
|
||||
schedule = await db.schedule.add(req.body.scheduleData);
|
||||
}
|
||||
var tagsEdit = {
|
||||
"24x7": schedule.is24x7? " " : false,
|
||||
"StartupTime": (schedule.isStartupTimeEnable && !schedule.is24x7 && schedule.utcTagStartupTime)? schedule.utcTagStartupTime : false,
|
||||
"ShutdownTime": (!schedule.is24x7 && schedule.utcTagShutdownTime)? schedule.utcTagShutdownTime : false
|
||||
}
|
||||
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),
|
||||
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) {
|
||||
next(error);
|
||||
@@ -235,26 +378,219 @@ router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (r
|
||||
router.delete('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
const mongoJob = await db.provision.getById(req.params.id);
|
||||
if (!mongoJob){
|
||||
const provision = await db.provision.getById(req.params.id);
|
||||
if (!provision){
|
||||
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
|
||||
}
|
||||
//var delDest = mongoJob.destroy._id;
|
||||
//if ( mongoJob.destroy ) {
|
||||
// delDest = await db.destroy.del(mongoJob.destroy._id);
|
||||
//var delDest = provision.destroy._id;
|
||||
//if ( provision.destroy ) {
|
||||
// delDest = await db.destroy.del(provision.destroy._id);
|
||||
//}
|
||||
const delProv = await db.provision.update(req.params.id, {"isDeleted": true});
|
||||
|
||||
//Move folder
|
||||
if (fs.existsSync(`/provisions/${mongoJob.scenario}_${req.params.id}`)) {
|
||||
fs.moveSync(`/provisions/${mongoJob.scenario}_${req.params.id}`, `/provisions/deleted/${mongoJob.scenario}_${req.params.id}`, { overwrite: true })
|
||||
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:
|
||||
@@ -282,11 +618,25 @@ router.delete('/:userId/provisions/:id', passport.ensureAuthenticatedAndIsMe, as
|
||||
router.post('/:userId/provisions/:id/deallocatevms', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
let mongoJob = await db.provision.getById(req.params.id);
|
||||
if (!mongoJob){
|
||||
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});
|
||||
}
|
||||
azurecli.deallocate(mongoJob);
|
||||
|
||||
//Set DivvyTags according to Schedule
|
||||
if ( provision.schedule && req.body.isStartupTimeEnable !== undefined ) {
|
||||
console.log("APIUser# Set DivvyTags according to schedule");
|
||||
var schedule = await db.schedule.update(provision.schedule._id, { "isStartupTimeEnable": req.body.isStartupTimeEnable });
|
||||
var tagsEdit = {
|
||||
"24x7": schedule.is24x7? " " : false,
|
||||
"StartupTime": (schedule.isStartupTimeEnable && !schedule.is24x7 && schedule.utcTagStartupTime)? schedule.utcTagStartupTime : false,
|
||||
"ShutdownTime": (!schedule.is24x7 && schedule.utcTagShutdownTime)? schedule.utcTagShutdownTime : false
|
||||
}
|
||||
cli.updateVmsTags(provision._id, tagsEdit);
|
||||
}
|
||||
|
||||
cli.deallocate(provision._id);
|
||||
|
||||
return res.json({"statusVms": "Stopping"});
|
||||
|
||||
} catch (error) {
|
||||
@@ -322,12 +672,27 @@ router.post('/:userId/provisions/:id/deallocatevms', passport.ensureAuthenticate
|
||||
router.post('/:userId/provisions/:id/startvms', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
let mongoJob = await db.provision.getById(req.params.id);
|
||||
if (!mongoJob){
|
||||
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});
|
||||
}
|
||||
|
||||
//Re-enable DivvyTags according to schedule
|
||||
if ( provision.schedule ) {
|
||||
|
||||
let schedule = await db.schedule.getById(provision.schedule._id);
|
||||
|
||||
console.log("APIUser# Re-enabling DivvyTags according to schedule");
|
||||
var tagsEdit = {
|
||||
"24x7": schedule.is24x7? " " : false,
|
||||
"StartupTime": (schedule.isStartupTimeEnable && !schedule.is24x7 && schedule.utcTagStartupTime)? schedule.utcTagStartupTime : false,
|
||||
"ShutdownTime": (!schedule.is24x7 && schedule.utcTagShutdownTime)? schedule.utcTagShutdownTime : false
|
||||
}
|
||||
cli.updateVmsTags(provision._id, tagsEdit);
|
||||
}
|
||||
|
||||
cli.start(provision._id);
|
||||
|
||||
azurecli.start(mongoJob);
|
||||
return res.json({"statusVms": "Starting"});
|
||||
|
||||
} catch (error) {
|
||||
@@ -363,20 +728,22 @@ router.post('/:userId/provisions/:id/startvms', passport.ensureAuthenticatedAndI
|
||||
router.post('/:userId/provisions/:id/extend', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
try {
|
||||
|
||||
let mongoJob = await db.provision.getById(req.params.id);
|
||||
if (!mongoJob){
|
||||
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});
|
||||
}
|
||||
|
||||
/*if ( mongoJob.countExtend === 5 ) {
|
||||
/*if ( provision.countExtend === 5 ) {
|
||||
return res.status(200).json({"msg": "You have reached the limit for the number of times to extend the Running VMs period."});
|
||||
}*/
|
||||
|
||||
let timeRunning = db.utils.getNewTimeRunning(mongoJob);
|
||||
let countExtend = db.utils.getNewCountExtend(mongoJob);
|
||||
mongoJob = await db.provision.update(req.params.id, {"runningFrom":new Date(), "timeRunning": timeRunning, "countExtend": countExtend, "pendingNextAction": undefined});
|
||||
let timeRunning = db.utils.getNewTimeRunning(provision);
|
||||
let countExtend = db.utils.getNewCountExtend(provision);
|
||||
provision = await db.provision.update(req.params.id, {"runningFrom":new Date(), "timeRunning": timeRunning, "countExtend": countExtend, "pendingNextAction": undefined});
|
||||
|
||||
return res.json(mongoJob);
|
||||
console.log(`APIUser# Extending running period fo provision (${provision._id}), new total extends: ${countExtend}`);
|
||||
|
||||
return res.json(provision);
|
||||
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -410,25 +777,269 @@ router.post('/:userId/provisions/:id/extend', passport.ensureAuthenticatedAndIsM
|
||||
router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
|
||||
try {
|
||||
|
||||
let mongoJob = await db.provision.getById(req.params.id);
|
||||
if (!mongoJob){
|
||||
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 });
|
||||
mongoJob = await db.provision.update(req.params.id, {"destroy": destroyJob._id});
|
||||
const scenarioSource = await db.scenario.getOne({name: mongoJob.scenario});
|
||||
|
||||
if ( provision.destroy && provision.destroy.status !== 'error' ) {
|
||||
console.log(`APIUser# This provision is already destroyed or being destroyed right now: ${provision._id}`);
|
||||
return res.status(200).json(provision);
|
||||
}
|
||||
|
||||
//
|
||||
console.log(`APIUser# Queueing destroy provision: ${provision._id}`);
|
||||
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});
|
||||
|
||||
queues[TF_DESTROY_QUEUE].add("tf_destroy_job", {
|
||||
scenario: mongoJob.scenario,
|
||||
provId: mongoJob._id,
|
||||
scenario: provision.scenario,
|
||||
provId: provision._id,
|
||||
user: req.user,
|
||||
id: destroyJob._id,
|
||||
_scenario: scenarioSource
|
||||
});
|
||||
|
||||
return res.status(200).json(mongoJob);
|
||||
//Check children provisions
|
||||
let children = await db.provision.get({ "parent": provision._id, "isDestroyed": false, "isDeleted": false });
|
||||
|
||||
if (children.results.length > 0 ) {
|
||||
await asyncForEach(children.results, async function(child) {
|
||||
|
||||
if ( !child.destroy || child.destroy.status === 'error' ) {
|
||||
console.log(`APIUser# Queueing destroy children provision: ${child._id}`);
|
||||
let destroyJobChild = await db.destroy.add({ "user": userId });
|
||||
await db.provision.update(child._id, {"destroy": destroyJobChild._id});
|
||||
let scenarioSourceChild = await db.scenario.getOne({name: child.scenario});
|
||||
|
||||
queues[TF_DESTROY_QUEUE].add("tf_destroy_job", {
|
||||
scenario: child.scenario,
|
||||
provId: child._id,
|
||||
user: req.user,
|
||||
id: destroyJobChild._id,
|
||||
_scenario: scenarioSourceChild
|
||||
});
|
||||
} else {
|
||||
console.log(`APIUser# This child provision is already destroyed or being destroyed right now: ${child._id}`);
|
||||
}
|
||||
})
|
||||
}
|
||||
return res.status(200).json(provision);
|
||||
|
||||
} catch (error) {
|
||||
return res.status(error.output.statusCode).json({"err":error});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /users/{userId}/provisions/{id}/share/{withUserId}:
|
||||
* put:
|
||||
* description: Share provision with another user
|
||||
* summary: Share provision with another user
|
||||
* produces:
|
||||
* - application/json
|
||||
* parameters:
|
||||
* - name: userId
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* - name: id
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* - name: withUserId
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Provision
|
||||
* 404:
|
||||
* description: Not found
|
||||
*
|
||||
*/
|
||||
router.put('/:userId/provisions/:id/share/:withUserId', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
|
||||
try {
|
||||
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||
|
||||
if ( req.params.withUserId === userId ) {
|
||||
return res.status(400).json({"msg": "Can't share with the same user"});
|
||||
}
|
||||
|
||||
let provision = await db.provision.getById(req.params.id);
|
||||
if (!provision){
|
||||
return res.status(404).json({"msg": "Not found privision with id "+req.params.id});
|
||||
}
|
||||
|
||||
let found = await db.sharedProvision.getOne({
|
||||
"user": userId,
|
||||
"provision": provision._id,
|
||||
"sharedWithUser": req.params.withUserId
|
||||
});
|
||||
if ( !found ) {
|
||||
found = await db.sharedProvision.add({"user": userId, "provision": provision._id, "sharedWithUser": req.params.withUserId})
|
||||
}
|
||||
|
||||
return res.json(found);
|
||||
|
||||
} catch (error) {
|
||||
return res.status(error.output.statusCode).json({"err":error});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /users/{userId}/provisions/{id}/share/{withUserId}:
|
||||
* delete:
|
||||
* description: Stop sharing this provision with another user
|
||||
* summary: Stop sharing this provision with another user
|
||||
* produces:
|
||||
* - application/json
|
||||
* parameters:
|
||||
* - name: userId
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* - name: id
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* - name: withUserId
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Provision
|
||||
* 404:
|
||||
* description: Not found
|
||||
*
|
||||
*/
|
||||
router.delete('/:userId/provisions/:id/share/:withUserId', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
|
||||
try {
|
||||
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||
|
||||
if ( req.params.withUserId === userId ) {
|
||||
return res.status(400).json({"msg": "Can't share with the same user"});
|
||||
}
|
||||
const result = await db.sharedProvision.delMany({"user": userId, "provision": req.params.id, "sharedWithUser": req.params.withUserId});
|
||||
return res.json(result);
|
||||
|
||||
} catch (error) {
|
||||
return res.status(error.output.statusCode).json({"err":error});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /users/{userId}/provisions/{id}/share:
|
||||
* delete:
|
||||
* description: Stop sharing this provision with everybody
|
||||
* summary: Stop sharing this provision with everybody
|
||||
* 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/share', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
|
||||
try {
|
||||
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||
|
||||
if ( req.params.withUserId === userId ) {
|
||||
return res.status(400).json({"msg": "Can't share with the same user"});
|
||||
}
|
||||
|
||||
const result = await db.sharedProvision.delMany({"user": userId, "provision": req.params.id});
|
||||
return res.json(result);
|
||||
|
||||
} catch (error) {
|
||||
return res.status(error.output.statusCode).json({"err":error});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /users/{userId}/provisions/{id}/share:
|
||||
* get:
|
||||
* description: Get shares of this provision
|
||||
* summary: Get shares of this 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.get('/:userId/provisions/:id/share', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
|
||||
try {
|
||||
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||
|
||||
const result = await db.sharedProvision.get({"user": userId, "provision": req.params.id});
|
||||
return res.json(result);
|
||||
|
||||
} catch (error) {
|
||||
return res.status(error.output.statusCode).json({"err":error});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /users/{userId}/sharedprovisions:
|
||||
* get:
|
||||
* description: Get provision shared with me
|
||||
* summary: Get provision shared with me
|
||||
* produces:
|
||||
* - application/json
|
||||
* parameters:
|
||||
* - name: userId
|
||||
* in: path
|
||||
* type: string
|
||||
* required: true
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Provision
|
||||
* 404:
|
||||
* description: Not found
|
||||
*
|
||||
*/
|
||||
router.get('/:userId/sharedprovisions', passport.ensureAuthenticatedAndIsMe, async (req, res, next) => {
|
||||
|
||||
try {
|
||||
const userId = req.params.userId === 'me'? req.user._id : req.params.userId;
|
||||
let result = await db.sharedProvision.get({"sharedWithUser": userId});
|
||||
return res.json(result);
|
||||
|
||||
} catch (error) {
|
||||
return res.status(error.output.statusCode).json({"err":error});
|
||||
@@ -455,7 +1066,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) {
|
||||
@@ -463,6 +1075,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
|
||||
@@ -484,7 +1199,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);
|
||||
|
||||
@@ -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');
|
||||
@@ -33,6 +34,7 @@ function _getRedisConfig(redisUrl) {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
app.use('/arena', Arena(
|
||||
{
|
||||
queues: [
|
||||
@@ -50,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)
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -71,7 +78,8 @@ app.use(cookieParser());
|
||||
app.use(bodyParser.urlencoded({ extended: false }))
|
||||
// parse application/json
|
||||
app.use(bodyParser.json())
|
||||
app.use(express.static(__dirname + '/../dist/qmi-cloud'));
|
||||
|
||||
app.use('/',express.static(__dirname + '/../dist/qmi-cloud'));
|
||||
|
||||
passport.init(app);
|
||||
|
||||
@@ -83,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 ) {
|
||||
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("/");
|
||||
@@ -100,6 +123,10 @@ app.get('/logout', function(req, res) {
|
||||
res.redirect("/");
|
||||
});
|
||||
|
||||
app.get('/backendlogs', function (req, res) {
|
||||
res.redirect(process.env.BACKEND_LOGS_URL);
|
||||
})
|
||||
|
||||
|
||||
const options = {
|
||||
definition: {
|
||||
@@ -125,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: []
|
||||
@@ -155,16 +164,41 @@ const options = {
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
app.use('/costexport*', passport.ensureAuthenticatedAndAdmin, function(req, res){
|
||||
if ( !req.query.file ) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||