Files
qmi-cloud/server/server.js
Manuel Romero e71e85aed3 version 10 cdk
2025-06-18 11:45:31 +02:00

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);
}