Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added option to download zip #94

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion admin/src/components/ActionButtons/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -30,6 +30,7 @@ const ActionButtons = () => {
<ActionButtonsStyling>
<Button disabled={isEmpty(partialDiff)} onClick={() => openModal('import')}>Import</Button>
<Button disabled={isEmpty(partialDiff)} onClick={() => openModal('export')}>Export</Button>
<Button onClick={() => dispatch(downloadZip(toggleNotification))}>Download Config</Button>
{!isEmpty(partialDiff) && (
<h4 style={{ display: 'inline' }}>{Object.keys(partialDiff).length} {Object.keys(partialDiff).length === 1 ? "config change" : "config changes"}</h4>
)}
Expand Down
27 changes: 23 additions & 4 deletions admin/src/state/actions/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import { request } from '@strapi/helper-plugin';

export function getAllConfigDiff(toggleNotification) {
return async function(dispatch) {
return async function (dispatch) {

Check warning on line 10 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (14)

Unexpected space before function parentheses

Check warning on line 10 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (16)

Unexpected space before function parentheses

Check warning on line 10 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (18)

Unexpected space before function parentheses
dispatch(setLoadingState(true));
try {
const configDiff = await request('/config-sync/diff', { method: 'GET' });
Expand Down Expand Up @@ -38,7 +38,7 @@
}

export function exportAllConfig(partialDiff, toggleNotification) {
return async function(dispatch) {
return async function (dispatch) {

Check warning on line 41 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (14)

Unexpected space before function parentheses

Check warning on line 41 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (16)

Unexpected space before function parentheses

Check warning on line 41 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (18)

Unexpected space before function parentheses
dispatch(setLoadingState(true));
try {
const { message } = await request('/config-sync/export', {
Expand All @@ -55,8 +55,27 @@
};
}

export function downloadZip(toggleNotification) {
return async function (dispatch) {

Check warning on line 59 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (14)

Unexpected space before function parentheses

Check warning on line 59 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (16)

Unexpected space before function parentheses

Check warning on line 59 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (18)

Unexpected space before function parentheses
dispatch(setLoadingState(true));
try {
const { message, url } = await request('/config-sync/zip', {
method: 'GET'

Check warning on line 63 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (14)

Missing trailing comma

Check warning on line 63 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (16)

Missing trailing comma

Check warning on line 63 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (18)

Missing trailing comma
});
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) {

Check warning on line 78 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (14)

Unexpected space before function parentheses

Check warning on line 78 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (16)

Unexpected space before function parentheses

Check warning on line 78 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (18)

Unexpected space before function parentheses
dispatch(setLoadingState(true));
try {
const { message } = await request('/config-sync/import', {
Expand Down Expand Up @@ -85,7 +104,7 @@
}

export function getAppEnv(toggleNotification) {
return async function(dispatch) {
return async function (dispatch) {

Check warning on line 107 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (14)

Unexpected space before function parentheses

Check warning on line 107 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (16)

Unexpected space before function parentheses

Check warning on line 107 in admin/src/state/actions/Config.js

View workflow job for this annotation

GitHub Actions / lint (18)

Unexpected space before function parentheses
try {
const envVars = await request('/config-sync/app-env', {
method: 'GET',
Expand Down
19 changes: 15 additions & 4 deletions server/config/type.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 = {};

Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand Down
13 changes: 13 additions & 0 deletions server/controllers/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 8 additions & 0 deletions server/routes/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ module.exports = {
policies: [],
},
},
{
method: "GET",
path: "/zip",
handler: "config.zipConfig",
config: {
policies: [],
},
},
{
method: "GET",
path: "/app-env",
Expand Down
36 changes: 32 additions & 4 deletions server/services/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
const util = require('util');
const difference = require('../utils/getObjectDiff');
const { logMessage } = require('../utils');
const child_process = require("child_process");

Check warning on line 8 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (14)

Identifier 'child_process' is not in camel case

Check failure on line 8 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (14)

`child_process` import should occur before import of `../utils/getObjectDiff`

Check warning on line 8 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (16)

Identifier 'child_process' is not in camel case

Check failure on line 8 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (16)

`child_process` import should occur before import of `../utils/getObjectDiff`

Check warning on line 8 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (18)

Identifier 'child_process' is not in camel case

Check failure on line 8 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (18)

`child_process` import should occur before import of `../utils/getObjectDiff`

/**
* Main services for config import/export.
Expand Down Expand Up @@ -54,7 +55,7 @@
* @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;
Expand All @@ -65,6 +66,33 @@
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`

Check warning on line 76 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (14)

Missing semicolon

Check warning on line 76 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (16)

Missing semicolon

Check warning on line 76 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (18)

Missing semicolon
child_process.execSync(`zip -r ${fileName} *`, {
cwd: strapi.config.get('plugin.config-sync.syncDir')

Check warning on line 78 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (14)

Missing trailing comma

Check warning on line 78 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (16)

Missing trailing comma

Check warning on line 78 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (18)

Missing trailing comma
});
const fullFilePath = `${strapi.config.get('plugin.config-sync.syncDir')}${fileName}`

Check warning on line 80 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (14)

Missing semicolon

Check warning on line 80 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (16)

Missing semicolon

Check warning on line 80 in server/services/main.js

View workflow job for this annotation

GitHub Actions / lint (18)

Missing semicolon
const stats = fs.statSync(fullFilePath);

const result = await strapi.plugins.upload.services.upload.upload({
Copy link
Member

@boazpoolman boazpoolman Jun 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uploading through the upload plugin makes the zip file publically accessible.
That would be a security risk as there may be sensitive data in the /config/sync directory.

Is there a way we can download the zip from memory, instead of writing it to the disk first?

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.
*
Expand Down Expand Up @@ -191,7 +219,7 @@
* @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();

Expand Down Expand Up @@ -242,8 +270,8 @@
*
* @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;

Expand Down
Loading