dbgate-api-premium 5.5.7-alpha.45

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.
Files changed (125) hide show
  1. package/.env +19 -0
  2. package/.yarnrc +2 -0
  3. package/README.md +1 -0
  4. package/env/dblogin/.env +14 -0
  5. package/env/portal/.env +70 -0
  6. package/env/singledb/.env +17 -0
  7. package/env/storage/.env +43 -0
  8. package/package.json +89 -0
  9. package/src/auth/authCommon.js +16 -0
  10. package/src/auth/authProvider.js +343 -0
  11. package/src/auth/storageAuthProvider.js +393 -0
  12. package/src/controllers/apps.js +280 -0
  13. package/src/controllers/archive.js +217 -0
  14. package/src/controllers/auth.js +136 -0
  15. package/src/controllers/config.js +271 -0
  16. package/src/controllers/connections.js +486 -0
  17. package/src/controllers/databaseConnections.js +561 -0
  18. package/src/controllers/files.js +222 -0
  19. package/src/controllers/jsldata.js +296 -0
  20. package/src/controllers/metadata.js +47 -0
  21. package/src/controllers/plugins.js +216 -0
  22. package/src/controllers/queryHistory.js +54 -0
  23. package/src/controllers/runners.js +234 -0
  24. package/src/controllers/scheduler.js +46 -0
  25. package/src/controllers/serverConnections.js +271 -0
  26. package/src/controllers/sessions.js +243 -0
  27. package/src/controllers/storage.js +380 -0
  28. package/src/controllers/storageDb.js +215 -0
  29. package/src/controllers/uploads.js +133 -0
  30. package/src/currentVersion.js +5 -0
  31. package/src/gistSecret.js +2 -0
  32. package/src/index.js +139 -0
  33. package/src/main.js +202 -0
  34. package/src/packagedPluginsContent.js +1 -0
  35. package/src/proc/connectProcess.js +38 -0
  36. package/src/proc/databaseConnectionProcess.js +431 -0
  37. package/src/proc/index.js +15 -0
  38. package/src/proc/jslDatastoreProcess.js +60 -0
  39. package/src/proc/serverConnectionProcess.js +188 -0
  40. package/src/proc/sessionProcess.js +390 -0
  41. package/src/proc/sshForwardProcess.js +75 -0
  42. package/src/shell/archiveReader.js +11 -0
  43. package/src/shell/archiveWriter.js +22 -0
  44. package/src/shell/autoIndexForeignKeysTransform.js +19 -0
  45. package/src/shell/collectorWriter.js +33 -0
  46. package/src/shell/consoleObjectWriter.js +16 -0
  47. package/src/shell/copyStream.js +48 -0
  48. package/src/shell/dataDuplicator.js +63 -0
  49. package/src/shell/dataTypeMapperTransform.js +21 -0
  50. package/src/shell/dbModelToJson.js +16 -0
  51. package/src/shell/deployDb.js +56 -0
  52. package/src/shell/download.js +15 -0
  53. package/src/shell/dropAllDbObjects.js +42 -0
  54. package/src/shell/dumpDatabase.js +49 -0
  55. package/src/shell/executeQuery.js +39 -0
  56. package/src/shell/fakeObjectReader.js +35 -0
  57. package/src/shell/finalizer.js +12 -0
  58. package/src/shell/generateDeploySql.js +95 -0
  59. package/src/shell/generateModelSql.js +30 -0
  60. package/src/shell/importDatabase.js +85 -0
  61. package/src/shell/index.js +80 -0
  62. package/src/shell/initializeApiEnvironment.js +9 -0
  63. package/src/shell/jslDataReader.js +9 -0
  64. package/src/shell/jsonLinesReader.js +52 -0
  65. package/src/shell/jsonLinesWriter.js +36 -0
  66. package/src/shell/jsonReader.js +84 -0
  67. package/src/shell/jsonToDbModel.js +9 -0
  68. package/src/shell/jsonWriter.js +97 -0
  69. package/src/shell/loadDatabase.js +27 -0
  70. package/src/shell/loadFile.js +10 -0
  71. package/src/shell/modifyJsonLinesReader.js +148 -0
  72. package/src/shell/queryReader.js +30 -0
  73. package/src/shell/registerPlugins.js +9 -0
  74. package/src/shell/requirePlugin.js +43 -0
  75. package/src/shell/runScript.js +19 -0
  76. package/src/shell/sqlDataWriter.js +52 -0
  77. package/src/shell/sqlTextReplacementTransform.js +32 -0
  78. package/src/shell/tableReader.js +39 -0
  79. package/src/shell/tableWriter.js +18 -0
  80. package/src/storageModel.js +819 -0
  81. package/src/utility/ColumnMapTransformStream.js +21 -0
  82. package/src/utility/DatastoreProxy.js +106 -0
  83. package/src/utility/EnsureStreamHeaderStream.js +31 -0
  84. package/src/utility/JsonLinesDatabase.js +148 -0
  85. package/src/utility/JsonLinesDatastore.js +232 -0
  86. package/src/utility/LineReader.js +88 -0
  87. package/src/utility/SSHConnection.js +251 -0
  88. package/src/utility/authProxy.js +133 -0
  89. package/src/utility/checkLicense.js +186 -0
  90. package/src/utility/childProcessChecker.js +21 -0
  91. package/src/utility/cleanDirectory.js +24 -0
  92. package/src/utility/cloudUpgrade.js +61 -0
  93. package/src/utility/connectUtility.js +111 -0
  94. package/src/utility/crypting.js +105 -0
  95. package/src/utility/diff2htmlPage.js +8 -0
  96. package/src/utility/directories.js +179 -0
  97. package/src/utility/downloadPackage.js +51 -0
  98. package/src/utility/downloader.js +25 -0
  99. package/src/utility/exceptions.js +9 -0
  100. package/src/utility/exportDbModel.js +31 -0
  101. package/src/utility/exportDbModelSql.js +80 -0
  102. package/src/utility/getChartExport.js +55 -0
  103. package/src/utility/getDiagramExport.js +25 -0
  104. package/src/utility/getExpressPath.js +10 -0
  105. package/src/utility/getJslFileName.js +16 -0
  106. package/src/utility/getMapExport.js +77 -0
  107. package/src/utility/hardwareFingerprint.js +89 -0
  108. package/src/utility/hasPermission.js +101 -0
  109. package/src/utility/importDbModel.js +9 -0
  110. package/src/utility/loadFilesRecursive.js +20 -0
  111. package/src/utility/loadModelFolder.js +29 -0
  112. package/src/utility/loadModelTransform.js +36 -0
  113. package/src/utility/pipeForkLogs.js +19 -0
  114. package/src/utility/platformInfo.js +62 -0
  115. package/src/utility/processArgs.js +39 -0
  116. package/src/utility/processComm.js +18 -0
  117. package/src/utility/requireEngineDriver.js +26 -0
  118. package/src/utility/requirePluginFunction.js +16 -0
  119. package/src/utility/socket.js +68 -0
  120. package/src/utility/sshTunnel.js +106 -0
  121. package/src/utility/sshTunnelProxy.js +36 -0
  122. package/src/utility/timingSafeCheckToken.js +9 -0
  123. package/src/utility/useController.js +99 -0
  124. package/tsconfig.json +13 -0
  125. package/webpack.config.js +55 -0
