infrastructure-components
Version:
Infrastructure-Components configure the infrastructure of your React-App as part of your React-Components.
379 lines (284 loc) • 15.8 kB
text/typescript
/**
* This module must not import anything globally not workin in web-mode! if needed, require it within the functions
*/
import { isIsomorphicApp } from './iso-component';
import { resolveAssetsPath, getStaticBucketName } from '../libs/iso-libs';
import * as deepmerge from 'deepmerge';
import { IConfigParseResult } from '../libs/config-parse-result';
import {IPlugin, forwardChildIamRoleStatements} from '../libs/plugin';
import { PARSER_MODES } from '../libs/parser';
/**
* Parameters that apply to the whole Plugin, passed by other plugins
*/
export interface IIsoPlugin {
/**
* one of the [[PARSER_MODES]]
*/
parserMode: string,
/**
* path to a directory where we put the final bundles
*/
buildPath: string,
/**
* path to the main config file
*/
configFilePath: string
}
/**
* A Plugin to detect Isomorphic-App-Components
* @param props
*/
export const IsoPlugin = (props: IIsoPlugin): IPlugin => {
//console.log("configFilePath: " , props.configFilePath);
const result: IPlugin = {
// identify Isomorphic-App-Components
applies: (component): boolean => {
return isIsomorphicApp(component);
},
// convert the component into configuration parts
process: (
component: any,
childConfigs: Array<IConfigParseResult>,
infrastructureMode: string | undefined
): IConfigParseResult => {
const path = require('path');
// we use the hardcoded name `server` as name
const serverName = "server";
const serverBuildPath = path.join(require("../../../infrastructure-scripts/dist/infra-comp-utils/system-libs").currentAbsolutePath(), props.buildPath);
// the isomorphic app has a server application
const serverWebPack = require("../../../infrastructure-scripts/dist/infra-comp-utils/webpack-libs").complementWebpackConfig(
require("../../../infrastructure-scripts/dist/infra-comp-utils/webpack-libs").createServerWebpackConfig(
"./"+path.join("node_modules", "infrastructure-components", "dist" , "assets", "server.js"), //entryPath: string,
serverBuildPath, //use the buildpath from the parent plugin
serverName, // name of the server
{
__CONFIG_FILE_PATH__: require("../../../infrastructure-scripts/dist/infra-comp-utils/system-libs").pathToConfigFile(props.configFilePath), // replace the IsoConfig-Placeholder with the real path to the main-config-bundle
// required of data-layer, makes the context match!
"infrastructure-components": path.join(
require("../../../infrastructure-scripts/dist/infra-comp-utils/system-libs").currentAbsolutePath(),
"node_modules", "infrastructure-components", "dist", "index.js"),
// required of the routed-app
"react-router-dom": path.join(
require("../../../infrastructure-scripts/dist/infra-comp-utils/system-libs").currentAbsolutePath(),
"node_modules", "react-router-dom"),
// required of the data-layer / apollo
"react-apollo": path.join(
require("../../../infrastructure-scripts/dist/infra-comp-utils/system-libs").currentAbsolutePath(),
"node_modules", "react-apollo"),
}, {
__ISOMORPHIC_ID__: `"${component.instanceId}"`,
__ASSETS_PATH__: `"${component.assetsPath}"`,
__DATALAYER_ID__: `"${component.dataLayerId}"`,
__RESOLVED_ASSETS_PATH__: `"${resolveAssetsPath(
component.buildPath,
serverName,
component.assetsPath )
}"`
// TODO add replacements of datalayers here!
}
),
props.parserMode === PARSER_MODES.MODE_DEPLOY //isProd
);
const domain = childConfigs.map(config => config.domain).reduce((result, domain) => result !== undefined ? result : domain, undefined);
const certArn = childConfigs.map(config => config.certArn).reduce((result, certArn) => result !== undefined ? result : certArn, undefined);
const environments = childConfigs.reduce((result, config) => (result !== undefined ? result : []).concat(config.environments !== undefined ? config.environments : []), []);
// provide all client configs in a flat list
const webpackConfigs: any = childConfigs.reduce((result, config) => result.concat(
config.webpackConfigs.map(wp => wp({
// when we deploy an Isomorphic App without a domain, we need to add the stagename!
stagePath: props.parserMode === PARSER_MODES.MODE_DEPLOY &&
domain == undefined &&
environments !== undefined &&
environments.length > 0 ? environments[0].name : undefined
}))
), []);
const copyAssetsPostBuild = () => {
//console.log("check for >>copyAssetsPostBuild<<");
if (props.parserMode !== PARSER_MODES.MODE_DOMAIN && props.parserMode !== PARSER_MODES.MODE_DEPLOY) {
// always copy the assets, unless we setup the domain
console.log("copyAssetsPostBuild: now copy the assets!");
webpackConfigs.map(config => require("../../../infrastructure-scripts/dist/infra-comp-utils/system-libs").copyAssets( config.output.path, path.join(serverBuildPath, serverName, component.assetsPath)));
} else {
// delete the assets folder for we don't want to include all these bundled files in the deployment-package
const rimraf = require("rimraf");
rimraf.sync(path.join(serverBuildPath, serverName, component.assetsPath));
}
};
/*
const postDeploy = async () => {
console.log("check for >>postDeploy<<");
if (props.parserMode === PARSER_MODES.MODE_DEPLOY) {
var endpointUrl = undefined;
var eps: any = {};
await require("../../../infrastructure-scripts/dist/infra-comp-utils/sls-libs").runSlsCmd("echo $(sls info)", data => {
//console.log("data: " , data);
eps = data.split(" ").reduce(({inSection, endpoints}, val, idx) => {
//console.log("eval: " , val);
if (inSection && val.indexOf("https://") > -1) {
return { inSection: true, endpoints: endpoints.concat(
val.indexOf("{proxy+}") == -1 ? [val] : []
)}
}
if (val.startsWith("endpoints:")) {
return { inSection: true, endpoints: endpoints }
}
if (val.startsWith("functions:")) {
return { inSection: false, endpoints: endpoints }
}
return { inSection: inSection, endpoints: endpoints }
}, {inSection: false, endpoints: []});
//console.log("endpoints" , eps)
}, false);
const data = Object.assign({
proj: component.stackName,
envi: component.name,
domain: domain
}, eps.endpoints.length > 0 ? { endp: eps.endpoints[0]} : {});
await require('../libs/scripts-libs').fetchData("deploy", data);
if (eps.endpoints.length > 0) {
console.log("Your React-App is now available at: ", eps.endpoints[0])
}
}
}*/
/*
async function uploadAssetsPostBuild () {
//console.log("check for >>copyAssetsPostBuild<<");
if (props.parserMode === PARSER_MODES.MODE_DEPLOY) {
// always copy the assets, unless we setup the domain
console.log("uploadAssetsPostBuild: now copy the assets!");
const staticBucketName = getStaticBucketName(component.stackName, component.assetsPath, stage);
// copy the client apps to the assets-folder
console.log("start S3 Sync");
await Promise.all(
// only copy webapps
webpackConfigs.filter(wpConfig => wpConfig.target === "web").map(async wpConfig => {
await s3sync(component.region, staticBucketName, path.join(component.buildPath, wpConfig.name))
})
);
//webpackConfigs.map(config => require("../../../infrastructure-scripts/dist/infra-comp-utils/system-libs").copyAssets( config.output.path, path.join(serverBuildPath, serverName, component.assetsPath)));
}
};*/
async function initDomain () {
//console.log("check for >>initDomain<<");
if (props.parserMode === PARSER_MODES.MODE_DOMAIN) {
console.log("initDomain!")
await require("../../../infrastructure-scripts/dist/infra-comp-utils/sls-libs").initDomain(component.stackName);
}
}
const domainConfig = domain !== undefined ? {
plugins: ["serverless-domain-manager"],
custom: {
customDomain: {
domainName: domain,
basePath: "''",
stage: "${self:provider.stage, env:STAGE, 'dev'}",
createRoute53Record: true
}
}
} : {};
const additionalStatements: Array<any> = forwardChildIamRoleStatements(childConfigs).concat(
component.iamRoleStatements ? component.iamRoleStatements : []
);
//console.log("additionalStatements: ", additionalStatements);
const iamRoleAssignment = {
functions: {}
};
iamRoleAssignment.functions[serverName] = {
role: "IsomorphicAppLambdaRole"
}
const iamPermissions = {
resources: {
Resources: {
IsomorphicAppLambdaRole: {
Type: "AWS::IAM::Role",
Properties: {
RoleName: "${self:service}-${self:provider.stage, env:STAGE, 'dev'}-IsomorphicAppLambdaRole",
AssumeRolePolicyDocument: {
Version: '"2012-10-17"',
Statement: [
{
Effect: "Allow",
Principal: {
Service: ["lambda.amazonaws.com"]
},
Action: "sts:AssumeRole"
}
]
},
Policies: [
{
PolicyName: "${self:service}-${self:provider.stage, env:STAGE, 'dev'}-IsomorphicAppLambdaPolicy",
PolicyDocument: {
Version: '"2012-10-17"',
Statement: [
{
Effect: "Allow",
Action: [
'"logs:*"',
'"cloudwatch:*"'
],
Resource: '"*"'
}, {
Effect: "Allow",
Action: [
"s3:Get*",
"s3:List*",
],
Resource: '"*"'
},
].concat(additionalStatements)
}
}
]
}
},
},
}
}
return {
stackType: "ISO",
slsConfigs: deepmerge.all([
require("../../../infrastructure-scripts/dist/infra-comp-utils/sls-libs").toSlsConfig(
component.stackName,
serverName,
component.buildPath,
component.assetsPath,
component.region),
// # allows running the stack locally on the dev-machine
{
plugins: ["serverless-offline", "serverless-pseudo-parameters"],
custom: {
"serverless-offline": {
host: "0.0.0.0",
port: "${self:provider.port, env:PORT, '3000'}"
}
}
},
// add the domain config
domainConfig,
...childConfigs.map(config => config.slsConfigs),
// add the IAM-Role-Statements
iamPermissions,
// assign the role
// assign the role
iamRoleAssignment
]),
// add the server config
webpackConfigs: webpackConfigs.concat([serverWebPack]),
postBuilds: childConfigs.reduce((result, config) => result.concat(config.postBuilds),
[copyAssetsPostBuild, initDomain /*, postDeploy*/]),
iamRoleStatements: [],
environments: environments,
stackName: component.stackName,
assetsPath: component.assetsPath,
buildPath: component.buildPath,
region: component.region,
domain: domain,
certArn: certArn,
supportOfflineStart: true,
supportCreateDomain: true
}
}
}
return result;
};