dbgate-api 4.4.0-alpha.1 → 4.4.2

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",
3
3
  "main": "src/index.js",
4
- "version": "4.4.0-alpha.1",
4
+ "version": "4.4.2",
5
5
  "homepage": "https://dbgate.org/",
6
6
  "repository": {
7
7
  "type": "git",
@@ -26,8 +26,11 @@
26
26
  "compare-versions": "^3.6.0",
27
27
  "cors": "^2.8.5",
28
28
  "cross-env": "^6.0.3",
29
- "dbgate-sqltree": "^4.4.0-alpha.1",
30
- "dbgate-tools": "^4.4.0-alpha.1",
29
+ "dbgate-query-splitter": "^4.4.2",
30
+ "dbgate-sqltree": "^4.4.2",
31
+ "dbgate-tools": "^4.4.2",
32
+ "diff": "^5.0.0",
33
+ "diff2html": "^3.4.13",
31
34
  "eslint": "^6.8.0",
32
35
  "express": "^4.17.1",
33
36
  "express-basic-auth": "^1.2.0",
@@ -62,7 +65,7 @@
62
65
  "devDependencies": {
63
66
  "@types/fs-extra": "^9.0.11",
64
67
  "@types/lodash": "^4.14.149",
65
- "dbgate-types": "^4.4.0-alpha.1",
68
+ "dbgate-types": "^4.4.2",
66
69
  "env-cmd": "^10.1.0",
67
70
  "node-loader": "^1.0.2",
68
71
  "nodemon": "^2.0.2",
@@ -71,6 +74,6 @@
71
74
  "webpack-cli": "^3.3.11"
72
75
  },
73
76
  "optionalDependencies": {
74
- "msnodesqlv8": "^2.0.10"
77
+ "msnodesqlv8": "^2.4.0"
75
78
  }
76
79
  }
