customize-setting.js 19 KB

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