import { SupportedAction } from '~/interfaces/activity'; import loggerFactory from '~/utils/logger'; const logger = loggerFactory('growi:routes:admin'); /* eslint-disable no-use-before-define */ module.exports = function(crowi, app) { const { configManager, exportService, } = crowi; const ApiResponse = require('../util/apiResponse'); const importer = require('../util/importer')(crowi); const actions = {}; const { check, param } = require('express-validator'); const activityEvent = crowi.event('activity'); const api = {}; // Importer management actions.importer = {}; actions.importer.api = api; api.validators = {}; api.validators.importer = {}; api.validators.importer.esa = function() { const validator = [ check('importer:esa:team_name').not().isEmpty().withMessage('Error. Empty esa:team_name'), check('importer:esa:access_token').not().isEmpty().withMessage('Error. Empty esa:access_token'), ]; return validator; }; api.validators.importer.qiita = function() { const validator = [ check('importer:qiita:team_name').not().isEmpty().withMessage('Error. Empty qiita:team_name'), check('importer:qiita:access_token').not().isEmpty().withMessage('Error. Empty qiita:access_token'), ]; return validator; }; // Export management actions.export = {}; actions.export.api = api; api.validators.export = {}; api.validators.export.download = function() { const validator = [ // https://regex101.com/r/mD4eZs/6 // prevent from pass traversal attack param('fileName').not().matches(/(\.\.\/|\.\.\\)/), ]; return validator; }; actions.export.download = (req, res) => { const { fileName } = req.params; const { validationResult } = require('express-validator'); const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(422).json({ errors: `${fileName} is invalid. Do not use path like '../'.` }); } try { const zipFile = exportService.getFile(fileName); const parameters = { ip: req.ip, endpoint: req.originalUrl, action: SupportedAction.ACTION_ADMIN_ARCHIVE_DATA_DOWNLOAD, user: req.user?._id, snapshot: { username: req.user?.username, }, }; crowi.activityService.createActivity(parameters); return res.download(zipFile); } catch (err) { // TODO: use ApiV3Error logger.error(err); return res.json(ApiResponse.error()); } }; actions.api = {}; /** * save esa settings, update config cache, and response json * * @param {*} req * @param {*} res */ actions.api.importerSettingEsa = async(req, res) => { const form = req.body; const { validationResult } = require('express-validator'); const errors = validationResult(req); if (!errors.isEmpty()) { return res.json(ApiResponse.error('esa.io form is blank')); } await configManager.updateConfigsInTheSameNamespace('crowi', form); importer.initializeEsaClient(); // let it run in the back aftert res const parameters = { action: SupportedAction.ACTION_ADMIN_ESA_DATA_UPDATED }; activityEvent.emit('update', res.locals.activity._id, parameters); return res.json(ApiResponse.success()); }; /** * save qiita settings, update config cache, and response json * * @param {*} req * @param {*} res */ actions.api.importerSettingQiita = async(req, res) => { const form = req.body; const { validationResult } = require('express-validator'); const errors = validationResult(req); if (!errors.isEmpty()) { return res.json(ApiResponse.error('Qiita form is blank')); } await configManager.updateConfigsInTheSameNamespace('crowi', form); importer.initializeQiitaClient(); // let it run in the back aftert res const parameters = { action: SupportedAction.ACTION_ADMIN_QIITA_DATA_UPDATED }; activityEvent.emit('update', res.locals.activity._id, parameters); return res.json(ApiResponse.success()); }; /** * Import all posts from esa * * @param {*} req * @param {*} res */ actions.api.importDataFromEsa = async(req, res) => { const user = req.user; let errors; try { errors = await importer.importDataFromEsa(user); const parameters = { action: SupportedAction.ACTION_ADMIN_ESA_DATA_IMPORTED }; activityEvent.emit('update', res.locals.activity._id, parameters); } catch (err) { errors = [err]; } if (errors.length > 0) { return res.json(ApiResponse.error(`
- ${errors.join('
- ')}`)); } return res.json(ApiResponse.success()); }; /** * Import all posts from qiita * * @param {*} req * @param {*} res */ actions.api.importDataFromQiita = async(req, res) => { const user = req.user; let errors; try { errors = await importer.importDataFromQiita(user); const parameters = { action: SupportedAction.ACTION_ADMIN_QIITA_DATA_IMPORTED }; activityEvent.emit('update', res.locals.activity._id, parameters); } catch (err) { errors = [err]; } if (errors.length > 0) { return res.json(ApiResponse.error(`
- ${errors.join('
- ')}`)); } return res.json(ApiResponse.success()); }; /** * Test connection to esa and response result with json * * @param {*} req * @param {*} res */ actions.api.testEsaAPI = async(req, res) => { try { await importer.testConnectionToEsa(); const parameters = { action: SupportedAction.ACTION_ADMIN_CONNECTION_TEST_OF_ESA_DATA }; activityEvent.emit('update', res.locals.activity._id, parameters); return res.json(ApiResponse.success()); } catch (err) { return res.json(ApiResponse.error(err)); } }; /** * Test connection to qiita and response result with json * * @param {*} req * @param {*} res */ actions.api.testQiitaAPI = async(req, res) => { try { await importer.testConnectionToQiita(); const parameters = { action: SupportedAction.ACTION_ADMIN_CONNECTION_TEST_OF_QIITA_DATA }; activityEvent.emit('update', res.locals.activity._id, parameters); return res.json(ApiResponse.success()); } catch (err) { return res.json(ApiResponse.error(err)); } }; actions.api.searchBuildIndex = async function(req, res) { const search = crowi.getSearcher(); if (!search) { return res.json(ApiResponse.error('ElasticSearch Integration is not set up.')); } try { search.buildIndex(); } catch (err) { return res.json(ApiResponse.error(err)); } return res.json(ApiResponse.success()); }; return actions; };