|
const dotenv = require('dotenv'); |
|
const path = require('path'); |
|
const fs = require('fs'); |
|
const crypto = require('crypto'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
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: this.resolve(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: this.resolve(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; |
|
|