Compare commits
23 Commits
p2
...
isexternal
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d742a4ff2f | ||
|
|
990b0d80e3 | ||
|
|
2d512b49be | ||
|
|
61b57d5bc7 | ||
|
|
8f48cfbc69 | ||
|
|
9cee830fd4 | ||
|
|
4f481fd88f | ||
|
|
2faf109353 | ||
|
|
19a0fa715e | ||
|
|
2e194b72b8 | ||
|
|
039a13bd30 | ||
|
|
26fa09541a | ||
|
|
cfbe52efc1 | ||
|
|
a267fedaef | ||
|
|
d6cb0fc78f | ||
|
|
1fbbbde1a1 | ||
|
|
08721bb810 | ||
|
|
68d2bef6ba | ||
|
|
5199cabd26 | ||
|
|
59546838ac | ||
|
|
4740163572 | ||
|
|
22af7f903e | ||
|
|
e921182575 |
2
dist/qmi-cloud/index.html
vendored
2
dist/qmi-cloud/index.html
vendored
@@ -9,5 +9,5 @@
|
||||
<link rel="stylesheet" href="styles.529f751cbb5308365172.css"></head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
<script src="runtime.689ba4fd6cadb82c1ac2.js" defer></script><script src="polyfills-es5.f752a17531a45fe93c1f.js" nomodule defer></script><script src="polyfills.06ba8d1a3d9dd3a8e8b9.js" defer></script><script src="scripts.6866cf66954a0b739d41.js" defer></script><script src="main.b7d3a2d901b927013a9e.js" defer></script></body>
|
||||
<script src="runtime.689ba4fd6cadb82c1ac2.js" defer></script><script src="polyfills-es5.f752a17531a45fe93c1f.js" nomodule defer></script><script src="polyfills.06ba8d1a3d9dd3a8e8b9.js" defer></script><script src="scripts.6866cf66954a0b739d41.js" defer></script><script src="main.ab99167120bc660b9a38.js" defer></script></body>
|
||||
</html>
|
||||
|
||||
1
dist/qmi-cloud/main.ab99167120bc660b9a38.js
vendored
Normal file
1
dist/qmi-cloud/main.ab99167120bc660b9a38.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/qmi-cloud/main.b7d3a2d901b927013a9e.js
vendored
1
dist/qmi-cloud/main.b7d3a2d901b927013a9e.js
vendored
File diff suppressed because one or more lines are too long
@@ -83,6 +83,7 @@ services:
|
||||
- REDIS_URL=redis://redis
|
||||
- MONGO_URI=mongodb://root:example@mongo/qmicloud?authSource=admin
|
||||
- PROJECT_PATH=${PWD}
|
||||
- GIT_SCENARIOS=git::git@gitlab.com:qmi/qmi-cloud-scenarios.git
|
||||
- SSHPATH=/Users/aor/.ssh
|
||||
command: "sh -c 'npm run worker:dev'"
|
||||
volumes:
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
branch=master
|
||||
rm -fr ./az-tf-templates
|
||||
git clone -b $branch git@gitlab.com:qmi/qmi-cloud-scenarios.git ./az-tf-templates
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "qmi-cloud",
|
||||
"version": "1.0.10",
|
||||
"version": "1.0.13",
|
||||
"scripts": {
|
||||
"start": "node -r esm server/server.js",
|
||||
"dev": "nodemon -r esm server/server.js",
|
||||
|
||||
@@ -6,8 +6,8 @@ if ( myArgs.length < 3 ) {
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
var db = require('./mongo');
|
||||
const sendEmail = require("./send-email");
|
||||
var db = require('../mongo');
|
||||
const sendEmail = require("../send-email");
|
||||
const moment = require('moment');
|
||||
const fetch = require('node-fetch');
|
||||
|
||||
@@ -36,7 +36,7 @@ async function postDestroy(provision) {
|
||||
}
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(json => console.log(json));
|
||||
.then(json => console.log(json));
|
||||
}
|
||||
|
||||
|
||||
@@ -11,10 +11,10 @@ const RUNNING_PERIOD = myArgs[1]; //Days
|
||||
const RUNNING_LIMIT_HOURS_WARNING = 24*(RUNNING_PERIOD-1);
|
||||
const RUNNING_LIMIT_HOURS_STOP = 24*RUNNING_PERIOD;
|
||||
|
||||
var db = require('./mongo');
|
||||
const sendEmail = require("./send-email");
|
||||
var db = require('../mongo');
|
||||
const sendEmail = require("../send-email");
|
||||
const moment = require('moment');
|
||||
const azurecli = require('./azurecli');
|
||||
const azurecli = require('../azurecli');
|
||||
|
||||
function timeRunning(p) {
|
||||
let runningFromTime = p.runningFrom? new Date(p.runningFrom).getTime() : new Date(p.created).getTime();
|
||||
@@ -28,6 +28,10 @@ const provisionSchema = new mongoose.Schema({
|
||||
logFile: String,
|
||||
outputs: Object,
|
||||
path: String,
|
||||
isExternalAccess: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isDestroyed: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
|
||||
@@ -32,6 +32,10 @@ const scenarioSchema = new mongoose.Schema({
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isDisabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
requred: true
|
||||
|
||||
@@ -14,11 +14,17 @@ const userSchema = new mongoose.Schema({
|
||||
},
|
||||
displayName: String,
|
||||
upn: String,
|
||||
oid: String,
|
||||
oid: {
|
||||
type: String,
|
||||
index: true
|
||||
},
|
||||
role: {
|
||||
type: String,
|
||||
default: "user"
|
||||
}
|
||||
},
|
||||
lastLogin: {
|
||||
type: Date
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@ mongoose.set('useFindAndModify', false);
|
||||
|
||||
const userSchema = new mongoose.Schema({
|
||||
type: String,
|
||||
desc: String
|
||||
desc: String,
|
||||
costHour: Number
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -53,27 +53,42 @@ const getNewTimeRunning = function (provision) {
|
||||
return Math.floor(minutesFromLastRunning + timeRunning);
|
||||
};
|
||||
|
||||
const get = async (model, filter, extras, reply) => {
|
||||
var sort = extras && extras.sort? extras.sort : {created: -1};
|
||||
|
||||
|
||||
const get = async (model, filter, skip, limit, reply) => {
|
||||
var sort = {created: -1};
|
||||
try {
|
||||
var exec = model.find(filter).sort(sort);
|
||||
var totalDocs = await model.countDocuments(filter);
|
||||
|
||||
if ( extras && extras.skip ) {
|
||||
exec = exec.skip(extras.skip);
|
||||
}
|
||||
if ( extras && extras.limit ) {
|
||||
exec = exec.limit(extras.limit);
|
||||
skip = skip? parseInt(skip) : 0;
|
||||
exec = exec.skip(skip);
|
||||
|
||||
if ( limit ) {
|
||||
limit = parseInt(limit);
|
||||
exec = exec.limit(limit);
|
||||
}
|
||||
|
||||
if ( model === Provision ) {
|
||||
exec = exec.populate({ path: 'user', select: 'displayName upn'}).populate('destroy');
|
||||
}
|
||||
|
||||
if ( model === ApiKey ) {
|
||||
exec = exec.populate('user');
|
||||
}
|
||||
|
||||
const entity = await exec;
|
||||
|
||||
return {
|
||||
total: await model.countDocuments(filter),
|
||||
var out = {
|
||||
total: totalDocs,
|
||||
count: entity.length,
|
||||
results: entity
|
||||
};
|
||||
}
|
||||
if ( limit && (skip + limit) < totalDocs) {
|
||||
out.nextSkip = skip+limit;
|
||||
out.nextLimit = limit;
|
||||
}
|
||||
return out;
|
||||
|
||||
} catch (err) {
|
||||
throw boom.boomify(err)
|
||||
}
|
||||
@@ -85,6 +100,9 @@ const getById = async (model, id, reply) => {
|
||||
if ( model === Provision ) {
|
||||
exec = exec.populate({ path: 'user', select: 'displayName upn'}).populate('destroy');
|
||||
}
|
||||
if ( model === ApiKey ) {
|
||||
exec = exec.populate('user');
|
||||
}
|
||||
const entity = await exec;
|
||||
return entity;
|
||||
} catch (err) {
|
||||
@@ -98,6 +116,9 @@ const getOne = async (model, filter, reply) => {
|
||||
if ( model === Provision ) {
|
||||
exec = exec.populate('user').populate('destroy');
|
||||
}
|
||||
if ( model === ApiKey ) {
|
||||
exec = exec.populate('user');
|
||||
}
|
||||
const entity = await exec;
|
||||
return entity;
|
||||
} catch (err) {
|
||||
@@ -141,8 +162,8 @@ const del = async (model, id, reply) => {
|
||||
|
||||
function _m(model) {
|
||||
return {
|
||||
get: async (filter, extras, reply) => {
|
||||
return get(model, filter, extras, reply);
|
||||
get: async (filter, skip, limit, reply) => {
|
||||
return get(model, filter, skip, limit, reply);
|
||||
},
|
||||
getById: async (id, reply) => {
|
||||
return getById(model, id, reply);
|
||||
|
||||
@@ -82,28 +82,30 @@ passport.use(new OIDCStrategy({
|
||||
if ( !profile.oid ) {
|
||||
return done(new Error("No oid found"), null);
|
||||
}
|
||||
console.log("accessToken", accessToken);
|
||||
console.log("iss", iss);
|
||||
console.log("sub", sub);
|
||||
console.log("refreshToken", refreshToken);
|
||||
console.log("jwtClaims", jwtClaims);
|
||||
console.log("params", params);
|
||||
console.log("profile", profile);
|
||||
//console.log("accessToken", accessToken);
|
||||
//console.log("iss", iss);
|
||||
//console.log("sub", sub);
|
||||
//console.log("refreshToken", refreshToken);
|
||||
//console.log("jwtClaims", jwtClaims);
|
||||
//console.log("params", params);
|
||||
console.log("New Auth: profile", profile);
|
||||
// asynchronous verification, for effect...
|
||||
process.nextTick(function () {
|
||||
_findByOid(profile.oid, async function(err, user) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
return done(err);
|
||||
}
|
||||
if (!user) {
|
||||
// "Auto-registration"
|
||||
user = await db.user.add({
|
||||
"oid": profile.oid,
|
||||
"upn": profile.upn,
|
||||
"displayName": profile.displayName
|
||||
"displayName": profile.displayName,
|
||||
"lastLogin": new Date()
|
||||
});
|
||||
return done(null, user);
|
||||
}
|
||||
db.user.update(user._id, {"lastLogin": new Date()});
|
||||
return done(null, user);
|
||||
});
|
||||
});
|
||||
@@ -198,13 +200,16 @@ module.exports.init = function(app){
|
||||
};
|
||||
|
||||
async function isApiKeyAuthenticated(req) {
|
||||
if (req.query && req.query.apiKey){
|
||||
if (req.query && req.query.apiKey){
|
||||
let key = req.query.apiKey;
|
||||
var result = await db.apiKey.get({apiKey: key});
|
||||
if ( result.length > 0 ) {
|
||||
req.user = result[0].user;
|
||||
var result = await db.apiKey.getOne({"apiKey": key});
|
||||
if ( result ) {
|
||||
req.user = result.user;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return result.length > 0;
|
||||
|
||||
}else {
|
||||
return false;
|
||||
}
|
||||
|
||||
28
server/routes/api-notifications.js
Normal file
28
server/routes/api-notifications.js
Normal file
@@ -0,0 +1,28 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const db = require('../mongo');
|
||||
const passport = require('../passport');
|
||||
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /notifications:
|
||||
* get:
|
||||
* description: Get all notifications (Only admin)
|
||||
* summary: Get all notifications (Only admin)
|
||||
* produces:
|
||||
* - application/json
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Notifications
|
||||
*/
|
||||
router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
try {
|
||||
const result = await db.notification.get();
|
||||
return res.json(result);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
@@ -22,28 +22,44 @@ const fs = require('fs-extra');
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* - name: extras
|
||||
* - name: skip
|
||||
* in: query
|
||||
* required: false
|
||||
* type: object
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* type: integer
|
||||
* - name: limit
|
||||
* in: query
|
||||
* required: false
|
||||
* type: integer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: JSON Array
|
||||
*/
|
||||
router.get('/', passport.ensureAuthenticatedAndAdmin, async (req, res, next) => {
|
||||
|
||||
|
||||
try {
|
||||
let filter = req.query.filter? JSON.parse(req.query.filter) : {};
|
||||
if ( filter.isDeleted === undefined ) {
|
||||
filter.isDeleted = false;
|
||||
}
|
||||
let extras = req.query.extras? JSON.parse(req.query.extras) : {};
|
||||
const result = await db.provision.get(filter, extras);
|
||||
return res.json(result);
|
||||
|
||||
const result = await db.provision.get(filter, req.query.skip, req.query.limit);
|
||||
|
||||
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 ( req.query.filter ) {
|
||||
out.nextUrl.searchParams.append("filter", req.query.filter);
|
||||
}
|
||||
out.nextUrl.searchParams.append("skip", result.nextSkip);
|
||||
out.nextUrl.searchParams.append("limit", result.nextLimit);
|
||||
}
|
||||
out.results = result.results;
|
||||
|
||||
return res.json(out);
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ router.get('/', passport.ensureAuthenticated, async (req, res, next) => {
|
||||
if (req.user.role === "user") {
|
||||
filter.isAdminOnly = false;
|
||||
}
|
||||
filter.isDisabled = filter.isDisabled || false;
|
||||
const result = await db.scenario.get(filter);
|
||||
return res.json(result);
|
||||
|
||||
@@ -42,7 +43,7 @@ router.get('/', passport.ensureAuthenticated, async (req, res, next) => {
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /scenarios:
|
||||
* /scenarios/vmtypes:
|
||||
* get:
|
||||
* description: Get scenarios VM Types
|
||||
* summary: Get scenarios VM Types
|
||||
|
||||
@@ -9,6 +9,7 @@ const routesApiScenarios = require('./routes/api-scenarios');
|
||||
const routesApiUsers = require('./routes/api-users');
|
||||
const routesApiProvisions = require('./routes/api-provisions');
|
||||
const routesApiDestroyProvisions = require('./routes/api-destroyprovisions');
|
||||
const routesApiNotifications = require('./routes/api-notifications');
|
||||
const swaggerUi = require('swagger-ui-express');
|
||||
const swaggerJsdoc = require('swagger-jsdoc');
|
||||
const cookieParser = require('cookie-parser');
|
||||
@@ -75,6 +76,7 @@ app.use("/api/v1/scenarios", routesApiScenarios);
|
||||
app.use("/api/v1/users", routesApiUsers);
|
||||
app.use("/api/v1/provisions", routesApiProvisions);
|
||||
app.use("/api/v1/destroyprovisions", routesApiDestroyProvisions);
|
||||
app.use("/api/v1/notifications", routesApiNotifications);
|
||||
|
||||
app.get('/*',(req, res, next) =>{
|
||||
if (req.originalUrl.indexOf("/api-docs") !== -1 || req.originalUrl.indexOf("/arena") !== -1 ) {
|
||||
@@ -147,6 +149,7 @@ const options = {
|
||||
'server/routes/api-users.js',
|
||||
'server/routes/api-provisions.js',
|
||||
'server/routes/api-destroyprovisions.js',
|
||||
'server/routes/api-notifications.js',
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
@@ -2,10 +2,9 @@ const Docker = require('dockerode');
|
||||
const docker = new Docker({
|
||||
'socketPath': '/home/docker.sock'
|
||||
});
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const PROJECT_PATH = process.env.PROJECT_PATH;
|
||||
const DOCKERIMAGE = "qlikgear/terraform:1.0.0";
|
||||
const GIT_SCENARIOS = process.env.GIT_SCENARIOS;
|
||||
const DOCKERIMAGE = "qlikgear/terraform:1.0.1";
|
||||
const SSHPATH = process.env.SSHPATH;
|
||||
|
||||
function hook_stdout(callback) {
|
||||
@@ -48,11 +47,21 @@ function _buildExec( exec, provision ) {
|
||||
exec.push(`vm_type_${key}=${provision.vmImage[key].vmType}`);
|
||||
}
|
||||
|
||||
if (provision.vmImage[key].diskSizeGb) {
|
||||
exec.push('-var');
|
||||
exec.push(`disk_size_gb_${key}=${provision.vmImage[key].diskSizeGb}`);
|
||||
}
|
||||
|
||||
if (provision.vmImage[key].version) {
|
||||
exec.push('-var');
|
||||
exec.push(`image_reference_${key}=${provision.vmImage[key].version.image}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (provision.isExternalAccess) {
|
||||
exec.push('-var');
|
||||
exec.push(`is_external_access=${provision.isExternalAccess}`);
|
||||
}
|
||||
|
||||
return exec;
|
||||
@@ -60,11 +69,10 @@ function _buildExec( exec, provision ) {
|
||||
|
||||
const init = function( provMongo ) {
|
||||
|
||||
const templatePath = path.join(PROJECT_PATH, 'az-tf-templates', provMongo.scenario);
|
||||
const name = `qmi-tf-init-${provMongo._id}`;
|
||||
console.log(`Init: will spin up container: ${name}`);
|
||||
var processStream = fs.createWriteStream(provMongo.logFile, {flags:'a'});
|
||||
let exec = ['terraform', 'init', '-no-color', '-from-module=/template'];
|
||||
let exec = ['terraform', 'init', '-no-color', `-from-module=${GIT_SCENARIOS}//${provMongo.scenario}`];
|
||||
console.log('Init: exec: '+exec.join(" "));
|
||||
|
||||
return docker.run(DOCKERIMAGE, exec, processStream, {
|
||||
@@ -73,8 +81,7 @@ const init = function( provMongo ) {
|
||||
"WorkingDir": "/app",
|
||||
"HostConfig": {
|
||||
"Binds": [
|
||||
`${provMongo.path}:/app`,
|
||||
`${templatePath}:/template`
|
||||
`${provMongo.path}:/app`
|
||||
]
|
||||
}
|
||||
}).then(function(data) {
|
||||
|
||||
5
shell-utils/checkdestroy.sh
Executable file
5
shell-utils/checkdestroy.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
BASEDIR=$(dirname "$0")
|
||||
d=`date`
|
||||
echo "------ $d"
|
||||
echo "------ TYPE: $1"
|
||||
MONGO_URI=mongodb://root:example@localhost:27017/qmicloud?authSource=admin API_KEY="c229219ccdd72d11e8ea253fd3876d247e5f489c9c84922cabdfb0cc194d8ff398a8d8d6528d8241efc99add2207e0ec75122a1b2c5598cc340cbe6b7c3c0dbf" node $BASEDIR/../server/cronjobs/destroy5.js $1 $2 $3
|
||||
@@ -1,4 +1,5 @@
|
||||
BASEDIR=$(dirname "$0")
|
||||
d=`date`
|
||||
echo "------ $d"
|
||||
MONGO_URI=mongodb://root:example@localhost:27017/qmicloud?authSource=admin node $BASEDIR/stop5.js $1 $2 $3
|
||||
echo "------ TYPE: $1"
|
||||
MONGO_URI=mongodb://root:example@localhost:27017/qmicloud?authSource=admin node $BASEDIR/../server/cronjobs/stop5.js $1 $2 $3
|
||||
6
shell-utils/mongodump.sh
Executable file
6
shell-utils/mongodump.sh
Executable file
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
id=`docker ps | grep 'mongo:4.2' | awk '{ print $1 }'`
|
||||
d=$(date +%Y-%m-%d-T%H:%M:%S%z)
|
||||
|
||||
docker exec $id sh -c 'exec mongodump --uri="mongodb://root:example@mongo/qmicloud?authSource=admin" --archive' > $1/qmicloud-$d.archive
|
||||
@@ -1,12 +1,6 @@
|
||||
<ul style="margin-top: 80px;" class="nav nav-pills nav-fill">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" (click)="tabSelect($event, 'Provisions')" [ngClass]="{'active': tab === 'Provisions'}">Provisions</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" (click)="tabSelect($event, 'Users')" [ngClass]="{'active': tab === 'Users'}">Users</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" (click)="tabSelect($event, 'Scenarios')" [ngClass]="{'active': tab === 'Scenarios'}">Scenarios</a>
|
||||
<li *ngFor="let item of sections; let i = index" class="nav-item">
|
||||
<a class="nav-link" (click)="tabSelect($event, item)" [ngClass]="{'active': tab === item}">{{item}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -24,4 +18,9 @@
|
||||
<!--<h1>Scenarios</h1>-->
|
||||
<table-scenarios></table-scenarios>
|
||||
</div>
|
||||
|
||||
<div *ngIf="tab === 'Notifications'">
|
||||
<!--<h1>Scenarios</h1>-->
|
||||
<table-notifications></table-notifications>
|
||||
</div>
|
||||
<qmi-alert></qmi-alert>
|
||||
|
||||
@@ -7,22 +7,18 @@ import { Component, OnInit } from '@angular/core';
|
||||
})
|
||||
export class AdminComponent implements OnInit {
|
||||
|
||||
sections = ['Provisions', 'Users', 'Scenarios', 'Notifications'];
|
||||
tab : string = 'Provisions';
|
||||
|
||||
|
||||
constructor() { }
|
||||
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
|
||||
tabSelect($event, tab) {
|
||||
$event.preventDefault();
|
||||
$event.stopPropagation();
|
||||
|
||||
this.tab = tab;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<b>Product version: </b> <span>{{item.value.version.name}}</span>
|
||||
</div>
|
||||
<div *ngIf="item.value.vmType" >
|
||||
<b>Instance Type: </b> <span>{{item.value.vmType}}</span> <span *ngIf="item.value.nodeCount">( {{item.value.nodeCount}} nodes )</span>
|
||||
<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>
|
||||
</div>
|
||||
<h5 *ngIf="info.outputs" class="info-subtitle">Connection resources</h5>
|
||||
|
||||
@@ -6,37 +6,57 @@
|
||||
</button>
|
||||
<h4 class="modal-title w-100 font-weight-bold">New provision</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<h5>{{scenario.title}}</h5>
|
||||
<div class="modal-body" style="max-height: 650px; overflow: auto;">
|
||||
<!--<h5>{{scenario.title}}</h5>-->
|
||||
<div class="md-form">
|
||||
<div *ngFor="let server of scenario.availableProductVersions">
|
||||
<div>
|
||||
<div>Purpose: (*)</div>
|
||||
<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</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</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 *ngFor="let server of scenario.availableProductVersions" style="margin-bottom: 15px;">
|
||||
<h5 style="font-weight: 600; margin-bottom: 0px;">- {{server.product}}</h5>
|
||||
<div *ngIf="server.versions && server.versions.length">
|
||||
<div >
|
||||
<label>Product version for {{server.product}}: (*)</label>
|
||||
<label>Product version: (*)</label>
|
||||
</div>
|
||||
<select id="pversion" class="browser-default custom-select" [(ngModel)]="selectedProductVersion[server.index]">
|
||||
<select id="pversion" class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedProductVersion[server.index]">
|
||||
<option *ngFor="let v of server.versions" [value]="v.name">{{v.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>VM size for {{server.product}}: (*)</label>
|
||||
<label>VM type: (*)</label>
|
||||
</div>
|
||||
<select id="vmtype" class="browser-default custom-select" [(ngModel)]="selectedVmType[server.index]">
|
||||
<select id="vmtype" class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedVmType[server.index]">
|
||||
<option *ngFor="let item of vmTypes" [value]="item.type">{{item.type}} ({{item.desc}})</option>
|
||||
</select>
|
||||
<div>
|
||||
<label>Disk size (GiB): (*)</label>
|
||||
</div>
|
||||
<select class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedDiskSizeGb[server.index]">
|
||||
<option *ngFor="let item of [128,250,500,750,1000]" [value]="item">{{item}}</option>
|
||||
</select>
|
||||
<div *ngIf="server.nodeCount">
|
||||
<label>Num. nodes: (*)</label>
|
||||
</div>
|
||||
<select *ngIf="scenario.nodeCount" id="pnodes" class="browser-default custom-select" [(ngModel)]="selectedNodeCount[server.index]">
|
||||
<select *ngIf="scenario.nodeCount" class="browser-default custom-select custom-select-sm" [(ngModel)]="selectedNodeCount[server.index]">
|
||||
<option *ngFor="let item of [1,2,3,4,5,6]" [value]="item">{{item}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="padding-top: 20px;">
|
||||
<div>Set purpose (short description): (*)</div>
|
||||
<textarea [(ngModel)]="sendData.description" type="text" class="md-textarea form-control" rows="2" mdbInput></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer d-flex justify-content-center">
|
||||
<button mdbBtn color="dark-green" size="sm" outline="true" class="waves-effect" mdbWavesEffect (click)="modalRef.hide()">Cancel</button>
|
||||
<button mdbBtn color="dark-green" class="waves-light" size="sm" mdbWavesEffect (click)="confirm();">Create</button>
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
label {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.md-form {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: #ccc;
|
||||
}
|
||||
@@ -15,11 +15,13 @@ export class NewProvisionConfirmComponent implements OnInit, OnDestroy {
|
||||
shortDesc: string;
|
||||
sendData = {
|
||||
description: "",
|
||||
servers: null
|
||||
servers: null,
|
||||
isExternalAccess: false,
|
||||
};
|
||||
selectedProductVersion: any = {};
|
||||
selectedVmType: any = {};
|
||||
selectedNodeCount: any = {};
|
||||
selectedDiskSizeGb: any = {};
|
||||
vmTypesSub: Subscription;
|
||||
vmTypes: any;
|
||||
servers: any = {};
|
||||
@@ -39,6 +41,9 @@ export class NewProvisionConfirmComponent implements OnInit, OnDestroy {
|
||||
if ( server.nodeCount ) {
|
||||
this.selectedNodeCount[server.index] = server.nodeCount;
|
||||
}
|
||||
|
||||
this.selectedDiskSizeGb[server.index] = server.diskSizeGbDefault || 500;
|
||||
|
||||
if ( server.versions && server.versions.length ) {
|
||||
let lastIndex = server.versions.length - 1;
|
||||
this.selectedProductVersion[server.index] = server.productVersionDefault? server.productVersionDefault : server.versions[lastIndex].name;
|
||||
@@ -72,6 +77,10 @@ export class NewProvisionConfirmComponent implements OnInit, OnDestroy {
|
||||
this.sendData.servers[key].nodeCount = this.selectedNodeCount[key];
|
||||
}
|
||||
|
||||
if ( this.selectedDiskSizeGb[key] ) {
|
||||
this.sendData.servers[key].diskSizeGb = this.selectedDiskSizeGb[key];
|
||||
}
|
||||
|
||||
this.scenario.availableProductVersions.forEach(server => {
|
||||
server.versions.forEach(v=> {
|
||||
if (v.name === this.selectedProductVersion[key]){
|
||||
@@ -80,8 +89,14 @@ export class NewProvisionConfirmComponent implements OnInit, OnDestroy {
|
||||
})
|
||||
});
|
||||
}
|
||||
this.action.next(this.sendData);
|
||||
console.log("sendData", this.sendData);
|
||||
//this.action.next(this.sendData);
|
||||
this.modalRef.hide();
|
||||
}
|
||||
|
||||
checkOnchange($event) {
|
||||
console.log("Checked?", $event.checked);
|
||||
this.sendData.isExternalAccess = $event.checked;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { TableProvisionsAdminComponent } from './tables/table-provisions.component';
|
||||
import { TableScenariosComponent } from './tables/table-scenarios.component';
|
||||
import { TableUsersComponent } from './tables/table-users.component';
|
||||
import { TableNotificationsComponent } from './tables/table-notifications.component';
|
||||
import { AlertComponent } from './alert/alert.component';
|
||||
import { AlertService } from './services/alert.service';
|
||||
import { ModalInfoComponent } from './alert/modalinfo.component';
|
||||
@@ -60,7 +61,8 @@ export function markedOptions(): MarkedOptions {
|
||||
FilterPipe,
|
||||
FaqComponent,
|
||||
NewProvisionConfirmComponent,
|
||||
TableScenariosComponent
|
||||
TableScenariosComponent,
|
||||
TableNotificationsComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
<p>
|
||||
<mdb-icon fas icon="globe-americas" size="lg" class="grey-text pr-1" aria-hidden="true"></mdb-icon>Scenario with External Access available
|
||||
<span *ngIf="user && (user.role === 'admin' || user.role === 'superadmin')"><mdb-icon fas icon="user-secret" size="lg" class="ml-5 grey-text pr-1" aria-hidden="true"></mdb-icon>Only Administrators can see</span>
|
||||
</p>
|
||||
|
||||
<div class="md-form">
|
||||
<mdb-icon fas icon="search" aria-hidden="true"></mdb-icon>
|
||||
<input type="search" [(ngModel)]="searchText" class="ml-2" placeholder="Search text">
|
||||
</div>
|
||||
<p>
|
||||
<mdb-icon fas icon="globe-americas" size="lg" class="grey-text pr-1" aria-hidden="true"></mdb-icon>Scenario with External Access
|
||||
<span *ngIf="user && (user.role === 'admin' || user.role === 'superadmin')"><mdb-icon fas icon="user-secret" size="lg" class="ml-5 grey-text pr-1" aria-hidden="true"></mdb-icon>Only Administrators can see</span>
|
||||
</p>
|
||||
|
||||
<div class="flexcontainer">
|
||||
|
||||
<mdb-card class="qmicard" *ngFor="let s of scenarios | filter: searchText">
|
||||
<!--Card content-->
|
||||
<div class="card-badge">
|
||||
<mdb-icon *ngIf="s.isExternal" fas icon="globe-americas" size="lg" class="grey-text" aria-hidden="true" mdbTooltip="External Access" placement="top"></mdb-icon>
|
||||
<mdb-icon *ngIf="s.isExternal" fas icon="globe-americas" size="lg" class="grey-text" aria-hidden="true" mdbTooltip="External Access available" placement="top"></mdb-icon>
|
||||
<mdb-icon *ngIf="s.isAdminOnly" fas icon="user-secret" size="lg" class="grey-text" aria-hidden="true" mdbTooltip="Only Administrators can see" placement="top"></mdb-icon>
|
||||
</div>
|
||||
<mdb-card-header>
|
||||
|
||||
@@ -40,7 +40,6 @@ export class ScenariosComponent implements OnInit, OnDestroy {
|
||||
ngOnDestroy() {}
|
||||
|
||||
openNewProvisionConfirmModal(scenario) {
|
||||
console.log("scenario", scenario);
|
||||
var modalRef = this.modalService.show(NewProvisionConfirmComponent, {
|
||||
class: 'modal-md modal-notify',
|
||||
containerClass: '',
|
||||
@@ -54,16 +53,13 @@ export class ScenariosComponent implements OnInit, OnDestroy {
|
||||
|
||||
const postData = {
|
||||
scenario: scenario.name,
|
||||
description: data.description
|
||||
description: data.description,
|
||||
isExternalAccess: data.isExternalAccess
|
||||
};
|
||||
|
||||
if ( data.servers ) {
|
||||
|
||||
if ( data.servers ) {
|
||||
postData["vmImage"] = data.servers;
|
||||
|
||||
}
|
||||
|
||||
console.log("postData", postData);
|
||||
|
||||
this._provisionsService.newProvision(postData, this.user._id).subscribe( res => {
|
||||
console.log("Done!", res);
|
||||
|
||||
@@ -21,4 +21,8 @@ export class UsersService {
|
||||
updateUser(userId, patchData): Observable<any> {
|
||||
return this.httpClient.put(`${environment.apiVersionPath}/users/${userId}`, patchData);
|
||||
}
|
||||
|
||||
getNotifications(): Observable<any> {
|
||||
return this.httpClient.get(`${environment.apiVersionPath}/notifications`);
|
||||
}
|
||||
}
|
||||
|
||||
46
src/app/tables/table-notifications.component.html
Normal file
46
src/app/tables/table-notifications.component.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<div class="md-form">
|
||||
<input type="text" class="form-control w-25" [(ngModel)]="searchText" (keyup)="searchItems()" id="search-input2"
|
||||
mdbInput>
|
||||
<label for="search-input2">Search</label>
|
||||
</div>
|
||||
<div style="padding: 5px 0px;">
|
||||
<button *ngIf="loading" mdbBtn color="grey" outline="true" type="button" size="sm" disabled mdbWavesEffect>
|
||||
<span>Refreshing...</span>
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button *ngIf="!loading" (click)="refreshData();" mdbBtn type="button" size="sm" color="grey" outline="true" mdbWavesEffect>
|
||||
Refresh<mdb-icon fas icon="redo-alt" class="ml-1" ></mdb-icon>
|
||||
</button>
|
||||
<span *ngIf="elements && elements.length">Total: {{elements.length}}</span>
|
||||
</div>
|
||||
<table mdbTable #tableEl="mdbTable" stickyHeader="true" hover="true" class="z-depth-1 table table-sm">
|
||||
<thead class="sticky-top">
|
||||
<tr>
|
||||
<th style="width: 155px;">Date</th>
|
||||
<th>Type</th>
|
||||
<th>ProvisionID</th>
|
||||
<th>Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let item of elements; let i = index">
|
||||
<th *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex"
|
||||
scope="row">{{item.created | date: 'MMM dd, yyyy - H:mm'}}</th>
|
||||
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">
|
||||
<span style="text-transform: capitalize;" class="badge" [ngClass]="{'badge-warning': item.type.indexOf('warning') !== -1, 'badge-secondary': item.type.indexOf('warning') === -1}">
|
||||
{{item.type.replace("warning", "")}}
|
||||
</span>
|
||||
|
||||
</td>
|
||||
<th *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">{{item.provision}}</th>
|
||||
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">{{item.message}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot class="grey lighten-5 w-100">
|
||||
<tr>
|
||||
<td colspan="4">
|
||||
<mdb-table-pagination [tableEl]="tableEl" [searchDataSource]="elements"></mdb-table-pagination>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
3
src/app/tables/table-notifications.component.scss
Normal file
3
src/app/tables/table-notifications.component.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
.table td, .table th {
|
||||
vertical-align: middle;
|
||||
}
|
||||
82
src/app/tables/table-notifications.component.ts
Normal file
82
src/app/tables/table-notifications.component.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { MdbTablePaginationComponent, MdbTableDirective } from 'angular-bootstrap-md';
|
||||
|
||||
import { Component, OnInit, ViewChild, HostListener, AfterViewInit, ChangeDetectorRef } from '@angular/core';
|
||||
import { UsersService } from '../services/users.service';
|
||||
|
||||
@Component({
|
||||
selector: 'table-notifications',
|
||||
templateUrl: './table-notifications.component.html',
|
||||
styleUrls: ['./table-notifications.component.scss']
|
||||
})
|
||||
export class TableNotificationsComponent implements OnInit, AfterViewInit {
|
||||
|
||||
@ViewChild(MdbTablePaginationComponent, { static: true }) mdbTablePagination: MdbTablePaginationComponent;
|
||||
@ViewChild(MdbTableDirective, { static: true }) mdbTable: MdbTableDirective;
|
||||
|
||||
previous: any = [];
|
||||
searchText: string = '';
|
||||
maxVisibleItems: number = 25;
|
||||
|
||||
loading: boolean = false;
|
||||
elements = [];
|
||||
|
||||
@HostListener('input') oninput() {
|
||||
this.mdbTablePagination.searchText = this.searchText;
|
||||
}
|
||||
|
||||
constructor(private cdRef: ChangeDetectorRef, private _usersService: UsersService) {
|
||||
}
|
||||
|
||||
private _initElements(): void {
|
||||
this.mdbTable.setDataSource(this.elements);
|
||||
this.elements = this.mdbTable.getDataSource();
|
||||
this.previous = this.mdbTable.getDataSource();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.refreshData();
|
||||
}
|
||||
|
||||
refreshData() {
|
||||
this.loading = true;
|
||||
this.searchText = "";
|
||||
var sub = this._usersService.getNotifications().subscribe( res => {
|
||||
sub.unsubscribe();
|
||||
this.elements = res.results;
|
||||
this.loading = false;
|
||||
this._initElements();
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
|
||||
this.mdbTablePagination.setMaxVisibleItemsNumberTo(this.maxVisibleItems);
|
||||
|
||||
this.mdbTablePagination.calculateFirstItemIndex();
|
||||
this.mdbTablePagination.calculateLastItemIndex();
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
|
||||
searchItems() {
|
||||
const prev = this.mdbTable.getDataSource();
|
||||
|
||||
if (!this.searchText) {
|
||||
this.mdbTable.setDataSource(this.previous);
|
||||
this.elements = this.mdbTable.getDataSource();
|
||||
}
|
||||
|
||||
if (this.searchText) {
|
||||
this.elements = this.mdbTable.searchLocalDataBy(this.searchText);
|
||||
this.mdbTable.setDataSource(prev);
|
||||
}
|
||||
|
||||
this.mdbTablePagination.calculateFirstItemIndex();
|
||||
this.mdbTablePagination.calculateLastItemIndex();
|
||||
|
||||
this.mdbTable.searchDataObservable(this.searchText).subscribe(() => {
|
||||
this.mdbTablePagination.calculateFirstItemIndex();
|
||||
this.mdbTablePagination.calculateLastItemIndex();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,7 +7,16 @@
|
||||
<label for="search-input">Search</label>
|
||||
</div>
|
||||
|
||||
<p *ngIf="elements && elements.length">Total: {{elements.length}}</p>
|
||||
<div style="padding: 5px 0px;">
|
||||
<button *ngIf="loading" mdbBtn color="grey" outline="true" type="button" size="sm" disabled mdbWavesEffect>
|
||||
<span>Refreshing...</span>
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button *ngIf="!loading" (click)="refreshData();" mdbBtn type="button" size="sm" color="grey" outline="true" mdbWavesEffect>
|
||||
Refresh<mdb-icon fas icon="redo-alt" class="ml-1" ></mdb-icon>
|
||||
</button>
|
||||
<span *ngIf="elements && elements.length">Total: {{elements.length}}</span>
|
||||
</div>
|
||||
|
||||
<table mdbTable #tableEl="mdbTable" stickyHeader="true" hover="true" class="z-depth-1 table table-sm">
|
||||
<thead class="sticky-top">
|
||||
@@ -16,7 +25,8 @@
|
||||
<th [mdbTableSort]="elements" sortBy="created" >Prov. Date <mdb-icon fas icon="sort"></mdb-icon></th>
|
||||
<th [mdbTableSort]="elements" sortBy="user.displayName">User <mdb-icon fas icon="sort"></mdb-icon></th>
|
||||
<th [mdbTableSort]="elements" sortBy="scenario">Scenario <mdb-icon fas icon="sort"></mdb-icon></th>
|
||||
<th [mdbTableSort]="elements" sortBy="statusVms">Status VMs (Running time)<mdb-icon fas icon="sort"></mdb-icon></th>
|
||||
<th [mdbTableSort]="elements" sortBy="isExternalAccess">Ext. Access? <mdb-icon fas icon="sort"></mdb-icon></th>
|
||||
<th [mdbTableSort]="elements" sortBy="statusVms">VMs (Running time)<mdb-icon fas icon="sort"></mdb-icon></th>
|
||||
<th [mdbTableSort]="elements" sortBy="status">Status <mdb-icon fas icon="sort"></mdb-icon></th>
|
||||
<th style="border-left:2px solid #ccc;">DestroyID</th>
|
||||
<th>Dest. Date</th>
|
||||
@@ -32,6 +42,7 @@
|
||||
<td (click)="openInfoModal(provision)" *ngIf="pagingIsDisabled || (i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex)">{{provision.created | date: 'MMM dd, yyyy - H:mm'}}</td>
|
||||
<td (click)="openInfoModal(provision)" *ngIf="pagingIsDisabled || (i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex)" class="ell" title="{{provision.path}}" >{{provision.user.displayName}}</td>
|
||||
<td (click)="openInfoModal(provision)"*ngIf="pagingIsDisabled || (i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex)">{{provision.scenario}}</td>
|
||||
<td style="text-align: center;" (click)="openInfoModal(provision)"*ngIf="pagingIsDisabled || (i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex)"><mdb-icon *ngIf="provision.isExternalAccess" fas icon="check"></mdb-icon></td>
|
||||
<td (click)="openInfoModal(provision)" *ngIf="pagingIsDisabled || (i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex)">
|
||||
<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>
|
||||
@@ -84,6 +95,9 @@
|
||||
<button style="margin-right: 3px;" title="Remove entry" *ngIf="provision.isDestroyed && provision.destroy.status === 'destroyed'" (click)="openConfirmDeleteModal(provision);" class="lui-button lui-text-danger">
|
||||
<span class="lui-icon lui-icon--bin" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button style="margin-right: 3px;" title="Extend Running VMs 4 days" *ngIf="!provision.isDestroyed && provision.status === 'provisioned' && provision.statusVms === 'Running'" (click)="extend(provision)" class="lui-button">
|
||||
+4d
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
tr:hover {
|
||||
.table tr:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -15,7 +15,6 @@ import { ScenariosService } from '../services/scenarios.service';
|
||||
export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterViewInit {
|
||||
|
||||
scenarios;
|
||||
provisions;
|
||||
subscription: Subscription;
|
||||
filter = {
|
||||
showDestroyed : false
|
||||
@@ -23,6 +22,7 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
|
||||
filterParams : any = {
|
||||
isDestroyed: false
|
||||
};
|
||||
loading: boolean = false;
|
||||
|
||||
pagingIsDisabled: Boolean = false;
|
||||
|
||||
@@ -63,10 +63,10 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
|
||||
p._scenario = this.scenarios.filter(s => s.name === p.scenario);
|
||||
this._provisionsService.timeRunning(p);
|
||||
});
|
||||
if ( !this.provisions ) {
|
||||
this.provisions = provisions;
|
||||
if ( this.elements.length === 0 ) {
|
||||
this.elements = provisions;
|
||||
} else {
|
||||
this.provisions.forEach( function(p, index, object) {
|
||||
this.elements.forEach( function(p, index, object) {
|
||||
let found = provisions.filter(a=>a._id.toString() === p._id.toString());
|
||||
if ( found.length ) {
|
||||
p.status = found[0].status;
|
||||
@@ -81,14 +81,13 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
|
||||
}.bind(this));
|
||||
|
||||
provisions.forEach(function(p) {
|
||||
let found = this.provisions.filter(a=>a._id.toString() === p._id.toString());
|
||||
let found = this.elements.filter(a=>a._id.toString() === p._id.toString());
|
||||
if (found.length === 0){
|
||||
this.provisions.unshift(p);
|
||||
this.elements.unshift(p);
|
||||
}
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
this.elements = this.provisions;
|
||||
this._initElements();
|
||||
|
||||
}
|
||||
@@ -99,9 +98,12 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
|
||||
scenariosSub.unsubscribe();
|
||||
this.scenarios = res.results;
|
||||
|
||||
this.subscription = timer(0, 8000).pipe( switchMap(() => this._provisionsService.getProvisionsAdmin(this.filterParams) ) ).subscribe(provisions => {
|
||||
/*this.subscription = timer(0, 8000).pipe( switchMap(() => this._provisionsService.getProvisionsAdmin(this.filterParams) ) ).subscribe(provisions => {
|
||||
this._process(provisions.results);
|
||||
});
|
||||
});*/
|
||||
|
||||
this.refreshData();
|
||||
|
||||
});
|
||||
|
||||
//this._initElements();
|
||||
@@ -289,11 +291,14 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
|
||||
});
|
||||
}
|
||||
|
||||
private _refresh(): void {
|
||||
this.provisions = null;
|
||||
refreshData() {
|
||||
this.elements = [];
|
||||
this.loading = true;
|
||||
this.searchText = "";
|
||||
var instantSubs = this._provisionsService.getProvisionsAdmin(this.filterParams).subscribe( provisions=>{
|
||||
instantSubs.unsubscribe();
|
||||
this._process(provisions.results);
|
||||
this.loading = false;
|
||||
this._process(provisions.results);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -302,7 +307,20 @@ export class TableProvisionsAdminComponent implements OnInit, OnDestroy, AfterVi
|
||||
if ( !this.filter.showDestroyed ) {
|
||||
this.filterParams.isDestroyed = false;
|
||||
}
|
||||
this._refresh();
|
||||
this.refreshData();
|
||||
}
|
||||
|
||||
extend(provision) : void {
|
||||
this._provisionsService.extend(provision._id.toString(), provision.user._id).subscribe( res => {
|
||||
provision.countExtend = res.countExtend;
|
||||
provision.timeRunning = res.timeRunning;
|
||||
provision.runningFrom = res.runningFrom;
|
||||
this._provisionsService.timeRunning(provision);
|
||||
this._alertService.showAlert({
|
||||
type: 'alert-primary',
|
||||
text: `Running period extended another ${this._provisionsService.RUNNING_PERIOD} days (from now) for provision '${provision.scenario}'`
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,6 +13,7 @@
|
||||
<th>IsAdminOnly</th>
|
||||
<th>IsExternal</th>
|
||||
<th>IsWafPolicyAppGw</th>
|
||||
<th>IsDisabled</th>
|
||||
<th>NewImageName(gen)</th>
|
||||
|
||||
</tr>
|
||||
@@ -27,6 +28,7 @@
|
||||
<td style="text-align: center;" *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex"><mdb-checkbox [checked]="item.isAdminOnly" [default]="false" (change)="FieldsChange(item, 'isAdminOnly', $event)"></mdb-checkbox></td>
|
||||
<td style="text-align: center;" *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex"><mdb-checkbox [checked]="item.isExternal" [default]="false" (change)="FieldsChange(item, 'isExternal', $event)"></mdb-checkbox></td>
|
||||
<td style="text-align: center;" *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex"><mdb-checkbox [checked]="item.isWafPolicyAppGw" [default]="false" (change)="FieldsChange(item, 'isWafPolicyAppGw', $event)"></mdb-checkbox></td>
|
||||
<td style="text-align: center;" *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex"><mdb-checkbox [checked]="item.isDisabled" [default]="false" (change)="FieldsChange(item, 'isDisabled', $event)"></mdb-checkbox></td>
|
||||
<td contenteditable="true" (keyup)="changeValue(item._id, 'newImageName', $event)" (blur)="updateList(item, 'newImageName', $event)" *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">{{item.newImageName}}</td>
|
||||
|
||||
</tr>
|
||||
|
||||
@@ -3,34 +3,51 @@
|
||||
mdbInput>
|
||||
<label for="search-input2">Search</label>
|
||||
</div>
|
||||
<div style="padding: 5px 0px;">
|
||||
<button *ngIf="loading" mdbBtn color="grey" outline="true" type="button" size="sm" disabled mdbWavesEffect>
|
||||
<span>Refreshing...</span>
|
||||
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||
</button>
|
||||
<button *ngIf="!loading" (click)="refreshData();" mdbBtn type="button" size="sm" color="grey" outline="true" mdbWavesEffect>
|
||||
Refresh<mdb-icon fas icon="redo-alt" class="ml-1" ></mdb-icon>
|
||||
</button>
|
||||
<span *ngIf="elements && elements.length">Total: {{elements.length}}</span>
|
||||
</div>
|
||||
<table mdbTable #tableEl="mdbTable" stickyHeader="true" hover="true" class="z-depth-1 table table-sm">
|
||||
<thead class="sticky-top">
|
||||
<tr>
|
||||
<th>Id</th>
|
||||
<th [mdbTableSort]="elements" sortBy="created">Created <mdb-icon fas icon="sort"></mdb-icon></th>
|
||||
<th>Name</th>
|
||||
<th>Email</th>
|
||||
<th>Role</th>
|
||||
<th></th>
|
||||
<th style="text-align: center;">Is Admin?</th>
|
||||
<th [mdbTableSort]="elements" sortBy="lastLogin">LastLogin <mdb-icon fas icon="sort"></mdb-icon></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr *ngFor="let user of elements; let i = index">
|
||||
<tr mdbTableCol *ngFor="let user of elements; let i = index">
|
||||
<th *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex"
|
||||
scope="row">{{user._id}}</th>
|
||||
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">{{user.created | date: 'MMM dd, yyyy - H:mm'}}</td>
|
||||
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">{{user.displayName}}</td>
|
||||
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">{{user.upn}}</td>
|
||||
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex"><span *ngIf="user.role">{{user.role}}</span></td>
|
||||
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">
|
||||
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex"><span>{{user.role}}</span></td>
|
||||
<td style="text-align: center;" *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">
|
||||
<mdb-checkbox *ngIf="user.role !== 'superadmin' && currentUser && currentUser._id.toString() !== user._id.toString()" [checked]="user.role === 'admin' || user.role === 'superadmin'" [default]="false" (change)="FieldsChange(user, $event)"></mdb-checkbox>
|
||||
</td>
|
||||
<!--<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex">
|
||||
<div *ngIf="currentUser && currentUser._id.toString() !== user._id.toString()">
|
||||
<button *ngIf="user.role !== 'admin' && user.role !== 'superadmin'" class="lui-button" (click)="setAdmin(user)">Set Admin</button>
|
||||
<button *ngIf="user.role === 'admin'" class="lui-button" (click)="removeAdmin(user)">Remove Admin</button>
|
||||
</div>
|
||||
</td>
|
||||
</td>-->
|
||||
<td *ngIf="i+1 >= mdbTablePagination.firstItemIndex && i < mdbTablePagination.lastItemIndex"><span *ngIf="user.lastLogin">{{user.lastLogin | date: 'MMM dd, yyyy - H:mm'}}</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot class="grey lighten-5 w-100">
|
||||
<tr>
|
||||
<td colspan="5">
|
||||
<td colspan="6">
|
||||
<mdb-table-pagination [tableEl]="tableEl" [searchDataSource]="elements"></mdb-table-pagination>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
@@ -19,7 +19,7 @@ export class TableUsersComponent implements OnInit, AfterViewInit {
|
||||
maxVisibleItems: number = 25;
|
||||
|
||||
currentUser;
|
||||
|
||||
loading: boolean = false;
|
||||
elements = [];
|
||||
|
||||
@HostListener('input') oninput() {
|
||||
@@ -39,12 +39,21 @@ export class TableUsersComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.refreshData();
|
||||
}
|
||||
|
||||
refreshData() {
|
||||
this.loading = true;
|
||||
this.searchText = "";
|
||||
var usersSub = this._usersService.getUsers().subscribe( res => {
|
||||
usersSub.unsubscribe();
|
||||
res.results.forEach(u=>{
|
||||
u.lastLogin = u.lastLogin || u.created;
|
||||
});
|
||||
this.elements = res.results;
|
||||
this.loading = false;
|
||||
this._initElements();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
@@ -78,22 +87,11 @@ export class TableUsersComponent implements OnInit, AfterViewInit {
|
||||
});
|
||||
}
|
||||
|
||||
setAdmin(user) : void {
|
||||
this._usersService.updateUser(user._id, {"role": "admin"}).subscribe( res1 => {
|
||||
this._usersService.getUsers().subscribe( res => {
|
||||
this.elements = res.results;
|
||||
this._initElements();
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
removeAdmin(user) : void {
|
||||
this._usersService.updateUser(user._id, {"role": "user"}).subscribe( res1 => {
|
||||
this._usersService.getUsers().subscribe( res => {
|
||||
this.elements = res.results;
|
||||
this._initElements();
|
||||
});
|
||||
})
|
||||
FieldsChange(user: any, value:any) {
|
||||
var patchData = {"role": value.checked? "admin": "user"};
|
||||
this._usersService.updateUser(user._id, patchData).subscribe( res1 => {
|
||||
user.role = res1.role;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
BASEDIR=$(dirname "$0")
|
||||
|
||||
branch=master
|
||||
|
||||
cd $BASEDIR/az-tf-templates
|
||||
git checkout master
|
||||
git checkout .
|
||||
git pull origin $branch
|
||||
cd $BASEDIR
|
||||
Reference in New Issue
Block a user