admin.js 6.7 KB

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