|  | @@ -3,7 +3,6 @@ const fs         = require('fs');
 | 
	
		
			
				|  |  |  const logger     = require('../logger').nginx;
 | 
	
		
			
				|  |  |  const utils      = require('../lib/utils');
 | 
	
		
			
				|  |  |  const error      = require('../lib/error');
 | 
	
		
			
				|  |  | -const { Liquid } = require('liquidjs');
 | 
	
		
			
				|  |  |  const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  const internalNginx = {
 | 
	
	
		
			
				|  | @@ -29,7 +28,9 @@ const internalNginx = {
 | 
	
		
			
				|  |  |  			.then(() => {
 | 
	
		
			
				|  |  |  				// Nginx is OK
 | 
	
		
			
				|  |  |  				// We're deleting this config regardless.
 | 
	
		
			
				|  |  | -				return internalNginx.deleteConfig(host_type, host); // Don't throw errors, as the file may not exist at all
 | 
	
		
			
				|  |  | +				// Don't throw errors, as the file may not exist at all
 | 
	
		
			
				|  |  | +				// Delete the .err file too
 | 
	
		
			
				|  |  | +				return internalNginx.deleteConfig(host_type, host, false, true);
 | 
	
		
			
				|  |  |  			})
 | 
	
		
			
				|  |  |  			.then(() => {
 | 
	
		
			
				|  |  |  				return internalNginx.generateConfig(host_type, host);
 | 
	
	
		
			
				|  | @@ -80,6 +81,9 @@ const internalNginx = {
 | 
	
		
			
				|  |  |  							.patch({
 | 
	
		
			
				|  |  |  								meta: combined_meta
 | 
	
		
			
				|  |  |  							})
 | 
	
		
			
				|  |  | +							.then(() => {
 | 
	
		
			
				|  |  | +								internalNginx.renameConfigAsError(host_type, host);
 | 
	
		
			
				|  |  | +							})
 | 
	
		
			
				|  |  |  							.then(() => {
 | 
	
		
			
				|  |  |  								return internalNginx.deleteConfig(host_type, host, true);
 | 
	
		
			
				|  |  |  							});
 | 
	
	
		
			
				|  | @@ -121,13 +125,10 @@ const internalNginx = {
 | 
	
		
			
				|  |  |  	 * @returns {String}
 | 
	
		
			
				|  |  |  	 */
 | 
	
		
			
				|  |  |  	getConfigName: (host_type, host_id) => {
 | 
	
		
			
				|  |  | -		host_type = host_type.replace(new RegExp('-', 'g'), '_');
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  		if (host_type === 'default') {
 | 
	
		
			
				|  |  |  			return '/data/nginx/default_host/site.conf';
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		return '/data/nginx/' + host_type + '/' + host_id + '.conf';
 | 
	
		
			
				|  |  | +		return '/data/nginx/' + internalNginx.getFileFriendlyHostType(host_type) + '/' + host_id + '.conf';
 | 
	
		
			
				|  |  |  	},
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  	/**
 | 
	
	
		
			
				|  | @@ -136,8 +137,6 @@ const internalNginx = {
 | 
	
		
			
				|  |  |  	 * @returns {Promise}
 | 
	
		
			
				|  |  |  	 */
 | 
	
		
			
				|  |  |  	renderLocations: (host) => {
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		//logger.info('host = ' + JSON.stringify(host, null, 2));
 | 
	
		
			
				|  |  |  		return new Promise((resolve, reject) => {
 | 
	
		
			
				|  |  |  			let template;
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -148,19 +147,17 @@ const internalNginx = {
 | 
	
		
			
				|  |  |  				return;
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -			let renderer          = new Liquid({
 | 
	
		
			
				|  |  | -				root: __dirname + '/../templates/'
 | 
	
		
			
				|  |  | -			});
 | 
	
		
			
				|  |  | +			const renderEngine    = utils.getRenderEngine();
 | 
	
		
			
				|  |  |  			let renderedLocations = '';
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  			const locationRendering = async () => {
 | 
	
		
			
				|  |  |  				for (let i = 0; i < host.locations.length; i++) {
 | 
	
		
			
				|  |  | -					let locationCopy = Object.assign({}, {access_list_id: host.access_list_id}, {certificate_id: host.certificate_id}, 
 | 
	
		
			
				|  |  | +					let locationCopy = Object.assign({}, {access_list_id: host.access_list_id}, {certificate_id: host.certificate_id},
 | 
	
		
			
				|  |  |  						{ssl_forced: host.ssl_forced}, {caching_enabled: host.caching_enabled}, {block_exploits: host.block_exploits},
 | 
	
		
			
				|  |  |  						{allow_websocket_upgrade: host.allow_websocket_upgrade}, {http2_support: host.http2_support},
 | 
	
		
			
				|  |  |  						{hsts_enabled: host.hsts_enabled}, {hsts_subdomains: host.hsts_subdomains}, {access_list: host.access_list},
 | 
	
		
			
				|  |  |  						{certificate: host.certificate}, host.locations[i]);
 | 
	
		
			
				|  |  | -			
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  					if (locationCopy.forward_host.indexOf('/') > -1) {
 | 
	
		
			
				|  |  |  						const splitted = locationCopy.forward_host.split('/');
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -168,16 +165,14 @@ const internalNginx = {
 | 
	
		
			
				|  |  |  						locationCopy.forward_path = `/${splitted.join('/')}`;
 | 
	
		
			
				|  |  |  					}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -					//logger.info('locationCopy = ' + JSON.stringify(locationCopy, null, 2));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  					// eslint-disable-next-line
 | 
	
		
			
				|  |  | -					renderedLocations += await renderer.parseAndRender(template, locationCopy);
 | 
	
		
			
				|  |  | +					renderedLocations += await renderEngine.parseAndRender(template, locationCopy);
 | 
	
		
			
				|  |  |  				}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  			};
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  			locationRendering().then(() => resolve(renderedLocations));
 | 
	
		
			
				|  |  | -			
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  		});
 | 
	
		
			
				|  |  |  	},
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -187,24 +182,20 @@ const internalNginx = {
 | 
	
		
			
				|  |  |  	 * @returns {Promise}
 | 
	
		
			
				|  |  |  	 */
 | 
	
		
			
				|  |  |  	generateConfig: (host_type, host) => {
 | 
	
		
			
				|  |  | -		host_type = host_type.replace(new RegExp('-', 'g'), '_');
 | 
	
		
			
				|  |  | +		const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		if (debug_mode) {
 | 
	
		
			
				|  |  | -			logger.info('Generating ' + host_type + ' Config:', host);
 | 
	
		
			
				|  |  | +			logger.info('Generating ' + nice_host_type + ' Config:', JSON.stringify(host, null, 2));
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		// logger.info('host = ' + JSON.stringify(host, null, 2));
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		let renderEngine = new Liquid({
 | 
	
		
			
				|  |  | -			root: __dirname + '/../templates/'
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | +		const renderEngine = utils.getRenderEngine();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		return new Promise((resolve, reject) => {
 | 
	
		
			
				|  |  |  			let template = null;
 | 
	
		
			
				|  |  | -			let filename = internalNginx.getConfigName(host_type, host.id);
 | 
	
		
			
				|  |  | +			let filename = internalNginx.getConfigName(nice_host_type, host.id);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  			try {
 | 
	
		
			
				|  |  | -				template = fs.readFileSync(__dirname + '/../templates/' + host_type + '.conf', {encoding: 'utf8'});
 | 
	
		
			
				|  |  | +				template = fs.readFileSync(__dirname + '/../templates/' + nice_host_type + '.conf', {encoding: 'utf8'});
 | 
	
		
			
				|  |  |  			} catch (err) {
 | 
	
		
			
				|  |  |  				reject(new error.ConfigurationError(err.message));
 | 
	
		
			
				|  |  |  				return;
 | 
	
	
		
			
				|  | @@ -214,7 +205,7 @@ const internalNginx = {
 | 
	
		
			
				|  |  |  			let origLocations;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  			// Manipulate the data a bit before sending it to the template
 | 
	
		
			
				|  |  | -			if (host_type !== 'default') {
 | 
	
		
			
				|  |  | +			if (nice_host_type !== 'default') {
 | 
	
		
			
				|  |  |  				host.use_default_location = true;
 | 
	
		
			
				|  |  |  				if (typeof host.advanced_config !== 'undefined' && host.advanced_config) {
 | 
	
		
			
				|  |  |  					host.use_default_location = !internalNginx.advancedConfigHasDefaultLocation(host.advanced_config);
 | 
	
	
		
			
				|  | @@ -281,9 +272,7 @@ const internalNginx = {
 | 
	
		
			
				|  |  |  			logger.info('Generating LetsEncrypt Request Config:', certificate);
 | 
	
		
			
				|  |  |  		}
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -		let renderEngine = new Liquid({
 | 
	
		
			
				|  |  | -			root: __dirname + '/../templates/'
 | 
	
		
			
				|  |  | -		});
 | 
	
		
			
				|  |  | +		const renderEngine = utils.getRenderEngine();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		return new Promise((resolve, reject) => {
 | 
	
		
			
				|  |  |  			let template = null;
 | 
	
	
		
			
				|  | @@ -319,33 +308,39 @@ const internalNginx = {
 | 
	
		
			
				|  |  |  		});
 | 
	
		
			
				|  |  |  	},
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	/**
 | 
	
		
			
				|  |  | +	 * A simple wrapper around unlinkSync that writes to the logger
 | 
	
		
			
				|  |  | +	 *
 | 
	
		
			
				|  |  | +	 * @param   {String}  filename
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	deleteFile: (filename) => {
 | 
	
		
			
				|  |  | +		logger.debug('Deleting file: ' + filename);
 | 
	
		
			
				|  |  | +		try {
 | 
	
		
			
				|  |  | +			fs.unlinkSync(filename);
 | 
	
		
			
				|  |  | +		} catch (err) {
 | 
	
		
			
				|  |  | +			logger.debug('Could not delete file:', JSON.stringify(err, null, 2));
 | 
	
		
			
				|  |  | +		}
 | 
	
		
			
				|  |  | +	},
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +	/**
 | 
	
		
			
				|  |  | +	 *
 | 
	
		
			
				|  |  | +	 * @param   {String} host_type
 | 
	
		
			
				|  |  | +	 * @returns String
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	getFileFriendlyHostType: (host_type) => {
 | 
	
		
			
				|  |  | +		return host_type.replace(new RegExp('-', 'g'), '_');
 | 
	
		
			
				|  |  | +	},
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	/**
 | 
	
		
			
				|  |  |  	 * This removes the temporary nginx config file generated by `generateLetsEncryptRequestConfig`
 | 
	
		
			
				|  |  |  	 *
 | 
	
		
			
				|  |  |  	 * @param   {Object}  certificate
 | 
	
		
			
				|  |  | -	 * @param   {Boolean} [throw_errors]
 | 
	
		
			
				|  |  |  	 * @returns {Promise}
 | 
	
		
			
				|  |  |  	 */
 | 
	
		
			
				|  |  | -	deleteLetsEncryptRequestConfig: (certificate, throw_errors) => {
 | 
	
		
			
				|  |  | -		return new Promise((resolve, reject) => {
 | 
	
		
			
				|  |  | -			try {
 | 
	
		
			
				|  |  | -				let config_file = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf';
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -				if (debug_mode) {
 | 
	
		
			
				|  |  | -					logger.warn('Deleting nginx config: ' + config_file);
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -				fs.unlinkSync(config_file);
 | 
	
		
			
				|  |  | -			} catch (err) {
 | 
	
		
			
				|  |  | -				if (debug_mode) {
 | 
	
		
			
				|  |  | -					logger.warn('Could not delete config:', err.message);
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -				if (throw_errors) {
 | 
	
		
			
				|  |  | -					reject(err);
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -			}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +	deleteLetsEncryptRequestConfig: (certificate) => {
 | 
	
		
			
				|  |  | +		const config_file = '/data/nginx/temp/letsencrypt_' + certificate.id + '.conf';
 | 
	
		
			
				|  |  | +		return new Promise((resolve/*, reject*/) => {
 | 
	
		
			
				|  |  | +			internalNginx.deleteFile(config_file);
 | 
	
		
			
				|  |  |  			resolve();
 | 
	
		
			
				|  |  |  		});
 | 
	
		
			
				|  |  |  	},
 | 
	
	
		
			
				|  | @@ -353,35 +348,42 @@ const internalNginx = {
 | 
	
		
			
				|  |  |  	/**
 | 
	
		
			
				|  |  |  	 * @param   {String}  host_type
 | 
	
		
			
				|  |  |  	 * @param   {Object}  [host]
 | 
	
		
			
				|  |  | -	 * @param   {Boolean} [throw_errors]
 | 
	
		
			
				|  |  | +	 * @param   {Boolean} [delete_err_file]
 | 
	
		
			
				|  |  |  	 * @returns {Promise}
 | 
	
		
			
				|  |  |  	 */
 | 
	
		
			
				|  |  | -	deleteConfig: (host_type, host, throw_errors) => {
 | 
	
		
			
				|  |  | -		host_type = host_type.replace(new RegExp('-', 'g'), '_');
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -		return new Promise((resolve, reject) => {
 | 
	
		
			
				|  |  | -			try {
 | 
	
		
			
				|  |  | -				let config_file = internalNginx.getConfigName(host_type, typeof host === 'undefined' ? 0 : host.id);
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -				if (debug_mode) {
 | 
	
		
			
				|  |  | -					logger.warn('Deleting nginx config: ' + config_file);
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -				fs.unlinkSync(config_file);
 | 
	
		
			
				|  |  | -			} catch (err) {
 | 
	
		
			
				|  |  | -				if (debug_mode) {
 | 
	
		
			
				|  |  | -					logger.warn('Could not delete config:', err.message);
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -				if (throw_errors) {
 | 
	
		
			
				|  |  | -					reject(err);
 | 
	
		
			
				|  |  | -				}
 | 
	
		
			
				|  |  | +	deleteConfig: (host_type, host, delete_err_file) => {
 | 
	
		
			
				|  |  | +		const config_file     = internalNginx.getConfigName(internalNginx.getFileFriendlyHostType(host_type), typeof host === 'undefined' ? 0 : host.id);
 | 
	
		
			
				|  |  | +		const config_file_err = config_file + '.err';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		return new Promise((resolve/*, reject*/) => {
 | 
	
		
			
				|  |  | +			internalNginx.deleteFile(config_file);
 | 
	
		
			
				|  |  | +			if (delete_err_file) {
 | 
	
		
			
				|  |  | +				internalNginx.deleteFile(config_file_err);
 | 
	
		
			
				|  |  |  			}
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  			resolve();
 | 
	
		
			
				|  |  |  		});
 | 
	
		
			
				|  |  |  	},
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +	/**
 | 
	
		
			
				|  |  | +	 * @param   {String}  host_type
 | 
	
		
			
				|  |  | +	 * @param   {Object}  [host]
 | 
	
		
			
				|  |  | +	 * @returns {Promise}
 | 
	
		
			
				|  |  | +	 */
 | 
	
		
			
				|  |  | +	renameConfigAsError: (host_type, host) => {
 | 
	
		
			
				|  |  | +		const config_file     = internalNginx.getConfigName(internalNginx.getFileFriendlyHostType(host_type), typeof host === 'undefined' ? 0 : host.id);
 | 
	
		
			
				|  |  | +		const config_file_err = config_file + '.err';
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +		return new Promise((resolve/*, reject*/) => {
 | 
	
		
			
				|  |  | +			fs.unlink(config_file, () => {
 | 
	
		
			
				|  |  | +				// ignore result, continue
 | 
	
		
			
				|  |  | +				fs.rename(config_file, config_file_err, () => {
 | 
	
		
			
				|  |  | +					// also ignore result, as this is a debugging informative file anyway
 | 
	
		
			
				|  |  | +					resolve();
 | 
	
		
			
				|  |  | +				});
 | 
	
		
			
				|  |  | +			});
 | 
	
		
			
				|  |  | +		});
 | 
	
		
			
				|  |  | +	},
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  	/**
 | 
	
		
			
				|  |  |  	 * @param   {String}  host_type
 | 
	
		
			
				|  |  |  	 * @param   {Array}   hosts
 | 
	
	
		
			
				|  | @@ -399,13 +401,12 @@ const internalNginx = {
 | 
	
		
			
				|  |  |  	/**
 | 
	
		
			
				|  |  |  	 * @param   {String}  host_type
 | 
	
		
			
				|  |  |  	 * @param   {Array}   hosts
 | 
	
		
			
				|  |  | -	 * @param   {Boolean} [throw_errors]
 | 
	
		
			
				|  |  |  	 * @returns {Promise}
 | 
	
		
			
				|  |  |  	 */
 | 
	
		
			
				|  |  | -	bulkDeleteConfigs: (host_type, hosts, throw_errors) => {
 | 
	
		
			
				|  |  | +	bulkDeleteConfigs: (host_type, hosts) => {
 | 
	
		
			
				|  |  |  		let promises = [];
 | 
	
		
			
				|  |  |  		hosts.map(function (host) {
 | 
	
		
			
				|  |  | -			promises.push(internalNginx.deleteConfig(host_type, host, throw_errors));
 | 
	
		
			
				|  |  | +			promises.push(internalNginx.deleteConfig(host_type, host, true));
 | 
	
		
			
				|  |  |  		});
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  		return Promise.all(promises);
 |