dbgate-api 4.4.0-alpha.2 → 4.4.3
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 +7 -4
- package/src/controllers/archive.js +27 -6
- package/src/controllers/connections.js +41 -1
- package/src/controllers/databaseConnections.js +78 -8
- package/src/controllers/files.js +37 -1
- package/src/controllers/uploads.js +8 -0
- package/src/currentVersion.js +2 -2
- package/src/proc/databaseConnectionProcess.js +40 -2
- package/src/shell/fakeObjectReader.js +1 -1
- package/src/shell/generateDeploySql.js +16 -7
- package/src/shell/tableWriter.js +6 -4
- package/src/utility/diff2htmlPage.js +8 -0
- package/src/utility/getChartExport.js +52 -0
- package/src/utility/importDbModel.js +3 -2
- package/src/utility/loadFilesRecursive.js +20 -0
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.
|
|
4
|
+
"version": "4.4.3",
|
|
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-
|
|
30
|
-
"dbgate-
|
|
29
|
+
"dbgate-query-splitter": "^4.4.3",
|
|
30
|
+
"dbgate-sqltree": "^4.4.3",
|
|
31
|
+
"dbgate-tools": "^4.4.3",
|
|
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.
|
|
68
|
+
"dbgate-types": "^4.4.3",
|
|
66
69
|
"env-cmd": "^10.1.0",
|
|
67
70
|
"node-loader": "^1.0.2",
|
|
68
71
|
"nodemon": "^2.0.2",
|
|
@@ -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
|
|
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}
|
|
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(), `${
|
|
149
|
+
while (await fs.exists(path.join(archivedir(), `${name}${index}${suffix}`))) {
|
|
129
150
|
index += 1;
|
|
130
151
|
}
|
|
131
|
-
return `${
|
|
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');
|
|
@@ -161,6 +162,29 @@ module.exports = {
|
|
|
161
162
|
return res;
|
|
162
163
|
},
|
|
163
164
|
|
|
165
|
+
update_meta: 'post',
|
|
166
|
+
async update({ _id, values }) {
|
|
167
|
+
if (portalConnections) return;
|
|
168
|
+
const res = await this.datastore.update({ _id }, { $set: values });
|
|
169
|
+
socket.emitChanged('connection-list-changed');
|
|
170
|
+
return res;
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
updateDatabase_meta: 'post',
|
|
174
|
+
async updateDatabase({ conid, database, values }) {
|
|
175
|
+
if (portalConnections) return;
|
|
176
|
+
const conn = await this.datastore.find({ _id: conid });
|
|
177
|
+
let databases = conn.databases || [];
|
|
178
|
+
if (databases.find(x => x.name == database)) {
|
|
179
|
+
databases = databases.map(x => (x.item == database ? { ...x, ...values } : x));
|
|
180
|
+
} else {
|
|
181
|
+
databases = [...databases, { name: database, ...values }];
|
|
182
|
+
}
|
|
183
|
+
const res = await this.datastore.update({ _id: conid }, { $set: { databases } });
|
|
184
|
+
socket.emitChanged('connection-list-changed');
|
|
185
|
+
return res;
|
|
186
|
+
},
|
|
187
|
+
|
|
164
188
|
delete_meta: 'post',
|
|
165
189
|
async delete(connection) {
|
|
166
190
|
if (portalConnections) return;
|
|
@@ -175,4 +199,20 @@ module.exports = {
|
|
|
175
199
|
const res = await this.datastore.find({ _id: conid });
|
|
176
200
|
return res[0];
|
|
177
201
|
},
|
|
202
|
+
|
|
203
|
+
newSqliteDatabase_meta: 'post',
|
|
204
|
+
async newSqliteDatabase({ file }) {
|
|
205
|
+
const sqliteDir = path.join(filesdir(), 'sqlite');
|
|
206
|
+
if (!(await fs.exists(sqliteDir))) {
|
|
207
|
+
await fs.mkdir(sqliteDir);
|
|
208
|
+
}
|
|
209
|
+
const databaseFile = path.join(sqliteDir, `${file}.sqlite`);
|
|
210
|
+
const res = await this.save({
|
|
211
|
+
engine: 'sqlite@dbgate-plugin-sqlite',
|
|
212
|
+
databaseFile,
|
|
213
|
+
singleDatabase: true,
|
|
214
|
+
defaultDatabase: `${file}.sqlite`,
|
|
215
|
+
});
|
|
216
|
+
return res;
|
|
217
|
+
},
|
|
178
218
|
};
|
|
@@ -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 {
|
|
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
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
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
|
};
|
package/src/controllers/files.js
CHANGED
|
@@ -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
|
};
|
package/src/currentVersion.js
CHANGED
|
@@ -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(
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
|
package/src/shell/tableWriter.js
CHANGED
|
@@ -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
|
-
|
|
10
|
-
|
|
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
|
|
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;
|