From 761024cc7437723e1a4928b2d04b72de64c01aec Mon Sep 17 00:00:00 2001 From: Alexander Engel Date: Thu, 15 Jun 2023 16:33:56 -0700 Subject: [PATCH] feat: added option to download zip --- admin/src/components/ActionButtons/index.js | 3 +- admin/src/state/actions/Config.js | 27 +++++++++++++--- server/config/type.js | 19 ++++++++--- server/controllers/config.js | 13 ++++++++ server/routes/admin.js | 8 +++++ server/services/main.js | 36 ++++++++++++++++++--- 6 files changed, 93 insertions(+), 13 deletions(-) diff --git a/admin/src/components/ActionButtons/index.js b/admin/src/components/ActionButtons/index.js index 4fa188b..4a30c07 100644 --- a/admin/src/components/ActionButtons/index.js +++ b/admin/src/components/ActionButtons/index.js @@ -7,7 +7,7 @@ import { Map } from 'immutable'; import { useNotification } from '@strapi/helper-plugin'; import ConfirmModal from '../ConfirmModal'; -import { exportAllConfig, importAllConfig } from '../../state/actions/Config'; +import { downloadZip, exportAllConfig, importAllConfig } from '../../state/actions/Config'; const ActionButtons = () => { const dispatch = useDispatch(); @@ -30,6 +30,7 @@ const ActionButtons = () => { + {!isEmpty(partialDiff) && (

{Object.keys(partialDiff).length} {Object.keys(partialDiff).length === 1 ? "config change" : "config changes"}

)} diff --git a/admin/src/state/actions/Config.js b/admin/src/state/actions/Config.js index 5922bfe..a9c6b64 100644 --- a/admin/src/state/actions/Config.js +++ b/admin/src/state/actions/Config.js @@ -7,7 +7,7 @@ import { request } from '@strapi/helper-plugin'; export function getAllConfigDiff(toggleNotification) { - return async function(dispatch) { + return async function (dispatch) { dispatch(setLoadingState(true)); try { const configDiff = await request('/config-sync/diff', { method: 'GET' }); @@ -38,7 +38,7 @@ export function setConfigPartialDiffInState(config) { } export function exportAllConfig(partialDiff, toggleNotification) { - return async function(dispatch) { + return async function (dispatch) { dispatch(setLoadingState(true)); try { const { message } = await request('/config-sync/export', { @@ -55,8 +55,27 @@ export function exportAllConfig(partialDiff, toggleNotification) { }; } +export function downloadZip(toggleNotification) { + return async function (dispatch) { + dispatch(setLoadingState(true)); + try { + const { message, url } = await request('/config-sync/zip', { + method: 'GET' + }); + toggleNotification({ type: 'success', message }); + if (url) { + window.location = url; + } + dispatch(setLoadingState(false)); + } catch (err) { + toggleNotification({ type: 'warning', message: { id: 'notification.error' } }); + dispatch(setLoadingState(false)); + } + }; +} + export function importAllConfig(partialDiff, force, toggleNotification) { - return async function(dispatch) { + return async function (dispatch) { dispatch(setLoadingState(true)); try { const { message } = await request('/config-sync/import', { @@ -85,7 +104,7 @@ export function setLoadingState(value) { } export function getAppEnv(toggleNotification) { - return async function(dispatch) { + return async function (dispatch) { try { const envVars = await request('/config-sync/app-env', { method: 'GET', diff --git a/server/config/type.js b/server/config/type.js index 74d15ca..343bcb1 100644 --- a/server/config/type.js +++ b/server/config/type.js @@ -166,7 +166,7 @@ const ConfigType = class ConfigType { * @param {string} configName - The name of the config file. * @returns {void} */ - exportSingle = async (configName) => { + exportSingle = async (configName) => { const formattedDiff = await strapi.plugin('config-sync').service('main').getFormattedDiff(this.configPrefix); // Check if the config should be excluded. @@ -186,12 +186,23 @@ const ConfigType = class ConfigType { } } + + /** + * Zip config files + * + * @param {string} configName - The name of the zip archive. + * @returns {void} + */ + zipConfig = async () => { + return strapi.plugin('config-sync').service('main').zipConfigFiles(); + } + /** * Get all role-permissions config from the db. * * @returns {object} Object with key value pairs of configs. */ - getAllFromDatabase = async () => { + getAllFromDatabase = async () => { const AllConfig = await noLimit(strapi.query(this.queryString), {}); const configs = {}; @@ -234,7 +245,7 @@ const ConfigType = class ConfigType { * * @returns {void} */ - importAll = async () => { + importAll = async () => { // The main.importAllConfig service will loop the core-store.importSingle service. await strapi.plugin('config-sync').service('main').importAllConfig(this.configPrefix); } @@ -244,7 +255,7 @@ const ConfigType = class ConfigType { * * @returns {void} */ - exportAll = async () => { + exportAll = async () => { // The main.importAllConfig service will loop the core-store.importSingle service. await strapi.plugin('config-sync').service('main').exportAllConfig(this.configPrefix); } diff --git a/server/controllers/config.js b/server/controllers/config.js index 4de75ee..ca06968 100644 --- a/server/controllers/config.js +++ b/server/controllers/config.js @@ -84,6 +84,19 @@ module.exports = { return strapi.plugin('config-sync').service('main').getFormattedDiff(); }, + zipConfig: async (ctx) => { + // Check for existance of the config file sync dir. + if (!fs.existsSync(strapi.config.get('plugin.config-sync.syncDir'))) { + ctx.send({ + message: 'No config files were found.', + }); + + return; + } + + return strapi.plugin('config-sync').service('main').zipConfigFiles(); + }, + /** * Get the current Strapi env. * @returns {string} The current Strapi environment. diff --git a/server/routes/admin.js b/server/routes/admin.js index 7422b45..f5fe7a1 100644 --- a/server/routes/admin.js +++ b/server/routes/admin.js @@ -27,6 +27,14 @@ module.exports = { policies: [], }, }, + { + method: "GET", + path: "/zip", + handler: "config.zipConfig", + config: { + policies: [], + }, + }, { method: "GET", path: "/app-env", diff --git a/server/services/main.js b/server/services/main.js index c603f62..3dc5935 100644 --- a/server/services/main.js +++ b/server/services/main.js @@ -5,6 +5,7 @@ const fs = require('fs'); const util = require('util'); const difference = require('../utils/getObjectDiff'); const { logMessage } = require('../utils'); +const child_process = require("child_process"); /** * Main services for config import/export. @@ -54,7 +55,7 @@ module.exports = () => ({ * @param {string} configName - The name of the config file. * @returns {void} */ - deleteConfigFile: async (configName) => { + deleteConfigFile: async (configName) => { // Check if the config should be excluded. const shouldExclude = !isEmpty(strapi.config.get('plugin.config-sync.excludedConfig').filter((option) => configName.startsWith(option))); if (shouldExclude) return; @@ -65,6 +66,33 @@ module.exports = () => ({ fs.unlinkSync(`${strapi.config.get('plugin.config-sync.syncDir')}${configName}.json`); }, + /** + * Zip config files. + * + * @param {string} configName - The name of the config file. + * @returns {void} + */ + zipConfigFiles: async () => { + const fileName = `config-${new Date().toJSON()}.zip` + child_process.execSync(`zip -r ${fileName} *`, { + cwd: strapi.config.get('plugin.config-sync.syncDir') + }); + const fullFilePath = `${strapi.config.get('plugin.config-sync.syncDir')}${fileName}` + const stats = fs.statSync(fullFilePath); + + const result = await strapi.plugins.upload.services.upload.upload({ + data: {}, //mandatory declare the data(can be empty), otherwise it will give you an undefined error. This parameters will be used to relate the file with a collection. + files: { + path: fullFilePath, + name: `configs/${fileName}`, + type: 'application/zip', // mime type of the file + size: stats.size, + }, + }); + fs.unlinkSync(fullFilePath); + return { url: result[0].url, message: 'Success' }; + }, + /** * Read from a config file. * @@ -191,7 +219,7 @@ module.exports = () => ({ * @param {object} onSuccess - Success callback to run on each single successfull import. * @returns {void} */ - exportAllConfig: async (configType = null, onSuccess) => { + exportAllConfig: async (configType = null, onSuccess) => { const fileConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromFiles(); const databaseConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromDatabase(); @@ -242,8 +270,8 @@ module.exports = () => ({ * * @returns {void} */ - exportSingleConfig: async (configName, onSuccess) => { - // Check if the config should be excluded. + exportSingleConfig: async (configName, onSuccess) => { + // Check if the config should be excluded. const shouldExclude = !isEmpty(strapi.config.get('plugin.config-sync.excludedConfig').filter((option) => configName.startsWith(option))); if (shouldExclude) return;