dbgate-api 4.6.0 → 4.7.0
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-portal +18 -1
- package/package.json +5 -6
- package/src/controllers/apps.js +267 -0
- package/src/controllers/archive.js +7 -0
- package/src/controllers/connections.js +38 -12
- package/src/controllers/databaseConnections.js +2 -1
- package/src/controllers/files.js +13 -1
- package/src/controllers/jsldata.js +22 -3
- package/src/controllers/plugins.js +2 -1
- package/src/controllers/queryHistory.js +1 -1
- package/src/currentVersion.js +2 -2
- package/src/main.js +2 -0
- package/src/proc/databaseConnectionProcess.js +7 -1
- package/src/shell/copyStream.js +8 -3
- package/src/shell/jsonArrayWriter.js +1 -4
- package/src/shell/jsonLinesReader.js +7 -7
- package/src/shell/jsonLinesWriter.js +1 -5
- package/src/shell/sqlDataWriter.js +1 -5
- package/src/utility/EnsureStreamHeaderStream.js +1 -5
- package/src/utility/JsonLinesDatabase.js +142 -0
- package/src/utility/JsonLinesDatastore.js +28 -8
- package/src/utility/directories.js +2 -0
- package/src/utility/socket.js +3 -2
- package/webpack.config.js +7 -4
package/.env-portal
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
DEVMODE=1
|
|
2
2
|
|
|
3
|
-
CONNECTIONS=mysql,postgres,mongo,mongo2
|
|
3
|
+
CONNECTIONS=mysql,postgres,mongo,mongo2,mysqlssh,sqlite
|
|
4
4
|
|
|
5
5
|
LABEL_mysql=MySql localhost
|
|
6
6
|
SERVER_mysql=localhost
|
|
@@ -24,4 +24,21 @@ LABEL_mongo2=Mongo Server
|
|
|
24
24
|
SERVER_mongo2=localhost
|
|
25
25
|
ENGINE_mongo2=mongo@dbgate-plugin-mongo
|
|
26
26
|
|
|
27
|
+
LABEL_mysqlssh=MySql SSH
|
|
28
|
+
SERVER_mysqlssh=localhost
|
|
29
|
+
USER_mysqlssh=root
|
|
30
|
+
PASSWORD_mysqlssh=xxx
|
|
31
|
+
PORT_mysqlssh=3316
|
|
32
|
+
ENGINE_mysqlssh=mysql@dbgate-plugin-mysql
|
|
33
|
+
USE_SSH_mysqlssh=1
|
|
34
|
+
SSH_HOST_mysqlssh=demo.dbgate.org
|
|
35
|
+
SSH_PORT_mysqlssh=22
|
|
36
|
+
SSH_MODE_mysqlssh=userPassword
|
|
37
|
+
SSH_LOGIN_mysqlssh=root
|
|
38
|
+
SSH_PASSWORD_mysqlssh=xxx
|
|
39
|
+
|
|
40
|
+
LABEL_sqlite=sqlite
|
|
41
|
+
FILE_sqlite=/home/jena/dbgate-data/files/sqlite/feeds.sqlite
|
|
42
|
+
ENGINE_sqlite=sqlite@dbgate-plugin-sqlite
|
|
43
|
+
|
|
27
44
|
# docker run -p 3000:3000 -e CONNECTIONS=mongo -e URL_mongo=mongodb://localhost:27017 -e ENGINE_mongo=mongo@dbgate-plugin-mongo -e LABEL_mongo=mongo dbgate/dbgate:beta
|
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.7.0",
|
|
5
5
|
"homepage": "https://dbgate.org/",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"compare-versions": "^3.6.0",
|
|
26
26
|
"cors": "^2.8.5",
|
|
27
27
|
"cross-env": "^6.0.3",
|
|
28
|
-
"dbgate-query-splitter": "^4.
|
|
29
|
-
"dbgate-sqltree": "^4.
|
|
30
|
-
"dbgate-tools": "^4.
|
|
28
|
+
"dbgate-query-splitter": "^4.7.0",
|
|
29
|
+
"dbgate-sqltree": "^4.7.0",
|
|
30
|
+
"dbgate-tools": "^4.7.0",
|
|
31
31
|
"diff": "^5.0.0",
|
|
32
32
|
"diff2html": "^3.4.13",
|
|
33
33
|
"eslint": "^6.8.0",
|
|
@@ -44,7 +44,6 @@
|
|
|
44
44
|
"line-reader": "^0.4.0",
|
|
45
45
|
"lodash": "^4.17.21",
|
|
46
46
|
"ncp": "^2.0.0",
|
|
47
|
-
"nedb-promises": "^4.0.1",
|
|
48
47
|
"node-cron": "^2.0.3",
|
|
49
48
|
"node-ssh-forward": "^0.7.2",
|
|
50
49
|
"portfinder": "^1.0.28",
|
|
@@ -64,7 +63,7 @@
|
|
|
64
63
|
"devDependencies": {
|
|
65
64
|
"@types/fs-extra": "^9.0.11",
|
|
66
65
|
"@types/lodash": "^4.14.149",
|
|
67
|
-
"dbgate-types": "^4.
|
|
66
|
+
"dbgate-types": "^4.7.0",
|
|
68
67
|
"env-cmd": "^10.1.0",
|
|
69
68
|
"node-loader": "^1.0.2",
|
|
70
69
|
"nodemon": "^2.0.2",
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
const fs = require('fs-extra');
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { appdir } = require('../utility/directories');
|
|
5
|
+
const socket = require('../utility/socket');
|
|
6
|
+
const connections = require('./connections');
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
folders_meta: true,
|
|
10
|
+
async folders() {
|
|
11
|
+
const folders = await fs.readdir(appdir());
|
|
12
|
+
return [
|
|
13
|
+
...folders.map(name => ({
|
|
14
|
+
name,
|
|
15
|
+
})),
|
|
16
|
+
];
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
createFolder_meta: true,
|
|
20
|
+
async createFolder({ folder }) {
|
|
21
|
+
const name = await this.getNewAppFolder({ name: folder });
|
|
22
|
+
await fs.mkdir(path.join(appdir(), name));
|
|
23
|
+
socket.emitChanged('app-folders-changed');
|
|
24
|
+
this.emitChangedDbApp(folder);
|
|
25
|
+
return name;
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
files_meta: true,
|
|
29
|
+
async files({ folder }) {
|
|
30
|
+
const dir = path.join(appdir(), folder);
|
|
31
|
+
if (!(await fs.exists(dir))) return [];
|
|
32
|
+
const files = await fs.readdir(dir);
|
|
33
|
+
|
|
34
|
+
function fileType(ext, type) {
|
|
35
|
+
return files
|
|
36
|
+
.filter(name => name.endsWith(ext))
|
|
37
|
+
.map(name => ({
|
|
38
|
+
name: name.slice(0, -ext.length),
|
|
39
|
+
label: path.parse(name.slice(0, -ext.length)).base,
|
|
40
|
+
type,
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return [
|
|
45
|
+
...fileType('.command.sql', 'command.sql'),
|
|
46
|
+
...fileType('.query.sql', 'query.sql'),
|
|
47
|
+
...fileType('.config.json', 'config.json'),
|
|
48
|
+
];
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
async emitChangedDbApp(folder) {
|
|
52
|
+
const used = await this.getUsedAppFolders();
|
|
53
|
+
if (used.includes(folder)) {
|
|
54
|
+
socket.emitChanged('used-apps-changed');
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
refreshFiles_meta: true,
|
|
59
|
+
async refreshFiles({ folder }) {
|
|
60
|
+
socket.emitChanged(`app-files-changed-${folder}`);
|
|
61
|
+
},
|
|
62
|
+
|
|
63
|
+
refreshFolders_meta: true,
|
|
64
|
+
async refreshFolders() {
|
|
65
|
+
socket.emitChanged(`app-folders-changed`);
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
deleteFile_meta: true,
|
|
69
|
+
async deleteFile({ folder, file, fileType }) {
|
|
70
|
+
await fs.unlink(path.join(appdir(), folder, `${file}.${fileType}`));
|
|
71
|
+
socket.emitChanged(`app-files-changed-${folder}`);
|
|
72
|
+
this.emitChangedDbApp(folder);
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
renameFile_meta: true,
|
|
76
|
+
async renameFile({ folder, file, newFile, fileType }) {
|
|
77
|
+
await fs.rename(
|
|
78
|
+
path.join(path.join(appdir(), folder), `${file}.${fileType}`),
|
|
79
|
+
path.join(path.join(appdir(), folder), `${newFile}.${fileType}`)
|
|
80
|
+
);
|
|
81
|
+
socket.emitChanged(`app-files-changed-${folder}`);
|
|
82
|
+
this.emitChangedDbApp(folder);
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
renameFolder_meta: true,
|
|
86
|
+
async renameFolder({ folder, newFolder }) {
|
|
87
|
+
const uniqueName = await this.getNewAppFolder({ name: newFolder });
|
|
88
|
+
await fs.rename(path.join(appdir(), folder), path.join(appdir(), uniqueName));
|
|
89
|
+
socket.emitChanged(`app-folders-changed`);
|
|
90
|
+
},
|
|
91
|
+
|
|
92
|
+
deleteFolder_meta: true,
|
|
93
|
+
async deleteFolder({ folder }) {
|
|
94
|
+
if (!folder) throw new Error('Missing folder parameter');
|
|
95
|
+
await fs.rmdir(path.join(appdir(), folder), { recursive: true });
|
|
96
|
+
socket.emitChanged(`app-folders-changed`);
|
|
97
|
+
socket.emitChanged(`app-files-changed-${folder}`);
|
|
98
|
+
socket.emitChanged('used-apps-changed');
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
async getNewAppFolder({ name }) {
|
|
102
|
+
if (!(await fs.exists(path.join(appdir(), name)))) return name;
|
|
103
|
+
let index = 2;
|
|
104
|
+
while (await fs.exists(path.join(appdir(), `${name}${index}`))) {
|
|
105
|
+
index += 1;
|
|
106
|
+
}
|
|
107
|
+
return `${name}${index}`;
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
getUsedAppFolders_meta: true,
|
|
111
|
+
async getUsedAppFolders() {
|
|
112
|
+
const list = await connections.list();
|
|
113
|
+
const apps = [];
|
|
114
|
+
|
|
115
|
+
for (const connection of list) {
|
|
116
|
+
for (const db of connection.databases || []) {
|
|
117
|
+
for (const key of _.keys(db || {})) {
|
|
118
|
+
if (key.startsWith('useApp:') && db[key]) {
|
|
119
|
+
apps.push(key.substring('useApp:'.length));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return _.intersection(_.uniq(apps), await fs.readdir(appdir()));
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
getUsedApps_meta: true,
|
|
129
|
+
async getUsedApps() {
|
|
130
|
+
const apps = await this.getUsedAppFolders();
|
|
131
|
+
const res = [];
|
|
132
|
+
|
|
133
|
+
for (const folder of apps) {
|
|
134
|
+
res.push(await this.loadApp({ folder }));
|
|
135
|
+
}
|
|
136
|
+
return res;
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
// getAppsForDb_meta: true,
|
|
140
|
+
// async getAppsForDb({ conid, database }) {
|
|
141
|
+
// const connection = await connections.get({ conid });
|
|
142
|
+
// if (!connection) return [];
|
|
143
|
+
// const db = (connection.databases || []).find(x => x.name == database);
|
|
144
|
+
// const apps = [];
|
|
145
|
+
// const res = [];
|
|
146
|
+
// if (db) {
|
|
147
|
+
// for (const key of _.keys(db || {})) {
|
|
148
|
+
// if (key.startsWith('useApp:') && db[key]) {
|
|
149
|
+
// apps.push(key.substring('useApp:'.length));
|
|
150
|
+
// }
|
|
151
|
+
// }
|
|
152
|
+
// }
|
|
153
|
+
// for (const folder of apps) {
|
|
154
|
+
// res.push(await this.loadApp({ folder }));
|
|
155
|
+
// }
|
|
156
|
+
// return res;
|
|
157
|
+
// },
|
|
158
|
+
|
|
159
|
+
loadApp_meta: true,
|
|
160
|
+
async loadApp({ folder }) {
|
|
161
|
+
const res = {
|
|
162
|
+
queries: [],
|
|
163
|
+
commands: [],
|
|
164
|
+
name: folder,
|
|
165
|
+
};
|
|
166
|
+
const dir = path.join(appdir(), folder);
|
|
167
|
+
if (await fs.exists(dir)) {
|
|
168
|
+
const files = await fs.readdir(dir);
|
|
169
|
+
|
|
170
|
+
async function processType(ext, field) {
|
|
171
|
+
for (const file of files) {
|
|
172
|
+
if (file.endsWith(ext)) {
|
|
173
|
+
res[field].push({
|
|
174
|
+
name: file.slice(0, -ext.length),
|
|
175
|
+
sql: await fs.readFile(path.join(dir, file), { encoding: 'utf-8' }),
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
await processType('.command.sql', 'commands');
|
|
182
|
+
await processType('.query.sql', 'queries');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
res.virtualReferences = JSON.parse(
|
|
187
|
+
await fs.readFile(path.join(dir, 'virtual-references.config.json'), { encoding: 'utf-8' })
|
|
188
|
+
);
|
|
189
|
+
} catch (err) {
|
|
190
|
+
res.virtualReferences = [];
|
|
191
|
+
}
|
|
192
|
+
try {
|
|
193
|
+
res.dictionaryDescriptions = JSON.parse(
|
|
194
|
+
await fs.readFile(path.join(dir, 'dictionary-descriptions.config.json'), { encoding: 'utf-8' })
|
|
195
|
+
);
|
|
196
|
+
} catch (err) {
|
|
197
|
+
res.dictionaryDescriptions = [];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return res;
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
async saveConfigFile(appFolder, filename, filterFunc, newItem) {
|
|
204
|
+
const file = path.join(appdir(), appFolder, filename);
|
|
205
|
+
|
|
206
|
+
let json;
|
|
207
|
+
try {
|
|
208
|
+
json = JSON.parse(await fs.readFile(file, { encoding: 'utf-8' }));
|
|
209
|
+
} catch (err) {
|
|
210
|
+
json = [];
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (filterFunc) {
|
|
214
|
+
json = json.filter(filterFunc);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
json = [...json, newItem];
|
|
218
|
+
|
|
219
|
+
await fs.writeFile(file, JSON.stringify(json, undefined, 2));
|
|
220
|
+
|
|
221
|
+
socket.emitChanged(`app-files-changed-${appFolder}`);
|
|
222
|
+
socket.emitChanged('used-apps-changed');
|
|
223
|
+
},
|
|
224
|
+
|
|
225
|
+
saveVirtualReference_meta: true,
|
|
226
|
+
async saveVirtualReference({ appFolder, schemaName, pureName, refSchemaName, refTableName, columns }) {
|
|
227
|
+
await this.saveConfigFile(
|
|
228
|
+
appFolder,
|
|
229
|
+
'virtual-references.config.json',
|
|
230
|
+
columns.length == 1
|
|
231
|
+
? x =>
|
|
232
|
+
!(
|
|
233
|
+
x.schemaName == schemaName &&
|
|
234
|
+
x.pureName == pureName &&
|
|
235
|
+
x.columns.length == 1 &&
|
|
236
|
+
x.columns[0].columnName == columns[0].columnName
|
|
237
|
+
)
|
|
238
|
+
: null,
|
|
239
|
+
{
|
|
240
|
+
schemaName,
|
|
241
|
+
pureName,
|
|
242
|
+
refSchemaName,
|
|
243
|
+
refTableName,
|
|
244
|
+
columns,
|
|
245
|
+
}
|
|
246
|
+
);
|
|
247
|
+
return true;
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
saveDictionaryDescription_meta: true,
|
|
251
|
+
async saveDictionaryDescription({ appFolder, pureName, schemaName, expression, columns, delimiter }) {
|
|
252
|
+
await this.saveConfigFile(
|
|
253
|
+
appFolder,
|
|
254
|
+
'dictionary-descriptions.config.json',
|
|
255
|
+
x => !(x.schemaName == schemaName && x.pureName == pureName),
|
|
256
|
+
{
|
|
257
|
+
schemaName,
|
|
258
|
+
pureName,
|
|
259
|
+
expression,
|
|
260
|
+
columns,
|
|
261
|
+
delimiter,
|
|
262
|
+
}
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
return true;
|
|
266
|
+
},
|
|
267
|
+
};
|
|
@@ -141,6 +141,13 @@ module.exports = {
|
|
|
141
141
|
});
|
|
142
142
|
},
|
|
143
143
|
|
|
144
|
+
saveText_meta: true,
|
|
145
|
+
async saveText({ folder, file, text }) {
|
|
146
|
+
await fs.writeFile(path.join(resolveArchiveFolder(folder), `${file}.jsonl`), text);
|
|
147
|
+
socket.emitChanged(`archive-files-changed-${folder}`);
|
|
148
|
+
return true;
|
|
149
|
+
},
|
|
150
|
+
|
|
144
151
|
async getNewArchiveFolder({ database }) {
|
|
145
152
|
const isLink = database.endsWith(database);
|
|
146
153
|
const name = isLink ? database.slice(0, -5) : database;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const { fork } = require('child_process');
|
|
3
3
|
const _ = require('lodash');
|
|
4
|
-
const nedb = require('nedb-promises');
|
|
5
4
|
const fs = require('fs-extra');
|
|
6
5
|
|
|
7
6
|
const { datadir, filesdir } = require('../utility/directories');
|
|
@@ -9,6 +8,7 @@ const socket = require('../utility/socket');
|
|
|
9
8
|
const { encryptConnection } = require('../utility/crypting');
|
|
10
9
|
const { handleProcessCommunication } = require('../utility/processComm');
|
|
11
10
|
const { pickSafeConnectionInfo } = require('../utility/crypting');
|
|
11
|
+
const JsonLinesDatabase = require('../utility/JsonLinesDatabase');
|
|
12
12
|
|
|
13
13
|
const processArgs = require('../utility/processArgs');
|
|
14
14
|
|
|
@@ -50,9 +50,29 @@ function getPortalCollections() {
|
|
|
50
50
|
databaseUrl: process.env[`URL_${id}`],
|
|
51
51
|
useDatabaseUrl: !!process.env[`URL_${id}`],
|
|
52
52
|
databaseFile: process.env[`FILE_${id}`],
|
|
53
|
-
defaultDatabase:
|
|
54
|
-
|
|
53
|
+
defaultDatabase:
|
|
54
|
+
process.env[`DATABASE_${id}`] ||
|
|
55
|
+
(process.env[`FILE_${id}`] ? getDatabaseFileLabel(process.env[`FILE_${id}`]) : null),
|
|
56
|
+
singleDatabase: !!process.env[`DATABASE_${id}`] || !!process.env[`FILE_${id}`],
|
|
55
57
|
displayName: process.env[`LABEL_${id}`],
|
|
58
|
+
|
|
59
|
+
// SSH tunnel
|
|
60
|
+
useSshTunnel: process.env[`USE_SSH_${id}`],
|
|
61
|
+
sshHost: process.env[`SSH_HOST_${id}`],
|
|
62
|
+
sshPort: process.env[`SSH_PORT_${id}`],
|
|
63
|
+
sshMode: process.env[`SSH_MODE_${id}`],
|
|
64
|
+
sshLogin: process.env[`SSH_LOGIN_${id}`],
|
|
65
|
+
sshPassword: process.env[`SSH_PASSWORD_${id}`],
|
|
66
|
+
sshKeyfile: process.env[`SSH_KEY_FILE_${id}`],
|
|
67
|
+
sshKeyfilePassword: process.env[`SSH_KEY_FILE_PASSWORD_${id}`],
|
|
68
|
+
|
|
69
|
+
// SSL
|
|
70
|
+
useSsl: process.env[`USE_SSL_${id}`],
|
|
71
|
+
sslCaFile: process.env[`SSL_CA_FILE_${id}`],
|
|
72
|
+
sslCertFile: process.env[`SSL_CERT_FILE_${id}`],
|
|
73
|
+
sslCertFilePassword: process.env[`SSL_CERT_FILE_PASSWORD_${id}`],
|
|
74
|
+
sslKeyFile: process.env[`SSL_KEY_FILE_${id}`],
|
|
75
|
+
sslRejectUnauthorized: process.env[`SSL_REJECT_UNAUTHORIZED_${id}`],
|
|
56
76
|
}));
|
|
57
77
|
console.log('Using connections from ENV variables:');
|
|
58
78
|
console.log(JSON.stringify(connections.map(pickSafeConnectionInfo), undefined, 2));
|
|
@@ -136,7 +156,7 @@ module.exports = {
|
|
|
136
156
|
const dir = datadir();
|
|
137
157
|
if (!portalConnections) {
|
|
138
158
|
// @ts-ignore
|
|
139
|
-
this.datastore =
|
|
159
|
+
this.datastore = new JsonLinesDatabase(path.join(dir, 'connections.jsonl'));
|
|
140
160
|
}
|
|
141
161
|
},
|
|
142
162
|
|
|
@@ -173,18 +193,22 @@ module.exports = {
|
|
|
173
193
|
let res;
|
|
174
194
|
const encrypted = encryptConnection(connection);
|
|
175
195
|
if (connection._id) {
|
|
176
|
-
res = await this.datastore.update(
|
|
196
|
+
res = await this.datastore.update(encrypted);
|
|
177
197
|
} else {
|
|
178
198
|
res = await this.datastore.insert(encrypted);
|
|
179
199
|
}
|
|
180
200
|
socket.emitChanged('connection-list-changed');
|
|
201
|
+
socket.emitChanged('used-apps-changed');
|
|
202
|
+
// for (const db of connection.databases || []) {
|
|
203
|
+
// socket.emitChanged(`db-apps-changed-${connection._id}-${db.name}`);
|
|
204
|
+
// }
|
|
181
205
|
return res;
|
|
182
206
|
},
|
|
183
207
|
|
|
184
208
|
update_meta: true,
|
|
185
209
|
async update({ _id, values }) {
|
|
186
210
|
if (portalConnections) return;
|
|
187
|
-
const res = await this.datastore.
|
|
211
|
+
const res = await this.datastore.patch(_id, values);
|
|
188
212
|
socket.emitChanged('connection-list-changed');
|
|
189
213
|
return res;
|
|
190
214
|
},
|
|
@@ -192,22 +216,24 @@ module.exports = {
|
|
|
192
216
|
updateDatabase_meta: true,
|
|
193
217
|
async updateDatabase({ conid, database, values }) {
|
|
194
218
|
if (portalConnections) return;
|
|
195
|
-
const conn = await this.datastore.
|
|
196
|
-
let databases = conn
|
|
219
|
+
const conn = await this.datastore.get(conid);
|
|
220
|
+
let databases = (conn && conn.databases) || [];
|
|
197
221
|
if (databases.find(x => x.name == database)) {
|
|
198
222
|
databases = databases.map(x => (x.name == database ? { ...x, ...values } : x));
|
|
199
223
|
} else {
|
|
200
224
|
databases = [...databases, { name: database, ...values }];
|
|
201
225
|
}
|
|
202
|
-
const res = await this.datastore.
|
|
226
|
+
const res = await this.datastore.patch(conid, { databases });
|
|
203
227
|
socket.emitChanged('connection-list-changed');
|
|
228
|
+
socket.emitChanged('used-apps-changed');
|
|
229
|
+
// socket.emitChanged(`db-apps-changed-${conid}-${database}`);
|
|
204
230
|
return res;
|
|
205
231
|
},
|
|
206
232
|
|
|
207
233
|
delete_meta: true,
|
|
208
234
|
async delete(connection) {
|
|
209
235
|
if (portalConnections) return;
|
|
210
|
-
const res = await this.datastore.remove(
|
|
236
|
+
const res = await this.datastore.remove(connection._id);
|
|
211
237
|
socket.emitChanged('connection-list-changed');
|
|
212
238
|
return res;
|
|
213
239
|
},
|
|
@@ -215,8 +241,8 @@ module.exports = {
|
|
|
215
241
|
get_meta: true,
|
|
216
242
|
async get({ conid }) {
|
|
217
243
|
if (portalConnections) return portalConnections.find(x => x._id == conid) || null;
|
|
218
|
-
const res = await this.datastore.
|
|
219
|
-
return res
|
|
244
|
+
const res = await this.datastore.get(conid);
|
|
245
|
+
return res || null;
|
|
220
246
|
},
|
|
221
247
|
|
|
222
248
|
newSqliteDatabase_meta: true,
|
|
@@ -62,9 +62,10 @@ module.exports = {
|
|
|
62
62
|
delete this.requests[msgid];
|
|
63
63
|
},
|
|
64
64
|
handle_status(conid, database, { status }) {
|
|
65
|
+
// console.log('HANDLE SET STATUS', status);
|
|
65
66
|
const existing = this.opened.find(x => x.conid == conid && x.database == database);
|
|
66
67
|
if (!existing) return;
|
|
67
|
-
if (existing.status
|
|
68
|
+
if (existing.status && status && existing.status.counter > status.counter) return;
|
|
68
69
|
existing.status = status;
|
|
69
70
|
socket.emitChanged(`database-status-changed-${conid}-${database}`);
|
|
70
71
|
},
|
package/src/controllers/files.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
const uuidv1 = require('uuid/v1');
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
3
|
const path = require('path');
|
|
4
|
-
const { filesdir, archivedir, resolveArchiveFolder, uploadsdir } = require('../utility/directories');
|
|
4
|
+
const { filesdir, archivedir, resolveArchiveFolder, uploadsdir, appdir } = require('../utility/directories');
|
|
5
5
|
const getChartExport = require('../utility/getChartExport');
|
|
6
6
|
const hasPermission = require('../utility/hasPermission');
|
|
7
7
|
const socket = require('../utility/socket');
|
|
8
8
|
const scheduler = require('./scheduler');
|
|
9
9
|
const getDiagramExport = require('../utility/getDiagramExport');
|
|
10
|
+
const apps = require('./apps');
|
|
10
11
|
|
|
11
12
|
function serialize(format, data) {
|
|
12
13
|
if (format == 'text') return data;
|
|
@@ -74,6 +75,11 @@ module.exports = {
|
|
|
74
75
|
encoding: 'utf-8',
|
|
75
76
|
});
|
|
76
77
|
return deserialize(format, text);
|
|
78
|
+
} else if (folder.startsWith('app:')) {
|
|
79
|
+
const text = await fs.readFile(path.join(appdir(), folder.substring('app:'.length), file), {
|
|
80
|
+
encoding: 'utf-8',
|
|
81
|
+
});
|
|
82
|
+
return deserialize(format, text);
|
|
77
83
|
} else {
|
|
78
84
|
if (!hasPermission(`files/${folder}/read`)) return null;
|
|
79
85
|
const text = await fs.readFile(path.join(filesdir(), folder, file), { encoding: 'utf-8' });
|
|
@@ -88,6 +94,12 @@ module.exports = {
|
|
|
88
94
|
await fs.writeFile(path.join(dir, file), serialize(format, data));
|
|
89
95
|
socket.emitChanged(`archive-files-changed-${folder.substring('archive:'.length)}`);
|
|
90
96
|
return true;
|
|
97
|
+
} else if (folder.startsWith('app:')) {
|
|
98
|
+
const app = folder.substring('app:'.length);
|
|
99
|
+
await fs.writeFile(path.join(appdir(), app, file), serialize(format, data));
|
|
100
|
+
socket.emitChanged(`app-files-changed-${app}`);
|
|
101
|
+
apps.emitChangedDbApp(folder);
|
|
102
|
+
return true;
|
|
91
103
|
} else {
|
|
92
104
|
if (!hasPermission(`files/${folder}/write`)) return false;
|
|
93
105
|
const dir = path.join(filesdir(), folder);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const lineReader = require('line-reader');
|
|
3
3
|
const _ = require('lodash');
|
|
4
|
+
const { __ } = require('lodash/fp');
|
|
4
5
|
const DatastoreProxy = require('../utility/DatastoreProxy');
|
|
5
6
|
const { saveFreeTableData } = require('../utility/freeTableStorage');
|
|
6
7
|
const getJslFileName = require('../utility/getJslFileName');
|
|
@@ -10,7 +11,10 @@ const socket = require('../utility/socket');
|
|
|
10
11
|
function readFirstLine(file) {
|
|
11
12
|
return new Promise((resolve, reject) => {
|
|
12
13
|
lineReader.open(file, (err, reader) => {
|
|
13
|
-
if (err)
|
|
14
|
+
if (err) {
|
|
15
|
+
reject(err);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
14
18
|
if (reader.hasNextLine()) {
|
|
15
19
|
reader.nextLine((err, line) => {
|
|
16
20
|
if (err) reject(err);
|
|
@@ -108,7 +112,16 @@ module.exports = {
|
|
|
108
112
|
async getInfo({ jslid }) {
|
|
109
113
|
const file = getJslFileName(jslid);
|
|
110
114
|
const firstLine = await readFirstLine(file);
|
|
111
|
-
if (firstLine)
|
|
115
|
+
if (firstLine) {
|
|
116
|
+
const parsed = JSON.parse(firstLine);
|
|
117
|
+
if (parsed.__isStreamHeader) {
|
|
118
|
+
return parsed;
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
__isStreamHeader: true,
|
|
122
|
+
__isDynamicStructure: true,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
112
125
|
return null;
|
|
113
126
|
},
|
|
114
127
|
|
|
@@ -132,7 +145,7 @@ module.exports = {
|
|
|
132
145
|
},
|
|
133
146
|
|
|
134
147
|
async notifyChangedStats(stats) {
|
|
135
|
-
console.log('SENDING STATS', JSON.stringify(stats));
|
|
148
|
+
// console.log('SENDING STATS', JSON.stringify(stats));
|
|
136
149
|
const datastore = this.datastores[stats.jslid];
|
|
137
150
|
if (datastore) await datastore.notifyChanged();
|
|
138
151
|
socket.emit(`jsldata-stats-${stats.jslid}`, stats);
|
|
@@ -151,4 +164,10 @@ module.exports = {
|
|
|
151
164
|
saveFreeTableData(getJslFileName(jslid), data);
|
|
152
165
|
return true;
|
|
153
166
|
},
|
|
167
|
+
|
|
168
|
+
saveText_meta: true,
|
|
169
|
+
async saveText({ jslid, text }) {
|
|
170
|
+
await fs.promises.writeFile(getJslFileName(jslid), text);
|
|
171
|
+
return true;
|
|
172
|
+
},
|
|
154
173
|
};
|
|
@@ -72,7 +72,8 @@ module.exports = {
|
|
|
72
72
|
|
|
73
73
|
const res = [];
|
|
74
74
|
for (const packageName of _.union(files1, files2)) {
|
|
75
|
-
if (
|
|
75
|
+
if (packageName == 'dist') continue;
|
|
76
|
+
// if (!/^dbgate-plugin-.*$/.test(packageName)) continue;
|
|
76
77
|
try {
|
|
77
78
|
if (packagedContent && packagedContent[packageName]) {
|
|
78
79
|
const manifest = {
|
|
@@ -48,7 +48,7 @@ module.exports = {
|
|
|
48
48
|
async write({ data }) {
|
|
49
49
|
const fileName = path.join(datadir(), 'query-history.jsonl');
|
|
50
50
|
await fs.appendFile(fileName, JSON.stringify(data) + '\n');
|
|
51
|
-
socket.
|
|
51
|
+
socket.emit('query-history-changed');
|
|
52
52
|
return 'OK';
|
|
53
53
|
},
|
|
54
54
|
};
|
package/src/currentVersion.js
CHANGED
package/src/main.js
CHANGED
|
@@ -19,6 +19,7 @@ const runners = require('./controllers/runners');
|
|
|
19
19
|
const jsldata = require('./controllers/jsldata');
|
|
20
20
|
const config = require('./controllers/config');
|
|
21
21
|
const archive = require('./controllers/archive');
|
|
22
|
+
const apps = require('./controllers/apps');
|
|
22
23
|
const uploads = require('./controllers/uploads');
|
|
23
24
|
const plugins = require('./controllers/plugins');
|
|
24
25
|
const files = require('./controllers/files');
|
|
@@ -157,6 +158,7 @@ function useAllControllers(app, electron) {
|
|
|
157
158
|
useController(app, electron, '/files', files);
|
|
158
159
|
useController(app, electron, '/scheduler', scheduler);
|
|
159
160
|
useController(app, electron, '/query-history', queryHistory);
|
|
161
|
+
useController(app, electron, '/apps', apps);
|
|
160
162
|
}
|
|
161
163
|
|
|
162
164
|
function initializeElectronSender(electronSender) {
|
|
@@ -18,6 +18,12 @@ let lastStatus = null;
|
|
|
18
18
|
let analysedTime = 0;
|
|
19
19
|
let serverVersion;
|
|
20
20
|
|
|
21
|
+
let statusCounter = 0;
|
|
22
|
+
function getStatusCounter() {
|
|
23
|
+
statusCounter += 1;
|
|
24
|
+
return statusCounter;
|
|
25
|
+
}
|
|
26
|
+
|
|
21
27
|
async function checkedAsyncCall(promise) {
|
|
22
28
|
try {
|
|
23
29
|
const res = await promise;
|
|
@@ -79,7 +85,7 @@ function handleSyncModel() {
|
|
|
79
85
|
function setStatus(status) {
|
|
80
86
|
const statusString = stableStringify(status);
|
|
81
87
|
if (lastStatus != statusString) {
|
|
82
|
-
process.send({ msgtype: 'status', status });
|
|
88
|
+
process.send({ msgtype: 'status', status: { ...status, counter: getStatusCounter() } });
|
|
83
89
|
lastStatus = statusString;
|
|
84
90
|
}
|
|
85
91
|
}
|
package/src/shell/copyStream.js
CHANGED
|
@@ -2,12 +2,17 @@ const EnsureStreamHeaderStream = require('../utility/EnsureStreamHeaderStream');
|
|
|
2
2
|
|
|
3
3
|
function copyStream(input, output) {
|
|
4
4
|
return new Promise((resolve, reject) => {
|
|
5
|
-
const ensureHeader = new EnsureStreamHeaderStream();
|
|
6
5
|
const finisher = output['finisher'] || output;
|
|
7
6
|
finisher.on('finish', resolve);
|
|
8
7
|
finisher.on('error', reject);
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
|
|
9
|
+
if (output.requireFixedStructure) {
|
|
10
|
+
const ensureHeader = new EnsureStreamHeaderStream();
|
|
11
|
+
input.pipe(ensureHeader);
|
|
12
|
+
ensureHeader.pipe(output);
|
|
13
|
+
} else {
|
|
14
|
+
input.pipe(output);
|
|
15
|
+
}
|
|
11
16
|
});
|
|
12
17
|
}
|
|
13
18
|
|
|
@@ -11,10 +11,7 @@ class StringifyStream extends stream.Transform {
|
|
|
11
11
|
let skip = false;
|
|
12
12
|
|
|
13
13
|
if (!this.wasHeader) {
|
|
14
|
-
skip =
|
|
15
|
-
chunk.__isStreamHeader ||
|
|
16
|
-
// TODO remove isArray test
|
|
17
|
-
Array.isArray(chunk.columns);
|
|
14
|
+
skip = chunk.__isStreamHeader;
|
|
18
15
|
this.wasHeader = true;
|
|
19
16
|
}
|
|
20
17
|
if (!skip) {
|
|
@@ -12,14 +12,14 @@ class ParseStream extends stream.Transform {
|
|
|
12
12
|
_transform(chunk, encoding, done) {
|
|
13
13
|
const obj = JSON.parse(chunk);
|
|
14
14
|
if (!this.wasHeader) {
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
15
|
+
if (!obj.__isStreamHeader) {
|
|
16
|
+
this.push({
|
|
17
|
+
__isStreamHeader: true,
|
|
18
|
+
__isDynamicStructure: true,
|
|
19
|
+
// columns: Object.keys(obj).map(columnName => ({ columnName })),
|
|
20
|
+
});
|
|
21
21
|
}
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
this.wasHeader = true;
|
|
24
24
|
}
|
|
25
25
|
if (!this.limitRows || this.rowsWritten < this.limitRows) {
|
|
@@ -10,11 +10,7 @@ class StringifyStream extends stream.Transform {
|
|
|
10
10
|
_transform(chunk, encoding, done) {
|
|
11
11
|
let skip = false;
|
|
12
12
|
if (!this.wasHeader) {
|
|
13
|
-
skip =
|
|
14
|
-
(chunk.__isStreamHeader ||
|
|
15
|
-
// TODO remove isArray test
|
|
16
|
-
Array.isArray(chunk.columns)) &&
|
|
17
|
-
!this.header;
|
|
13
|
+
skip = (chunk.__isStreamHeader && !this.header) || (chunk.__isStreamHeader && chunk.__isDynamicStructure);
|
|
18
14
|
this.wasHeader = true;
|
|
19
15
|
}
|
|
20
16
|
if (!skip) {
|
|
@@ -14,11 +14,7 @@ class SqlizeStream extends stream.Transform {
|
|
|
14
14
|
_transform(chunk, encoding, done) {
|
|
15
15
|
let skip = false;
|
|
16
16
|
if (!this.wasHeader) {
|
|
17
|
-
if (
|
|
18
|
-
chunk.__isStreamHeader ||
|
|
19
|
-
// TODO remove isArray test
|
|
20
|
-
Array.isArray(chunk.columns)
|
|
21
|
-
) {
|
|
17
|
+
if (chunk.__isStreamHeader) {
|
|
22
18
|
skip = true;
|
|
23
19
|
this.tableName = chunk.pureName;
|
|
24
20
|
if (chunk.engine) {
|
|
@@ -13,11 +13,7 @@ class EnsureStreamHeaderStream extends stream.Transform {
|
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
if (
|
|
17
|
-
!chunk.__isStreamHeader &&
|
|
18
|
-
// TODO remove isArray test
|
|
19
|
-
!Array.isArray(chunk.columns)
|
|
20
|
-
) {
|
|
16
|
+
if (!chunk.__isStreamHeader) {
|
|
21
17
|
this.push({
|
|
22
18
|
__isStreamHeader: true,
|
|
23
19
|
__isComputedStructure: true,
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
const AsyncLock = require('async-lock');
|
|
2
|
+
const fs = require('fs-extra');
|
|
3
|
+
const uuidv1 = require('uuid/v1');
|
|
4
|
+
|
|
5
|
+
const lock = new AsyncLock();
|
|
6
|
+
|
|
7
|
+
// const lineReader = require('line-reader');
|
|
8
|
+
// const { fetchNextLineFromReader } = require('./JsonLinesDatastore');
|
|
9
|
+
|
|
10
|
+
class JsonLinesDatabase {
|
|
11
|
+
constructor(filename) {
|
|
12
|
+
this.filename = filename;
|
|
13
|
+
this.data = [];
|
|
14
|
+
this.loadedOk = false;
|
|
15
|
+
this.loadPerformed = false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async _save() {
|
|
19
|
+
if (!this.loadedOk) {
|
|
20
|
+
// don't override data
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
await fs.writeFile(this.filename, this.data.map(x => JSON.stringify(x)).join('\n'));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async _ensureLoaded() {
|
|
27
|
+
if (!this.loadPerformed) {
|
|
28
|
+
await lock.acquire('reader', async () => {
|
|
29
|
+
if (!this.loadPerformed) {
|
|
30
|
+
if (!(await fs.exists(this.filename))) {
|
|
31
|
+
this.loadedOk = true;
|
|
32
|
+
this.loadPerformed = true;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const text = await fs.readFile(this.filename, { encoding: 'utf-8' });
|
|
37
|
+
this.data = text
|
|
38
|
+
.split('\n')
|
|
39
|
+
.filter(x => x.trim())
|
|
40
|
+
.map(x => JSON.parse(x));
|
|
41
|
+
this.loadedOk = true;
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.error(`Error loading file ${this.filename}`, err);
|
|
44
|
+
}
|
|
45
|
+
this.loadPerformed = true;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async insert(obj) {
|
|
52
|
+
await this._ensureLoaded();
|
|
53
|
+
if (obj._id && (await this.get(obj._id))) {
|
|
54
|
+
throw new Error(`Cannot insert duplicate ID ${obj._id} into ${this.filename}`);
|
|
55
|
+
}
|
|
56
|
+
const elem = obj._id
|
|
57
|
+
? obj
|
|
58
|
+
: {
|
|
59
|
+
...obj,
|
|
60
|
+
_id: uuidv1(),
|
|
61
|
+
};
|
|
62
|
+
this.data.push(elem);
|
|
63
|
+
await this._save();
|
|
64
|
+
return elem;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async get(id) {
|
|
68
|
+
await this._ensureLoaded();
|
|
69
|
+
return this.data.find(x => x._id == id);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async find(cond) {
|
|
73
|
+
await this._ensureLoaded();
|
|
74
|
+
if (cond) {
|
|
75
|
+
return this.data.filter(x => {
|
|
76
|
+
for (const key of Object.keys(cond)) {
|
|
77
|
+
if (x[key] != cond[key]) return false;
|
|
78
|
+
}
|
|
79
|
+
return true;
|
|
80
|
+
});
|
|
81
|
+
} else {
|
|
82
|
+
return this.data;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async update(obj) {
|
|
87
|
+
await this._ensureLoaded();
|
|
88
|
+
this.data = this.data.map(x => (x._id == obj._id ? obj : x));
|
|
89
|
+
await this._save();
|
|
90
|
+
return obj;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async patch(id, values) {
|
|
94
|
+
await this._ensureLoaded();
|
|
95
|
+
this.data = this.data.map(x => (x._id == id ? { ...x, ...values } : x));
|
|
96
|
+
await this._save();
|
|
97
|
+
return this.data.find(x => x._id == id);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async remove(id) {
|
|
101
|
+
await this._ensureLoaded();
|
|
102
|
+
const removed = this.data.find(x => x._id == id);
|
|
103
|
+
this.data = this.data.filter(x => x._id != id);
|
|
104
|
+
await this._save();
|
|
105
|
+
return removed;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// async _openReader() {
|
|
109
|
+
// return new Promise((resolve, reject) =>
|
|
110
|
+
// lineReader.open(this.filename, (err, reader) => {
|
|
111
|
+
// if (err) reject(err);
|
|
112
|
+
// resolve(reader);
|
|
113
|
+
// })
|
|
114
|
+
// );
|
|
115
|
+
// }
|
|
116
|
+
|
|
117
|
+
// async _read() {
|
|
118
|
+
// this.data = [];
|
|
119
|
+
// if (!(await fs.exists(this.filename))) return;
|
|
120
|
+
// try {
|
|
121
|
+
// const reader = await this._openReader();
|
|
122
|
+
// for (;;) {
|
|
123
|
+
// const line = await fetchNextLineFromReader(reader);
|
|
124
|
+
// if (!line) break;
|
|
125
|
+
// this.data.push(JSON.parse(line));
|
|
126
|
+
// }
|
|
127
|
+
// } catch (err) {
|
|
128
|
+
// console.error(`Error loading file ${this.filename}`, err);
|
|
129
|
+
// }
|
|
130
|
+
// }
|
|
131
|
+
|
|
132
|
+
// async _write() {
|
|
133
|
+
// const fw = fs.createWriteStream(this.filename);
|
|
134
|
+
// for (const obj of this.data) {
|
|
135
|
+
// await fw.write(JSON.stringify(obj));
|
|
136
|
+
// await fw.write('\n');
|
|
137
|
+
// }
|
|
138
|
+
// await fw.end();
|
|
139
|
+
// }
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = JsonLinesDatabase;
|
|
@@ -4,7 +4,7 @@ const lock = new AsyncLock();
|
|
|
4
4
|
const stableStringify = require('json-stable-stringify');
|
|
5
5
|
const { evaluateCondition } = require('dbgate-sqltree');
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
function fetchNextLineFromReader(reader) {
|
|
8
8
|
return new Promise((resolve, reject) => {
|
|
9
9
|
if (!reader.hasNextLine()) {
|
|
10
10
|
resolve(null);
|
|
@@ -27,6 +27,7 @@ class JsonLinesDatastore {
|
|
|
27
27
|
this.reader = null;
|
|
28
28
|
this.readedDataRowCount = 0;
|
|
29
29
|
this.readedSchemaRow = false;
|
|
30
|
+
// this.firstRowToBeReturned = null;
|
|
30
31
|
this.notifyChangedCallback = null;
|
|
31
32
|
this.currentFilter = null;
|
|
32
33
|
}
|
|
@@ -37,6 +38,7 @@ class JsonLinesDatastore {
|
|
|
37
38
|
this.reader = null;
|
|
38
39
|
this.readedDataRowCount = 0;
|
|
39
40
|
this.readedSchemaRow = false;
|
|
41
|
+
// this.firstRowToBeReturned = null;
|
|
40
42
|
this.currentFilter = null;
|
|
41
43
|
reader.close(() => {});
|
|
42
44
|
}
|
|
@@ -61,8 +63,13 @@ class JsonLinesDatastore {
|
|
|
61
63
|
}
|
|
62
64
|
|
|
63
65
|
async _readLine(parse) {
|
|
66
|
+
// if (this.firstRowToBeReturned) {
|
|
67
|
+
// const res = this.firstRowToBeReturned;
|
|
68
|
+
// this.firstRowToBeReturned = null;
|
|
69
|
+
// return res;
|
|
70
|
+
// }
|
|
64
71
|
for (;;) {
|
|
65
|
-
const line = await
|
|
72
|
+
const line = await fetchNextLineFromReader(this.reader);
|
|
66
73
|
if (!line) {
|
|
67
74
|
// EOF
|
|
68
75
|
return null;
|
|
@@ -70,7 +77,11 @@ class JsonLinesDatastore {
|
|
|
70
77
|
|
|
71
78
|
if (!this.readedSchemaRow) {
|
|
72
79
|
this.readedSchemaRow = true;
|
|
73
|
-
|
|
80
|
+
const parsedLine = JSON.parse(line);
|
|
81
|
+
if (parsedLine.__isStreamHeader) {
|
|
82
|
+
// skip to next line
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
74
85
|
}
|
|
75
86
|
if (this.currentFilter) {
|
|
76
87
|
const parsedLine = JSON.parse(line);
|
|
@@ -130,11 +141,21 @@ class JsonLinesDatastore {
|
|
|
130
141
|
this.reader = reader;
|
|
131
142
|
this.currentFilter = filter;
|
|
132
143
|
}
|
|
133
|
-
if (!this.readedSchemaRow) {
|
|
134
|
-
|
|
135
|
-
|
|
144
|
+
// if (!this.readedSchemaRow) {
|
|
145
|
+
// const line = await this._readLine(true); // skip structure
|
|
146
|
+
// if (!line.__isStreamHeader) {
|
|
147
|
+
// // line contains data
|
|
148
|
+
// this.firstRowToBeReturned = line;
|
|
149
|
+
// }
|
|
150
|
+
// }
|
|
136
151
|
while (this.readedDataRowCount < offset) {
|
|
137
|
-
await this._readLine(false);
|
|
152
|
+
const line = await this._readLine(false);
|
|
153
|
+
if (line == null) break;
|
|
154
|
+
// if (this.firstRowToBeReturned) {
|
|
155
|
+
// this.firstRowToBeReturned = null;
|
|
156
|
+
// } else {
|
|
157
|
+
// await this._readLine(false);
|
|
158
|
+
// }
|
|
138
159
|
}
|
|
139
160
|
}
|
|
140
161
|
|
|
@@ -148,7 +169,6 @@ class JsonLinesDatastore {
|
|
|
148
169
|
res.push(line);
|
|
149
170
|
}
|
|
150
171
|
});
|
|
151
|
-
// console.log('RETURN', res.length);
|
|
152
172
|
return res;
|
|
153
173
|
}
|
|
154
174
|
}
|
|
@@ -38,6 +38,7 @@ const rundir = dirFunc('run', true);
|
|
|
38
38
|
const uploadsdir = dirFunc('uploads', true);
|
|
39
39
|
const pluginsdir = dirFunc('plugins');
|
|
40
40
|
const archivedir = dirFunc('archive');
|
|
41
|
+
const appdir = dirFunc('apps');
|
|
41
42
|
const filesdir = dirFunc('files');
|
|
42
43
|
|
|
43
44
|
function packagedPluginsDir() {
|
|
@@ -103,6 +104,7 @@ module.exports = {
|
|
|
103
104
|
rundir,
|
|
104
105
|
uploadsdir,
|
|
105
106
|
archivedir,
|
|
107
|
+
appdir,
|
|
106
108
|
ensureDirectory,
|
|
107
109
|
pluginsdir,
|
|
108
110
|
filesdir,
|
package/src/utility/socket.js
CHANGED
package/webpack.config.js
CHANGED
|
@@ -10,16 +10,16 @@ var config = {
|
|
|
10
10
|
target: 'node',
|
|
11
11
|
node: {
|
|
12
12
|
__dirname: false,
|
|
13
|
-
},
|
|
13
|
+
},
|
|
14
14
|
output: {
|
|
15
15
|
path: path.resolve(__dirname, 'dist'),
|
|
16
16
|
filename: 'bundle.js',
|
|
17
17
|
libraryTarget: 'commonjs2',
|
|
18
18
|
},
|
|
19
19
|
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
20
|
+
// optimization: {
|
|
21
|
+
// minimize: false,
|
|
22
|
+
// },
|
|
23
23
|
|
|
24
24
|
module: {
|
|
25
25
|
rules: [
|
|
@@ -45,6 +45,9 @@ var config = {
|
|
|
45
45
|
},
|
|
46
46
|
}),
|
|
47
47
|
],
|
|
48
|
+
externals: {
|
|
49
|
+
'better-sqlite3': 'commonjs better-sqlite3',
|
|
50
|
+
},
|
|
48
51
|
};
|
|
49
52
|
|
|
50
53
|
module.exports = config;
|