customize-setting.js 20 KB

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