helper.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. const fs = require('fs')
  2. const merge = require('lodash.merge')
  3. const stripIndent = require('common-tags/lib/stripIndent')
  4. const utils = require('../server/helpers/utils')
  5. const logger = require('../server/logger')
  6. const crypto = require('crypto')
  7. // @ts-ignore
  8. const { version } = require('../../package.json')
  9. /**
  10. * Reads all companion configuration set via environment variables
  11. * and via the config file path
  12. *
  13. * @returns {object}
  14. */
  15. exports.getCompanionOptions = (options = {}) => {
  16. return merge({}, getConfigFromEnv(), getConfigFromFile(), options)
  17. }
  18. /**
  19. * Loads the config from environment variables
  20. *
  21. * @returns {object}
  22. */
  23. const getConfigFromEnv = () => {
  24. const uploadUrls = process.env.COMPANION_UPLOAD_URLS
  25. const domains = process.env.COMPANION_DOMAINS || process.env.COMPANION_DOMAIN || null
  26. const validHosts = domains ? domains.split(',') : []
  27. return {
  28. providerOptions: {
  29. drive: {
  30. key: process.env.COMPANION_GOOGLE_KEY,
  31. secret: getSecret('COMPANION_GOOGLE_SECRET')
  32. },
  33. dropbox: {
  34. key: process.env.COMPANION_DROPBOX_KEY,
  35. secret: getSecret('COMPANION_DROPBOX_SECRET')
  36. },
  37. box: {
  38. key: process.env.COMPANION_BOX_KEY,
  39. secret: getSecret('COMPANION_BOX_SECRET')
  40. },
  41. instagram: {
  42. key: process.env.COMPANION_INSTAGRAM_KEY,
  43. secret: getSecret('COMPANION_INSTAGRAM_SECRET')
  44. },
  45. facebook: {
  46. key: process.env.COMPANION_FACEBOOK_KEY,
  47. secret: getSecret('COMPANION_FACEBOOK_SECRET')
  48. },
  49. onedrive: {
  50. key: process.env.COMPANION_ONEDRIVE_KEY,
  51. secret: getSecret('COMPANION_ONEDRIVE_SECRET')
  52. },
  53. zoom: {
  54. key: process.env.COMPANION_ZOOM_KEY,
  55. secret: getSecret('COMPANION_ZOOM_SECRET'),
  56. verificationToken: getSecret('COMPANION_ZOOM_VERIFICATION_TOKEN')
  57. },
  58. searchProviders: {
  59. unsplash: {
  60. key: process.env.COMPANION_UNSPLASH_KEY
  61. }
  62. },
  63. s3: {
  64. key: process.env.COMPANION_AWS_KEY,
  65. secret: getSecret('COMPANION_AWS_SECRET'),
  66. bucket: process.env.COMPANION_AWS_BUCKET,
  67. endpoint: process.env.COMPANION_AWS_ENDPOINT,
  68. region: process.env.COMPANION_AWS_REGION,
  69. useAccelerateEndpoint:
  70. process.env.COMPANION_AWS_USE_ACCELERATE_ENDPOINT === 'true',
  71. expires: parseInt(process.env.COMPANION_AWS_EXPIRES || '300', 10),
  72. acl: process.env.COMPANION_AWS_ACL || 'public-read'
  73. }
  74. },
  75. server: {
  76. host: process.env.COMPANION_DOMAIN,
  77. protocol: process.env.COMPANION_PROTOCOL,
  78. path: process.env.COMPANION_PATH,
  79. implicitPath: process.env.COMPANION_IMPLICIT_PATH,
  80. oauthDomain: process.env.COMPANION_OAUTH_DOMAIN,
  81. validHosts: validHosts
  82. },
  83. filePath: process.env.COMPANION_DATADIR,
  84. redisUrl: process.env.COMPANION_REDIS_URL,
  85. // adding redisOptions to keep all companion options easily visible
  86. // redisOptions refers to https://www.npmjs.com/package/redis#options-object-properties
  87. redisOptions: {},
  88. sendSelfEndpoint: process.env.COMPANION_SELF_ENDPOINT,
  89. uploadUrls: uploadUrls ? uploadUrls.split(',') : null,
  90. secret: getSecret('COMPANION_SECRET') || generateSecret(),
  91. debug: process.env.NODE_ENV && process.env.NODE_ENV !== 'production',
  92. // TODO: this is a temporary hack to support distributed systems.
  93. // it is not documented, because it should be changed soon.
  94. cookieDomain: process.env.COMPANION_COOKIE_DOMAIN,
  95. multipleInstances: true
  96. }
  97. }
  98. /**
  99. * Tries to read the secret from a file if the according environment variable is set.
  100. * Otherwise it falls back to the standard secret environment variable.
  101. *
  102. * @param {string} baseEnvVar
  103. *
  104. * @returns {string}
  105. */
  106. const getSecret = (baseEnvVar) => {
  107. const secretFile = process.env[`${baseEnvVar}_FILE`]
  108. return secretFile
  109. ? fs.readFileSync(secretFile).toString()
  110. : process.env[baseEnvVar]
  111. }
  112. /**
  113. * Auto-generates server secret
  114. *
  115. * @returns {string}
  116. */
  117. const generateSecret = () => {
  118. logger.warn('auto-generating server secret because none was specified', 'startup.secret')
  119. return crypto.randomBytes(64).toString('hex')
  120. }
  121. /**
  122. * Loads the config from a file and returns it as an object
  123. *
  124. * @returns {object}
  125. */
  126. const getConfigFromFile = () => {
  127. const path = getConfigPath()
  128. if (!path) return {}
  129. const rawdata = fs.readFileSync(getConfigPath())
  130. // @ts-ignore
  131. return JSON.parse(rawdata)
  132. }
  133. /**
  134. * Returns the config path specified via cli arguments
  135. *
  136. * @returns {string}
  137. */
  138. const getConfigPath = () => {
  139. let configPath
  140. for (let i = process.argv.length - 1; i >= 0; i--) {
  141. const isConfigFlag = process.argv[i] === '-c' || process.argv[i] === '--config'
  142. const flagHasValue = i + 1 <= process.argv.length
  143. if (isConfigFlag && flagHasValue) {
  144. configPath = process.argv[i + 1]
  145. break
  146. }
  147. }
  148. return configPath
  149. }
  150. /**
  151. *
  152. * @param {string} url
  153. */
  154. exports.hasProtocol = (url) => {
  155. return url.startsWith('http://') || url.startsWith('https://')
  156. }
  157. exports.buildHelpfulStartupMessage = (companionOptions) => {
  158. const buildURL = utils.getURLBuilder(companionOptions)
  159. const callbackURLs = []
  160. Object.keys(companionOptions.providerOptions).forEach((providerName) => {
  161. // s3 does not need redirect_uris
  162. if (providerName === 's3') {
  163. return
  164. }
  165. callbackURLs.push(buildURL(`/connect/${providerName}/callback`, true))
  166. })
  167. return stripIndent`
  168. Welcome to Companion v${version}
  169. ===================================
  170. Congratulations on setting up Companion! Thanks for joining our cause, you have taken
  171. the first step towards the future of file uploading! We
  172. hope you are as excited about this as we are!
  173. While you did an awesome job on getting Companion running, this is just the welcome
  174. message, so let's talk about the places that really matter:
  175. - Be sure to add ${callbackURLs.join(', ')} as your Oauth redirect uris on their corresponding developer interfaces.
  176. - The URL ${buildURL('/metrics', true)} is available for statistics to keep Companion running smoothly
  177. - https://github.com/transloadit/uppy/issues - report your bugs here
  178. So quit lollygagging, start uploading and experience the future!
  179. `
  180. }