Node.js definindo configurações específicas do ambiente para serem usadas com everyauth

117

Estou usando node.js + express.js + everyauth.js. Mudei toda a minha lógica everyauth para um arquivo de módulo

var login = require('./lib/everyauthLogin');

dentro disso, carrego meu arquivo de configuração oAuth com as combinações de chave / segredo:

var conf = require('./conf');
.....
twitter: {
    consumerKey: 'ABC', 
    consumerSecret: '123'
}

Esses códigos são diferentes para diferentes ambientes - desenvolvimento / teste / produção, pois os retornos de chamada são para diferentes urls.

Qu. Como faço para defini-los na configuração ambiental para filtrar todos os módulos ou posso passar o caminho diretamente para o módulo?

Definido em env:

app.configure('development', function(){
  app.set('configPath', './confLocal');
});

app.configure('production', function(){
  app.set('configPath', './confProduction');
});

var conf = require(app.get('configPath'));

Passe em

app.configure('production', function(){
  var login = require('./lib/everyauthLogin', {configPath: './confProduction'});
});

? espero que faça sentido

Andy t
fonte
Encontrei uma solução que está usando algumas das idéias abaixo, por ter a função module = ao invés de um objeto, posso avaliar process.env.NODE_ENV e retornar o objeto correto para o ambiente. Um pouco bagunçado, mas funciona.
Andy em
Perdoe a autopromoção desavergonhada, mas escrevi um módulo para node.js que fará isso por meio de arquivos separados e uma opção de linha de comando: node-configure
Randolpho

Respostas:

192

Minha solução,

carregue o aplicativo usando

NODE_ENV=production node app.js

Em seguida, configure config.jscomo uma função em vez de um objeto

module.exports = function(){
    switch(process.env.NODE_ENV){
        case 'development':
            return {dev setting};

        case 'production':
            return {prod settings};

        default:
            return {error or other settings};
    }
};

Então, de acordo com a solução Jans, carregue o arquivo e crie uma nova instância que poderíamos passar em um valor se necessário, neste caso process.env.NODE_ENVé global, portanto não é necessário.

var Config = require('./conf'),
    conf = new Config();

Então podemos acessar as propriedades do objeto de configuração exatamente como antes

conf.twitter.consumerKey
Andy t
fonte
2
Por que você está usando novo aqui?
bluehallu 01 de
5
Eu segundo @bluehallu. É newnecessário?
Sung Cho
2
o equivalente no Windows seria SET NODE_ENV = development
mujaffars
3
Em vez de fazer new. Eu faço seguindo no config.js....Config = function(){...}; module.exports = Config()
Atu
E se eu tiver 50 servidores web, caso em que será difícil acessar cada servidor para iniciar manualmente o script
Rajesh
60

Você também pode ter um arquivo JSON com NODE_ENV como o nível superior. IMO, esta é a melhor maneira de expressar as definições de configuração (em vez de usar um script que retorna as configurações).

var config = require('./env.json')[process.env.NODE_ENV || 'development'];

Exemplo para env.json:

{
    "development": {
        "MONGO_URI": "mongodb://localhost/test",
        "MONGO_OPTIONS": { "db": { "safe": true } }
    },
    "production": {
        "MONGO_URI": "mongodb://localhost/production",
        "MONGO_OPTIONS": { "db": { "safe": true } }
    }
}
mattwad
fonte
Olá, você poderia explicar por que acha que essa é a melhor maneira de expressar as configurações (em vez de usar um script que retorna as configurações). ?
Venkat Kotra
14
Acho que não faz muita diferença. Mentalmente, quando vejo JSON penso em 'dados estáticos' vs. quando vejo um arquivo JS, acho que há alguma lógica dentro dele. Além disso, outro benefício de usar o tipo .json é que outros idiomas podem importar o mesmo arquivo.
mattwad
1
A configuração do @VenkatKotra é geralmente considerada estática e, portanto, é melhor expressa declarativamente com coisas como json, yaml, ini, etc. Feito imperativamente, com um script que produz esse estado, meio que implica que algo dinâmico está acontecendo, o que seria ruim.
máx.
9
Esteja ciente de que esse método expõe credenciais no controle de origem.
Pier-Luc Gendreau
posso fazer diffrence url para encenação e produção?
Alex
34

