Add import/export services for roles, permissions & webhooks.
parent
c85fa56223
commit
4f0cce1b54
|
@ -2,5 +2,10 @@
|
||||||
"destination": "extensions/config-sync/files/",
|
"destination": "extensions/config-sync/files/",
|
||||||
"minify": false,
|
"minify": false,
|
||||||
"importOnBootstrap": false,
|
"importOnBootstrap": false,
|
||||||
|
"include": [
|
||||||
|
"core-store",
|
||||||
|
"role-permissions",
|
||||||
|
"webhooks"
|
||||||
|
],
|
||||||
"exclude": []
|
"exclude": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,14 +12,10 @@ const fs = require('fs');
|
||||||
* See more details here: https://strapi.io/documentation/v3.x/concepts/configurations.html#bootstrap
|
* See more details here: https://strapi.io/documentation/v3.x/concepts/configurations.html#bootstrap
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = () => {
|
module.exports = async () => {
|
||||||
if (strapi.plugins['config-sync'].config.importOnBootstrap) {
|
if (strapi.plugins['config-sync'].config.importOnBootstrap) {
|
||||||
if (fs.existsSync(strapi.plugins['config-sync'].config.destination)) {
|
if (fs.existsSync(strapi.plugins['config-sync'].config.destination)) {
|
||||||
const configFiles = fs.readdirSync(strapi.plugins['config-sync'].config.destination);
|
await strapi.plugins['config-sync'].services.main.importAllConfig();
|
||||||
|
|
||||||
configFiles.map((file) => {
|
|
||||||
strapi.plugins['config-sync'].services.config.importFromFile(file.slice(0, -5));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
{
|
{
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"path": "/export",
|
"path": "/export",
|
||||||
"handler": "config.export",
|
"handler": "config.exportAll",
|
||||||
"config": {
|
"config": {
|
||||||
"policies": []
|
"policies": []
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
||||||
{
|
{
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"path": "/import",
|
"path": "/import",
|
||||||
"handler": "config.import",
|
"handler": "config.importAll",
|
||||||
"config": {
|
"config": {
|
||||||
"policies": []
|
"policies": []
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,8 @@ module.exports = {
|
||||||
* @param {object} ctx - Request context object.
|
* @param {object} ctx - Request context object.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export: async (ctx) => {
|
exportAll: async (ctx) => {
|
||||||
const coreStoreAPI = strapi.query('core_store');
|
await strapi.plugins['config-sync'].services.main.exportAllConfig();
|
||||||
const coreStore = await coreStoreAPI.find({ _limit: -1 });
|
|
||||||
|
|
||||||
Object.values(coreStore).map(async ({ key, value }) => {
|
|
||||||
await strapi.plugins['config-sync'].services.config.writeConfigFile(key, value);
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.send({
|
ctx.send({
|
||||||
message: `Config was successfully exported to ${strapi.plugins['config-sync'].config.destination}.`
|
message: `Config was successfully exported to ${strapi.plugins['config-sync'].config.destination}.`
|
||||||
|
@ -32,7 +27,7 @@ module.exports = {
|
||||||
* @param {object} ctx - Request context object.
|
* @param {object} ctx - Request context object.
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
import: async (ctx) => {
|
importAll: async (ctx) => {
|
||||||
// Check for existance of the config file destination dir.
|
// Check for existance of the config file destination dir.
|
||||||
if (!fs.existsSync(strapi.plugins['config-sync'].config.destination)) {
|
if (!fs.existsSync(strapi.plugins['config-sync'].config.destination)) {
|
||||||
ctx.send({
|
ctx.send({
|
||||||
|
@ -42,11 +37,7 @@ module.exports = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const configFiles = fs.readdirSync(strapi.plugins['config-sync'].config.destination);
|
await strapi.plugins['config-sync'].services.main.importAllConfig();
|
||||||
|
|
||||||
configFiles.map((file) => {
|
|
||||||
strapi.plugins['config-sync'].services.config.importFromFile(file.slice(0, -5));
|
|
||||||
});
|
|
||||||
|
|
||||||
ctx.send({
|
ctx.send({
|
||||||
message: 'Config was successfully imported.'
|
message: 'Config was successfully imported.'
|
||||||
|
@ -75,7 +66,7 @@ module.exports = {
|
||||||
const getConfigs = async () => {
|
const getConfigs = async () => {
|
||||||
return Promise.all(configFiles.map(async (file) => {
|
return Promise.all(configFiles.map(async (file) => {
|
||||||
const formattedConfigName = file.slice(0, -5); // remove the .json extension.
|
const formattedConfigName = file.slice(0, -5); // remove the .json extension.
|
||||||
const fileContents = await strapi.plugins['config-sync'].services.config.readConfigFile(formattedConfigName);
|
const fileContents = await strapi.plugins['config-sync'].services.main.readConfigFile(formattedConfigName);
|
||||||
formattedConfigs[formattedConfigName] = fileContents;
|
formattedConfigs[formattedConfigName] = fileContents;
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const util = require('util');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main services for config import/export.
|
|
||||||
*/
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
/**
|
|
||||||
* Write a single config file.
|
|
||||||
*
|
|
||||||
* @param {string} configName - The name of the config file.
|
|
||||||
* @param {string} fileContents - The JSON content of the config file.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
writeConfigFile: async (configName, fileContents) => {
|
|
||||||
// Check if the config should be excluded.
|
|
||||||
const shouldExclude = strapi.plugins['config-sync'].config.exclude.includes(configName);
|
|
||||||
if (shouldExclude) return;
|
|
||||||
|
|
||||||
// Check if the JSON content should be minified.
|
|
||||||
const json =
|
|
||||||
!strapi.plugins['config-sync'].config.minify ?
|
|
||||||
JSON.stringify(JSON.parse(fileContents), null, 2)
|
|
||||||
: fileContents;
|
|
||||||
|
|
||||||
if (!fs.existsSync(strapi.plugins['config-sync'].config.destination)) {
|
|
||||||
fs.mkdirSync(strapi.plugins['config-sync'].config.destination, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
const writeFile = util.promisify(fs.writeFile);
|
|
||||||
await writeFile(`${strapi.plugins['config-sync'].config.destination}${configName}.json`, json);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read from a config file.
|
|
||||||
*
|
|
||||||
* @param {string} configName - The name of the config file.
|
|
||||||
* @returns {object} The JSON content of the config file.
|
|
||||||
*/
|
|
||||||
readConfigFile: async (configName) => {
|
|
||||||
const readFile = util.promisify(fs.readFile);
|
|
||||||
return await readFile(`${strapi.plugins['config-sync'].config.destination}${configName}.json`)
|
|
||||||
.then((data) => {
|
|
||||||
return JSON.parse(data);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Import a config file into the db.
|
|
||||||
*
|
|
||||||
* @param {string} configName - The name of the config file.
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
importFromFile: async (configName) => {
|
|
||||||
// Check if the config should be excluded.
|
|
||||||
const shouldExclude = strapi.plugins['config-sync'].config.exclude.includes(configName);
|
|
||||||
if (shouldExclude) return;
|
|
||||||
|
|
||||||
const coreStoreAPI = strapi.query('core_store');
|
|
||||||
const fileContents = await strapi.plugins['config-sync'].services.config.readConfigFile(configName);
|
|
||||||
|
|
||||||
const configExists = await strapi
|
|
||||||
.query('core_store')
|
|
||||||
.findOne({ key: configName });
|
|
||||||
|
|
||||||
if (!configExists) {
|
|
||||||
await coreStoreAPI.create({ key: configName, value: fileContents });
|
|
||||||
} else {
|
|
||||||
await coreStoreAPI.update({ key: configName }, { value: fileContents });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const coreStoreQueryString = 'core_store';
|
||||||
|
const configPrefix = 'core-store'; // Should be the same as the filename.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import/Export for core-store configs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Export all core-store config to files.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
exportAll: async () => {
|
||||||
|
const coreStore = await strapi.query(coreStoreQueryString).find({ _limit: -1 });
|
||||||
|
|
||||||
|
await Promise.all(Object.values(coreStore).map(async ({ id, ...config }) => {
|
||||||
|
config.value = JSON.parse(config.value);
|
||||||
|
await strapi.plugins['config-sync'].services.main.writeConfigFile(configPrefix, config.key, config);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import a single core-store config file into the db.
|
||||||
|
*
|
||||||
|
* @param {string} configName - The name of the config file.
|
||||||
|
* @param {string} configContent - The JSON content of the config file.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
importSingle: async (configName, configContent) => {
|
||||||
|
const { value, ...fileContent } = configContent;
|
||||||
|
const coreStoreAPI = strapi.query(coreStoreQueryString);
|
||||||
|
|
||||||
|
const configExists = await coreStoreAPI
|
||||||
|
.findOne({ key: configName, environment: fileContent.environment });
|
||||||
|
|
||||||
|
if (!configExists) {
|
||||||
|
await coreStoreAPI.create({ value: JSON.stringify(value), ...fileContent });
|
||||||
|
} else {
|
||||||
|
await coreStoreAPI.update({ key: configName }, { value: JSON.stringify(value), ...fileContent });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import all core-store config files into the db.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
importAll: async () => {
|
||||||
|
// The main.importAllConfig service will loop the core-store.importSingle service.
|
||||||
|
await strapi.plugins['config-sync'].services.main.importAllConfig(configPrefix);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a single core-store config to a file.
|
||||||
|
*
|
||||||
|
* @param {string} configName - The name of the config file.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
exportSingle: async (configName) => {
|
||||||
|
// @TODO: write export for a single core-store config.
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,126 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const util = require('util');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main services for config import/export.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Write a single config file.
|
||||||
|
*
|
||||||
|
* @param {string} configType - The type of the config.
|
||||||
|
* @param {string} configName - The name of the config file.
|
||||||
|
* @param {string} fileContents - The JSON content of the config file.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
writeConfigFile: async (configType, configName, fileContents) => {
|
||||||
|
// Check if the config should be excluded.
|
||||||
|
const shouldExclude = strapi.plugins['config-sync'].config.exclude.includes(`${configType}.${configName}`);
|
||||||
|
if (shouldExclude) return;
|
||||||
|
|
||||||
|
// Check if the JSON content should be minified.
|
||||||
|
const json =
|
||||||
|
!strapi.plugins['config-sync'].config.minify ?
|
||||||
|
JSON.stringify(fileContents, null, 2)
|
||||||
|
: JSON.stringify(fileContents);
|
||||||
|
|
||||||
|
if (!fs.existsSync(strapi.plugins['config-sync'].config.destination)) {
|
||||||
|
fs.mkdirSync(strapi.plugins['config-sync'].config.destination, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const writeFile = util.promisify(fs.writeFile);
|
||||||
|
await writeFile(`${strapi.plugins['config-sync'].config.destination}${configType}.${configName}.json`, json)
|
||||||
|
.then(() => {
|
||||||
|
// @TODO:
|
||||||
|
// Add logging for successfull config export.
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// @TODO:
|
||||||
|
// Add logging for failed config export.
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read from a config file.
|
||||||
|
*
|
||||||
|
* @param {string} configType - The type of config.
|
||||||
|
* @param {string} configName - The name of the config file.
|
||||||
|
* @returns {object} The JSON content of the config file.
|
||||||
|
*/
|
||||||
|
readConfigFile: async (configType, configName) => {
|
||||||
|
const readFile = util.promisify(fs.readFile);
|
||||||
|
return await readFile(`${strapi.plugins['config-sync'].config.destination}${configType}.${configName}.json`)
|
||||||
|
.then((data) => {
|
||||||
|
return JSON.parse(data);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import all config files into the db.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
importAllConfig: async (configType = null) => {
|
||||||
|
const configFiles = fs.readdirSync(strapi.plugins['config-sync'].config.destination);
|
||||||
|
|
||||||
|
configFiles.map((file) => {
|
||||||
|
const type = file.split('.')[0]; // Grab the first part of the filename.
|
||||||
|
const name = file.split(/\.(.+)/)[1].split('.').slice(0, -1).join('.'); // Grab the rest of the filename minus the file extension.
|
||||||
|
|
||||||
|
if (configType && configType !== type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
strapi.plugins['config-sync'].services.main.importSingleConfig(type, name);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export all config files.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
exportAllConfig: async (configType = null) => {
|
||||||
|
await Promise.all(strapi.plugins['config-sync'].config.include.map(async (type) => {
|
||||||
|
if (configType && configType !== type) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await strapi.plugins['config-sync'].services[type].exportAll();
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import a single config file into the db.
|
||||||
|
*
|
||||||
|
* @param {string} configType - The type of config.
|
||||||
|
* @param {string} configName - The name of the config file.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
importSingleConfig: async (configType, configName) => {
|
||||||
|
// Check if the config should be excluded.
|
||||||
|
const shouldExclude = strapi.plugins['config-sync'].config.exclude.includes(`${configType}.${configName}`);
|
||||||
|
if (shouldExclude) return;
|
||||||
|
|
||||||
|
const fileContents = await strapi.plugins['config-sync'].services.main.readConfigFile(configType, configName);
|
||||||
|
|
||||||
|
await strapi.plugins['config-sync'].services[configType].importSingle(configName, fileContents);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a single config file.
|
||||||
|
*
|
||||||
|
* @param {string} configType - The type of config.
|
||||||
|
* @param {string} configName - The name of the config file.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
exportSingleConfig: async (configType, configName) => {
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,85 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { sanitizeEntity } = require('strapi-utils');
|
||||||
|
|
||||||
|
const configPrefix = 'role-permissions'; // Should be the same as the filename.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import/Export for role-permissions configs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Export all role-permissions config to files.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
exportAll: async () => {
|
||||||
|
const service =
|
||||||
|
strapi.plugins['users-permissions'].services.userspermissions;
|
||||||
|
|
||||||
|
const [roles, plugins] = await Promise.all([
|
||||||
|
service.getRoles(),
|
||||||
|
service.getPlugins(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const rolesWithPermissions = await Promise.all(
|
||||||
|
roles.map(async role => service.getRole(role.id, plugins))
|
||||||
|
);
|
||||||
|
|
||||||
|
const sanitizedRolesArray = rolesWithPermissions.map(role =>
|
||||||
|
sanitizeEntity(role, {
|
||||||
|
model: strapi.plugins['users-permissions'].models.role,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.all(sanitizedRolesArray.map(async (config) => {
|
||||||
|
await strapi.plugins['config-sync'].services.main.writeConfigFile(configPrefix, config.type, config);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import a single role-permissions config file into the db.
|
||||||
|
*
|
||||||
|
* @param {string} configName - The name of the config file.
|
||||||
|
* @param {string} configContent - The JSON content of the config file.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
importSingle: async (configName, configContent) => {
|
||||||
|
const service =
|
||||||
|
strapi.plugins['users-permissions'].services.userspermissions;
|
||||||
|
|
||||||
|
const role = await strapi
|
||||||
|
.query('role', 'users-permissions')
|
||||||
|
.findOne({ type: configName });
|
||||||
|
|
||||||
|
const users = role ? role.users : [];
|
||||||
|
configContent.users = users;
|
||||||
|
|
||||||
|
if (!role) {
|
||||||
|
await service.createRole(configContent);
|
||||||
|
} else {
|
||||||
|
await service.updateRole(role.id, configContent);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import all role-permissions config files into the db.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
importAll: async () => {
|
||||||
|
// The main.importAllConfig service will loop the role-permissions.importSingle service.
|
||||||
|
await strapi.plugins['config-sync'].services.main.importAllConfig(configPrefix);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a single role-permissions config to a file.
|
||||||
|
*
|
||||||
|
* @param {string} configName - The name of the config file.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
exportSingle: async (configName) => {
|
||||||
|
// @TODO: write export for a single role-permissions config.
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,62 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import/Export for webhook configs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const webhookQueryString = 'strapi_webhooks';
|
||||||
|
const configPrefix = 'webhooks'; // Should be the same as the filename.
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
/**
|
||||||
|
* Export all webhooks to config files.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
exportAll: async () => {
|
||||||
|
const webhooks = await strapi.query(webhookQueryString).find({ _limit: -1 });
|
||||||
|
|
||||||
|
await Promise.all(Object.values(webhooks).map(async (config) => {
|
||||||
|
await strapi.plugins['config-sync'].services.main.writeConfigFile(configPrefix, config.id, config);
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import a single webhook config file into the db.
|
||||||
|
*
|
||||||
|
* @param {string} configName - The name of the config file.
|
||||||
|
* @param {string} configContent - The JSON content of the config file.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
importSingle: async (configName, configContent) => {
|
||||||
|
const webhookAPI = strapi.query(webhookQueryString);
|
||||||
|
|
||||||
|
const configExists = await webhookAPI.findOne({ id: configName });
|
||||||
|
|
||||||
|
if (!configExists) {
|
||||||
|
await webhookAPI.create(configContent);
|
||||||
|
} else {
|
||||||
|
await webhookAPI.update({ id: configName }, configContent);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Import all webhook config files into the db.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
importAll: async () => {
|
||||||
|
// The main.importAllConfig service will loop the webhooks.importSingle service.
|
||||||
|
await strapi.plugins['config-sync'].services.main.importAllConfig(configPrefix);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export a single webhook into a config file.
|
||||||
|
*
|
||||||
|
* @param {string} configName - The name of the config file.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
exportSingle: async (configName) => {
|
||||||
|
// @TODO: write export for a single webhook config.
|
||||||
|
},
|
||||||
|
};
|
Loading…
Reference in New Issue