Skip to content

Commit 8ba4dbb

Browse files
authored
Merge pull request #94 from The-Real-Established/master
feat: added option to download zip
2 parents 79a4a0b + a794d30 commit 8ba4dbb

10 files changed

Lines changed: 95 additions & 6 deletions

File tree

admin/src/components/ActionButtons/index.jsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { getFetchClient, useNotification } from '@strapi/strapi/admin';
88
import { useIntl } from 'react-intl';
99

1010
import ConfirmModal from '../ConfirmModal';
11-
import { exportAllConfig, importAllConfig } from '../../state/actions/Config';
11+
import { exportAllConfig, importAllConfig, downloadZip } from '../../state/actions/Config';
1212

1313
const ActionButtons = () => {
1414
const { post, get } = getFetchClient();
@@ -40,6 +40,7 @@ const ActionButtons = () => {
4040
{!isEmpty(partialDiff) && (
4141
<Typography variant="epsilon">{Object.keys(partialDiff).length} {Object.keys(partialDiff).length === 1 ? "config change" : "config changes"}</Typography>
4242
)}
43+
<Button onClick={() => dispatch(downloadZip(toggleNotification, formatMessage, post, get))}>{formatMessage({ id: 'config-sync.Buttons.DownloadConfig' })}</Button>
4344
</ActionButtonsStyling>
4445
);
4546
};
@@ -52,6 +53,9 @@ const ActionButtonsStyling = styled.div`
5253
> button {
5354
margin-right: 10px;
5455
}
56+
> button:last-of-type {
57+
margin-left: auto;
58+
}
5559
`;
5660

5761
export default ActionButtons;

admin/src/helpers/blob.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function b64toBlob(dataURI, type) {
2+
const byteString = atob(dataURI);
3+
const ab = new ArrayBuffer(byteString.length);
4+
const ia = new Uint8Array(ab);
5+
for (let i = 0; i < byteString.length; i++) {
6+
ia[i] = byteString.charCodeAt(i);
7+
}
8+
return new Blob([ab], { type });
9+
}

admin/src/state/actions/Config.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* Main actions
44
*
55
*/
6+
import { saveAs } from 'file-saver';
7+
import { b64toBlob } from '../../helpers/blob';
68

