Node.js konfiguruje specyficzne dla środowiska konfiguracje do użycia z everyauth

117

Używam node.js + express.js + everyauth.js. Przeniosłem całą moją logikę everyauth do pliku modułu

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

wewnątrz tego ładuję mój plik konfiguracyjny OAuth z kombinacjami klucz / sekret:

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

Kody te są różne w różnych środowiskach - programowanie / przemieszczanie / produkcja, ponieważ wywołania zwrotne dotyczą różnych adresów URL.

Qu. Jak ustawić je w konfiguracji środowiskowej, aby filtrowały przez wszystkie moduły, czy mogę przekazać ścieżkę bezpośrednio do modułu?

Ustaw w środ .:

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

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

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

Przechodzą w

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

? mam nadzieję, że to ma sens

andy t
źródło
Znalazłem rozwiązanie, które wykorzystuje niektóre z poniższych pomysłów, mając moduł = function zamiast obiektu, mogę ocenić process.env.NODE_ENV i zwrócić prawidłowy obiekt dla środowiska. Trochę niechlujny, ale działa.
andy t
Przepraszam za bezwstydną autopromocję, ale napisałem moduł dla node.js, który zrobi to za pomocą oddzielnych plików i przełącznika wiersza poleceń: node-configure
Randolpho,

Odpowiedzi:

192

Moje rozwiązanie,

załaduj aplikację za pomocą

NODE_ENV=production node app.js

Następnie skonfiguruj config.jsjako funkcję, a nie obiekt

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

        case 'production':
            return {prod settings};

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

Następnie, zgodnie z rozwiązaniem Jans, załaduj plik i utwórz nową instancję, którą w razie potrzeby moglibyśmy przekazać w wartości, w tym przypadku process.env.NODE_ENVjest ona globalna, więc nie jest potrzebna.

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

Wtedy możemy uzyskać dostęp do właściwości obiektu konfiguracyjnego dokładnie tak, jak poprzednio

conf.twitter.consumerKey
andy t
źródło
2
Dlaczego używasz tutaj nowego?
bluehallu
5
Drugie miejsce w @bluehallu. Jest newkonieczne?
Sung Cho
2
odpowiednikiem w systemie Windows byłoby SET NODE_ENV = development
mujaffars,
3
Zamiast robić new. Robię to w config.js....Config = function(){...}; module.exports = Config()
Atu
Co jeśli mam 50 serwerów WWW, w takim przypadku trudno będzie przejść na każdy serwer w celu ręcznego uruchomienia skryptu
Rajesh
60

Możesz również mieć plik JSON z NODE_ENV jako najwyższym poziomem. IMO, jest to lepszy sposób wyrażania ustawień konfiguracyjnych (w przeciwieństwie do używania skryptu, który zwraca ustawienia).

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

Przykład dla 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
źródło
Cześć, czy mógłbyś wyjaśnić, dlaczego uważasz, że jest to lepszy sposób wyrażenia ustawień konfiguracyjnych (w przeciwieństwie do używania skryptu, który zwraca ustawienia). ?
Venkat Kotra
14
Myślę, że to nie robi dużej różnicy. Mentalnie, kiedy widzę JSON, myślę „statyczne dane”, a kiedy widzę plik JS, myślę, że jest w nim jakaś logika. Kolejną zaletą używania typu .json jest to, że inne języki mogą importować ten sam plik.
mattwad
1
Konfiguracja @VenkatKotra jest ogólnie uważana za statyczną i dlatego najlepiej jest wyrażać ją deklaratywnie za pomocą rzeczy takich jak json, yaml, ini itp. Wykonane imperatywnie, ze skryptem, który zwraca ten stan, sortof sugeruje, że dzieje się coś dynamicznego , co byłoby złe.
maksymalnie
9
Należy pamiętać, że ta metoda ujawnia poświadczenia w kontroli źródła.
Pier-Luc Gendreau
czy mogę utworzyć inny adres URL dla etapów i produkcji?
Alex,
34

Bardzo przydatnym rozwiązaniem jest skorzystanie z modułu config .

po zainstalowaniu modułu:

$ npm install config

Możesz utworzyć plik konfiguracyjny default.json . (możesz użyć obiektu JSON lub JS z rozszerzeniem .json5)

Na przykład

$ vi config/default.json

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

Ta domyślna konfiguracja może zostać zastąpiona przez plik konfiguracyjny środowiska lub lokalny plik konfiguracyjny dla lokalnego środowiska programistycznego:

production.json może być:

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

development.json może być:

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

Na lokalnym komputerze możesz mieć plik local.json, który przesłania całe środowisko, lub możesz mieć określoną konfigurację lokalną, taką jak local-production.json lub local-development.json .

Pełna lista zleceń załadunku .

Wewnątrz Twojej aplikacji

W swojej aplikacji potrzebujesz tylko konfiguracji i wymaganego atrybutu.

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

Załaduj aplikację

załaduj aplikację za pomocą:

NODE_ENV=production node app.js

lub ustawienie odpowiedniego środowiska za pomocą forever lub pm2

Na zawsze:

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

PM2 (przez powłokę):

export NODE_ENV=staging
pm2 start app.js

PM2 (przez .json):

process.json

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

I wtedy

$ pm2 start process.json --env production

