From 7f72442c54fd2f91941c2ce97e9256bfa227cd7b Mon Sep 17 00:00:00 2001
From: Charles Powell <charlie.powell1@outlook.com>
Date: Wed, 29 Jan 2025 13:11:43 +0000
Subject: [PATCH 1/3] feat: add option to only include listed types and config

---
 server/bootstrap.js     | 13 ++++++++++---
 server/config.js        |  4 +++-
 server/services/main.js | 27 ++++++++++++++++++---------
 3 files changed, 31 insertions(+), 13 deletions(-)

diff --git a/server/bootstrap.js b/server/bootstrap.js
index 583b8f4..ccacb42 100644
--- a/server/bootstrap.js
+++ b/server/bootstrap.js
@@ -2,6 +2,7 @@
 
 import fs from 'fs';
 
+import { isEmpty } from 'lodash';
 import ConfigType from './config/type';
 import defaultTypes from './config/types';
 import { logMessage } from './utils';
@@ -23,21 +24,27 @@ export default async () => {
 
     // The default types provided by the plugin.
     defaultTypes(strapi).map((type) => {
-      if (!strapi.config.get('plugin::config-sync.excludedTypes').includes(type.configName)) {
+      const shouldInclude = isEmpty(strapi.config.get('plugin::config-sync.includedTypes')) || strapi.config.get('plugin::config-sync.includedTypes').includes(type.configName);
+      const shouldExclude = strapi.config.get('plugin::config-sync.excludedTypes').includes(type.configName);
+      if (shouldInclude && !shouldExclude) {
         types[type.configName] = new ConfigType(type);
       }
     });
 
     // The types provided by other plugins.
     strapi.plugin('config-sync').pluginTypes.map((type) => {
-      if (!strapi.config.get('plugin::config-sync.excludedTypes').includes(type.configName)) {
+      const shouldInclude = isEmpty(strapi.config.get('plugin::config-sync.includedTypes')) || strapi.config.get('plugin::config-sync.includedTypes').includes(type.configName);
+      const shouldExclude = strapi.config.get('plugin::config-sync.excludedTypes').includes(type.configName);
+      if (shouldInclude && !shouldExclude) {
         types[type.configName] = new ConfigType(type);
       }
     });
 
     // The custom types provided by the user.
     strapi.config.get('plugin::config-sync.customTypes').map((type) => {
-      if (!strapi.config.get('plugin::config-sync.excludedTypes').includes(type.configName)) {
+      const shouldInclude = isEmpty(strapi.config.get('plugin::config-sync.includedTypes')) || strapi.config.get('plugin::config-sync.includedTypes').includes(type.configName);
+      const shouldExclude = strapi.config.get('plugin::config-sync.excludedTypes').includes(type.configName);
+      if (shouldInclude && !shouldExclude) {
         types[type.configName] = new ConfigType(type);
       }
     });
diff --git a/server/config.js b/server/config.js
index 053c4fd..052da86 100644
--- a/server/config.js
+++ b/server/config.js
@@ -1,4 +1,4 @@
-'use strict';
+"use strict";
 
 export default {
   default: {
@@ -7,7 +7,9 @@ export default {
     soft: false,
     importOnBootstrap: false,
     customTypes: [],
+    includedTypes: [],
     excludedTypes: [],
+    includedConfig: [],
     excludedConfig: [
       "core-store.plugin_users-permissions_grant",
       "core-store.plugin_upload_metrics",
diff --git a/server/services/main.js b/server/services/main.js
index 5a73e6a..071c142 100644
--- a/server/services/main.js
+++ b/server/services/main.js
@@ -1,12 +1,12 @@
 'use strict';
 
-import isEmpty from 'lodash/isEmpty';
+import AdmZip from 'adm-zip';
 import fs from 'fs';
+import isEmpty from 'lodash/isEmpty';
 import util from 'util';
-import AdmZip from 'adm-zip';
 
-import difference from '../utils/getObjectDiff';
 import { logMessage } from '../utils';
+import difference from '../utils/getObjectDiff';
 
 /**
  * Main services for config import/export.
@@ -23,8 +23,9 @@ export default () => ({
    */
   writeConfigFile: async (configType, configName, fileContents) => {
     // Check if the config should be excluded.
+    const shouldInclude = isEmpty(strapi.config.get('plugin::config-sync.includedConfig')) || !isEmpty(strapi.config.get('plugin::config-sync.includedConfig').filter((option) => `${configType}.${configName}`.startsWith(option)));
     const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => `${configType}.${configName}`.startsWith(option)));
-    if (shouldExclude) return;
+    if (!shouldInclude || shouldExclude) return;
 
     // Replace reserved characters in filenames.
     configName = configName.replace(/:/g, "#").replace(/\//g, "$");
@@ -58,8 +59,9 @@ export default () => ({
    */
   deleteConfigFile: async (configName) => {
     // Check if the config should be excluded.
+    const shouldInclude = isEmpty(strapi.config.get('plugin::config-sync.includedConfig')) || !isEmpty(strapi.config.get('plugin::config-sync.includedConfig').filter((option) => configName.startsWith(option)));
     const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => configName.startsWith(option)));
-    if (shouldExclude) return;
+    if (!shouldInclude || shouldExclude) return;
 
     // Replace reserved characters in filenames.
     configName = configName.replace(/:/g, "#").replace(/\//g, "$");
@@ -128,10 +130,14 @@ export default () => ({
         // Put back reserved characters from filenames.
         const formattedName = name.replace(/#/g, ":").replace(/\$/g, "/");
 
+        const shouldInclude = isEmpty(strapi.config.get('plugin::config-sync.includedConfig')) || !isEmpty(strapi.config.get('plugin::config-sync.includedConfig').filter((option) => `${type}.${name}`.startsWith(option)));
+        const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => `${type}.${name}`.startsWith(option)));
+
         if (
           configType && configType !== type
           || !strapi.plugin('config-sync').types[type]
-          || !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => `${type}.${name}`.startsWith(option)))
+          || !shouldInclude
+          || shouldExclude
         ) {
           return;
         }
@@ -237,8 +243,9 @@ export default () => ({
    */
   importSingleConfig: async (configName, onSuccess, force) => {
     // Check if the config should be excluded.
+    const shouldInclude = isEmpty(strapi.config.get('plugin::config-sync.includedConfig')) || !isEmpty(strapi.config.get('plugin::config-sync.includedConfig').filter((option) => configName.startsWith(option)));
     const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => configName.startsWith(option)));
-    if (shouldExclude) return;
+    if (!shouldInclude || shouldExclude) return;
 
     const type = configName.split('.')[0]; // Grab the first part of the filename.
     const name = configName.split(/\.(.+)/)[1]; // Grab the rest of the filename.
@@ -261,9 +268,11 @@ export default () => ({
    * @returns {void}
    */
    exportSingleConfig: async (configName, onSuccess) => {
-     // Check if the config should be excluded.
+    console.log(configName);
+    // Check if the config should be excluded.
+    const shouldInclude = isEmpty(strapi.config.get('plugin::config-sync.includedConfig')) || !isEmpty(strapi.config.get('plugin::config-sync.includedConfig').filter((option) => configName.startsWith(option)));
     const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => configName.startsWith(option)));
-    if (shouldExclude) return;
+    if (!shouldInclude || shouldExclude) return;
 
     const type = configName.split('.')[0]; // Grab the first part of the filename.
     const name = configName.split(/\.(.+)/)[1]; // Grab the rest of the filename.

From e5b59a831f092a1b6b32dcc2833ff56a43c35d50 Mon Sep 17 00:00:00 2001
From: Charles Powell <charlie.powell1@outlook.com>
Date: Wed, 29 Jan 2025 13:13:50 +0000
Subject: [PATCH 2/3] chore: update comments

---
 server/services/main.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/server/services/main.js b/server/services/main.js
index 071c142..be63dcc 100644
--- a/server/services/main.js
+++ b/server/services/main.js
@@ -22,7 +22,7 @@ export default () => ({
    * @returns {void}
    */
   writeConfigFile: async (configType, configName, fileContents) => {
-    // Check if the config should be excluded.
+    // Check if the config should be included.
     const shouldInclude = isEmpty(strapi.config.get('plugin::config-sync.includedConfig')) || !isEmpty(strapi.config.get('plugin::config-sync.includedConfig').filter((option) => `${configType}.${configName}`.startsWith(option)));
     const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => `${configType}.${configName}`.startsWith(option)));
     if (!shouldInclude || shouldExclude) return;
@@ -58,7 +58,7 @@ export default () => ({
    * @returns {void}
    */
   deleteConfigFile: async (configName) => {
-    // Check if the config should be excluded.
+    // Check if the config should be included.
     const shouldInclude = isEmpty(strapi.config.get('plugin::config-sync.includedConfig')) || !isEmpty(strapi.config.get('plugin::config-sync.includedConfig').filter((option) => configName.startsWith(option)));
     const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => configName.startsWith(option)));
     if (!shouldInclude || shouldExclude) return;
@@ -242,7 +242,7 @@ export default () => ({
    * @returns {void}
    */
   importSingleConfig: async (configName, onSuccess, force) => {
-    // Check if the config should be excluded.
+    // Check if the config should be included.
     const shouldInclude = isEmpty(strapi.config.get('plugin::config-sync.includedConfig')) || !isEmpty(strapi.config.get('plugin::config-sync.includedConfig').filter((option) => configName.startsWith(option)));
     const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => configName.startsWith(option)));
     if (!shouldInclude || shouldExclude) return;
@@ -269,7 +269,7 @@ export default () => ({
    */
    exportSingleConfig: async (configName, onSuccess) => {
     console.log(configName);
-    // Check if the config should be excluded.
+    // Check if the config should be included.
     const shouldInclude = isEmpty(strapi.config.get('plugin::config-sync.includedConfig')) || !isEmpty(strapi.config.get('plugin::config-sync.includedConfig').filter((option) => configName.startsWith(option)));
     const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => configName.startsWith(option)));
     if (!shouldInclude || shouldExclude) return;

From c7bdf8fb3d992fc308224ef98390676210ac556a Mon Sep 17 00:00:00 2001
From: Charles Powell <charlie.powell1@outlook.com>
Date: Fri, 31 Jan 2025 10:31:34 +0000
Subject: [PATCH 3/3] chore: action comments

---
 server/bootstrap.js     | 2 +-
 server/services/main.js | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)

diff --git a/server/bootstrap.js b/server/bootstrap.js
index ccacb42..c8b2164 100644
--- a/server/bootstrap.js
+++ b/server/bootstrap.js
@@ -2,7 +2,7 @@
 
 import fs from 'fs';
 
-import { isEmpty } from 'lodash';
+import isEmpty from 'lodash/isEmpty';
 import ConfigType from './config/type';
 import defaultTypes from './config/types';
 import { logMessage } from './utils';
diff --git a/server/services/main.js b/server/services/main.js
index be63dcc..602efaa 100644
--- a/server/services/main.js
+++ b/server/services/main.js
@@ -268,7 +268,6 @@ export default () => ({
    * @returns {void}
    */
    exportSingleConfig: async (configName, onSuccess) => {
-    console.log(configName);
     // Check if the config should be included.
     const shouldInclude = isEmpty(strapi.config.get('plugin::config-sync.includedConfig')) || !isEmpty(strapi.config.get('plugin::config-sync.includedConfig').filter((option) => configName.startsWith(option)));
     const shouldExclude = !isEmpty(strapi.config.get('plugin::config-sync.excludedConfig').filter((option) => configName.startsWith(option)));