@@ -7,6 +7,7 @@ const { archivedir, clearArchiveLinksCache, resolveArchiveFolder } = require('..
7
7
  const socket = require('../utility/socket');
8
8
  const JsonLinesDatastore = require('../utility/JsonLinesDatastore');
9
9
  const { saveFreeTableData } = require('../utility/freeTableStorage');
10
+ const loadFilesRecursive = require('../utility/loadFilesRecursive');
10
11
 
11
12
  module.exports = {
12
13
  folders_meta: 'get',
@@ -39,20 +40,21 @@ module.exports = {
39
40
  fs.writeFile(path.join(archivedir(), folder), linkedFolder);
40
41
  clearArchiveLinksCache();
41
42
  socket.emitChanged('archive-folders-changed');
42
- return true;
43
+ return folder;
43
44
  },
44
45
 
45
46
  files_meta: 'get',
46
47
  async files({ folder }) {
47
48
  const dir = resolveArchiveFolder(folder);
48
49
  if (!(await fs.exists(dir))) return [];
49
- const files = await fs.readdir(dir);
50
+ const files = await loadFilesRecursive(dir); // fs.readdir(dir);
50
51
 
51
52
  function fileType(ext, type) {
52
53
  return files
53
54
  .filter(name => name.endsWith(ext))
54
55
  .map(name => ({
55
56
  name: name.slice(0, -ext.length),
57
+ label: path.parse(name.slice(0, -ext.length)).base,
56
58
  type,
57
59
  }));
58
60
  }
@@ -79,11 +81,27 @@ module.exports = {
79
81
  },
80
82
 
81
83
  deleteFile_meta: 'post',
82
- async deleteFile({ folder, file }) {
83
- await fs.unlink(path.join(resolveArchiveFolder(folder), `${file}.jsonl`));
84
+ async deleteFile({ folder, file, fileType }) {
85
+ await fs.unlink(path.join(resolveArchiveFolder(folder), `${file}.${fileType}`));
86
+ socket.emitChanged(`archive-files-changed-${folder}`);
87
+ },
88
+
89
+ renameFile_meta: 'post',
90
+ async renameFile({ folder, file, newFile, fileType }) {
91
+ await fs.rename(
92
+ path.join(resolveArchiveFolder(folder), `${file}.${fileType}`),
93
+ path.join(resolveArchiveFolder(folder), `${newFile}.${fileType}`)
94
+ );
84
95
  socket.emitChanged(`archive-files-changed-${folder}`);
85
96
  },
86
97
 
98
+ renameFolder_meta: 'post',
99
+ async renameFolder({ folder, newFolder }) {
100
+ const uniqueName = await this.getNewArchiveFolder({ database: newFolder });
101
+ await fs.rename(path.join(archivedir(), folder), path.join(archivedir(), uniqueName));
102
+ socket.emitChanged(`archive-folders-changed`);
103
+ },
104
+
87
105
  deleteFolder_meta: 'post',
88
106
  async deleteFolder({ folder }) {
89
107
  if (!folder) throw new Error('Missing folder parameter');
@@ -123,11 +141,14 @@ module.exports = {
123
141
  },
124
142
 
125
143
  async getNewArchiveFolder({ database }) {
144
+ const isLink = database.endsWith(database);
145
+ const name = isLink ? database.slice(0, -5) : database;
146
+ const suffix = isLink ? '.link' : '';
126
147
  if (!(await fs.exists(path.join(archivedir(), database)))) return database;
127
148
  let index = 2;
128
- while (await fs.exists(path.join(archivedir(), `${database}${index}`))) {
149
+ while (await fs.exists(path.join(archivedir(), `${name}${index}${suffix}`))) {
129
150
  index += 1;
130
151
  }
131
- return `${database}${index}`;
152
+ return `${name}${index}${suffix}`;
132
153
  },
133
154
  };
@@ -2,8 +2,9 @@ const path = require('path');
2
2
  const { fork } = require('child_process');
3
3
  const _ = require('lodash');
4
4
  const nedb = require('nedb-promises');
5
+ const fs = require('fs-extra');
5
6
 
6
- const { datadir } = require('../utility/directories');
7
+ const { datadir, filesdir } = require('../utility/directories');
7
8
  const socket = require('../utility/socket');
8
9
  const { encryptConnection } = require('../utility/crypting');
9
10
  const { handleProcessCommunication } = require('../utility/processComm');
@@ -175,4 +176,20 @@ module.exports = {
175
176
  const res = await this.datastore.find({ _id: conid });
176
177
  return res[0];
177
178
  },
179
+
180
+ newSqliteDatabase_meta: 'post',
181
+ async newSqliteDatabase({ file }) {
182
+ const sqliteDir = path.join(filesdir(), 'sqlite');
183
+ if (!(await fs.exists(sqliteDir))) {
184
+ await fs.mkdir(sqliteDir);
185
+ }
186
+ const databaseFile = path.join(sqliteDir, `${file}.sqlite`);
187
+ const res = await this.save({
188
+ engine: 'sqlite@dbgate-plugin-sqlite',
189
+ databaseFile,
190
+ singleDatabase: true,
191
+ defaultDatabase: `${file}.sqlite`,
192
+ });
193
+ return res;
194
+ },
178
195
  };
@@ -3,16 +3,28 @@ const connections = require('./connections');
3
3
  const archive = require('./archive');
4
4
  const socket = require('../utility/socket');
5
5
  const { fork } = require('child_process');
6
- const { DatabaseAnalyser, getAlterDatabaseScript, generateDbPairingId, matchPairedObjects } = require('dbgate-tools');
6
+ const {
7
+ DatabaseAnalyser,
8
+ computeDbDiffRows,
9
+ getCreateObjectScript,
10
+ getAlterDatabaseScript,
11
+ generateDbPairingId,
12
+ matchPairedObjects,
13
+ extendDatabaseInfo,
14
+ modelCompareDbDiffOptions,
15
+ } = require('dbgate-tools');
16
+ const { html, parse } = require('diff2html');
7
17
  const { handleProcessCommunication } = require('../utility/processComm');
8
18
  const config = require('./config');
9
19
  const fs = require('fs-extra');
10
20
  const exportDbModel = require('../utility/exportDbModel');
11
- const { archivedir, resolveArchiveFolder } = require('../utility/directories');
21
+ const { archivedir, resolveArchiveFolder, uploadsdir } = require('../utility/directories');
12
22
  const path = require('path');
13
23
  const importDbModel = require('../utility/importDbModel');
14
24
  const requireEngineDriver = require('../utility/requireEngineDriver');
15
25
  const generateDeploySql = require('../shell/generateDeploySql');
26
+ const { createTwoFilesPatch } = require('diff');
27
+ const diff2htmlPage = require('../utility/diff2htmlPage');
16
28
 
17
29
  module.exports = {
18
30
  /** @type {import('dbgate-types').OpenedDatabaseConnection[]} */
@@ -220,6 +232,11 @@ module.exports = {
220
232
 
221
233
  structure_meta: 'get',
222
234
  async structure({ conid, database }) {
235
+ if (conid == '__model') {
236
+ const model = await importDbModel(database);
237
+ return model;
238
+ }
239
+
223
240
  const opened = await this.ensureOpened(conid, database);
224
241
  return opened.structure;
225
242
  // const existing = this.opened.find((x) => x.conid == conid && x.database == database);
@@ -258,12 +275,17 @@ module.exports = {
258
275
 
259
276
  generateDeploySql_meta: 'post',
260
277
  async generateDeploySql({ conid, database, archiveFolder }) {
261
- const connection = await connections.get({ conid });
262
- return generateDeploySql({
263
- connection,
264
- analysedStructure: await this.structure({ conid, database }),
265
- modelFolder: resolveArchiveFolder(archiveFolder),
266
- });
278
+ const opened = await this.ensureOpened(conid, database);
279
+ const res = await this.sendRequest(opened, { msgtype: 'generateDeploySql', modelFolder: resolveArchiveFolder(archiveFolder) });
280
+ return res;
281
+
282
+ // const connection = await connections.get({ conid });
283
+ // return generateDeploySql({
284
+ // connection,
285
+ // analysedStructure: await this.structure({ conid, database }),
286
+ // modelFolder: resolveArchiveFolder(archiveFolder),
287
+ // });
288
+
267
289
  // const deployedModel = generateDbPairingId(await importDbModel(path.join(archivedir(), archiveFolder)));
268
290
  // const currentModel = generateDbPairingId(await this.structure({ conid, database }));
269
291
  // const currentModelPaired = matchPairedObjects(deployedModel, currentModel);
@@ -285,4 +307,52 @@ module.exports = {
285
307
  // const res = await this.sendRequest(opened, { msgtype: 'queryData', sql });
286
308
  // return res;
287
309
  // },
310
+
311
+ async getUnifiedDiff({ sourceConid, sourceDatabase, targetConid, targetDatabase }) {
312
+ const dbDiffOptions = sourceConid == '__model' ? modelCompareDbDiffOptions : {};
313
+
314
+ const sourceDb = generateDbPairingId(
315
+ extendDatabaseInfo(await this.structure({ conid: sourceConid, database: sourceDatabase }))
316
+ );
317
+ const targetDb = generateDbPairingId(
318
+ extendDatabaseInfo(await this.structure({ conid: targetConid, database: targetDatabase }))
319
+ );
320
+ // const sourceConnection = await connections.get({conid:sourceConid})
321
+ const connection = await connections.get({ conid: targetConid });
322
+ const driver = requireEngineDriver(connection);
323
+ const targetDbPaired = matchPairedObjects(sourceDb, targetDb, dbDiffOptions);
324
+ const diffRows = computeDbDiffRows(sourceDb, targetDbPaired, dbDiffOptions, driver);
325
+
326
+ // console.log('sourceDb', sourceDb);
327
+ // console.log('targetDb', targetDb);
328
+ // console.log('sourceConid, sourceDatabase', sourceConid, sourceDatabase);
329
+
330
+ let res = '';
331
+ for (const row of diffRows) {
332
+ // console.log('PAIR', row.source && row.source.pureName, row.target && row.target.pureName);
333
+ const unifiedDiff = createTwoFilesPatch(
334
+ (row.target && row.target.pureName) || '',
335
+ (row.source && row.source.pureName) || '',
336
+ getCreateObjectScript(row.target, driver),
337
+ getCreateObjectScript(row.source, driver),
338
+ '',
339
+ ''
340
+ );
341
+ res += unifiedDiff;
342
+ }
343
+ return res;
344
+ },
345
+
346
+ generateDbDiffReport_meta: 'post',
347
+ async generateDbDiffReport({ filePath, sourceConid, sourceDatabase, targetConid, targetDatabase }) {
348
+ const unifiedDiff = await this.getUnifiedDiff({ sourceConid, sourceDatabase, targetConid, targetDatabase });
349
+
350
+ const diffJson = parse(unifiedDiff);
351
+ // $: diffHtml = html(diffJson, { outputFormat: 'side-by-side', drawFileList: false });
352
+ const diffHtml = html(diffJson, { outputFormat: 'side-by-side' });
353
+
354
+ await fs.writeFile(filePath, diff2htmlPage(diffHtml));
355
+
356
+ return true;
357
+ },
288
358
  };
@@ -1,6 +1,8 @@
1
+ const uuidv1 = require('uuid/v1');
1
2
  const fs = require('fs-extra');
2
3
  const path = require('path');
3
- const { filesdir, archivedir, resolveArchiveFolder } = require('../utility/directories');
4
+ const { filesdir, archivedir, resolveArchiveFolder, uploadsdir } = require('../utility/directories');
5
+ const getChartExport = require('../utility/getChartExport');
4
6
  const hasPermission = require('../utility/hasPermission');
5
7
  const socket = require('../utility/socket');
6
8
  const scheduler = require('./scheduler');
@@ -56,6 +58,14 @@ module.exports = {
56
58
  socket.emitChanged(`all-files-changed`);
57
59
  },
58
60
 
61
+ copy_meta: 'post',
62
+ async copy({ folder, file, newFile }) {
63
+ if (!hasPermission(`files/${folder}/write`)) return;
64
+ await fs.copyFile(path.join(filesdir(), folder, file), path.join(filesdir(), folder, newFile));
65
+ socket.emitChanged(`files-changed-${folder}`);
66
+ socket.emitChanged(`all-files-changed`);
67
+ },
68
+
59
69
  load_meta: 'post',
60
70
  async load({ folder, file, format }) {
61
71
  if (folder.startsWith('archive:')) {
@@ -114,4 +124,30 @@ module.exports = {
114
124
  }
115
125
  return res;
116
126
  },
127
+
128
+ generateUploadsFile_meta: 'get',
129
+ async generateUploadsFile() {
130
+ const fileName = `${uuidv1()}.html`;
131
+ return {
132
+ fileName,
133
+ filePath: path.join(uploadsdir(), fileName),
134
+ };
135
+ },
136
+
137
+ exportChart_meta: 'post',
138
+ async exportChart({ filePath, title, config, image }) {
139
+ const fileName = path.parse(filePath).base;
140
+ const imageFile = fileName.replace('.html', '-preview.png');
141
+ const html = getChartExport(title, config, imageFile);
142
+ await fs.writeFile(filePath, html);
143
+ if (image) {
144
+ const index = image.indexOf('base64,');
145
+ if (index > 0) {
146
+ const data = image.substr(index + 'base64,'.length);
147
+ const buf = Buffer.from(data, 'base64');
148
+ await fs.writeFile(filePath.replace('.html', '-preview.png'), buf);
149
+ }
150
+ }
151
+ return true;
152
+ },
117
153
  };
@@ -15,17 +15,20 @@ function extractPlugins(script) {
15
15
  return matches.map(x => x[1]);
16
16
  }
17
17
 
18
- const requirePluginsTemplate = plugins =>
18
+ const requirePluginsTemplate = (plugins, isExport) =>
19
19
  plugins
20
20
  .map(
21
- packageName => `const ${_.camelCase(packageName)} = require(process.env.PLUGIN_${_.camelCase(packageName)});\n`
21
+ packageName =>
22
+ `const ${_.camelCase(packageName)} = require(${
23
+ isExport ? `'${packageName}'` : `process.env.PLUGIN_${_.camelCase(packageName)}`
24
+ });\n`
22
25
  )
23
26
  .join('') + `dbgateApi.registerPlugins(${plugins.map(x => _.camelCase(x)).join(',')});\n`;
24
27
 
25
- const scriptTemplate = script => `
26
- const dbgateApi = require(process.env.DBGATE_API);
28
+ const scriptTemplate = (script, isExport) => `
29
+ const dbgateApi = require(${isExport ? `'dbgate-api'` : 'process.env.DBGATE_API'});
27
30
  dbgateApi.initializeApiEnvironment();
28
- ${requirePluginsTemplate(extractPlugins(script))}
31
+ ${requirePluginsTemplate(extractPlugins(script), isExport)}
29
32
  require=null;
30
33
  async function run() {
31
34
  ${script}
@@ -139,7 +142,12 @@ module.exports = {
139
142
  start_meta: 'post',
140
143
  async start({ script }) {
141
144
  const runid = uuidv1();
142
- return this.startCore(runid, scriptTemplate(script));
145
+ return this.startCore(runid, scriptTemplate(script, false));
146
+ },
147
+
148
+ getNodeScript_meta: 'post',
149
+ async getNodeScript({ script }) {
150
+ return scriptTemplate(script, true);
143
151
  },
144
152
 
145
153
  cancel_meta: 'post',
@@ -25,4 +25,12 @@ module.exports = {
25
25
  });
26
26
  });
27
27
  },
28
+
29
+ get_meta: {
30
+ method: 'get',
31
+ raw: true,
32
+ },
33
+ get(req, res) {
34
+ res.sendFile(path.join(uploadsdir(), req.query.file));
35
+ },
28
36
  };
@@ -1,5 +1,5 @@
1
1
 
2
2
  module.exports = {
3
- version: '4.4.0-alpha.1',
4
- buildTime: '2021-10-14T18:25:45.470Z'
3
+ version: '4.4.2',
4
+ buildTime: '2021-12-02T07:20:50.672Z'
5
5
  };
@@ -6,10 +6,12 @@ const requireEngineDriver = require('../utility/requireEngineDriver');
6
6
  const connectUtility = require('../utility/connectUtility');
7
7
  const { handleProcessCommunication } = require('../utility/processComm');
8
8
  const { SqlGenerator } = require('dbgate-tools');
9
+ const generateDeploySql = require('../shell/generateDeploySql');
9
10
 
10
11
  let systemConnection;
11
12
  let storedConnection;
12
13
  let afterConnectCallbacks = [];
14
+ let afterAnalyseCallbacks = [];
13
15
  let analysedStructure = null;
14
16
  let lastPing = null;
15
17
  let lastStatus = null;
@@ -42,14 +44,18 @@ async function handleFullRefresh() {
42
44
  process.send({ msgtype: 'structure', structure: analysedStructure });
43
45
  process.send({ msgtype: 'structureTime', analysedTime });
44
46
  setStatusName('ok');
47
+
45
48
  loadingModel = false;
49
+ resolveAnalysedPromises();
46
50
  }
47
51
 
48
52
  async function handleIncrementalRefresh(forceSend) {
49
53
  loadingModel = true;
50
54
  const driver = requireEngineDriver(storedConnection);
51
55
  setStatusName('checkStructure');
52
- const newStructure = await checkedAsyncCall(driver.analyseIncremental(systemConnection, analysedStructure, serverVersion));
56
+ const newStructure = await checkedAsyncCall(
57
+ driver.analyseIncremental(systemConnection, analysedStructure, serverVersion)
58
+ );
53
59
  analysedTime = new Date().getTime();
54
60
  if (newStructure != null) {
55
61
  analysedStructure = newStructure;
@@ -62,6 +68,7 @@ async function handleIncrementalRefresh(forceSend) {
62
68
  process.send({ msgtype: 'structureTime', analysedTime });
63
69
  setStatusName('ok');
64
70
  loadingModel = false;
71
+ resolveAnalysedPromises();
65
72
  }
66
73
 
67
74
  function handleSyncModel() {
@@ -123,6 +130,20 @@ function waitConnected() {
123
130
  });
124
131
  }
125
132
 
133
+ function waitStructure() {
134
+ if (analysedStructure) return Promise.resolve();
135
+ return new Promise((resolve, reject) => {
136
+ afterAnalyseCallbacks.push([resolve, reject]);
137
+ });
138
+ }
139
+
140
+ function resolveAnalysedPromises() {
141
+ for (const [resolve] of afterAnalyseCallbacks) {
142
+ resolve();
143
+ }
144
+ afterAnalyseCallbacks = [];
145
+ }
146
+
126
147
  async function handleRunScript({ msgid, sql }) {
127
148
  await waitConnected();
128
149
  const driver = requireEngineDriver(storedConnection);
@@ -168,7 +189,7 @@ async function handleUpdateCollection({ msgid, changeSet }) {
168
189
  }
169
190
 
170
191
  async function handleSqlPreview({ msgid, objects, options }) {
171
- await waitConnected();
192
+ await waitStructure();
172
193
  const driver = requireEngineDriver(storedConnection);
173
194
 
174
195
  try {
@@ -188,6 +209,22 @@ async function handleSqlPreview({ msgid, objects, options }) {
188
209
  }
189
210
  }
190
211
 
212
+ async function handleGenerateDeploySql({ msgid, modelFolder }) {
213
+ await waitStructure();
214
+
215
+ try {
216
+ const res = await generateDeploySql({
217
+ systemConnection,
218
+ connection: storedConnection,
219
+ analysedStructure,
220
+ modelFolder,
221
+ });
222
+ process.send({ ...res, msgtype: 'response', msgid });
223
+ } catch (err) {
224
+ process.send({ msgtype: 'response', msgid, isError: true, errorMessage: err.message });
225
+ }
226
+ }
227
+
191
228
  // async function handleRunCommand({ msgid, sql }) {
192
229
  // await waitConnected();
193
230
  // const driver = engines(storedConnection);
@@ -208,6 +245,7 @@ const messageHandlers = {
208
245
  sqlPreview: handleSqlPreview,
209
246
  ping: handlePing,
210
247
  syncModel: handleSyncModel,
248
+ generateDeploySql: handleGenerateDeploySql,
211
249
  // runCommand: handleRunCommand,
212
250
  };
213
251
 
@@ -5,7 +5,7 @@ async function fakeObjectReader({ delay = 0 } = {}) {
5
5
  objectMode: true,
6
6
  });
7
7
  function doWrite() {
8
- pass.write({ columns: [{ columnName: 'id' }, { columnName: 'country' }] });
8
+ pass.write({ columns: [{ columnName: 'id' }, { columnName: 'country' }], __isStreamHeader: true });
9
9
  pass.write({ id: 1, country: 'Czechia' });
10
10
  pass.write({ id: 2, country: 'Austria' });
11
11
  pass.write({ country: 'Germany', id: 3 });
@@ -4,6 +4,8 @@ const {
4
4
  matchPairedObjects,
5
5
  databaseInfoFromYamlModel,
6
6
  extendDatabaseInfo,
7
+ modelCompareDbDiffOptions,
8
+ enrichWithPreloadedRows,
7
9
  } = require('dbgate-tools');
8
10
  const importDbModel = require('../utility/importDbModel');
9
11
  const requireEngineDriver = require('../utility/requireEngineDriver');
@@ -18,8 +20,9 @@ async function generateDeploySql({
18
20
  loadedDbModel = undefined,
19
21
  }) {
20
22
  if (!driver) driver = requireEngineDriver(connection);
23
+
24
+ const pool = systemConnection || (await connectUtility(driver, connection));
21
25
  if (!analysedStructure) {
22
- const pool = systemConnection || (await connectUtility(driver, connection));
23
26
  analysedStructure = await driver.analyseFull(pool);
24
27
  }
25
28
 
@@ -28,9 +31,7 @@ async function generateDeploySql({
28
31
  );
29
32
  const currentModel = generateDbPairingId(extendDatabaseInfo(analysedStructure));
30
33
  const opts = {
31
- ignoreCase: true,
32
- schemaMode: 'ignore',
33
- ignoreConstraintNames: true,
34
+ ...modelCompareDbDiffOptions,
34
35
 
35
36
  noDropTable: true,
36
37
  noDropColumn: true,
@@ -38,14 +39,22 @@ async function generateDeploySql({
38
39
  noDropSqlObject: true,
39
40
  noRenameTable: true,
40
41
  noRenameColumn: true,
41
- ignoreForeignKeyActions: true,
42
- ignoreDataTypes: true,
43
42
  };
44
43
  const currentModelPaired = matchPairedObjects(deployedModel, currentModel, opts);
44
+ const currentModelPairedPreloaded = await enrichWithPreloadedRows(deployedModel, currentModelPaired, pool, driver);
45
+
46
+ // console.log('currentModelPairedPreloaded', currentModelPairedPreloaded.tables[0]);
45
47
  // console.log('deployedModel', deployedModel.tables[0]);
46
48
  // console.log('currentModel', currentModel.tables[0]);
47
49
  // console.log('currentModelPaired', currentModelPaired.tables[0]);
48
- const res = getAlterDatabaseScript(currentModelPaired, deployedModel, opts, deployedModel, driver);
50
+ const res = getAlterDatabaseScript(
51
+ currentModelPairedPreloaded,
52
+ deployedModel,
53
+ opts,
54
+ currentModelPairedPreloaded,
55
+ deployedModel,
56
+ driver
57
+ );
49
58
  return res;
50
59
  }
51
60
 
@@ -1,13 +1,15 @@
1
1
  const { fullNameToString } = require('dbgate-tools');
2
2
  const requireEngineDriver = require('../utility/requireEngineDriver');
3
- const { decryptConnection } = require('../utility/crypting');
4
3
  const connectUtility = require('../utility/connectUtility');
5
4
 
6
- async function tableWriter({ connection, schemaName, pureName, ...options }) {
5
+ async function tableWriter({ connection, schemaName, pureName, driver, systemConnection, ...options }) {
7
6
  console.log(`Writing table ${fullNameToString({ schemaName, pureName })}`);
8
7
 
9
- const driver = requireEngineDriver(connection);
10
- const pool = await connectUtility(driver, connection);
8
+ if (!driver) {
9
+ driver = requireEngineDriver(connection);
10
+ }
11
+ const pool = systemConnection || (await connectUtility(driver, connection));
12
+
11
13
  console.log(`Connected.`);
12
14
  return await driver.writeTable(pool, { schemaName, pureName }, options);
13
15
  }
@@ -0,0 +1,8 @@
1
+ const diff2htmlCss =
2
+ '.d2h-d-none{display:none}.d2h-wrapper{text-align:left}.d2h-file-header{background-color:#f7f7f7;border-bottom:1px solid #d8d8d8;font-family:Source Sans Pro,Helvetica Neue,Helvetica,Arial,sans-serif;height:35px;padding:5px 10px}.d2h-file-header,.d2h-file-stats{display:-webkit-box;display:-ms-flexbox;display:flex}.d2h-file-stats{font-size:14px;margin-left:auto}.d2h-lines-added{border:1px solid #b4e2b4;border-radius:5px 0 0 5px;color:#399839;padding:2px;text-align:right;vertical-align:middle}.d2h-lines-deleted{border:1px solid #e9aeae;border-radius:0 5px 5px 0;color:#c33;margin-left:1px;padding:2px;text-align:left;vertical-align:middle}.d2h-file-name-wrapper{-webkit-box-align:center;-ms-flex-align:center;align-items:center;display:-webkit-box;display:-ms-flexbox;display:flex;font-size:15px;width:100%}.d2h-file-name{overflow-x:hidden;text-overflow:ellipsis;white-space:nowrap}.d2h-file-wrapper{border:1px solid #ddd;border-radius:3px;margin-bottom:1em}.d2h-file-collapse{-webkit-box-pack:end;-ms-flex-pack:end;-webkit-box-align:center;-ms-flex-align:center;align-items:center;border:1px solid #ddd;border-radius:3px;cursor:pointer;display:none;font-size:12px;justify-content:flex-end;padding:4px 8px}.d2h-file-collapse.d2h-selected{background-color:#c8e1ff}.d2h-file-collapse-input{margin:0 4px 0 0}.d2h-diff-table{border-collapse:collapse;font-family:Menlo,Consolas,monospace;font-size:13px;width:100%}.d2h-files-diff{width:100%}.d2h-file-diff{overflow-y:hidden}.d2h-file-side-diff{display:inline-block;margin-bottom:-8px;margin-right:-4px;overflow-x:scroll;overflow-y:hidden;width:50%}.d2h-code-line{padding:0 8em}.d2h-code-line,.d2h-code-side-line{display:inline-block;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;white-space:nowrap;width:100%}.d2h-code-side-line{padding:0 4.5em}.d2h-code-line-ctn{word-wrap:normal;background:none;display:inline-block;padding:0;-webkit-user-select:text;-moz-user-select:text;-ms-user-select:text;user-select:text;vertical-align:middle;white-space:pre;width:100%}.d2h-code-line del,.d2h-code-side-line del{background-color:#ffb6ba}.d2h-code-line del,.d2h-code-line ins,.d2h-code-side-line del,.d2h-code-side-line ins{border-radius:.2em;display:inline-block;margin-top:-1px;text-decoration:none;vertical-align:middle}.d2h-code-line ins,.d2h-code-side-line ins{background-color:#97f295;text-align:left}.d2h-code-line-prefix{word-wrap:normal;background:none;display:inline;padding:0;white-space:pre}.line-num1{float:left}.line-num1,.line-num2{-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;padding:0 .5em;text-overflow:ellipsis;width:3.5em}.line-num2{float:right}.d2h-code-linenumber{background-color:#fff;border:solid #eee;border-width:0 1px;-webkit-box-sizing:border-box;box-sizing:border-box;color:rgba(0,0,0,.3);cursor:pointer;display:inline-block;position:absolute;text-align:right;width:7.5em}.d2h-code-linenumber:after{content:"\200b"}.d2h-code-side-linenumber{background-color:#fff;border:solid #eee;border-width:0 1px;-webkit-box-sizing:border-box;box-sizing:border-box;color:rgba(0,0,0,.3);cursor:pointer;display:inline-block;overflow:hidden;padding:0 .5em;position:absolute;text-align:right;text-overflow:ellipsis;width:4em}.d2h-code-side-linenumber:after{content:"\200b"}.d2h-code-side-emptyplaceholder,.d2h-emptyplaceholder{background-color:#f1f1f1;border-color:#e1e1e1}.d2h-code-line-prefix,.d2h-code-linenumber,.d2h-code-side-linenumber,.d2h-emptyplaceholder{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.d2h-code-linenumber,.d2h-code-side-linenumber{direction:rtl}.d2h-del{background-color:#fee8e9;border-color:#e9aeae}.d2h-ins{background-color:#dfd;border-color:#b4e2b4}.d2h-info{background-color:#f8fafd;border-color:#d5e4f2;color:rgba(0,0,0,.3)}.d2h-file-diff .d2h-del.d2h-change{background-color:#fdf2d0}.d2h-file-diff .d2h-ins.d2h-change{background-color:#ded}.d2h-file-list-wrapper{margin-bottom:10px}.d2h-file-list-wrapper a{color:#3572b0;text-decoration:none}.d2h-file-list-wrapper a:visited{color:#3572b0}.d2h-file-list-header{text-align:left}.d2h-file-list-title{font-weight:700}.d2h-file-list-line{display:-webkit-box;display:-ms-flexbox;display:flex;text-align:left}.d2h-file-list{display:block;list-style:none;margin:0;padding:0}.d2h-file-list>li{border-bottom:1px solid #ddd;margin:0;padding:5px 10px}.d2h-file-list>li:last-child{border-bottom:none}.d2h-file-switch{cursor:pointer;display:none;font-size:10px}.d2h-icon{fill:currentColor;margin-right:10px;vertical-align:middle}.d2h-deleted{color:#c33}.d2h-added{color:#399839}.d2h-changed{color:#d0b44c}.d2h-moved{color:#3572b0}.d2h-tag{background-color:#fff;display:-webkit-box;display:-ms-flexbox;display:flex;font-size:10px;margin-left:5px;padding:0 2px}.d2h-deleted-tag{border:1px solid #c33}.d2h-added-tag{border:1px solid #399839}.d2h-changed-tag{border:1px solid #d0b44c}.d2h-moved-tag{border:1px solid #3572b0}';
3
+
4
+ function diff2htmlPage(content) {
5
+ return `<html><head><style>${diff2htmlCss}</style><body>${content}</body></html>`;
6
+ }
7
+
8
+ module.exports = diff2htmlPage;
@@ -0,0 +1,52 @@
1
+ const getChartExport = (title, config, imageFile) => {
2
+ return `<html>
3
+ <meta charset='utf-8'>
4
+
5
+ <head>
6
+ ${title ? `<title>${title}</title>` : ''}
7
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.6.0/chart.min.js" integrity="sha512-GMGzUEevhWh8Tc/njS0bDpwgxdCJLQBWG3Z2Ct+JGOpVnEmjvNx6ts4v6A2XJf1HOrtOsfhv3hBKpK9kE5z8AQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
8
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js" integrity="sha512-qTXRIMyZIFb8iQcfjXWCO8+M5Tbc38Qi5WzdPOYZHIlZpzBHG3L3by84BBBOiRGiEb7KKtAOAs5qYdUiZiQNNQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-adapter-moment/1.0.0/chartjs-adapter-moment.min.js" integrity="sha512-oh5t+CdSBsaVVAvxcZKy3XJdP7ZbYUBSRCXDTVn0ODewMDDNnELsrG9eDm8rVZAQg7RsDD/8K3MjPAFB13o6eA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
10
+ <style>
11
+ a { text-decoration: none }
12
+
13
+ .footer {
14
+ float: right;
15
+ font-family: Arial;
16
+ color: #888;
17
+ margin-top: 10px;
18
+ margin-right: 10px;
19
+ font-size: 10pt;
20
+ }
21
+ </style>
22
+
23
+ <script>
24
+ const config = ${JSON.stringify(config)};
25
+
26
+ function showChart() {
27
+ document.getElementById('myImage').style.display = "none";
28
+
29
+ const myChart = new Chart(
30
+ document.getElementById('myChart'),
31
+ config
32
+ );
33
+ }
34
+ </script>
35
+ </head>
36
+
37
+ <body onload="showChart()">
38
+ <img src="${imageFile}" id="myImage" />
39
+
40
+ <div>
41
+ <canvas id="myChart"></canvas>
42
+ </div>
43
+
44
+ <div class="footer">
45
+ Exported from <a href='https://dbgate.org/' target='_blank'>DbGate</a>, powered by <a href='https://www.chartjs.org/' target='_blank'>Chart.js</a>
46
+ </div>
47
+ </body>
48
+
49
+ </html>`;
50
+ };
51
+
52
+ module.exports = getChartExport;
@@ -4,18 +4,19 @@ const yaml = require('js-yaml');
4
4
  const { databaseInfoFromYamlModel, DatabaseAnalyser } = require('dbgate-tools');
5
5
  const { startsWith } = require('lodash');
6
6
  const { archivedir, resolveArchiveFolder } = require('./directories');
7
+ const loadFilesRecursive = require('./loadFilesRecursive');
7
8
 
8
9
  async function importDbModel(inputDir) {
9
10
  const files = [];
10
11
 
11
12
  const dir = inputDir.startsWith('archive:') ? resolveArchiveFolder(inputDir.substring('archive:'.length)) : inputDir;
12
13
 
13
- for (const name of await fs.readdir(dir)) {
14
+ for (const name of await loadFilesRecursive(dir)) {
14
15
  if (name.endsWith('.table.yaml') || name.endsWith('.sql')) {
15
16
  const text = await fs.readFile(path.join(dir, name), { encoding: 'utf-8' });
16
17
 
17
18
  files.push({
18
- name,
19
+ name: path.parse(name).base,
19
20
  text,
20
21
  json: name.endsWith('.yaml') ? yaml.load(text) : null,
21
22
  });
@@ -0,0 +1,20 @@
1
+ const fs = require('fs-extra');
2
+ const stream = require('stream');
3
+ const readline = require('readline');
4
+ const path = require('path');
5
+
6
+ async function loadFilesRecursive(dir, root = null) {
7
+ if (!root) root = dir;
8
+ if (!root.endsWith(path.sep)) root += path.sep;
9
+ const dirents = await fs.readdir(dir, { withFileTypes: true });
10
+ const files = await Promise.all(
11
+ dirents.map(dirent => {
12
+ const res = path.join(dir, dirent.name);
13
+ return dirent.isDirectory() ? loadFilesRecursive(res, root) : res;
14
+ })
15
+ );
16
+ const flatten = Array.prototype.concat(...files);
17
+ return flatten.map(file => (file.startsWith(root) ? file.substr(root.length) : file));
18
+ }
19
+
20
+ module.exports = loadFilesRecursive;