dbgate-api-premium 6.4.2 → 6.5.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.4.2",
4
+ "version": "6.5.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.4.2",
33
+ "dbgate-datalib": "^6.5.0",
34
34
  "dbgate-query-splitter": "^4.11.5",
35
- "dbgate-sqltree": "^6.4.2",
36
- "dbgate-tools": "^6.4.2",
35
+ "dbgate-sqltree": "^6.5.0",
36
+ "dbgate-tools": "^6.5.0",
37
37
  "debug": "^4.3.4",
38
38
  "diff": "^5.0.0",
39
39
  "diff2html": "^3.4.13",
@@ -85,7 +85,7 @@
85
85
  "devDependencies": {
86
86
  "@types/fs-extra": "^9.0.11",
87
87
  "@types/lodash": "^4.14.149",
88
- "dbgate-types": "^6.4.2",
88
+ "dbgate-types": "^6.5.0",
89
89
  "env-cmd": "^10.1.0",
90
90
  "jsdoc-to-markdown": "^9.0.5",
91
91
  "node-loader": "^1.0.2",
@@ -13,6 +13,8 @@ const {
13
13
  } = require('../auth/authProvider');
14
14
  const storage = require('./storage');
15
15
  const { decryptPasswordString } = require('../utility/crypting');
16
+ const { createDbGateIdentitySession, startCloudTokenChecking } = require('../utility/cloudIntf');
17
+ const socket = require('../utility/socket');
16
18
 
17
19
  const logger = getLogger('auth');
18
20
 
@@ -135,5 +137,14 @@ module.exports = {
135
137
  return getAuthProviderById(amoid).redirect(params);
136
138
  },
137
139
 
140
+ createCloudLoginSession_meta: true,
141
+ async createCloudLoginSession({ client }) {
142
+ const res = await createDbGateIdentitySession(client);
143
+ startCloudTokenChecking(res.sid, tokenHolder => {
144
+ socket.emit('got-cloud-token', tokenHolder);
145
+ });
146
+ return res;
147
+ },
148
+
138
149
  authMiddleware,
139
150
  };
