app-settings.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. const loggerFactory = require('@alias/logger');
  2. const logger = loggerFactory('growi:routes:apiv3:app-settings');
  3. const debug = require('debug')('growi:routes:admin');
  4. const express = require('express');
  5. const router = express.Router();
  6. const { body } = require('express-validator/check');
  7. const ErrorV3 = require('../../models/vo/error-apiv3');
  8. /**
  9. * @swagger
  10. * tags:
  11. * name: AppSettings
  12. */
  13. /**
  14. * @swagger
  15. *
  16. * components:
  17. * schemas:
  18. * AppSettingParams:
  19. * description: AppSettingParams
  20. * type: object
  21. * properties:
  22. * title:
  23. * type: string
  24. * description: site name show on page header and tilte of HTML
  25. * confidential:
  26. * type: string
  27. * description: confidential show on page header
  28. * globalLang:
  29. * type: string
  30. * description: language set when create user
  31. * fileUpload:
  32. * type: boolean
  33. * description: enable upload file except image file
  34. * SiteUrlSettingParams:
  35. * description: SiteUrlSettingParams
  36. * type: object
  37. * properties:
  38. * siteUrl:
  39. * type: string
  40. * description: Site URL. e.g. https://example.com, https://example.com:8080
  41. * envSiteUrl:
  42. * type: string
  43. * description: environment variable 'APP_SITE_URL'
  44. * MailSettingParams:
  45. * description: MailSettingParams
  46. * type: object
  47. * properties:
  48. * fromAddress:
  49. * type: string
  50. * description: e-mail address used as from address of mail which sent from GROWI app
  51. * smtpHost:
  52. * type: string
  53. * description: host name of client's smtp server
  54. * smtpPort:
  55. * type: string
  56. * description: port of client's smtp server
  57. * smtpUser:
  58. * type: string
  59. * description: user name of client's smtp server
  60. * smtpPassword:
  61. * type: string
  62. * description: password of client's smtp server
  63. * AwsSettingParams:
  64. * description: AwsSettingParams
  65. * type: object
  66. * properties:
  67. * region:
  68. * type: string
  69. * description: region of AWS S3
  70. * customEndpoint:
  71. * type: string
  72. * description: custom endpoint of AWS S3
  73. * bucket:
  74. * type: string
  75. * description: AWS S3 bucket name
  76. * accessKeyId:
  77. * type: string
  78. * description: accesskey id for authentification of AWS
  79. * secretAccessKey:
  80. * type: string
  81. * description: secret key for authentification of AWS
  82. * PluginSettingParams:
  83. * description: PluginSettingParams
  84. * type: object
  85. * properties:
  86. * isEnabledPlugins:
  87. * type: string
  88. * description: enable use plugins
  89. */
  90. module.exports = (crowi) => {
  91. const accessTokenParser = require('../../middleware/access-token-parser')(crowi);
  92. const loginRequired = require('../../middleware/login-required')(crowi);
  93. const loginRequiredStrictly = require('../../middleware/login-required')(crowi);
  94. const adminRequired = require('../../middleware/admin-required')(crowi);
  95. const csrf = require('../../middleware/csrf')(crowi);
  96. const { ApiV3FormValidator } = crowi.middlewares;
  97. const validator = {
  98. appSetting: [
  99. body('title').trim(),
  100. body('confidential'),
  101. body('globalLang').isIn(['en-US', 'ja']),
  102. body('fileUpload').isBoolean(),
  103. ],
  104. siteUrlSetting: [
  105. body('siteUrl').trim().matches(/^(https?:\/\/[^/]+|)$/).isURL({ require_tld: false }),
  106. ],
  107. mailSetting: [
  108. body('fromAddress').trim().isEmail(),
  109. body('smtpHost').trim(),
  110. body('smtpPort').trim().isPort(),
  111. body('smtpUser').trim(),
  112. body('smtpPassword').trim(),
  113. ],
  114. awsSetting: [
  115. body('region').trim().matches(/^[a-z]+-[a-z]+-\d+$/).withMessage('リージョンには、AWSリージョン名を入力してください。 例: ap-northeast-1'),
  116. body('customEndpoint').trim().matches(/^(https?:\/\/[^/]+|)$/).withMessage('カスタムエンドポイントは、http(s)://で始まるURLを指定してください。また、末尾の/は不要です。'),
  117. body('bucket').trim(),
  118. body('accessKeyId').trim().matches(/^[\da-zA-Z]+$/),
  119. body('secretAccessKey').trim(),
  120. ],
  121. pluginSetting: [
  122. body('isEnabledPlugins').isBoolean(),
  123. ],
  124. };
  125. /**
  126. * @swagger
  127. *
  128. * /app-settings:
  129. * get:
  130. * tags: [AppSettings]
  131. * operationId: getAppSettings
  132. * summary: /app-settings
  133. * description: get app setting params
  134. * responses:
  135. * 200:
  136. * description: Resources are available
  137. * content:
  138. * application/json:
  139. * schema:
  140. * properties:
  141. * appSettingsParams:
  142. * type: object
  143. * description: app settings params
  144. */
  145. router.get('/', accessTokenParser, loginRequired, adminRequired, async(req, res) => {
  146. const appSettingsParams = {
  147. title: crowi.configManager.getConfig('crowi', 'app:title'),
  148. confidential: crowi.configManager.getConfig('crowi', 'app:confidential'),
  149. globalLang: crowi.configManager.getConfig('crowi', 'app:globalLang'),
  150. fileUpload: crowi.configManager.getConfig('crowi', 'app:fileUpload'),
  151. siteUrl: crowi.configManager.getConfig('crowi', 'app:siteUrl'),
  152. envSiteUrl: crowi.configManager.getConfigFromEnvVars('crowi', 'app:siteUrl'),
  153. fromAddress: crowi.configManager.getConfig('crowi', 'mail:from'),
  154. smtpHost: crowi.configManager.getConfig('crowi', 'mail:smtpHost'),
  155. smtpPort: crowi.configManager.getConfig('crowi', 'mail:smtpPort'),
  156. smtpUser: crowi.configManager.getConfig('crowi', 'mail:smtpUser'),
  157. smtpPassword: crowi.configManager.getConfig('crowi', 'mail:smtpPassword'),
  158. region: crowi.configManager.getConfig('crowi', 'aws:region'),
  159. customEndpoint: crowi.configManager.getConfig('crowi', 'aws:customEndpoint'),
  160. bucket: crowi.configManager.getConfig('crowi', 'aws:bucket'),
  161. accessKeyId: crowi.configManager.getConfig('crowi', 'aws:accessKeyId'),
  162. secretAccessKey: crowi.configManager.getConfig('crowi', 'aws:secretAccessKey'),
  163. isEnabledPlugins: crowi.configManager.getConfig('crowi', 'plugin:isEnabledPlugins'),
  164. };
  165. return res.apiv3({ appSettingsParams });
  166. });
  167. /**
  168. * @swagger
  169. *
  170. * /app-settings/app-setting:
  171. * put:
  172. * tags: [AppSettings]
  173. * summary: /app-settings/app-setting
  174. * operationId: updateAppSettings
  175. * description: Update app setting
  176. * requestBody:
  177. * required: true
  178. * content:
  179. * application/json:
  180. * schema:
  181. * $ref: '#/components/schemas/AppSettingParams'
  182. * responses:
  183. * 200:
  184. * description: Succeeded to update app setting
  185. * content:
  186. * application/json:
  187. * schema:
  188. * $ref: '#/components/schemas/AppSettingParams'
  189. */
  190. router.put('/app-setting', loginRequiredStrictly, adminRequired, csrf, validator.appSetting, ApiV3FormValidator, async(req, res) => {
  191. const requestAppSettingParams = {
  192. 'app:title': req.body.title,
  193. 'app:confidential': req.body.confidential,
  194. 'app:globalLang': req.body.globalLang,
  195. 'app:fileUpload': req.body.fileUpload,
  196. };
  197. try {
  198. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestAppSettingParams);
  199. const appSettingParams = {
  200. title: crowi.configManager.getConfig('crowi', 'app:title'),
  201. confidential: crowi.configManager.getConfig('crowi', 'app:confidential'),
  202. globalLang: crowi.configManager.getConfig('crowi', 'app:globalLang'),
  203. fileUpload: crowi.configManager.getConfig('crowi', 'app:fileUpload'),
  204. };
  205. return res.apiv3({ appSettingParams });
  206. }
  207. catch (err) {
  208. const msg = 'Error occurred in updating app setting';
  209. logger.error('Error', err);
  210. return res.apiv3Err(new ErrorV3(msg, 'update-appSetting-failed'));
  211. }
  212. });
  213. /**
  214. * @swagger
  215. *
  216. * /app-settings/site-url-setting:
  217. * put:
  218. * tags: [AppSettings]
  219. * operationId: updateAppSettingSiteUrlSetting
  220. * summary: /app-settings/site-url-setting
  221. * description: Update site url setting
  222. * requestBody:
  223. * required: true
  224. * content:
  225. * application/json:
  226. * schema:
  227. * $ref: '#/components/schemas/SiteUrlSettingParams'
  228. * responses:
  229. * 200:
  230. * description: Succeeded to update site url setting
  231. * content:
  232. * application/json:
  233. * schema:
  234. * $ref: '#/components/schemas/SiteUrlSettingParams'
  235. */
  236. router.put('/site-url-setting', loginRequiredStrictly, adminRequired, csrf, validator.siteUrlSetting, ApiV3FormValidator, async(req, res) => {
  237. const requestSiteUrlSettingParams = {
  238. 'app:siteUrl': req.body.siteUrl,
  239. };
  240. try {
  241. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestSiteUrlSettingParams);
  242. const siteUrlSettingParams = {
  243. siteUrl: crowi.configManager.getConfig('crowi', 'app:siteUrl'),
  244. };
  245. return res.apiv3({ siteUrlSettingParams });
  246. }
  247. catch (err) {
  248. const msg = 'Error occurred in updating site url setting';
  249. logger.error('Error', err);
  250. return res.apiv3Err(new ErrorV3(msg, 'update-siteUrlSetting-failed'));
  251. }
  252. });
  253. /**
  254. * send mail (Promise wrapper)
  255. */
  256. async function sendMailPromiseWrapper(smtpClient, options) {
  257. return new Promise((resolve, reject) => {
  258. smtpClient.sendMail(options, (err, res) => {
  259. if (err) {
  260. reject(err);
  261. }
  262. else {
  263. resolve(res);
  264. }
  265. });
  266. });
  267. }
  268. /**
  269. * validate mail setting send test mail
  270. */
  271. async function validateMailSetting(req) {
  272. const mailer = crowi.mailer;
  273. const option = {
  274. host: req.body.smtpHost,
  275. port: req.body.smtpPort,
  276. };
  277. if (req.body.smtpUser && req.body.smtpPassword) {
  278. option.auth = {
  279. user: req.body.smtpUser,
  280. pass: req.body.smtpPassword,
  281. };
  282. }
  283. if (option.port === 465) {
  284. option.secure = true;
  285. }
  286. const smtpClient = mailer.createSMTPClient(option);
  287. debug('mailer setup for validate SMTP setting', smtpClient);
  288. const mailOptions = {
  289. from: req.body.fromAddress,
  290. to: req.user.email,
  291. subject: 'Wiki管理設定のアップデートによるメール通知',
  292. text: 'このメールは、WikiのSMTP設定のアップデートにより送信されています。',
  293. };
  294. await sendMailPromiseWrapper(smtpClient, mailOptions);
  295. }
  296. /**
  297. * @swagger
  298. *
  299. * /app-settings/mail-setting:
  300. * put:
  301. * tags: [AppSettings]
  302. * operationId: updateAppSettingMailSetting
  303. * summary: /app-settings/site-url-setting
  304. * description: Update mail setting
  305. * requestBody:
  306. * required: true
  307. * content:
  308. * application/json:
  309. * schema:
  310. * $ref: '#/components/schemas/MailSettingParams'
  311. * responses:
  312. * 200:
  313. * description: Succeeded to update mail setting
  314. * content:
  315. * application/json:
  316. * schema:
  317. * $ref: '#/components/schemas/MailSettingParams'
  318. */
  319. router.put('/mail-setting', loginRequiredStrictly, adminRequired, csrf, validator.mailSetting, ApiV3FormValidator, async(req, res) => {
  320. // テストメール送信によるバリデート
  321. try {
  322. await validateMailSetting(req);
  323. }
  324. catch (err) {
  325. const msg = 'SMTPを利用したテストメール送信に失敗しました。設定をみなおしてください。';
  326. logger.error('Error', err);
  327. debug('Error validate mail setting: ', err);
  328. return res.apiv3Err(new ErrorV3(msg, 'update-mailSetting-failed'));
  329. }
  330. const requestMailSettingParams = {
  331. 'mail:from': req.body.fromAddress,
  332. 'mail:smtpHost': req.body.smtpHost,
  333. 'mail:smtpPort': req.body.smtpPort,
  334. 'mail:smtpUser': req.body.smtpUser,
  335. 'mail:smtpPassword': req.body.smtpPassword,
  336. };
  337. try {
  338. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestMailSettingParams);
  339. const mailSettingParams = {
  340. fromAddress: crowi.configManager.getConfig('crowi', 'mail:from'),
  341. smtpHost: crowi.configManager.getConfig('crowi', 'mail:smtpHost'),
  342. smtpPort: crowi.configManager.getConfig('crowi', 'mail:smtpPort'),
  343. smtpUser: crowi.configManager.getConfig('crowi', 'mail:smtpUser'),
  344. smtpPassword: crowi.configManager.getConfig('crowi', 'mail:smtpPassword'),
  345. };
  346. return res.apiv3({ mailSettingParams });
  347. }
  348. catch (err) {
  349. const msg = 'Error occurred in updating mail setting';
  350. logger.error('Error', err);
  351. return res.apiv3Err(new ErrorV3(msg, 'update-mailSetting-failed'));
  352. }
  353. });
  354. /**
  355. * @swagger
  356. *
  357. * /app-settings/aws-setting:
  358. * put:
  359. * tags: [AppSettings]
  360. * operationId: updateAppSettingAwsSetting
  361. * summary: /app-settings/aws-setting
  362. * description: Update aws setting
  363. * requestBody:
  364. * required: true
  365. * content:
  366. * application/json:
  367. * schema:
  368. * $ref: '#/components/schemas/AwsSettingParams'
  369. * responses:
  370. * 200:
  371. * description: Succeeded to update aws setting
  372. * content:
  373. * application/json:
  374. * schema:
  375. * $ref: '#/components/schemas/AwsSettingParams'
  376. */
  377. router.put('/aws-setting', loginRequiredStrictly, adminRequired, csrf, validator.awsSetting, ApiV3FormValidator, async(req, res) => {
  378. const requestAwsSettingParams = {
  379. 'aws:region': req.body.region,
  380. 'aws:customEndpoint': req.body.customEndpoint,
  381. 'aws:bucket': req.body.bucket,
  382. 'aws:accessKeyId': req.body.accessKeyId,
  383. 'aws:secretAccessKey': req.body.secretAccessKey,
  384. };
  385. try {
  386. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestAwsSettingParams);
  387. const awsSettingParams = {
  388. region: crowi.configManager.getConfig('crowi', 'aws:region'),
  389. customEndpoint: crowi.configManager.getConfig('crowi', 'aws:customEndpoint'),
  390. bucket: crowi.configManager.getConfig('crowi', 'aws:bucket'),
  391. accessKeyId: crowi.configManager.getConfig('crowi', 'aws:accessKeyId'),
  392. secretAccessKey: crowi.configManager.getConfig('crowi', 'aws:secretAccessKey'),
  393. };
  394. return res.apiv3({ awsSettingParams });
  395. }
  396. catch (err) {
  397. const msg = 'Error occurred in updating aws setting';
  398. logger.error('Error', err);
  399. return res.apiv3Err(new ErrorV3(msg, 'update-awsSetting-failed'));
  400. }
  401. });
  402. /**
  403. * @swagger
  404. *
  405. * /app-settings/plugin-setting:
  406. * put:
  407. * tags: [AppSettings]
  408. * operationId: updateAppSettingPluginSetting
  409. * summary: /app-settings/plugin-setting
  410. * description: Update plugin setting
  411. * requestBody:
  412. * required: true
  413. * content:
  414. * application/json:
  415. * schema:
  416. * $ref: '#/components/schemas/PluginSettingParams'
  417. * responses:
  418. * 200:
  419. * description: Succeeded to update plugin setting
  420. * content:
  421. * application/json:
  422. * schema:
  423. * $ref: '#/components/schemas/PluginSettingParams'
  424. */
  425. router.put('/plugin-setting', loginRequiredStrictly, adminRequired, csrf, validator.pluginSetting, ApiV3FormValidator, async(req, res) => {
  426. const requestPluginSettingParams = {
  427. 'plugin:isEnabledPlugins': req.body.isEnabledPlugins,
  428. };
  429. try {
  430. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestPluginSettingParams);
  431. const pluginSettingParams = {
  432. isEnabledPlugins: crowi.configManager.getConfig('crowi', 'plugin:isEnabledPlugins'),
  433. };
  434. return res.apiv3({ pluginSettingParams });
  435. }
  436. catch (err) {
  437. const msg = 'Error occurred in updating plugin setting';
  438. logger.error('Error', err);
  439. return res.apiv3Err(new ErrorV3(msg, 'update-pluginSetting-failed'));
  440. }
  441. });
  442. return router;
  443. };