dbgate-api-premium 6.5.3 → 6.5.5

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.
@@ -41,6 +41,7 @@ const { decryptConnection } = require('../utility/crypting');
41
41
  const { getSshTunnel } = require('../utility/sshTunnel');
42
42
  const sessions = require('./sessions');
43
43
  const jsldata = require('./jsldata');
44
+ const { sendToAuditLog } = require('../utility/auditlog');
44
45
 
45
46
  const logger = getLogger('databaseConnections');
46
47
 
@@ -83,8 +84,11 @@ module.exports = {
83
84
  }
84
85
  },
85
86
  handle_response(conid, database, { msgid, ...response }) {
86
- const [resolve, reject] = this.requests[msgid];
87
+ const [resolve, reject, additionalData] = this.requests[msgid];
87
88
  resolve(response);
89
+ if (additionalData?.auditLogger) {
90
+ additionalData?.auditLogger(response);
91
+ }
88
92
  delete this.requests[msgid];
89
93
  },
90
94
  handle_status(conid, database, { status }) {
@@ -215,10 +219,10 @@ module.exports = {
215
219
  },
216
220
 
217
221
  /** @param {import('dbgate-types').OpenedDatabaseConnection} conn */
218
- sendRequest(conn, message) {
222
+ sendRequest(conn, message, additionalData = {}) {
219
223
  const msgid = crypto.randomUUID();
220
224
  const promise = new Promise((resolve, reject) => {
221
- this.requests[msgid] = [resolve, reject];
225
+ this.requests[msgid] = [resolve, reject, additionalData];
222
226
  try {
223
227
  conn.subprocess.send({ msgid, ...message });
224
228
  } catch (err) {
@@ -242,18 +246,57 @@ module.exports = {
242
246
  },
243
247
 
244
248
  sqlSelect_meta: true,
245
- async sqlSelect({ conid, database, select }, req) {
249
+ async sqlSelect({ conid, database, select, auditLogSessionGroup }, req) {
246
250
  testConnectionPermission(conid, req);
247
251
  const opened = await this.ensureOpened(conid, database);
248
- const res = await this.sendRequest(opened, { msgtype: 'sqlSelect', select });
252
+ const res = await this.sendRequest(
253
+ opened,
254
+ { msgtype: 'sqlSelect', select },
255
+ {
256
+ auditLogger:
257
+ auditLogSessionGroup && select?.from?.name?.pureName
258
+ ? response => {
259
+ sendToAuditLog(req, {
260
+ category: 'dbop',
261
+ component: 'DatabaseConnectionsController',
262
+ event: 'sql.select',
263
+ action: 'select',
264
+ severity: 'info',
265
+ conid,
266
+ database,
267
+ schemaName: select?.from?.name?.schemaName,
268
+ pureName: select?.from?.name?.pureName,
269
+ sumint1: response?.rows?.length,
270
+ sessionParam: `${conid}::${database}::${select?.from?.name?.schemaName || '0'}::${
271
+ select?.from?.name?.pureName
272
+ }`,
273
+ sessionGroup: auditLogSessionGroup,
274
+ message: `Loaded table data from ${select?.from?.name?.pureName}`,
275
+ });
276
+ }
277
+ : null,
278
+ }
279
+ );
249
280
  return res;
250
281
  },
251
282
 
252
283
  runScript_meta: true,
253
- async runScript({ conid, database, sql, useTransaction }, req) {
284
+ async runScript({ conid, database, sql, useTransaction, logMessage }, req) {
254
285
  testConnectionPermission(conid, req);
255
286
  logger.info({ conid, database, sql }, 'Processing script');
256
287
  const opened = await this.ensureOpened(conid, database);
288
+ sendToAuditLog(req, {
289
+ category: 'dbop',
290
+ component: 'DatabaseConnectionsController',
291
+ event: 'sql.runscript',
292
+ action: 'runscript',
293
+ severity: 'info',
294
+ conid,
295
+ database,
296
+ detail: sql,
297
+ message: logMessage || `Running SQL script`,
298
+ });
299
+
257
300
  const res = await this.sendRequest(opened, { msgtype: 'runScript', sql, useTransaction });
258
301
  return res;
259
302
  },
@@ -262,16 +305,53 @@ module.exports = {
262
305
  async runOperation({ conid, database, operation, useTransaction }, req) {
263
306
  testConnectionPermission(conid, req);
264
307
  logger.info({ conid, database, operation }, 'Processing operation');
308
+
309
+ sendToAuditLog(req, {
310
+ category: 'dbop',
311
+ component: 'DatabaseConnectionsController',
312
+ event: 'sql.runoperation',
313
+ action: operation.type,
314
+ severity: 'info',
315
+ conid,
316
+ database,
317
+ detail: operation,
318
+ message: `Running DB operation: ${operation.type}`,
319
+ });
320
+
265
321
  const opened = await this.ensureOpened(conid, database);
266
322
  const res = await this.sendRequest(opened, { msgtype: 'runOperation', operation, useTransaction });
267
323
  return res;
268
324
  },
269
325
 
270
326
  collectionData_meta: true,
271
- async collectionData({ conid, database, options }, req) {
327
+ async collectionData({ conid, database, options, auditLogSessionGroup }, req) {
272
328
  testConnectionPermission(conid, req);
273
329
  const opened = await this.ensureOpened(conid, database);
274
- const res = await this.sendRequest(opened, { msgtype: 'collectionData', options });
330
+ const res = await this.sendRequest(
331
+ opened,
332
+ { msgtype: 'collectionData', options },
333
+ {
334
+ auditLogger:
335
+ auditLogSessionGroup && options?.pureName
336
+ ? response => {
337
+ sendToAuditLog(req, {
338
+ category: 'dbop',
339
+ component: 'DatabaseConnectionsController',
340
+ event: 'nosql.collectionData',
341
+ action: 'select',
342
+ severity: 'info',
343
+ conid,
344
+ database,
345
+ pureName: options?.pureName,
346
+ sumint1: response?.result?.rows?.length,
347
+ sessionParam: `${conid}::${database}::${options?.pureName}`,
348
+ sessionGroup: auditLogSessionGroup,
349
+ message: `Loaded collection data ${options?.pureName}`,
350
+ });
351
+ }
352
+ : null,
353
+ }
354
+ );
275
355
  return res.result || null;
276
356
  },
277
357
 
@@ -492,6 +572,20 @@ module.exports = {
492
572
  }
493
573
 
494
574
  const opened = await this.ensureOpened(conid, database);
575
+
576
+ sendToAuditLog(req, {
577
+ category: 'dbop',
578
+ component: 'DatabaseConnectionsController',
579
+ action: 'structure',
580
+ event: 'dbStructure.get',
581
+ severity: 'info',
582
+ conid,
583
+ database,
584
+ sessionParam: `${conid}::${database}`,
585
+ sessionGroup: 'getStructure',
586
+ message: `Loaded database structure for ${database}`,
587
+ });
588
+
495
589
  return opened.structure;
496
590
  // const existing = this.opened.find((x) => x.conid == conid && x.database == database);
497
591
  // if (existing) return existing.status;
@@ -203,10 +203,10 @@ module.exports = {
203
203
  },
204
204
 
205
205
  exportChart_meta: true,
206
- async exportChart({ filePath, title, config, image }) {
206
+ async exportChart({ filePath, title, config, image, plugins }) {
207
207
  const fileName = path.parse(filePath).base;
208
208
  const imageFile = fileName.replace('.html', '-preview.png');
209
- const html = getChartExport(title, config, imageFile);
209
+ const html = getChartExport(title, config, imageFile, plugins);
210
210
  await fs.writeFile(filePath, html);
211
211
  if (image) {
212
212
  const index = image.indexOf('base64,');
@@ -20,6 +20,7 @@ const { handleProcessCommunication } = require('../utility/processComm');
20
20
  const processArgs = require('../utility/processArgs');
21
21
  const platformInfo = require('../utility/platformInfo');
22
22
  const { checkSecureDirectories, checkSecureDirectoriesInScript } = require('../utility/security');
23
+ const { sendToAuditLog, logJsonRunnerScript } = require('../utility/auditlog');
23
24
  const logger = getLogger('runners');
24
25
 
25
26
  function extractPlugins(script) {
@@ -270,7 +271,7 @@ module.exports = {
270
271
  },
271
272
 
272
273
  start_meta: true,
273
- async start({ script }) {
274
+ async start({ script }, req) {
274
275
  const runid = crypto.randomUUID();
275
276
 
276
277
  if (script.type == 'json') {
@@ -280,14 +281,36 @@ module.exports = {
280
281
  }
281
282
  }
282
283
 
284
+ logJsonRunnerScript(req, script);
285
+
283
286
  const js = await jsonScriptToJavascript(script);
284
287
  return this.startCore(runid, scriptTemplate(js, false));
285
288
  }
286
289
 
287
290
  if (!platformInfo.allowShellScripting) {
291
+ sendToAuditLog(req, {
292
+ category: 'shell',
293
+ component: 'RunnersController',
294
+ event: 'script.runFailed',
295
+ action: 'script',
296
+ severity: 'warn',
297
+ detail: script,
298
+ message: 'Scripts are not allowed',
299
+ });
300
+
288
301
  return { errorMessage: 'Shell scripting is not allowed' };
289
302
  }
290
303
 
304
+ sendToAuditLog(req, {
305
+ category: 'shell',
306
+ component: 'RunnersController',
307
+ event: 'script.run.shell',
308
+ action: 'script',
309
+ severity: 'info',
310
+ detail: script,
311
+ message: 'Running JS script',
312
+ });
313
+
291
314
  return this.startCore(runid, scriptTemplate(script, false));
292
315
  },
293
316
 
@@ -12,6 +12,7 @@ const { testConnectionPermission } = require('../utility/hasPermission');
12
12
  const { MissingCredentialsError } = require('../utility/exceptions');
13
13
  const pipeForkLogs = require('../utility/pipeForkLogs');
14
14
  const { getLogger, extractErrorLogData } = require('dbgate-tools');
15
+ const { sendToAuditLog } = require('../utility/auditlog');
15
16
 
16
17
  const logger = getLogger('serverConnection');
17
18
 
@@ -145,6 +146,17 @@ module.exports = {
145
146
  if (conid == '__model') return [];
146
147
  testConnectionPermission(conid, req);
147
148
  const opened = await this.ensureOpened(conid);
149
+ sendToAuditLog(req, {
150
+ category: 'serverop',
151
+ component: 'ServerConnectionsController',
152
+ action: 'listDatabases',
153
+ event: 'databases.list',
154
+ severity: 'info',
155
+ conid,
156
+ sessionParam: `${conid}`,
157
+ sessionGroup: 'listDatabases',
158
+ message: `Loaded databases for connection`,
159
+ });
148
160
  return opened?.databases ?? [];
149
161
  },
150
162
 
@@ -11,6 +11,7 @@ const { appdir } = require('../utility/directories');
11
11
  const { getLogger, extractErrorLogData } = require('dbgate-tools');
12
12
  const pipeForkLogs = require('../utility/pipeForkLogs');
13
13
  const config = require('./config');
14
+ const { sendToAuditLog } = require('../utility/auditlog');
14
15
 
15
16
  const logger = getLogger('sessions');
16
17
 
@@ -146,12 +147,24 @@ module.exports = {
146
147
  },
147
148
 
148
149
  executeQuery_meta: true,
149
- async executeQuery({ sesid, sql, autoCommit, autoDetectCharts, limitRows, frontMatter }) {
150
+ async executeQuery({ sesid, sql, autoCommit, autoDetectCharts, limitRows, frontMatter }, req) {
150
151
  const session = this.opened.find(x => x.sesid == sesid);
151
152
  if (!session) {
152
153
  throw new Error('Invalid session');
153
154
  }
154
155
 
156
+ sendToAuditLog(req, {
157
+ category: 'dbop',
158
+ component: 'SessionController',
159
+ action: 'executeQuery',
160
+ event: 'query.execute',
161
+ severity: 'info',
162
+ detail: sql,
163
+ conid: session.conid,
164
+ database: session.database,
165
+ message: 'Executing query',
166
+ });
167
+
155
168
  logger.info({ sesid, sql }, 'Processing query');
156
169
  this.dispatchMessage(sesid, 'Query execution started');
157
170
  session.subprocess.send({
@@ -18,13 +18,14 @@ const {
18
18
  const { hasPermission } = require('../utility/hasPermission');
19
19
  const { changeSetToSql, removeSchemaFromChangeSet } = require('dbgate-datalib');
20
20
  const storageModel = require('../storageModel');
21
- const { dumpSqlCommand } = require('dbgate-sqltree');
21
+ const { dumpSqlCommand, dumpSqlSelect } = require('dbgate-sqltree');
22
22
  const {
23
23
  runCommandOnDriver,
24
24
  getLogger,
25
25
  runQueryFmt,
26
26
  getPredefinedPermissions,
27
27
  runQueryOnDriver,
28
+ safeJsonParse,
28
29
  } = require('dbgate-tools');
29
30
  const socket = require('../utility/socket');
30
31
  const { obtainRefreshedLicense } = require('../utility/authProxy');
@@ -38,6 +39,8 @@ const {
38
39
  const crypto = require('crypto');
39
40
  const dataReplicator = require('../shell/dataReplicator');
40
41
  const storageReplicatorItems = require('../utility/storageReplicatorItems');
42
+ const { sendToAuditLog } = require('../utility/auditlog');
43
+ const { markUserAsActive } = require('../utility/loginchecker');
41
44
 
42
45
  const logger = getLogger('storage');
43
46
 
@@ -226,9 +229,9 @@ module.exports = {
226
229
  conn,
227
230
  'update ~auth_methods set ~name=%v, ~is_disabled=%v, ~is_default = %v, ~is_collapsed = %v where ~id = %v',
228
231
  method.name,
229
- method.isDisabled,
230
- method.isDefault,
231
- method.isCollapsed,
232
+ method.isDisabled ? 1 : 0,
233
+ method.isDefault ? 1 : 0,
234
+ method.isCollapsed ? 1 : 0,
232
235
  method.id
233
236
  );
234
237
  } else {
@@ -618,6 +621,30 @@ module.exports = {
618
621
  return true;
619
622
  },
620
623
 
624
+ copyConnection_meta: true,
625
+ async copyConnection({ id, name }) {
626
+ const [conn, driver] = await getStorageConnection();
627
+ if (!conn) {
628
+ return null;
629
+ }
630
+
631
+ const oldConnection = await storageSelectFmt(`select * from ~connections where ~id = %v`, id);
632
+ const oldConnectionPairs = Object.entries(oldConnection[0] || {})
633
+ .filter(x => x[0] != 'id')
634
+ .map(x => (x[0] == 'displayName' ? [x[0], name] : [x[0], x[1]]))
635
+ .map(x => (x[0] == 'conid' ? [x[0], crypto.randomUUID()] : [x[0], x[1]]));
636
+ await runQueryFmt(
637
+ driver,
638
+ conn,
639
+ 'insert into ~connections (%,i) values (%,v)',
640
+ oldConnectionPairs.map(x => x[0]),
641
+ oldConnectionPairs.map(x => x[1])
642
+ );
643
+ socket.emitChanged('connection-list-changed');
644
+
645
+ return true;
646
+ },
647
+
621
648
  getRoleList_meta: true,
622
649
  async getRoleList() {
623
650
  const resp = await storageSelectFmt(`select ~roles.~id,~roles.~name from ~roles`);
@@ -699,6 +726,103 @@ module.exports = {
699
726
  return true;
700
727
  },
701
728
 
729
+ getAuditLog_meta: true,
730
+ async getAuditLog({ offset = 0, limit = 100, dateFrom = 0, dateTo = new Date().getTime(), filters = {} }) {
731
+ const [conn, driver] = await getStorageConnection();
732
+ const conditions = [
733
+ {
734
+ conditionType: 'binary',
735
+ operator: '>=',
736
+ left: { exprType: 'column', columnName: 'created' },
737
+ right: { exprType: 'value', value: dateFrom },
738
+ },
739
+ {
740
+ conditionType: 'binary',
741
+ operator: '<=',
742
+ left: { exprType: 'column', columnName: 'created' },
743
+ right: { exprType: 'value', value: dateTo },
744
+ },
745
+ ];
746
+ for (const [key, values] of Object.entries(filters)) {
747
+ if (values.length == 1 && values[0] == null) {
748
+ // @ts-ignore
749
+ conditions.push({
750
+ conditionType: 'isNull',
751
+ expr: { exprType: 'column', columnName: key },
752
+ });
753
+ continue;
754
+ }
755
+ // @ts-ignore
756
+ conditions.push({
757
+ conditionType: 'in',
758
+ expr: { exprType: 'column', columnName: key },
759
+ values,
760
+ });
761
+ }
762
+ const COLUMNS = [
763
+ 'id',
764
+ 'created',
765
+ 'user_login',
766
+ 'event',
767
+ 'conid',
768
+ 'database',
769
+ 'connection_data',
770
+ 'pure_name',
771
+ 'message',
772
+ ];
773
+ const select = {
774
+ commandType: 'select',
775
+ from: {
776
+ name: {
777
+ pureName: 'audit_log',
778
+ },
779
+ },
780
+ columns: COLUMNS.map(columnName => ({
781
+ exprType: 'column',
782
+ columnName,
783
+ })),
784
+ where: {
785
+ conditionType: 'and',
786
+ conditions,
787
+ },
788
+ range: {
789
+ limit: limit,
790
+ offset: offset,
791
+ },
792
+ orderBy: [
793
+ {
794
+ exprType: 'column',
795
+ columnName: 'id',
796
+ direction: 'desc',
797
+ },
798
+ ],
799
+ };
800
+ const dmp = driver.createDumper();
801
+ // @ts-ignore
802
+ dumpSqlSelect(dmp, select);
803
+ const resp = await driver.query(conn, dmp.s);
804
+ return resp.rows.map(x => ({
805
+ ..._.mapKeys(x, (_v, k) => _.camelCase(k)),
806
+ connectionData: x.connection_data ? safeJsonParse(x.connection_data, null) : null,
807
+ }));
808
+ },
809
+
810
+ getAuditLogDetail_meta: true,
811
+ async getAuditLogDetail({ id }) {
812
+ const [_conn, driver] = await getStorageConnection();
813
+ const res = await storageSelectFmt(
814
+ `select *
815
+ from ~audit_log
816
+ where ~audit_log.~id = %v`,
817
+ id
818
+ );
819
+ if (!res[0]) return null;
820
+ return {
821
+ ..._.mapKeys(res[0], (_v, k) => _.camelCase(k)),
822
+ connectionData: res[0].connection_data ? safeJsonParse(res[0].connection_data, null) : null,
823
+ };
824
+ },
825
+
702
826
  async getExportedDatabase() {
703
827
  const connections = await storageSelectFmt(`select * from ~connections`);
704
828
  const user_permissions = await storageSelectFmt(`select * from ~user_permissions`);
@@ -751,4 +875,14 @@ module.exports = {
751
875
  const resp = await storageSelectFmt(`select distinct ~engine from ~connections`);
752
876
  return resp.map(x => x.engine);
753
877
  },
878
+
879
+ sendAuditLog_meta: true,
880
+ async sendAuditLog(props, req) {
881
+ sendToAuditLog(req, props);
882
+ return null;
883
+ },
884
+
885
+ markUserAsActive(licenseUid) {
886
+ markUserAsActive(licenseUid);
887
+ },
754
888
  };
@@ -7,6 +7,7 @@ const {
7
7
  extractErrorLogData,
8
8
  runQueryFmt,
9
9
  runQueryOnDriver,
10
+ adaptDatabaseInfo,
10
11
  } = require('dbgate-tools');
11
12
  const _ = require('lodash');
12
13
  const logger = getLogger('storageDb');
@@ -122,7 +123,8 @@ async function getStorageConnectionCore() {
122
123
  await dbgateApi.deployDb({
123
124
  systemConnection: newConnection,
124
125
  driver: storageDriver,
125
- loadedDbModel: storageModel,
126
+ // @ts-ignore
127
+ loadedDbModel: adaptDatabaseInfo(storageModel, storageDriver),
126
128
  targetSchema: process.env.STORAGE_SCHEMA,
127
129
  });
128
130
 
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '6.5.3',
4
- buildTime: '2025-06-20T13:03:33.111Z'
3
+ version: '6.5.5',
4
+ buildTime: '2025-07-04T07:10:42.669Z'
5
5
  };
@@ -14,7 +14,7 @@ const crypto = require('crypto');
14
14
  * @param {object} options.driver - driver object. If not provided, it will be loaded from connection
15
15
  * @param {object} options.analysedStructure - analysed structure of the database. If not provided, it will be loaded
16
16
  * @param {string} options.modelFolder - folder with model files (YAML files for tables, SQL files for views, procedures, ...)
17
- * @param {import('dbgate-tools').DatabaseModelFile[]} options.loadedDbModel - loaded database model - collection of yaml and SQL files loaded into array
17
+ * @param {import('dbgate-tools').DatabaseModelFile[] | import('dbgate-types').DatabaseInfo} options.loadedDbModel - loaded database model - collection of yaml and SQL files loaded into array
18
18
  * @param {function[]} options.modelTransforms - array of functions for transforming model
19
19
  * @param {object} options.dbdiffOptionsExtra - extra options for dbdiff
20
20
  * @param {string} options.ignoreNameRegex - regex for ignoring objects by name
@@ -23,7 +23,7 @@ const { connectUtility } = require('../utility/connectUtility');
23
23
  * @param {object} options.driver - driver object. If not provided, it will be loaded from connection
24
24
  * @param {object} options.analysedStructure - analysed structure of the database. If not provided, it will be loaded
25
25
  * @param {string} options.modelFolder - folder with model files (YAML files for tables, SQL files for views, procedures, ...)
26
- * @param {import('dbgate-tools').DatabaseModelFile[]} options.loadedDbModel - loaded database model - collection of yaml and SQL files loaded into array
26
+ * @param {import('dbgate-tools').DatabaseModelFile[] | import('dbgate-types').DatabaseInfo} options.loadedDbModel - loaded database model - collection of yaml and SQL files loaded into array
27
27
  * @param {function[]} options.modelTransforms - array of functions for transforming model
28
28
  * @param {object} options.dbdiffOptionsExtra - extra options for dbdiff
29
29
  * @param {string} options.ignoreNameRegex - regex for ignoring objects by name
@@ -3,7 +3,7 @@ const fs = require('fs-extra');
3
3
  const executeQuery = require('./executeQuery');
4
4
  const { connectUtility } = require('../utility/connectUtility');
5
5
  const requireEngineDriver = require('../utility/requireEngineDriver');
6
- const { getAlterDatabaseScript, DatabaseAnalyser, runCommandOnDriver } = require('dbgate-tools');
6
+ const { getAlterDatabaseScript, DatabaseAnalyser, runCommandOnDriver, adaptDatabaseInfo } = require('dbgate-tools');
7
7
  const importDbModel = require('../utility/importDbModel');
8
8
  const jsonLinesReader = require('./jsonLinesReader');
9
9
  const tableWriter = require('./tableWriter');
@@ -26,10 +26,7 @@ async function importDbFromFolder({ connection, systemConnection, driver, folder
26
26
  if (driver?.databaseEngineTypes?.includes('sql')) {
27
27
  const model = await importDbModel(folder);
28
28
 
29
- let modelAdapted = {
30
- ...model,
31
- tables: model.tables.map(table => driver.adaptTableInfo(table)),
32
- };
29
+ let modelAdapted = adaptDatabaseInfo(model, driver);
33
30
  for (const transform of modelTransforms || []) {
34
31
  modelAdapted = transform(modelAdapted);
35
32
  }