config-loader.ts 21 KB

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