| const fs = require('fs'); |
| const path = require('path'); |
| const crypto = require('crypto'); |
| const dotenv = require('dotenv'); |
|
|
| |
| |
| |
| |
| |
| class Env { |
| constructor() { |
| this.envMap = { |
| default: '.env', |
| development: '.env.development', |
| test: '.env.test', |
| production: '.env.production', |
| }; |
|
|
| this.init(); |
|
|
| this.isProduction = process.env.NODE_ENV === 'production'; |
| this.domains = { |
| client: process.env.DOMAIN_CLIENT, |
| server: process.env.DOMAIN_SERVER, |
| }; |
| } |
|
|
| |
| |
| |
| init() { |
| let hasDefault = false; |
| |
| if (fs.existsSync(this.envMap.default)) { |
| hasDefault = true; |
| dotenv.config({ |
| |
| path: path.resolve(__dirname, '..', this.envMap.default), |
| }); |
| } else { |
| console.warn('The default .env file was not found'); |
| } |
|
|
| const environment = this.currentEnvironment(); |
|
|
| |
| const envFile = this.envMap[environment]; |
|
|
| |
| if (fs.existsSync(envFile)) { |
| dotenv.config({ |
| |
| path: path.resolve(__dirname, '..', envFile), |
| }); |
| } else if (!hasDefault) { |
| console.warn('No env files found, have you completed the install process?'); |
| } |
| } |
|
|
| |
| |
| |
| validate() { |
| const requiredKeys = [ |
| 'NODE_ENV', |
| 'JWT_SECRET', |
| 'DOMAIN_CLIENT', |
| 'DOMAIN_SERVER', |
| 'CREDS_KEY', |
| 'CREDS_IV', |
| ]; |
|
|
| const missingKeys = requiredKeys |
| .map((key) => { |
| const variable = process.env[key]; |
| if (variable === undefined || variable === null) { |
| return key; |
| } |
| }) |
| .filter((value) => value !== undefined); |
|
|
| |
| if (missingKeys.length) { |
| const message = ` |
| The following required env variables are missing: |
| ${missingKeys.toString()}. |
| Please add them to your env file or run 'npm run install' |
| `; |
| throw new Error(message); |
| } |
|
|
| |
| if (process.env.JWT_SECRET === 'secret') { |
| console.warn('Warning: JWT_SECRET is set to default value'); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| resolve(envFile) { |
| return path.resolve(process.cwd(), envFile); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| addSecureEnvVar(filePath, key, length) { |
| const env = {}; |
| env[key] = this.generateSecureRandomString(length); |
| this.writeEnvFile(filePath, env); |
| } |
|
|
| |
| |
| |
| writeEnvFile(filePath, env) { |
| const content = fs.readFileSync(filePath, 'utf-8'); |
| const lines = content.split('\n'); |
| const updatedLines = lines |
| .map((line) => { |
| if (line.trim().startsWith('#')) { |
| |
| if (env[line] === 'remove') { |
| return null; |
| } |
| |
| return line; |
| } |
|
|
| const [key, value] = line.split('='); |
| if (key && value && Object.prototype.hasOwnProperty.call(env, key.trim())) { |
| if (env[key.trim()] === 'remove') { |
| return null; |
| } |
| return `${key.trim()}=${env[key.trim()]}`; |
| } |
| return line; |
| }) |
| .filter((line) => line !== null); |
|
|
| |
| Object.entries(env).forEach(([key, value]) => { |
| if (value !== 'remove' && !updatedLines.some((line) => line.startsWith(`${key}=`))) { |
| updatedLines.push(`${key}=${value}`); |
| } |
| }); |
|
|
| |
| const fixedLines = updatedLines.map((line) => { |
| |
| const [key, value] = line.split(/=(.+)/); |
| if (typeof value === 'undefined' || line.trim().startsWith('#')) { |
| return line; |
| } |
| |
| |
| const wrappedValue = |
| value.includes(' ') && !value.includes('"') && !value.includes('\'') && !/\d/.test(value) |
| ? `"${value}"` |
| : value; |
| return `${key}=${wrappedValue}`; |
| }); |
|
|
| const updatedContent = fixedLines.join('\n'); |
| fs.writeFileSync(filePath, updatedContent); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| generateSecureRandomString(length = 32) { |
| return crypto.randomBytes(length).toString('hex'); |
| } |
|
|
| |
| |
| |
| all() { |
| return process.env; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| get(variable) { |
| return process.env[variable]; |
| } |
|
|
| |
| |
| |
| |
| |
| currentEnvironment() { |
| return this.get('NODE_ENV'); |
| } |
|
|
| |
| |
| |
| |
| |
| isDevelopment() { |
| return this.currentEnvironment() === 'development'; |
| } |
|
|
| |
| |
| |
| |
| |
| isTest() { |
| return this.currentEnvironment() === 'test'; |
| } |
|
|
| |
| |
| |
| |
| |
| isProduction() { |
| return this.currentEnvironment() === 'production'; |
| } |
|
|
| |
| |
| |
| |
| |
| isCI() { |
| return this.currentEnvironment() === 'CI'; |
| } |
| } |
|
|
| const env = new Env(); |
|
|
| module.exports = env; |
|
|