339 lines
11 KiB
JavaScript
339 lines
11 KiB
JavaScript
const url = require("url");
|
|
const express = require("express");
|
|
const { createProxyMiddleware } = require('http-proxy-middleware');
|
|
|
|
const Arena = require('bull-arena');
|
|
|
|
const MYQUEUES = require('@QMI/qmi-cloud-common/queues');
|
|
const TF_APPLY_QUEUE = MYQUEUES.TF_APPLY_QUEUE;
|
|
const TF_APPLY_QSEOK_QUEUE = MYQUEUES.TF_APPLY_QSEOK_QUEUE;
|
|
const TF_DESTROY_QUEUE = MYQUEUES.TF_DESTROY_QUEUE;
|
|
const STOP_CONTAINER_QUEUE = MYQUEUES.STOP_CONTAINER_QUEUE;
|
|
const SYNAPSE_QUEUE = MYQUEUES.SYNAPSE_QUEUE;
|
|
const WEBHOOK_QUEUE = MYQUEUES.WEBHOOK_QUEUE;
|
|
|
|
const app = express();
|
|
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 routesApiDivvy = require('./routes/api-divvy');
|
|
const routesApiDeployOpts = require('./routes/api-deployopts');
|
|
const routesApiApikeys = require('./routes/api-apikeys');
|
|
const routesApiWebhooks = require('./routes/api-webhooks');
|
|
const routesApiStats = require('./routes/api-stats');
|
|
const routesApiTraining = require('./routes/api-training');
|
|
const routesApiConfig = require('./routes/api-config');
|
|
const routesApiSnapshots= require('./routes/api-snapshots');
|
|
const swaggerUi = require('swagger-ui-express');
|
|
const swaggerJsdoc = require('swagger-jsdoc');
|
|
const cookieParser = require('cookie-parser');
|
|
const bodyParser = require('body-parser');
|
|
const https = require('https');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const passport = require('./passport-okta');
|
|
|
|
//const qsProxy = require("./routes/qsProxy");
|
|
|
|
const QMI_MONGO_URL = process.env.QMI_MONGO_URL || "http://host.docker.internal:8081/";
|
|
const BACKEND_LOGS_URL = process.env.BACKEND_LOGS_URL || "http://host.docker.internal:8888/";
|
|
const GUACAMOLE_URL = process.env.GUACAMOLE_URL || "http://qmicloud-dev.qliktech.com:8080/guacamole/";
|
|
|
|
const IS_SECURE = process.env.CERT_PFX_PASSWORD && process.env.CERT_PFX_FILENAME;
|
|
|
|
|
|
|
|
function _getRedisConfig(redisUrl) {
|
|
const redisConfig = url.parse(redisUrl);
|
|
return {
|
|
host: redisConfig.hostname || 'localhost',
|
|
port: Number(redisConfig.port || 6379),
|
|
database: (redisConfig.pathname || '/0').substr(1) || '0',
|
|
password: redisConfig.auth ? redisConfig.auth.split(':')[1] : undefined
|
|
};
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Config the app, include middlewares
|
|
//-----------------------------------------------------------------------------
|
|
//app.set('views', __dirname + '/views');
|
|
//app.set('view engine', 'ejs');
|
|
app.use(cookieParser());
|
|
|
|
/*app.use(function myMiddleware(req, res, next) {
|
|
res.set('X-Content-Type-Options', 'nosniff');
|
|
res.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
|
|
res.set('Cache-Control', 'max-age=86400');
|
|
res.set('Content-Security-Policy', "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://*.qlikcloud.com; connect-src 'self' https://*.amazonaws.com https://*.launchdarkly.com https://*.qlikcloud.com https://*.qlikdataengineering.com wss://innovation.us.qlikcloud.com; style-src 'self' 'unsafe-inline'; font-src 'self' *.qlikcloud.com; img-src 'self' data: https:; frame-src 'self' *.qlik-innovation.com; frame-ancestors 'self' *.qlikcloud.com *.qlik-innovation.com;");
|
|
next();
|
|
});*/
|
|
|
|
app.use('/', function myMiddleware(req, res, next) {
|
|
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
|
|
res.setHeader('Cache-Control', 'public, max-age=86400');
|
|
res.setHeader('Content-Security-Policy', "default-src 'self'; script-src 'self' https://cdn.jsdelivr.net https://*.qlikcloud.com 'unsafe-inline'; connect-src 'self' https://*.amazonaws.com https://*.launchdarkly.com https://*.qlikcloud.com https://*.qlikdataengineering.com wss://innovation.us.qlikcloud.com; style-src 'self' 'unsafe-inline'; font-src 'self' *.qlikcloud.com; img-src 'self' data: https:; frame-src 'self' *.qlik-innovation.com; frame-ancestors 'self' *.qlikcloud.com *.qlik-innovation.com;");
|
|
next();
|
|
}, express.static(__dirname + '/../dist/qmi-cloud'));
|
|
|
|
|
|
passport.init(app, IS_SECURE ? true : false);
|
|
|
|
/*
|
|
GUACAMOLE
|
|
*/
|
|
app.use('/guacamole/', passport.ensureAuthenticatedAndVPNDoLogin, createProxyMiddleware({
|
|
target: GUACAMOLE_URL,
|
|
ws: true,
|
|
changeOrigin: true,
|
|
followRedirects: true,
|
|
secure: false,
|
|
on: {
|
|
proxyReq: (proxyReq, req, res) => {
|
|
if (req.user && req.user.mail) {
|
|
proxyReq.setHeader('X-Guaca-Auth', req.user.mail);
|
|
}
|
|
},
|
|
error: (err, req, res) => {
|
|
console.log("error guac proxy", err);
|
|
}
|
|
}
|
|
}));
|
|
|
|
/*
|
|
DOZZLE
|
|
*/
|
|
app.use('/dockerlogs/', passport.ensureAuthenticatedAndAdmin, createProxyMiddleware({
|
|
target: BACKEND_LOGS_URL,
|
|
ws: false,
|
|
changeOrigin: true,
|
|
followRedirects: false,
|
|
secure: false
|
|
}
|
|
));
|
|
|
|
/*
|
|
MONGOEXPRESS
|
|
*/
|
|
app.use('/mongo', passport.ensureAuthenticatedAndAdmin, createProxyMiddleware({
|
|
target: QMI_MONGO_URL,
|
|
ws: false,
|
|
changeOrigin: true,
|
|
followRedirects: true,
|
|
secure: false
|
|
}
|
|
));
|
|
|
|
|
|
/*
|
|
Swagger DOCS
|
|
*/
|
|
const specs = swaggerJsdoc({
|
|
definition: {
|
|
openapi: "3.0.3",
|
|
// Like the one described here: https://swagger.io/specification/#infoObject
|
|
info: {
|
|
title: 'QMI Cloud - API',
|
|
version: '1.0.0',
|
|
description: 'REST API for QMI Cloud solutions',
|
|
contact: {
|
|
"name": "Qlik Presales - Innovation & Excellence Team",
|
|
"email": "DL-PresalesGlobalInnovation@qlik.com"
|
|
}
|
|
},
|
|
servers: [{
|
|
"url": "/api/v1",
|
|
"description": "Production Server"
|
|
}],
|
|
components: {
|
|
securitySchemes: {
|
|
ApiKeyAuth: {
|
|
type: "apiKey",
|
|
name: "apiKey",
|
|
in: "query"
|
|
}
|
|
}
|
|
},
|
|
security: [{
|
|
ApiKeyAuth: []
|
|
}]
|
|
},
|
|
// List of files to be processes. You can also set globs './routes/*.js'
|
|
apis: [
|
|
'server/routes/api-*.js'
|
|
]
|
|
});
|
|
app.use('/api-docs', [passport.ensureAuthenticatedAndVPNDoLogin,swaggerUi.serve], swaggerUi.setup(specs, {
|
|
"customSiteTitle": "QMI Cloud - API",
|
|
"customfavIcon": "/assets/favicon.svg"
|
|
}));
|
|
|
|
/*app.use('/arena', passport.ensureAuthenticatedAndVPNDoLogin, Arena(
|
|
{
|
|
queues: [
|
|
{
|
|
name: TF_APPLY_QUEUE,
|
|
hostId: 'Worker',
|
|
redis: _getRedisConfig(process.env.REDIS_URL)
|
|
},
|
|
{
|
|
name: TF_APPLY_QSEOK_QUEUE,
|
|
hostId: 'Worker',
|
|
redis: _getRedisConfig(process.env.REDIS_URL)
|
|
},
|
|
{
|
|
name: TF_DESTROY_QUEUE,
|
|
hostId: 'Worker',
|
|
redis: _getRedisConfig(process.env.REDIS_URL)
|
|
},
|
|
{
|
|
name: STOP_CONTAINER_QUEUE,
|
|
hostId: 'Worker',
|
|
redis: _getRedisConfig(process.env.REDIS_URL)
|
|
},
|
|
{
|
|
name: SYNAPSE_QUEUE,
|
|
hostId: 'Worker',
|
|
redis: _getRedisConfig(process.env.REDIS_URL)
|
|
},
|
|
{
|
|
name: WEBHOOK_QUEUE,
|
|
hostId: 'Worker',
|
|
redis: _getRedisConfig(process.env.REDIS_URL)
|
|
}
|
|
]
|
|
},
|
|
{
|
|
basePath: '/',
|
|
disableListen: true
|
|
}
|
|
));*/
|
|
|
|
app.use(bodyParser.urlencoded({ extended: false }));
|
|
// parse application/json
|
|
app.use(bodyParser.json());
|
|
|
|
const middlewareApi = function(req, res, next){
|
|
res.setHeader("Cache-Control", "no-cache, no-store");
|
|
res.removeHeader("Content-Security-Policy");
|
|
next();
|
|
}
|
|
app.use("/api/v1/scenarios", middlewareApi, routesApiScenarios);
|
|
app.use("/api/v1/users", middlewareApi, routesApiUsers);
|
|
app.use("/api/v1/provisions", middlewareApi, routesApiProvisions);
|
|
app.use("/api/v1/destroyprovisions", middlewareApi, routesApiDestroyProvisions);
|
|
app.use("/api/v1/notifications", middlewareApi, routesApiNotifications);
|
|
app.use("/api/v1/divvy", middlewareApi, routesApiDivvy);
|
|
app.use("/api/v1/deployopts", middlewareApi, routesApiDeployOpts);
|
|
app.use("/api/v1/apikeys", middlewareApi ,routesApiApikeys);
|
|
app.use("/api/v1/webhooks", middlewareApi, routesApiWebhooks);
|
|
app.use("/api/v1/stats", middlewareApi, routesApiStats);
|
|
app.use("/api/v1/training", middlewareApi, routesApiTraining);
|
|
app.use("/api/v1/config", middlewareApi, routesApiConfig);
|
|
app.use("/api/v1/snapshots", middlewareApi, routesApiSnapshots);
|
|
//app.use("/qcsproxy", qsProxy.router);
|
|
|
|
function _isAllowedPath(path) {
|
|
const allowedPaths = ['/api-docs', '/arena', '/costexport', '/backendlogs', '/photos/user/', '/qmimongo', '/guacamole/'];
|
|
let isAllowed = false;
|
|
for (let i = 0; i < allowedPaths.length; i++) {
|
|
if (path.startsWith(allowedPaths[i])) {
|
|
isAllowed = true;
|
|
break;
|
|
}
|
|
}
|
|
return isAllowed;
|
|
}
|
|
|
|
/* Checking allowedPaths */
|
|
app.get('/*', (req, res, next) => {
|
|
if (_isAllowedPath(req.originalUrl)) {
|
|
return next();
|
|
} else if (req.originalUrl.indexOf("oauth-callback.html") !== -1) {
|
|
res.sendFile(path.join(__dirname, '/../dist/qmi-cloud/oauth-callback.html'));
|
|
} else {
|
|
res.setHeader("Cache-Control", "no-cache, no-store");
|
|
res.sendFile(path.join(__dirname, '/../dist/qmi-cloud/index.html'));
|
|
}
|
|
});
|
|
/* -----------------------*/
|
|
|
|
|
|
app.get('/qmimongo/*', function (req, res) {
|
|
let path = req.path.split("/qmimongo")[1];
|
|
console.log("QMI-Mongo# Redirect: " + `${QMI_MONGO_URL}${path}`);
|
|
res.redirect(`${QMI_MONGO_URL}${path}`);
|
|
res.end();
|
|
});
|
|
|
|
|
|
|
|
app.use('/costexport*', passport.ensureAuthenticatedAndAdmin, function (req, res) {
|
|
if (!req.query.file) {
|
|
res.status(404).send("Not found");
|
|
} else {
|
|
res.header("Content-Type", 'application/json');
|
|
res.sendFile(path.resolve(__dirname, '..', 'costexport', req.query.file));
|
|
}
|
|
});
|
|
|
|
|
|
app.use('/photos/user/:oid', passport.ensureAuthenticatedPhoto, function (req, res) {
|
|
if (!req.params.oid) {
|
|
res.status(404).send("Not found");
|
|
} else {
|
|
var pic = path.resolve(__dirname, '..', 'photos', `${req.params.oid}.jpg`);
|
|
if (fs.existsSync(pic)) {
|
|
res.sendFile(pic);
|
|
} else {
|
|
res.status(404).send();
|
|
}
|
|
}
|
|
});
|
|
|
|
|
|
/**
|
|
* Create necessary folders
|
|
*/
|
|
|
|
var dirs = ['/logs', '/logs/provision', '/logs/destroy', '/costexports', '/photos'];
|
|
dirs.forEach(d => {
|
|
if (!fs.existsSync(d)) {
|
|
console.log(`--- Creating folder '${d}' since it does not exist`);
|
|
fs.mkdirSync(d);
|
|
}
|
|
});
|
|
|
|
|
|
/**
|
|
* Start App
|
|
*/
|
|
|
|
|
|
const server = app.listen(3000, () => {
|
|
console.log(`Server listening on port 3000`)
|
|
});
|
|
//qsProxy.init(server);
|
|
|
|
if (IS_SECURE) {
|
|
var optionsHttps = {
|
|
pfx: fs.readFileSync(path.resolve(__dirname, 'certs', process.env.CERT_PFX_FILENAME)),
|
|
passphrase: process.env.CERT_PFX_PASSWORD
|
|
};
|
|
|
|
const httpsServer = https.createServer(optionsHttps, app).listen(3100, function () {
|
|
console.log(`Secure server listening on port 3100`);
|
|
});
|
|
|
|
//qsProxy.init(httpsServer);
|
|
}
|
|
|
|
|
|
|
|
|