@@ -0,0 +1,390 @@
1
+ const crypto = require('crypto');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const _ = require('lodash');
5
+ const childProcessChecker = require('../utility/childProcessChecker');
6
+ const { splitQuery } = require('dbgate-query-splitter');
7
+
8
+ const { jsldir } = require('../utility/directories');
9
+ const requireEngineDriver = require('../utility/requireEngineDriver');
10
+ const { decryptConnection } = require('../utility/crypting');
11
+ const connectUtility = require('../utility/connectUtility');
12
+ const { handleProcessCommunication } = require('../utility/processComm');
13
+ const { getLogger, extractIntSettingsValue, extractBoolSettingsValue } = require('dbgate-tools');
14
+
15
+ const logger = getLogger('sessionProcess');
16
+
17
+ let dbhan;
18
+ let storedConnection;
19
+ let afterConnectCallbacks = [];
20
+ // let currentHandlers = [];
21
+ let lastPing = null;
22
+ let lastActivity = null;
23
+ let currentProfiler = null;
24
+ let executingScripts = 0;
25
+
26
+ class TableWriter {
27
+ constructor() {
28
+ this.currentRowCount = 0;
29
+ this.currentChangeIndex = 1;
30
+ this.initializedFile = false;
31
+ }
32
+
33
+ initializeFromQuery(structure, resultIndex) {
34
+ this.jslid = crypto.randomUUID();
35
+ this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
36
+ fs.writeFileSync(
37
+ this.currentFile,
38
+ JSON.stringify({
39
+ ...structure,
40
+ __isStreamHeader: true,
41
+ }) + '\n'
42
+ );
43
+ this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
44
+ this.writeCurrentStats(false, false);
45
+ this.resultIndex = resultIndex;
46
+ this.initializedFile = true;
47
+ process.send({ msgtype: 'recordset', jslid: this.jslid, resultIndex });
48
+ }
49
+
50
+ initializeFromReader(jslid) {
51
+ this.jslid = jslid;
52
+ this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
53
+ this.writeCurrentStats(false, false);
54
+ }
55
+
56
+ row(row) {
57
+ // console.log('ACCEPT ROW', row);
58
+ this.currentStream.write(JSON.stringify(row) + '\n');
59
+ this.currentRowCount += 1;
60
+
61
+ if (!this.plannedStats) {
62
+ this.plannedStats = true;
63
+ process.nextTick(() => {
64
+ if (this.currentStream) this.currentStream.uncork();
65
+ process.nextTick(() => this.writeCurrentStats(false, true));
66
+ this.plannedStats = false;
67
+ });
68
+ }
69
+ }
70
+
71
+ rowFromReader(row) {
72
+ if (!this.initializedFile) {
73
+ process.send({ msgtype: 'initializeFile', jslid: this.jslid });
74
+ this.initializedFile = true;
75
+
76
+ fs.writeFileSync(this.currentFile, JSON.stringify(row) + '\n');
77
+ this.currentStream = fs.createWriteStream(this.currentFile, { flags: 'a' });
78
+ this.writeCurrentStats(false, false);
79
+ this.initializedFile = true;
80
+ return;
81
+ }
82
+
83
+ this.row(row);
84
+ }
85
+
86
+ writeCurrentStats(isFinished = false, emitEvent = false) {
87
+ const stats = {
88
+ rowCount: this.currentRowCount,
89
+ changeIndex: this.currentChangeIndex,
90
+ isFinished,
91
+ jslid: this.jslid,
92
+ };
93
+ fs.writeFileSync(`${this.currentFile}.stats`, JSON.stringify(stats));
94
+ this.currentChangeIndex += 1;
95
+ if (emitEvent) {
96
+ process.send({ msgtype: 'stats', ...stats });
97
+ }
98
+ }
99
+
100
+ close(afterClose) {
101
+ if (this.currentStream) {
102
+ this.currentStream.end(() => {
103
+ this.writeCurrentStats(true, true);
104
+ if (afterClose) afterClose();
105
+ });
106
+ }
107
+ }
108
+ }
109
+
110
+ class StreamHandler {
111
+ constructor(resultIndexHolder, resolve, startLine) {
112
+ this.recordset = this.recordset.bind(this);
113
+ this.startLine = startLine;
114
+ this.row = this.row.bind(this);
115
+ // this.error = this.error.bind(this);
116
+ this.done = this.done.bind(this);
117
+ this.info = this.info.bind(this);
118
+
119
+ // use this for cancelling - not implemented
120
+ // this.stream = null;
121
+
122
+ this.plannedStats = false;
123
+ this.resultIndexHolder = resultIndexHolder;
124
+ this.resolve = resolve;
125
+ // currentHandlers = [...currentHandlers, this];
126
+ }
127
+
128
+ closeCurrentWriter() {
129
+ if (this.currentWriter) {
130
+ this.currentWriter.close();
131
+ this.currentWriter = null;
132
+ }
133
+ }
134
+
135
+ recordset(columns) {
136
+ this.closeCurrentWriter();
137
+ this.currentWriter = new TableWriter();
138
+ this.currentWriter.initializeFromQuery(
139
+ Array.isArray(columns) ? { columns } : columns,
140
+ this.resultIndexHolder.value
141
+ );
142
+ this.resultIndexHolder.value += 1;
143
+
144
+ // this.writeCurrentStats();
145
+
146
+ // this.onRow = _.throttle((jslid) => {
147
+ // if (jslid == this.jslid) {
148
+ // this.writeCurrentStats(false, true);
149
+ // }
150
+ // }, 500);
151
+ }
152
+ row(row) {
153
+ if (this.currentWriter) this.currentWriter.row(row);
154
+ else if (row.message) process.send({ msgtype: 'info', info: { message: row.message } });
155
+ // this.onRow(this.jslid);
156
+ }
157
+ // error(error) {
158
+ // process.send({ msgtype: 'error', error });
159
+ // }
160
+ done(result) {
161
+ this.closeCurrentWriter();
162
+ // currentHandlers = currentHandlers.filter((x) => x != this);
163
+ this.resolve();
164
+ }
165
+ info(info) {
166
+ if (info && info.line != null) {
167
+ info = {
168
+ ...info,
169
+ line: this.startLine + info.line,
170
+ };
171
+ }
172
+ process.send({ msgtype: 'info', info });
173
+ }
174
+ }
175
+
176
+ function handleStream(driver, resultIndexHolder, sqlItem) {
177
+ return new Promise((resolve, reject) => {
178
+ const start = sqlItem.trimStart || sqlItem.start;
179
+ const handler = new StreamHandler(resultIndexHolder, resolve, start && start.line);
180
+ driver.stream(dbhan, sqlItem.text, handler);
181
+ });
182
+ }
183
+
184
+ function allowExecuteCustomScript(driver) {
185
+ if (driver.readOnlySessions) {
186
+ return true;
187
+ }
188
+ if (storedConnection.isReadOnly) {
189
+ return false;
190
+ // throw new Error('Connection is read only');
191
+ }
192
+ return true;
193
+ }
194
+
195
+ async function handleConnect(connection) {
196
+ storedConnection = connection;
197
+
198
+ const driver = requireEngineDriver(storedConnection);
199
+ dbhan = await connectUtility(driver, storedConnection, 'app');
200
+ for (const [resolve] of afterConnectCallbacks) {
201
+ resolve();
202
+ }
203
+ afterConnectCallbacks = [];
204
+ }
205
+
206
+ // function handleCancel() {
207
+ // for (const handler of currentHandlers) {
208
+ // if (handler.stream) handler.stream.cancel();
209
+ // }
210
+ // }
211
+
212
+ function waitConnected() {
213
+ if (dbhan) return Promise.resolve();
214
+ return new Promise((resolve, reject) => {
215
+ afterConnectCallbacks.push([resolve, reject]);
216
+ });
217
+ }
218
+
219
+ async function handleStartProfiler({ jslid }) {
220
+ lastActivity = new Date().getTime();
221
+
222
+ await waitConnected();
223
+ const driver = requireEngineDriver(storedConnection);
224
+
225
+ if (!allowExecuteCustomScript(driver)) {
226
+ process.send({ msgtype: 'done' });
227
+ return;
228
+ }
229
+
230
+ const writer = new TableWriter();
231
+ writer.initializeFromReader(jslid);
232
+
233
+ currentProfiler = await driver.startProfiler(dbhan, {
234
+ row: data => writer.rowFromReader(data),
235
+ });
236
+ currentProfiler.writer = writer;
237
+ }
238
+
239
+ async function handleStopProfiler({ jslid }) {
240
+ lastActivity = new Date().getTime();
241
+
242
+ const driver = requireEngineDriver(storedConnection);
243
+ currentProfiler.writer.close();
244
+ driver.stopProfiler(dbhan, currentProfiler);
245
+ currentProfiler = null;
246
+ }
247
+
248
+ async function handleExecuteQuery({ sql }) {
249
+ lastActivity = new Date().getTime();
250
+
251
+ await waitConnected();
252
+ const driver = requireEngineDriver(storedConnection);
253
+
254
+ if (!allowExecuteCustomScript(driver)) {
255
+ process.send({
256
+ msgtype: 'info',
257
+ info: {
258
+ message: 'Connection without read-only sessions is read only',
259
+ severity: 'error',
260
+ },
261
+ });
262
+ process.send({ msgtype: 'done', skipFinishedMessage: true });
263
+ return;
264
+ //process.send({ msgtype: 'error', error: e.message });
265
+ }
266
+
267
+ executingScripts++;
268
+ try {
269
+ const resultIndexHolder = {
270
+ value: 0,
271
+ };
272
+ for (const sqlItem of splitQuery(sql, {
273
+ ...driver.getQuerySplitterOptions('stream'),
274
+ returnRichInfo: true,
275
+ })) {
276
+ await handleStream(driver, resultIndexHolder, sqlItem);
277
+ // const handler = new StreamHandler(resultIndex);
278
+ // const stream = await driver.stream(systemConnection, sqlItem, handler);
279
+ // handler.stream = stream;
280
+ // resultIndex = handler.resultIndex;
281
+ }
282
+ process.send({ msgtype: 'done' });
283
+ } finally {
284
+ executingScripts--;
285
+ }
286
+ }
287
+
288
+ async function handleExecuteReader({ jslid, sql, fileName }) {
289
+ lastActivity = new Date().getTime();
290
+
291
+ await waitConnected();
292
+
293
+ const driver = requireEngineDriver(storedConnection);
294
+
295
+ if (fileName) {
296
+ sql = fs.readFileSync(fileName, 'utf-8');
297
+ } else {
298
+ if (!allowExecuteCustomScript(driver)) {
299
+ process.send({ msgtype: 'done' });
300
+ return;
301
+ }
302
+ }
303
+
304
+ const writer = new TableWriter();
305
+ writer.initializeFromReader(jslid);
306
+
307
+ const reader = await driver.readQuery(dbhan, sql);
308
+
309
+ reader.on('data', data => {
310
+ writer.rowFromReader(data);
311
+ });
312
+ reader.on('end', () => {
313
+ writer.close(() => {
314
+ process.send({ msgtype: 'done' });
315
+ });
316
+ });
317
+ }
318
+
319
+ function handlePing() {
320
+ lastPing = new Date().getTime();
321
+ }
322
+
323
+ const messageHandlers = {
324
+ connect: handleConnect,
325
+ executeQuery: handleExecuteQuery,
326
+ executeReader: handleExecuteReader,
327
+ startProfiler: handleStartProfiler,
328
+ stopProfiler: handleStopProfiler,
329
+ ping: handlePing,
330
+ // cancel: handleCancel,
331
+ };
332
+
333
+ async function handleMessage({ msgtype, ...other }) {
334
+ const handler = messageHandlers[msgtype];
335
+ await handler(other);
336
+ }
337
+
338
+ function start() {
339
+ childProcessChecker();
340
+
341
+ lastPing = new Date().getTime();
342
+
343
+ setInterval(async () => {
344
+ const time = new Date().getTime();
345
+ if (time - lastPing > 25 * 1000) {
346
+ logger.info('Session not alive, exiting');
347
+ const driver = requireEngineDriver(storedConnection);
348
+ await driver.close(dbhan);
349
+ process.exit(0);
350
+ }
351
+
352
+ const useSessionTimeout =
353
+ storedConnection && storedConnection.globalSettings
354
+ ? extractBoolSettingsValue(storedConnection.globalSettings, 'session.autoClose', true)
355
+ : false;
356
+ const sessionTimeout =
357
+ storedConnection && storedConnection.globalSettings
358
+ ? extractIntSettingsValue(storedConnection.globalSettings, 'session.autoCloseTimeout', 15, 1, 120)
359
+ : 15;
360
+ if (
361
+ useSessionTimeout &&
362
+ time - lastActivity > sessionTimeout * 60 * 1000 &&
363
+ !currentProfiler &&
364
+ executingScripts == 0
365
+ ) {
366
+ logger.info('Session not active, exiting');
367
+ const driver = requireEngineDriver(storedConnection);
368
+ await driver.close(dbhan);
369
+ process.exit(0);
370
+ }
371
+ }, 10 * 1000);
372
+
373
+ process.on('message', async message => {
374
+ if (handleProcessCommunication(message)) return;
375
+ try {
376
+ await handleMessage(message);
377
+ } catch (e) {
378
+ process.send({
379
+ msgtype: 'info',
380
+ info: {
381
+ message: e.message,
382
+ severity: 'error',
383
+ },
384
+ });
385
+ //process.send({ msgtype: 'error', error: e.message });
386
+ }
387
+ });
388
+ }
389
+
390
+ module.exports = { start };
@@ -0,0 +1,75 @@
1
+ const fs = require('fs-extra');
2
+ const platformInfo = require('../utility/platformInfo');
3
+ const childProcessChecker = require('../utility/childProcessChecker');
4
+ const { handleProcessCommunication } = require('../utility/processComm');
5
+ const { SSHConnection } = require('../utility/SSHConnection');
6
+ const { getLogger, extractErrorLogData, extractErrorMessage } = require('dbgate-tools');
7
+
8
+ const logger = getLogger('sshProcess');
9
+
10
+ async function getSshConnection(connection) {
11
+ const sshConfig = {
12
+ endHost: connection.sshHost || '',
13
+ endPort: connection.sshPort || 22,
14
+ bastionHost: connection.sshBastionHost || '',
15
+ agentForward: connection.sshMode == 'agent',
16
+ passphrase: connection.sshMode == 'keyFile' ? connection.sshKeyfilePassword : undefined,
17
+ username: connection.sshLogin,
18
+ password: (connection.sshMode || 'userPassword') == 'userPassword' ? connection.sshPassword : undefined,
19
+ agentSocket: connection.sshMode == 'agent' ? platformInfo.sshAuthSock : undefined,
20
+ privateKey:
21
+ connection.sshMode == 'keyFile' && (connection.sshKeyfile || platformInfo?.defaultKeyfile)
22
+ ? await fs.readFile(connection.sshKeyfile || platformInfo?.defaultKeyfile)
23
+ : undefined,
24
+ skipAutoPrivateKey: true,
25
+ noReadline: true,
26
+ };
27
+
28
+ const sshConn = new SSHConnection(sshConfig);
29
+ return sshConn;
30
+ }
31
+
32
+ async function handleStart({ connection, tunnelConfig }) {
33
+ try {
34
+ const sshConn = await getSshConnection(connection);
35
+ await sshConn.forward(tunnelConfig);
36
+
37
+ process.send({
38
+ msgtype: 'connected',
39
+ connection,
40
+ tunnelConfig,
41
+ });
42
+ } catch (err) {
43
+ logger.error(extractErrorLogData(err), 'Error creating SSH tunnel connection:');
44
+
45
+ process.send({
46
+ msgtype: 'error',
47
+ connection,
48
+ tunnelConfig,
49
+ errorMessage: extractErrorMessage(err.message),
50
+ });
51
+ }
52
+ }
53
+
54
+ const messageHandlers = {
55
+ connect: handleStart,
56
+ };
57
+
58
+ async function handleMessage({ msgtype, ...other }) {
59
+ const handler = messageHandlers[msgtype];
60
+ await handler(other);
61
+ }
62
+
63
+ function start() {
64
+ childProcessChecker();
65
+ process.on('message', async message => {
66
+ if (handleProcessCommunication(message)) return;
67
+ try {
68
+ await handleMessage(message);
69
+ } catch (e) {
70
+ console.error('sshForwardProcess - unhandled error', e);
71
+ }
72
+ });
73
+ }
74
+
75
+ module.exports = { start };
@@ -0,0 +1,11 @@
1
+ const path = require('path');
2
+ const { archivedir, resolveArchiveFolder } = require('../utility/directories');
3
+ const jsonLinesReader = require('./jsonLinesReader');
4
+
5
+ function archiveReader({ folderName, fileName, ...other }) {
6
+ const jsonlFile = path.join(resolveArchiveFolder(folderName), `${fileName}.jsonl`);
7
+ const res = jsonLinesReader({ fileName: jsonlFile, ...other });
8
+ return res;
9
+ }
10
+
11
+ module.exports = archiveReader;
@@ -0,0 +1,22 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+ const { archivedir, resolveArchiveFolder } = require('../utility/directories');
4
+ // const socket = require('../utility/socket');
5
+ const jsonLinesWriter = require('./jsonLinesWriter');
6
+ const { getLogger } = require('dbgate-tools');
7
+
8
+ const logger = getLogger();
9
+
10
+ function archiveWriter({ folderName, fileName }) {
11
+ const dir = resolveArchiveFolder(folderName);
12
+ if (!fs.existsSync(dir)) {
13
+ logger.info(`Creating directory ${dir}`);
14
+ fs.mkdirSync(dir);
15
+ }
16
+ const jsonlFile = path.join(dir, `${fileName}.jsonl`);
17
+ const res = jsonLinesWriter({ fileName: jsonlFile });
18
+ // socket.emitChanged(`archive-files-changed-${folderName}`);
19
+ return res;
20
+ }
21
+
22
+ module.exports = archiveWriter;
@@ -0,0 +1,19 @@
1
+ const autoIndexForeignKeysTransform = () => database => {
2
+ return {
3
+ ...database,
4
+ tables: database.tables.map(table => {
5
+ return {
6
+ ...table,
7
+ indexes: [
8
+ ...(table.indexes || []),
9
+ ...table.foreignKeys.map(fk => ({
10
+ constraintName: `IX_${fk.constraintName}`,
11
+ columns: fk.columns.map(x => ({ columnName: x.columnName })),
12
+ })),
13
+ ],
14
+ };
15
+ }),
16
+ };
17
+ };
18
+
19
+ module.exports = autoIndexForeignKeysTransform;
@@ -0,0 +1,33 @@
1
+ const stream = require('stream');
2
+
3
+ class CollectorWriterStream extends stream.Writable {
4
+ constructor(options) {
5
+ super(options);
6
+ this.rows = [];
7
+ this.structure = null;
8
+ this.runid = options.runid;
9
+ }
10
+ _write(chunk, enc, next) {
11
+ if (!this.structure) this.structure = chunk;
12
+ else this.rows.push(chunk);
13
+ next();
14
+ }
15
+
16
+ _final(callback) {
17
+ process.send({
18
+ msgtype: 'freeData',
19
+ runid: this.runid,
20
+ freeData: { rows: this.rows, structure: this.structure },
21
+ });
22
+ callback();
23
+ }
24
+ }
25
+
26
+ async function collectorWriter({ runid }) {
27
+ return new CollectorWriterStream({
28
+ objectMode: true,
29
+ runid,
30
+ });
31
+ }
32
+
33
+ module.exports = collectorWriter;
@@ -0,0 +1,16 @@
1
+ const stream = require('stream');
2
+
3
+ class ObjectWriterStream extends stream.Writable {
4
+ _write(chunk, enc, next) {
5
+ console.log(JSON.stringify(chunk));
6
+ next();
7
+ }
8
+ }
9
+
10
+ async function consoleObjectWriter() {
11
+ return new ObjectWriterStream({
12
+ objectMode: true,
13
+ });
14
+ }
15
+
16
+ module.exports = consoleObjectWriter;
@@ -0,0 +1,48 @@
1
+ const EnsureStreamHeaderStream = require('../utility/EnsureStreamHeaderStream');
2
+ const Stream = require('stream');
3
+ const ColumnMapTransformStream = require('../utility/ColumnMapTransformStream');
4
+
5
+ function copyStream(input, output, options) {
6
+ const { columns } = options || {};
7
+
8
+ const transforms = [];
9
+ if (columns) {
10
+ transforms.push(new ColumnMapTransformStream(columns));
11
+ }
12
+ if (output.requireFixedStructure) {
13
+ transforms.push(new EnsureStreamHeaderStream());
14
+ }
15
+
16
+ // return new Promise((resolve, reject) => {
17
+ // Stream.pipeline(input, ...transforms, output, err => {
18
+ // if (err) {
19
+ // reject(err);
20
+ // } else {
21
+ // resolve();
22
+ // }
23
+ // });
24
+ // });
25
+
26
+ return new Promise((resolve, reject) => {
27
+ const finisher = output['finisher'] || output;
28
+ finisher.on('finish', resolve);
29
+ finisher.on('error', reject);
30
+
31
+ let lastStream = input;
32
+ for (const tran of transforms) {
33
+ lastStream.pipe(tran);
34
+ lastStream = tran;
35
+ }
36
+ lastStream.pipe(output);
37
+
38
+ // if (output.requireFixedStructure) {
39
+ // const ensureHeader = new EnsureStreamHeaderStream();
40
+ // input.pipe(ensureHeader);
41
+ // ensureHeader.pipe(output);
42
+ // } else {
43
+ // input.pipe(output);
44
+ // }
45
+ });
46
+ }
47
+
48
+ module.exports = copyStream;
@@ -0,0 +1,63 @@
1
+ const stream = require('stream');
2
+ const path = require('path');
3
+ const { quoteFullName, fullNameToString, getLogger } = require('dbgate-tools');
4
+ const requireEngineDriver = require('../utility/requireEngineDriver');
5
+ const connectUtility = require('../utility/connectUtility');
6
+ const logger = getLogger('dataDuplicator');
7
+ const { DataDuplicator } = require('dbgate-datalib');
8
+ const copyStream = require('./copyStream');
9
+ const jsonLinesReader = require('./jsonLinesReader');
10
+ const { resolveArchiveFolder } = require('../utility/directories');
11
+
12
+ async function dataDuplicator({
13
+ connection,
14
+ archive,
15
+ folder,
16
+ items,
17
+ options,
18
+ analysedStructure = null,
19
+ driver,
20
+ systemConnection,
21
+ }) {
22
+ if (!driver) driver = requireEngineDriver(connection);
23
+
24
+ const dbhan = systemConnection || (await connectUtility(driver, connection, 'write'));
25
+
26
+ try {
27
+ logger.info(`Connected.`);
28
+
29
+ if (!analysedStructure) {
30
+ analysedStructure = await driver.analyseFull(dbhan);
31
+ }
32
+
33
+ const sourceDir = archive
34
+ ? resolveArchiveFolder(archive)
35
+ : folder?.startsWith('archive:')
36
+ ? resolveArchiveFolder(folder.substring('archive:'.length))
37
+ : folder;
38
+
39
+ const dupl = new DataDuplicator(
40
+ dbhan,
41
+ driver,
42
+ analysedStructure,
43
+ items.map(item => ({
44
+ name: item.name,
45
+ operation: item.operation,
46
+ matchColumns: item.matchColumns,
47
+ openStream:
48
+ item.openStream || (() => jsonLinesReader({ fileName: path.join(sourceDir, `${item.name}.jsonl`) })),
49
+ })),
50
+ stream,
51
+ copyStream,
52
+ options
53
+ );
54
+
55
+ await dupl.run();
56
+ } finally {
57
+ if (!systemConnection) {
58
+ await driver.close(dbhan);
59
+ }
60
+ }
61
+ }
62
+
63
+ module.exports = dataDuplicator;
@@ -0,0 +1,21 @@
1
+ const dataTypeMapperTransform = (oldType, newType) => database => {
2
+ return {
3
+ ...database,
4
+ tables: database.tables.map(table => {
5
+ return {
6
+ ...table,
7
+ columns: table.columns.map(column => {
8
+ if (column.dataType?.toLowerCase() === oldType?.toLowerCase()) {
9
+ return {
10
+ ...column,
11
+ dataType: newType,
12
+ };
13
+ }
14
+ return column;
15
+ }),
16
+ };
17
+ }),
18
+ };
19
+ };
20
+
21
+ module.exports = dataTypeMapperTransform;