config.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. const convict = require('convict');
  2. const { tmpdir } = require('os');
  3. const path = require('path');
  4. const { randomBytes } = require('crypto');
  5. convict.addFormat({
  6. name: 'positive-int-array',
  7. coerce: ints => {
  8. // can take: int[] | string[] | string (csv), returns -> int[]
  9. const ints_arr = Array.isArray(ints) ? ints : ints.trim().split(',');
  10. return ints_arr.map(int =>
  11. typeof int === 'number'
  12. ? int
  13. : parseInt(int.replace(/['"]+/g, '').trim(), 10)
  14. );
  15. },
  16. validate: ints => {
  17. // takes: int[], errors if any NaNs, negatives, or floats present
  18. for (const int of ints) {
  19. if (typeof int !== 'number' || isNaN(int) || int < 0 || int % 1 > 0)
  20. throw new Error('must be a comma-separated list of positive integers');
  21. }
  22. }
  23. });
  24. const conf = convict({
  25. s3_bucket: {
  26. format: String,
  27. default: '',
  28. env: 'S3_BUCKET'
  29. },
  30. s3_endpoint: {
  31. format: String,
  32. default: '',
  33. env: 'S3_ENDPOINT'
  34. },
  35. s3_use_path_style_endpoint: {
  36. format: Boolean,
  37. default: false,
  38. env: 'S3_USE_PATH_STYLE_ENDPOINT'
  39. },
  40. gcs_bucket: {
  41. format: String,
  42. default: '',
  43. env: 'GCS_BUCKET'
  44. },
  45. expire_times_seconds: {
  46. format: 'positive-int-array',
  47. default: [300, 3600, 86400, 604800],
  48. env: 'EXPIRE_TIMES_SECONDS'
  49. },
  50. default_expire_seconds: {
  51. format: Number,
  52. default: 86400,
  53. env: 'DEFAULT_EXPIRE_SECONDS'
  54. },
  55. max_expire_seconds: {
  56. format: Number,
  57. default: 86400 * 7,
  58. env: 'MAX_EXPIRE_SECONDS'
  59. },
  60. download_counts: {
  61. format: 'positive-int-array',
  62. default: [1, 2, 3, 4, 5, 20, 50, 100],
  63. env: 'DOWNLOAD_COUNTS'
  64. },
  65. default_downloads: {
  66. format: Number,
  67. default: 1,
  68. env: 'DEFAULT_DOWNLOADS'
  69. },
  70. max_downloads: {
  71. format: Number,
  72. default: 100,
  73. env: 'MAX_DOWNLOADS'
  74. },
  75. max_files_per_archive: {
  76. format: Number,
  77. default: 64,
  78. env: 'MAX_FILES_PER_ARCHIVE'
  79. },
  80. max_archives_per_user: {
  81. format: Number,
  82. default: 16,
  83. env: 'MAX_ARCHIVES_PER_USER'
  84. },
  85. redis_host: {
  86. format: String,
  87. default: 'localhost',
  88. env: 'REDIS_HOST'
  89. },
  90. redis_port: {
  91. format: Number,
  92. default: 6379,
  93. env: 'REDIS_PORT'
  94. },
  95. redis_user: {
  96. format: String,
  97. default: '',
  98. env: 'REDIS_USER'
  99. },
  100. redis_password: {
  101. format: String,
  102. default: '',
  103. env: 'REDIS_PASSWORD'
  104. },
  105. redis_db: {
  106. format: String,
  107. default: '',
  108. env: 'REDIS_DB'
  109. },
  110. redis_event_expire: {
  111. format: Boolean,
  112. default: false,
  113. env: 'REDIS_EVENT_EXPIRE'
  114. },
  115. redis_retry_time: {
  116. format: Number,
  117. default: 10000,
  118. env: 'REDIS_RETRY_TIME'
  119. },
  120. redis_retry_delay: {
  121. format: Number,
  122. default: 500,
  123. env: 'REDIS_RETRY_DELAY'
  124. },
  125. listen_address: {
  126. format: 'ipaddress',
  127. default: '0.0.0.0',
  128. env: 'IP_ADDRESS'
  129. },
  130. listen_port: {
  131. format: 'port',
  132. default: 1443,
  133. arg: 'port',
  134. env: 'PORT'
  135. },
  136. sentry_id: {
  137. format: String,
  138. default: '',
  139. env: 'SENTRY_CLIENT'
  140. },
  141. sentry_dsn: {
  142. format: String,
  143. default: '',
  144. env: 'SENTRY_DSN'
  145. },
  146. env: {
  147. format: ['production', 'development', 'test'],
  148. default: 'development',
  149. env: 'NODE_ENV'
  150. },
  151. max_file_size: {
  152. format: Number,
  153. default: 1024 * 1024 * 1024 * 2.5,
  154. env: 'MAX_FILE_SIZE'
  155. },
  156. l10n_dev: {
  157. format: Boolean,
  158. default: false,
  159. env: 'L10N_DEV'
  160. },
  161. base_url: {
  162. format: 'url',
  163. default: 'https://send.firefox.com',
  164. env: 'BASE_URL'
  165. },
  166. detect_base_url: {
  167. format: Boolean,
  168. default: false,
  169. env: 'DETECT_BASE_URL'
  170. },
  171. file_dir: {
  172. format: 'String',
  173. default: `${tmpdir()}${path.sep}send-${randomBytes(4).toString('hex')}`,
  174. env: 'FILE_DIR'
  175. },
  176. fxa_url: {
  177. format: 'url',
  178. default: 'https://send-fxa.dev.lcip.org',
  179. env: 'FXA_URL'
  180. },
  181. fxa_client_id: {
  182. format: String,
  183. default: '', // disabled
  184. env: 'FXA_CLIENT_ID'
  185. },
  186. fxa_key_scope: {
  187. format: String,
  188. default: 'https://identity.mozilla.com/apps/send',
  189. env: 'FXA_KEY_SCOPE'
  190. },
  191. fxa_csp_oauth_url: {
  192. format: String,
  193. default: '',
  194. env: 'FXA_CSP_OAUTH_URL'
  195. },
  196. fxa_csp_content_url: {
  197. format: String,
  198. default: '',
  199. env: 'FXA_CSP_CONTENT_URL'
  200. },
  201. fxa_csp_profile_url: {
  202. format: String,
  203. default: '',
  204. env: 'FXA_CSP_PROFILE_URL'
  205. },
  206. fxa_csp_profileimage_url: {
  207. format: String,
  208. default: '',
  209. env: 'FXA_CSP_PROFILEIMAGE_URL'
  210. },
  211. survey_url: {
  212. format: String,
  213. default: '',
  214. env: 'SURVEY_URL'
  215. },
  216. ip_db: {
  217. format: String,
  218. default: '',
  219. env: 'IP_DB'
  220. },
  221. footer_donate_url: {
  222. format: String,
  223. default: '',
  224. env: 'SEND_FOOTER_DONATE_URL'
  225. },
  226. footer_cli_url: {
  227. format: String,
  228. default: 'https://github.com/timvisee/ffsend',
  229. env: 'SEND_FOOTER_CLI_URL'
  230. },
  231. footer_dmca_url: {
  232. format: String,
  233. default: '',
  234. env: 'SEND_FOOTER_DMCA_URL'
  235. },
  236. footer_source_url: {
  237. format: String,
  238. default: 'https://github.com/timvisee/send',
  239. env: 'SEND_FOOTER_SOURCE_URL'
  240. }
  241. });
  242. // Perform validation
  243. conf.validate({ allowed: 'strict' });
  244. const props = conf.getProperties();
  245. const deriveBaseUrl = req => {
  246. if (!props.detect_base_url) {
  247. return props.base_url;
  248. }
  249. const protocol = req.secure ? 'https://' : 'http://';
  250. return `${protocol}${req.headers.host}`;
  251. };
  252. module.exports = {
  253. ...props,
  254. deriveBaseUrl
  255. };