Uma solução muito útil é usar o módulo de configuração .

depois de instalar o módulo:

$ npm install config

Você pode criar um default.json arquivo de configuração . (você pode usar o objeto JSON ou JS usando a extensão .json5)

Por exemplo

$ vi config/default.json

{
  "name": "My App Name",
  "configPath": "/my/default/path",
  "port": 3000
}

Esta configuração padrão pode ser substituída pelo arquivo de configuração do ambiente ou por um arquivo de configuração local para um ambiente de desenvolvimento local:

production.json pode ser:

{
  "configPath": "/my/production/path",
  "port": 8080
}

development.json pode ser:

{
  "configPath": "/my/development/path",
  "port": 8081
}

Em seu PC local, você pode ter um local.json que substitui todo o ambiente ou pode ter uma configuração local específica como local-production.json ou local-development.json .

A lista completa de ordem de carregamento .

Dentro do seu aplicativo

Em seu aplicativo, você só precisa exigir configuração e o atributo necessário.

var conf = require('config'); // it loads the right file
var login = require('./lib/everyauthLogin', {configPath: conf.get('configPath'));

Carregue o aplicativo

carregue o aplicativo usando:

NODE_ENV=production node app.js

ou configurar o ambiente correto com forever ou pm2

Para sempre:

NODE_ENV=production forever [flags] start app.js [app_flags]

PM2 (via shell):

export NODE_ENV=staging
pm2 start app.js

PM2 (via .json):

process.json

{
   "apps" : [{
    "name": "My App",
    "script": "worker.js",
    "env": {
      "NODE_ENV": "development",
    },
    "env_production" : {
       "NODE_ENV": "production"
    }
  }]
}

E depois

$ pm2 start process.json --env production

Esta solução é muito limpa e facilita a configuração de diferentes arquivos de configuração para ambiente de Produção / Preparação / Desenvolvimento e também para configuração local.

Zauker
fonte
npm install config --save, não é melhor?
stackdave
14

Em resumo

Este tipo de configuração é simples e elegante:

env.json

{
  "development": {
      "facebook_app_id": "facebook_dummy_dev_app_id",
      "facebook_app_secret": "facebook_dummy_dev_app_secret",
  }, 
  "production": {
      "facebook_app_id": "facebook_dummy_prod_app_id",
      "facebook_app_secret": "facebook_dummy_prod_app_secret",
  }
}

common.js

var env = require('env.json');

exports.config = function() {
  var node_env = process.env.NODE_ENV || 'development';
  return env[node_env];
};

app.js

var common = require('./routes/common')
var config = common.config();

var facebook_app_id = config.facebook_app_id;
// do something with facebook_app_id

Para executar no modo de produção: $ NODE_ENV=production node app.js


Em detalhe

Esta solução é de: http://himanshu.gilani.info/blog/2012/09/26/bootstraping-a-node-dot-js-app-for-dev-slash-prod-environment/ , verifique para Mais detalhes.

Kris
fonte
5

A maneira como fazemos isso é passando um argumento ao iniciar o aplicativo com o ambiente. Por exemplo:

node app.js -c dev

Em app.js, carregamos dev.jscomo nosso arquivo de configuração. Você pode analisar essas opções com optparse-js .

Agora você tem alguns módulos principais que dependem deste arquivo de configuração. Quando você os escreve como tal:

var Workspace = module.exports = function(config) {
    if (config) {
         // do something;
    }
}

(function () {
    this.methodOnWorkspace = function () {

    };
}).call(Workspace.prototype);

E você pode chamá-lo app.jsassim:

var Workspace = require("workspace");
this.workspace = new Workspace(config);
Jan Jongboom
fonte
Prefiro manter toda a lógica dentro do app.configure('developmentcódigo app.js , mas vou dar uma olhada para ver se posso usar essa solução com isso
andy t
Atualize para esta resposta: o Architect é uma estrutura de gerenciamento de dependência que resolve isso de uma maneira mais agradável.
Jan Jongboom
5

Uma maneira elegante é usar o .envarquivo para substituir localmente as configurações de produção. Não há necessidade de opções de linha de comando. Não há necessidade de todas as vírgulas e colchetes em um config.jsonarquivo. Veja minha resposta aqui

Exemplo: na minha máquina o .envarquivo é este:

NODE_ENV=dev
TWITTER_AUTH_TOKEN=something-needed-for-api-calls

Meu local .envsubstitui quaisquer variáveis ​​de ambiente. Mas nos servidores de teste ou produção (talvez eles estejam em heroku.com), as variáveis ​​de ambiente são predefinidas para o estágio NODE_ENV=stageou produção NODE_ENV=prod.

Benxamin
fonte
4

definir a variável de ambiente no servidor de implementação (ex: como NODE_ENV = produção). Você pode acessar sua variável de ambiente por meio de process.env.NODE_ENV. Encontre o seguinte arquivo de configuração para as configurações globais

const env = process.env.NODE_ENV || "development"

const configs = {
    base: {
        env,
        host: '0.0.0.0',
        port: 3000,
        dbPort: 3306,
        secret: "secretKey for sessions",
        dialect: 'mysql',
        issuer : 'Mysoft corp',
        subject : '[email protected]',
    },
    development: {
        port: 3000,
        dbUser: 'root',
        dbPassword: 'root',

    },
    smoke: {
        port: 3000,
        dbUser: 'root',
    },
    integration: {
        port: 3000,
        dbUser: 'root',
    },
    production: {
        port: 3000,
        dbUser: 'root',
    }
};

const config = Object.assign(configs.base, configs[env]);

module.exports= config;

base contém configuração comum para todos os ambientes.

em seguida, importe em outros módulos como

const config =  require('path/to/config.js')
console.log(config.port)

Happy Coding ...

ajaykumar mp
fonte
3

Que tal fazer isso de uma maneira muito mais elegante com nodejs-config módulo .

Este módulo é capaz de definir o ambiente de configuração com base no nome do seu computador. Depois disso, ao solicitar uma configuração, você obterá o valor específico do ambiente.

Por exemplo, vamos supor que você tenha duas máquinas de desenvolvimento chamadas pc1 e pc2 e uma máquina de produção chamada pc3. Sempre que você solicitar valores de configuração em seu código no pc1 ou pc2, você deve obter a configuração do ambiente de "desenvolvimento" e no pc3 deve obter a configuração do ambiente de "produção". Isso pode ser conseguido assim:

  1. Crie um arquivo de configuração de base no diretório config, digamos "app.json" e adicione as configurações necessárias a ele.
  2. Agora, basta criar pastas dentro do diretório de configuração que correspondam ao nome do seu ambiente, neste caso "desenvolvimento" e "produção".
  3. Em seguida, crie os arquivos de configuração que deseja substituir e especifique as opções para cada ambiente nos diretórios de ambiente (observe que você não precisa especificar todas as opções que estão no arquivo de configuração base, mas apenas as opções que deseja substituir. os arquivos de configuração do ambiente irão "cascatear" sobre os arquivos base.).

Agora crie uma nova instância de configuração com a seguinte sintaxe.

var config = require('nodejs-config')(
   __dirname,  // an absolute path to your applications 'config' directory
   {
      development: ["pc1", "pc2"],
      production: ["pc3"],

   }
);

Agora você pode obter qualquer valor de configuração sem se preocupar com o ambiente como este:

config.get('app').configurationKey;
Harish Anchu
fonte
0

Essa resposta não é algo novo. É semelhante ao que @andy_t mencionou. Mas eu uso o padrão abaixo por dois motivos.

  1. Implementação limpa sem dependências externas de npm

  2. Mesclar as configurações padrão com as configurações baseadas no ambiente.

Implementação de Javascript

const settings = {
    _default: {
       timeout: 100
       baseUrl: "http://some.api/",
    },
    production: {
       baseUrl: "http://some.prod.api/",
    },
}
// If you are not using ECMAScript 2018 Standard
// https://stackoverflow.com/a/171256/1251350
module.exports = { ...settings._default, ...settings[process.env.NODE_ENV] }

Eu geralmente uso o texto digitado em meu projeto de nó Abaixo está minha implementação real copiada e colada.

Implementação de typescript

const settings: { default: ISettings, production: any } = {
    _default: {
        timeout: 100,
        baseUrl: "",
    },
    production: {
        baseUrl: "",
    },
}

export interface ISettings {
    baseUrl: string
}

export const config = ({ ...settings._default, ...settings[process.env.NODE_ENV] } as ISettings)
Faraj Farook
fonte