@@ -0,0 +1,261 @@
1
+ const {
2
+ getPublicCloudFiles,
3
+ getPublicFileData,
4
+ refreshPublicFiles,
5
+ callCloudApiGet,
6
+ callCloudApiPost,
7
+ getCloudFolderEncryptor,
8
+ getCloudContent,
9
+ putCloudContent,
10
+ removeCloudCachedConnection,
11
+ } = require('../utility/cloudIntf');
12
+ const connections = require('./connections');
13
+ const socket = require('../utility/socket');
14
+ const { recryptConnection, getInternalEncryptor, encryptConnection } = require('../utility/crypting');
15
+ const { getConnectionLabel, getLogger, extractErrorLogData } = require('dbgate-tools');
16
+ const logger = getLogger('cloud');
17
+ const _ = require('lodash');
18
+ const fs = require('fs-extra');
19
+
20
+ module.exports = {
21
+ publicFiles_meta: true,
22
+ async publicFiles() {
23
+ const res = await getPublicCloudFiles();
24
+ return res;
25
+ },
26
+
27
+ publicFileData_meta: true,
28
+ async publicFileData({ path }) {
29
+ const res = getPublicFileData(path);
30
+ return res;
31
+ },
32
+
33
+ refreshPublicFiles_meta: true,
34
+ async refreshPublicFiles({ isRefresh }) {
35
+ await refreshPublicFiles(isRefresh);
36
+ return {
37
+ status: 'ok',
38
+ };
39
+ },
40
+
41
+ contentList_meta: true,
42
+ async contentList() {
43
+ try {
44
+ const resp = await callCloudApiGet('content-list');
45
+ return resp;
46
+ } catch (err) {
47
+ logger.error(extractErrorLogData(err), 'Error getting cloud content list');
48
+
49
+ return [];
50
+ }
51
+ },
52
+
53
+ getContent_meta: true,
54
+ async getContent({ folid, cntid }) {
55
+ const resp = await getCloudContent(folid, cntid);
56
+ return resp;
57
+ },
58
+
59
+ putContent_meta: true,
60
+ async putContent({ folid, cntid, content, name, type }) {
61
+ const resp = await putCloudContent(folid, cntid, content, name, type, {});
62
+ socket.emitChanged('cloud-content-changed');
63
+ socket.emit('cloud-content-updated');
64
+ return resp;
65
+ },
66
+
67
+ createFolder_meta: true,
68
+ async createFolder({ name }) {
69
+ const resp = await callCloudApiPost(`folders/create`, { name });
70
+ socket.emitChanged('cloud-content-changed');
71
+ socket.emit('cloud-content-updated');
72
+ return resp;
73
+ },
74
+
75
+ grantFolder_meta: true,
76
+ async grantFolder({ inviteLink }) {
77
+ const m = inviteLink.match(/^dbgate\:\/\/folder\/v1\/([a-zA-Z0-9]+)\?mode=(read|write|admin)$/);
78
+ if (!m) {
79
+ throw new Error('Invalid invite link format');
80
+ }
81
+ const invite = m[1];
82
+ const mode = m[2];
83
+
84
+ const resp = await callCloudApiPost(`folders/grant/${mode}`, { invite });
85
+ socket.emitChanged('cloud-content-changed');
86
+ socket.emit('cloud-content-updated');
87
+ return resp;
88
+ },
89
+
90
+ renameFolder_meta: true,
91
+ async renameFolder({ folid, name }) {
92
+ const resp = await callCloudApiPost(`folders/rename`, { folid, name });
93
+ socket.emitChanged('cloud-content-changed');
94
+ socket.emit('cloud-content-updated');
95
+ return resp;
96
+ },
97
+
98
+ deleteFolder_meta: true,
99
+ async deleteFolder({ folid }) {
100
+ const resp = await callCloudApiPost(`folders/delete`, { folid });
101
+ socket.emitChanged('cloud-content-changed');
102
+ socket.emit('cloud-content-updated');
103
+ return resp;
104
+ },
105
+
106
+ getInviteToken_meta: true,
107
+ async getInviteToken({ folid, role }) {
108
+ const resp = await callCloudApiGet(`invite-token/${folid}/${role}`);
109
+ return resp;
110
+ },
111
+
112
+ refreshContent_meta: true,
113
+ async refreshContent() {
114
+ socket.emitChanged('cloud-content-changed');
115
+ socket.emit('cloud-content-updated');
116
+ return {
117
+ status: 'ok',
118
+ };
119
+ },
120
+
121
+ copyConnectionCloud_meta: true,
122
+ async copyConnectionCloud({ conid, folid }) {
123
+ const conn = await connections.getCore({ conid });
124
+ const folderEncryptor = await getCloudFolderEncryptor(folid);
125
+ const recryptedConn = recryptConnection(conn, getInternalEncryptor(), folderEncryptor);
126
+ const connToSend = _.omit(recryptedConn, ['_id']);
127
+ const resp = await putCloudContent(
128
+ folid,
129
+ undefined,
130
+ JSON.stringify(connToSend),
131
+ getConnectionLabel(conn),
132
+ 'connection',
133
+ {
134
+ connectionColor: conn.connectionColor,
135
+ connectionEngine: conn.engine,
136
+ }
137
+ );
138
+ return resp;
139
+ },
140
+
141
+ saveConnection_meta: true,
142
+ async saveConnection({ folid, connection }) {
143
+ let cntid = undefined;
144
+ if (connection._id) {
145
+ const m = connection._id.match(/^cloud\:\/\/(.+)\/(.+)$/);
146
+ if (!m) {
147
+ throw new Error('Invalid cloud connection ID format');
148
+ }
149
+ folid = m[1];
150
+ cntid = m[2];
151
+ }
152
+
153
+ if (!folid) {
154
+ throw new Error('Missing cloud folder ID');
155
+ }
156
+
157
+ const folderEncryptor = await getCloudFolderEncryptor(folid);
158
+ const recryptedConn = encryptConnection(connection, folderEncryptor);
159
+ const resp = await putCloudContent(
160
+ folid,
161
+ cntid,
162
+ JSON.stringify(recryptedConn),
163
+ getConnectionLabel(recryptedConn),
164
+ 'connection',
165
+ {
166
+ connectionColor: connection.connectionColor,
167
+ connectionEngine: connection.engine,
168
+ }
169
+ );
170
+
171
+ if (resp.apiErrorMessage) {
172
+ return resp;
173
+ }
174
+
175
+ removeCloudCachedConnection(folid, resp.cntid);
176
+ cntid = resp.cntid;
177
+ socket.emitChanged('cloud-content-changed');
178
+ socket.emit('cloud-content-updated');
179
+ return {
180
+ ...recryptedConn,
181
+ _id: `cloud://${folid}/${cntid}`,
182
+ };
183
+ },
184
+
185
+ duplicateConnection_meta: true,
186
+ async duplicateConnection({ conid }) {
187
+ const m = conid.match(/^cloud\:\/\/(.+)\/(.+)$/);
188
+ if (!m) {
189
+ throw new Error('Invalid cloud connection ID format');
190
+ }
191
+ const folid = m[1];
192
+ const cntid = m[2];
193
+ const respGet = await getCloudContent(folid, cntid);
194
+ const conn = JSON.parse(respGet.content);
195
+ const conn2 = {
196
+ ...conn,
197
+ displayName: getConnectionLabel(conn) + ' - copy',
198
+ };
199
+ const respPut = await putCloudContent(folid, undefined, JSON.stringify(conn2), conn2.displayName, 'connection', {
200
+ connectionColor: conn.connectionColor,
201
+ connectionEngine: conn.engine,
202
+ });
203
+ return respPut;
204
+ },
205
+
206
+ deleteConnection_meta: true,
207
+ async deleteConnection({ conid }) {
208
+ const m = conid.match(/^cloud\:\/\/(.+)\/(.+)$/);
209
+ if (!m) {
210
+ throw new Error('Invalid cloud connection ID format');
211
+ }
212
+ const folid = m[1];
213
+ const cntid = m[2];
214
+ const resp = await callCloudApiPost(`content/delete/${folid}/${cntid}`);
215
+ socket.emitChanged('cloud-content-changed');
216
+ socket.emit('cloud-content-updated');
217
+ return resp;
218
+ },
219
+
220
+ deleteContent_meta: true,
221
+ async deleteContent({ folid, cntid }) {
222
+ const resp = await callCloudApiPost(`content/delete/${folid}/${cntid}`);
223
+ socket.emitChanged('cloud-content-changed');
224
+ socket.emit('cloud-content-updated');
225
+ return resp;
226
+ },
227
+
228
+ renameContent_meta: true,
229
+ async renameContent({ folid, cntid, name }) {
230
+ const resp = await callCloudApiPost(`content/rename/${folid}/${cntid}`, { name });
231
+ socket.emitChanged('cloud-content-changed');
232
+ socket.emit('cloud-content-updated');
233
+ return resp;
234
+ },
235
+
236
+ saveFile_meta: true,
237
+ async saveFile({ folid, cntid, fileName, data, contentFolder, format }) {
238
+ const resp = await putCloudContent(folid, cntid, data, fileName, 'file', { contentFolder, contentType: format });
239
+ socket.emitChanged('cloud-content-changed');
240
+ socket.emit('cloud-content-updated');
241
+ return resp;
242
+ },
243
+
244
+ copyFile_meta: true,
245
+ async copyFile({ folid, cntid, name }) {
246
+ const resp = await callCloudApiPost(`content/duplicate/${folid}/${cntid}`, { name });
247
+ socket.emitChanged('cloud-content-changed');
248
+ socket.emit('cloud-content-updated');
249
+ return resp;
250
+ },
251
+
252
+ exportFile_meta: true,
253
+ async exportFile({ folid, cntid, filePath }, req) {
254
+ const { content } = await getCloudContent(folid, cntid);
255
+ if (!content) {
256
+ throw new Error('File not found');
257
+ }
258
+ await fs.writeFile(filePath, content);
259
+ return true;
260
+ },
261
+ };
@@ -116,6 +116,7 @@ module.exports = {
116
116
  processArgs.runE2eTests ? 'connections-e2etests.jsonl' : 'connections.jsonl'
117
117
  ),
