customize-setting.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. /* eslint-disable no-unused-vars */
  2. const loggerFactory = require('@alias/logger');
  3. const logger = loggerFactory('growi:routes:apiv3:customize-setting');
  4. const express = require('express');
  5. const router = express.Router();
  6. const { body, query } = require('express-validator');
  7. const ErrorV3 = require('../../models/vo/error-apiv3');
  8. /**
  9. * @swagger
  10. * tags:
  11. * name: CustomizeSetting
  12. */
  13. /**
  14. * @swagger
  15. *
  16. * components:
  17. * schemas:
  18. * CustomizeLayout:
  19. * description: CustomizeLayout
  20. * type: object
  21. * properties:
  22. * isContainerFluid:
  23. * type: boolean
  24. * CustomizeTheme:
  25. * description: CustomizeTheme
  26. * type: object
  27. * properties:
  28. * themeType:
  29. * type: string
  30. * CustomizeFunction:
  31. * description: CustomizeFunction
  32. * type: object
  33. * properties:
  34. * isEnabledTimeline:
  35. * type: boolean
  36. * isSavedStatesOfTabChanges:
  37. * type: boolean
  38. * isEnabledAttachTitleHeader:
  39. * type: boolean
  40. * pageLimitationS:
  41. * type: number
  42. * pageLimitationM:
  43. * type: number
  44. * isEnabledStaleNotification:
  45. * type: boolean
  46. * isAllReplyShown:
  47. * type: boolean
  48. * CustomizeHighlight:
  49. * description: CustomizeHighlight
  50. * type: object
  51. * properties:
  52. * styleName:
  53. * type: string
  54. * styleBorder:
  55. * type: boolean
  56. * CustomizeTitle:
  57. * description: CustomizeTitle
  58. * type: object
  59. * properties:
  60. * customizeTitle:
  61. * type: string
  62. * CustomizeHeader:
  63. * description: CustomizeHeader
  64. * type: object
  65. * properties:
  66. * customizeHeader:
  67. * type: string
  68. * CustomizeCss:
  69. * description: CustomizeCss
  70. * type: object
  71. * properties:
  72. * customizeCss:
  73. * type: string
  74. * CustomizeScript:
  75. * description: CustomizeScript
  76. * type: object
  77. * properties:
  78. * customizeScript:
  79. * type: string
  80. */
  81. module.exports = (crowi) => {
  82. const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
  83. const adminRequired = require('../../middlewares/admin-required')(crowi);
  84. const csrf = require('../../middlewares/csrf')(crowi);
  85. const apiV3FormValidator = require('../../middlewares/apiv3-form-validator')(crowi);
  86. const { customizeService } = crowi;
  87. const validator = {
  88. layout: [
  89. body('isContainerFluid').isBoolean(),
  90. ],
  91. themeAssetPath: [
  92. query('themeName').isString(),
  93. ],
  94. theme: [
  95. body('themeType').isString(),
  96. ],
  97. function: [
  98. body('isEnabledTimeline').isBoolean(),
  99. body('isSavedStatesOfTabChanges').isBoolean(),
  100. body('isEnabledAttachTitleHeader').isBoolean(),
  101. body('pageLimitationS').isInt().isInt({ min: 1, max: 1000 }),
  102. body('pageLimitationM').isInt().isInt({ min: 1, max: 1000 }),
  103. body('pageLimitationL').isInt().isInt({ min: 1, max: 1000 }),
  104. body('pageLimitationXL').isInt().isInt({ min: 1, max: 1000 }),
  105. body('isEnabledStaleNotification').isBoolean(),
  106. body('isAllReplyShown').isBoolean(),
  107. ],
  108. customizeTitle: [
  109. body('customizeTitle').isString(),
  110. ],
  111. customizeHeader: [
  112. body('customizeHeader').isString(),
  113. ],
  114. highlight: [
  115. body('highlightJsStyle').isString().isIn([
  116. 'github', 'github-gist', 'atom-one-light', 'xcode', 'vs', 'atom-one-dark', 'hybrid', 'monokai', 'tomorrow-night', 'vs2015',
  117. ]),
  118. body('highlightJsStyleBorder').isBoolean(),
  119. ],
  120. customizeCss: [
  121. body('customizeCss').isString(),
  122. ],
  123. customizeScript: [
  124. body('customizeScript').isString(),
  125. ],
  126. };
  127. /**
  128. * @swagger
  129. *
  130. * /customize-setting:
  131. * get:
  132. * tags: [CustomizeSetting]
  133. * operationId: getCustomizeSetting
  134. * summary: /customize-setting
  135. * description: Get customize parameters
  136. * responses:
  137. * 200:
  138. * description: params of customize
  139. * content:
  140. * application/json:
  141. * schema:
  142. * properties:
  143. * customizeParams:
  144. * type: object
  145. * description: customize params
  146. */
  147. router.get('/', loginRequiredStrictly, adminRequired, async(req, res) => {
  148. const customizeParams = {
  149. themeType: await crowi.configManager.getConfig('crowi', 'customize:theme'),
  150. isEnabledTimeline: await crowi.configManager.getConfig('crowi', 'customize:isEnabledTimeline'),
  151. isSavedStatesOfTabChanges: await crowi.configManager.getConfig('crowi', 'customize:isSavedStatesOfTabChanges'),
  152. isEnabledAttachTitleHeader: await crowi.configManager.getConfig('crowi', 'customize:isEnabledAttachTitleHeader'),
  153. pageLimitationS: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS'),
  154. pageLimitationM: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationM'),
  155. pageLimitationL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'),
  156. pageLimitationXL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'),
  157. isEnabledStaleNotification: await crowi.configManager.getConfig('crowi', 'customize:isEnabledStaleNotification'),
  158. isAllReplyShown: await crowi.configManager.getConfig('crowi', 'customize:isAllReplyShown'),
  159. styleName: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyle'),
  160. styleBorder: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
  161. customizeTitle: await crowi.configManager.getConfig('crowi', 'customize:title'),
  162. customizeHeader: await crowi.configManager.getConfig('crowi', 'customize:header'),
  163. customizeCss: await crowi.configManager.getConfig('crowi', 'customize:css'),
  164. customizeScript: await crowi.configManager.getConfig('crowi', 'customize:script'),
  165. };
  166. return res.apiv3({ customizeParams });
  167. });
  168. /**
  169. * @swagger
  170. *
  171. * /customize-setting/layout:
  172. * get:
  173. * tags: [CustomizeSetting]
  174. * operationId: getLayoutCustomizeSetting
  175. * summary: /customize-setting/layout
  176. * description: Get layout
  177. * responses:
  178. * 200:
  179. * description: Succeeded to get layout
  180. * content:
  181. * application/json:
  182. * schema:
  183. * $ref: '#/components/schemas/CustomizeLayout'
  184. */
  185. router.get('/layout', loginRequiredStrictly, adminRequired, async(req, res) => {
  186. try {
  187. const isContainerFluid = await crowi.configManager.getConfig('crowi', 'customize:isContainerFluid');
  188. return res.apiv3({ isContainerFluid });
  189. }
  190. catch (err) {
  191. const msg = 'Error occurred in getting layout';
  192. logger.error('Error', err);
  193. return res.apiv3Err(new ErrorV3(msg, 'get-layout-failed'));
  194. }
  195. });
  196. /**
  197. * @swagger
  198. *
  199. * /customize-setting/layout:
  200. * put:
  201. * tags: [CustomizeSetting]
  202. * operationId: updateLayoutCustomizeSetting
  203. * summary: /customize-setting/layout
  204. * description: Update layout
  205. * requestBody:
  206. * required: true
  207. * content:
  208. * application/json:
  209. * schema:
  210. * $ref: '#/components/schemas/CustomizeLayout'
  211. * responses:
  212. * 200:
  213. * description: Succeeded to update layout
  214. * content:
  215. * application/json:
  216. * schema:
  217. * $ref: '#/components/schemas/CustomizeLayout'
  218. */
  219. router.put('/layout', loginRequiredStrictly, adminRequired, csrf, validator.layout, apiV3FormValidator, async(req, res) => {
  220. const requestParams = {
  221. 'customize:isContainerFluid': req.body.isContainerFluid,
  222. };
  223. try {
  224. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  225. const customizedParams = {
  226. isContainerFluid: await crowi.configManager.getConfig('crowi', 'customize:isContainerFluid'),
  227. };
  228. return res.apiv3({ customizedParams });
  229. }
  230. catch (err) {
  231. const msg = 'Error occurred in updating layout';
  232. logger.error('Error', err);
  233. return res.apiv3Err(new ErrorV3(msg, 'update-layout-failed'));
  234. }
  235. });
  236. /**
  237. * @swagger
  238. *
  239. * /customize-setting/theme/asset-path:
  240. * put:
  241. * tags: [CustomizeSetting]
  242. * operationId: getThemeAssetPath
  243. * summary: /customize-setting/theme/asset-path
  244. * description: Get theme asset path
  245. * parameters:
  246. * - name: themeName
  247. * in: query
  248. * required: true
  249. * schema:
  250. * type: string
  251. * responses:
  252. * 200:
  253. * description: Succeeded to get theme asset path
  254. * content:
  255. * application/json:
  256. * schema:
  257. * properties:
  258. * assetPath:
  259. * type: string
  260. */
  261. router.get('/theme/asset-path', loginRequiredStrictly, adminRequired, validator.themeAssetPath, apiV3FormValidator, async(req, res) => {
  262. const { themeName } = req.query;
  263. const webpackAssetKey = `styles/theme-${themeName}.css`;
  264. const assetPath = res.locals.webpack_asset(webpackAssetKey);
  265. if (assetPath == null) {
  266. return res.apiv3Err(new ErrorV3(`The asset for '${webpackAssetKey}' is undefined.`, 'invalid-asset'));
  267. }
  268. return res.apiv3({ assetPath });
  269. });
  270. /**
  271. * @swagger
  272. *
  273. * /customize-setting/theme:
  274. * put:
  275. * tags: [CustomizeSetting]
  276. * operationId: updateThemeCustomizeSetting
  277. * summary: /customize-setting/theme
  278. * description: Update theme
  279. * requestBody:
  280. * required: true
  281. * content:
  282. * application/json:
  283. * schema:
  284. * $ref: '#/components/schemas/CustomizeTheme'
  285. * responses:
  286. * 200:
  287. * description: Succeeded to update theme
  288. * content:
  289. * application/json:
  290. * schema:
  291. * $ref: '#/components/schemas/CustomizeTheme'
  292. */
  293. router.put('/theme', loginRequiredStrictly, adminRequired, csrf, validator.theme, apiV3FormValidator, async(req, res) => {
  294. const requestParams = {
  295. 'customize:theme': req.body.themeType,
  296. };
  297. try {
  298. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  299. const customizedParams = {
  300. themeType: await crowi.configManager.getConfig('crowi', 'customize:theme'),
  301. };
  302. return res.apiv3({ customizedParams });
  303. }
  304. catch (err) {
  305. const msg = 'Error occurred in updating theme';
  306. logger.error('Error', err);
  307. return res.apiv3Err(new ErrorV3(msg, 'update-theme-failed'));
  308. }
  309. });
  310. /**
  311. * @swagger
  312. *
  313. * /customize-setting/function:
  314. * put:
  315. * tags: [CustomizeSetting]
  316. * operationId: updateFunctionCustomizeSetting
  317. * summary: /customize-setting/function
  318. * description: Update function
  319. * requestBody:
  320. * required: true
  321. * content:
  322. * application/json:
  323. * schema:
  324. * $ref: '#/components/schemas/CustomizeFunction'
  325. * responses:
  326. * 200:
  327. * description: Succeeded to update function
  328. * content:
  329. * application/json:
  330. * schema:
  331. * $ref: '#/components/schemas/CustomizeFunction'
  332. */
  333. router.put('/function', loginRequiredStrictly, adminRequired, csrf, validator.function, apiV3FormValidator, async(req, res) => {
  334. const requestParams = {
  335. 'customize:isEnabledTimeline': req.body.isEnabledTimeline,
  336. 'customize:isSavedStatesOfTabChanges': req.body.isSavedStatesOfTabChanges,
  337. 'customize:isEnabledAttachTitleHeader': req.body.isEnabledAttachTitleHeader,
  338. 'customize:showPageLimitationS': req.body.pageLimitationS,
  339. 'customize:showPageLimitationM': req.body.pageLimitationM,
  340. 'customize:showPageLimitationL': req.body.pageLimitationL,
  341. 'customize:showPageLimitationXL': req.body.pageLimitationXL,
  342. 'customize:isEnabledStaleNotification': req.body.isEnabledStaleNotification,
  343. 'customize:isAllReplyShown': req.body.isAllReplyShown,
  344. };
  345. try {
  346. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  347. const customizedParams = {
  348. isEnabledTimeline: await crowi.configManager.getConfig('crowi', 'customize:isEnabledTimeline'),
  349. isSavedStatesOfTabChanges: await crowi.configManager.getConfig('crowi', 'customize:isSavedStatesOfTabChanges'),
  350. isEnabledAttachTitleHeader: await crowi.configManager.getConfig('crowi', 'customize:isEnabledAttachTitleHeader'),
  351. pageLimitationS: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationS'),
  352. pageLimitationM: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationM'),
  353. pageLimitationL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationL'),
  354. pageLimitationXL: await crowi.configManager.getConfig('crowi', 'customize:showPageLimitationXL'),
  355. isEnabledStaleNotification: await crowi.configManager.getConfig('crowi', 'customize:isEnabledStaleNotification'),
  356. isAllReplyShown: await crowi.configManager.getConfig('crowi', 'customize:isAllReplyShown'),
  357. };
  358. return res.apiv3({ customizedParams });
  359. }
  360. catch (err) {
  361. const msg = 'Error occurred in updating function';
  362. logger.error('Error', err);
  363. return res.apiv3Err(new ErrorV3(msg, 'update-function-failed'));
  364. }
  365. });
  366. /**
  367. * @swagger
  368. *
  369. * /customize-setting/highlight:
  370. * put:
  371. * tags: [CustomizeSetting]
  372. * operationId: updateHighlightCustomizeSetting
  373. * summary: /customize-setting/highlight
  374. * description: Update highlight
  375. * requestBody:
  376. * required: true
  377. * content:
  378. * application/json:
  379. * schema:
  380. * $ref: '#/components/schemas/CustomizeHighlight'
  381. * responses:
  382. * 200:
  383. * description: Succeeded to update highlight
  384. * content:
  385. * application/json:
  386. * schema:
  387. * $ref: '#/components/schemas/CustomizeHighlight'
  388. */
  389. router.put('/highlight', loginRequiredStrictly, adminRequired, csrf, validator.highlight, apiV3FormValidator, async(req, res) => {
  390. const requestParams = {
  391. 'customize:highlightJsStyle': req.body.highlightJsStyle,
  392. 'customize:highlightJsStyleBorder': req.body.highlightJsStyleBorder,
  393. };
  394. try {
  395. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  396. const customizedParams = {
  397. styleName: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyle'),
  398. styleBorder: await crowi.configManager.getConfig('crowi', 'customize:highlightJsStyleBorder'),
  399. };
  400. return res.apiv3({ customizedParams });
  401. }
  402. catch (err) {
  403. const msg = 'Error occurred in updating highlight';
  404. logger.error('Error', err);
  405. return res.apiv3Err(new ErrorV3(msg, 'update-highlight-failed'));
  406. }
  407. });
  408. /**
  409. * @swagger
  410. *
  411. * /customize-setting/customizeTitle:
  412. * put:
  413. * tags: [CustomizeSetting]
  414. * operationId: updateCustomizeTitleCustomizeSetting
  415. * summary: /customize-setting/customizeTitle
  416. * description: Update customizeTitle
  417. * requestBody:
  418. * required: true
  419. * content:
  420. * application/json:
  421. * schema:
  422. * $ref: '#/components/schemas/CustomizeTitle'
  423. * responses:
  424. * 200:
  425. * description: Succeeded to update customizeTitle
  426. * content:
  427. * application/json:
  428. * schema:
  429. * $ref: '#/components/schemas/CustomizeTitle'
  430. */
  431. router.put('/customize-title', loginRequiredStrictly, adminRequired, csrf, validator.customizeTitle, apiV3FormValidator, async(req, res) => {
  432. const requestParams = {
  433. 'customize:title': req.body.customizeTitle,
  434. };
  435. try {
  436. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams, true);
  437. crowi.customizeService.publishUpdatedMessage();
  438. const customizedParams = {
  439. customizeTitle: await crowi.configManager.getConfig('crowi', 'customize:title'),
  440. };
  441. customizeService.initCustomTitle();
  442. return res.apiv3({ customizedParams });
  443. }
  444. catch (err) {
  445. const msg = 'Error occurred in updating customizeTitle';
  446. logger.error('Error', err);
  447. return res.apiv3Err(new ErrorV3(msg, 'update-customizeTitle-failed'));
  448. }
  449. });
  450. /**
  451. * @swagger
  452. *
  453. * /customize-setting/customizeHeader:
  454. * put:
  455. * tags: [CustomizeSetting]
  456. * operationId: updateCustomizeHeaderCustomizeSetting
  457. * summary: /customize-setting/customizeHeader
  458. * description: Update customizeHeader
  459. * requestBody:
  460. * required: true
  461. * content:
  462. * application/json:
  463. * schema:
  464. * $ref: '#/components/schemas/CustomizeHeader'
  465. * responses:
  466. * 200:
  467. * description: Succeeded to update customize header
  468. * content:
  469. * application/json:
  470. * schema:
  471. * $ref: '#/components/schemas/CustomizeHeader'
  472. */
  473. router.put('/customize-header', loginRequiredStrictly, adminRequired, csrf, validator.customizeHeader, apiV3FormValidator, async(req, res) => {
  474. const requestParams = {
  475. 'customize:header': req.body.customizeHeader,
  476. };
  477. try {
  478. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  479. const customizedParams = {
  480. customizeHeader: await crowi.configManager.getConfig('crowi', 'customize:header'),
  481. };
  482. return res.apiv3({ customizedParams });
  483. }
  484. catch (err) {
  485. const msg = 'Error occurred in updating customizeHeader';
  486. logger.error('Error', err);
  487. return res.apiv3Err(new ErrorV3(msg, 'update-customizeHeader-failed'));
  488. }
  489. });
  490. /**
  491. * @swagger
  492. *
  493. * /customize-setting/customizeCss:
  494. * put:
  495. * tags: [CustomizeSetting]
  496. * operationId: updateCustomizeCssCustomizeSetting
  497. * summary: /customize-setting/customizeCss
  498. * description: Update customizeCss
  499. * requestBody:
  500. * required: true
  501. * content:
  502. * application/json:
  503. * schema:
  504. * $ref: '#/components/schemas/CustomizeCss'
  505. * responses:
  506. * 200:
  507. * description: Succeeded to update customize css
  508. * content:
  509. * application/json:
  510. * schema:
  511. * $ref: '#/components/schemas/CustomizeCss'
  512. */
  513. router.put('/customize-css', loginRequiredStrictly, adminRequired, csrf, validator.customizeCss, apiV3FormValidator, async(req, res) => {
  514. const requestParams = {
  515. 'customize:css': req.body.customizeCss,
  516. };
  517. try {
  518. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams, true);
  519. crowi.customizeService.publishUpdatedMessage();
  520. const customizedParams = {
  521. customizeCss: await crowi.configManager.getConfig('crowi', 'customize:css'),
  522. };
  523. customizeService.initCustomCss();
  524. return res.apiv3({ customizedParams });
  525. }
  526. catch (err) {
  527. const msg = 'Error occurred in updating customizeCss';
  528. logger.error('Error', err);
  529. return res.apiv3Err(new ErrorV3(msg, 'update-customizeCss-failed'));
  530. }
  531. });
  532. /**
  533. * @swagger
  534. *
  535. * /customize-setting/customizeScript:
  536. * put:
  537. * tags: [CustomizeSetting]
  538. * operationId: updateCustomizeScriptCustomizeSetting
  539. * summary: /customize-setting/customizeScript
  540. * description: Update customizeScript
  541. * requestBody:
  542. * required: true
  543. * content:
  544. * application/json:
  545. * schema:
  546. * $ref: '#/components/schemas/CustomizeScript'
  547. * responses:
  548. * 200:
  549. * description: Succeeded to update customize script
  550. * content:
  551. * application/json:
  552. * schema:
  553. * $ref: '#/components/schemas/CustomizeScript'
  554. */
  555. router.put('/customize-script', loginRequiredStrictly, adminRequired, csrf, validator.customizeScript, apiV3FormValidator, async(req, res) => {
  556. const requestParams = {
  557. 'customize:script': req.body.customizeScript,
  558. };
  559. try {
  560. await crowi.configManager.updateConfigsInTheSameNamespace('crowi', requestParams);
  561. const customizedParams = {
  562. customizeScript: await crowi.configManager.getConfig('crowi', 'customize:script'),
  563. };
  564. return res.apiv3({ customizedParams });
  565. }
  566. catch (err) {
  567. const msg = 'Error occurred in updating customizeScript';
  568. logger.error('Error', err);
  569. return res.apiv3Err(new ErrorV3(msg, 'update-customizeScript-failed'));
  570. }
  571. });
  572. return router;
  573. };