dbgate-api-premium 5.5.7-alpha.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +19 -0
- package/.yarnrc +2 -0
- package/README.md +1 -0
- package/env/dblogin/.env +14 -0
- package/env/portal/.env +70 -0
- package/env/singledb/.env +17 -0
- package/env/storage/.env +43 -0
- package/package.json +89 -0
- package/src/auth/authCommon.js +16 -0
- package/src/auth/authProvider.js +343 -0
- package/src/auth/storageAuthProvider.js +393 -0
- package/src/controllers/apps.js +280 -0
- package/src/controllers/archive.js +217 -0
- package/src/controllers/auth.js +136 -0
- package/src/controllers/config.js +271 -0
- package/src/controllers/connections.js +486 -0
- package/src/controllers/databaseConnections.js +561 -0
- package/src/controllers/files.js +222 -0
- package/src/controllers/jsldata.js +296 -0
- package/src/controllers/metadata.js +47 -0
- package/src/controllers/plugins.js +216 -0
- package/src/controllers/queryHistory.js +54 -0
- package/src/controllers/runners.js +234 -0
- package/src/controllers/scheduler.js +46 -0
- package/src/controllers/serverConnections.js +271 -0
- package/src/controllers/sessions.js +243 -0
- package/src/controllers/storage.js +380 -0
- package/src/controllers/storageDb.js +215 -0
- package/src/controllers/uploads.js +133 -0
- package/src/currentVersion.js +5 -0
- package/src/gistSecret.js +2 -0
- package/src/index.js +139 -0
- package/src/main.js +202 -0
- package/src/packagedPluginsContent.js +1 -0
- package/src/proc/connectProcess.js +38 -0
- package/src/proc/databaseConnectionProcess.js +431 -0
- package/src/proc/index.js +15 -0
- package/src/proc/jslDatastoreProcess.js +60 -0
- package/src/proc/serverConnectionProcess.js +188 -0
- package/src/proc/sessionProcess.js +390 -0
- package/src/proc/sshForwardProcess.js +75 -0
- package/src/shell/archiveReader.js +11 -0
- package/src/shell/archiveWriter.js +22 -0
- package/src/shell/autoIndexForeignKeysTransform.js +19 -0
- package/src/shell/collectorWriter.js +33 -0
- package/src/shell/consoleObjectWriter.js +16 -0
- package/src/shell/copyStream.js +48 -0
- package/src/shell/dataDuplicator.js +63 -0
- package/src/shell/dataTypeMapperTransform.js +21 -0
- package/src/shell/dbModelToJson.js +16 -0
- package/src/shell/deployDb.js +56 -0
- package/src/shell/download.js +15 -0
- package/src/shell/dropAllDbObjects.js +42 -0
- package/src/shell/dumpDatabase.js +49 -0
- package/src/shell/executeQuery.js +39 -0
- package/src/shell/fakeObjectReader.js +35 -0
- package/src/shell/finalizer.js +12 -0
- package/src/shell/generateDeploySql.js +95 -0
- package/src/shell/generateModelSql.js +30 -0
- package/src/shell/importDatabase.js +85 -0
- package/src/shell/index.js +80 -0
- package/src/shell/initializeApiEnvironment.js +9 -0
- package/src/shell/jslDataReader.js +9 -0
- package/src/shell/jsonLinesReader.js +52 -0
- package/src/shell/jsonLinesWriter.js +36 -0
- package/src/shell/jsonReader.js +84 -0
- package/src/shell/jsonToDbModel.js +9 -0
- package/src/shell/jsonWriter.js +97 -0
- package/src/shell/loadDatabase.js +27 -0
- package/src/shell/loadFile.js +10 -0
- package/src/shell/modifyJsonLinesReader.js +148 -0
- package/src/shell/queryReader.js +30 -0
- package/src/shell/registerPlugins.js +9 -0
- package/src/shell/requirePlugin.js +43 -0
- package/src/shell/runScript.js +19 -0
- package/src/shell/sqlDataWriter.js +52 -0
- package/src/shell/sqlTextReplacementTransform.js +32 -0
- package/src/shell/tableReader.js +39 -0
- package/src/shell/tableWriter.js +18 -0
- package/src/storageModel.js +819 -0
- package/src/utility/ColumnMapTransformStream.js +21 -0
- package/src/utility/DatastoreProxy.js +106 -0
- package/src/utility/EnsureStreamHeaderStream.js +31 -0
- package/src/utility/JsonLinesDatabase.js +148 -0
- package/src/utility/JsonLinesDatastore.js +232 -0
- package/src/utility/LineReader.js +88 -0
- package/src/utility/SSHConnection.js +251 -0
- package/src/utility/authProxy.js +133 -0
- package/src/utility/checkLicense.js +186 -0
- package/src/utility/childProcessChecker.js +21 -0
- package/src/utility/cleanDirectory.js +24 -0
- package/src/utility/cloudUpgrade.js +61 -0
- package/src/utility/connectUtility.js +111 -0
- package/src/utility/crypting.js +105 -0
- package/src/utility/diff2htmlPage.js +8 -0
- package/src/utility/directories.js +179 -0
- package/src/utility/downloadPackage.js +51 -0
- package/src/utility/downloader.js +25 -0
- package/src/utility/exceptions.js +9 -0
- package/src/utility/exportDbModel.js +31 -0
- package/src/utility/exportDbModelSql.js +80 -0
- package/src/utility/getChartExport.js +55 -0
- package/src/utility/getDiagramExport.js +25 -0
- package/src/utility/getExpressPath.js +10 -0
- package/src/utility/getJslFileName.js +16 -0
- package/src/utility/getMapExport.js +77 -0
- package/src/utility/hardwareFingerprint.js +89 -0
- package/src/utility/hasPermission.js +101 -0
- package/src/utility/importDbModel.js +9 -0
- package/src/utility/loadFilesRecursive.js +20 -0
- package/src/utility/loadModelFolder.js +29 -0
- package/src/utility/loadModelTransform.js +36 -0
- package/src/utility/pipeForkLogs.js +19 -0
- package/src/utility/platformInfo.js +62 -0
- package/src/utility/processArgs.js +39 -0
- package/src/utility/processComm.js +18 -0
- package/src/utility/requireEngineDriver.js +26 -0
- package/src/utility/requirePluginFunction.js +16 -0
- package/src/utility/socket.js +68 -0
- package/src/utility/sshTunnel.js +106 -0
- package/src/utility/sshTunnelProxy.js +36 -0
- package/src/utility/timingSafeCheckToken.js +9 -0
- package/src/utility/useController.js +99 -0
- package/tsconfig.json +13 -0
- package/webpack.config.js +55 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const axios = require('axios');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { extractPackageName } = require('dbgate-tools');
|
|
5
|
+
const { pluginsdir, packagedPluginsDir } = require('../utility/directories');
|
|
6
|
+
const socket = require('../utility/socket');
|
|
7
|
+
const compareVersions = require('compare-versions');
|
|
8
|
+
const requirePlugin = require('../shell/requirePlugin');
|
|
9
|
+
const downloadPackage = require('../utility/downloadPackage');
|
|
10
|
+
const { hasPermission } = require('../utility/hasPermission');
|
|
11
|
+
const _ = require('lodash');
|
|
12
|
+
const packagedPluginsContent = require('../packagedPluginsContent');
|
|
13
|
+
|
|
14
|
+
module.exports = {
|
|
15
|
+
script_meta: true,
|
|
16
|
+
async script({ packageName }) {
|
|
17
|
+
const packagedContent = packagedPluginsContent();
|
|
18
|
+
|
|
19
|
+
if (packagedContent && packagedContent[packageName]) {
|
|
20
|
+
return packagedContent[packageName].frontend;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const file1 = path.join(packagedPluginsDir(), packageName, 'dist', 'frontend.js');
|
|
24
|
+
const file2 = path.join(pluginsdir(), packageName, 'dist', 'frontend.js');
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
const file = (await fs.exists(file1)) ? file1 : file2;
|
|
27
|
+
const data = await fs.readFile(file, {
|
|
28
|
+
encoding: 'utf-8',
|
|
29
|
+
});
|
|
30
|
+
return data;
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
search_meta: true,
|
|
34
|
+
async search({ filter }) {
|
|
35
|
+
// DOCS: https://github.com/npm/registry/blob/master/docs/REGISTRY-API.md#get-v1search
|
|
36
|
+
const resp = await axios.default.get(
|
|
37
|
+
`http://registry.npmjs.com/-/v1/search?text=${encodeURIComponent(filter)}+keywords:dbgateplugin&size=25&from=0`
|
|
38
|
+
);
|
|
39
|
+
const { objects } = resp.data || {};
|
|
40
|
+
return (objects || []).map(x => x.package);
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
info_meta: true,
|
|
44
|
+
async info({ packageName }) {
|
|
45
|
+
// @ts-ignore
|
|
46
|
+
const isPackaged = await fs.exists(path.join(packagedPluginsDir(), packageName));
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const infoResp = await axios.default.get(`https://registry.npmjs.org/${packageName}`);
|
|
50
|
+
const { latest } = infoResp.data['dist-tags'];
|
|
51
|
+
const manifest = infoResp.data.versions[latest];
|
|
52
|
+
const { readme } = infoResp.data;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
readme,
|
|
56
|
+
manifest,
|
|
57
|
+
isPackaged,
|
|
58
|
+
};
|
|
59
|
+
} catch (err) {
|
|
60
|
+
return {
|
|
61
|
+
isPackaged,
|
|
62
|
+
state: 'error',
|
|
63
|
+
error: err.message,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
installed_meta: true,
|
|
69
|
+
async installed() {
|
|
70
|
+
const packagedContent = packagedPluginsContent();
|
|
71
|
+
|
|
72
|
+
const files1 = packagedContent ? _.keys(packagedContent) : await fs.readdir(packagedPluginsDir());
|
|
73
|
+
const files2 = await fs.readdir(pluginsdir());
|
|
74
|
+
|
|
75
|
+
const res = [];
|
|
76
|
+
for (const packageName of _.union(files1, files2)) {
|
|
77
|
+
if (packageName == 'dist') continue;
|
|
78
|
+
if (!/^dbgate-plugin-.*$/.test(packageName)) continue;
|
|
79
|
+
try {
|
|
80
|
+
if (packagedContent && packagedContent[packageName]) {
|
|
81
|
+
const manifest = {
|
|
82
|
+
...packagedContent[packageName].manifest,
|
|
83
|
+
};
|
|
84
|
+
manifest.isPackaged = true;
|
|
85
|
+
manifest.readme = packagedContent[packageName].readme;
|
|
86
|
+
res.push(manifest);
|
|
87
|
+
} else {
|
|
88
|
+
const isPackaged = files1.includes(packageName);
|
|
89
|
+
const manifest = await fs
|
|
90
|
+
.readFile(path.join(isPackaged ? packagedPluginsDir() : pluginsdir(), packageName, 'package.json'), {
|
|
91
|
+
encoding: 'utf-8',
|
|
92
|
+
})
|
|
93
|
+
.then(x => JSON.parse(x));
|
|
94
|
+
if (!manifest.keywords) {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (!manifest.keywords.includes('dbgateplugin')) {
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
const readmeFile = path.join(isPackaged ? packagedPluginsDir() : pluginsdir(), packageName, 'README.md');
|
|
101
|
+
// @ts-ignore
|
|
102
|
+
if (await fs.exists(readmeFile)) {
|
|
103
|
+
manifest.readme = await fs.readFile(readmeFile, { encoding: 'utf-8' });
|
|
104
|
+
}
|
|
105
|
+
manifest.isPackaged = isPackaged;
|
|
106
|
+
res.push(manifest);
|
|
107
|
+
}
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.log(`Skipped plugin ${packageName}, error:`, err.message);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return res;
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
// async saveRemovePlugins() {
|
|
116
|
+
// await fs.writeFile(path.join(datadir(), 'removed-plugins'), this.removedPlugins.join('\n'));
|
|
117
|
+
// },
|
|
118
|
+
|
|
119
|
+
install_meta: true,
|
|
120
|
+
async install({ packageName }, req) {
|
|
121
|
+
if (!hasPermission(`plugins/install`, req)) return;
|
|
122
|
+
const dir = path.join(pluginsdir(), packageName);
|
|
123
|
+
// @ts-ignore
|
|
124
|
+
if (!(await fs.exists(dir))) {
|
|
125
|
+
await downloadPackage(packageName, dir);
|
|
126
|
+
}
|
|
127
|
+
socket.emitChanged(`installed-plugins-changed`);
|
|
128
|
+
// this.removedPlugins = this.removedPlugins.filter(x => x != packageName);
|
|
129
|
+
// await this.saveRemovePlugins();
|
|
130
|
+
return true;
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
uninstall_meta: true,
|
|
134
|
+
async uninstall({ packageName }, req) {
|
|
135
|
+
if (!hasPermission(`plugins/install`, req)) return;
|
|
136
|
+
const dir = path.join(pluginsdir(), packageName);
|
|
137
|
+
await fs.rmdir(dir, { recursive: true });
|
|
138
|
+
socket.emitChanged(`installed-plugins-changed`);
|
|
139
|
+
// this.removedPlugins.push(packageName);
|
|
140
|
+
// await this.saveRemovePlugins();
|
|
141
|
+
return true;
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
upgrade_meta: true,
|
|
145
|
+
async upgrade({ packageName }, req) {
|
|
146
|
+
if (!hasPermission(`plugins/install`, req)) return;
|
|
147
|
+
const dir = path.join(pluginsdir(), packageName);
|
|
148
|
+
// @ts-ignore
|
|
149
|
+
if (await fs.exists(dir)) {
|
|
150
|
+
await fs.rmdir(dir, { recursive: true });
|
|
151
|
+
await downloadPackage(packageName, dir);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
socket.emitChanged(`installed-plugins-changed`);
|
|
155
|
+
return true;
|
|
156
|
+
},
|
|
157
|
+
|
|
158
|
+
command_meta: true,
|
|
159
|
+
async command({ packageName, command, args }) {
|
|
160
|
+
const content = requirePlugin(packageName);
|
|
161
|
+
return content.commands[command](args);
|
|
162
|
+
},
|
|
163
|
+
|
|
164
|
+
authTypes_meta: true,
|
|
165
|
+
async authTypes({ engine }) {
|
|
166
|
+
const packageName = extractPackageName(engine);
|
|
167
|
+
if (!packageName) return null;
|
|
168
|
+
const content = requirePlugin(packageName);
|
|
169
|
+
const driver = content.drivers.find(x => x.engine == engine);
|
|
170
|
+
if (!driver || !driver.getAuthTypes) return null;
|
|
171
|
+
return driver.getAuthTypes() || null;
|
|
172
|
+
},
|
|
173
|
+
|
|
174
|
+
// async _init() {
|
|
175
|
+
// const installed = await this.installed();
|
|
176
|
+
// try {
|
|
177
|
+
// this.removedPlugins = (await fs.readFile(path.join(datadir(), 'removed-plugins'), { encoding: 'utf-8' })).split(
|
|
178
|
+
// '\n'
|
|
179
|
+
// );
|
|
180
|
+
// } catch (err) {
|
|
181
|
+
// this.removedPlugins = [];
|
|
182
|
+
// }
|
|
183
|
+
|
|
184
|
+
// for (const packageName of Object.keys(preinstallPluginMinimalVersions)) {
|
|
185
|
+
// const installedVersion = installed.find(x => x.name == packageName);
|
|
186
|
+
// if (installedVersion) {
|
|
187
|
+
// // plugin installed, test, whether upgrade
|
|
188
|
+
// const requiredVersion = preinstallPluginMinimalVersions[packageName];
|
|
189
|
+
// if (compareVersions(installedVersion.version, requiredVersion) < 0) {
|
|
190
|
+
// console.log(
|
|
191
|
+
// `Upgrading preinstalled plugin ${packageName}, found ${installedVersion.version}, required version ${requiredVersion}`
|
|
192
|
+
// );
|
|
193
|
+
// await this.upgrade({ packageName });
|
|
194
|
+
// } else {
|
|
195
|
+
// console.log(
|
|
196
|
+
// `Plugin ${packageName} not upgraded, found ${installedVersion.version}, required version ${requiredVersion}`
|
|
197
|
+
// );
|
|
198
|
+
// }
|
|
199
|
+
|
|
200
|
+
// continue;
|
|
201
|
+
// }
|
|
202
|
+
|
|
203
|
+
// if (this.removedPlugins.includes(packageName)) {
|
|
204
|
+
// // plugin was remvoed, don't install again
|
|
205
|
+
// continue;
|
|
206
|
+
// }
|
|
207
|
+
|
|
208
|
+
// try {
|
|
209
|
+
// console.log('Preinstalling plugin', packageName);
|
|
210
|
+
// await this.install({ packageName });
|
|
211
|
+
// } catch (err) {
|
|
212
|
+
// console.error('Error preinstalling plugin', packageName, err);
|
|
213
|
+
// }
|
|
214
|
+
// }
|
|
215
|
+
// },
|
|
216
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const fsReverse = require('fs-reverse');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { datadir } = require('../utility/directories');
|
|
5
|
+
const _ = require('lodash');
|
|
6
|
+
const { filterName } = require('dbgate-tools');
|
|
7
|
+
const socket = require('../utility/socket');
|
|
8
|
+
|
|
9
|
+
function readCore(reader, skip, limit, filter) {
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const res = [];
|
|
12
|
+
let readed = 0;
|
|
13
|
+
reader.on('data', line => {
|
|
14
|
+
if (!line && !line.trim()) return;
|
|
15
|
+
try {
|
|
16
|
+
const json = JSON.parse(line);
|
|
17
|
+
if (filterName(filter, json.sql, json.database)) {
|
|
18
|
+
if (!skip || readed >= skip) {
|
|
19
|
+
res.push(json);
|
|
20
|
+
}
|
|
21
|
+
readed++;
|
|
22
|
+
if (limit && readed > (skip || 0) + limit) {
|
|
23
|
+
reader.destroy();
|
|
24
|
+
resolve(res);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
} catch (err) {
|
|
28
|
+
reader.destroy();
|
|
29
|
+
reject(err);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
reader.on('end', () => resolve(res));
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
read_meta: true,
|
|
38
|
+
async read({ skip, limit, filter }) {
|
|
39
|
+
const fileName = path.join(datadir(), 'query-history.jsonl');
|
|
40
|
+
// @ts-ignore
|
|
41
|
+
if (!(await fs.exists(fileName))) return [];
|
|
42
|
+
const reader = fsReverse(fileName);
|
|
43
|
+
const res = await readCore(reader, skip, limit, filter);
|
|
44
|
+
return res;
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
write_meta: true,
|
|
48
|
+
async write({ data }) {
|
|
49
|
+
const fileName = path.join(datadir(), 'query-history.jsonl');
|
|
50
|
+
await fs.appendFile(fileName, JSON.stringify(data) + '\n');
|
|
51
|
+
socket.emit('query-history-changed');
|
|
52
|
+
return 'OK';
|
|
53
|
+
},
|
|
54
|
+
};
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const fs = require('fs-extra');
|
|
5
|
+
const byline = require('byline');
|
|
6
|
+
const socket = require('../utility/socket');
|
|
7
|
+
const { fork } = require('child_process');
|
|
8
|
+
const { rundir, uploadsdir, pluginsdir, getPluginBackendPath, packagedPluginList } = require('../utility/directories');
|
|
9
|
+
const {
|
|
10
|
+
extractShellApiPlugins,
|
|
11
|
+
extractShellApiFunctionName,
|
|
12
|
+
jsonScriptToJavascript,
|
|
13
|
+
getLogger,
|
|
14
|
+
safeJsonParse,
|
|
15
|
+
pinoLogRecordToMessageRecord,
|
|
16
|
+
} = require('dbgate-tools');
|
|
17
|
+
const { handleProcessCommunication } = require('../utility/processComm');
|
|
18
|
+
const processArgs = require('../utility/processArgs');
|
|
19
|
+
const platformInfo = require('../utility/platformInfo');
|
|
20
|
+
const logger = getLogger('runners');
|
|
21
|
+
|
|
22
|
+
function extractPlugins(script) {
|
|
23
|
+
const requireRegex = /\s*\/\/\s*@require\s+([^\s]+)\s*\n/g;
|
|
24
|
+
const matches = [...script.matchAll(requireRegex)];
|
|
25
|
+
return matches.map(x => x[1]);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const requirePluginsTemplate = (plugins, isExport) =>
|
|
29
|
+
plugins
|
|
30
|
+
.map(
|
|
31
|
+
packageName =>
|
|
32
|
+
`const ${_.camelCase(packageName)} = require(${
|
|
33
|
+
isExport ? `'${packageName}'` : `process.env.PLUGIN_${_.camelCase(packageName)}`
|
|
34
|
+
});\n`
|
|
35
|
+
)
|
|
36
|
+
.join('') + `dbgateApi.registerPlugins(${plugins.map(x => _.camelCase(x)).join(',')});\n`;
|
|
37
|
+
|
|
38
|
+
const scriptTemplate = (script, isExport) => `
|
|
39
|
+
const dbgateApi = require(${isExport ? `'dbgate-api'` : 'process.env.DBGATE_API'});
|
|
40
|
+
const logger = dbgateApi.getLogger('script');
|
|
41
|
+
dbgateApi.initializeApiEnvironment();
|
|
42
|
+
${requirePluginsTemplate(extractPlugins(script), isExport)}
|
|
43
|
+
require=null;
|
|
44
|
+
async function run() {
|
|
45
|
+
${script}
|
|
46
|
+
await dbgateApi.finalizer.run();
|
|
47
|
+
logger.info('Finished job script');
|
|
48
|
+
}
|
|
49
|
+
dbgateApi.runScript(run);
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
const loaderScriptTemplate = (functionName, props, runid) => `
|
|
53
|
+
const dbgateApi = require(process.env.DBGATE_API);
|
|
54
|
+
dbgateApi.initializeApiEnvironment();
|
|
55
|
+
${requirePluginsTemplate(extractShellApiPlugins(functionName, props))}
|
|
56
|
+
require=null;
|
|
57
|
+
async function run() {
|
|
58
|
+
const reader=await ${extractShellApiFunctionName(functionName)}(${JSON.stringify(props)});
|
|
59
|
+
const writer=await dbgateApi.collectorWriter({runid: '${runid}'});
|
|
60
|
+
await dbgateApi.copyStream(reader, writer);
|
|
61
|
+
}
|
|
62
|
+
dbgateApi.runScript(run);
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
module.exports = {
|
|
66
|
+
/** @type {import('dbgate-types').OpenedRunner[]} */
|
|
67
|
+
opened: [],
|
|
68
|
+
requests: {},
|
|
69
|
+
|
|
70
|
+
dispatchMessage(runid, message) {
|
|
71
|
+
if (message) {
|
|
72
|
+
if (_.isPlainObject(message)) logger.log(message);
|
|
73
|
+
else logger.info(message);
|
|
74
|
+
|
|
75
|
+
const toEmit = _.isPlainObject(message)
|
|
76
|
+
? {
|
|
77
|
+
time: new Date(),
|
|
78
|
+
...message,
|
|
79
|
+
}
|
|
80
|
+
: {
|
|
81
|
+
message,
|
|
82
|
+
time: new Date(),
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
if (toEmit.level >= 50) {
|
|
86
|
+
toEmit.severity = 'error';
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
socket.emit(`runner-info-${runid}`, toEmit);
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
handle_ping() {},
|
|
94
|
+
|
|
95
|
+
handle_freeData(runid, { freeData }) {
|
|
96
|
+
const [resolve, reject] = this.requests[runid];
|
|
97
|
+
resolve(freeData);
|
|
98
|
+
delete this.requests[runid];
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
rejectRequest(runid, error) {
|
|
102
|
+
if (this.requests[runid]) {
|
|
103
|
+
const [resolve, reject] = this.requests[runid];
|
|
104
|
+
reject(error);
|
|
105
|
+
delete this.requests[runid];
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
startCore(runid, scriptText) {
|
|
110
|
+
const directory = path.join(rundir(), runid);
|
|
111
|
+
const scriptFile = path.join(uploadsdir(), runid + '.js');
|
|
112
|
+
fs.writeFileSync(`${scriptFile}`, scriptText);
|
|
113
|
+
fs.mkdirSync(directory);
|
|
114
|
+
const pluginNames = _.union(fs.readdirSync(pluginsdir()), packagedPluginList);
|
|
115
|
+
logger.info({ scriptFile }, 'Running script');
|
|
116
|
+
// const subprocess = fork(scriptFile, ['--checkParent', '--max-old-space-size=8192'], {
|
|
117
|
+
const subprocess = fork(
|
|
118
|
+
scriptFile,
|
|
119
|
+
[
|
|
120
|
+
'--checkParent', // ...process.argv.slice(3)
|
|
121
|
+
'--is-forked-api',
|
|
122
|
+
'--process-display-name',
|
|
123
|
+
'script',
|
|
124
|
+
...processArgs.getPassArgs(),
|
|
125
|
+
],
|
|
126
|
+
{
|
|
127
|
+
cwd: directory,
|
|
128
|
+
stdio: ['ignore', 'pipe', 'pipe', 'ipc'],
|
|
129
|
+
env: {
|
|
130
|
+
...process.env,
|
|
131
|
+
DBGATE_API: global['API_PACKAGE'] || process.argv[1],
|
|
132
|
+
..._.fromPairs(pluginNames.map(name => [`PLUGIN_${_.camelCase(name)}`, getPluginBackendPath(name)])),
|
|
133
|
+
},
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
const pipeDispatcher = severity => data => {
|
|
137
|
+
const json = safeJsonParse(data, null);
|
|
138
|
+
|
|
139
|
+
if (json) {
|
|
140
|
+
return this.dispatchMessage(runid, pinoLogRecordToMessageRecord(json));
|
|
141
|
+
} else {
|
|
142
|
+
return this.dispatchMessage(runid, {
|
|
143
|
+
message: json == null ? data.toString().trim() : null,
|
|
144
|
+
severity,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
byline(subprocess.stdout).on('data', pipeDispatcher('info'));
|
|
150
|
+
byline(subprocess.stderr).on('data', pipeDispatcher('error'));
|
|
151
|
+
subprocess.on('exit', code => {
|
|
152
|
+
this.rejectRequest(runid, { message: 'No data retured, maybe input data source is too big' });
|
|
153
|
+
logger.info({ code, pid: subprocess.pid }, 'Exited process');
|
|
154
|
+
socket.emit(`runner-done-${runid}`, code);
|
|
155
|
+
});
|
|
156
|
+
subprocess.on('error', error => {
|
|
157
|
+
this.rejectRequest(runid, { message: error && (error.message || error.toString()) });
|
|
158
|
+
console.error('... ERROR subprocess', error);
|
|
159
|
+
this.dispatchMessage({
|
|
160
|
+
severity: 'error',
|
|
161
|
+
message: error.toString(),
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
const newOpened = {
|
|
165
|
+
runid,
|
|
166
|
+
subprocess,
|
|
167
|
+
};
|
|
168
|
+
this.opened.push(newOpened);
|
|
169
|
+
subprocess.on('message', message => {
|
|
170
|
+
// @ts-ignore
|
|
171
|
+
const { msgtype } = message;
|
|
172
|
+
if (handleProcessCommunication(message, subprocess)) return;
|
|
173
|
+
this[`handle_${msgtype}`](runid, message);
|
|
174
|
+
});
|
|
175
|
+
return _.pick(newOpened, ['runid']);
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
start_meta: true,
|
|
179
|
+
async start({ script }) {
|
|
180
|
+
const runid = crypto.randomUUID();
|
|
181
|
+
|
|
182
|
+
if (script.type == 'json') {
|
|
183
|
+
const js = jsonScriptToJavascript(script);
|
|
184
|
+
return this.startCore(runid, scriptTemplate(js, false));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!platformInfo.allowShellScripting) {
|
|
188
|
+
return { errorMessage: 'Shell scripting is not allowed' };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return this.startCore(runid, scriptTemplate(script, false));
|
|
192
|
+
},
|
|
193
|
+
|
|
194
|
+
getNodeScript_meta: true,
|
|
195
|
+
async getNodeScript({ script }) {
|
|
196
|
+
return scriptTemplate(script, true);
|
|
197
|
+
},
|
|
198
|
+
|
|
199
|
+
cancel_meta: true,
|
|
200
|
+
async cancel({ runid }) {
|
|
201
|
+
const runner = this.opened.find(x => x.runid == runid);
|
|
202
|
+
if (!runner) {
|
|
203
|
+
throw new Error('Invalid runner');
|
|
204
|
+
}
|
|
205
|
+
runner.subprocess.kill();
|
|
206
|
+
return { state: 'ok' };
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
files_meta: true,
|
|
210
|
+
async files({ runid }) {
|
|
211
|
+
const directory = path.join(rundir(), runid);
|
|
212
|
+
const files = await fs.readdir(directory);
|
|
213
|
+
const res = [];
|
|
214
|
+
for (const file of files) {
|
|
215
|
+
const stat = await fs.stat(path.join(directory, file));
|
|
216
|
+
res.push({
|
|
217
|
+
name: file,
|
|
218
|
+
size: stat.size,
|
|
219
|
+
path: path.join(directory, file),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
return res;
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
loadReader_meta: true,
|
|
226
|
+
async loadReader({ functionName, props }) {
|
|
227
|
+
const promise = new Promise((resolve, reject) => {
|
|
228
|
+
const runid = crypto.randomUUID();
|
|
229
|
+
this.requests[runid] = [resolve, reject];
|
|
230
|
+
this.startCore(runid, loaderScriptTemplate(functionName, props, runid));
|
|
231
|
+
});
|
|
232
|
+
return promise;
|
|
233
|
+
},
|
|
234
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const { filesdir } = require('../utility/directories');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const cron = require('node-cron');
|
|
5
|
+
const runners = require('./runners');
|
|
6
|
+
const { hasPermission } = require('../utility/hasPermission');
|
|
7
|
+
const { getLogger } = require('dbgate-tools');
|
|
8
|
+
|
|
9
|
+
const logger = getLogger('scheduler');
|
|
10
|
+
|
|
11
|
+
const scheduleRegex = /\s*\/\/\s*@schedule\s+([^\n]+)\n/;
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
tasks: [],
|
|
15
|
+
|
|
16
|
+
async unload() {
|
|
17
|
+
this.tasks.forEach(x => x.destroy());
|
|
18
|
+
this.tasks = [];
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
async processFile(file) {
|
|
22
|
+
const text = await fs.readFile(file, { encoding: 'utf-8' });
|
|
23
|
+
const match = text.match(scheduleRegex);
|
|
24
|
+
if (!match) return;
|
|
25
|
+
const pattern = match[1];
|
|
26
|
+
if (!cron.validate(pattern)) return;
|
|
27
|
+
logger.info(`Schedule script ${file} with pattern ${pattern}`);
|
|
28
|
+
const task = cron.schedule(pattern, () => runners.start({ script: text }));
|
|
29
|
+
this.tasks.push(task);
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
async reload(_params, req) {
|
|
33
|
+
if (!hasPermission('files/shell/read', req)) return;
|
|
34
|
+
const shellDir = path.join(filesdir(), 'shell');
|
|
35
|
+
await this.unload();
|
|
36
|
+
if (!(await fs.exists(shellDir))) return;
|
|
37
|
+
const files = await fs.readdir(shellDir);
|
|
38
|
+
for (const file of files) {
|
|
39
|
+
await this.processFile(path.join(shellDir, file));
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
async _init() {
|
|
44
|
+
this.reload();
|
|
45
|
+
},
|
|
46
|
+
};
|