customize-setting.js 19 KB

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