config-loader.ts 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. import { envUtils } from '@growi/core';
  2. import { parseISO } from 'date-fns';
  3. import loggerFactory from '~/utils/logger';
  4. import ConfigModel, {
  5. Config, defaultCrowiConfigs, defaultMarkdownConfigs, defaultNotificationConfigs,
  6. } from '../models/config';
  7. const logger = loggerFactory('growi:service:ConfigLoader');
  8. enum ValueType { NUMBER, STRING, BOOLEAN, DATE }
  9. interface ValueParser<T> {
  10. parse(value: string): T;
  11. }
  12. interface EnvConfig {
  13. ns: string,
  14. key: string,
  15. type: ValueType,
  16. default?: number | string | boolean | null,
  17. }
  18. type EnumDictionary<T extends string | symbol | number, U> = {
  19. [K in T]: U;
  20. };
  21. const parserDictionary: EnumDictionary<ValueType, ValueParser<number | string | boolean | Date>> = {
  22. [ValueType.NUMBER]: { parse: (v: string) => { return parseInt(v, 10) } },
  23. [ValueType.STRING]: { parse: (v: string) => { return v } },
  24. [ValueType.BOOLEAN]: { parse: (v: string) => { return envUtils.toBoolean(v) } },
  25. [ValueType.DATE]: { parse: (v: string) => { return parseISO(v) } },
  26. };
  27. /**
  28. * The following env vars are excluded because these are currently used before the configuration setup.
  29. * - MONGO_URI
  30. * - NODE_ENV
  31. * - PORT
  32. * - REDIS_URI
  33. * - SESSION_NAME
  34. * - PASSWORD_SEED
  35. * - SECRET_TOKEN
  36. *
  37. * The commented out item has not yet entered the migration work.
  38. * So, parameters of these are under consideration.
  39. */
  40. const ENV_VAR_NAME_TO_CONFIG_INFO = {
  41. FILE_UPLOAD: {
  42. ns: 'crowi',
  43. key: 'app:fileUploadType',
  44. type: ValueType.STRING,
  45. default: 'aws',
  46. },
  47. FILE_UPLOAD_USES_ONLY_ENV_VAR_FOR_FILE_UPLOAD_TYPE: {
  48. ns: 'crowi',
  49. key: 'app:useOnlyEnvVarForFileUploadType',
  50. type: ValueType.BOOLEAN,
  51. default: false,
  52. },
  53. HACKMD_URI: {
  54. ns: 'crowi',
  55. key: 'app:hackmdUri',
  56. type: ValueType.STRING,
  57. default: null,
  58. },
  59. HACKMD_URI_FOR_SERVER: {
  60. ns: 'crowi',
  61. key: 'app:hackmdUriForServer',
  62. type: ValueType.STRING,
  63. default: null,
  64. },
  65. MATHJAX: {
  66. ns: 'crowi',
  67. key: 'app:mathJax',
  68. type: ValueType.STRING,
  69. default: null,
  70. },
  71. NO_CDN: {
  72. ns: 'crowi',
  73. key: 'app:noCdn',
  74. type: ValueType.STRING,
  75. default: null,
  76. },
  77. // PLANTUML_URI: {
  78. // ns: ,
  79. // key: ,
  80. // type: ,
  81. // default:
  82. // },
  83. // BLOCKDIAG_URI: {
  84. // ns: ,
  85. // key: ,
  86. // type: ,
  87. // default:
  88. // },
  89. // OAUTH_GOOGLE_CLIENT_ID: {
  90. // ns: 'crowi',
  91. // key: 'security:passport-google:clientId',
  92. // type: ,
  93. // default:
  94. // },
  95. // OAUTH_GOOGLE_CLIENT_SECRET: {
  96. // ns: 'crowi',
  97. // key: 'security:passport-google:clientSecret',
  98. // type: ,
  99. // default:
  100. // },
  101. // OAUTH_GOOGLE_CALLBACK_URI: {
  102. // ns: 'crowi',
  103. // key: 'security:passport-google:callbackUrl',
  104. // type: ,
  105. // default:
  106. // },
  107. // OAUTH_GITHUB_CLIENT_ID: {
  108. // ns: 'crowi',
  109. // key: 'security:passport-github:clientId',
  110. // type: ,
  111. // default:
  112. // },
  113. // OAUTH_GITHUB_CLIENT_SECRET: {
  114. // ns: 'crowi',
  115. // key: 'security:passport-github:clientSecret',
  116. // type: ,
  117. // default:
  118. // },
  119. // OAUTH_GITHUB_CALLBACK_URI: {
  120. // ns: 'crowi',
  121. // key: 'security:passport-github:callbackUrl',
  122. // type: ,
  123. // default:
  124. // },
  125. // OAUTH_TWITTER_CONSUMER_KEY: {
  126. // ns: 'crowi',
  127. // key: 'security:passport-twitter:consumerKey',
  128. // type: ,
  129. // default:
  130. // },
  131. // OAUTH_TWITTER_CONSUMER_SECRET: {
  132. // ns: 'crowi',
  133. // key: 'security:passport-twitter:consumerSecret',
  134. // type: ,
  135. // default:
  136. // },
  137. // OAUTH_TWITTER_CALLBACK_URI: {
  138. // ns: 'crowi',
  139. // key: 'security:passport-twitter:callbackUrl',
  140. // type: ,
  141. // default:
  142. // },
  143. DRAWIO_URI: {
  144. ns: 'crowi',
  145. key: 'app:drawioUri',
  146. type: ValueType.STRING,
  147. default: 'https://embed.diagrams.net/',
  148. },
  149. NCHAN_URI: {
  150. ns: 'crowi',
  151. key: 'app:nchanUri',
  152. type: ValueType.STRING,
  153. default: null,
  154. },
  155. APP_SITE_URL: {
  156. ns: 'crowi',
  157. key: 'app:siteUrl',
  158. type: ValueType.STRING,
  159. default: null,
  160. },
  161. PUBLISH_OPEN_API: {
  162. ns: 'crowi',
  163. key: 'app:publishOpenAPI',
  164. type: ValueType.BOOLEAN,
  165. default: false,
  166. },
  167. IS_V5_COMPATIBLE: {
  168. ns: 'crowi',
  169. key: 'app:isV5Compatible',
  170. type: ValueType.BOOLEAN,
  171. default: undefined,
  172. },
  173. IS_MAINTENANCE_MODE: {
  174. ns: 'crowi',
  175. key: 'app:isMaintenanceMode',
  176. type: ValueType.BOOLEAN,
  177. default: false,
  178. },
  179. AUTO_INSTALL_ADMIN_USERNAME: {
  180. ns: 'crowi',
  181. key: 'autoInstall:adminUsername',
  182. type: ValueType.STRING,
  183. default: null,
  184. },
  185. AUTO_INSTALL_ADMIN_NAME: {
  186. ns: 'crowi',
  187. key: 'autoInstall:adminName',
  188. type: ValueType.STRING,
  189. default: null,
  190. },
  191. AUTO_INSTALL_ADMIN_EMAIL: {
  192. ns: 'crowi',
  193. key: 'autoInstall:adminEmail',
  194. type: ValueType.STRING,
  195. default: null,
  196. },
  197. AUTO_INSTALL_ADMIN_PASSWORD: {
  198. ns: 'crowi',
  199. key: 'autoInstall:adminPassword',
  200. type: ValueType.STRING,
  201. default: null,
  202. },
  203. AUTO_INSTALL_GLOBAL_LANG: {
  204. ns: 'crowi',
  205. key: 'autoInstall:globalLang',
  206. type: ValueType.STRING,
  207. default: null,
  208. },
  209. AUTO_INSTALL_ALLOW_GUEST_MODE: {
  210. ns: 'crowi',
  211. key: 'autoInstall:allowGuestMode',
  212. type: ValueType.BOOLEAN,
  213. default: false,
  214. },
  215. AUTO_INSTALL_SERVER_DATE: {
  216. ns: 'crowi',
  217. key: 'autoInstall:serverDate',
  218. type: ValueType.DATE,
  219. default: null,
  220. },
  221. S2SMSG_PUBSUB_SERVER_TYPE: {
  222. ns: 'crowi',
  223. key: 's2sMessagingPubsub:serverType',
  224. type: ValueType.STRING,
  225. default: null,
  226. },
  227. S2SMSG_PUBSUB_NCHAN_PUBLISH_PATH: {
  228. ns: 'crowi',
  229. key: 's2sMessagingPubsub:nchan:publishPath',
  230. type: ValueType.STRING,
  231. default: '/pubsub',
  232. },
  233. S2SMSG_PUBSUB_NCHAN_SUBSCRIBE_PATH: {
  234. ns: 'crowi',
  235. key: 's2sMessagingPubsub:nchan:subscribePath',
  236. type: ValueType.STRING,
  237. default: '/pubsub',
  238. },
  239. S2SMSG_PUBSUB_NCHAN_CHANNEL_ID: {
  240. ns: 'crowi',
  241. key: 's2sMessagingPubsub:nchan:channelId',
  242. type: ValueType.STRING,
  243. default: null,
  244. },
  245. S2CMSG_PUBSUB_CONNECTIONS_LIMIT: {
  246. ns: 'crowi',
  247. key: 's2cMessagingPubsub:connectionsLimit',
  248. type: ValueType.NUMBER,
  249. default: 5000,
  250. },
  251. S2CMSG_PUBSUB_CONNECTIONS_LIMIT_FOR_ADMIN: {
  252. ns: 'crowi',
  253. key: 's2cMessagingPubsub:connectionsLimitForAdmin',
  254. type: ValueType.NUMBER,
  255. default: 100,
  256. },
  257. S2CMSG_PUBSUB_CONNECTIONS_LIMIT_FOR_GUEST: {
  258. ns: 'crowi',
  259. key: 's2cMessagingPubsub:connectionsLimitForGuest',
  260. type: ValueType.NUMBER,
  261. default: 2000,
  262. },
  263. MAX_FILE_SIZE: {
  264. ns: 'crowi',
  265. key: 'app:maxFileSize',
  266. type: ValueType.NUMBER,
  267. default: Infinity,
  268. },
  269. FILE_UPLOAD_TOTAL_LIMIT: {
  270. ns: 'crowi',
  271. key: 'app:fileUploadTotalLimit',
  272. type: ValueType.NUMBER,
  273. default: Infinity,
  274. },
  275. FILE_UPLOAD_DISABLED: {
  276. ns: 'crowi',
  277. key: 'app:fileUploadDisabled',
  278. type: ValueType.BOOLEAN,
  279. default: false,
  280. },
  281. FILE_UPLOAD_LOCAL_USE_INTERNAL_REDIRECT: {
  282. ns: 'crowi',
  283. key: 'fileUpload:local:useInternalRedirect',
  284. type: ValueType.BOOLEAN,
  285. default: false,
  286. },
  287. FILE_UPLOAD_LOCAL_INTERNAL_REDIRECT_PATH: {
  288. ns: 'crowi',
  289. key: 'fileUpload:local:internalRedirectPath',
  290. type: ValueType.STRING,
  291. default: '/growi-internal/',
  292. },
  293. ELASTICSEARCH_VERSION: {
  294. ns: 'crowi',
  295. key: 'app:elasticsearchVersion',
  296. type: ValueType.NUMBER,
  297. default: 7,
  298. },
  299. ELASTICSEARCH_URI: {
  300. ns: 'crowi',
  301. key: 'app:elasticsearchUri',
  302. type: ValueType.STRING,
  303. default: null,
  304. },
  305. ELASTICSEARCH_REQUEST_TIMEOUT: {
  306. ns: 'crowi',
  307. key: 'app:elasticsearchRequestTimeout',
  308. type: ValueType.NUMBER,
  309. default: 8000, // msec
  310. },
  311. ELASTICSEARCH_REJECT_UNAUTHORIZED: {
  312. ns: 'crowi',
  313. key: 'app:elasticsearchRejectUnauthorized',
  314. type: ValueType.BOOLEAN,
  315. default: false,
  316. },
  317. ELASTICSEARCH_REINDEX_ON_BOOT: {
  318. ns: 'crowi',
  319. key: 'app:elasticsearchReindexOnBoot',
  320. type: ValueType.BOOLEAN,
  321. default: false,
  322. },
  323. MONGO_GRIDFS_TOTAL_LIMIT: {
  324. ns: 'crowi',
  325. key: 'gridfs:totalLimit',
  326. type: ValueType.NUMBER,
  327. default: null, // set null in default for backward compatibility
  328. // cz: Newer system respects FILE_UPLOAD_TOTAL_LIMIT.
  329. // If the default value of MONGO_GRIDFS_TOTAL_LIMIT is Infinity,
  330. // the system can't distinguish between "not specified" and "Infinity is specified".
  331. },
  332. FORCE_WIKI_MODE: {
  333. ns: 'crowi',
  334. key: 'security:wikiMode',
  335. type: ValueType.STRING,
  336. default: undefined,
  337. },
  338. SESSION_MAX_AGE: {
  339. ns: 'crowi',
  340. key: 'security:sessionMaxAge',
  341. type: ValueType.NUMBER,
  342. default: undefined,
  343. },
  344. USER_UPPER_LIMIT: {
  345. ns: 'crowi',
  346. key: 'security:userUpperLimit',
  347. type: ValueType.NUMBER,
  348. default: Infinity,
  349. },
  350. DISABLE_LINK_SHARING: {
  351. ns: 'crowi',
  352. key: 'security:disableSharing',
  353. type: ValueType.BOOLEAN,
  354. default: false,
  355. },
  356. LOCAL_STRATEGY_ENABLED: {
  357. ns: 'crowi',
  358. key: 'security:passport-local:isEnabled',
  359. type: ValueType.BOOLEAN,
  360. default: true,
  361. },
  362. LOCAL_STRATEGY_USES_ONLY_ENV_VARS_FOR_SOME_OPTIONS: {
  363. ns: 'crowi',
  364. key: 'security:passport-local:useOnlyEnvVarsForSomeOptions',
  365. type: ValueType.BOOLEAN,
  366. default: false,
  367. },
  368. LOCAL_STRATEGY_PASSWORD_RESET_ENABLED: {
  369. ns: 'crowi',
  370. key: 'security:passport-local:isPasswordResetEnabled',
  371. type: ValueType.BOOLEAN,
  372. default: true,
  373. },
  374. LOCAL_STRATEGY_EMAIL_AUTHENTICATION_ENABLED: {
  375. ns: 'crowi',
  376. key: 'security:passport-local:isEmailAuthenticationEnabled',
  377. type: ValueType.BOOLEAN,
  378. default: false,
  379. },
  380. SAML_USES_ONLY_ENV_VARS_FOR_SOME_OPTIONS: {
  381. ns: 'crowi',
  382. key: 'security:passport-saml:useOnlyEnvVarsForSomeOptions',
  383. type: ValueType.BOOLEAN,
  384. default: false,
  385. },
  386. SAML_ENABLED: {
  387. ns: 'crowi',
  388. key: 'security:passport-saml:isEnabled',
  389. type: ValueType.BOOLEAN,
  390. default: null,
  391. },
  392. SAML_ENTRY_POINT: {
  393. ns: 'crowi',
  394. key: 'security:passport-saml:entryPoint',
  395. type: ValueType.STRING,
  396. default: null,
  397. },
  398. SAML_CALLBACK_URI: {
  399. ns: 'crowi',
  400. key: 'security:passport-saml:callbackUrl',
  401. type: ValueType.STRING,
  402. default: null,
  403. },
  404. SAML_ISSUER: {
  405. ns: 'crowi',
  406. key: 'security:passport-saml:issuer',
  407. type: ValueType.STRING,
  408. default: null,
  409. },
  410. SAML_ATTR_MAPPING_ID: {
  411. ns: 'crowi',
  412. key: 'security:passport-saml:attrMapId',
  413. type: ValueType.STRING,
  414. default: null,
  415. },
  416. SAML_ATTR_MAPPING_USERNAME: {
  417. ns: 'crowi',
  418. key: 'security:passport-saml:attrMapUsername',
  419. type: ValueType.STRING,
  420. default: null,
  421. },
  422. SAML_ATTR_MAPPING_MAIL: {
  423. ns: 'crowi',
  424. key: 'security:passport-saml:attrMapMail',
  425. type: ValueType.STRING,
  426. default: null,
  427. },
  428. SAML_ATTR_MAPPING_FIRST_NAME: {
  429. ns: 'crowi',
  430. key: 'security:passport-saml:attrMapFirstName',
  431. type: ValueType.STRING,
  432. default: null,
  433. },
  434. SAML_ATTR_MAPPING_LAST_NAME: {
  435. ns: 'crowi',
  436. key: 'security:passport-saml:attrMapLastName',
  437. type: ValueType.STRING,
  438. default: null,
  439. },
  440. SAML_CERT: {
  441. ns: 'crowi',
  442. key: 'security:passport-saml:cert',
  443. type: ValueType.STRING,
  444. default: null,
  445. },
  446. SAML_ABLC_RULE: {
  447. ns: 'crowi',
  448. key: 'security:passport-saml:ABLCRule',
  449. type: ValueType.STRING,
  450. default: null,
  451. },
  452. OIDC_TIMEOUT_MULTIPLIER: {
  453. ns: 'crowi',
  454. key: 'security:passport-oidc:timeoutMultiplier',
  455. type: ValueType.NUMBER,
  456. default: 1.5,
  457. },
  458. OIDC_DISCOVERY_RETRIES: {
  459. ns: 'crowi',
  460. key: 'security:passport-oidc:discoveryRetries',
  461. type: ValueType.NUMBER,
  462. default: 3,
  463. },
  464. OIDC_CLIENT_CLOCK_TOLERANCE: {
  465. ns: 'crowi',
  466. key: 'security:passport-oidc:oidcClientClockTolerance',
  467. type: ValueType.NUMBER,
  468. default: 60,
  469. },
  470. OIDC_ISSUER_TIMEOUT_OPTION: {
  471. ns: 'crowi',
  472. key: 'security:passport-oidc:oidcIssuerTimeoutOption',
  473. type: ValueType.NUMBER,
  474. default: 5000,
  475. },
  476. S3_REFERENCE_FILE_WITH_RELAY_MODE: {
  477. ns: 'crowi',
  478. key: 'aws:referenceFileWithRelayMode',
  479. type: ValueType.BOOLEAN,
  480. default: false,
  481. },
  482. S3_LIFETIME_SEC_FOR_TEMPORARY_URL: {
  483. ns: 'crowi',
  484. key: 'aws:lifetimeSecForTemporaryUrl',
  485. type: ValueType.NUMBER,
  486. default: 120,
  487. },
  488. GCS_API_KEY_JSON_PATH: {
  489. ns: 'crowi',
  490. key: 'gcs:apiKeyJsonPath',
  491. type: ValueType.STRING,
  492. default: null,
  493. },
  494. GCS_BUCKET: {
  495. ns: 'crowi',
  496. key: 'gcs:bucket',
  497. type: ValueType.STRING,
  498. default: null,
  499. },
  500. GCS_UPLOAD_NAMESPACE: {
  501. ns: 'crowi',
  502. key: 'gcs:uploadNamespace',
  503. type: ValueType.STRING,
  504. default: null,
  505. },
  506. GCS_REFERENCE_FILE_WITH_RELAY_MODE: {
  507. ns: 'crowi',
  508. key: 'gcs:referenceFileWithRelayMode',
  509. type: ValueType.BOOLEAN,
  510. default: false,
  511. },
  512. GCS_USES_ONLY_ENV_VARS_FOR_SOME_OPTIONS: {
  513. ns: 'crowi',
  514. key: 'gcs:useOnlyEnvVarsForSomeOptions',
  515. type: ValueType.BOOLEAN,
  516. default: false,
  517. },
  518. GCS_LIFETIME_SEC_FOR_TEMPORARY_URL: {
  519. ns: 'crowi',
  520. key: 'gcs:lifetimeSecForTemporaryUrl',
  521. type: ValueType.NUMBER,
  522. default: 120,
  523. },
  524. PROMSTER_ENABLED: {
  525. ns: 'crowi',
  526. key: 'promster:isEnabled',
  527. type: ValueType.BOOLEAN,
  528. default: false,
  529. },
  530. PROMSTER_PORT: {
  531. ns: 'crowi',
  532. key: 'promster:port',
  533. type: ValueType.NUMBER,
  534. default: 7788,
  535. },
  536. GROWI_CLOUD_URI: {
  537. ns: 'crowi',
  538. key: 'app:growiCloudUri',
  539. type: ValueType.STRING,
  540. default: null,
  541. },
  542. GROWI_APP_ID_FOR_GROWI_CLOUD: {
  543. ns: 'crowi',
  544. key: 'app:growiAppIdForCloud',
  545. type: ValueType.STRING,
  546. default: null,
  547. },
  548. DEFAULT_EMAIL_PUBLISHED: {
  549. ns: 'crowi',
  550. key: 'customize:isEmailPublishedForNewUser',
  551. type: ValueType.BOOLEAN,
  552. default: true,
  553. },
  554. SLACKBOT_TYPE: {
  555. ns: 'crowi',
  556. key: 'slackbot:currentBotType', // enum SlackbotType
  557. type: ValueType.STRING,
  558. default: null,
  559. },
  560. SLACKBOT_INTEGRATION_PROXY_URI: {
  561. ns: 'crowi',
  562. key: 'slackbot:proxyUri',
  563. type: ValueType.STRING,
  564. default: null,
  565. },
  566. SLACKBOT_WITHOUT_PROXY_SIGNING_SECRET: {
  567. ns: 'crowi',
  568. key: 'slackbot:withoutProxy:signingSecret',
  569. type: ValueType.STRING,
  570. default: null,
  571. },
  572. SLACKBOT_WITHOUT_PROXY_BOT_TOKEN: {
  573. ns: 'crowi',
  574. key: 'slackbot:withoutProxy:botToken',
  575. type: ValueType.STRING,
  576. default: null,
  577. },
  578. SLACKBOT_WITHOUT_PROXY_COMMAND_PERMISSION: {
  579. ns: 'crowi',
  580. key: 'slackbot:withoutProxy:commandPermission',
  581. type: ValueType.STRING,
  582. default: null,
  583. },
  584. SLACKBOT_WITHOUT_PROXY_EVENT_ACTIONS_PERMISSION: {
  585. ns: 'crowi',
  586. key: 'slackbot:withoutProxy:eventActionsPermission',
  587. type: ValueType.STRING,
  588. default: null,
  589. },
  590. SLACKBOT_WITH_PROXY_SALT_FOR_GTOP: {
  591. ns: 'crowi',
  592. key: 'slackbot:withProxy:saltForGtoP',
  593. type: ValueType.STRING,
  594. default: 'gtop',
  595. },
  596. SLACKBOT_WITH_PROXY_SALT_FOR_PTOG: {
  597. ns: 'crowi',
  598. key: 'slackbot:withProxy:saltForPtoG',
  599. type: ValueType.STRING,
  600. default: 'ptog',
  601. },
  602. OGP_URI: {
  603. ns: 'crowi',
  604. key: 'app:ogpUri',
  605. type: ValueType.STRING,
  606. default: null,
  607. },
  608. MIN_PASSWORD_LENGTH: {
  609. ns: 'crowi',
  610. key: 'app:minPasswordLength',
  611. type: ValueType.NUMBER,
  612. default: 8,
  613. },
  614. ACTIVITY_EXPIRATION_SECONDS: {
  615. ns: 'crowi',
  616. key: 'app:activityExpirationSeconds',
  617. type: ValueType.NUMBER,
  618. default: 2592000, // 30 days
  619. },
  620. };
  621. /**
  622. * return whether env belongs to Security settings
  623. * @param key ex. 'security:passport-saml:isEnabled' is true
  624. * @returns
  625. */
  626. const isSecurityEnv = (key) => {
  627. const array = key.split(':');
  628. return (array[0] === 'security');
  629. };
  630. export interface ConfigObject extends Record<string, any> {
  631. fromDB: any,
  632. fromEnvVars: any,
  633. }
  634. export default class ConfigLoader {
  635. /**
  636. * return a config object
  637. */
  638. async load(): Promise<ConfigObject> {
  639. const configFromDB: any = await this.loadFromDB();
  640. const configFromEnvVars: any = this.loadFromEnvVars();
  641. // merge defaults per ns
  642. const mergedConfigFromDB = {
  643. crowi: Object.assign(defaultCrowiConfigs, configFromDB.crowi),
  644. markdown: Object.assign(defaultMarkdownConfigs, configFromDB.markdown),
  645. notification: Object.assign(defaultNotificationConfigs, configFromDB.notification),
  646. };
  647. // In getConfig API, only null is used as a value to indicate that a config is not set.
  648. // So, if a value loaded from the database is empty,
  649. // it is converted to null because an empty string is used as the same meaning in the config model.
  650. // By this processing, whether a value is loaded from the database or from the environment variable,
  651. // only null indicates a config is not set.
  652. for (const namespace of Object.keys(mergedConfigFromDB)) {
  653. for (const key of Object.keys(mergedConfigFromDB[namespace])) {
  654. if (mergedConfigFromDB[namespace][key] === '') {
  655. mergedConfigFromDB[namespace][key] = null;
  656. }
  657. }
  658. }
  659. return {
  660. fromDB: mergedConfigFromDB,
  661. fromEnvVars: configFromEnvVars,
  662. };
  663. }
  664. async loadFromDB(): Promise<any> {
  665. const config = {};
  666. const docs: Config[] = await ConfigModel.find().exec();
  667. for (const doc of docs) {
  668. if (!config[doc.ns]) {
  669. config[doc.ns] = {};
  670. }
  671. config[doc.ns][doc.key] = doc.value ? JSON.parse(doc.value) : null;
  672. }
  673. logger.debug('ConfigLoader#loadFromDB', config);
  674. return config;
  675. }
  676. loadFromEnvVars(): any {
  677. const config = {};
  678. for (const ENV_VAR_NAME of Object.keys(ENV_VAR_NAME_TO_CONFIG_INFO)) {
  679. const configInfo = ENV_VAR_NAME_TO_CONFIG_INFO[ENV_VAR_NAME];
  680. if (config[configInfo.ns] === undefined) {
  681. config[configInfo.ns] = {};
  682. }
  683. if (process.env[ENV_VAR_NAME] === undefined) {
  684. config[configInfo.ns][configInfo.key] = configInfo.default;
  685. }
  686. else {
  687. const parser: ValueParser<number | string | boolean> = parserDictionary[configInfo.type];
  688. config[configInfo.ns][configInfo.key] = parser.parse(process.env[ENV_VAR_NAME] as string);
  689. }
  690. }
  691. logger.debug('ConfigLoader#loadFromEnvVars', config);
  692. return config;
  693. }
  694. /**
  695. * get config from the environment variables for display admin page
  696. *
  697. * **use this only admin home page.**
  698. */
  699. static getEnvVarsForDisplay(avoidSecurity = false): any {
  700. const config = {};
  701. for (const ENV_VAR_NAME of Object.keys(ENV_VAR_NAME_TO_CONFIG_INFO)) {
  702. const configInfo = ENV_VAR_NAME_TO_CONFIG_INFO[ENV_VAR_NAME];
  703. if (process.env[ENV_VAR_NAME] === undefined) {
  704. continue;
  705. }
  706. if (isSecurityEnv(configInfo.key) && avoidSecurity) {
  707. continue;
  708. }
  709. const parser: ValueParser<number | string | boolean> = parserDictionary[configInfo.type];
  710. config[ENV_VAR_NAME] = parser.parse(process.env[ENV_VAR_NAME] as string);
  711. }
  712. logger.debug('ConfigLoader#getEnvVarsForDisplay', config);
  713. return config;
  714. }
  715. }