To rozwiązanie jest bardzo przejrzyste i ułatwia ustawianie różnych plików konfiguracyjnych dla środowiska produkcyjnego / przejściowego / programistycznego, a także ustawień lokalnych.

Zauker
źródło
npm install config - zapisz, nie jest lepiej?
stackdave
14

W skrócie

Taka konfiguracja jest prosta i elegancka:

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

Aby uruchomić w trybie produkcyjnym: $ NODE_ENV=production node app.js


Szczegółowo

To rozwiązanie pochodzi z: http://himanshu.gilani.info/blog/2012/09/26/bootstraping-a-node-dot-js-app-for-dev-slash-prod-environment/ , sprawdź je więcej szczegółów.

kris
źródło
5

Sposób, w jaki to robimy, polega na przekazaniu argumentu podczas uruchamiania aplikacji ze środowiskiem. Na przykład:

node app.js -c dev

W app.js ładujemy następnie dev.jsjako nasz plik konfiguracyjny. Możesz przeanalizować te opcje za pomocą optparse-js .

Teraz masz kilka podstawowych modułów, które są zależne od tego pliku konfiguracyjnego. Kiedy piszesz je jako takie:

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

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

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

I możesz to nazwać w ten sposób app.js:

var Workspace = require("workspace");
this.workspace = new Workspace(config);
Jan Jongboom
źródło
Wolałbym raczej zachować całą logikę w app.configure('developmentkodzie app.js , ale spróbuję sprawdzić, czy mogę użyć tego rozwiązania z tym
andy t
Zaktualizuj tę odpowiedź: Architekt to platforma zarządzania zależnościami, która rozwiązuje ten problem w lepszy sposób.
Jan Jongboom
5

Eleganckim sposobem jest użycie .envpliku do lokalnego zastąpienia ustawień produkcyjnych. Nie ma potrzeby stosowania przełączników wiersza poleceń. Nie ma potrzeby stosowania tych wszystkich przecinków i nawiasów w config.jsonpliku. Zobacz moją odpowiedź tutaj

Przykład: na moim komputerze .envplik jest następujący:

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

Moja lokalna .envprzesłania wszelkie zmienne środowiskowe. Ale na serwerach pomostowych lub produkcyjnych (może są one na heroku.com) zmienne środowiskowe są wstępnie ustawione na etap NODE_ENV=stagelub produkcję NODE_ENV=prod.

Benxamin
źródło
4

ustaw zmienną środowiskową na serwerze wdrożeniowym (np. jak NODE_ENV = produkcja). Możesz uzyskać dostęp do zmiennej środowiskowej poprzez process.env.NODE_ENV. Znajdź następujący plik konfiguracyjny dla ustawień globalnych

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 zawiera wspólną konfigurację dla wszystkich środowisk.

następnie zaimportuj do innych modułów, takich jak

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

Miłego kodowania ...

ajaykumar mp
źródło
3

Co powiesz na zrobienie tego w znacznie bardziej elegancki sposób za pomocą nodejs-config .

Ten moduł jest w stanie ustawić środowisko konfiguracyjne na podstawie nazwy twojego komputera. Następnie, gdy zażądasz konfiguracji, otrzymasz wartość specyficzną dla środowiska.

Na przykład załóżmy, że masz dwie maszyny programistyczne o nazwach pc1 i pc2 oraz maszynę produkcyjną o nazwie pc3. Kiedykolwiek zażądasz wartości konfiguracyjnych w swoim kodzie na pc1 lub pc2, musisz uzyskać konfigurację środowiska "programistycznego", aw pc3 musisz otrzymać konfigurację środowiska "produkcyjnego". Można to osiągnąć w następujący sposób:

  1. Utwórz podstawowy plik konfiguracyjny w katalogu konfiguracyjnym, powiedzmy „app.json” i dodaj do niego wymagane konfiguracje.
  2. Teraz po prostu utwórz foldery w katalogu konfiguracyjnym, które odpowiadają nazwie twojego środowiska, w tym przypadku „programowanie” i „produkcja”.
  3. Następnie utwórz pliki konfiguracyjne, które chcesz przesłonić, i określ opcje dla każdego środowiska w katalogach środowiska (zwróć uwagę, że nie musisz określać wszystkich opcji znajdujących się w podstawowym pliku konfiguracyjnym, a jedynie opcje, które chcesz przesłonić. pliki konfiguracyjne środowiska będą „kaskadowe” na pliki podstawowe).

Teraz utwórz nową instancję konfiguracji z następującą składnią.

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

   }
);

Teraz możesz uzyskać dowolną wartość konfiguracyjną bez martwienia się o środowisko w następujący sposób:

config.get('app').configurationKey;
Harish Anchu
źródło
0

Ta odpowiedź nie jest czymś nowym. Jest podobny do tego, o czym wspomniał @andy_t. Ale używam poniższego wzoru z dwóch powodów.

  1. Czysta implementacja bez zewnętrznych zależności npm

  2. Scal domyślne ustawienia konfiguracji z ustawieniami opartymi na środowisku.

Implementacja 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] }

Zwykle używam maszynopisu w moim projekcie węzła. Poniżej jest wklejona kopia mojej rzeczywistej implementacji.

Implementacja maszynopisu

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
źródło