customize-setting.js 19 KB

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