connection-parameters.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. 'use strict'
  2. var dns = require('dns')
  3. var defaults = require('./defaults')
  4. var parse = require('pg-connection-string').parse // parses a connection string
  5. var val = function (key, config, envVar) {
  6. if (envVar === undefined) {
  7. envVar = process.env['PG' + key.toUpperCase()]
  8. } else if (envVar === false) {
  9. // do nothing ... use false
  10. } else {
  11. envVar = process.env[envVar]
  12. }
  13. return config[key] || envVar || defaults[key]
  14. }
  15. var readSSLConfigFromEnvironment = function () {
  16. switch (process.env.PGSSLMODE) {
  17. case 'disable':
  18. return false
  19. case 'prefer':
  20. case 'require':
  21. case 'verify-ca':
  22. case 'verify-full':
  23. return true
  24. case 'no-verify':
  25. return { rejectUnauthorized: false }
  26. }
  27. return defaults.ssl
  28. }
  29. // Convert arg to a string, surround in single quotes, and escape single quotes and backslashes
  30. var quoteParamValue = function (value) {
  31. return "'" + ('' + value).replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'"
  32. }
  33. var add = function (params, config, paramName) {
  34. var value = config[paramName]
  35. if (value !== undefined && value !== null) {
  36. params.push(paramName + '=' + quoteParamValue(value))
  37. }
  38. }
  39. class ConnectionParameters {
  40. constructor(config) {
  41. // if a string is passed, it is a raw connection string so we parse it into a config
  42. config = typeof config === 'string' ? parse(config) : config || {}
  43. // if the config has a connectionString defined, parse IT into the config we use
  44. // this will override other default values with what is stored in connectionString
  45. if (config.connectionString) {
  46. config = Object.assign({}, config, parse(config.connectionString))
  47. }
  48. this.user = val('user', config)
  49. this.database = val('database', config)
  50. if (this.database === undefined) {
  51. this.database = this.user
  52. }
  53. this.port = parseInt(val('port', config), 10)
  54. this.host = val('host', config)
  55. // "hiding" the password so it doesn't show up in stack traces
  56. // or if the client is console.logged
  57. Object.defineProperty(this, 'password', {
  58. configurable: true,
  59. enumerable: false,
  60. writable: true,
  61. value: val('password', config),
  62. })
  63. this.binary = val('binary', config)
  64. this.options = val('options', config)
  65. this.ssl = typeof config.ssl === 'undefined' ? readSSLConfigFromEnvironment() : config.ssl
  66. if (typeof this.ssl === 'string') {
  67. if (this.ssl === 'true') {
  68. this.ssl = true
  69. }
  70. }
  71. // support passing in ssl=no-verify via connection string
  72. if (this.ssl === 'no-verify') {
  73. this.ssl = { rejectUnauthorized: false }
  74. }
  75. if (this.ssl && this.ssl.key) {
  76. Object.defineProperty(this.ssl, 'key', {
  77. enumerable: false,
  78. })
  79. }
  80. this.client_encoding = val('client_encoding', config)
  81. this.replication = val('replication', config)
  82. // a domain socket begins with '/'
  83. this.isDomainSocket = !(this.host || '').indexOf('/')
  84. this.application_name = val('application_name', config, 'PGAPPNAME')
  85. this.fallback_application_name = val('fallback_application_name', config, false)
  86. this.statement_timeout = val('statement_timeout', config, false)
  87. this.idle_in_transaction_session_timeout = val('idle_in_transaction_session_timeout', config, false)
  88. this.query_timeout = val('query_timeout', config, false)
  89. if (config.connectionTimeoutMillis === undefined) {
  90. this.connect_timeout = process.env.PGCONNECT_TIMEOUT || 0
  91. } else {
  92. this.connect_timeout = Math.floor(config.connectionTimeoutMillis / 1000)
  93. }
  94. if (config.keepAlive === false) {
  95. this.keepalives = 0
  96. } else if (config.keepAlive === true) {
  97. this.keepalives = 1
  98. }
  99. if (typeof config.keepAliveInitialDelayMillis === 'number') {
  100. this.keepalives_idle = Math.floor(config.keepAliveInitialDelayMillis / 1000)
  101. }
  102. }
  103. getLibpqConnectionString(cb) {
  104. var params = []
  105. add(params, this, 'user')
  106. add(params, this, 'password')
  107. add(params, this, 'port')
  108. add(params, this, 'application_name')
  109. add(params, this, 'fallback_application_name')
  110. add(params, this, 'connect_timeout')
  111. add(params, this, 'options')
  112. var ssl = typeof this.ssl === 'object' ? this.ssl : this.ssl ? { sslmode: this.ssl } : {}
  113. add(params, ssl, 'sslmode')
  114. add(params, ssl, 'sslca')
  115. add(params, ssl, 'sslkey')
  116. add(params, ssl, 'sslcert')
  117. add(params, ssl, 'sslrootcert')
  118. if (this.database) {
  119. params.push('dbname=' + quoteParamValue(this.database))
  120. }
  121. if (this.replication) {
  122. params.push('replication=' + quoteParamValue(this.replication))
  123. }
  124. if (this.host) {
  125. params.push('host=' + quoteParamValue(this.host))
  126. }
  127. if (this.isDomainSocket) {
  128. return cb(null, params.join(' '))
  129. }
  130. if (this.client_encoding) {
  131. params.push('client_encoding=' + quoteParamValue(this.client_encoding))
  132. }
  133. dns.lookup(this.host, function (err, address) {
  134. if (err) return cb(err, null)
  135. params.push('hostaddr=' + quoteParamValue(address))
  136. return cb(null, params.join(' '))
  137. })
  138. }
  139. }
  140. module.exports = ConnectionParameters