config-loader.ts 22 KB

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