admin.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import { SupportedAction } from '~/interfaces/activity';
  2. import loggerFactory from '~/utils/logger';
  3. const logger = loggerFactory('growi:routes:admin');
  4. /* eslint-disable no-use-before-define */
  5. module.exports = function(crowi, app) {
  6. const {
  7. configManager,
  8. exportService,
  9. } = crowi;
  10. const ApiResponse = require('../util/apiResponse');
  11. const importer = require('../util/importer')(crowi);
  12. const actions = {};
  13. const { check, param } = require('express-validator');
  14. const activityEvent = crowi.event('activity');
  15. const api = {};
  16. // Importer management
  17. actions.importer = {};
  18. actions.importer.api = api;
  19. api.validators = {};
  20. api.validators.importer = {};
  21. api.validators.importer.esa = function() {
  22. const validator = [
  23. check('importer:esa:team_name').not().isEmpty().withMessage('Error. Empty esa:team_name'),
  24. check('importer:esa:access_token').not().isEmpty().withMessage('Error. Empty esa:access_token'),
  25. ];
  26. return validator;
  27. };
  28. api.validators.importer.qiita = function() {
  29. const validator = [
  30. check('importer:qiita:team_name').not().isEmpty().withMessage('Error. Empty qiita:team_name'),
  31. check('importer:qiita:access_token').not().isEmpty().withMessage('Error. Empty qiita:access_token'),
  32. ];
  33. return validator;
  34. };
  35. // Export management
  36. actions.export = {};
  37. actions.export.api = api;
  38. api.validators.export = {};
  39. api.validators.export.download = function() {
  40. const validator = [
  41. // https://regex101.com/r/mD4eZs/6
  42. // prevent from pass traversal attack
  43. param('fileName').not().matches(/(\.\.\/|\.\.\\)/),
  44. ];
  45. return validator;
  46. };
  47. actions.export.download = (req, res) => {
  48. const { fileName } = req.params;
  49. const { validationResult } = require('express-validator');
  50. const errors = validationResult(req);
  51. if (!errors.isEmpty()) {
  52. return res.status(422).json({ errors: `${fileName} is invalid. Do not use path like '../'.` });
  53. }
  54. try {
  55. const zipFile = exportService.getFile(fileName);
  56. const parameters = {
  57. ip: req.ip,
  58. endpoint: req.originalUrl,
  59. action: SupportedAction.ACTION_ADMIN_ARCHIVE_DATA_DOWNLOAD,
  60. user: req.user?._id,
  61. snapshot: {
  62. username: req.user?.username,
  63. },
  64. };
  65. crowi.activityService.createActivity(parameters);
  66. return res.download(zipFile);
  67. }
  68. catch (err) {
  69. // TODO: use ApiV3Error
  70. logger.error(err);
  71. return res.json(ApiResponse.error());
  72. }
  73. };
  74. actions.api = {};
  75. /**
  76. * save esa settings, update config cache, and response json
  77. *
  78. * @param {*} req
  79. * @param {*} res
  80. */
  81. actions.api.importerSettingEsa = async(req, res) => {
  82. const form = req.body;
  83. const { validationResult } = require('express-validator');
  84. const errors = validationResult(req);
  85. if (!errors.isEmpty()) {
  86. return res.json(ApiResponse.error('esa.io form is blank'));
  87. }
  88. await configManager.updateConfigsInTheSameNamespace('crowi', form);
  89. importer.initializeEsaClient(); // let it run in the back aftert res
  90. const parameters = { action: SupportedAction.ACTION_ADMIN_ESA_DATA_UPDATED };
  91. activityEvent.emit('update', res.locals.activity._id, parameters);
  92. return res.json(ApiResponse.success());
  93. };
  94. /**
  95. * save qiita settings, update config cache, and response json
  96. *
  97. * @param {*} req
  98. * @param {*} res
  99. */
  100. actions.api.importerSettingQiita = async(req, res) => {
  101. const form = req.body;
  102. const { validationResult } = require('express-validator');
  103. const errors = validationResult(req);
  104. if (!errors.isEmpty()) {
  105. return res.json(ApiResponse.error('Qiita form is blank'));
  106. }
  107. await configManager.updateConfigsInTheSameNamespace('crowi', form);
  108. importer.initializeQiitaClient(); // let it run in the back aftert res
  109. const parameters = { action: SupportedAction.ACTION_ADMIN_QIITA_DATA_UPDATED };
  110. activityEvent.emit('update', res.locals.activity._id, parameters);
  111. return res.json(ApiResponse.success());
  112. };
  113. /**
  114. * Import all posts from esa
  115. *
  116. * @param {*} req
  117. * @param {*} res
  118. */
  119. actions.api.importDataFromEsa = async(req, res) => {
  120. const user = req.user;
  121. let errors;
  122. try {
  123. errors = await importer.importDataFromEsa(user);
  124. const parameters = { action: SupportedAction.ACTION_ADMIN_ESA_DATA_IMPORTED };
  125. activityEvent.emit('update', res.locals.activity._id, parameters);
  126. }
  127. catch (err) {
  128. errors = [err];
  129. }
  130. if (errors.length > 0) {
  131. return res.json(ApiResponse.error(`<br> - ${errors.join('<br> - ')}`));
  132. }
  133. return res.json(ApiResponse.success());
  134. };
  135. /**
  136. * Import all posts from qiita
  137. *
  138. * @param {*} req
  139. * @param {*} res
  140. */
  141. actions.api.importDataFromQiita = async(req, res) => {
  142. const user = req.user;
  143. let errors;
  144. try {
  145. errors = await importer.importDataFromQiita(user);
  146. const parameters = { action: SupportedAction.ACTION_ADMIN_QIITA_DATA_IMPORTED };
  147. activityEvent.emit('update', res.locals.activity._id, parameters);
  148. }
  149. catch (err) {
  150. errors = [err];
  151. }
  152. if (errors.length > 0) {
  153. return res.json(ApiResponse.error(`<br> - ${errors.join('<br> - ')}`));
  154. }
  155. return res.json(ApiResponse.success());
  156. };
  157. /**
  158. * Test connection to esa and response result with json
  159. *
  160. * @param {*} req
  161. * @param {*} res
  162. */
  163. actions.api.testEsaAPI = async(req, res) => {
  164. try {
  165. await importer.testConnectionToEsa();
  166. const parameters = { action: SupportedAction.ACTION_ADMIN_CONNECTION_TEST_OF_ESA_DATA };
  167. activityEvent.emit('update', res.locals.activity._id, parameters);
  168. return res.json(ApiResponse.success());
  169. }
  170. catch (err) {
  171. return res.json(ApiResponse.error(err));
  172. }
  173. };
  174. /**
  175. * Test connection to qiita and response result with json
  176. *
  177. * @param {*} req
  178. * @param {*} res
  179. */
  180. actions.api.testQiitaAPI = async(req, res) => {
  181. try {
  182. await importer.testConnectionToQiita();
  183. const parameters = { action: SupportedAction.ACTION_ADMIN_CONNECTION_TEST_OF_QIITA_DATA };
  184. activityEvent.emit('update', res.locals.activity._id, parameters);
  185. return res.json(ApiResponse.success());
  186. }
  187. catch (err) {
  188. return res.json(ApiResponse.error(err));
  189. }
  190. };
  191. actions.api.searchBuildIndex = async function(req, res) {
  192. const search = crowi.getSearcher();
  193. if (!search) {
  194. return res.json(ApiResponse.error('ElasticSearch Integration is not set up.'));
  195. }
  196. try {
  197. search.buildIndex();
  198. }
  199. catch (err) {
  200. return res.json(ApiResponse.error(err));
  201. }
  202. return res.json(ApiResponse.success());
  203. };
  204. return actions;
  205. };