forked from RedHatInsights/frontend-components
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
4c22945
commit 467b6d4
Showing
8 changed files
with
784 additions
and
305 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,212 +1,194 @@ | ||
/* eslint-disable no-console */ | ||
// Webpack proxy and express config for `useProxy: true` or `standalone: true` | ||
const { execSync } = require('child_process'); | ||
const path = require('path'); | ||
const standaloneProxy = require('./standalone/proxy'); | ||
const { readFileSync } = require('fs'); | ||
const { sync } = require('glob'); | ||
const ESI = require('nodesi'); | ||
const cookieTransform = require('./cookieTransform'); | ||
const router = require('./standalone/helpers/router'); | ||
const { getConfig, isGitUrl, getExposedPort, resolvePath } = require('./standalone/helpers/index'); | ||
const { checkoutRepo } = require('./standalone/helpers/checkout'); | ||
const { startService, stopService } = require('./standalone/startService'); | ||
const { NET } = require('./standalone/helpers'); | ||
const defaultServices = require('./standalone/services/default'); | ||
const { registerChrome } = require('./standalone/services/default/chrome'); | ||
|
||
const defaultReposDir = path.join(__dirname, 'repos'); | ||
|
||
module.exports = ({ | ||
env = 'ci-beta', | ||
customProxy = [], | ||
routes, | ||
routesPath, | ||
useProxy, | ||
standalone, | ||
port, | ||
reposDir = defaultReposDir, | ||
localChrome, | ||
appUrl = [], | ||
publicPath, | ||
proxyVerbose, | ||
useCloud = false | ||
}) => { | ||
const proxy = []; | ||
const registry = []; | ||
const majorEnv = env.split('-')[0]; | ||
const minorEnv = majorEnv === 'prod' ? '' : `${majorEnv}.`; | ||
const target = env === 'prod-stable' | ||
? `https://${useCloud ? 'cloud' : 'console'}.redhat.com/` | ||
: `https://${minorEnv}${useCloud ? 'cloud' : 'console'}.redhat.com/`; | ||
if (!Array.isArray(appUrl)) { | ||
appUrl = [ appUrl ]; | ||
const transformUrl = require('./router'); | ||
|
||
module.exports = (options) => { | ||
const { | ||
betaEnv = 'ci', | ||
customProxy = [], | ||
routes, | ||
routesPath, | ||
standalone, | ||
port = 1337, | ||
localChrome, | ||
appUrl = [], | ||
publicPath, | ||
proxyVerbose, | ||
rootFolder, | ||
useCloud = false, | ||
https = true, | ||
exactUrl, | ||
disableFallback | ||
} = options; | ||
|
||
if (standalone) { | ||
return standaloneProxy(options); | ||
} | ||
|
||
appUrl.push(publicPath); | ||
let appUrls = appUrl; | ||
if (appUrls && !Array.isArray(appUrls)) { | ||
appUrls = [ appUrls ]; | ||
} | ||
|
||
if (routesPath) { | ||
routes = require(routesPath); | ||
let comparator = (path, url) => path.includes(url); | ||
if (exactUrl) { | ||
comparator = (path, url) => path === url; | ||
} | ||
|
||
if (routes) { | ||
routes = routes.routes || routes; | ||
console.log('Making proxy from SPANDX routes'); | ||
proxy.push( | ||
...Object.entries(routes || {}).map(([ route, redirect ]) => { | ||
const currTarget = redirect.host || redirect; | ||
delete redirect.host; | ||
return { | ||
context: (path) => path.includes(route), | ||
target: currTarget === 'PORTAL_BACKEND_MARKER' ? target : currTarget, | ||
secure: false, | ||
changeOrigin: true, | ||
autoRewrite: true, | ||
ws: true, | ||
onProxyReq: cookieTransform, | ||
...(currTarget === 'PORTAL_BACKEND_MARKER' && { router }), | ||
...typeof redirect === 'object' ? redirect : {} | ||
}; | ||
}) | ||
); | ||
// disable self signed CERT checks for esi | ||
if (appUrl) { | ||
process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0; | ||
} | ||
|
||
if (customProxy) { | ||
proxy.push(...customProxy); | ||
const target = betaEnv === 'prod' ? `https://${useCloud ? 'cloud' : 'console'}.redhat.com/` : `https://${betaEnv}.${useCloud ? 'cloud' : 'console'}.redhat.com/`; | ||
|
||
let proxyRoutes = routes; | ||
|
||
if (routesPath) { | ||
proxyRoutes = require(routesPath); | ||
} | ||
|
||
let standaloneConfig; | ||
if (standalone) { | ||
standaloneConfig = getConfig(standalone, localChrome, env, port); | ||
// Create network for services. | ||
execSync(`docker network inspect ${NET} >/dev/null 2>&1 || docker network create ${NET}`); | ||
|
||
// Clone repos | ||
// If we manage the repos it's okay to overwrite the contents | ||
const overwrite = reposDir === defaultReposDir; | ||
// Need to use for loop for `await` | ||
for (const [ projName, proj ] of Object.entries(standaloneConfig)) { | ||
const { services, path, assets, onProxyReq, keycloakUri, register, target, ...rest } = proj; | ||
if (typeof register === 'function') { | ||
registry.push(register); | ||
} | ||
|
||
if (isGitUrl(path)) { | ||
// Add typical branch if not included | ||
if (!path.includes('#')) { | ||
proj.path = `${path}#${env}`; | ||
} | ||
if (proxyRoutes) { | ||
proxyRoutes = proxyRoutes.routes || proxyRoutes; | ||
} | ||
|
||
proj.path = checkoutRepo({ repo: proj.path, reposDir, overwrite }); | ||
} | ||
const esi = appUrl && new ESI({ | ||
allowedHosts: [ /^https:\/\/.*cloud.redhat.com$/ ], | ||
baseUrl: `${https ? 'https' : 'http'}://${betaEnv}.foo.redhat.com:${port}`, | ||
cache: false, | ||
onError: (src, error) => { | ||
console.error( | ||
`An error occurred while resolving an ESI tag` | ||
); | ||
console.error(error); | ||
} | ||
}); | ||
|
||
Object.keys(assets || []).forEach(key => { | ||
if (isGitUrl(assets[key])) { | ||
assets[key] = checkoutRepo({ repo: assets[key], reposDir, overwrite }); | ||
} | ||
}); | ||
|
||
// Resolve functions that depend on env, port, or assets | ||
if (typeof services === 'function') { | ||
proj.services = services({ env, port, assets }); | ||
} | ||
|
||
// Start standalone services. | ||
const serviceNames = []; | ||
for (let [ subServiceName, subService ] of Object.entries(proj.services || {})) { | ||
const name = [ projName, subServiceName ].join('_'); | ||
startService(standaloneConfig, name, subService); | ||
serviceNames.push(name); | ||
const port = getExposedPort(subService.args); | ||
console.log('Container', name, 'listening', port ? 'on' : '', port || ''); | ||
} | ||
|
||
process.on('SIGINT', () => { | ||
console.log(); | ||
serviceNames.forEach(stopService); | ||
process.exit(); | ||
}); | ||
|
||
if (target) { | ||
proxy.push({ | ||
secure: false, | ||
changeOrigin: true, | ||
onProxyReq: cookieTransform, | ||
target, | ||
...rest | ||
}); | ||
} | ||
proxyVerbose && proxyRoutes && console.log(`Using proxy routes: ${JSON.stringify(proxyRoutes, null, 2)}`); | ||
|
||
const isNotCustomContext = (path) => { | ||
if (Object.keys(proxyRoutes || {}).some((route) => path.includes(route))) { | ||
return false; | ||
} | ||
} | ||
|
||
if (useProxy) { | ||
// Catch-all | ||
proxy.push({ | ||
secure: false, | ||
changeOrigin: true, | ||
autoRewrite: true, | ||
context: url => { | ||
const shouldProxy = !appUrl.find(u => url.startsWith(u)); | ||
if (shouldProxy) { | ||
console.log('proxy', url); | ||
return true; | ||
} | ||
return customProxy.length > 0 ? customProxy.reduce((acc, curr) => acc && !curr.context(path), true) : true; | ||
}; | ||
|
||
return false; | ||
}, | ||
target, | ||
router: router(target, useCloud) | ||
}); | ||
const isChrome = (path) => localChrome && path.includes(process.env.BETA ? '/beta/apps/chrome/' : '/apps/chrome/'); | ||
const pathInCustomUrl = (path) => appUrls && appUrls.some((customPath) => comparator(path, customPath)); | ||
const removeHashQuery = (path) => path.replace(/(\?|#).*/, ''); | ||
|
||
if (appUrls && proxyVerbose) { | ||
console.log('\n\nServing HTML on: ', appUrls.join(', ')); | ||
console.log('Exact URL: ', exactUrl ? 'true' : 'false', '\n\n'); | ||
} | ||
|
||
const router = transformUrl(target, useCloud); | ||
|
||
return { | ||
...(proxy.length > 0 && { proxy }), | ||
onListening(server) { | ||
if (useProxy || standaloneConfig) { | ||
const host = useProxy ? `${majorEnv}.foo.redhat.com` : 'localhost'; | ||
const origin = `http${server.options.https ? 's' : ''}://${host}:${server.options.port}`; | ||
console.log('App should run on:'); | ||
|
||
console.log('\u001b[34m'); // Use same webpack-dev-server blue | ||
if (appUrl.length > 0) { | ||
appUrl.forEach(url => console.log(` - ${origin}${url}`)); | ||
} else { | ||
console.log(` - ${origin}`); | ||
contentBase: `${rootFolder || ''}/dist`, | ||
index: `${rootFolder || ''}/dist/index.html`, | ||
host: `${betaEnv}.foo.redhat.com`, | ||
port, | ||
https, | ||
inline: true, | ||
disableHostCheck: true, | ||
historyApiFallback: true, | ||
writeToDisk: true, | ||
publicPath, | ||
proxy: [ | ||
{ | ||
context: (path) => isNotCustomContext(path) && !pathInCustomUrl(removeHashQuery(path)) && !isChrome(path), | ||
target, | ||
secure: false, | ||
changeOrigin: true, | ||
autoRewrite: true, | ||
router | ||
}, | ||
...(appUrl ? [{ | ||
context: path => pathInCustomUrl(removeHashQuery(path)), | ||
target, | ||
secure: false, | ||
changeOrigin: true, | ||
autoRewrite: true, | ||
selfHandleResponse: true, | ||
// serve index.html from local and replace ESI tags for chrome | ||
onProxyReq: async (_proxyReq, req, res) => { | ||
const fileName = removeHashQuery(req.url.split('/').pop()) || 'index.html'; | ||
let localPath = sync(`${rootFolder}/dist/${fileName}`); | ||
|
||
if (!localPath[0] && !disableFallback) { | ||
proxyVerbose && console.log(`page ${fileName} not found, fallback to index.html`); | ||
localPath = sync(`${rootFolder}/dist/index.html`); | ||
} | ||
|
||
proxyVerbose && console.log('serving locally', req.url, '>', fileName, '--->', localPath[0], '\n\n'); | ||
|
||
if (localPath[0]) { | ||
const localFile = readFileSync(localPath[0]); | ||
const newData = await esi.process(localFile.toString()); | ||
res.end(newData); | ||
} | ||
} | ||
|
||
console.log('\u001b[0m'); | ||
} | ||
}, | ||
before(app, server, compiler) { | ||
app.enable('strict routing'); // trailing slashes are mean | ||
let chromePath = localChrome; | ||
if (standaloneConfig) { | ||
chromePath = resolvePath(reposDir, standaloneConfig.chrome.path); | ||
} else if (!localChrome && useProxy) { | ||
if (typeof defaultServices.chrome === 'function') { | ||
defaultServices.chrome = defaultServices.chrome({}); | ||
}] : []), | ||
...(localChrome ? [{ | ||
context: (path) => isChrome(path), | ||
target, | ||
secure: false, | ||
changeOrigin: true, | ||
autoRewrite: true, | ||
ws: true, | ||
// When running chrome locally, we have to redirect all requests and serve files locally | ||
selfHandleResponse: true, | ||
onProxyReq: (_proxyReq, req, res) => { | ||
let newPath = req.url; | ||
const fileType = req.url.match(/\.([a-z]|\d)+$/); | ||
|
||
newPath = newPath.replace(process.env.BETA ? '/beta/apps/chrome/' : '/apps/chrome/', ''); //remove chrome URL | ||
|
||
let localPath = sync(`${localChrome}${newPath}`); // try to find it locally | ||
|
||
// if the file does not exist locally and it is a JS/CSS, let's try to remove hash | ||
if (localPath.length === 0 && fileType && (fileType[0] === '.js' || fileType[0] === '.css')) { | ||
newPath = newPath | ||
.replace(/\.([a-z]|\d)+\.[a-z]+$/, '**') //removeHash and add wilcard | ||
.concat(fileType[0]); // add file extension back | ||
|
||
localPath = sync(`${localChrome}${newPath}`); | ||
} | ||
|
||
proxyVerbose && console.log('serving locally', req.url, '--->', newPath, '------>', localPath[0], '\n\n'); | ||
|
||
// if there is a local file with the same name, it's served | ||
if (localPath[0]) { | ||
res.sendFile(localPath[0]); | ||
} | ||
} | ||
|
||
chromePath = checkoutRepo({ | ||
repo: `${defaultServices.chrome.path}#${env}`, | ||
reposDir, | ||
overwrite: true | ||
}); | ||
} | ||
|
||
if (chromePath) { | ||
registerChrome({ | ||
app, | ||
chromePath, | ||
keycloakUri: (standaloneConfig && standaloneConfig.chrome) ? standaloneConfig.chrome.keycloakUri : null, | ||
https: Boolean(server.options.https), | ||
proxyVerbose | ||
}); | ||
} | ||
|
||
registry.forEach(cb => cb({ | ||
app, | ||
server, | ||
compiler, | ||
config: standaloneConfig | ||
})); | ||
} | ||
}] : []), | ||
...proxyRoutes ? Object.entries(proxyRoutes).map(([ route, redirect ]) => { | ||
const currTarget = redirect.host || redirect; | ||
delete redirect.host; | ||
return { | ||
context: (path) => path.includes(route), | ||
target: currTarget === 'PORTAL_BACKEND_MARKER' ? target : currTarget, | ||
secure: false, | ||
changeOrigin: true, | ||
autoRewrite: true, | ||
ws: true, | ||
onProxyReq: (...args) => { | ||
cookieTransform(...args); | ||
}, | ||
...(currTarget === 'PORTAL_BACKEND_MARKER' && { router }), | ||
...typeof redirect === 'object' ? redirect : {} | ||
}; | ||
}) : [], | ||
...customProxy | ||
] | ||
}; | ||
}; | ||
|
Oops, something went wrong.