config-loader.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. const debug = require('debug')('growi:service:ConfigLoader');
  2. const TYPES = {
  3. NUMBER: { parse: (v) => { return parseInt(v, 10) } },
  4. STRING: { parse: (v) => { return v } },
  5. BOOLEAN: { parse: (v) => { return /^(true|1)$/i.test(v) } },
  6. };
  7. /**
  8. * The following env vars are excluded because these are currently used before the configuration setup.
  9. * - MONGO_URI
  10. * - NODE_ENV
  11. * - PORT
  12. * - REDIS_URI
  13. * - SESSION_NAME
  14. * - PASSWORD_SEED
  15. * - SECRET_TOKEN
  16. *
  17. * The commented out item has not yet entered the migration work.
  18. * So, parameters of these are under consideration.
  19. */
  20. const ENV_VAR_NAME_TO_CONFIG_INFO = {
  21. // ELASTICSEARCH_URI: {
  22. // ns: ,
  23. // key: ,
  24. // type: ,
  25. // default:
  26. // },
  27. // FILE_UPLOAD: {
  28. // ns: ,
  29. // key: ,
  30. // type: ,
  31. // default:
  32. // },
  33. // HACKMD_URI: {
  34. // ns: ,
  35. // key: ,
  36. // type: ,
  37. // default:
  38. // },
  39. // HACKMD_URI_FOR_SERVER: {
  40. // ns: ,
  41. // key: ,
  42. // type: ,
  43. // default:
  44. // },
  45. // PLANTUML_URI: {
  46. // ns: ,
  47. // key: ,
  48. // type: ,
  49. // default:
  50. // },
  51. // BLOCKDIAG_URI: {
  52. // ns: ,
  53. // key: ,
  54. // type: ,
  55. // default:
  56. // },
  57. // OAUTH_GOOGLE_CLIENT_ID: {
  58. // ns: 'crowi',
  59. // key: 'security:passport-google:clientId',
  60. // type: ,
  61. // default:
  62. // },
  63. // OAUTH_GOOGLE_CLIENT_SECRET: {
  64. // ns: 'crowi',
  65. // key: 'security:passport-google:clientSecret',
  66. // type: ,
  67. // default:
  68. // },
  69. // OAUTH_GOOGLE_CALLBACK_URI: {
  70. // ns: 'crowi',
  71. // key: 'security:passport-google:callbackUrl',
  72. // type: ,
  73. // default:
  74. // },
  75. // OAUTH_GITHUB_CLIENT_ID: {
  76. // ns: 'crowi',
  77. // key: 'security:passport-github:clientId',
  78. // type: ,
  79. // default:
  80. // },
  81. // OAUTH_GITHUB_CLIENT_SECRET: {
  82. // ns: 'crowi',
  83. // key: 'security:passport-github:clientSecret',
  84. // type: ,
  85. // default:
  86. // },
  87. // OAUTH_GITHUB_CALLBACK_URI: {
  88. // ns: 'crowi',
  89. // key: 'security:passport-github:callbackUrl',
  90. // type: ,
  91. // default:
  92. // },
  93. // OAUTH_TWITTER_CONSUMER_KEY: {
  94. // ns: 'crowi',
  95. // key: 'security:passport-twitter:consumerKey',
  96. // type: ,
  97. // default:
  98. // },
  99. // OAUTH_TWITTER_CONSUMER_SECRET: {
  100. // ns: 'crowi',
  101. // key: 'security:passport-twitter:consumerSecret',
  102. // type: ,
  103. // default:
  104. // },
  105. // OAUTH_TWITTER_CALLBACK_URI: {
  106. // ns: 'crowi',
  107. // key: 'security:passport-twitter:callbackUrl',
  108. // type: ,
  109. // default:
  110. // },
  111. APP_SITE_URL: {
  112. ns: 'crowi',
  113. key: 'app:siteUrl',
  114. type: TYPES.STRING,
  115. default: null,
  116. },
  117. PUBLISH_OPEN_API: {
  118. ns: 'crowi',
  119. key: 'app:publishOpenAPI',
  120. type: TYPES.BOOLEAN,
  121. default: false,
  122. },
  123. MAX_FILE_SIZE: {
  124. ns: 'crowi',
  125. key: 'app:maxFileSize',
  126. type: TYPES.NUMBER,
  127. default: Infinity,
  128. },
  129. MONGO_GRIDFS_TOTAL_LIMIT: {
  130. ns: 'crowi',
  131. key: 'gridfs:totalLimit',
  132. type: TYPES.NUMBER,
  133. default: Infinity,
  134. },
  135. SAML_USES_ONLY_ENV_VARS_FOR_SOME_OPTIONS: {
  136. ns: 'crowi',
  137. key: 'security:passport-saml:useOnlyEnvVarsForSomeOptions',
  138. type: TYPES.BOOLEAN,
  139. default: false,
  140. },
  141. SAML_ENABLED: {
  142. ns: 'crowi',
  143. key: 'security:passport-saml:isEnabled',
  144. type: TYPES.BOOLEAN,
  145. default: null,
  146. },
  147. SAML_ENTRY_POINT: {
  148. ns: 'crowi',
  149. key: 'security:passport-saml:entryPoint',
  150. type: TYPES.STRING,
  151. default: null,
  152. },
  153. SAML_CALLBACK_URI: {
  154. ns: 'crowi',
  155. key: 'security:passport-saml:callbackUrl',
  156. type: TYPES.STRING,
  157. default: null,
  158. },
  159. SAML_ISSUER: {
  160. ns: 'crowi',
  161. key: 'security:passport-saml:issuer',
  162. type: TYPES.STRING,
  163. default: null,
  164. },
  165. SAML_ATTR_MAPPING_ID: {
  166. ns: 'crowi',
  167. key: 'security:passport-saml:attrMapId',
  168. type: TYPES.STRING,
  169. default: null,
  170. },
  171. SAML_ATTR_MAPPING_USERNAME: {
  172. ns: 'crowi',
  173. key: 'security:passport-saml:attrMapUsername',
  174. type: TYPES.STRING,
  175. default: null,
  176. },
  177. SAML_ATTR_MAPPING_MAIL: {
  178. ns: 'crowi',
  179. key: 'security:passport-saml:attrMapMail',
  180. type: TYPES.STRING,
  181. default: null,
  182. },
  183. SAML_ATTR_MAPPING_FIRST_NAME: {
  184. ns: 'crowi',
  185. key: 'security:passport-saml:attrMapFirstName',
  186. type: TYPES.STRING,
  187. default: null,
  188. },
  189. SAML_ATTR_MAPPING_LAST_NAME: {
  190. ns: 'crowi',
  191. key: 'security:passport-saml:attrMapLastName',
  192. type: TYPES.STRING,
  193. default: null,
  194. },
  195. SAML_CERT: {
  196. ns: 'crowi',
  197. key: 'security:passport-saml:cert',
  198. type: TYPES.STRING,
  199. default: null,
  200. },
  201. DELETE_COMPLETELY: {
  202. ns: 'crowi',
  203. key: 'security:isEnabledDeleteCompletely',
  204. type: TYPES.STRING,
  205. default: false,
  206. },
  207. };
  208. class ConfigLoader {
  209. constructor(configModel) {
  210. this.configModel = configModel;
  211. }
  212. /**
  213. * return a config object
  214. */
  215. async load() {
  216. const configFromDB = await this.loadFromDB();
  217. const configFromEnvVars = this.loadFromEnvVars();
  218. // merge defaults
  219. let mergedConfigFromDB = Object.assign({ crowi: this.configModel.getDefaultCrowiConfigsObject() }, configFromDB);
  220. mergedConfigFromDB = Object.assign({ markdown: this.configModel.getDefaultMarkdownConfigsObject() }, mergedConfigFromDB);
  221. // In getConfig API, only null is used as a value to indicate that a config is not set.
  222. // So, if a value loaded from the database is emtpy,
  223. // it is converted to null because an empty string is used as the same meaning in the config model.
  224. // By this processing, whether a value is loaded from the database or from the environment variable,
  225. // only null indicates a config is not set.
  226. for (const namespace of Object.keys(mergedConfigFromDB)) {
  227. for (const key of Object.keys(mergedConfigFromDB[namespace])) {
  228. if (mergedConfigFromDB[namespace][key] === '') {
  229. mergedConfigFromDB[namespace][key] = null;
  230. }
  231. }
  232. }
  233. return {
  234. fromDB: mergedConfigFromDB,
  235. fromEnvVars: configFromEnvVars,
  236. };
  237. }
  238. async loadFromDB() {
  239. const config = {};
  240. const docs = await this.configModel.find().exec();
  241. for (const doc of docs) {
  242. if (!config[doc.ns]) {
  243. config[doc.ns] = {};
  244. }
  245. config[doc.ns][doc.key] = JSON.parse(doc.value);
  246. }
  247. debug('ConfigLoader#loadFromDB', config);
  248. return config;
  249. }
  250. loadFromEnvVars() {
  251. const config = {};
  252. for (const ENV_VAR_NAME of Object.keys(ENV_VAR_NAME_TO_CONFIG_INFO)) {
  253. const configInfo = ENV_VAR_NAME_TO_CONFIG_INFO[ENV_VAR_NAME];
  254. if (config[configInfo.ns] === undefined) {
  255. config[configInfo.ns] = {};
  256. }
  257. if (process.env[ENV_VAR_NAME] === undefined) {
  258. config[configInfo.ns][configInfo.key] = configInfo.default;
  259. }
  260. else {
  261. config[configInfo.ns][configInfo.key] = configInfo.type.parse(process.env[ENV_VAR_NAME]);
  262. }
  263. }
  264. debug('ConfigLoader#loadFromEnvVars', config);
  265. return config;
  266. }
  267. }
  268. module.exports = ConfigLoader;