formio
Version:
The formio server application.
404 lines (364 loc) • 10.7 kB
JavaScript
;
const inquirer = require('inquirer').default;
const async = require('async');
const fs = require('fs-extra');
const nunjucks = require('nunjucks');
nunjucks.configure([], {watch: false});
const util = require('./src/util/util');
const debug = require('debug')('formio:error');
const path = require('path');
module.exports = function(formio, items, done) {
// The project that was created.
let project = {};
// The directory for the client application.
const directories = {
client: path.join(__dirname, 'client')
};
let templateFile = '';
/**
* Download a zip file.
*
* @param url
* @param zipFile
* @param dir
* @param done
* @returns {*}
*/
const download = function(url, zipFile, dir, done) {
// Check to see if the client already exists.
if (fs.existsSync(zipFile)) {
util.log(`${directories[dir]} file already exists, skipping download.`);
return done();
}
const ProgressBar = require('progress');
util.log(`Downloading ${dir}${'...'.green}`);
// Download the project.
let downloadError = null;
let tries = 0;
let bar = null;
(function downloadProject() {
util.fetch(url)
.then(function(res) {
if (
!res.headers.has('content-disposition') ||
!parseInt(res.headers.get('content-length'), 10)
) {
if (tries++ > 3) {
return done('Unable to download project. Please try again.');
}
setTimeout(downloadProject, 200);
return;
}
// Setup the progress bar.
bar = new ProgressBar(' downloading [:bar] :percent :etas', {
complete: '=',
incomplete: ' ',
width: 50,
total: parseInt(res.headers.get('content-length'), 10)
});
res.body.pipe(fs.createWriteStream(zipFile, {
flags: 'w'
}));
res.body.on('data', function(chunk) {
if (bar) {
bar.tick(chunk.length);
}
});
res.body.on('error', function(err) {
downloadError = err;
});
res.body.on('end', function() {
setTimeout(function() {
done(downloadError);
}, 100);
});
});
})();
};
/**
* Extract a download to a folder.
*
* @param zipFile
* @param fromDir
* @param dir
* @param done
* @returns {*}
*/
const extract = function(zipFile, fromDir, dir, done) {
// See if we need to extract.
if (fs.existsSync(directories[dir])) {
util.log(`${directories[dir]} already exists, skipping extraction.`);
return done();
}
// Unzip the contents.
const AdmZip = require('adm-zip');
util.log('Extracting contents...'.green);
const zip = new AdmZip(zipFile);
zip.extractAllTo('', true);
fs.move(fromDir, directories[dir], function(err) {
if (err) {
return done(err);
}
// Delete the zip file.
fs.remove(zipFile);
// Get the package json file.
let info = {};
try {
info = JSON.parse(fs.readFileSync(path.join(directories[dir], 'package.json')));
}
catch (err) {
debug(err);
return done(err);
}
// Set local variable to directory path.
let directoryPath = directories[dir];
// Change the document root if we need to.
if (info.formio && info.formio.docRoot) {
directoryPath = path.join(directories[dir], info.formio.docRoot);
}
if (!fs.existsSync(path.join(directoryPath, 'config.template.js'))) {
return done('Missing config.template.js file');
}
// Change the project configuration.
const config = fs.readFileSync(path.join(directoryPath, 'config.template.js'));
const newConfig = nunjucks.renderString(config.toString(), {
domain: formio.config.domain ? formio.config.domain : 'https://form.io'
});
fs.writeFileSync(path.join(directoryPath, 'config.js'), newConfig);
done();
});
};
// All the steps in the installation.
const steps = {
/**
* Step to perform the are you sure step.
*
* @param done
*/
areYouSure: function(done) {
if (process.env.ROOT_EMAIL) {
done();
}
prompt.get([
{
name: 'install',
description: 'Are you sure you wish to install? (y/N)',
required: true
}
], function(err, results) {
if (err) {
return done(err);
}
if (results.install.toLowerCase() !== 'y') {
return done('Installation canceled.');
}
done();
});
},
/**
* Download the Form.io admin client.
*
* @param done
* @returns {*}
*/
downloadClient: function(done) {
if (!items.download) {
return done();
}
// Download the client.
download(
'https://codeload.github.com/formio/formio-app-formio/zip/master',
'client.zip',
'client',
done
);
},
/**
* Extract the client.
*
* @param done
* @returns {*}
*/
extractClient: function(done) {
if (!items.extract) {
return done();
}
extract('client.zip', 'formio-app-formio-master', 'client', done);
},
/**
* Select the template to use.
*
* @param done
* @return {*}
*/
whatTemplate: function(done) {
if (process.env.ROOT_EMAIL) {
templateFile = 'client';
done();
}
let message = '\nWhich project template would you like to install?\n'.green;
message += '\n Please provide the local file path of the project.json file.'.yellow;
message += '\n Or, just press '.yellow + 'ENTER'.green + ' to use the default template.\n'.yellow;
util.log(message);
inquirer.prompt([
{
name: 'templateFile',
message: 'Enter a local file path or press Enter for the default template.',
default: './default-template.json',
validate: function(input) {
if (!input) {
return 'Template file is not specified';
}
return true;
},
},
]).then((results) => {
if (!results.templateFile) {
return done('Cannot find the template file!'.red);
}
templateFile = results.templateFile ? results.templateFile : 'client';
done();
}).catch((err) => {
done(err);
});
},
/**
* Import the template.
* @param done
*/
importTemplate: function(done) {
if (!items.import) {
return done();
}
// Determine if this is a custom project.
const customProject = (['app', 'client'].indexOf(templateFile) === -1);
let directoryPath = '';
if (!customProject) {
directoryPath = directories[templateFile];
// Get the package json file.
let info = {};
try {
info = JSON.parse(fs.readFileSync(path.join(directoryPath, 'package.json')));
}
catch (err) {
debug(err);
return done(err);
}
// Change the document root if we need to.
if (info.formio && info.formio.docRoot) {
directoryPath = path.join(directoryPath, info.formio.docRoot);
}
}
const projectJson = customProject ? templateFile : path.join(directoryPath, 'project.json');
if (!fs.existsSync(projectJson)) {
util.log(projectJson);
return done('Missing project.json file'.red);
}
let template = {};
try {
template = JSON.parse(fs.readFileSync(projectJson));
}
catch (err) {
debug(err);
return done(err);
}
// Get the form.io service.
util.log('Importing template...'.green);
const importer = require('./src/templates/import')({formio: formio});
importer.template(template, function(err, template) {
if (err) {
return done(err);
}
project = template;
done(null, template);
});
},
/**
* Create the root user object.
*
* @param done
*/
createRootUser: function(done) {
if (!items.user) {
return done();
}
util.log('Creating root user account...'.green);
inquirer.prompt([
{
name: 'email',
message: 'Enter your email address for the root account.',
when: function() {
return process.env.ROOT_EMAIL ? false : true;
},
validate: function(input) {
if (!input) {
return 'Email is not specified';
}
const pattern = /^[a-zA-Z0-9.!#$%&’*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/;
if (!pattern.test(input)) {
return 'Must be a valid email';
}
return true;
},
},
{
name: 'password',
type: 'password',
message: 'Enter your password for the root account.',
when: function() {
return process.env.ROOT_PASSWORD ? false : true;
},
validate: function(input) {
if (!input) {
return 'Password is not specified';
}
return true;
},
}
]).then(function(result) {
util.log('Encrypting password');
formio.encrypt(result.password, async function(err, hash) {
if (err) {
return done(err);
}
// Create the root user submission.
util.log('Creating root user account');
try {
await formio.resources.submission.model.create({
form: project.resources.admin._id,
data: {
email: result.email,
password: hash
},
roles: [
project.roles.administrator._id
]
});
return done();
}
catch (err) {
return done(err);
}
});
}).catch(function(err) {
done(err);
});
}
};
util.log('Installing...');
async.series([
steps.areYouSure,
steps.downloadClient,
steps.extractClient,
steps.whatTemplate,
steps.importTemplate,
steps.createRootUser
], function(err, result) {
if (err) {
util.log(err);
return done(err);
}
util.log('Install successful!'.green);
done();
});
};