Node.js设置环境特定配置以与everyauth一起使用

127

我正在使用node.js + express.js + everyauth.js。我已经将所有的everyauth逻辑移动到一个模块文件中。

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

在这个中,我使用键/密钥组合加载我的oAuth配置文件:

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

这些代码针对不同的环境 - 开发/预演/生产,因为回调指向不同的URL。

问题:我该如何在环境配置中设置它们以过滤所有模块?还是可以直接将路径传递到模块中吗?

在环境中设置:

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

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

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

传入参数

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

? 希望这样说会更清楚


找到了一个解决方案,它使用了下面的一些思路,通过将模块设置为函数而不是对象,我可以评估 process.env.NODE_ENV 并返回适合该环境的正确对象。有点混乱但有效。 - andy t
请原谅我这个不要脸的自我宣传,但我为 node.js 写了一个模块,可以通过单独文件和命令行开关来完成这个任务: node-configure - Randolpho
9个回答

202

我的解决方案,

使用以下方式加载应用程序

NODE_ENV=production node app.js

config.js设置为函数而不是对象

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

        case 'production':
            return {prod settings};

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

根据Jans的解决方案,加载文件并创建一个新实例,如果需要可以传入一个值,在这种情况下,process.env.NODE_ENV是全局的,因此不需要。

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

然后我们可以像以前一样访问配置对象属性

conf.twitter.consumerKey

2
你为什么在这里使用new? - bluehallu
5
我同意 @bluehallu 的观点。"new" 这个词必要吗? - mc9
2
在Windows中的等效命令是SET NODE_ENV=development。 - mujaffars
3
config.js中,我不使用new,而是采用以下方式…… Config = function(){...}; module.exports = Config() - Atu
如果我有50个Web服务器,那么手动启动脚本将会很困难,该怎么办? - Rajesh

63

你还可以使用NODE_ENV作为顶层,将配置设置存储在JSON文件中。我认为这是表达配置设置的更好方式(而不是使用返回设置的脚本)。

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

env.json示例:

{
    "development": {
        "MONGO_URI": "mongodb://localhost/test",
        "MONGO_OPTIONS": { "db": { "safe": true } }
    },
    "production": {
        "MONGO_URI": "mongodb://localhost/production",
        "MONGO_OPTIONS": { "db": { "safe": true } }
    }
}

你好,能否请您解释一下为什么您认为这种表达配置设置的方式更好(而不是使用返回设置的脚本)? - Venkat Kotra
14
我猜这并没有太大的区别。在我的心里,当我看到JSON时,我会想到“静态数据”,而当我看到JS文件时,我会认为其中有一些逻辑。此外,使用“.json”文件类型的另一个好处是其他语言也可以导入同一文件。 - mattwad
2
@VenkatKotra 配置通常被认为是静态的,因此最好使用诸如json、yaml、ini等声明性语言来表达。如果用脚本来实现状态的生成,这意味着某种“动态”的情况正在发生,这将是不好的。 - max
12
请注意,这种方法会将凭据暴露在源代码控制中。 - Pier-Luc Gendreau
我可以为测试和生产环境创建不同的URL吗? - Alex
很遗憾,这不起作用。请参见:https://stackoverflow.com/questions/46755852/nodejs-development-and-production-environment-config-file-loading - Kristjan O.

39

一个非常有用的解决方案是使用配置模块

安装该模块后:

$ npm install config

你可以创建一个名为 default.json 的配置文件。(你可以使用扩展名为 .json5 的 JSON 或 JS 对象)。
例如:
$ vi config/default.json

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

这个默认配置可以通过环境配置文件或本地开发环境的本地配置文件进行覆盖: production.json 可能是:
{
  "configPath": "/my/production/path",
  "port": 8080
}

development.json 可能是:

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

在您的本地PC上,您可以拥有一个名为local.json的文件,该文件将覆盖所有环境,或者您可以拥有特定的本地配置,例如local-production.jsonlocal-development.json

加载顺序列表的完整版。

在您的应用程序内部

在您的应用程序中,您只需要要求配置和所需的属性即可。

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

加载应用程序

使用以下方法加载应用程序:

NODE_ENV=production node app.js

或者使用 foreverpm2 来设置正确的环境。

Forever:

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

PM2(通过shell):

