Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
885f2986ef | ||
|
|
e45445f919 | ||
|
|
2040c9f914 | ||
|
|
93884ebeea | ||
|
|
dc9354a36a | ||
|
|
5e1e193688 |
|
Before Width: | Height: | Size: 211 B After Width: | Height: | Size: 211 B |
|
Before Width: | Height: | Size: 213 B After Width: | Height: | Size: 213 B |
|
Before Width: | Height: | Size: 209 B After Width: | Height: | Size: 209 B |
|
Before Width: | Height: | Size: 211 B After Width: | Height: | Size: 211 B |
|
Before Width: | Height: | Size: 213 B After Width: | Height: | Size: 213 B |
|
Before Width: | Height: | Size: 211 B After Width: | Height: | Size: 211 B |
|
Before Width: | Height: | Size: 215 B After Width: | Height: | Size: 215 B |
|
Before Width: | Height: | Size: 146 B After Width: | Height: | Size: 146 B |
|
Before Width: | Height: | Size: 137 B After Width: | Height: | Size: 137 B |
BIN
dist/qmi-cloud/3rdpartylicenses.txt
vendored
|
Before Width: | Height: | Size: 215 B After Width: | Height: | Size: 215 B |
|
Before Width: | Height: | Size: 214 B After Width: | Height: | Size: 214 B |
|
Before Width: | Height: | Size: 699 KiB After Width: | Height: | Size: 699 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 829 KiB After Width: | Height: | Size: 829 KiB |
|
Before Width: | Height: | Size: 434 KiB After Width: | Height: | Size: 434 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>
|
||||
</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.js" defer></script><script src="polyfills-es5.js" nomodule defer></script><script src="polyfills.js" defer></script><script src="styles.js" defer></script><script src="scripts.js" defer></script><script src="vendor.js" defer></script><script src="main.js" defer></script></body>
|
||||
</html>
|
||||
|
||||
1
dist/qmi-cloud/main.412f22a84714f529779a.js
vendored
5459
dist/qmi-cloud/main.js
vendored
Normal file
1
dist/qmi-cloud/main.js.map
vendored
Normal file
14575
dist/qmi-cloud/polyfills-es5.js
vendored
Normal file
1
dist/qmi-cloud/polyfills-es5.js.map
vendored
Normal file
6848
dist/qmi-cloud/polyfills.js
vendored
Normal file
1
dist/qmi-cloud/polyfills.js.map
vendored
Normal file
@@ -1 +0,0 @@
|
||||
!function(e){function r(r){for(var n,l,f=r[0],i=r[1],p=r[2],c=0,s=[];c<f.length;c++)l=f[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in i)Object.prototype.hasOwnProperty.call(i,n)&&(e[n]=i[n]);for(a&&a(r);s.length;)s.shift()();return u.push.apply(u,p||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,f=1;f<t.length;f++)0!==o[t[f]]&&(n=!1);n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={0:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,(function(r){return e[r]}).bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="";var f=window.webpackJsonp=window.webpackJsonp||[],i=f.push.bind(f);f.push=r,f=f.slice();for(var p=0;p<f.length;p++)r(f[p]);var a=i;t()}([]);
|
||||
155
dist/qmi-cloud/runtime.js
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // install a JSONP callback for chunk loading
|
||||
/******/ function webpackJsonpCallback(data) {
|
||||
/******/ var chunkIds = data[0];
|
||||
/******/ var moreModules = data[1];
|
||||
/******/ var executeModules = data[2];
|
||||
/******/
|
||||
/******/ // add "moreModules" to the modules object,
|
||||
/******/ // then flag all "chunkIds" as loaded and fire callback
|
||||
/******/ var moduleId, chunkId, i = 0, resolves = [];
|
||||
/******/ for(;i < chunkIds.length; i++) {
|
||||
/******/ chunkId = chunkIds[i];
|
||||
/******/ if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
|
||||
/******/ resolves.push(installedChunks[chunkId][0]);
|
||||
/******/ }
|
||||
/******/ installedChunks[chunkId] = 0;
|
||||
/******/ }
|
||||
/******/ for(moduleId in moreModules) {
|
||||
/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
|
||||
/******/ modules[moduleId] = moreModules[moduleId];
|
||||
/******/ }
|
||||
/******/ }
|
||||
/******/ if(parentJsonpFunction) parentJsonpFunction(data);
|
||||
/******/
|
||||
/******/ while(resolves.length) {
|
||||
/******/ resolves.shift()();
|
||||
/******/ }
|
||||
/******/
|
||||
/******/ // add entry modules from loaded chunk to deferred list
|
||||
/******/ deferredModules.push.apply(deferredModules, executeModules || []);
|
||||
/******/
|
||||
/******/ // run deferred modules when all chunks ready
|
||||
/******/ return checkDeferredModules();
|
||||
/******/ };
|
||||
/******/ function checkDeferredModules() {
|
||||
/******/ var result;
|
||||
/******/ for(var i = 0; i < deferredModules.length; i++) {
|
||||
/******/ var deferredModule = deferredModules[i];
|
||||
/******/ var fulfilled = true;
|
||||
/******/ for(var j = 1; j < deferredModule.length; j++) {
|
||||
/******/ var depId = deferredModule[j];
|
||||
/******/ if(installedChunks[depId] !== 0) fulfilled = false;
|
||||
/******/ }
|
||||
/******/ if(fulfilled) {
|
||||
/******/ deferredModules.splice(i--, 1);
|
||||
/******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
|
||||
/******/ }
|
||||
/******/ }
|
||||
/******/
|
||||
/******/ return result;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // object to store loaded and loading chunks
|
||||
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
|
||||
/******/ // Promise = chunk loading, 0 = chunk loaded
|
||||
/******/ var installedChunks = {
|
||||
/******/ "runtime": 0
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ var deferredModules = [];
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // define __esModule on exports
|
||||
/******/ __webpack_require__.r = function(exports) {
|
||||
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
||||
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||
/******/ }
|
||||
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // create a fake namespace object
|
||||
/******/ // mode & 1: value is a module id, require it
|
||||
/******/ // mode & 2: merge all properties of value into the ns
|
||||
/******/ // mode & 4: return value when already ns object
|
||||
/******/ // mode & 8|1: behave like require
|
||||
/******/ __webpack_require__.t = function(value, mode) {
|
||||
/******/ if(mode & 1) value = __webpack_require__(value);
|
||||
/******/ if(mode & 8) return value;
|
||||
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
||||
/******/ var ns = Object.create(null);
|
||||
/******/ __webpack_require__.r(ns);
|
||||
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
||||
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
||||
/******/ return ns;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/ var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
|
||||
/******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
|
||||
/******/ jsonpArray.push = webpackJsonpCallback;
|
||||
/******/ jsonpArray = jsonpArray.slice();
|
||||
/******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
|
||||
/******/ var parentJsonpFunction = oldJsonpFunction;
|
||||
/******/
|
||||
/******/
|
||||
/******/ // run deferred modules from other chunks
|
||||
/******/ checkDeferredModules();
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([]);
|
||||
//# sourceMappingURL=runtime.js.map
|
||||
1
dist/qmi-cloud/runtime.js.map
vendored
Normal file
20176
dist/qmi-cloud/scripts.js
vendored
Normal file
1
dist/qmi-cloud/scripts.js.map
vendored
Normal file
80
dist/qmi-cloud/styles.23a05ae088b0f881d0eb.css
vendored
659
dist/qmi-cloud/styles.js
vendored
Normal file
1
dist/qmi-cloud/styles.js.map
vendored
Normal file
201117
dist/qmi-cloud/vendor.js
vendored
Normal file
1
dist/qmi-cloud/vendor.js.map
vendored
Normal file
@@ -43,6 +43,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 +76,4 @@
|
||||
"tslint": "~5.11.0",
|
||||
"typescript": "~3.7.5"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,21 +56,17 @@ 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];
|
||||
}
|
||||
timeRunning(p);
|
||||
if (!IS_REAL) {
|
||||
console.log(`${p._id} - limit: ${limit} hs - actual duration: ${p.duration.hours} hs`);
|
||||
}
|
||||
if ( p.duration && p.duration.hours >= limit) {
|
||||
await cb(p);
|
||||
//Only if 24x7
|
||||
if ( !p.autoShutdown || p.autoShutdown.is24x7 ) {
|
||||
timeRunning(p);
|
||||
if (!IS_REAL) {
|
||||
console.log(`${p._id} - limit: ${limit} hs - actual duration: ${p.duration.hours} hs`);
|
||||
}
|
||||
if ( p.duration && p.duration.hours >= limit) {
|
||||
await cb(p);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -79,11 +75,11 @@ const doSendEmailWarning = async function(p) {
|
||||
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}) being 'Running' more than ${RUNNING_LIMIT_HOURS_WARNING} hours (exactly ${p.duration.complete})`;
|
||||
console.log(msg);
|
||||
if ( IS_REAL ) {
|
||||
await db.provision.update(p._id, {"pendingNextAction": "stopVms"});
|
||||
await sendEmail.sendWillStopIn24(p, p._scenario);
|
||||
await sendEmail.sendWillStopIn24(p, p._scenarioDoc);
|
||||
await db.notification.add({ provision: p._id.toString(), type: 'warningStop', message: msg });
|
||||
}
|
||||
}
|
||||
@@ -91,11 +87,17 @@ const doSendEmailWarning = async function(p) {
|
||||
|
||||
const doStop = async function(p) {
|
||||
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}) being 'Running' more than ${RUNNING_LIMIT_HOURS_STOP} hours (exactly ${p.duration.complete})`
|
||||
console.log(msg);
|
||||
|
||||
if ( IS_REAL ) {
|
||||
await azurecli.deallocate(p, true);
|
||||
if ( IS_REAL ) {
|
||||
//Disable Divvy
|
||||
await azurecli.updateVmsTags(p, {
|
||||
"24x7": false,
|
||||
"StartupTime": false
|
||||
});
|
||||
//Stop VMs
|
||||
await azurecli.deallocate(p, true);
|
||||
await db.notification.add({ provision: p._id.toString(), type: 'stop', message: msg });
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,17 @@ async function start(provision){
|
||||
let rgName = _getRgName(provision);
|
||||
console.log("Starting VMs for resource group: "+rgName);
|
||||
|
||||
//Re-enable Divvy according to what's in database
|
||||
if (provision.autoShutdown){
|
||||
console.log("Re-enabling Divvy Tags according to database");
|
||||
var tagsEdit = {
|
||||
"24x7": provision.autoShutdown.is24x7? " " : false,
|
||||
"StartupTime": provision.autoShutdown.utcTagStartupTime || false,
|
||||
"ShutdownTime": provision.autoShutdown.utcTagShutdownTime || false
|
||||
}
|
||||
await updateVmsTags(provision, tagsEdit);
|
||||
}
|
||||
|
||||
var computeClient = await _getClient(provision.scenario);
|
||||
let finalResult = await computeClient.virtualMachines.list(rgName);
|
||||
|
||||
|
||||
@@ -15,6 +15,9 @@ const sc = new mongoose.Schema({
|
||||
localeStartupTime: {
|
||||
type: String
|
||||
},
|
||||
localTimezone: {
|
||||
type: String
|
||||
},
|
||||
utcTagShutdownTime: {
|
||||
type: String
|
||||
},
|
||||
|
||||
@@ -170,7 +170,7 @@ 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");
|
||||
|
||||
@@ -178,15 +178,15 @@ router.post('/:userId/provisions', passport.ensureAuthenticatedAndIsMe, async (r
|
||||
req.body.autoShutdown = autoShutdown._id;
|
||||
}
|
||||
|
||||
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,
|
||||
nodeCount: req.body.nodeCount,
|
||||
id: mongoJob._id,
|
||||
id: provision._id,
|
||||
user: req.user,
|
||||
_scenario: scenarioSource
|
||||
});
|
||||
@@ -195,13 +195,78 @@ 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}:
|
||||
* 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) => {
|
||||
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});
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
var resSchedule;
|
||||
if ( req.body.autoShutdownData ) {
|
||||
if ( req.body.autoShutdownData._id ) {
|
||||
resSchedule = await db.provisionAutoShutdown.update(req.body.autoShutdownData._id, req.body.autoShutdownData);
|
||||
} else {
|
||||
resSchedule = await db.provisionAutoShutdown.add(req.body.autoShutdownData);
|
||||
}
|
||||
var tagsEdit = {
|
||||
"24x7": resSchedule.is24x7? " " : false,
|
||||
"StartupTime": resSchedule.utcTagStartupTime || false,
|
||||
"ShutdownTime": resSchedule.utcTagShutdownTime || false
|
||||
}
|
||||
azurecli.updateVmsTags(provision, tagsEdit);
|
||||
}
|
||||
|
||||
var patch = {};
|
||||
if ( req.body.user ) {
|
||||
patch.user = req.body.user;
|
||||
}
|
||||
if ( resSchedule ) {
|
||||
patch.autoShutdown = resSchedule._id;
|
||||
}
|
||||
var result = {
|
||||
provision: await db.provision.update(provision._id, patch)
|
||||
}
|
||||
return res.json(result);
|
||||
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -235,19 +300,19 @@ 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 })
|
||||
}
|
||||
return res.json({"provision": delProv, "destroy": delProv.destroy});
|
||||
} catch (error) {
|
||||
@@ -282,11 +347,11 @@ 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);
|
||||
azurecli.deallocate(provision);
|
||||
return res.json({"statusVms": "Stopping"});
|
||||
|
||||
} catch (error) {
|
||||
@@ -322,12 +387,12 @@ 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});
|
||||
}
|
||||
|
||||
azurecli.start(mongoJob);
|
||||
azurecli.start(provision);
|
||||
return res.json({"statusVms": "Starting"});
|
||||
|
||||
} catch (error) {
|
||||
@@ -363,20 +428,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(`Extending running period fo provision (${provision._id}), new total extends: ${countExtend}`);
|
||||
|
||||
return res.json(provision);
|
||||
|
||||
} catch (error) {
|
||||
next(error);
|
||||
@@ -411,24 +478,24 @@ router.post('/:userId/provisions/:id/destroy', passport.ensureAuthenticatedAndIs
|
||||
|
||||
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});
|
||||
}
|
||||
|
||||
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});
|
||||
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);
|
||||
return res.status(200).json(provision);
|
||||
|
||||
} catch (error) {
|
||||
return res.status(error.output.statusCode).json({"err":error});
|
||||
|
||||
@@ -7,14 +7,41 @@
|
||||
<h4 class="modal-title w-100 font-weight-bold">Edit Provision</h4>
|
||||
</div>
|
||||
<div class="modal-body" style="max-height: 650px; overflow: auto;">
|
||||
<div>
|
||||
<section style="padding: 20px 0px;">
|
||||
<label>Onwer (*):</label>
|
||||
<select class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedUser">
|
||||
<option *ngFor="let item of users" [value]="item._id">{{item.displayName}}</option>
|
||||
</select>
|
||||
<div *ngIf="provision._scenarioDoc && provision._scenarioDoc.isDivvyEnabled" style="background: #f0f0f0; padding: 15px 15px; margin: 7px 0px;">
|
||||
<div>
|
||||
<h5><mdb-icon far icon="calendar-alt"></mdb-icon> Scenario schedule <small style="font-size: 50%;">(everyday this scenario will startup and shutdown as scheduled bellow:)</small></h5>
|
||||
</div>
|
||||
<div>
|
||||
<b>Timezone:</b> {{getUTCTimes().timezone}} <i>(times relative to this timezone)</i>
|
||||
</div>
|
||||
<div class="md-form row" [ngClass]="{'disabled': autoShutdownData.is24x7}">
|
||||
<div class="col-sm-6">
|
||||
<label>Startup time:</label>
|
||||
<ngb-timepicker readonly="true" style="font-size: 10px;" [(ngModel)]="autoShutdownData.startupTime" [meridian]="true" [minuteStep]="15" [disabled]="autoShutdownData.is24x7"></ngb-timepicker>
|
||||
<div class="utc">
|
||||
<i>UTC time: {{getUTCTimes().startupTime}}</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label>Shutdown time:</label>
|
||||
<ngb-timepicker style="font-size: 10px;" [(ngModel)]="autoShutdownData.shutdownTime" [meridian]="true" [minuteStep]="15" [disabled]="autoShutdownData.is24x7"></ngb-timepicker>
|
||||
<div class="utc">
|
||||
<i>UTC time: {{getUTCTimes().shutdownTime}}</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section style="padding: 20px 0px; margin: 0px 20px">
|
||||
<mdb-checkbox [default]="false" [checked]="autoShutdownData.is24x7" (change)="checkOnchangeAutoshutdown($event)">Run 24x7 <i>(<mdb-icon fas icon="exclamation-triangle"></mdb-icon> requires permission from regional manager)</i></mdb-checkbox>
|
||||
</section>
|
||||
</div>
|
||||
<!--<div>
|
||||
<section style="padding: 20px 0px;">
|
||||
<label>Change Owner (*):</label>
|
||||
<select class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedUser">
|
||||
<option *ngFor="let item of users" [value]="item._id">{{item.displayName}}</option>
|
||||
</select>
|
||||
</section>
|
||||
</div>-->
|
||||
</div>
|
||||
|
||||
<div class="modal-footer d-flex justify-content-center">
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
label {
|
||||
position: relative;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.md-form {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.utc {
|
||||
padding-left: 20px;
|
||||
padding-top: 5px;
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.disabled {
|
||||
opacity: 0.2;
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { MDBModalRef } from 'angular-bootstrap-md';
|
||||
import { Subject } from 'rxjs';
|
||||
import { UsersService } from '../services/users.service';
|
||||
import { ProvisionsService } from '../services/provisions.service';
|
||||
import * as moment from 'moment-timezone';
|
||||
|
||||
@Component({
|
||||
selector: 'qmi-edit-provision',
|
||||
@@ -15,6 +16,17 @@ export class ProvisionModalComponent implements OnInit, OnDestroy {
|
||||
action: Subject<any> = new Subject();
|
||||
users;
|
||||
selectedUser;
|
||||
autoShutdownData: any = {
|
||||
is24x7: false,
|
||||
startupTime: {
|
||||
"hour": 7,
|
||||
"minute": 0
|
||||
},
|
||||
shutdownTime: {
|
||||
"hour": 20,
|
||||
"minute": 0
|
||||
}
|
||||
};
|
||||
|
||||
sendData : any = {};
|
||||
|
||||
@@ -28,6 +40,20 @@ export class ProvisionModalComponent implements OnInit, OnDestroy {
|
||||
if (this.provision.user ) {
|
||||
this.selectedUser = this.provision.user._id;
|
||||
}
|
||||
if ( this.provision.autoShutdown ) {
|
||||
console.log("Database autoshutdown", this.provision.autoShutdown);
|
||||
this.autoShutdownData.shutdownTime = {
|
||||
"hour": parseInt(this.provision.autoShutdown.localeShutdownTime.split(":")[0]),
|
||||
"minute": parseInt(this.provision.autoShutdown.localeShutdownTime.split(":")[1])
|
||||
}
|
||||
this.autoShutdownData.startupTime = {
|
||||
"hour": parseInt(this.provision.autoShutdown.localeStartupTime.split(":")[0]),
|
||||
"minute": parseInt(this.provision.autoShutdown.localeStartupTime.split(":")[1])
|
||||
}
|
||||
this.autoShutdownData.is24x7 = this.provision.autoShutdown.is24x7;
|
||||
} else {
|
||||
this.autoShutdownData.is24x7 = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -35,11 +61,35 @@ export class ProvisionModalComponent implements OnInit, OnDestroy {
|
||||
|
||||
}
|
||||
|
||||
confirm() : void {
|
||||
this._provisionsService.updateProvisionAdmin(this.provision._id, { user: this.selectedUser } ).subscribe( res=>{
|
||||
confirm() : void {
|
||||
|
||||
this.sendData.user = this.selectedUser;
|
||||
|
||||
let autoShutdownUTC = this.getUTCTimes();
|
||||
this.sendData.autoShutdownData = this.provision.autoShutdown || {};
|
||||
this.sendData.autoShutdownData.is24x7 = this.autoShutdownData.is24x7;
|
||||
this.sendData.autoShutdownData.utcTagStartupTime = autoShutdownUTC.tagStartup;
|
||||
this.sendData.autoShutdownData.utcTagShutdownTime = autoShutdownUTC.tagShutdown;
|
||||
this.sendData.autoShutdownData.localeStartupTime = this.autoShutdownData.startupTime.hour+":"+this._provisionsService.addZero(this.autoShutdownData.startupTime.minute);
|
||||
this.sendData.autoShutdownData.localeShutdownTime = this.autoShutdownData.shutdownTime.hour+":"+this._provisionsService.addZero(this.autoShutdownData.shutdownTime.minute);
|
||||
this.sendData.autoShutdownData.localTimezone = autoShutdownUTC.timezone;
|
||||
this.sendData.autoShutdownData.timezoneOffset = autoShutdownUTC.timezoneOffset;
|
||||
|
||||
console.log("SendData", this.sendData);
|
||||
|
||||
this._provisionsService.updateProvisionUser(this.provision._id, this.provision.user._id, this.sendData ).subscribe( res=>{
|
||||
this.action.next(res);
|
||||
this.modalRef.hide();
|
||||
});
|
||||
}
|
||||
|
||||
checkOnchangeAutoshutdown($event) {
|
||||
this.autoShutdownData.is24x7 = $event.checked;
|
||||
}
|
||||
|
||||
getUTCTimes() {
|
||||
const zone = this.provision.autoShutdown? this.provision.autoShutdown.localTimezone : moment.tz.guess(true);
|
||||
return this._provisionsService.getUTCTimes(this.autoShutdownData, zone);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -3,35 +3,41 @@
|
||||
<button type="button" class="close pull-right" aria-label="Close" (click)="modalRef.hide()">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h3 style="text-align: center;" class="modal-title w-100">Provision information</h3>
|
||||
<h3 style="text-align: center;" class="modal-title w-100">{{info._scenarioDoc.title}}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div>
|
||||
<b>ProvisionID: </b> <span class="mydata">{{info._id}}</span>
|
||||
<div style="text-align: right;">
|
||||
<span class="mydata">{{info._id}}</span>
|
||||
</div>
|
||||
<div>
|
||||
<b>Purpose: </b> {{info.description}}
|
||||
</div>
|
||||
<div>
|
||||
<b>Scenario: </b> {{info._scenarioDoc.title}} <span *ngIf="info.isExternalAccess">( External Access is enabled )</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="info.autoShutdown">
|
||||
<mdb-icon far icon="calendar-alt"></mdb-icon> <span *ngIf="info.autoShutdown.is24x7"> 24x7</span><span *ngIf="!info.autoShutdown.is24x7"> Startup time: {{info.autoShutdown.localeStartupTime}}h, Shutdown time: {{info.autoShutdown.localeShutdownTime}}h</span>
|
||||
</div>
|
||||
<div *ngIf="!info.autoShutdown">
|
||||
<mdb-icon far icon="calendar-alt"></mdb-icon> 24x7
|
||||
</div>
|
||||
<div *ngIf="info.vmType && !info.vmImage">
|
||||
<b>Instance Type: </b> {{info.vmType}} <span *ngIf="info.nodeCount">( {{info.nodeCount}} nodes )</span>
|
||||
<mdb-icon fas icon="server"></mdb-icon> {{info.vmType}} <span *ngIf="info.nodeCount">( {{info.nodeCount}} nodes )</span>
|
||||
</div>
|
||||
<div *ngFor="let item of info.vmImage | keyvalue">
|
||||
<div *ngIf="item.value.version">
|
||||
<b>Product version: </b> <span>{{item.value.version.name}}</span>
|
||||
<div>
|
||||
<mdb-icon fas icon="server"></mdb-icon> {{item.value.version.name}}
|
||||
</div>
|
||||
<div *ngIf="item.value.vmType" style="padding-left: 25px;">
|
||||
<mdb-icon fas icon="long-arrow-alt-right"></mdb-icon> {{item.value.vmType}} <span *ngIf="item.value.diskSizeGb">( {{item.value.diskSizeGb}} GiB on disk ) </span> <span *ngIf="item.value.nodeCount">( {{item.value.nodeCount}} nodes )</span>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="item.value.vmType" >
|
||||
<b>Instance Type: </b> <span>{{item.value.vmType}} </span> <span *ngIf="item.value.diskSizeGb">( {{item.value.diskSizeGb}} GiB on disk ) </span> <span *ngIf="item.value.nodeCount">( {{item.value.nodeCount}} nodes )</span>
|
||||
<div *ngIf="!item.value.version">
|
||||
<div *ngIf="item.value.vmType">
|
||||
<mdb-icon fas icon="server"></mdb-icon> {{item.value.vmType}} <span *ngIf="item.value.diskSizeGb">( {{item.value.diskSizeGb}} GiB on disk ) </span> <span *ngIf="item.value.nodeCount">( {{item.value.nodeCount}} nodes )</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="info.autoShutdown">
|
||||
<b>Running window: </b> <span *ngIf="info.autoShutdown.is24x7">24x7</span><span *ngIf="!info.autoShutdown.is24x7">Startup time: {{info.autoShutdown.localeStartupTime}}h, Shutdown time: {{info.autoShutdown.localeShutdownTime}}h</span>
|
||||
</div>
|
||||
<div *ngIf="!info.autoShutdown">
|
||||
<b>Running window: </b> 24x7
|
||||
<div *ngIf="info.isExternalAccess">
|
||||
<mdb-icon fas icon="globe-americas"></mdb-icon> External access enabled
|
||||
</div>
|
||||
<h5 *ngIf="info.outputs" class="info-subtitle">Connection resources</h5>
|
||||
<div *ngFor="let item of info.outputs | keyvalue">
|
||||
|
||||
@@ -8,25 +8,44 @@
|
||||
</div>
|
||||
<div class="modal-body" style="max-height: 650px; overflow: auto;">
|
||||
<!--<h5>{{scenario.title}}</h5>-->
|
||||
<div class="md-form">
|
||||
<div class="md-form" style="background: #f0f0f0; padding: 15px;">
|
||||
<div>
|
||||
<div>Purpose: (*)</div>
|
||||
<h5>Purpose: (*)</h5>
|
||||
<textarea [(ngModel)]="sendData.description" type="text" class="md-textarea form-control" rows="1" mdbInput placeholder="Short description or Salesforce opportunity"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<section *ngIf="scenario.isExternal" style="padding: 20px 0px; margin: 0px 20px">
|
||||
<mdb-checkbox [default]="false" (change)="checkOnchange($event)">Enable External Access <i>(<mdb-icon fas icon="exclamation-triangle"></mdb-icon> only if it's strictly necessary)</i></mdb-checkbox>
|
||||
</section>
|
||||
|
||||
<section *ngIf="!scenario.isExternal" style="padding: 20px 0px; margin: 0px 20px">
|
||||
<mdb-checkbox [default]="false" (change)="checkOnchange($event)" [disabled]="true"><span style="color:#ccc">Enable External Access <i>(only if it's strictly necessary)</i></span><br><i><mdb-icon fas icon="exclamation-triangle"></mdb-icon> This scenario only allows access from VPN</i></mdb-checkbox>
|
||||
</section>
|
||||
|
||||
<div style="padding-top: 10px" class="md-form" >
|
||||
<div *ngIf="scenario.isDivvyEnabled" style="background: #f0f0f0; padding: 15px 15px; margin: 7px 0px;">
|
||||
<div>
|
||||
<h5><mdb-icon far icon="calendar-alt"></mdb-icon> Scenario schedule <small style="font-size: 50%;">(everyday this scenario will startup and shutdown as scheduled bellow:)</small></h5>
|
||||
</div>
|
||||
<div>
|
||||
<b>Local timezone:</b> {{zone}} <i>(times relative to this timezone)</i>
|
||||
</div>
|
||||
<div class="md-form row" [ngClass]="{'disabled': autoShutdown.is24x7}">
|
||||
<div class="col-sm-6">
|
||||
<label>Startup time:</label>
|
||||
<ngb-timepicker style="font-size: 10px;" [(ngModel)]="autoShutdown.startupTime" [meridian]="true" [minuteStep]="15" [disabled]="autoShutdown.is24x7"></ngb-timepicker>
|
||||
<div class="utc">
|
||||
<i>UTC time: {{getUTCTimes().startupTime}}</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label>Shutdown time:</label>
|
||||
<ngb-timepicker style="font-size: 10px;" [(ngModel)]="autoShutdown.shutdownTime" [meridian]="true" [minuteStep]="15" [disabled]="autoShutdown.is24x7"></ngb-timepicker>
|
||||
<div class="utc">
|
||||
<i>UTC time: {{getUTCTimes().shutdownTime}}</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section style="padding: 20px 0px; margin: 0px 20px">
|
||||
<mdb-checkbox [default]="false" [checked]="autoShutdown.is24x7" (change)="checkOnchangeAutoshutdown($event)">Run 24x7 <i>(<mdb-icon fas icon="exclamation-triangle"></mdb-icon> requires permission from regional manager)</i></mdb-checkbox>
|
||||
</section>
|
||||
</div>
|
||||
<div style="padding: 15px; margin: 7px 0px; background: #f0f0f0;" class="md-form" >
|
||||
|
||||
<div *ngFor="let server of scenario.availableProductVersions" style="margin-bottom: 15px;">
|
||||
<h5 style="font-weight: 600; margin-bottom: 0px;">- {{server.product}}</h5>
|
||||
<h5 style="font-weight: 600; margin-bottom: 0px;"><mdb-icon fas icon="server"></mdb-icon> {{server.product}}</h5>
|
||||
<div *ngIf="server.versions && server.versions.length">
|
||||
<div >
|
||||
<label>Product version: (*)</label>
|
||||
@@ -61,31 +80,17 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div *ngIf="scenario.isDivvyEnabled" style="background: #f0f0f0; padding: 0px 15px;">
|
||||
<hr>
|
||||
|
||||
<div style="background: #f0f0f0; padding: 15px 15px; margin: 7px 0px;">
|
||||
<div>
|
||||
<h5>Running window <small style="font-size: 50%;">(Everyday this scenario will startup and shutdown as scheduled bellow:)</small></h5>
|
||||
<h5><mdb-icon fas icon="globe-americas"></mdb-icon> External access</h5>
|
||||
</div>
|
||||
<div class="md-form row" *ngIf="!autoShutdown.is24x7">
|
||||
<div class="col-sm-6">
|
||||
<label>Startup time:</label>
|
||||
<ngb-timepicker style="font-size: 10px;" [(ngModel)]="autoShutdown.startupTime" [meridian]="true" [minuteStep]="15"></ngb-timepicker>
|
||||
<div>
|
||||
<i>UTC - {{getUTCTimes().startupTime}}</i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<label>Shutdown time:</label>
|
||||
<ngb-timepicker [(ngModel)]="autoShutdown.shutdownTime" [meridian]="true" [minuteStep]="15"></ngb-timepicker>
|
||||
<div>
|
||||
<i>UTC - {{getUTCTimes().shutdownTime}}</i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<section style="padding: 20px 0px; margin: 0px 20px">
|
||||
<mdb-checkbox [default]="false" [checked]="autoShutdown.is24x7" (change)="checkOnchangeAutoshutdown($event)">Enable 24x7 access <i>(Requires permission from regional manager)</i></mdb-checkbox>
|
||||
<section *ngIf="scenario.isExternal" style="padding-left: 20px;">
|
||||
<mdb-checkbox [default]="false" (change)="checkOnchange($event)">Enable (<mdb-icon fas icon="exclamation-triangle"></mdb-icon> only if it's strictly necessary)</mdb-checkbox>
|
||||
</section>
|
||||
<i *ngIf="!scenario.isExternal">This scenario only allows access from VPN</i>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer d-flex justify-content-center">
|
||||
|
||||
@@ -10,8 +10,14 @@ label {
|
||||
|
||||
::placeholder {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.utc {
|
||||
padding-left: 20px;
|
||||
padding-top: 5px;
|
||||
font-size: 12px
|
||||
}
|
||||
|
||||
.ngb-tp-meridian .btn {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
.disabled {
|
||||
opacity: 0.2;
|
||||
}
|
||||
@@ -1,7 +1,10 @@
|
||||
import { Component, Injectable, OnInit, OnDestroy } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { MDBModalRef } from 'angular-bootstrap-md';
|
||||
import { Subject, Subscription } from 'rxjs';
|
||||
import { ScenariosService } from '../services/scenarios.service';
|
||||
import { ProvisionsService } from '../services/provisions.service';
|
||||
|
||||
import * as moment from 'moment-timezone';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -38,9 +41,13 @@ export class NewProvisionConfirmComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
};
|
||||
|
||||
constructor( public modalRef: MDBModalRef, private _scenariosService: ScenariosService ) {}
|
||||
zone;
|
||||
|
||||
constructor( public modalRef: MDBModalRef, private _scenariosService: ScenariosService, private _provisionsService: ProvisionsService ) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.zone = moment.tz.guess(true);
|
||||
|
||||
this.vmTypesSub = this._scenariosService.getScenarioVmtypes().subscribe ( res => {
|
||||
this.vmTypes = res.results.filter(v=>!v.disabled);
|
||||
|
||||
@@ -68,34 +75,6 @@ export class NewProvisionConfirmComponent implements OnInit, OnDestroy {
|
||||
})
|
||||
}
|
||||
|
||||
private _addZero(i) {
|
||||
if (i < 10) {
|
||||
i = "0" + i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
getUTCTimes() : any {
|
||||
var dateStartup = new Date();
|
||||
var dateShutdown = new Date();
|
||||
dateStartup.setHours(this.autoShutdown.startupTime.hour);
|
||||
dateStartup.setMinutes(this.autoShutdown.startupTime.minute);
|
||||
dateStartup.setSeconds(0);
|
||||
dateShutdown.setHours(this.autoShutdown.shutdownTime.hour);
|
||||
dateShutdown.setMinutes(this.autoShutdown.shutdownTime.minute);
|
||||
dateShutdown.setSeconds(0);
|
||||
//var hourStart = this._addZero(dateStartup.getUTCHours());
|
||||
//var hourShut = this._addZero(dateShutdown.getUTCHours());
|
||||
var minShut = this._addZero(dateShutdown.getUTCMinutes());
|
||||
var minStart = this._addZero(dateStartup.getUTCMinutes());
|
||||
return {
|
||||
startupTime: dateStartup.getUTCHours() +":"+minStart,
|
||||
shutdownTime: dateShutdown.getUTCHours() +":"+minShut,
|
||||
tagStartup: this._addZero(dateStartup.getUTCHours())+""+minStart,
|
||||
tagShutdown: this._addZero(dateShutdown.getUTCHours())+""+minShut,
|
||||
timezoneOffset: dateStartup.getTimezoneOffset()
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
|
||||
@@ -131,13 +110,15 @@ export class NewProvisionConfirmComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
if ( this.scenario.isDivvyEnabled ) {
|
||||
if ( !this.autoShutdown.is24x7 ) {
|
||||
let autoShutdownUTC = this._provisionsService.getUTCTimes(this.autoShutdown, this.zone);
|
||||
this.sendData.autoShutdownData = {
|
||||
is24x7: false,
|
||||
utcTagStartupTime: this.getUTCTimes().tagStartup,
|
||||
utcTagShutdownTime: this.getUTCTimes().tagShutdown,
|
||||
localeStartupTime: this.autoShutdown.startupTime.hour+":"+this._addZero(this.autoShutdown.startupTime.minute),
|
||||
localeShutdownTime: this.autoShutdown.shutdownTime.hour+":"+this._addZero(this.autoShutdown.shutdownTime.minute),
|
||||
timezoneOffset: this.getUTCTimes().timezoneOffset
|
||||
utcTagStartupTime: autoShutdownUTC.tagStartup,
|
||||
utcTagShutdownTime: autoShutdownUTC.tagShutdown,
|
||||
localeStartupTime: this.autoShutdown.startupTime.hour+":"+this._provisionsService.addZero(this.autoShutdown.startupTime.minute),
|
||||
localeShutdownTime: this.autoShutdown.shutdownTime.hour+":"+this._provisionsService.addZero(this.autoShutdown.shutdownTime.minute),
|
||||
localTimezone: autoShutdownUTC.timezone,
|
||||
timezoneOffset: this.zone
|
||||
}
|
||||
} else {
|
||||
this.sendData.autoShutdownData = {
|
||||
@@ -159,4 +140,8 @@ export class NewProvisionConfirmComponent implements OnInit, OnDestroy {
|
||||
this.autoShutdown.is24x7 = $event.checked;
|
||||
}
|
||||
|
||||
getUTCTimes() {
|
||||
return this._provisionsService.getUTCTimes(this.autoShutdown, this.zone);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,40 +13,34 @@
|
||||
</div>
|
||||
<div class="contentbox">
|
||||
<div *ngIf="provision.statusVms">
|
||||
VMs status:
|
||||
Current VMs status:
|
||||
<span>
|
||||
<span style="text-transform: capitalize;" class="badge badge-secondary" [ngClass]="{'badge-success': provision.statusVms === 'Running', 'badge-warning': provision.statusVms === 'Stopped' || provision.statusVms === 'Stopping' || provision.statusVms === 'Starting'}">{{provision.statusVms}}</span>
|
||||
<span *ngIf="provision.statusVms === 'Starting' || provision.statusVms === 'Stopping'" class="spinner-border spinner-border-sm text-warning" role="status">
|
||||
<span class="sr-only"></span>
|
||||
</span>
|
||||
</span>
|
||||
( Running time:
|
||||
<span style="font-weight: bold;" *ngIf="provision.runningDays">{{provision.runningDays}}d </span>
|
||||
<span style="font-weight: bold;" *ngIf="provision.runningHours">{{provision.runningHours}}h </span>
|
||||
<span style="font-weight: bold;">{{provision.runningMinutes}}m</span> )
|
||||
|
||||
</div>
|
||||
<div *ngIf="provision.statusVms === 'Running' || provision.statusVms === 'Stopping'">
|
||||
VMs will Auto-Stop in ~
|
||||
<span style="font-weight: bold;">
|
||||
<span *ngIf="provision.autoshutdownDays">{{provision.autoshutdownDays}}d </span>
|
||||
<span *ngIf="provision.autoshutdownHours">{{provision.autoshutdownHours}}h </span>
|
||||
<span>{{provision.autoshutdownMinutes}}m</span>
|
||||
</span>
|
||||
</div>
|
||||
<div *ngIf="provision.autoShutdown">
|
||||
<span *ngIf="!provision.autoShutdown.is24x7"><mdb-icon far icon="calendar-alt"></mdb-icon> From <b>{{provision.autoShutdown.localeStartupTime}}h</b> until <b>{{provision.autoShutdown.localeShutdownTime}}h</b></span>
|
||||
<span *ngIf="provision.autoShutdown.is24x7"><mdb-icon far icon="calendar-alt"></mdb-icon> 24x7</span>
|
||||
</div>
|
||||
<div *ngIf="!provision.autoShutdown">
|
||||
<mdb-icon far icon="calendar-alt"></mdb-icon> 24x7
|
||||
</div>
|
||||
<div *ngIf="provision.statusVms">
|
||||
<mdb-icon far icon="clock"></mdb-icon> Accumulated running time: <span style="font-weight: bold;">{{provision.runningAsDays}}</span>
|
||||
</div>
|
||||
<div *ngIf="provision.statusVms === 'Running' || provision.statusVms === 'Stopping'">
|
||||
<mdb-icon fas icon="stopwatch"></mdb-icon>
|
||||
<span *ngIf="!provision.autoShutdown || provision.autoShutdown.is24x7"> 24x7 ends in ~</span>
|
||||
<span *ngIf="provision.autoShutdown && !provision.autoShutdown.is24x7"> Scheduled shutdown in ~</span>
|
||||
<span style="font-weight: bold;">{{provision.autoshutdownAsDays}}</span>
|
||||
</div>
|
||||
<div *ngIf="provision.statusVms === 'Stopped' || provision.statusVms === 'Starting'">
|
||||
VMs will Auto-Destroy in ~
|
||||
<span style="font-weight: bold;">
|
||||
<span *ngIf="provision.autoDestroyDays">{{provision.autoDestroyDays}}d </span>
|
||||
<span *ngIf="provision.autoDestroyHours">{{provision.autoDestroyHours}}h </span>
|
||||
<span>{{provision.autoDestroyMinutes}}m</span>
|
||||
</span>
|
||||
(Inactive for
|
||||
<span>
|
||||
<span *ngIf="provision.inactiveDays">{{provision.inactiveDays}}d </span>
|
||||
<span *ngIf="provision.inactiveHours">{{provision.inactiveHours}}h </span>
|
||||
<span>{{provision.inactiveMinutes}}m</span>
|
||||
</span>)
|
||||
<mdb-icon fas icon="stopwatch"></mdb-icon> Scenario auto-destroy in ~
|
||||
<span style="font-weight: bold;">{{provision.autoDestroyAsDays}}</span>
|
||||
(Inactive for {{provision.inactiveAsDays}})
|
||||
</div>
|
||||
<hr>
|
||||
<div>
|
||||
@@ -85,8 +79,8 @@
|
||||
<button style="margin-right: 5px;" mdbTooltip="Start VMs" *ngIf="provision.status === 'provisioned' && provision.statusVms === 'Stopped'" (click)="openConfirmStartModal(provision)" class="lui-button">
|
||||
<span class="lui-icon lui-icon--run" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button style="margin-right: 5px;" mdbTooltip="Extend Running period" *ngIf="provision.status === 'provisioned' && provision.statusVms === 'Running'" (click)="openConfirmExtendModal(provision)" class="lui-button">
|
||||
<mdb-icon fas icon="plus-square"></mdb-icon>4d
|
||||
<button style="margin-right: 5px;" mdbTooltip="Edit running schedule" *ngIf="provision._scenarioDoc.isDivvyEnabled && provision.status === 'provisioned'" (click)="openScheduleModal(provision)" class="lui-button">
|
||||
<mdb-icon far icon="calendar-alt"></mdb-icon>
|
||||
</button>
|
||||
<button style="float: right;" mdbTooltip="Destroy provision" *ngIf="!provision.isDestroyed && (provision.status === 'provisioned' || provision.status === 'error') && (!provision.destroy || provision.destroy.status !== 'destroying')" (click)="openConfirmDestroyModal(provision)" class="lui-button">
|
||||
<span class="lui-icon lui-icon--remove lui-text-danger" aria-hidden="true"></span>
|
||||
|
||||
@@ -7,6 +7,8 @@ import { AlertService } from '../services/alert.service';
|
||||
import { MDBModalService } from 'angular-bootstrap-md';
|
||||
import { ModalInfoComponent } from '../modals/modalinfo.component';
|
||||
import { ModalConfirmComponent } from '../modals/confirm.component';
|
||||
import { ProvisionModalComponent } from '../modals/edit-provision.component';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-provisions',
|
||||
@@ -186,6 +188,22 @@ export class ProvisionsComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
openScheduleModal(provision) {
|
||||
var modalRef = this.modalService.show(ProvisionModalComponent, {
|
||||
class: 'modal-lg modal-notify',
|
||||
containerClass: '',
|
||||
data: {
|
||||
provision: provision
|
||||
}
|
||||
} );
|
||||
|
||||
var sub = modalRef.content.action.subscribe( (data: any) => {
|
||||
sub.unsubscribe();
|
||||
|
||||
console.log("openScheduleModal returns", data);
|
||||
});
|
||||
}
|
||||
|
||||
showLogs($event, provision, type): void {
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { HttpClient, HttpParams } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { environment } from '../../environments/environment.prod';
|
||||
|
||||
import * as moment from 'moment';
|
||||
import * as moment from 'moment-timezone';
|
||||
|
||||
|
||||
@Injectable({
|
||||
@@ -12,8 +12,9 @@ import * as moment from 'moment';
|
||||
export class ProvisionsService {
|
||||
|
||||
selectedProv;
|
||||
RUNNING_PERIOD : number = 4;
|
||||
STOP_PERIOD : number = 20;
|
||||
RUNNING_PERIOD : number = 7; //days
|
||||
STOP_PERIOR_IS_EXTERNAL = 10; //days;
|
||||
STOP_PERIOD : number = 20; //days
|
||||
|
||||
constructor( private httpClient: HttpClient ) { }
|
||||
|
||||
@@ -55,6 +56,10 @@ export class ProvisionsService {
|
||||
return this.httpClient.put(`${environment.apiVersionPath}/provisions/${id}`, patch);
|
||||
}
|
||||
|
||||
updateProvisionUser(id, userId, patch): Observable<any> {
|
||||
return this.httpClient.put(`${environment.apiVersionPath}/users/${userId}/provisions/${id}`, patch);
|
||||
}
|
||||
|
||||
newDestroy(id, userId) : Observable<any> {
|
||||
return this.httpClient.post(`${environment.apiVersionPath}/users/${userId}/provisions/${id}/destroy`, null);
|
||||
}
|
||||
@@ -105,7 +110,7 @@ export class ProvisionsService {
|
||||
return this.selectedProv;
|
||||
}
|
||||
|
||||
timeRunning(p) : void{
|
||||
timeRunning(p) : void {
|
||||
if (!p.statusVms) {
|
||||
return;
|
||||
}
|
||||
@@ -117,37 +122,97 @@ export class ProvisionsService {
|
||||
totalRunningTime = totalRunningTime + Math.abs(now.getTime() - runningFromTime);
|
||||
}
|
||||
|
||||
let authShutdownDate = new Date(runningFromTime);
|
||||
authShutdownDate.setDate(authShutdownDate.getDate()+this.RUNNING_PERIOD);
|
||||
let autoshutDown = authShutdownDate.getTime() - now.getTime();
|
||||
|
||||
let durationAutoShutdown = moment.duration(autoshutDown);
|
||||
let durationRunningLeft;
|
||||
if ( !p.isDestroyed && p.statusVms !== 'Stopped' && p.autoShutdown && !p.autoShutdown.is24x7 ) {
|
||||
let split = p.autoShutdown.localeShutdownTime.split(":");
|
||||
let runningToDate = new Date();
|
||||
runningToDate.setHours(split[0], split[1]);
|
||||
durationRunningLeft = moment.duration(runningToDate.getTime() - now.getTime())
|
||||
} else {
|
||||
let runningFromDate = new Date(runningFromTime);
|
||||
var runningPeriodMils = this.RUNNING_PERIOD*24*60*60*1000; //7 days
|
||||
runningFromDate.setTime(runningFromDate.getTime()+runningPeriodMils);
|
||||
durationRunningLeft = moment.duration(runningFromDate.getTime() - now.getTime());
|
||||
}
|
||||
|
||||
let duration = moment.duration(totalRunningTime);
|
||||
p.runningDays = Math.floor(duration.asDays());
|
||||
p.runningHours = duration.hours();
|
||||
p.runningMinutes = duration.minutes();
|
||||
p.autoshutdownDays = Math.floor(durationAutoShutdown.asDays());
|
||||
p.autoshutdownHours = durationAutoShutdown.hours();
|
||||
p.autoshutdownMinutes = durationAutoShutdown.minutes();
|
||||
let runningDays = Math.floor(duration.asDays());
|
||||
let runningHours = duration.hours();
|
||||
let runningAsHours = Math.floor(duration.asHours());
|
||||
let runningMinutes = duration.minutes();
|
||||
|
||||
|
||||
p.runningAsDays = (runningDays? (runningDays+"d "):"") + (runningHours? runningHours+"h ": "") + runningMinutes+"m";
|
||||
p.runningAsHours = (runningAsHours? runningAsHours+"h ": "")+runningMinutes+"m";
|
||||
|
||||
let autoshutdownDays = Math.abs(Math.floor(durationRunningLeft.asDays()));
|
||||
let autoshutdownHours = durationRunningLeft.hours();
|
||||
let autoshutdownAsHours = Math.floor(durationRunningLeft.asHours())
|
||||
let autoshutdownMinutes = durationRunningLeft.minutes();
|
||||
|
||||
p.autoshutdownAsDays = (autoshutdownDays? (autoshutdownDays+"d "):"") + (autoshutdownHours? autoshutdownHours+"h ": "") + autoshutdownMinutes+"m";
|
||||
p.autoshutdownAsHours = (autoshutdownAsHours? autoshutdownAsHours+"h ": "")+runningMinutes+"m";
|
||||
|
||||
if ( (p.statusVms === 'Stopped' || p.statusVms === 'Starting') && !p.isDestroyed ) {
|
||||
let autoDestroyDate = new Date(p.stoppedFrom);
|
||||
autoDestroyDate.setDate(autoDestroyDate.getDate() + this.STOP_PERIOD);
|
||||
if (p.isExternalAccess){
|
||||
autoDestroyDate.setTime(autoDestroyDate.getTime() + this.STOP_PERIOR_IS_EXTERNAL*24*60*60*1000);
|
||||
} else {
|
||||
autoDestroyDate.setTime(autoDestroyDate.getTime() + this.STOP_PERIOD*24*60*60*1000);
|
||||
}
|
||||
let autoDestroy = autoDestroyDate.getTime() - now.getTime();
|
||||
let durationStop = moment.duration(autoDestroy);
|
||||
p.autoDestroyDays = Math.floor(durationStop.asDays());
|
||||
p.autoDestroyHours = durationStop.hours();
|
||||
p.autoDestroyMinutes = durationStop.minutes();
|
||||
let autoDestroyDays = Math.floor(durationStop.asDays());
|
||||
let autoDestroyHours = durationStop.hours();
|
||||
let autoDestroyAsHours = Math.floor(durationStop.asHours())
|
||||
let autoDestroyMinutes = durationStop.minutes();
|
||||
|
||||
p.autoDestroyAsDays = (autoDestroyDays? (autoDestroyDays+"d "):"") + (autoDestroyHours? autoDestroyHours+"h ": "") + autoDestroyMinutes+"m";
|
||||
p.autoshutdownAsHours = (autoDestroyAsHours? autoDestroyAsHours+"h ": "")+autoDestroyMinutes+"m";
|
||||
|
||||
let inactiveDate = new Date(p.stoppedFrom);
|
||||
let inactive = Math.abs(inactiveDate.getTime() - now.getTime());
|
||||
let durationInactive = moment.duration(inactive);
|
||||
p.inactiveDays = Math.floor(durationInactive.asDays());
|
||||
p.inactiveHours = durationInactive.hours();
|
||||
p.inactiveMinutes = durationInactive.minutes();
|
||||
let inactiveDays = Math.floor(durationInactive.asDays());
|
||||
let inactiveHours = durationInactive.hours();
|
||||
let inactiveAsHours = Math.floor(durationInactive.asHours());
|
||||
let inactiveMinutes = durationInactive.minutes();
|
||||
|
||||
p.inactiveAsDays = (inactiveDays? (inactiveDays+"d "):"") + (inactiveHours? inactiveHours+"h ": "") + inactiveMinutes+"m";
|
||||
p.inactiveAsHours = (inactiveAsHours? inactiveAsHours+"h ": "")+inactiveMinutes+"m";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
addZero(i) : any {
|
||||
if (i < 10) {
|
||||
i = "0" + i;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
getUTCTimes(autoShutdown, timezone) : any {
|
||||
var dateStartup = moment.tz(timezone);
|
||||
var dateShutdown = moment.tz(timezone);
|
||||
dateStartup.set({
|
||||
hour: autoShutdown.startupTime.hour,
|
||||
minute: autoShutdown.startupTime.minute,
|
||||
second: 0
|
||||
});
|
||||
dateShutdown.set({
|
||||
hour: autoShutdown.shutdownTime.hour,
|
||||
minute: autoShutdown.shutdownTime.minute,
|
||||
second: 0
|
||||
});
|
||||
return {
|
||||
startupTime: dateStartup.utc().format("H:mm"),
|
||||
shutdownTime: dateShutdown.utc().format("H:mm"),
|
||||
tagStartup: this.addZero(dateStartup.utc().format("H")) + dateStartup.utc().format("mm"),
|
||||
tagShutdown: this.addZero(dateShutdown.utc().format("H")) + dateShutdown.utc().format("mm"),
|
||||
timezoneOffset: moment.tz(timezone).utcOffset(),
|
||||
timezone: timezone
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -52,22 +52,13 @@
|
||||
<span class="sr-only"></span>
|
||||
</span>
|
||||
</span>
|
||||
<span *ngIf="provision.statusVms">(<span *ngIf="provision.runningDays">{{provision.runningDays}}d </span><span *ngIf="provision.runningHours">{{provision.runningHours}}h </span>{{provision.runningMinutes}}m)</span>
|
||||
<span *ngIf="provision.statusVms" mdbTooltip="As hours: {{provision.runningAsHours}}">({{provision.runningAsDays}})</span>
|
||||
<div style="font-size: 10px;" *ngIf="!provision.isDestroyed && provision.statusVms === 'Running'">
|
||||
<i>VMs Auto-Stop in: </i>
|
||||
<i>
|
||||
<span *ngIf="provision.autoshutdownDays">{{provision.autoshutdownDays}}d </span>
|
||||
<span *ngIf="provision.autoshutdownHours">{{provision.autoshutdownHours}}h </span>
|
||||
<span>{{provision.autoshutdownMinutes}}m</span>
|
||||
</i>
|
||||
<i><mdb-icon fas icon="stopwatch"></mdb-icon> Auto-shutdown in: {{provision.autoshutdownAsDays}}</i>
|
||||
</div>
|
||||
<div style="font-size: 10px;" *ngIf="!provision.isDestroyed && provision.statusVms === 'Stopped'">
|
||||
<i>VMs Auto-Destroy in: </i>
|
||||
<i>
|
||||
<span *ngIf="provision.autoDestroyDays">{{provision.autoDestroyDays}}d </span>
|
||||
<span *ngIf="provision.autoDestroyHours">{{provision.autoDestroyHours}}h </span>
|
||||
<span>{{provision.autoDestroyMinutes}}m</span>
|
||||
</i>
|
||||
<i><mdb-icon fas icon="stopwatch"></mdb-icon> Auto-destroy in: </i>
|
||||
<i>{{provision.autoDestroyAsDays}}</i>
|
||||
</div>
|
||||
</td>
|
||||
<td (click)="openInfoModal(provision._id)" *ngIf="pagingIsDisabled || (i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex)">
|
||||
|
||||
@@ -42,3 +42,12 @@ body {
|
||||
//@import "assets/album.css";
|
||||
/* Importing Bootstrap SCSS file. */
|
||||
@import '~bootstrap/scss/bootstrap';
|
||||
|
||||
.btn.btn-link {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.ngb-tp-meridian .btn {
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
@@ -7420,6 +7420,13 @@ moment-timezone@^0.5.25:
|
||||
dependencies:
|
||||
moment ">= 2.9.0"
|
||||
|
||||
moment-timezone@^0.5.31:
|
||||
version "0.5.31"
|
||||
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.31.tgz#9c40d8c5026f0c7ab46eda3d63e49c155148de05"
|
||||
integrity sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==
|
||||
dependencies:
|
||||
moment ">= 2.9.0"
|
||||
|
||||
"moment@>= 2.9.0", moment@^2.10.2, moment@^2.10.6, moment@^2.18.1, moment@^2.21.0, moment@^2.22.2:
|
||||
version "2.24.0"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b"
|
||||
|
||||