dbgate-api 4.3.4 → 4.4.1
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 +8 -5
- package/src/controllers/archive.js +66 -14
- package/src/controllers/connections.js +18 -1
- package/src/controllers/databaseConnections.js +106 -1
- package/src/controllers/files.js +63 -14
- package/src/controllers/runners.js +14 -6
- package/src/controllers/uploads.js +8 -0
- package/src/currentVersion.js +2 -2
- package/src/shell/archiveReader.js +2 -2
- package/src/shell/archiveWriter.js +2 -2
- package/src/shell/deployDb.js +17 -0
- package/src/shell/executeQuery.js +4 -10
- package/src/shell/fakeObjectReader.js +1 -1
- package/src/shell/generateDeploySql.js +49 -0
- package/src/shell/index.js +2 -0
- package/src/shell/tableWriter.js +6 -4
- package/src/utility/diff2htmlPage.js +8 -0
- package/src/utility/directories.js +18 -0
- package/src/utility/exportDbModel.js +31 -0
- package/src/utility/getChartExport.js +52 -0
- package/src/utility/getJslFileName.js +2 -2
- package/src/utility/importDbModel.js +29 -0
- 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
|
+
"version": "4.4.1",
|
|
5
5
|
"homepage": "https://dbgate.org/",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -26,9 +26,11 @@
|
|
|
26
26
|
"compare-versions": "^3.6.0",
|
|
27
27
|
"cors": "^2.8.5",
|
|
28
28
|
"cross-env": "^6.0.3",
|
|
29
|
-
"dbgate-query-splitter": "^4.
|
|
30
|
-
"dbgate-sqltree": "^4.
|
|
31
|
-
"dbgate-tools": "^4.
|
|
29
|
+
"dbgate-query-splitter": "^4.4.1",
|
|
30
|
+
"dbgate-sqltree": "^4.4.1",
|
|
31
|
+
"dbgate-tools": "^4.4.1",
|
|
32
|
+
"diff": "^5.0.0",
|
|
33
|
+
"diff2html": "^3.4.13",
|
|
32
34
|
"eslint": "^6.8.0",
|
|
33
35
|
"express": "^4.17.1",
|
|
34
36
|
"express-basic-auth": "^1.2.0",
|
|
@@ -37,6 +39,7 @@
|
|
|
37
39
|
"fs-reverse": "^0.0.3",
|
|
38
40
|
"get-port": "^5.1.1",
|
|
39
41
|
"http": "^0.0.0",
|
|
42
|
+
"js-yaml": "^4.1.0",
|
|
40
43
|
"json-stable-stringify": "^1.0.1",
|
|
41
44
|
"line-reader": "^0.4.0",
|
|
42
45
|
"lodash": "^4.17.21",
|
|
@@ -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.
|
|
68
|
+
"dbgate-types": "^4.4.1",
|
|
66
69
|
"env-cmd": "^10.1.0",
|
|
67
70
|
"node-loader": "^1.0.2",
|
|
68
71
|
"nodemon": "^2.0.2",
|
|
@@ -3,10 +3,11 @@ const stream = require('stream');
|
|
|
3
3
|
const readline = require('readline');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const { formatWithOptions } = require('util');
|
|
6
|
-
const { archivedir } = require('../utility/directories');
|
|
6
|
+
const { archivedir, clearArchiveLinksCache, resolveArchiveFolder } = require('../utility/directories');
|
|
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',
|
|
@@ -33,17 +34,40 @@ module.exports = {
|
|
|
33
34
|
return true;
|
|
34
35
|
},
|
|
35
36
|
|
|
37
|
+
createLink_meta: 'post',
|
|
38
|
+
async createLink({ linkedFolder }) {
|
|
39
|
+
const folder = await this.getNewArchiveFolder({ database: path.parse(linkedFolder).name + '.link' });
|
|
40
|
+
fs.writeFile(path.join(archivedir(), folder), linkedFolder);
|
|
41
|
+
clearArchiveLinksCache();
|
|
42
|
+
socket.emitChanged('archive-folders-changed');
|
|
43
|
+
return folder;
|
|
44
|
+
},
|
|
45
|
+
|
|
36
46
|
files_meta: 'get',
|
|
37
47
|
async files({ folder }) {
|
|
38
|
-
const dir =
|
|
48
|
+
const dir = resolveArchiveFolder(folder);
|
|
39
49
|
if (!(await fs.exists(dir))) return [];
|
|
40
|
-
const files = await fs.readdir(dir);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
name
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
const files = await loadFilesRecursive(dir); // fs.readdir(dir);
|
|
51
|
+
|
|
52
|
+
function fileType(ext, type) {
|
|
53
|
+
return files
|
|
54
|
+
.filter(name => name.endsWith(ext))
|
|
55
|
+
.map(name => ({
|
|
56
|
+
name: name.slice(0, -ext.length),
|
|
57
|
+
label: path.parse(name.slice(0, -ext.length)).base,
|
|
58
|
+
type,
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return [
|
|
63
|
+
...fileType('.jsonl', 'jsonl'),
|
|
64
|
+
...fileType('.table.yaml', 'table.yaml'),
|
|
65
|
+
...fileType('.view.sql', 'view.sql'),
|
|
66
|
+
...fileType('.proc.sql', 'proc.sql'),
|
|
67
|
+
...fileType('.func.sql', 'func.sql'),
|
|
68
|
+
...fileType('.trigger.sql', 'trigger.sql'),
|
|
69
|
+
...fileType('.matview.sql', 'matview.sql'),
|
|
70
|
+
];
|
|
47
71
|
},
|
|
48
72
|
|
|
49
73
|
refreshFiles_meta: 'post',
|
|
@@ -57,28 +81,47 @@ module.exports = {
|
|
|
57
81
|
},
|
|
58
82
|
|
|
59
83
|
deleteFile_meta: 'post',
|
|
60
|
-
async deleteFile({ folder, file }) {
|
|
61
|
-
await fs.unlink(path.join(
|
|
84
|
+
async deleteFile({ folder, file, fileType }) {
|
|
85
|
+
await fs.unlink(path.join(resolveArchiveFolder(folder), `${file}.${fileType}`));
|
|
62
86
|
socket.emitChanged(`archive-files-changed-${folder}`);
|
|
63
87
|
},
|
|
64
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
|
+
);
|
|
95
|
+
socket.emitChanged(`archive-files-changed-${folder}`);
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
renameFolder_meta: 'post',
|
|
99
|
+
async renameFolder({ folder, newFolder }) {
|
|
100
|
+
await fs.rename(path.join(resolveArchiveFolder(folder)), path.join(resolveArchiveFolder(newFolder)));
|
|
101
|
+
socket.emitChanged(`archive-folders-changed`);
|
|
102
|
+
},
|
|
103
|
+
|
|
65
104
|
deleteFolder_meta: 'post',
|
|
66
105
|
async deleteFolder({ folder }) {
|
|
67
106
|
if (!folder) throw new Error('Missing folder parameter');
|
|
68
|
-
|
|
107
|
+
if (folder.endsWith('.link')) {
|
|
108
|
+
await fs.unlink(path.join(archivedir(), folder));
|
|
109
|
+
} else {
|
|
110
|
+
await fs.rmdir(path.join(archivedir(), folder), { recursive: true });
|
|
111
|
+
}
|
|
69
112
|
socket.emitChanged(`archive-folders-changed`);
|
|
70
113
|
},
|
|
71
114
|
|
|
72
115
|
saveFreeTable_meta: 'post',
|
|
73
116
|
async saveFreeTable({ folder, file, data }) {
|
|
74
|
-
saveFreeTableData(path.join(
|
|
117
|
+
saveFreeTableData(path.join(resolveArchiveFolder(folder), `${file}.jsonl`), data);
|
|
75
118
|
return true;
|
|
76
119
|
},
|
|
77
120
|
|
|
78
121
|
loadFreeTable_meta: 'post',
|
|
79
122
|
async loadFreeTable({ folder, file }) {
|
|
80
123
|
return new Promise((resolve, reject) => {
|
|
81
|
-
const fileStream = fs.createReadStream(path.join(
|
|
124
|
+
const fileStream = fs.createReadStream(path.join(resolveArchiveFolder(folder), `${file}.jsonl`));
|
|
82
125
|
const liner = readline.createInterface({
|
|
83
126
|
input: fileStream,
|
|
84
127
|
});
|
|
@@ -95,4 +138,13 @@ module.exports = {
|
|
|
95
138
|
});
|
|
96
139
|
});
|
|
97
140
|
},
|
|
141
|
+
|
|
142
|
+
async getNewArchiveFolder({ database }) {
|
|
143
|
+
if (!(await fs.exists(path.join(archivedir(), database)))) return database;
|
|
144
|
+
let index = 2;
|
|
145
|
+
while (await fs.exists(path.join(archivedir(), `${database}${index}`))) {
|
|
146
|
+
index += 1;
|
|
147
|
+
}
|
|
148
|
+
return `${database}${index}`;
|
|
149
|
+
},
|
|
98
150
|
};
|
|
@@ -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
|
};
|
|
@@ -1,10 +1,30 @@
|
|
|
1
1
|
const uuidv1 = require('uuid/v1');
|
|
2
2
|
const connections = require('./connections');
|
|
3
|
+
const archive = require('./archive');
|
|
3
4
|
const socket = require('../utility/socket');
|
|
4
5
|
const { fork } = require('child_process');
|
|
5
|
-
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');
|
|
6
17
|
const { handleProcessCommunication } = require('../utility/processComm');
|
|
7
18
|
const config = require('./config');
|
|
19
|
+
const fs = require('fs-extra');
|
|
20
|
+
const exportDbModel = require('../utility/exportDbModel');
|
|
21
|
+
const { archivedir, resolveArchiveFolder, uploadsdir } = require('../utility/directories');
|
|
22
|
+
const path = require('path');
|
|
23
|
+
const importDbModel = require('../utility/importDbModel');
|
|
24
|
+
const requireEngineDriver = require('../utility/requireEngineDriver');
|
|
25
|
+
const generateDeploySql = require('../shell/generateDeploySql');
|
|
26
|
+
const { createTwoFilesPatch } = require('diff');
|
|
27
|
+
const diff2htmlPage = require('../utility/diff2htmlPage');
|
|
8
28
|
|
|
9
29
|
module.exports = {
|
|
10
30
|
/** @type {import('dbgate-types').OpenedDatabaseConnection[]} */
|
|
@@ -212,6 +232,11 @@ module.exports = {
|
|
|
212
232
|
|
|
213
233
|
structure_meta: 'get',
|
|
214
234
|
async structure({ conid, database }) {
|
|
235
|
+
if (conid == '__model') {
|
|
236
|
+
const model = await importDbModel(database);
|
|
237
|
+
return model;
|
|
238
|
+
}
|
|
239
|
+
|
|
215
240
|
const opened = await this.ensureOpened(conid, database);
|
|
216
241
|
return opened.structure;
|
|
217
242
|
// const existing = this.opened.find((x) => x.conid == conid && x.database == database);
|
|
@@ -238,6 +263,38 @@ module.exports = {
|
|
|
238
263
|
return res;
|
|
239
264
|
},
|
|
240
265
|
|
|
266
|
+
exportModel_meta: 'post',
|
|
267
|
+
async exportModel({ conid, database }) {
|
|
268
|
+
const archiveFolder = await archive.getNewArchiveFolder({ database });
|
|
269
|
+
await fs.mkdir(path.join(archivedir(), archiveFolder));
|
|
270
|
+
const model = await this.structure({ conid, database });
|
|
271
|
+
await exportDbModel(model, path.join(archivedir(), archiveFolder));
|
|
272
|
+
socket.emitChanged(`archive-folders-changed`);
|
|
273
|
+
return { archiveFolder };
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
generateDeploySql_meta: 'post',
|
|
277
|
+
async generateDeploySql({ conid, database, archiveFolder }) {
|
|
278
|
+
const connection = await connections.get({ conid });
|
|
279
|
+
return generateDeploySql({
|
|
280
|
+
connection,
|
|
281
|
+
analysedStructure: await this.structure({ conid, database }),
|
|
282
|
+
modelFolder: resolveArchiveFolder(archiveFolder),
|
|
283
|
+
});
|
|
284
|
+
// const deployedModel = generateDbPairingId(await importDbModel(path.join(archivedir(), archiveFolder)));
|
|
285
|
+
// const currentModel = generateDbPairingId(await this.structure({ conid, database }));
|
|
286
|
+
// const currentModelPaired = matchPairedObjects(deployedModel, currentModel);
|
|
287
|
+
// const connection = await connections.get({ conid });
|
|
288
|
+
// const driver = requireEngineDriver(connection);
|
|
289
|
+
// const { sql } = getAlterDatabaseScript(currentModelPaired, deployedModel, {}, deployedModel, driver);
|
|
290
|
+
// return {
|
|
291
|
+
// deployedModel,
|
|
292
|
+
// currentModel,
|
|
293
|
+
// currentModelPaired,
|
|
294
|
+
// sql,
|
|
295
|
+
// };
|
|
296
|
+
// return sql;
|
|
297
|
+
},
|
|
241
298
|
// runCommand_meta: 'post',
|
|
242
299
|
// async runCommand({ conid, database, sql }) {
|
|
243
300
|
// console.log(`Running SQL command , conid=${conid}, database=${database}, sql=${sql}`);
|
|
@@ -245,4 +302,52 @@ module.exports = {
|
|
|
245
302
|
// const res = await this.sendRequest(opened, { msgtype: 'queryData', sql });
|
|
246
303
|
// return res;
|
|
247
304
|
// },
|
|
305
|
+
|
|
306
|
+
async getUnifiedDiff({ sourceConid, sourceDatabase, targetConid, targetDatabase }) {
|
|
307
|
+
const dbDiffOptions = sourceConid == '__model' ? modelCompareDbDiffOptions : {};
|
|
308
|
+
|
|
309
|
+
const sourceDb = generateDbPairingId(
|
|
310
|
+
extendDatabaseInfo(await this.structure({ conid: sourceConid, database: sourceDatabase }))
|
|
311
|
+
);
|
|
312
|
+
const targetDb = generateDbPairingId(
|
|
313
|
+
extendDatabaseInfo(await this.structure({ conid: targetConid, database: targetDatabase }))
|
|
314
|
+
);
|
|
315
|
+
// const sourceConnection = await connections.get({conid:sourceConid})
|
|
316
|
+
const connection = await connections.get({ conid: targetConid });
|
|
317
|
+
const driver = requireEngineDriver(connection);
|
|
318
|
+
const targetDbPaired = matchPairedObjects(sourceDb, targetDb, dbDiffOptions);
|
|
319
|
+
const diffRows = computeDbDiffRows(sourceDb, targetDbPaired, dbDiffOptions, driver);
|
|
320
|
+
|
|
321
|
+
// console.log('sourceDb', sourceDb);
|
|
322
|
+
// console.log('targetDb', targetDb);
|
|
323
|
+
// console.log('sourceConid, sourceDatabase', sourceConid, sourceDatabase);
|
|
324
|
+
|
|
325
|
+
let res = '';
|
|
326
|
+
for (const row of diffRows) {
|
|
327
|
+
// console.log('PAIR', row.source && row.source.pureName, row.target && row.target.pureName);
|
|
328
|
+
const unifiedDiff = createTwoFilesPatch(
|
|
329
|
+
(row.target && row.target.pureName) || '',
|
|
330
|
+
(row.source && row.source.pureName) || '',
|
|
331
|
+
getCreateObjectScript(row.target, driver),
|
|
332
|
+
getCreateObjectScript(row.source, driver),
|
|
333
|
+
'',
|
|
334
|
+
''
|
|
335
|
+
);
|
|
336
|
+
res += unifiedDiff;
|
|
337
|
+
}
|
|
338
|
+
return res;
|
|
339
|
+
},
|
|
340
|
+
|
|
341
|
+
generateDbDiffReport_meta: 'post',
|
|
342
|
+
async generateDbDiffReport({ filePath, sourceConid, sourceDatabase, targetConid, targetDatabase }) {
|
|
343
|
+
const unifiedDiff = await this.getUnifiedDiff({ sourceConid, sourceDatabase, targetConid, targetDatabase });
|
|
344
|
+
|
|
345
|
+
const diffJson = parse(unifiedDiff);
|
|
346
|
+
// $: diffHtml = html(diffJson, { outputFormat: 'side-by-side', drawFileList: false });
|
|
347
|
+
const diffHtml = html(diffJson, { outputFormat: 'side-by-side' });
|
|
348
|
+
|
|
349
|
+
await fs.writeFile(filePath, diff2htmlPage(diffHtml));
|
|
350
|
+
|
|
351
|
+
return true;
|
|
352
|
+
},
|
|
248
353
|
};
|
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 } = 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,25 +58,46 @@ 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
|
-
if (
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
if (folder.startsWith('archive:')) {
|
|
72
|
+
const text = await fs.readFile(path.join(resolveArchiveFolder(folder.substring('archive:'.length)), file), {
|
|
73
|
+
encoding: 'utf-8',
|
|
74
|
+
});
|
|
75
|
+
return deserialize(format, text);
|
|
76
|
+
} else {
|
|
77
|
+
if (!hasPermission(`files/${folder}/read`)) return null;
|
|
78
|
+
const text = await fs.readFile(path.join(filesdir(), folder, file), { encoding: 'utf-8' });
|
|
79
|
+
return deserialize(format, text);
|
|
80
|
+
}
|
|
64
81
|
},
|
|
65
82
|
|
|
66
83
|
save_meta: 'post',
|
|
67
84
|
async save({ folder, file, data, format }) {
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
if (folder.startsWith('archive:')) {
|
|
86
|
+
const dir = resolveArchiveFolder(folder.substring('archive:'.length));
|
|
87
|
+
await fs.writeFile(path.join(dir, file), serialize(format, data));
|
|
88
|
+
socket.emitChanged(`archive-files-changed-${folder.substring('archive:'.length)}`);
|
|
89
|
+
} else {
|
|
90
|
+
if (!hasPermission(`files/${folder}/write`)) return;
|
|
91
|
+
const dir = path.join(filesdir(), folder);
|
|
92
|
+
if (!(await fs.exists(dir))) {
|
|
93
|
+
await fs.mkdir(dir);
|
|
94
|
+
}
|
|
95
|
+
await fs.writeFile(path.join(dir, file), serialize(format, data));
|
|
96
|
+
socket.emitChanged(`files-changed-${folder}`);
|
|
97
|
+
socket.emitChanged(`all-files-changed`);
|
|
98
|
+
if (folder == 'shell') {
|
|
99
|
+
scheduler.reload();
|
|
100
|
+
}
|
|
78
101
|
}
|
|
79
102
|
},
|
|
80
103
|
|
|
@@ -101,4 +124,30 @@ module.exports = {
|
|
|
101
124
|
}
|
|
102
125
|
return res;
|
|
103
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
|
+
},
|
|
104
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 =>
|
|
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',
|
package/src/currentVersion.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
-
const { archivedir } = require('../utility/directories');
|
|
2
|
+
const { archivedir, resolveArchiveFolder } = require('../utility/directories');
|
|
3
3
|
const jsonLinesReader = require('./jsonLinesReader');
|
|
4
4
|
|
|
5
5
|
function archiveReader({ folderName, fileName, ...other }) {
|
|
6
|
-
const jsonlFile = path.join(
|
|
6
|
+
const jsonlFile = path.join(resolveArchiveFolder(folderName), `${fileName}.jsonl`);
|
|
7
7
|
const res = jsonLinesReader({ fileName: jsonlFile, ...other });
|
|
8
8
|
return res;
|
|
9
9
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs');
|
|
3
|
-
const { archivedir } = require('../utility/directories');
|
|
3
|
+
const { archivedir, resolveArchiveFolder } = require('../utility/directories');
|
|
4
4
|
// const socket = require('../utility/socket');
|
|
5
5
|
const jsonLinesWriter = require('./jsonLinesWriter');
|
|
6
6
|
|
|
7
7
|
function archiveWriter({ folderName, fileName }) {
|
|
8
|
-
const dir =
|
|
8
|
+
const dir = resolveArchiveFolder(folderName);
|
|
9
9
|
if (!fs.existsSync(dir)) {
|
|
10
10
|
console.log(`Creating directory ${dir}`);
|
|
11
11
|
fs.mkdirSync(dir);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const generateDeploySql = require('./generateDeploySql');
|
|
2
|
+
const executeQuery = require('./executeQuery');
|
|
3
|
+
|
|
4
|
+
async function deployDb({ connection, systemConnection, driver, analysedStructure, modelFolder, loadedDbModel }) {
|
|
5
|
+
const { sql } = await generateDeploySql({
|
|
6
|
+
connection,
|
|
7
|
+
systemConnection,
|
|
8
|
+
driver,
|
|
9
|
+
analysedStructure,
|
|
10
|
+
modelFolder,
|
|
11
|
+
loadedDbModel,
|
|
12
|
+
});
|
|
13
|
+
// console.log('RUNNING DEPLOY SCRIPT:', sql);
|
|
14
|
+
await executeQuery({ connection, systemConnection, driver, sql });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = deployDb;
|
|
@@ -1,20 +1,14 @@
|
|
|
1
|
-
const { splitQuery } = require('dbgate-query-splitter');
|
|
2
1
|
const requireEngineDriver = require('../utility/requireEngineDriver');
|
|
3
2
|
const connectUtility = require('../utility/connectUtility');
|
|
4
3
|
|
|
5
|
-
async function executeQuery({ connection, sql }) {
|
|
4
|
+
async function executeQuery({ connection = undefined, systemConnection = undefined, driver = undefined, sql }) {
|
|
6
5
|
console.log(`Execute query ${sql}`);
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
const pool = await connectUtility(driver, connection);
|
|
7
|
+
if (!driver) driver = requireEngineDriver(connection);
|
|
8
|
+
const pool = systemConnection || (await connectUtility(driver, connection));
|
|
10
9
|
console.log(`Connected.`);
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
console.log('Executing query', sqlItem);
|
|
14
|
-
await driver.query(pool, sqlItem, { discardResult: true });
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
console.log(`Query finished`);
|
|
11
|
+
await driver.script(pool, sql);
|
|
18
12
|
}
|
|
19
13
|
|
|
20
14
|
module.exports = executeQuery;
|
|
@@ -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 });
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const {
|
|
2
|
+
getAlterDatabaseScript,
|
|
3
|
+
generateDbPairingId,
|
|
4
|
+
matchPairedObjects,
|
|
5
|
+
databaseInfoFromYamlModel,
|
|
6
|
+
extendDatabaseInfo,
|
|
7
|
+
modelCompareDbDiffOptions,
|
|
8
|
+
} = require('dbgate-tools');
|
|
9
|
+
const importDbModel = require('../utility/importDbModel');
|
|
10
|
+
const requireEngineDriver = require('../utility/requireEngineDriver');
|
|
11
|
+
const connectUtility = require('../utility/connectUtility');
|
|
12
|
+
|
|
13
|
+
async function generateDeploySql({
|
|
14
|
+
connection,
|
|
15
|
+
systemConnection = undefined,
|
|
16
|
+
driver = undefined,
|
|
17
|
+
analysedStructure = undefined,
|
|
18
|
+
modelFolder = undefined,
|
|
19
|
+
loadedDbModel = undefined,
|
|
20
|
+
}) {
|
|
21
|
+
if (!driver) driver = requireEngineDriver(connection);
|
|
22
|
+
if (!analysedStructure) {
|
|
23
|
+
const pool = systemConnection || (await connectUtility(driver, connection));
|
|
24
|
+
analysedStructure = await driver.analyseFull(pool);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const deployedModel = generateDbPairingId(
|
|
28
|
+
extendDatabaseInfo(loadedDbModel ? databaseInfoFromYamlModel(loadedDbModel) : await importDbModel(modelFolder))
|
|
29
|
+
);
|
|
30
|
+
const currentModel = generateDbPairingId(extendDatabaseInfo(analysedStructure));
|
|
31
|
+
const opts = {
|
|
32
|
+
...modelCompareDbDiffOptions,
|
|
33
|
+
|
|
34
|
+
noDropTable: true,
|
|
35
|
+
noDropColumn: true,
|
|
36
|
+
noDropConstraint: true,
|
|
37
|
+
noDropSqlObject: true,
|
|
38
|
+
noRenameTable: true,
|
|
39
|
+
noRenameColumn: true,
|
|
40
|
+
};
|
|
41
|
+
const currentModelPaired = matchPairedObjects(deployedModel, currentModel, opts);
|
|
42
|
+
// console.log('deployedModel', deployedModel.tables[0]);
|
|
43
|
+
// console.log('currentModel', currentModel.tables[0]);
|
|
44
|
+
// console.log('currentModelPaired', currentModelPaired.tables[0]);
|
|
45
|
+
const res = getAlterDatabaseScript(currentModelPaired, deployedModel, opts, deployedModel, driver);
|
|
46
|
+
return res;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
module.exports = generateDeploySql;
|
package/src/shell/index.js
CHANGED
|
@@ -19,6 +19,7 @@ const requirePlugin = require('./requirePlugin');
|
|
|
19
19
|
const download = require('./download');
|
|
20
20
|
const executeQuery = require('./executeQuery');
|
|
21
21
|
const loadFile = require('./loadFile');
|
|
22
|
+
const deployDb = require('./deployDb');
|
|
22
23
|
const initializeApiEnvironment = require('./initializeApiEnvironment');
|
|
23
24
|
|
|
24
25
|
const dbgateApi = {
|
|
@@ -42,6 +43,7 @@ const dbgateApi = {
|
|
|
42
43
|
registerPlugins,
|
|
43
44
|
executeQuery,
|
|
44
45
|
loadFile,
|
|
46
|
+
deployDb,
|
|
45
47
|
initializeApiEnvironment,
|
|
46
48
|
};
|
|
47
49
|
|
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;
|
|
@@ -71,6 +71,22 @@ function getPluginBackendPath(packageName) {
|
|
|
71
71
|
return path.join(pluginsdir(), packageName, 'dist', 'backend.js');
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
let archiveLinksCache = {};
|
|
75
|
+
|
|
76
|
+
function resolveArchiveFolder(folder) {
|
|
77
|
+
if (folder.endsWith('.link')) {
|
|
78
|
+
if (!archiveLinksCache[folder]) {
|
|
79
|
+
archiveLinksCache[folder] = fs.readFileSync(path.join(archivedir(), folder), 'utf-8');
|
|
80
|
+
}
|
|
81
|
+
return archiveLinksCache[folder];
|
|
82
|
+
}
|
|
83
|
+
return path.join(archivedir(), folder);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function clearArchiveLinksCache() {
|
|
87
|
+
archiveLinksCache = {};
|
|
88
|
+
}
|
|
89
|
+
|
|
74
90
|
module.exports = {
|
|
75
91
|
datadir,
|
|
76
92
|
jsldir,
|
|
@@ -83,4 +99,6 @@ module.exports = {
|
|
|
83
99
|
packagedPluginsDir,
|
|
84
100
|
packagedPluginList,
|
|
85
101
|
getPluginBackendPath,
|
|
102
|
+
resolveArchiveFolder,
|
|
103
|
+
clearArchiveLinksCache,
|
|
86
104
|
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const yaml = require('js-yaml');
|
|
4
|
+
const { tableInfoToYaml } = require('dbgate-tools');
|
|
5
|
+
|
|
6
|
+
async function exportDbModel(dbModel, outputDir) {
|
|
7
|
+
const { tables, views, procedures, functions, triggers, matviews } = dbModel;
|
|
8
|
+
|
|
9
|
+
if (!fs.existsSync(outputDir)) {
|
|
10
|
+
await fs.mkdir(outputDir);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
for (const table of tables || []) {
|
|
14
|
+
const content = yaml.dump(tableInfoToYaml(table));
|
|
15
|
+
await fs.writeFile(path.join(outputDir, `${table.pureName}.table.yaml`), content);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function writeList(list, ext) {
|
|
19
|
+
for (const obj of list || []) {
|
|
20
|
+
await fs.writeFile(path.join(outputDir, `${obj.pureName}.${ext}.sql`), obj.createSql);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
await writeList(views, 'view');
|
|
25
|
+
await writeList(procedures, 'proc');
|
|
26
|
+
await writeList(functions, 'func');
|
|
27
|
+
await writeList(triggers, 'trigger');
|
|
28
|
+
await writeList(matviews, 'matview');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = exportDbModel;
|
|
@@ -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;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
|
-
const { jsldir, archivedir } = require('./directories');
|
|
2
|
+
const { jsldir, archivedir, resolveArchiveFolder } = require('./directories');
|
|
3
3
|
|
|
4
4
|
function getJslFileName(jslid) {
|
|
5
5
|
const archiveMatch = jslid.match(/^archive:\/\/([^/]+)\/(.*)$/);
|
|
6
6
|
if (archiveMatch) {
|
|
7
|
-
return path.join(
|
|
7
|
+
return path.join(resolveArchiveFolder(archiveMatch[1]), `${archiveMatch[2]}.jsonl`);
|
|
8
8
|
}
|
|
9
9
|
return path.join(jsldir(), `${jslid}.jsonl`);
|
|
10
10
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const yaml = require('js-yaml');
|
|
4
|
+
const { databaseInfoFromYamlModel, DatabaseAnalyser } = require('dbgate-tools');
|
|
5
|
+
const { startsWith } = require('lodash');
|
|
6
|
+
const { archivedir, resolveArchiveFolder } = require('./directories');
|
|
7
|
+
const loadFilesRecursive = require('./loadFilesRecursive');
|
|
8
|
+
|
|
9
|
+
async function importDbModel(inputDir) {
|
|
10
|
+
const files = [];
|
|
11
|
+
|
|
12
|
+
const dir = inputDir.startsWith('archive:') ? resolveArchiveFolder(inputDir.substring('archive:'.length)) : inputDir;
|
|
13
|
+
|
|
14
|
+
for (const name of await loadFilesRecursive(dir)) {
|
|
15
|
+
if (name.endsWith('.table.yaml') || name.endsWith('.sql')) {
|
|
16
|
+
const text = await fs.readFile(path.join(dir, name), { encoding: 'utf-8' });
|
|
17
|
+
|
|
18
|
+
files.push({
|
|
19
|
+
name: path.parse(name).base,
|
|
20
|
+
text,
|
|
21
|
+
json: name.endsWith('.yaml') ? yaml.load(text) : null,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return databaseInfoFromYamlModel(files);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports = importDbModel;
|
|
@@ -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;
|