Przeglądaj źródła

Refactor configuration

- No longer use config npm package
- Prefer config from env vars, though still has support for config file
- No longer writes a config file for database config
- Writes keys to a new file in /data folder
- Removes a lot of cruft and improves config understanding
Jamie Curnow 2 lat temu
rodzic
commit
2a07445005

+ 4 - 3
backend/app.js

@@ -2,6 +2,7 @@ const express     = require('express');
 const bodyParser  = require('body-parser');
 const fileUpload  = require('express-fileupload');
 const compression = require('compression');
+const config      = require('./lib/config');
 const log         = require('./logger').express;
 
 /**
@@ -24,7 +25,7 @@ app.enable('trust proxy', ['loopback', 'linklocal', 'uniquelocal']);
 app.enable('strict routing');
 
 // pretty print JSON when not live
-if (process.env.NODE_ENV !== 'production') {
+if (config.debug()) {
 	app.set('json spaces', 2);
 }
 
@@ -65,7 +66,7 @@ app.use(function (err, req, res, next) {
 		}
 	};
 
-	if (process.env.NODE_ENV === 'development' || (req.baseUrl + req.path).includes('nginx/certificates')) {
+	if (config.debug() || (req.baseUrl + req.path).includes('nginx/certificates')) {
 		payload.debug = {
 			stack:    typeof err.stack !== 'undefined' && err.stack ? err.stack.split('\n') : null,
 			previous: err.previous
@@ -74,7 +75,7 @@ app.use(function (err, req, res, next) {
 
 	// Not every error is worth logging - but this is good for now until it gets annoying.
 	if (typeof err.stack !== 'undefined' && err.stack) {
-		if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
+		if (config.debug()) {
 			log.debug(err.stack);
 		} else if (typeof err.public == 'undefined' || !err.public) {
 			log.warn(err.message);

+ 20 - 26
backend/db.js

@@ -1,33 +1,27 @@
-const config = require('config');
+const config = require('./lib/config');
 
 if (!config.has('database')) {
-	throw new Error('Database config does not exist! Please read the instructions: https://github.com/jc21/nginx-proxy-manager/blob/master/doc/INSTALL.md');
+	throw new Error('Database config does not exist! Please read the instructions: https://nginxproxymanager.com/setup/');
 }
 
 function generateDbConfig() {
-	if (config.database.engine === 'knex-native') {
-		return config.database.knex;
-	} else
-		return {
-			client:     config.database.engine,
-			connection: {
-				host:     config.database.host,
-				user:     config.database.user,
-				password: config.database.password,
-				database: config.database.name,
-				port:     config.database.port
-			},
-			migrations: {
-				tableName: 'migrations'
-			}
-		};
+	const cfg = config.get('database');
+	if (cfg.engine === 'knex-native') {
+		return cfg.knex;
+	}
+	return {
+		client:     cfg.engine,
+		connection: {
+			host:     cfg.host,
+			user:     cfg.user,
+			password: cfg.password,
+			database: cfg.name,
+			port:     cfg.port
+		},
+		migrations: {
+			tableName: 'migrations'
+		}
+	};
 }
 
-
-let data = generateDbConfig();
-
-if (typeof config.database.version !== 'undefined') {
-	data.version = config.database.version;
-}
-
-module.exports = require('knex')(data);
+module.exports = require('knex')(generateDbConfig());

+ 1 - 87
backend/index.js

@@ -1,11 +1,9 @@
 #!/usr/bin/env node
 
+const fs     = require('fs');
 const logger = require('./logger').global;
 
 async function appStart () {
-	// Create config file db settings if environment variables have been set
-	await createDbConfigFromEnvironment();
-
 	const migrate             = require('./migrate');
 	const setup               = require('./setup');
 	const app                 = require('./app');
@@ -42,90 +40,6 @@ async function appStart () {
 		});
 }
 
-async function createDbConfigFromEnvironment() {
-	return new Promise((resolve, reject) => {
-		const envMysqlHost = process.env.DB_MYSQL_HOST || null;
-		const envMysqlPort = process.env.DB_MYSQL_PORT || null;
-		const envMysqlUser = process.env.DB_MYSQL_USER || null;
-		const envMysqlName = process.env.DB_MYSQL_NAME || null;
-		let envSqliteFile  = process.env.DB_SQLITE_FILE || null;
-
-		const fs       = require('fs');
-		const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json';
-		let configData = {};
-
-		try {
-			configData = require(filename);
-		} catch (err) {
-			// do nothing
-		}
-
-		if (configData.database && configData.database.engine && !configData.database.fromEnv) {
-			logger.info('Manual db configuration already exists, skipping config creation from environment variables');
-			resolve();
-			return;
-		}
-
-		if ((!envMysqlHost || !envMysqlPort || !envMysqlUser || !envMysqlName) && !envSqliteFile){
-			envSqliteFile = '/data/database.sqlite';
-			logger.info(`No valid environment variables for database provided, using default SQLite file '${envSqliteFile}'`);
-		}
-
-		if (envMysqlHost && envMysqlPort && envMysqlUser && envMysqlName) {
-			const newConfig = {
-				fromEnv:  true,
-				engine:   'mysql',
-				host:     envMysqlHost,
-				port:     envMysqlPort,
-				user:     envMysqlUser,
-				password: process.env.DB_MYSQL_PASSWORD,
-				name:     envMysqlName,
-			};
-
-			if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
-				// Config is unchanged, skip overwrite
-				resolve();
-				return;
-			}
-
-			logger.info('Generating MySQL knex configuration from environment variables');
-			configData.database = newConfig;
-
-		} else {
-			const newConfig = {
-				fromEnv: true,
-				engine:  'knex-native',
-				knex:    {
-					client:     'sqlite3',
-					connection: {
-						filename: envSqliteFile
-					},
-					useNullAsDefault: true
-				}
-			};
-			if (JSON.stringify(configData.database) === JSON.stringify(newConfig)) {
-				// Config is unchanged, skip overwrite
-				resolve();
-				return;
-			}
-
-			logger.info('Generating SQLite knex configuration');
-			configData.database = newConfig;
-		}
-
-		// Write config
-		fs.writeFile(filename, JSON.stringify(configData, null, 2), (err) => {
-			if (err) {
-				logger.error('Could not write db config to config file: ' + filename);
-				reject(err);
-			} else {
-				logger.debug('Wrote db configuration to config file: ' + filename);
-				resolve();
-			}
-		});
-	});
-}
-
 try {
 	appStart();
 } catch (err) {

+ 19 - 17
backend/internal/certificate.js

@@ -1,22 +1,24 @@
-const _                  = require('lodash');
-const fs                 = require('fs');
-const https              = require('https');
-const tempWrite          = require('temp-write');
-const moment             = require('moment');
-const logger             = require('../logger').ssl;
-const error              = require('../lib/error');
-const utils              = require('../lib/utils');
-const certificateModel   = require('../models/certificate');
-const dnsPlugins         = require('../global/certbot-dns-plugins');
-const internalAuditLog   = require('./audit-log');
-const internalNginx      = require('./nginx');
-const internalHost       = require('./host');
-const letsencryptStaging = process.env.NODE_ENV !== 'production';
+const _                = require('lodash');
+const fs               = require('fs');
+const https            = require('https');
+const tempWrite        = require('temp-write');
+const moment           = require('moment');
+const logger           = require('../logger').ssl;
+const config           = require('../lib/config');
+const error            = require('../lib/error');
+const utils            = require('../lib/utils');
+const certificateModel = require('../models/certificate');
+const dnsPlugins       = require('../global/certbot-dns-plugins');
+const internalAuditLog = require('./audit-log');
+const internalNginx    = require('./nginx');
+const internalHost     = require('./host');
+const archiver         = require('archiver');
+const path             = require('path');
+const { isArray }      = require('lodash');
+
+const letsencryptStaging = config.useLetsencryptStaging();
 const letsencryptConfig  = '/etc/letsencrypt.ini';
 const certbotCommand     = 'certbot';
-const archiver           = require('archiver');
-const path               = require('path');
-const { isArray }        = require('lodash');
 
 function omissions() {
 	return ['is_deleted'];

+ 16 - 16
backend/internal/nginx.js

@@ -1,9 +1,9 @@
-const _          = require('lodash');
-const fs         = require('fs');
-const logger     = require('../logger').nginx;
-const utils      = require('../lib/utils');
-const error      = require('../lib/error');
-const debug_mode = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
+const _      = require('lodash');
+const fs     = require('fs');
+const logger = require('../logger').nginx;
+const config = require('../lib/config');
+const utils  = require('../lib/utils');
+const error  = require('../lib/error');
 
 const internalNginx = {
 
@@ -65,7 +65,7 @@ const internalNginx = {
 							}
 						});
 
-						if (debug_mode) {
+						if (config.debug()) {
 							logger.error('Nginx test failed:', valid_lines.join('\n'));
 						}
 
@@ -101,7 +101,7 @@ const internalNginx = {
 	 * @returns {Promise}
 	 */
 	test: () => {
-		if (debug_mode) {
+		if (config.debug()) {
 			logger.info('Testing Nginx configuration');
 		}
 
@@ -184,7 +184,7 @@ const internalNginx = {
 	generateConfig: (host_type, host) => {
 		const nice_host_type = internalNginx.getFileFriendlyHostType(host_type);
 
-		if (debug_mode) {
+		if (config.debug()) {
 			logger.info('Generating ' + nice_host_type + ' Config:', JSON.stringify(host, null, 2));
 		}
 
@@ -239,7 +239,7 @@ const internalNginx = {
 					.then((config_text) => {
 						fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
 
-						if (debug_mode) {
+						if (config.debug()) {
 							logger.success('Wrote config:', filename, config_text);
 						}
 
@@ -249,7 +249,7 @@ const internalNginx = {
 						resolve(true);
 					})
 					.catch((err) => {
-						if (debug_mode) {
+						if (config.debug()) {
 							logger.warn('Could not write ' + filename + ':', err.message);
 						}
 
@@ -268,7 +268,7 @@ const internalNginx = {
 	 * @returns {Promise}
 	 */
 	generateLetsEncryptRequestConfig: (certificate) => {
-		if (debug_mode) {
+		if (config.debug()) {
 			logger.info('Generating LetsEncrypt Request Config:', certificate);
 		}
 
@@ -292,14 +292,14 @@ const internalNginx = {
 				.then((config_text) => {
 					fs.writeFileSync(filename, config_text, {encoding: 'utf8'});
 
-					if (debug_mode) {
+					if (config.debug()) {
 						logger.success('Wrote config:', filename, config_text);
 					}
 
 					resolve(true);
 				})
 				.catch((err) => {
-					if (debug_mode) {
+					if (config.debug()) {
 						logger.warn('Could not write ' + filename + ':', err.message);
 					}
 
@@ -416,8 +416,8 @@ const internalNginx = {
 	 * @param   {string}  config
 	 * @returns {boolean}
 	 */
-	advancedConfigHasDefaultLocation: function (config) {
-		return !!config.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
+	advancedConfigHasDefaultLocation: function (cfg) {
+		return !!cfg.match(/^(?:.*;)?\s*?location\s*?\/\s*?{/im);
 	},
 
 	/**

+ 181 - 0
backend/lib/config.js

@@ -0,0 +1,181 @@
+const fs      = require('fs');
+const NodeRSA = require('node-rsa');
+const { config } = require('process');
+const logger  = require('../logger').global;
+
+const keysFile = '/data/keys.json';
+
+let instance = null;
+
+// 1. Load from config file first (not recommended anymore)
+// 2. Use config env variables next
+const configure = () => {
+	const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json';
+	if (fs.existsSync(filename)) {
+		let configData;
+		try {
+			configData = require(filename);
+		} catch (err) {
+			// do nothing
+		}
+		if (configData?.database && configData?.database?.engine) {
+			logger.info(`Using configuration from file: ${filename}`);
+			instance = configData;
+			return;
+		}
+	}
+
+	const envMysqlHost = process.env.DB_MYSQL_HOST || null;
+	const envMysqlUser = process.env.DB_MYSQL_USER || null;
+	const envMysqlName = process.env.DB_MYSQL_NAME || null;
+	if (envMysqlHost && envMysqlUser && envMysqlName) {
+		// we have enough mysql creds to go with mysql
+		logger.info('Using MySQL configuration');
+		instance = {
+			database: {
+				engine:   'mysql',
+				host:     envMysqlHost,
+				port:     process.env.DB_MYSQL_PORT || 3306,
+				user:     envMysqlUser,
+				password: process.env.DB_MYSQL_PASSWORD,
+				name:     envMysqlName,
+			}
+		};
+		return;
+	}
+
+	const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/database.sqlite';
+	logger.info(`Using Sqlite: ${envSqliteFile}`);
+	instance = {
+		database: {
+			engine:  'knex-native',
+			knex:    {
+				client:     'sqlite3',
+				connection: {
+					filename: envSqliteFile
+				},
+				useNullAsDefault: true
+			}
+		}
+	};
+
+	// Get keys from file
+	if (!fs.existsSync(keysFile)) {
+		generateKeys();
+	} else if (!!process.env.DEBUG) {
+		logger.info('Keys file exists OK');
+	}
+	try {
+		instance.keys = require(keysFile);
+	} catch (err) {
+		logger.error('Could not read JWT key pair from config file: ' + keysFile, err);
+		process.exit(1);
+	}
+
+	logger.debug('Configuration: ' + JSON.stringify(instance, null, 2));
+};
+
+const generateKeys = () => {
+	logger.info('Creating a new JWT key pair...');
+	// Now create the keys and save them in the config.
+	const key = new NodeRSA({ b: 2048 });
+	key.generateKeyPair();
+
+	const keys = {
+		key: key.exportKey('private').toString(),
+		pub: key.exportKey('public').toString(),
+	};
+
+	// Write keys config
+	try {
+		fs.writeFileSync(keysFile, JSON.stringify(keys, null, 2));
+	} catch (err) {
+		logger.error('Could not write JWT key pair to config file: ' + keysFile + ': ' . err.message);
+		process.exit(1);
+	}
+	logger.info('Wrote JWT key pair to config file: ' + keysFile);
+};
+
+module.exports = {
+
+	/**
+	 *
+	 * @param   {string}  key   ie: 'database' or 'database.engine'
+	 * @returns {boolean}
+	 */
+	has: function(key) {
+		instance === null && configure();
+		const keys = key.split('.');
+		let level = instance;
+		let has = true;
+		keys.forEach((keyItem) =>{
+			if (typeof level[keyItem] === 'undefined') {
+				has = false;
+			} else {
+				level = level[keyItem];
+			}
+		});
+
+		return has;
+	},
+
+	/**
+	 * Gets a specific key from the top level
+	 *
+	 * @param {string} key
+	 * @returns {*}
+	 */
+	get: function (key) {
+		instance === null && configure();
+		if (key && typeof instance[key] !== 'undefined') {
+			return instance[key];
+		}
+		return instance;
+	},
+
+	/**
+	 * Is this a sqlite configuration?
+	 *
+	 * @returns {boolean}
+	 */
+	isSqlite: function () {
+		instance === null && configure();
+		return instance.database?.knex && instance.database?.knex?.client === 'sqlite3';
+	},
+
+	/**
+	 * Are we running in debug mdoe?
+	 *
+	 * @returns {boolean}
+	 */
+	debug: function () {
+		return !!process.env.DEBUG;
+	},
+
+	/**
+	 * Returns a public key
+	 *
+	 * @returns {string}
+	 */
+	getPublicKey: function () {
+		instance === null && configure();
+		return instance?.keys?.pub
+	},
+
+	/**
+	 * Returns a private key
+	 *
+	 * @returns {string}
+	 */
+	getPrivateKey: function () {
+		instance === null && configure();
+		return instance?.keys?.key;
+	},
+
+	/**
+	 * @returns {boolean}
+	 */
+	useLetsencryptStaging: function () {
+		return !!process.env.LE_STAGING;
+	}
+};

+ 1 - 1
backend/lib/validator/index.js

@@ -5,7 +5,7 @@ const definitions = require('../../schema/definitions.json');
 RegExp.prototype.toJSON = RegExp.prototype.toString;
 
 const ajv = require('ajv')({
-	verbose:     true, //process.env.NODE_ENV === 'development',
+	verbose:     true,
 	allErrors:   true,
 	format:      'full',  // strict regexes for format checks
 	coerceTypes: true,

+ 2 - 2
backend/models/now_helper.js

@@ -1,11 +1,11 @@
 const db     = require('../db');
-const config = require('config');
+const config = require('../lib/config');
 const Model  = require('objection').Model;
 
 Model.knex(db);
 
 module.exports = function () {
-	if (config.database.knex && config.database.knex.client === 'sqlite3') {
+	if (config.isSqlite()) {
 		// eslint-disable-next-line
 		return Model.raw("datetime('now','localtime')");
 	}

+ 14 - 20
backend/models/token.js

@@ -6,44 +6,36 @@
 const _      = require('lodash');
 const jwt    = require('jsonwebtoken');
 const crypto = require('crypto');
+const config = require('../lib/config');
 const error  = require('../lib/error');
+const logger = require('../logger').global;
 const ALGO   = 'RS256';
 
-let public_key  = null;
-let private_key = null;
-
-function checkJWTKeyPair() {
-	if (!public_key || !private_key) {
-		let config  = require('config');
-		public_key  = config.get('jwt.pub');
-		private_key = config.get('jwt.key');
-	}
-}
-
 module.exports = function () {
 
 	let token_data = {};
 
-	let self = {
+	const self = {
 		/**
 		 * @param {Object}  payload
 		 * @returns {Promise}
 		 */
 		create: (payload) => {
+			if (!config.getPrivateKey()) {
+				logger.error('Private key is empty!')
+			}
 			// sign with RSA SHA256
-			let options = {
+			const options = {
 				algorithm: ALGO,
 				expiresIn: payload.expiresIn || '1d'
 			};
 
 			payload.jti = crypto.randomBytes(12)
 				.toString('base64')
-				.substr(-8);
-
-			checkJWTKeyPair();
+				.substring(-8);
 
 			return new Promise((resolve, reject) => {
-				jwt.sign(payload, private_key, options, (err, token) => {
+				jwt.sign(payload, config.getPrivateKey(), options, (err, token) => {
 					if (err) {
 						reject(err);
 					} else {
@@ -62,13 +54,15 @@ module.exports = function () {
 		 * @returns {Promise}
 		 */
 		load: function (token) {
+			if (!config.getPublicKey()) {
+				logger.error('Public key is empty!')
+			}
 			return new Promise((resolve, reject) => {
-				checkJWTKeyPair();
 				try {
 					if (!token || token === null || token === 'null') {
 						reject(new error.AuthError('Empty token'));
 					} else {
-						jwt.verify(token, public_key, {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => {
+						jwt.verify(token, config.getPublicKey(), {ignoreExpiration: false, algorithms: [ALGO]}, (err, result) => {
 							if (err) {
 
 								if (err.name === 'TokenExpiredError') {
@@ -132,7 +126,7 @@ module.exports = function () {
 		 * @returns {Integer}
 		 */
 		getUserId: (default_value) => {
-			let attrs = self.get('attrs');
+			const attrs = self.get('attrs');
 			if (attrs && typeof attrs.id !== 'undefined' && attrs.id) {
 				return attrs.id;
 			}

+ 0 - 1
backend/package.json

@@ -10,7 +10,6 @@
 		"bcrypt": "^5.0.0",
 		"body-parser": "^1.19.0",
 		"compression": "^1.7.4",
-		"config": "^3.3.1",
 		"express": "^4.17.3",
 		"express-fileupload": "^1.1.9",
 		"gravatar": "^1.8.0",

+ 6 - 65
backend/setup.js

@@ -1,6 +1,4 @@
-const fs                  = require('fs');
-const NodeRSA             = require('node-rsa');
-const config              = require('config');
+const config              = require('./lib/config');
 const logger              = require('./logger').setup;
 const certificateModel    = require('./models/certificate');
 const userModel           = require('./models/user');
@@ -9,62 +7,6 @@ const utils               = require('./lib/utils');
 const authModel           = require('./models/auth');
 const settingModel        = require('./models/setting');
 const dns_plugins         = require('./global/certbot-dns-plugins');
-const debug_mode          = process.env.NODE_ENV !== 'production' || !!process.env.DEBUG;
-
-/**
- * Creates a new JWT RSA Keypair if not alread set on the config
- *
- * @returns {Promise}
- */
-const setupJwt = () => {
-	return new Promise((resolve, reject) => {
-		// Now go and check if the jwt gpg keys have been created and if not, create them
-		if (!config.has('jwt') || !config.has('jwt.key') || !config.has('jwt.pub')) {
-			logger.info('Creating a new JWT key pair...');
-
-			// jwt keys are not configured properly
-			const filename  = config.util.getEnv('NODE_CONFIG_DIR') + '/' + (config.util.getEnv('NODE_ENV') || 'default') + '.json';
-			let config_data = {};
-
-			try {
-				config_data = require(filename);
-			} catch (err) {
-				// do nothing
-				if (debug_mode) {
-					logger.debug(filename + ' config file could not be required');
-				}
-			}
-
-			// Now create the keys and save them in the config.
-			let key = new NodeRSA({ b: 2048 });
-			key.generateKeyPair();
-
-			config_data.jwt = {
-				key: key.exportKey('private').toString(),
-				pub: key.exportKey('public').toString(),
-			};
-
-			// Write config
-			fs.writeFile(filename, JSON.stringify(config_data, null, 2), (err) => {
-				if (err) {
-					logger.error('Could not write JWT key pair to config file: ' + filename);
-					reject(err);
-				} else {
-					logger.info('Wrote JWT key pair to config file: ' + filename);
-					delete require.cache[require.resolve('config')];
-					resolve();
-				}
-			});
-		} else {
-			// JWT key pair exists
-			if (debug_mode) {
-				logger.debug('JWT Keypair already exists');
-			}
-
-			resolve();
-		}
-	});
-};
 
 /**
  * Creates a default admin users if one doesn't already exist in the database
@@ -119,8 +61,8 @@ const setupDefaultUser = () => {
 					.then(() => {
 						logger.info('Initial admin setup completed');
 					});
-			} else if (debug_mode) {
-				logger.debug('Admin user setup not required');
+			} else if (config.debug()) {
+				logger.info('Admin user setup not required');
 			}
 		});
 };
@@ -151,8 +93,8 @@ const setupDefaultSettings = () => {
 						logger.info('Default settings added');
 					});
 			}
-			if (debug_mode) {
-				logger.debug('Default setting setup not required');
+			if (config.debug()) {
+				logger.info('Default setting setup not required');
 			}
 		});
 };
@@ -225,8 +167,7 @@ const setupLogrotation = () => {
 };
 
 module.exports = function () {
-	return setupJwt()
-		.then(setupDefaultUser)
+	return setupDefaultUser()
 		.then(setupDefaultSettings)
 		.then(setupCertbotPlugins)
 		.then(setupLogrotation);

+ 0 - 12
backend/yarn.lock

@@ -677,13 +677,6 @@ [email protected]:
   resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
   integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
 
-config@^3.3.1:
-  version "3.3.1"
-  resolved "https://registry.yarnpkg.com/config/-/config-3.3.1.tgz#b6a70e2908a43b98ed20be7e367edf0cc8ed5a19"
-  integrity sha512-+2/KaaaAzdwUBE3jgZON11L1ggLLhpf2FsGrfqYFHZW22ySGv/HqYIXrBwKKvn+XZh1UBUjHwAcrfsSkSygT+Q==
-  dependencies:
-    json5 "^2.1.1"
-
 configstore@^5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96"
@@ -1769,11 +1762,6 @@ json-stable-stringify-without-jsonify@^1.0.1:
   resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
   integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
 
-json5@^2.1.1:
-  version "2.2.3"
-  resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
-  integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
-
 jsonwebtoken@^9.0.0:
   version "9.0.0"
   resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d"

+ 14 - 11
docker/docker-compose.dev.yml

@@ -16,14 +16,17 @@ services:
     environment:
       PUID: 1000
       PGID: 1000
-      NODE_ENV: "development"
       FORCE_COLOR: 1
-      DEVELOPMENT: "true"
-      DB_MYSQL_HOST: "db"
-      DB_MYSQL_PORT: 3306
-      DB_MYSQL_USER: "npm"
-      DB_MYSQL_PASSWORD: "npm"
-      DB_MYSQL_NAME: "npm"
+      # specifically for dev:
+      DEBUG: 'true'
+      DEVELOPMENT: 'true'
+      LE_STAGING: 'true'
+      # db:
+      DB_MYSQL_HOST: 'db'
+      DB_MYSQL_PORT: '3306'
+      DB_MYSQL_USER: 'npm'
+      DB_MYSQL_PASSWORD: 'npm'
+      DB_MYSQL_NAME: 'npm'
       # DB_SQLITE_FILE: "/data/database.sqlite"
       # DISABLE_IPV6: "true"
     volumes:
@@ -44,10 +47,10 @@ services:
     networks:
       - nginx_proxy_manager
     environment:
-      MYSQL_ROOT_PASSWORD: "npm"
-      MYSQL_DATABASE: "npm"
-      MYSQL_USER: "npm"
-      MYSQL_PASSWORD: "npm"
+      MYSQL_ROOT_PASSWORD: 'npm'
+      MYSQL_DATABASE: 'npm'
+      MYSQL_USER: 'npm'
+      MYSQL_PASSWORD: 'npm'
     volumes:
       - db_data:/var/lib/mysql