79
export function getAllConfigDiff(toggleNotification, formatMessage, get) {
810
return async function(dispatch) {
@@ -50,6 +52,23 @@ export function exportAllConfig(partialDiff, toggleNotification, formatMessage,
5052
};
5153
}
5254

55+
export function downloadZip(toggleNotification, formatMessage, post, get) {
56+
return async function(dispatch) {
57+
dispatch(setLoadingState(true));
58+
try {
59+
const { message, base64Data, name } = (await get('/config-sync/zip')).data;
60+
toggleNotification({ type: 'success', message });
61+
if (base64Data) {
62+
saveAs(b64toBlob(base64Data, 'application/zip'), name, { type: 'application/zip' });
63+
}
64+
dispatch(setLoadingState(false));
65+
} catch (err) {
66+
toggleNotification({ type: 'warning', message: formatMessage({ id: 'notification.error' }) });
67+
dispatch(setLoadingState(false));
68+
}
69+
};
70+
}
71+
5372
export function importAllConfig(partialDiff, force, toggleNotification, formatMessage, post, get) {
5473
return async function(dispatch) {
5574
dispatch(setLoadingState(true));

admin/src/translations/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"ConfigDiff.Database": "Database",
2929

3030
"Buttons.Export": "Export",
31+
"Buttons.DownloadConfig": "Download Config",
3132
"Buttons.Import": "Import",
3233

3334
"FirstExport.Message": "Looks like this is your first time using config-sync for this project.",

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
"chalk": "^4.1.2",
3131
"cli-table": "^0.3.6",
3232
"commander": "^8.3.0",
33+
"file-saver": "^2.0.5",
3334
"git-diff": "^2.0.6",
3435
"immutable": "^3.8.2",
3536
"inquirer": "^8.2.0",

server/config/type.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ const ConfigType = class ConfigType {
173173
* @param {string} configName - The name of the config file.
174174
* @returns {void}
175175
*/
176-
exportSingle = async (configName) => {
176+
exportSingle = async (configName) => {
177177
const formattedDiff = await strapi.plugin('config-sync').service('main').getFormattedDiff(this.configPrefix);
178178

179179
// Check if the config should be excluded.
@@ -193,6 +193,17 @@ const ConfigType = class ConfigType {
193193
}
194194
}
195195

196+
197+
/**
198+
* Zip config files
199+
*
200+
* @param {string} configName - The name of the zip archive.
201+
* @returns {void}
202+
*/
203+
zipConfig = async () => {
204+
return strapi.plugin('config-sync').service('main').zipConfigFiles();
205+
}
206+
196207
/**
197208
* Get all role-permissions config from the db.
198209
*
@@ -243,7 +254,7 @@ const ConfigType = class ConfigType {
243254
*
244255
* @returns {void}
245256
*/
246-
importAll = async () => {
257+
importAll = async () => {
247258
// The main.importAllConfig service will loop the core-store.importSingle service.
248259
await strapi.plugin('config-sync').service('main').importAllConfig(this.configPrefix);
249260
}
@@ -253,7 +264,7 @@ const ConfigType = class ConfigType {
253264
*
254265
* @returns {void}
255266
*/
256-
exportAll = async () => {
267+
exportAll = async () => {
257268
// The main.importAllConfig service will loop the core-store.importSingle service.
258269
await strapi.plugin('config-sync').service('main').exportAllConfig(this.configPrefix);
259270
}

server/controllers/config.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,19 @@ module.exports = {
8484
return strapi.plugin('config-sync').service('main').getFormattedDiff();
8585
},
8686

87+
zipConfig: async (ctx) => {
88+
// Check for existance of the config file sync dir.
89+
if (!fs.existsSync(strapi.config.get('plugin.config-sync.syncDir'))) {
90+
ctx.send({
91+
message: 'No config files were found.',
92+
});
93+
94+
return;
95+
}
96+
97+
return strapi.plugin('config-sync').service('main').zipConfigFiles();
98+
},
99+
87100
/**
88101
* Get the current Strapi env.
89102
* @returns {string} The current Strapi environment.

server/routes/admin.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,14 @@ module.exports = {
2727
policies: [],
2828
},
2929
},
30+
{
31+
method: "GET",
32+
path: "/zip",
33+
handler: "config.zipConfig",
34+
config: {
35+
policies: [],
36+
},
37+
},
3038
{
3139
method: "GET",
3240
path: "/app-env",

server/services/main.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const { isEmpty } = require('lodash');
44
const fs = require('fs');
55
const util = require('util');
6+
const childProcess = require("child_process");
67
const difference = require('../utils/getObjectDiff');
78
const { logMessage } = require('../utils');
89

@@ -54,7 +55,7 @@ module.exports = () => ({
5455
* @param {string} configName - The name of the config file.
5556
* @returns {void}
5657
*/
57-
deleteConfigFile: async (configName) => {
58+
deleteConfigFile: async (configName) => {
5859
// Check if the config should be excluded.
5960
const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => configName.startsWith(option)));
6061
if (shouldExclude) return;
@@ -65,6 +66,23 @@ module.exports = () => ({
6566
fs.unlinkSync(`${strapi.config.get('plugin::config-sync.syncDir')}${configName}.json`);
6667
},
6768

69+
/**
70+
* Zip config files.
71+
*
72+
* @param {string} configName - The name of the config file.
73+
* @returns {void}
74+
*/
75+
zipConfigFiles: async () => {
76+
const fileName = `config-${new Date().toJSON()}.zip`;
77+
childProcess.execSync(`zip -r ${fileName} *`, {
78+
cwd: strapi.config.get('plugin.config-sync.syncDir'),
79+
});
80+
const fullFilePath = `${strapi.config.get('plugin.config-sync.syncDir')}${fileName}`;
81+
const base64Data = fs.readFileSync(fullFilePath, { encoding: 'base64' });
82+
fs.unlinkSync(fullFilePath);
83+
return { base64Data, name: fileName, message: 'Success' };
84+
},
85+
6886
/**
6987
* Read from a config file.
7088
*
@@ -191,7 +209,7 @@ module.exports = () => ({
191209
* @param {object} onSuccess - Success callback to run on each single successfull import.
192210
* @returns {void}
193211
*/
194-
exportAllConfig: async (configType = null, onSuccess) => {
212+
exportAllConfig: async (configType = null, onSuccess) => {
195213
const fileConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromFiles();
196214
const databaseConfig = await strapi.plugin('config-sync').service('main').getAllConfigFromDatabase();
197215

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6610,6 +6610,11 @@ file-entry-cache@^6.0.1:
66106610
dependencies:
66116611
flat-cache "^3.0.4"
66126612

6613+
file-saver@^2.0.5:
6614+
version "2.0.5"
6615+
resolved "https://registry.yarnpkg.com/file-saver/-/file-saver-2.0.5.tgz#d61cfe2ce059f414d899e9dd6d4107ee25670c38"
6616+
integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==
6617+
66136618
fill-range@^4.0.0:
66146619
version "4.0.0"
66156620
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7"

0 commit comments

Comments
 (0)