export NODE_ENV=staging
pm2 start app.js

PM2(通过 .json 文件):

process.json

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

然后

$ pm2 start process.json --env production

这个解决方案非常干净,可以轻松地为生产/暂存/开发环境以及本地设置设置不同的配置文件。

npm install config --save,这样不是更好吗? - stackdave

21

简述

这种设置简单而优雅:

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

在生产环境中运行: $ NODE_ENV=production node app.js


详细说明

此解决方案来自:http://himanshu.gilani.info/blog/2012/09/26/bootstraping-a-node-dot-js-app-for-dev-slash-prod-environment/,更多细节请查看。


5
我们实现这个的方法是在启动应用程序时使用环境参数传递参数。例如:
node app.js -c dev

在 app.js 中,我们加载 dev.js 作为我们的配置文件。您可以使用 optparse-js 解析这些选项。
现在您有一些核心模块依赖于此配置文件。当您将它们写成这样时:
var Workspace = module.exports = function(config) {
    if (config) {
         // do something;
    }
}

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

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

您可以在app.js中调用它,如下所示:

var Workspace = require("workspace");
this.workspace = new Workspace(config);

我宁愿将所有逻辑保留在app.js的“app.configure('development')”代码中,但会查看是否可以使用此解决方案。 - andy t
更新此答案:Architect 是一个依赖管理框架,以更好的方式解决了这个问题。 - Jan Jongboom

5

一种优雅的方法是使用.env文件以本地方式覆盖生产设置。无需命令行开关。无需在config.json文件中使用那些逗号和括号。 请查看我的答案

例如:在我的电脑上,.env文件内容如下:

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

我的本地.env文件会覆盖任何环境变量。但是在演示或生产服务器上(可能在heroku.com上),环境变量预设为stage NODE_ENV=stage或production NODE_ENV=prod


5

在部署服务器中设置环境变量(例如:像NODE_ENV=production这样)。您可以通过process.env.NODE_ENV访问您的环境变量。寻找全局设置的以下配置文件。

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 : 'some@user.com',
    },
    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" 包含所有环境共用的配置。

然后可以在其他模块中导入它,例如:

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

愉快编码...


这个文件包含敏感信息,因此应该被 Git 忽略,对吗?如果要在部署过程中包含这个文件,应该怎么做? - The.Wolfgang.Grimmer
这是一个非常好的问题,在这种情况下,您可以将特定于环境的文件存储在各自的服务器上。然后在部署时,只需从本地服务器拉取配置即可。 - ajaykumar mp

3
如何使用nodejs-config模块以更加优雅的方式完成此操作呢?这个模块能够根据您计算机的名称设置配置环境。之后,当您请求配置时,您将获得特定于该环境的值。
例如,假设您有两台名为pc1和pc2的开发机器和一台名为pc3的生产机器。每当您在pc1或pc2中的代码中请求配置值时,您必须获取“开发”环境配置,在pc3中,您必须获取“生产”环境配置。可以像这样实现:
1. 在config目录中创建一个基本的配置文件,比如“app.json”,并添加所需的配置。 2. 现在只需在config目录中创建与您的环境名称匹配的文件夹,例如“development”和“production”。 3. 接下来,在环境目录中创建您希望覆盖的配置文件,并为每个环境指定选项(请注意,您不必指定基本配置文件中的每个选项,而仅需覆盖您希望覆盖的选项。环境配置文件将在基本文件上“级联”。)。
现在,使用以下语法创建新的config实例。
var config = require('nodejs-config')(
   __dirname,  // an absolute path to your applications 'config' directory
   {
      development: ["pc1", "pc2"],
      production: ["pc3"],

   }
);

现在你可以轻松地获取任何配置值,而不必担心环境,方法如下:

config.get('app').configurationKey;

0

这个答案并不是什么新鲜事。它类似于 @andy_t 提到的内容。但我使用下面的模式有两个原因:

  1. 干净的实现,没有外部npm依赖

  2. 将默认配置设置与基于环境的设置合并。

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://dev59.com/MHVC5IYBdhLWcg3w1E1q#171256
module.exports = { ...settings._default, ...settings[process.env.NODE_ENV] }

我通常在我的node项目中使用typescript。以下是我实际的实现复制粘贴。

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)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接