dbgate-api-premium 6.7.3 → 6.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dbgate-api-premium",
3
3
  "main": "src/index.js",
4
- "version": "6.7.3",
4
+ "version": "6.8.0",
5
5
  "homepage": "https://dbgate.org/",
6
6
  "repository": {
7
7
  "type": "git",
@@ -30,10 +30,10 @@
30
30
  "compare-versions": "^3.6.0",
31
31
  "cors": "^2.8.5",
32
32
  "cross-env": "^6.0.3",
33
- "dbgate-datalib": "^6.7.3",
33
+ "dbgate-datalib": "^6.8.0",
34
34
  "dbgate-query-splitter": "^4.11.9",
35
- "dbgate-sqltree": "^6.7.3",
36
- "dbgate-tools": "^6.7.3",
35
+ "dbgate-sqltree": "^6.8.0",
36
+ "dbgate-tools": "^6.8.0",
37
37
  "debug": "^4.3.4",
38
38
  "diff": "^5.0.0",
39
39
  "diff2html": "^3.4.13",
@@ -75,6 +75,7 @@
75
75
  "start:dblogin": "env-cmd -f env/dblogin/.env node src/index.js --listen-api",
76
76
  "start:filedb": "env-cmd node src/index.js /home/jena/test/chinook/Chinook.db --listen-api",
77
77
  "start:storage": "env-cmd -f env/storage/.env node src/index.js --listen-api",
78
+ "start:sfill": "env-cmd -f env/sfill/.env node src/index.js --listen-api",
78
79
  "start:storage:built": "env-cmd -f env/storage/.env cross-env DEVMODE= BUILTWEBMODE=1 node dist/bundle.js --listen-api",
79
80
  "start:singleconn": "env-cmd node src/index.js --server localhost --user root --port 3307 --engine mysql@dbgate-plugin-mysql --password test --listen-api",
80
81
  "start:azure": "env-cmd -f env/azure/.env node src/index.js --listen-api",
@@ -86,7 +87,7 @@
86
87
  "devDependencies": {
87
88
  "@types/fs-extra": "^9.0.11",
88
89
  "@types/lodash": "^4.14.149",
89
- "dbgate-types": "^6.7.3",
90
+ "dbgate-types": "^6.8.0",
90
91
  "env-cmd": "^10.1.0",
91
92
  "jsdoc-to-markdown": "^9.0.5",
92
93
  "node-loader": "^1.0.2",
@@ -23,6 +23,7 @@ const pipeForkLogs = require('../utility/pipeForkLogs');
23
23
  const requireEngineDriver = require('../utility/requireEngineDriver');
24
24
  const { getAuthProviderById } = require('../auth/authProvider');
25
25
  const { startTokenChecking } = require('../utility/authProxy');
26
+ const { extractConnectionsFromEnv } = require('../utility/envtools');
26
27
 
27
28
  const logger = getLogger('connections');
28
29
 
@@ -61,55 +62,7 @@ function getDatabaseFileLabel(databaseFile) {
61
62
 
62
63
  function getPortalCollections() {
63
64
  if (process.env.CONNECTIONS) {
64
- const connections = _.compact(process.env.CONNECTIONS.split(',')).map(id => ({
65
- _id: id,
66
- engine: process.env[`ENGINE_${id}`],
67
- server: process.env[`SERVER_${id}`],
68
- user: process.env[`USER_${id}`],
69
- password: process.env[`PASSWORD_${id}`],
70
- passwordMode: process.env[`PASSWORD_MODE_${id}`],
71
- port: process.env[`PORT_${id}`],
72
- databaseUrl: process.env[`URL_${id}`],
73
- useDatabaseUrl: !!process.env[`URL_${id}`],
74
- databaseFile: process.env[`FILE_${id}`]?.replace(
75
- '%%E2E_TEST_DATA_DIRECTORY%%',
76
- path.join(path.dirname(path.dirname(__dirname)), 'e2e-tests', 'tmpdata')
77
- ),
78
- socketPath: process.env[`SOCKET_PATH_${id}`],
79
- serviceName: process.env[`SERVICE_NAME_${id}`],
80
- authType: process.env[`AUTH_TYPE_${id}`] || (process.env[`SOCKET_PATH_${id}`] ? 'socket' : undefined),
81
- defaultDatabase:
82
- process.env[`DATABASE_${id}`] ||
83
- (process.env[`FILE_${id}`] ? getDatabaseFileLabel(process.env[`FILE_${id}`]) : null),
84
- singleDatabase: !!process.env[`DATABASE_${id}`] || !!process.env[`FILE_${id}`],
85
- displayName: process.env[`LABEL_${id}`],
86
- isReadOnly: process.env[`READONLY_${id}`],
87
- databases: process.env[`DBCONFIG_${id}`] ? safeJsonParse(process.env[`DBCONFIG_${id}`]) : null,
88
- allowedDatabases: process.env[`ALLOWED_DATABASES_${id}`]?.replace(/\|/g, '\n'),
89
- allowedDatabasesRegex: process.env[`ALLOWED_DATABASES_REGEX_${id}`],
90
- parent: process.env[`PARENT_${id}`] || undefined,
91
- useSeparateSchemas: !!process.env[`USE_SEPARATE_SCHEMAS_${id}`],
92
- localDataCenter: process.env[`LOCAL_DATA_CENTER_${id}`],
93
-
94
- // SSH tunnel
95
- useSshTunnel: process.env[`USE_SSH_${id}`],
96
- sshHost: process.env[`SSH_HOST_${id}`],
97
- sshPort: process.env[`SSH_PORT_${id}`],
98
- sshMode: process.env[`SSH_MODE_${id}`],
99
- sshLogin: process.env[`SSH_LOGIN_${id}`],
100
- sshPassword: process.env[`SSH_PASSWORD_${id}`],
101
- sshKeyfile: process.env[`SSH_KEY_FILE_${id}`],
102
- sshKeyfilePassword: process.env[`SSH_KEY_FILE_PASSWORD_${id}`],
103
-
104
- // SSL
105
- useSsl: process.env[`USE_SSL_${id}`],
106
- sslCaFile: process.env[`SSL_CA_FILE_${id}`],
107
- sslCertFile: process.env[`SSL_CERT_FILE_${id}`],
108
- sslCertFilePassword: process.env[`SSL_CERT_FILE_PASSWORD_${id}`],
109
- sslKeyFile: process.env[`SSL_KEY_FILE_${id}`],
110
- sslRejectUnauthorized: process.env[`SSL_REJECT_UNAUTHORIZED_${id}`],
111
- trustServerCertificate: process.env[`SSL_TRUST_CERTIFICATE_${id}`],
112
- }));
65
+ const connections = extractConnectionsFromEnv(process.env);
113
66
 
114
67
  for (const conn of connections) {
115
68
  for (const prop in process.env) {
@@ -229,6 +182,15 @@ module.exports = {
229
182
  );
230
183
  }
231
184
  await this.checkUnsavedConnectionsLimit();
185
+
186
+ if (process.env.STORAGE_DATABASE && process.env.CONNECTIONS) {
187
+ const storage = require('./storage');
188
+ try {
189
+ await storage.fillStorageConnectionsFromEnv();
190
+ } catch (err) {
191
+ logger.error(extractErrorLogData(err), 'DBGM-00268 Error filling storage connections from env');
192
+ }
193
+ }
232
194
  },
233
195
 
234
196
  list_meta: true,
@@ -41,6 +41,7 @@ const crypto = require('crypto');
41
41
  const dataReplicator = require('../shell/dataReplicator');
42
42
  const storageReplicatorItems = require('../utility/storageReplicatorItems');
43
43
  const { sendToAuditLog } = require('../utility/auditlog');
44
+ const { extractImportEntitiesFromEnv, createStorageFromEnvReplicatorItems } = require('../utility/envtools');
44
45
 
45
46
  const logger = getLogger('storage');
46
47
 
@@ -642,7 +643,7 @@ module.exports = {
642
643
  getConnectionList_meta: true,
643
644
  async getConnectionList() {
644
645
  const resp = await storageSelectFmt(
645
- `select ~connections.~id,~connections.~engine,~connections.~displayName,~connections.~server,~connections.~user from ~connections`
646
+ `select ~connections.~id,~connections.~engine,~connections.~displayName,~connections.~server,~connections.~user,~connections.~import_source_id from ~connections`
646
647
  );
647
648
  return resp;
648
649
  },
@@ -739,7 +740,7 @@ module.exports = {
739
740
 
740
741
  getRoleList_meta: true,
741
742
  async getRoleList() {
742
- const resp = await storageSelectFmt(`select ~roles.~id,~roles.~name from ~roles`);
743
+ const resp = await storageSelectFmt(`select ~roles.~id,~roles.~name,~roles.~import_source_id from ~roles`);
743
744
  const usedPermissions = await storageSelectFmt(`select * from ~role_permissions`);
744
745
  return resp.map(x => ({
745
746
  ...x,
@@ -750,7 +751,7 @@ module.exports = {
750
751
  getRoleDetail_meta: true,
751
752
  async getRoleDetail({ id }) {
752
753
  const resp =
753
- id == 'new' ? {} : await storageSelectFmt(`select ~roles.~id,~roles.~name from ~roles where ~roles.~id = %v`, id);
754
+ id == 'new' ? {} : await storageSelectFmt(`select ~roles.~id,~roles.~name,~roles.~import_source_id from ~roles where ~roles.~id = %v`, id);
754
755
 
755
756
  const basePermissions = getPredefinedPermissions(id < 0 && resp[0]?.name ? resp[0]?.name : 'logged-user');
756
757
  const permissions =
@@ -1034,4 +1035,20 @@ module.exports = {
1034
1035
  sendToAuditLog(req, props);
1035
1036
  return null;
1036
1037
  },
1038
+
1039
+ async fillStorageConnectionsFromEnv() {
1040
+ const importEntities = extractImportEntitiesFromEnv(process.env);
1041
+ if (!importEntities) {
1042
+ return;
1043
+ }
1044
+
1045
+ const [conn, driver] = await getStorageConnection();
1046
+ // @ts-ignore
1047
+ await dataReplicator({
1048
+ systemConnection: conn,
1049
+ driver,
1050
+ items: createStorageFromEnvReplicatorItems(importEntities),
1051
+ });
1052
+ socket.emitChanged('connection-list-changed');
1053
+ },
1037
1054
  };
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '6.7.3',
4
- buildTime: '2025-12-03T17:24:02.395Z'
3
+ version: '6.8.0',
4
+ buildTime: '2025-12-17T14:40:41.323Z'
5
5
  };
@@ -65,6 +65,8 @@ async function copyStream(input, output, options) {
65
65
  });
66
66
  }
67
67
  } catch (err) {
68
+ logger.error(extractErrorLogData(err, { progressName }), 'DBGM-00157 Import/export job failed');
69
+
68
70
  process.send({
69
71
  msgtype: 'copyStreamError',
70
72
  copyStreamError: {
@@ -82,8 +84,6 @@ async function copyStream(input, output, options) {
82
84
  errorMessage: extractErrorMessage(err),
83
85
  });
84
86
  }
85
-
86
- logger.error(extractErrorLogData(err, { progressName }), 'DBGM-00157 Import/export job failed');
87
87
  // throw err;
88
88
  }
89
89
  }
@@ -64,6 +64,7 @@ async function dataReplicator({
64
64
  createNew: compileOperationFunction(item.createNew, item.createCondition),
65
65
  updateExisting: compileOperationFunction(item.updateExisting, item.updateCondition),
66
66
  deleteMissing: !!item.deleteMissing,
67
+ skipUpdateColumns: item.skipUpdateColumns,
67
68
  deleteRestrictionColumns: item.deleteRestrictionColumns ?? [],
68
69
  openStream: item.openStream
69
70
  ? item.openStream
@@ -686,9 +686,34 @@ module.exports = {
686
686
  "columnName": "connectionDefinition",
687
687
  "dataType": "text",
688
688
  "notNull": false
689
+ },
690
+ {
691
+ "pureName": "connections",
692
+ "columnName": "import_source_id",
693
+ "dataType": "int",
694
+ "notNull": false
695
+ },
696
+ {
697
+ "pureName": "connections",
698
+ "columnName": "id_original",
699
+ "dataType": "varchar(250)",
700
+ "notNull": false
701
+ }
702
+ ],
703
+ "foreignKeys": [
704
+ {
705
+ "constraintType": "foreignKey",
706
+ "constraintName": "FK_connections_import_source_id",
707
+ "pureName": "connections",
708
+ "refTableName": "import_sources",
709
+ "columns": [
710
+ {
711
+ "columnName": "import_source_id",
712
+ "refColumnName": "id"
713
+ }
714
+ ]
689
715
  }
690
716
  ],
691
- "foreignKeys": [],
692
717
  "primaryKey": {
693
718
  "pureName": "connections",
694
719
  "constraintType": "primaryKey",
@@ -790,6 +815,41 @@ module.exports = {
790
815
  }
791
816
  ]
792
817
  },
818
+ {
819
+ "pureName": "import_sources",
820
+ "columns": [
821
+ {
822
+ "pureName": "import_sources",
823
+ "columnName": "id",
824
+ "dataType": "int",
825
+ "autoIncrement": true,
826
+ "notNull": true
827
+ },
828
+ {
829
+ "pureName": "import_sources",
830
+ "columnName": "name",
831
+ "dataType": "varchar(250)",
832
+ "notNull": true
833
+ }
834
+ ],
835
+ "foreignKeys": [],
836
+ "primaryKey": {
837
+ "pureName": "import_sources",
838
+ "constraintType": "primaryKey",
839
+ "constraintName": "PK_import_sources",
840
+ "columns": [
841
+ {
842
+ "columnName": "id"
843
+ }
844
+ ]
845
+ },
846
+ "preloadedRows": [
847
+ {
848
+ "id": -1,
849
+ "name": "env"
850
+ }
851
+ ]
852
+ },
793
853
  {
794
854
  "pureName": "roles",
795
855
  "columns": [
@@ -805,9 +865,34 @@ module.exports = {
805
865
  "columnName": "name",
806
866
  "dataType": "varchar(250)",
807
867
  "notNull": false
868
+ },
869
+ {
870
+ "pureName": "roles",
871
+ "columnName": "import_source_id",
872
+ "dataType": "int",
873
+ "notNull": false
874
+ },
875
+ {
876
+ "pureName": "roles",
877
+ "columnName": "id_original",
878
+ "dataType": "varchar(250)",
879
+ "notNull": false
880
+ }
881
+ ],
882
+ "foreignKeys": [
883
+ {
884
+ "constraintType": "foreignKey",
885
+ "constraintName": "FK_roles_import_source_id",
886
+ "pureName": "roles",
887
+ "refTableName": "import_sources",
888
+ "columns": [
889
+ {
890
+ "columnName": "import_source_id",
891
+ "refColumnName": "id"
892
+ }
893
+ ]
808
894
  }
809
895
  ],
810
- "foreignKeys": [],
811
896
  "primaryKey": {
812
897
  "pureName": "roles",
813
898
  "constraintType": "primaryKey",
@@ -854,6 +939,12 @@ module.exports = {
854
939
  "columnName": "connection_id",
855
940
  "dataType": "int",
856
941
  "notNull": true
942
+ },
943
+ {
944
+ "pureName": "role_connections",
945
+ "columnName": "import_source_id",
946
+ "dataType": "int",
947
+ "notNull": false
857
948
  }
858
949
  ],
859
950
  "foreignKeys": [
@@ -882,6 +973,18 @@ module.exports = {
882
973
  "refColumnName": "id"
883
974
  }
884
975
  ]
976
+ },
977
+ {
978
+ "constraintType": "foreignKey",
979
+ "constraintName": "FK_role_connections_import_source_id",
980
+ "pureName": "role_connections",
981
+ "refTableName": "import_sources",
982
+ "columns": [
983
+ {
984
+ "columnName": "import_source_id",
985
+ "refColumnName": "id"
986
+ }
987
+ ]
885
988
  }
886
989
  ],
887
990
  "primaryKey": {
@@ -934,6 +1037,18 @@ module.exports = {
934
1037
  "columnName": "database_permission_role_id",
935
1038
  "dataType": "int",
936
1039
  "notNull": true
1040
+ },
1041
+ {
1042
+ "pureName": "role_databases",
1043
+ "columnName": "import_source_id",
1044
+ "dataType": "int",
1045
+ "notNull": false
1046
+ },
1047
+ {
1048
+ "pureName": "role_databases",
1049
+ "columnName": "id_original",
1050
+ "dataType": "varchar(250)",
1051
+ "notNull": false
937
1052
  }
938
1053
  ],
939
1054
  "foreignKeys": [
@@ -974,6 +1089,18 @@ module.exports = {
974
1089
  "refColumnName": "id"
975
1090
  }
976
1091
  ]
1092
+ },
1093
+ {
1094
+ "constraintType": "foreignKey",
1095
+ "constraintName": "FK_role_databases_import_source_id",
1096
+ "pureName": "role_databases",
1097
+ "refTableName": "import_sources",
1098
+ "columns": [
1099
+ {
1100
+ "columnName": "import_source_id",
1101
+ "refColumnName": "id"
1102
+ }
1103
+ ]
977
1104
  }
978
1105
  ],
979
1106
  "primaryKey": {
@@ -1087,6 +1214,12 @@ module.exports = {
1087
1214
  "columnName": "permission",
1088
1215
  "dataType": "varchar(250)",
1089
1216
  "notNull": true
1217
+ },
1218
+ {
1219
+ "pureName": "role_permissions",
1220
+ "columnName": "import_source_id",
1221
+ "dataType": "int",
1222
+ "notNull": false
1090
1223
  }
1091
1224
  ],
1092
1225
  "foreignKeys": [
@@ -1102,6 +1235,18 @@ module.exports = {
1102
1235
  "refColumnName": "id"
1103
1236
  }
1104
1237
  ]
1238
+ },
1239
+ {
1240
+ "constraintType": "foreignKey",
1241
+ "constraintName": "FK_role_permissions_import_source_id",
1242
+ "pureName": "role_permissions",
1243
+ "refTableName": "import_sources",
1244
+ "columns": [
1245
+ {
1246
+ "columnName": "import_source_id",
1247
+ "refColumnName": "id"
1248
+ }
1249
+ ]
1105
1250
  }
1106
1251
  ],
1107
1252
  "primaryKey": {
@@ -1184,6 +1329,18 @@ module.exports = {
1184
1329
  "columnName": "table_permission_scope_id",
1185
1330
  "dataType": "int",
1186
1331
  "notNull": true
1332
+ },
1333
+ {
1334
+ "pureName": "role_tables",
1335
+ "columnName": "import_source_id",
1336
+ "dataType": "int",
1337
+ "notNull": false
1338
+ },
1339
+ {
1340
+ "pureName": "role_tables",
1341
+ "columnName": "id_original",
1342
+ "dataType": "varchar(250)",
1343
+ "notNull": false
1187
1344
  }
1188
1345
  ],
1189
1346
  "foreignKeys": [
@@ -1236,6 +1393,18 @@ module.exports = {
1236
1393
  "refColumnName": "id"
1237
1394
  }
1238
1395
  ]
1396
+ },
1397
+ {
1398
+ "constraintType": "foreignKey",
1399
+ "constraintName": "FK_role_tables_import_source_id",
1400
+ "pureName": "role_tables",
1401
+ "refTableName": "import_sources",
1402
+ "columns": [
1403
+ {
1404
+ "columnName": "import_source_id",
1405
+ "refColumnName": "id"
1406
+ }
1407
+ ]
1239
1408
  }
1240
1409
  ],
1241
1410
  "primaryKey": {
@@ -0,0 +1,445 @@
1
+ const path = require('path');
2
+ const _ = require('lodash');
3
+ const { safeJsonParse, getDatabaseFileLabel } = require('dbgate-tools');
4
+ const crypto = require('crypto');
5
+
6
+ function extractConnectionsFromEnv(env) {
7
+ if (!env?.CONNECTIONS) {
8
+ return null;
9
+ }
10
+
11
+ const connections = _.compact(env.CONNECTIONS.split(',')).map(id => ({
12
+ _id: id,
13
+ engine: env[`ENGINE_${id}`],
14
+ server: env[`SERVER_${id}`],
15
+ user: env[`USER_${id}`],
16
+ password: env[`PASSWORD_${id}`],
17
+ passwordMode: env[`PASSWORD_MODE_${id}`],
18
+ port: env[`PORT_${id}`],
19
+ databaseUrl: env[`URL_${id}`],
20
+ useDatabaseUrl: !!env[`URL_${id}`],
21
+ databaseFile: env[`FILE_${id}`]?.replace(
22
+ '%%E2E_TEST_DATA_DIRECTORY%%',
23
+ path.join(path.dirname(path.dirname(__dirname)), 'e2e-tests', 'tmpdata')
24
+ ),
25
+ socketPath: env[`SOCKET_PATH_${id}`],
26
+ serviceName: env[`SERVICE_NAME_${id}`],
27
+ authType: env[`AUTH_TYPE_${id}`] || (env[`SOCKET_PATH_${id}`] ? 'socket' : undefined),
28
+ defaultDatabase: env[`DATABASE_${id}`] || (env[`FILE_${id}`] ? getDatabaseFileLabel(env[`FILE_${id}`]) : null),
29
+ singleDatabase: !!env[`DATABASE_${id}`] || !!env[`FILE_${id}`],
30
+ displayName: env[`LABEL_${id}`],
31
+ isReadOnly: env[`READONLY_${id}`],
32
+ databases: env[`DBCONFIG_${id}`] ? safeJsonParse(env[`DBCONFIG_${id}`]) : null,
33
+ allowedDatabases: env[`ALLOWED_DATABASES_${id}`]?.replace(/\|/g, '\n'),
34
+ allowedDatabasesRegex: env[`ALLOWED_DATABASES_REGEX_${id}`],
35
+ parent: env[`PARENT_${id}`] || undefined,
36
+ useSeparateSchemas: !!env[`USE_SEPARATE_SCHEMAS_${id}`],
37
+ localDataCenter: env[`LOCAL_DATA_CENTER_${id}`],
38
+
39
+ // SSH tunnel
40
+ useSshTunnel: env[`USE_SSH_${id}`],
41
+ sshHost: env[`SSH_HOST_${id}`],
42
+ sshPort: env[`SSH_PORT_${id}`],
43
+ sshMode: env[`SSH_MODE_${id}`],
44
+ sshLogin: env[`SSH_LOGIN_${id}`],
45
+ sshPassword: env[`SSH_PASSWORD_${id}`],
46
+ sshKeyfile: env[`SSH_KEY_FILE_${id}`],
47
+ sshKeyfilePassword: env[`SSH_KEY_FILE_PASSWORD_${id}`],
48
+
49
+ // SSL
50
+ useSsl: env[`USE_SSL_${id}`],
51
+ sslCaFile: env[`SSL_CA_FILE_${id}`],
52
+ sslCertFile: env[`SSL_CERT_FILE_${id}`],
53
+ sslCertFilePassword: env[`SSL_CERT_FILE_PASSWORD_${id}`],
54
+ sslKeyFile: env[`SSL_KEY_FILE_${id}`],
55
+ sslRejectUnauthorized: env[`SSL_REJECT_UNAUTHORIZED_${id}`],
56
+ trustServerCertificate: env[`SSL_TRUST_CERTIFICATE_${id}`],
57
+ }));
58
+
59
+ return connections;
60
+ }
61
+
62
+ function extractImportEntitiesFromEnv(env) {
63
+ const portalConnections = extractConnectionsFromEnv(env) || [];
64
+
65
+ const connections = portalConnections.map((conn, index) => ({
66
+ ...conn,
67
+ id_original: conn._id,
68
+ import_source_id: -1,
69
+ conid: crypto.randomUUID(),
70
+ _id: undefined,
71
+ id: index + 1, // autoincrement id
72
+ }));
73
+
74
+ const connectionEnvIdToDbId = {};
75
+ for (const conn of connections) {
76
+ connectionEnvIdToDbId[conn.id_original] = conn.id;
77
+ }
78
+
79
+ const connectionsRegex = /^ROLE_(.+)_CONNECTIONS$/;
80
+ const permissionsRegex = /^ROLE_(.+)_PERMISSIONS$/;
81
+
82
+ const dbConnectionRegex = /^ROLE_(.+)_DATABASES_(.+)_CONNECTION$/;
83
+ const dbDatabasesRegex = /^ROLE_(.+)_DATABASES_(.+)_DATABASES$/;
84
+ const dbDatabasesRegexRegex = /^ROLE_(.+)_DATABASES_(.+)_DATABASES_REGEX$/;
85
+ const dbPermissionRegex = /^ROLE_(.+)_DATABASES_(.+)_PERMISSION$/;
86
+
87
+ const tableConnectionRegex = /^ROLE_(.+)_TABLES_(.+)_CONNECTION$/;
88
+ const tableDatabasesRegex = /^ROLE_(.+)_TABLES_(.+)_DATABASES$/;
89
+ const tableDatabasesRegexRegex = /^ROLE_(.+)_TABLES_(.+)_DATABASES_REGEX$/;
90
+ const tableSchemasRegex = /^ROLE_(.+)_TABLES_(.+)_SCHEMAS$/;
91
+ const tableSchemasRegexRegex = /^ROLE_(.+)_TABLES_(.+)_SCHEMAS_REGEX$/;
92
+ const tableTablesRegex = /^ROLE_(.+)_TABLES_(.+)_TABLES$/;
93
+ const tableTablesRegexRegex = /^ROLE_(.+)_TABLES_(.+)_TABLES_REGEX$/;
94
+ const tablePermissionRegex = /^ROLE_(.+)_TABLES_(.+)_PERMISSION$/;
95
+ const tableScopeRegex = /^ROLE_(.+)_TABLES_(.+)_SCOPE$/;
96
+
97
+ const roles = [];
98
+ const role_connections = [];
99
+ const role_permissions = [];
100
+ const role_databases = [];
101
+ const role_tables = [];
102
+
103
+ // Permission name to ID mappings
104
+ const databasePermissionMap = {
105
+ view: -1,
106
+ read_content: -2,
107
+ write_data: -3,
108
+ run_script: -4,
109
+ deny: -5,
110
+ };
111
+
112
+ const tablePermissionMap = {
113
+ read: -1,
114
+ update_only: -2,
115
+ create_update_delete: -3,
116
+ run_script: -4,
117
+ deny: -5,
118
+ };
119
+
120
+ const tableScopeMap = {
121
+ all_objects: -1,
122
+ tables: -2,
123
+ views: -3,
124
+ tables_views_collections: -4,
125
+ procedures: -5,
126
+ functions: -6,
127
+ triggers: -7,
128
+ sql_objects: -8,
129
+ collections: -9,
130
+ };
131
+
132
+ // Collect database and table permissions data
133
+ const databasePermissions = {};
134
+ const tablePermissions = {};
135
+
136
+ // First pass: collect all database and table permission data
137
+ for (const key in env) {
138
+ const dbConnMatch = key.match(dbConnectionRegex);
139
+ const dbDatabasesMatch = key.match(dbDatabasesRegex);
140
+ const dbDatabasesRegexMatch = key.match(dbDatabasesRegexRegex);
141
+ const dbPermMatch = key.match(dbPermissionRegex);
142
+
143
+ const tableConnMatch = key.match(tableConnectionRegex);
144
+ const tableDatabasesMatch = key.match(tableDatabasesRegex);
145
+ const tableDatabasesRegexMatch = key.match(tableDatabasesRegexRegex);
146
+ const tableSchemasMatch = key.match(tableSchemasRegex);
147
+ const tableSchemasRegexMatch = key.match(tableSchemasRegexRegex);
148
+ const tableTablesMatch = key.match(tableTablesRegex);
149
+ const tableTablesRegexMatch = key.match(tableTablesRegexRegex);
150
+ const tablePermMatch = key.match(tablePermissionRegex);
151
+ const tableScopeMatch = key.match(tableScopeRegex);
152
+
153
+ // Database permissions
154
+ if (dbConnMatch) {
155
+ const [, roleName, permId] = dbConnMatch;
156
+ if (!databasePermissions[roleName]) databasePermissions[roleName] = {};
157
+ if (!databasePermissions[roleName][permId]) databasePermissions[roleName][permId] = {};
158
+ databasePermissions[roleName][permId].connection = env[key];
159
+ }
160
+ if (dbDatabasesMatch) {
161
+ const [, roleName, permId] = dbDatabasesMatch;
162
+ if (!databasePermissions[roleName]) databasePermissions[roleName] = {};
163
+ if (!databasePermissions[roleName][permId]) databasePermissions[roleName][permId] = {};
164
+ databasePermissions[roleName][permId].databases = env[key]?.replace(/\|/g, '\n');
165
+ }
166
+ if (dbDatabasesRegexMatch) {
167
+ const [, roleName, permId] = dbDatabasesRegexMatch;
168
+ if (!databasePermissions[roleName]) databasePermissions[roleName] = {};
169
+ if (!databasePermissions[roleName][permId]) databasePermissions[roleName][permId] = {};
170
+ databasePermissions[roleName][permId].databasesRegex = env[key];
171
+ }
172
+ if (dbPermMatch) {
173
+ const [, roleName, permId] = dbPermMatch;
174
+ if (!databasePermissions[roleName]) databasePermissions[roleName] = {};
175
+ if (!databasePermissions[roleName][permId]) databasePermissions[roleName][permId] = {};
176
+ databasePermissions[roleName][permId].permission = env[key];
177
+ }
178
+
179
+ // Table permissions
180
+ if (tableConnMatch) {
181
+ const [, roleName, permId] = tableConnMatch;
182
+ if (!tablePermissions[roleName]) tablePermissions[roleName] = {};
183
+ if (!tablePermissions[roleName][permId]) tablePermissions[roleName][permId] = {};
184
+ tablePermissions[roleName][permId].connection = env[key];
185
+ }
186
+ if (tableDatabasesMatch) {
187
+ const [, roleName, permId] = tableDatabasesMatch;
188
+ if (!tablePermissions[roleName]) tablePermissions[roleName] = {};
189
+ if (!tablePermissions[roleName][permId]) tablePermissions[roleName][permId] = {};
190
+ tablePermissions[roleName][permId].databases = env[key]?.replace(/\|/g, '\n');
191
+ }
192
+ if (tableDatabasesRegexMatch) {
193
+ const [, roleName, permId] = tableDatabasesRegexMatch;
194
+ if (!tablePermissions[roleName]) tablePermissions[roleName] = {};
195
+ if (!tablePermissions[roleName][permId]) tablePermissions[roleName][permId] = {};
196
+ tablePermissions[roleName][permId].databasesRegex = env[key];
197
+ }
198
+ if (tableSchemasMatch) {
199
+ const [, roleName, permId] = tableSchemasMatch;
200
+ if (!tablePermissions[roleName]) tablePermissions[roleName] = {};
201
+ if (!tablePermissions[roleName][permId]) tablePermissions[roleName][permId] = {};
202
+ tablePermissions[roleName][permId].schemas = env[key];
203
+ }
204
+ if (tableSchemasRegexMatch) {
205
+ const [, roleName, permId] = tableSchemasRegexMatch;
206
+ if (!tablePermissions[roleName]) tablePermissions[roleName] = {};
207
+ if (!tablePermissions[roleName][permId]) tablePermissions[roleName][permId] = {};
208
+ tablePermissions[roleName][permId].schemasRegex = env[key];
209
+ }
210
+ if (tableTablesMatch) {
211
+ const [, roleName, permId] = tableTablesMatch;
212
+ if (!tablePermissions[roleName]) tablePermissions[roleName] = {};
213
+ if (!tablePermissions[roleName][permId]) tablePermissions[roleName][permId] = {};
214
+ tablePermissions[roleName][permId].tables = env[key]?.replace(/\|/g, '\n');
215
+ }
216
+ if (tableTablesRegexMatch) {
217
+ const [, roleName, permId] = tableTablesRegexMatch;
218
+ if (!tablePermissions[roleName]) tablePermissions[roleName] = {};
219
+ if (!tablePermissions[roleName][permId]) tablePermissions[roleName][permId] = {};
220
+ tablePermissions[roleName][permId].tablesRegex = env[key];
221
+ }
222
+ if (tablePermMatch) {
223
+ const [, roleName, permId] = tablePermMatch;
224
+ if (!tablePermissions[roleName]) tablePermissions[roleName] = {};
225
+ if (!tablePermissions[roleName][permId]) tablePermissions[roleName][permId] = {};
226
+ tablePermissions[roleName][permId].permission = env[key];
227
+ }
228
+ if (tableScopeMatch) {
229
+ const [, roleName, permId] = tableScopeMatch;
230
+ if (!tablePermissions[roleName]) tablePermissions[roleName] = {};
231
+ if (!tablePermissions[roleName][permId]) tablePermissions[roleName][permId] = {};
232
+ tablePermissions[roleName][permId].scope = env[key];
233
+ }
234
+ }
235
+
236
+ // Second pass: process roles, connections, and permissions
237
+ for (const key in env) {
238
+ const connMatch = key.match(connectionsRegex);
239
+ const permMatch = key.match(permissionsRegex);
240
+ if (connMatch) {
241
+ const roleName = connMatch[1];
242
+ let role = roles.find(r => r.name === roleName);
243
+ if (!role) {
244
+ role = {
245
+ id: roles.length + 1,
246
+ name: roleName,
247
+ import_source_id: -1,
248
+ };
249
+ roles.push(role);
250
+ }
251
+ const connIds = env[key]
252
+ .split(',')
253
+ .map(id => id.trim())
254
+ .filter(id => id.length > 0);
255
+ for (const connId of connIds) {
256
+ const dbId = connectionEnvIdToDbId[connId];
257
+ if (dbId) {
258
+ role_connections.push({
259
+ role_id: role.id,
260
+ connection_id: dbId,
261
+ import_source_id: -1,
262
+ });
263
+ }
264
+ }
265
+ }
266
+ if (permMatch) {
267
+ const roleName = permMatch[1];
268
+ let role = roles.find(r => r.name === roleName);
269
+ if (!role) {
270
+ role = {
271
+ id: roles.length + 1,
272
+ name: roleName,
273
+ import_source_id: -1,
274
+ };
275
+ roles.push(role);
276
+ }
277
+ const permissions = env[key]
278
+ .split(',')
279
+ .map(p => p.trim())
280
+ .filter(p => p.length > 0);
281
+ for (const permission of permissions) {
282
+ role_permissions.push({
283
+ role_id: role.id,
284
+ permission,
285
+ import_source_id: -1,
286
+ });
287
+ }
288
+ }
289
+ }
290
+
291
+ // Process database permissions
292
+ for (const roleName in databasePermissions) {
293
+ let role = roles.find(r => r.name === roleName);
294
+ if (!role) {
295
+ role = {
296
+ id: roles.length + 1,
297
+ name: roleName,
298
+ import_source_id: -1,
299
+ };
300
+ roles.push(role);
301
+ }
302
+
303
+ for (const permId in databasePermissions[roleName]) {
304
+ const perm = databasePermissions[roleName][permId];
305
+ if (perm.connection && perm.permission) {
306
+ const dbId = connectionEnvIdToDbId[perm.connection];
307
+ const permissionId = databasePermissionMap[perm.permission];
308
+ if (dbId && permissionId) {
309
+ role_databases.push({
310
+ role_id: role.id,
311
+ connection_id: dbId,
312
+ database_names_list: perm.databases || null,
313
+ database_names_regex: perm.databasesRegex || null,
314
+ database_permission_role_id: permissionId,
315
+ id_original: permId,
316
+ import_source_id: -1,
317
+ });
318
+ }
319
+ }
320
+ }
321
+ }
322
+
323
+ // Process table permissions
324
+ for (const roleName in tablePermissions) {
325
+ let role = roles.find(r => r.name === roleName);
326
+ if (!role) {
327
+ role = {
328
+ id: roles.length + 1,
329
+ name: roleName,
330
+ import_source_id: -1,
331
+ };
332
+ roles.push(role);
333
+ }
334
+
335
+ for (const permId in tablePermissions[roleName]) {
336
+ const perm = tablePermissions[roleName][permId];
337
+ if (perm.connection && perm.permission) {
338
+ const dbId = connectionEnvIdToDbId[perm.connection];
339
+ const permissionId = tablePermissionMap[perm.permission];
340
+ const scopeId = tableScopeMap[perm.scope || 'all_objects'];
341
+ if (dbId && permissionId && scopeId) {
342
+ role_tables.push({
343
+ role_id: role.id,
344
+ connection_id: dbId,
345
+ database_names_list: perm.databases || null,
346
+ database_names_regex: perm.databasesRegex || null,
347
+ schema_names_list: perm.schemas || null,
348
+ schema_names_regex: perm.schemasRegex || null,
349
+ table_names_list: perm.tables || null,
350
+ table_names_regex: perm.tablesRegex || null,
351
+ table_permission_role_id: permissionId,
352
+ table_permission_scope_id: scopeId,
353
+ id_original: permId,
354
+ import_source_id: -1,
355
+ });
356
+ }
357
+ }
358
+ }
359
+ }
360
+
361
+ if (connections.length == 0 && roles.length == 0) {
362
+ return null;
363
+ }
364
+
365
+ return {
366
+ connections,
367
+ roles,
368
+ role_connections,
369
+ role_permissions,
370
+ role_databases,
371
+ role_tables,
372
+ };
373
+ }
374
+
375
+ function createStorageFromEnvReplicatorItems(importEntities) {
376
+ return [
377
+ {
378
+ name: 'connections',
379
+ findExisting: true,
380
+ createNew: true,
381
+ updateExisting: true,
382
+ matchColumns: ['id_original', 'import_source_id'],
383
+ deleteMissing: true,
384
+ deleteRestrictionColumns: ['import_source_id'],
385
+ skipUpdateColumns: ['conid'],
386
+ jsonArray: importEntities.connections,
387
+ },
388
+ {
389
+ name: 'roles',
390
+ findExisting: true,
391
+ createNew: true,
392
+ updateExisting: true,
393
+ matchColumns: ['name', 'import_source_id'],
394
+ deleteMissing: true,
395
+ deleteRestrictionColumns: ['import_source_id'],
396
+ jsonArray: importEntities.roles,
397
+ },
398
+ {
399
+ name: 'role_connections',
400
+ findExisting: true,
401
+ createNew: true,
402
+ updateExisting: false,
403
+ deleteMissing: true,
404
+ matchColumns: ['role_id', 'connection_id', 'import_source_id'],
405
+ jsonArray: importEntities.role_connections,
406
+ deleteRestrictionColumns: ['import_source_id'],
407
+ },
408
+ {
409
+ name: 'role_permissions',
410
+ findExisting: true,
411
+ createNew: true,
412
+ updateExisting: false,
413
+ deleteMissing: true,
414
+ matchColumns: ['role_id', 'permission', 'import_source_id'],
415
+ jsonArray: importEntities.role_permissions,
416
+ deleteRestrictionColumns: ['import_source_id'],
417
+ },
418
+ {
419
+ name: 'role_databases',
420
+ findExisting: true,
421
+ createNew: true,
422
+ updateExisting: true,
423
+ deleteMissing: true,
424
+ matchColumns: ['role_id', 'id_original', 'import_source_id'],
425
+ jsonArray: importEntities.role_databases,
426
+ deleteRestrictionColumns: ['import_source_id'],
427
+ },
428
+ {
429
+ name: 'role_tables',
430
+ findExisting: true,
431
+ createNew: true,
432
+ updateExisting: true,
433
+ deleteMissing: true,
434
+ matchColumns: ['role_id', 'id_original', 'import_source_id'],
435
+ jsonArray: importEntities.role_tables,
436
+ deleteRestrictionColumns: ['import_source_id'],
437
+ },
438
+ ];
439
+ }
440
+
441
+ module.exports = {
442
+ extractConnectionsFromEnv,
443
+ extractImportEntitiesFromEnv,
444
+ createStorageFromEnvReplicatorItems,
445
+ };