constructor(config) { super(config); this.port = _.get(Webiny.getConfig(), 'browserSync.port', 3000); this.domain = _.get(Webiny.getConfig(), 'browserSync.domain', 'http://localhost'); this.webpackCallback = config.webpackCallback || null; this.progressCallback = config.progressCallback || null; }
return inquirer.prompt(questions).then(function (answers) { answers.domain = _.trimEnd(answers.domain, '/'); const configs = { dockerCompose: Webiny.projectRoot('docker-compose.yaml'), environments: Webiny.projectRoot('Configs/Environments.yaml'), base: { application: Webiny.projectRoot('Configs/Base/Webiny.yaml'), database: Webiny.projectRoot('Configs/Base/Database.yaml'), security: Webiny.projectRoot('Configs/Base/Security.yaml') }, local: { application: Webiny.projectRoot('Configs/Local/Webiny.yaml') } }; try { // Populate Environments.yaml let config = yaml.safeLoad(Webiny.readFile(configs.environments)); config.Environments.Local = answers.domain; Webiny.writeFile(configs.environments, yaml.safeDump(config, {indent: 4})); // Populate Base/Webiny.yaml config = yaml.safeLoad(Webiny.readFile(configs.base.application)); config.Webiny.Acl.Token = generatePassword(40, false, /[\dA-Za-z#_\$]/); Webiny.writeFile(configs.base.application, yaml.safeDump(config, {indent: 4})); // Populate Base/Database.yaml config = yaml.safeLoad(Webiny.readFile(configs.base.database)); config.Mongo.Services.Webiny.Calls[0][1] = [answers.database]; if (docker) { config.Mongo.Services.Webiny.Arguments.Uri = 'mongodb:27017'; } Webiny.writeFile(configs.base.database, yaml.safeDump(config, {indent: 4, flowLevel: 5})); // Populate Base/Security.yaml config = yaml.safeLoad(Webiny.readFile(configs.base.security)); config.Security.Tokens.Webiny.SecurityKey = generatePassword(30, false, /[\dA-Za-z#_\$:\?#]/); config.Security.TwoFactorAuth.Key = generatePassword(30, false, /[\dA-Za-z#_\$:\?#]/); Webiny.writeFile(configs.base.security, yaml.safeDump(config, {indent: 4, flowLevel: 5})); // Populate Local/Webiny.yaml config = yaml.safeLoad(Webiny.readFile(configs.local.application)); config.Webiny.WebUrl = answers.domain; config.Webiny.ApiUrl = answers.domain + '/api'; Webiny.writeFile(configs.local.application, yaml.safeDump(config, {indent: 4})); // Populate docker-compose.yaml if (docker) { config = yaml.safeLoad(Webiny.readFile(configs.dockerCompose)); config.services.nginx.ports.push(nginxPort + ':80'); let alias = answers.domain; alias = alias.replace('http://', '').replace('https://', '').split(':')[0]; config.services.nginx.networks.default.aliases.push(alias); config.services.php.extra_hosts.push('dockerhost:' + answers.hostIp); config.services.php.environment.XDEBUG_CONFIG = `remote_enable=0 remote_host=${answers.hostIp}`; config.services.mongodb.ports.push(answers.databasePort + ':27017'); Webiny.writeFile(configs.dockerCompose, yaml.safeDump(config, {indent: 4})); setupDockerVirtualHost(answers); } Webiny.success('Configuration files written successfully!'); // We need to store the env if the project is run using docker if (docker) { const wConfig = Webiny.getConfig(); wConfig.env = 'docker'; // Set default browserSync port for Docker only wConfig.browserSync = {port: 3010}; Webiny.saveConfig(wConfig); } } catch (err) { Webiny.failure(err.message, err); return; } if (docker) { Webiny.info('Initializing Docker containers...'); // Run Docker containers so we can execute install scripts. Webiny.shellExecute('docker-compose up -d'); } const php = docker ? 'docker-compose run php php' : 'php'; // Run Webiny installation procedure Webiny.info('Running Webiny app installation...'); Webiny.shellExecute(`${php} Apps/Webiny/Php/Cli/install.php Local Webiny`); // Create admin user const params = [answers.user, answers.password].join(' '); try { let output = Webiny.shellExecute(`${php} Apps/Webiny/Php/Cli/admin.php Local ${params}`, {stdio: 'pipe'}); output = JSON.parse(output); if (output.status === 'created') { Webiny.success('Admin user created successfully!'); } if (output.status === 'failed') { Webiny.exclamation(output.message); } } catch (err) { Webiny.failure(err.message); } // If Docker - we do not run nginx setup wizard if (docker) { return answers; } // Optionally create nginx config try { // Virtual host wizard const hostAnswers = { domain: answers.domain }; Webiny.shellExecute('nginx -v', {stdio: 'pipe'}); return inquirer.prompt({ type: 'confirm', name: 'createHost', message: 'Would you like us to create a new nginx virtual host for you?', default: true }).then(a => { if (a.createHost) { hostAnswers.createHost = true; return inquirer.prompt({ type: 'input', name: 'errorLogFile', message: 'Where do you want to place your error log file (including file name)?', default: function () { const server = answers.domain.replace('http://', '').replace('https://', '').split(':')[0]; return '/var/log/nginx/' + server + '-error.log'; } }).then(a => { hostAnswers.errorLogFile = a.errorLogFile; return setupVirtualHost(hostAnswers); }); } return answers; }); } catch (err) { // Skip host prompts } });
runWizard({env}) { const docker = env === 'docker'; const inquirer = require('inquirer'); const yaml = require('js-yaml'); const _ = require('lodash'); const generatePassword = require('password-generator'); const interfaces = require('./interfaces'); // Save env as it needs to be available as soon as possible for conditional execution const wConfig = Webiny.getConfig(); wConfig.env = env; Webiny.saveConfig(wConfig); Webiny.log("\nNow we need to create a platform configuration and your first user:\n"); // Need this later in setup let nginxPort = null; // Define wizard questions const questions = [ { type: 'input', name: 'domain', message: 'What\'s your local domain (e.g. http://domain.app:8000)?', validate: url => { let valid = Webiny.validate.domain(url); // Check if URL contains port which is mandatory for Docker setup if (valid === true && docker) { const message = 'Docker requires a port to be provided. Please add a port number to the URL.'; nginxPort = parseInt(_.get(url.split(':'), 2)); if (_.isNaN(nginxPort)) { return message; } } return valid; } }, { type: 'list', choices: interfaces(), name: 'hostIp', message: 'Select your host IP address:', when: docker }, { type: 'input', name: 'database', message: 'What\'s your database name?', default: () => { return 'webiny'; } }, { type: 'input', name: 'databasePort', when: docker, message: 'What\'s your mongodb service port?', default: ({domain}) => { try { return parseInt(domain.split(':')[2]) + 1; } catch (e) { return ''; } } }, { type: 'input', name: 'user', message: 'Enter your admin user email:', validate: Webiny.validate.email }, { type: 'password', name: 'password', message: 'Enter your admin user password:'******'dev' && value !== 'admin' && value.length < 8) { return 'Please enter at least 8 characters!'; } return true; } } ]; return inquirer.prompt(questions).then(function (answers) { answers.domain = _.trimEnd(answers.domain, '/'); const configs = { dockerCompose: Webiny.projectRoot('docker-compose.yaml'), environments: Webiny.projectRoot('Configs/Environments.yaml'), base: { application: Webiny.projectRoot('Configs/Base/Webiny.yaml'), database: Webiny.projectRoot('Configs/Base/Database.yaml'), security: Webiny.projectRoot('Configs/Base/Security.yaml') }, local: { application: Webiny.projectRoot('Configs/Local/Webiny.yaml') } }; try { // Populate Environments.yaml let config = yaml.safeLoad(Webiny.readFile(configs.environments)); config.Environments.Local = answers.domain; Webiny.writeFile(configs.environments, yaml.safeDump(config, {indent: 4})); // Populate Base/Webiny.yaml config = yaml.safeLoad(Webiny.readFile(configs.base.application)); config.Webiny.Acl.Token = generatePassword(40, false, /[\dA-Za-z#_\$]/); Webiny.writeFile(configs.base.application, yaml.safeDump(config, {indent: 4})); // Populate Base/Database.yaml config = yaml.safeLoad(Webiny.readFile(configs.base.database)); config.Mongo.Services.Webiny.Calls[0][1] = [answers.database]; if (docker) { config.Mongo.Services.Webiny.Arguments.Uri = 'mongodb:27017'; } Webiny.writeFile(configs.base.database, yaml.safeDump(config, {indent: 4, flowLevel: 5})); // Populate Base/Security.yaml config = yaml.safeLoad(Webiny.readFile(configs.base.security)); config.Security.Tokens.Webiny.SecurityKey = generatePassword(30, false, /[\dA-Za-z#_\$:\?#]/); config.Security.TwoFactorAuth.Key = generatePassword(30, false, /[\dA-Za-z#_\$:\?#]/); Webiny.writeFile(configs.base.security, yaml.safeDump(config, {indent: 4, flowLevel: 5})); // Populate Local/Webiny.yaml config = yaml.safeLoad(Webiny.readFile(configs.local.application)); config.Webiny.WebUrl = answers.domain; config.Webiny.ApiUrl = answers.domain + '/api'; Webiny.writeFile(configs.local.application, yaml.safeDump(config, {indent: 4})); // Populate docker-compose.yaml if (docker) { config = yaml.safeLoad(Webiny.readFile(configs.dockerCompose)); config.services.nginx.ports.push(nginxPort + ':80'); let alias = answers.domain; alias = alias.replace('http://', '').replace('https://', '').split(':')[0]; config.services.nginx.networks.default.aliases.push(alias); config.services.php.extra_hosts.push('dockerhost:' + answers.hostIp); config.services.php.environment.XDEBUG_CONFIG = `remote_enable=0 remote_host=${answers.hostIp}`; config.services.mongodb.ports.push(answers.databasePort + ':27017'); Webiny.writeFile(configs.dockerCompose, yaml.safeDump(config, {indent: 4})); setupDockerVirtualHost(answers); } Webiny.success('Configuration files written successfully!'); // We need to store the env if the project is run using docker if (docker) { const wConfig = Webiny.getConfig(); wConfig.env = 'docker'; // Set default browserSync port for Docker only wConfig.browserSync = {port: 3010}; Webiny.saveConfig(wConfig); } } catch (err) { Webiny.failure(err.message, err); return; } if (docker) { Webiny.info('Initializing Docker containers...'); // Run Docker containers so we can execute install scripts. Webiny.shellExecute('docker-compose up -d'); } const php = docker ? 'docker-compose run php php' : 'php'; // Run Webiny installation procedure Webiny.info('Running Webiny app installation...'); Webiny.shellExecute(`${php} Apps/Webiny/Php/Cli/install.php Local Webiny`); // Create admin user const params = [answers.user, answers.password].join(' '); try { let output = Webiny.shellExecute(`${php} Apps/Webiny/Php/Cli/admin.php Local ${params}`, {stdio: 'pipe'}); output = JSON.parse(output); if (output.status === 'created') { Webiny.success('Admin user created successfully!'); } if (output.status === 'failed') { Webiny.exclamation(output.message); } } catch (err) { Webiny.failure(err.message); } // If Docker - we do not run nginx setup wizard if (docker) { return answers; } // Optionally create nginx config try { // Virtual host wizard const hostAnswers = { domain: answers.domain }; Webiny.shellExecute('nginx -v', {stdio: 'pipe'}); return inquirer.prompt({ type: 'confirm', name: 'createHost', message: 'Would you like us to create a new nginx virtual host for you?', default: true }).then(a => { if (a.createHost) { hostAnswers.createHost = true; return inquirer.prompt({ type: 'input', name: 'errorLogFile', message: 'Where do you want to place your error log file (including file name)?', default: function () { const server = answers.domain.replace('http://', '').replace('https://', '').split(':')[0]; return '/var/log/nginx/' + server + '-error.log'; } }).then(a => { hostAnswers.errorLogFile = a.errorLogFile; return setupVirtualHost(hostAnswers); }); } return answers; }); } catch (err) { // Skip host prompts } }); }
module.exports = function (app) { // Construct URL for hot-reload and assets const port = _.get(Webiny.getConfig(), 'browserSync.port', 3000); const domain = _.get(Webiny.getConfig(), 'browserSync.domain', 'http://localhost'); const url = domain + ':' + port; const name = app.getName(); const context = Webiny.projectRoot(app.getSourceDir()); const outputPath = path.resolve(Webiny.projectRoot(), 'public_html/build/development', app.getPath()); const publicPath = url + '/build/development/' + app.getPath() + '/'; const plugins = [ new webpack.NamedModulesPlugin(), new ModuleIdsPlugin(), new ChunkIdsPlugin(), new webpack.NoEmitOnErrorsPlugin(), new webpack.DefinePlugin({ 'DEVELOPMENT': true, 'PRODUCTION': false, 'process.env': { 'NODE_ENV': JSON.stringify(process.env.NODE_ENV) } }), new webpack.HotModuleReplacementPlugin(), new ExtractTextPlugin('app.css'), // Generate meta.json to use for app bootstrap based on generated assets new AssetsPlugin({ manifestVariable: 'window["webinyConfig"]["Meta"]["' + name + '"].chunks' }), new Visualizer({filename: 'stats.html'}) ]; // Check if app has vendor DLL defined const dllPath = path.resolve(Webiny.projectRoot(), 'public_html/build/development', app.getPath(), 'vendor.manifest.json'); if (Webiny.fileExists(dllPath)) { plugins.push( new webpack.DllReferencePlugin({ context, manifest: require(dllPath) }) ); } if (name !== 'Webiny.Core') { plugins.push( new webpack.DllReferencePlugin({ context: Webiny.projectRoot('Apps/Webiny/Js/Core'), manifest: Webiny.projectRoot('public_html/build/development') + '/Webiny_Core/vendor.manifest.json' }) ); } const fileExtensionRegex = /\.(png|jpg|gif|jpeg|mp4|mp3|woff2?|ttf|otf|eot|svg|ico)$/; return { name, cache: true, watch: false, context, entry: { app: [ 'react-hot-loader/patch', 'webpack-hot-middleware/client?name=' + name + '&path=' + url + '/__webpack_hmr&quiet=false&noInfo=true&warn=false&overlay=true&reload=false', 'webpack/hot/only-dev-server', './App.js' ] }, output: { path: outputPath, filename: '[name].js', chunkFilename: 'chunks/[name].js', jsonpFunction: 'webpackJsonp' + app.getName().replace('.', ''), publicPath }, plugins, externals: name === 'Webiny.Core' ? {} : require('./externals'), module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: [ { loader: 'babel-loader', options: { presets: [ [require.resolve('babel-preset-es2015')], require.resolve('babel-preset-react') ], plugins: [ require.resolve('react-hot-loader/babel'), require.resolve('babel-plugin-transform-async-to-generator'), [require.resolve('babel-plugin-transform-object-rest-spread'), {'useBuiltIns': true}], [require.resolve('babel-plugin-syntax-dynamic-import')], [require.resolve('babel-plugin-lodash')], [require.resolve('babel-plugin-transform-builtin-extend'), {globals: ['Error']}], // This plugin is required to force all css/scss imports to have a resourceQuery [require.resolve('babel-plugin-transform-rename-import'), { original: '^(.*?\.s?css)$', replacement: '$1?', }] ] } }, 'hot-accept-loader' ] }, { test: /\.(js|jsx)$/, exclude: /node_modules/, include: Webiny.projectRoot(), use: 'i18n-loader' }, require('./styles')(app), { test: /node_modules/, include: fileExtensionRegex, loader: 'file-loader', options: { context: path.resolve(Webiny.projectRoot(), 'Apps', app.getAppName(), 'node_modules'), name: 'external/[path][name].[ext]' } }, { test: fileExtensionRegex, exclude: /node_modules/, loader: 'file-loader', options: { context: path.resolve(Webiny.projectRoot(), app.getSourceDir(), 'Assets'), name: '[path][name].[ext]', outputPath: (file) => { if (file.startsWith('_/')) { const parts = file.replace(/_\//g, '').split('/Assets/'); file = path.join('external', parts[0], parts[1]); } return file; } } } ] }, resolve: require('./resolve')(app), resolveLoader: { modules: [ __dirname + '/loaders', 'node_modules', path.resolve(Webiny.projectRoot(), 'Apps/Webiny/node_modules'), path.resolve(Webiny.projectRoot(), 'Apps/' + app.getAppName() + '/node_modules') ] } } };