118
118
  supportCloudAutoUpgrade: !!process.env.CLOUD_UPGRADE_FILE,
119
+ allowPrivateCloud: platformInfo.isElectron || !!process.env.ALLOW_DBGATE_PRIVATE_CLOUD,
119
120
  ...currentVersion,
120
121
  };
121
122
 
@@ -199,7 +200,7 @@ module.exports = {
199
200
 
200
201
  saveLicenseKey_meta: true,
201
202
  async saveLicenseKey({ licenseKey }) {
202
- const decoded = jwt.decode(licenseKey);
203
+ const decoded = jwt.decode(licenseKey?.trim());
203
204
  if (!decoded) {
204
205
  return {
205
206
  status: 'error',
@@ -239,6 +239,19 @@ module.exports = {
239
239
  return (await this.datastore.find()).filter(x => connectionHasPermission(x, req));
240
240
  },
241
241
 
242
+ async getUsedEngines() {
243
+ const storage = require('./storage');
244
+
245
+ const storageEngines = await storage.getUsedEngines();
246
+ if (storageEngines) {
247
+ return storageEngines;
248
+ }
249
+ if (portalConnections) {
250
+ return _.uniq(_.compact(portalConnections.map(x => x.engine)));
251
+ }
252
+ return _.uniq((await this.datastore.find()).map(x => x.engine));
253
+ },
254
+
242
255
  test_meta: true,
243
256
  test({ connection, requestDbList = false }) {
244
257
  const subprocess = fork(
@@ -410,6 +423,13 @@ module.exports = {
410
423
  return volatile;
411
424
  }
412
425
 
426
+ const cloudMatch = conid.match(/^cloud\:\/\/(.+)\/(.+)$/);
427
+ if (cloudMatch) {
428
+ const { loadCachedCloudConnection } = require('../utility/cloudIntf');
429
+ const conn = await loadCachedCloudConnection(cloudMatch[1], cloudMatch[2]);
430
+ return conn;
431
+ }
432
+
413
433
  const storage = require('./storage');
414
434
 
415
435
  const storageConnection = await storage.getConnection({ conid });
@@ -148,6 +148,9 @@ module.exports = {
148
148
  const existing = this.opened.find(x => x.conid == conid && x.database == database);
149
149
  if (existing) return existing;
150
150
  const connection = await connections.getCore({ conid });
151
+ if (!connection) {
152
+ throw new Error(`databaseConnections: Connection with conid="${conid}" not found`);
153
+ }
151
154
  if (connection.passwordMode == 'askPassword' || connection.passwordMode == 'askUser') {
152
155
  throw new MissingCredentialsError({ conid, passwordMode: connection.passwordMode });
153
156
  }
@@ -11,6 +11,8 @@ const apps = require('./apps');
11
11
  const getMapExport = require('../utility/getMapExport');
12
12
  const dbgateApi = require('../shell');
13
13
  const { getLogger } = require('dbgate-tools');
14
+ const platformInfo = require('../utility/platformInfo');
15
+ const { checkSecureFilePathsWithoutDirectory, checkSecureDirectories } = require('../utility/security');
14
16
  const logger = getLogger('files');
15
17
 
16
18
  function serialize(format, data) {
@@ -51,6 +53,9 @@ module.exports = {
51
53
  delete_meta: true,
52
54
  async delete({ folder, file }, req) {
53
55
  if (!hasPermission(`files/${folder}/write`, req)) return false;
56
+ if (!checkSecureFilePathsWithoutDirectory(folder, file)) {
57
+ return false;
58
+ }
54
59
  await fs.unlink(path.join(filesdir(), folder, file));
55
60
  socket.emitChanged(`files-changed`, { folder });
56
61
  socket.emitChanged(`all-files-changed`);
@@ -60,6 +65,9 @@ module.exports = {
60
65
  rename_meta: true,
61
66
  async rename({ folder, file, newFile }, req) {
62
67
  if (!hasPermission(`files/${folder}/write`, req)) return false;
68
+ if (!checkSecureFilePathsWithoutDirectory(folder, file, newFile)) {
69
+ return false;
70
+ }
63
71
  await fs.rename(path.join(filesdir(), folder, file), path.join(filesdir(), folder, newFile));
64
72
  socket.emitChanged(`files-changed`, { folder });
65
73
  socket.emitChanged(`all-files-changed`);
@@ -77,6 +85,9 @@ module.exports = {
77
85
 
78
86
  copy_meta: true,
79
87
  async copy({ folder, file, newFile }, req) {
88
+ if (!checkSecureFilePathsWithoutDirectory(folder, file, newFile)) {
89
+ return false;
90
+ }
80
91
  if (!hasPermission(`files/${folder}/write`, req)) return false;
81
92
  await fs.copyFile(path.join(filesdir(), folder, file), path.join(filesdir(), folder, newFile));
82
93
  socket.emitChanged(`files-changed`, { folder });
@@ -86,6 +97,10 @@ module.exports = {
86
97
 
87
98
  load_meta: true,
88
99
  async load({ folder, file, format }, req) {
100
+ if (!checkSecureFilePathsWithoutDirectory(folder, file)) {
101
+ return false;
102
+ }
103
+
89
104
  if (folder.startsWith('archive:')) {
90
105
  const text = await fs.readFile(path.join(resolveArchiveFolder(folder.substring('archive:'.length)), file), {
91
106
  encoding: 'utf-8',
@@ -105,12 +120,20 @@ module.exports = {
105
120
 
106
121
  loadFrom_meta: true,
107
122
  async loadFrom({ filePath, format }, req) {
123
+ if (!platformInfo.isElectron) {
124
+ // this is available only in electron app
125
+ return false;
126
+ }
108
127
  const text = await fs.readFile(filePath, { encoding: 'utf-8' });
109
128
  return deserialize(format, text);
110
129
  },
111
130
 
112
131
  save_meta: true,
113
132
  async save({ folder, file, data, format }, req) {
133
+ if (!checkSecureFilePathsWithoutDirectory(folder, file)) {
134
+ return false;
135
+ }
136
+
114
137
  if (folder.startsWith('archive:')) {
115
138
  if (!hasPermission(`archive/write`, req)) return false;
116
139
  const dir = resolveArchiveFolder(folder.substring('archive:'.length));
@@ -143,6 +166,11 @@ module.exports = {
143
166
 
144
167
  saveAs_meta: true,
145
168
  async saveAs({ filePath, data, format }) {
169
+ if (!platformInfo.isElectron) {
170
+ // this is available only in electron app
171
+ return false;
172
+ }
173
+
146
174
  await fs.writeFile(filePath, serialize(format, data));
147
175
  },
148
176
 
@@ -275,6 +303,11 @@ module.exports = {
275
303
 
276
304
  simpleCopy_meta: true,
277
305
  async simpleCopy({ sourceFilePath, targetFilePath }, req) {
306
+ if (!platformInfo.isElectron) {
307
+ if (!checkSecureDirectories(sourceFilePath, targetFilePath)) {
308
+ return false;
309
+ }
310
+ }
278
311
  await fs.copyFile(sourceFilePath, targetFilePath);
279
312
  return true;
280
313
  },
@@ -10,6 +10,7 @@ const requirePluginFunction = require('../utility/requirePluginFunction');
10
10
  const socket = require('../utility/socket');
11
11
  const crypto = require('crypto');
12
12
  const dbgateApi = require('../shell');
13
+ const { ChartProcessor } = require('dbgate-datalib');
13
14
 
14
15
  function readFirstLine(file) {
15
16
  return new Promise((resolve, reject) => {
@@ -302,4 +303,29 @@ module.exports = {
302
303
  await dbgateApi.download(uri, { targetFile: getJslFileName(jslid) });
303
304
  return { jslid };
304
305
  },
306
+
307
+ buildChart_meta: true,
308
+ async buildChart({ jslid, definition }) {
309
+ const datastore = new JsonLinesDatastore(getJslFileName(jslid));
310
+ const processor = new ChartProcessor(definition ? [definition] : undefined);
311
+ await datastore.enumRows(row => {
312
+ processor.addRow(row);
313
+ return true;
314
+ });
315
+ processor.finalize();
316
+ return processor.charts;
317
+ },
318
+
319
+ detectChartColumns_meta: true,
320
+ async detectChartColumns({ jslid }) {
321
+ const datastore = new JsonLinesDatastore(getJslFileName(jslid));
322
+ const processor = new ChartProcessor();
323
+ processor.autoDetectCharts = false;
324
+ await datastore.enumRows(row => {
325
+ processor.addRow(row);
326
+ return true;
327
+ });
328
+ processor.finalize();
329
+ return processor.availableColumns;
330
+ },
305
331
  };
@@ -19,6 +19,7 @@ const {
19
19
  const { handleProcessCommunication } = require('../utility/processComm');
20
20
  const processArgs = require('../utility/processArgs');
21
21
  const platformInfo = require('../utility/platformInfo');
22
+ const { checkSecureDirectories, checkSecureDirectoriesInScript } = require('../utility/security');
22
23
  const logger = getLogger('runners');
23
24
 
24
25
  function extractPlugins(script) {
@@ -273,6 +274,12 @@ module.exports = {
273
274
  const runid = crypto.randomUUID();
274
275
 
275
276
  if (script.type == 'json') {
277
+ if (!platformInfo.isElectron) {
278
+ if (!checkSecureDirectoriesInScript(script)) {
279
+ return { errorMessage: 'Unallowed directories in script' };
280
+ }
281
+ }
282
+
276
283
  const js = await jsonScriptToJavascript(script);
277
284
  return this.startCore(runid, scriptTemplate(js, false));
278
285
  }
@@ -317,6 +324,11 @@ module.exports = {
317
324
 
318
325
  loadReader_meta: true,
319
326
  async loadReader({ functionName, props }) {
327
+ if (!platformInfo.isElectron) {
328
+ if (props?.fileName && !checkSecureDirectories(props.fileName)) {
329
+ return { errorMessage: 'Unallowed file' };
330
+ }
331
+ }
320
332
  const prefix = extractShellApiPlugins(functionName)
321
333
  .map(packageName => `// @require ${packageName}\n`)
322
334
  .join('');
@@ -52,7 +52,7 @@ module.exports = {
52
52
  if (existing) return existing;
53
53
  const connection = await connections.getCore({ conid });
54
54
  if (!connection) {
55
- throw new Error(`Connection with conid="${conid}" not found`);
55
+ throw new Error(`serverConnections: Connection with conid="${conid}" not found`);
56
56
  }
57
57
  if (connection.singleDatabase) {
58
58
  return null;
@@ -83,6 +83,11 @@ module.exports = {
83
83
  jsldata.notifyChangedStats(stats);
84
84
  },
85
85
 
86
+ handle_charts(sesid, props) {
87
+ const { jslid, charts, resultIndex } = props;
88
+ socket.emit(`session-charts-${sesid}`, { jslid, resultIndex, charts });
89
+ },
90
+
86
91
  handle_initializeFile(sesid, props) {
87
92
  const { jslid } = props;
88
93
  socket.emit(`session-initialize-file-${jslid}`);
@@ -141,7 +146,7 @@ module.exports = {
141
146
  },
142
147
 
143
148
  executeQuery_meta: true,
144
- async executeQuery({ sesid, sql, autoCommit, limitRows }) {
149
+ async executeQuery({ sesid, sql, autoCommit, limitRows, frontMatter }) {
145
150
  const session = this.opened.find(x => x.sesid == sesid);
146
151
  if (!session) {
147
152
  throw new Error('Invalid session');
@@ -149,7 +154,7 @@ module.exports = {
149
154
 
150
155
  logger.info({ sesid, sql }, 'Processing query');
151
156
  this.dispatchMessage(sesid, 'Query execution started');
152
- session.subprocess.send({ msgtype: 'executeQuery', sql, autoCommit, limitRows });
157
+ session.subprocess.send({ msgtype: 'executeQuery', sql, autoCommit, limitRows, frontMatter });
153
158
 
154
159
  return { state: 'ok' };
155
160
  },
@@ -742,4 +742,13 @@ module.exports = {
742
742
  });
743
743
  socket.emitChanged('connection-list-changed');
744
744
  },
745
+
746
+ async getUsedEngines() {
747
+ if (!process.env.STORAGE_DATABASE) {
748
+ return null;
749
+ }
750
+
751
+ const resp = await storageSelectFmt(`select distinct ~engine from ~connections`);
752
+ return resp.map(x => x.engine);
753
+ },
745
754
  };
@@ -44,6 +44,10 @@ module.exports = {
44
44
  raw: true,
45
45
  },
46
46
  get(req, res) {
47
+ if (req.query.file.includes('..') || req.query.file.includes('/') || req.query.file.includes('\\')) {
48
+ res.status(400).send('Invalid file path');
49
+ return;
50
+ }
47
51
  res.sendFile(path.join(uploadsdir(), req.query.file));
48
52
  },
49
53
 
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '6.4.2',
4
- buildTime: '2025-05-14T12:55:08.993Z'
3
+ version: '6.5.0',
4
+ buildTime: '2025-06-17T08:04:29.562Z'
5
5
  };
package/src/main.js CHANGED
@@ -27,6 +27,7 @@ const plugins = require('./controllers/plugins');
27
27
  const files = require('./controllers/files');
28
28
  const scheduler = require('./controllers/scheduler');
29
29
  const queryHistory = require('./controllers/queryHistory');
30
+ const cloud = require('./controllers/cloud');
30
31
  const onFinished = require('on-finished');
31
32
  const processArgs = require('./utility/processArgs');
32
33
 
@@ -39,6 +40,7 @@ const { getDefaultAuthProvider } = require('./auth/authProvider');
39
40
  const startCloudUpgradeTimer = require('./utility/cloudUpgrade');
40
41
  const { isProApp } = require('./utility/checkLicense');
41
42
  const { getHealthStatus, getHealthStatusSprinx } = require('./utility/healthStatus');
43
+ const { startCloudFiles } = require('./utility/cloudIntf');
42
44
 
43
45
  const logger = getLogger('main');
44
46
 
@@ -200,6 +202,8 @@ function start() {
200
202
  if (process.env.CLOUD_UPGRADE_FILE) {
201
203
  startCloudUpgradeTimer();
202
204
  }
205
+
206
+ startCloudFiles();
203
207
  }
204
208
 
205
209
  function useAllControllers(app, electron) {
@@ -220,6 +224,7 @@ function useAllControllers(app, electron) {
220
224
  useController(app, electron, '/query-history', queryHistory);
221
225
  useController(app, electron, '/apps', apps);
222
226
  useController(app, electron, '/auth', auth);
227
+ useController(app, electron, '/cloud', cloud);
223
228
  }
224
229
 
225
230
  function setElectronSender(electronSender) {
@@ -28,14 +28,7 @@ function start() {
28
28
  let version = {
29
29
  version: 'Unknown',
30
30
  };
31
- try {
32
- version = await driver.getVersion(dbhan);
33
- } catch (err) {
34
- logger.error(extractErrorLogData(err), 'Error getting DB server version');
35
- version = {
36
- version: 'Unknown',
37
- };
38
- }
31
+ version = await driver.getVersion(dbhan);
39
32
  let databases = undefined;
40
33
  if (requestDbList) {
41
34
  databases = await driver.listDatabases(dbhan);
@@ -117,7 +117,7 @@ async function handleExecuteControlCommand({ command }) {
117
117
  }
118
118
  }
119
119
 
120
- async function handleExecuteQuery({ sql, autoCommit, limitRows }) {
120
+ async function handleExecuteQuery({ sql, autoCommit, limitRows, frontMatter }) {
121
121
  lastActivity = new Date().getTime();
122
122
 
123
123
  await waitConnected();
@@ -146,7 +146,7 @@ async function handleExecuteQuery({ sql, autoCommit, limitRows }) {
146
146
  ...driver.getQuerySplitterOptions('stream'),
147
147
  returnRichInfo: true,
148
148
  })) {
149
- await handleQueryStream(dbhan, driver, queryStreamInfoHolder, sqlItem, undefined, limitRows);
149
+ await handleQueryStream(dbhan, driver, queryStreamInfoHolder, sqlItem, undefined, limitRows, frontMatter);
150
150
  // const handler = new StreamHandler(resultIndex);
151
151
  // const stream = await driver.stream(systemConnection, sqlItem, handler);
152
152
  // handler.stream = stream;