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,393 @@
|
|
|
1
|
+
const {
|
|
2
|
+
sortPermissionsFromTheSameLevel,
|
|
3
|
+
getPredefinedPermissions,
|
|
4
|
+
getLogger,
|
|
5
|
+
getConnectionLabel,
|
|
6
|
+
} = require('dbgate-tools');
|
|
7
|
+
const {
|
|
8
|
+
storageSelectFmt,
|
|
9
|
+
storageReadUserRolePermissions,
|
|
10
|
+
storageReadUserPermissions,
|
|
11
|
+
storageReadRolePermissions,
|
|
12
|
+
} = require('../controllers/storageDb');
|
|
13
|
+
const { getTokenSecret, getTokenLifetime } = require('./authCommon');
|
|
14
|
+
const { AuthProviderBase } = require('./authProvider');
|
|
15
|
+
const AD = require('activedirectory2').promiseWrapper;
|
|
16
|
+
const jwt = require('jsonwebtoken');
|
|
17
|
+
const logger = getLogger('storageAuthProvider');
|
|
18
|
+
const axios = require('axios');
|
|
19
|
+
const _ = require('lodash');
|
|
20
|
+
const { authProxyGetTokenFromCode, authProxyGetRedirectUrl } = require('../utility/authProxy');
|
|
21
|
+
|
|
22
|
+
async function loadPermissionsForUserId(userId) {
|
|
23
|
+
const rolePermissions = sortPermissionsFromTheSameLevel(await storageReadUserRolePermissions(userId));
|
|
24
|
+
const userPermissions = await storageReadUserPermissions(userId);
|
|
25
|
+
const loggedUserPermissions = await storageReadRolePermissions(-2);
|
|
26
|
+
return [...getPredefinedPermissions('logged-user'), ...loggedUserPermissions, ...rolePermissions, ...userPermissions];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
class StorageProviderBase extends AuthProviderBase {
|
|
30
|
+
constructor(config) {
|
|
31
|
+
super();
|
|
32
|
+
this.config = config;
|
|
33
|
+
this.amoid = config.amoid;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getCurrentPermissions(req) {
|
|
37
|
+
return req?.user?.permissions || process.env.PERMISSIONS;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
toJson() {
|
|
41
|
+
return {
|
|
42
|
+
...super.toJson(),
|
|
43
|
+
name: this.config.name,
|
|
44
|
+
type: this.config.type,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class AnonymousProvider extends StorageProviderBase {
|
|
50
|
+
async login(login, password, options = undefined) {
|
|
51
|
+
const permissions = await [
|
|
52
|
+
...getPredefinedPermissions('anonymous-user'),
|
|
53
|
+
...(await storageReadRolePermissions(-1)),
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
accessToken: jwt.sign(
|
|
58
|
+
{
|
|
59
|
+
amoid: this.amoid,
|
|
60
|
+
permissions,
|
|
61
|
+
},
|
|
62
|
+
getTokenSecret(),
|
|
63
|
+
{ expiresIn: getTokenLifetime() }
|
|
64
|
+
),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
class LocalAuthProvider extends StorageProviderBase {
|
|
70
|
+
constructor(config) {
|
|
71
|
+
super(config);
|
|
72
|
+
}
|
|
73
|
+
async login(login, password) {
|
|
74
|
+
const rows = await storageSelectFmt('select * from ~users where login = %v', login);
|
|
75
|
+
if (rows.length == 0) {
|
|
76
|
+
return { error: 'Login not allowed' };
|
|
77
|
+
}
|
|
78
|
+
const row = rows[0];
|
|
79
|
+
if (row.password == password) {
|
|
80
|
+
const userId = row.id;
|
|
81
|
+
const permissions = await loadPermissionsForUserId(userId);
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
accessToken: jwt.sign(
|
|
85
|
+
{
|
|
86
|
+
amoid: this.amoid,
|
|
87
|
+
login,
|
|
88
|
+
permissions,
|
|
89
|
+
userId,
|
|
90
|
+
},
|
|
91
|
+
getTokenSecret(),
|
|
92
|
+
{ expiresIn: getTokenLifetime() }
|
|
93
|
+
),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return { error: 'Invalid credentials' };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
getCurrentLogin(req) {
|
|
100
|
+
const login = req?.user?.login ?? req?.auth?.user ?? null;
|
|
101
|
+
return login;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
toJson() {
|
|
105
|
+
return {
|
|
106
|
+
...super.toJson(),
|
|
107
|
+
workflowType: 'credentials',
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
class OauthProvider extends StorageProviderBase {
|
|
113
|
+
constructor(config) {
|
|
114
|
+
super(config);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async getLogoutUrl() {
|
|
118
|
+
return this.config.oauthLogout;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async oauthToken(params) {
|
|
122
|
+
const { redirectUri, code } = params;
|
|
123
|
+
|
|
124
|
+
const scopeParam = this.config.oauthScope ? `&scope=${this.config.oauthScope}` : '';
|
|
125
|
+
const resp = await axios.default.post(
|
|
126
|
+
`${this.config.oauthToken}`,
|
|
127
|
+
`grant_type=authorization_code&code=${encodeURIComponent(code)}&redirect_uri=${encodeURIComponent(
|
|
128
|
+
redirectUri
|
|
129
|
+
)}&client_id=${this.config.oauthClient}&client_secret=${this.config.oauthClientSecret}${scopeParam}`
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const { access_token, refresh_token } = resp.data;
|
|
133
|
+
|
|
134
|
+
const payload = jwt.decode(access_token);
|
|
135
|
+
|
|
136
|
+
logger.info({ payload }, 'User payload returned from OAUTH');
|
|
137
|
+
|
|
138
|
+
const login =
|
|
139
|
+
this.config.oauthLoginField && payload && payload[this.config.oauthLoginField]
|
|
140
|
+
? payload[this.config.oauthLoginField]
|
|
141
|
+
: 'oauth';
|
|
142
|
+
|
|
143
|
+
const loginRows = await storageSelectFmt('select * from ~users where ~login = %v', login);
|
|
144
|
+
const permissions = await loadPermissionsForUserId(loginRows[0]?.id ?? -1);
|
|
145
|
+
|
|
146
|
+
if (this.config.oauthOnlyDefinedLogins == 1) {
|
|
147
|
+
if (loginRows.length == 0) {
|
|
148
|
+
return { error: `Username ${login} not allowed to log in` };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
let groups =
|
|
153
|
+
this.config.oauthGroupField && payload && _.get(payload, this.config.oauthGroupField)
|
|
154
|
+
? _.get(payload, this.config.oauthGroupField)
|
|
155
|
+
: [];
|
|
156
|
+
|
|
157
|
+
if (_.isString(groups)) {
|
|
158
|
+
groups = groups.split(',');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (this.config.oauthOnlyDefinedGroups == 1) {
|
|
162
|
+
const roleRows = await storageSelectFmt('select * from ~roles where ~name in (%,v)', groups);
|
|
163
|
+
if (roleRows.length == 0) {
|
|
164
|
+
return { error: `Groups ${groups.join(', ')} not allowed to log in` };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (access_token) {
|
|
169
|
+
return {
|
|
170
|
+
accessToken: jwt.sign(
|
|
171
|
+
{
|
|
172
|
+
amoid: this.amoid,
|
|
173
|
+
login,
|
|
174
|
+
permissions,
|
|
175
|
+
userId: loginRows[0]?.id,
|
|
176
|
+
},
|
|
177
|
+
getTokenSecret(),
|
|
178
|
+
{ expiresIn: getTokenLifetime() }
|
|
179
|
+
),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return { error: 'Token not found' };
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
redirect({ state, redirectUri }) {
|
|
187
|
+
const scopeParam = this.config.oauthScope ? `&scope=${this.config.oauthScope}` : '';
|
|
188
|
+
return {
|
|
189
|
+
status: 'ok',
|
|
190
|
+
uri: `${this.config.oauthAuth}?client_id=${
|
|
191
|
+
this.config.oauthClient
|
|
192
|
+
}&response_type=code&redirect_uri=${encodeURIComponent(redirectUri)}&state=${encodeURIComponent(
|
|
193
|
+
state
|
|
194
|
+
)}${scopeParam}`,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
toJson() {
|
|
199
|
+
return {
|
|
200
|
+
...super.toJson(),
|
|
201
|
+
workflowType: 'redirect',
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
class ADProvider extends StorageProviderBase {
|
|
207
|
+
constructor(config) {
|
|
208
|
+
super(config);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async login(login, password) {
|
|
212
|
+
const adConfig = {
|
|
213
|
+
url: this.config.adUrl,
|
|
214
|
+
baseDN: this.config.adBaseDN,
|
|
215
|
+
username: this.config.adLogin,
|
|
216
|
+
password: this.config.adPassword,
|
|
217
|
+
};
|
|
218
|
+
const ad = new AD(adConfig);
|
|
219
|
+
try {
|
|
220
|
+
const res = await ad.authenticate(login, password);
|
|
221
|
+
if (!res) {
|
|
222
|
+
return { error: `Login failed, AD rejected login ${login}` };
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const loginRows = await storageSelectFmt('select * from ~users where ~login = %v', login);
|
|
226
|
+
const permissions = await loadPermissionsForUserId(loginRows[0]?.id ?? -1);
|
|
227
|
+
|
|
228
|
+
if (this.config.adOnlyDefinedLogins == 1) {
|
|
229
|
+
if (loginRows.length == 0) {
|
|
230
|
+
return { error: `Username ${login} not allowed to log in` };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return {
|
|
235
|
+
accessToken: jwt.sign(
|
|
236
|
+
{
|
|
237
|
+
amoid: this.amoid,
|
|
238
|
+
login,
|
|
239
|
+
permissions,
|
|
240
|
+
},
|
|
241
|
+
getTokenSecret(),
|
|
242
|
+
{ expiresIn: getTokenLifetime() }
|
|
243
|
+
),
|
|
244
|
+
};
|
|
245
|
+
} catch (e) {
|
|
246
|
+
return { error: `Login failed: ${e.message}` };
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
toJson() {
|
|
251
|
+
return {
|
|
252
|
+
...super.toJson(),
|
|
253
|
+
workflowType: 'credentials',
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
class DatabaseProvider extends StorageProviderBase {
|
|
259
|
+
constructor(config) {
|
|
260
|
+
super(config);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async getLoginPageConnections() {
|
|
264
|
+
const resp = await storageSelectFmt('select * from ~connections');
|
|
265
|
+
return resp.map(x => ({
|
|
266
|
+
conid: x.conid,
|
|
267
|
+
label: getConnectionLabel(x),
|
|
268
|
+
passwordMode: x.passwordMode,
|
|
269
|
+
useRedirectDbLogin: x.useRedirectDbLogin,
|
|
270
|
+
}));
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async login(login, password, options) {
|
|
274
|
+
const { conid } = options;
|
|
275
|
+
const rows = login ? await storageSelectFmt('select * from ~users where ~login = %v', login) : [];
|
|
276
|
+
if (this.config.dbloginOnlyDefinedLogins == 1 && rows.length == 0) {
|
|
277
|
+
return { error: 'Login not allowed' };
|
|
278
|
+
}
|
|
279
|
+
const userId = rows[0]?.id;
|
|
280
|
+
const permissions = await loadPermissionsForUserId(userId ?? -1);
|
|
281
|
+
return {
|
|
282
|
+
accessToken: jwt.sign(
|
|
283
|
+
{
|
|
284
|
+
amoid: this.amoid,
|
|
285
|
+
login,
|
|
286
|
+
permissions,
|
|
287
|
+
userId,
|
|
288
|
+
conid,
|
|
289
|
+
},
|
|
290
|
+
getTokenSecret(),
|
|
291
|
+
{ expiresIn: getTokenLifetime() }
|
|
292
|
+
),
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
getSingleConnectionId(req) {
|
|
297
|
+
return req?.user?.conid;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
toJson() {
|
|
301
|
+
return {
|
|
302
|
+
...super.toJson(),
|
|
303
|
+
workflowType: 'database',
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
class MsEntraProvider extends StorageProviderBase {
|
|
309
|
+
constructor(config) {
|
|
310
|
+
super(config);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
async getLogoutUrl() {
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async oauthToken(params) {
|
|
318
|
+
const { sid, code } = params;
|
|
319
|
+
|
|
320
|
+
const token = await authProxyGetTokenFromCode({ sid, code });
|
|
321
|
+
|
|
322
|
+
const payload = jwt.decode(token);
|
|
323
|
+
|
|
324
|
+
logger.info({ payload }, 'User payload returned from MS Entra');
|
|
325
|
+
|
|
326
|
+
const { email } = payload;
|
|
327
|
+
|
|
328
|
+
const loginRows = await storageSelectFmt('select * from ~users where ~email = %v', email);
|
|
329
|
+
const permissions = await loadPermissionsForUserId(loginRows[0]?.id ?? -1);
|
|
330
|
+
|
|
331
|
+
if (this.config.msentraOnlyDefinedLogins == 1) {
|
|
332
|
+
if (loginRows.length == 0) {
|
|
333
|
+
return { error: `Email ${email} not allowed to log in` };
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (token) {
|
|
338
|
+
return {
|
|
339
|
+
accessToken: jwt.sign(
|
|
340
|
+
{
|
|
341
|
+
amoid: this.amoid,
|
|
342
|
+
permissions,
|
|
343
|
+
msentraToken: token,
|
|
344
|
+
userId: loginRows[0]?.id,
|
|
345
|
+
login: payload.unique_name,
|
|
346
|
+
},
|
|
347
|
+
getTokenSecret(),
|
|
348
|
+
{ expiresIn: getTokenLifetime() }
|
|
349
|
+
),
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return { error: 'Token not found' };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async redirect({ state, redirectUri }) {
|
|
357
|
+
const authUrl = await authProxyGetRedirectUrl({ type: 'msentra', client: 'web', redirectUri, state });
|
|
358
|
+
return {
|
|
359
|
+
status: 'ok',
|
|
360
|
+
uri: authUrl.url,
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
toJson() {
|
|
365
|
+
return {
|
|
366
|
+
...super.toJson(),
|
|
367
|
+
workflowType: 'redirect',
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function createStorageAuthProvider(config) {
|
|
373
|
+
switch (config.type) {
|
|
374
|
+
case 'local':
|
|
375
|
+
return new LocalAuthProvider(config);
|
|
376
|
+
case 'oauth':
|
|
377
|
+
return new OauthProvider(config);
|
|
378
|
+
case 'ad':
|
|
379
|
+
return new ADProvider(config);
|
|
380
|
+
case 'database':
|
|
381
|
+
return new DatabaseProvider(config);
|
|
382
|
+
case 'msentra':
|
|
383
|
+
return new MsEntraProvider(config);
|
|
384
|
+
case 'none':
|
|
385
|
+
return new AnonymousProvider(config);
|
|
386
|
+
default:
|
|
387
|
+
return new StorageProviderBase(config);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
module.exports = {
|
|
392
|
+
createStorageAuthProvider,
|
|
393
|
+
};
|
|
@@ -0,0 +1,280 @@
|
|
|
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
|
+
if (!folder) return [];
|
|
31
|
+
const dir = path.join(appdir(), folder);
|
|
32
|
+
if (!(await fs.exists(dir))) return [];
|
|
33
|
+
const files = await fs.readdir(dir);
|
|
34
|
+
|
|
35
|
+
function fileType(ext, type) {
|
|
36
|
+
return files
|
|
37
|
+
.filter(name => name.endsWith(ext))
|
|
38
|
+
.map(name => ({
|
|
39
|
+
name: name.slice(0, -ext.length),
|
|
40
|
+
label: path.parse(name.slice(0, -ext.length)).base,
|
|
41
|
+
type,
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return [
|
|
46
|
+
...fileType('.command.sql', 'command.sql'),
|
|
47
|
+
...fileType('.query.sql', 'query.sql'),
|
|
48
|
+
...fileType('.config.json', 'config.json'),
|
|
49
|
+
];
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
async emitChangedDbApp(folder) {
|
|
53
|
+
const used = await this.getUsedAppFolders();
|
|
54
|
+
if (used.includes(folder)) {
|
|
55
|
+
socket.emitChanged('used-apps-changed');
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
refreshFiles_meta: true,
|
|
60
|
+
async refreshFiles({ folder }) {
|
|
61
|
+
socket.emitChanged('app-files-changed', { app: folder });
|
|
62
|
+
},
|
|
63
|
+
|
|
64
|
+
refreshFolders_meta: true,
|
|
65
|
+
async refreshFolders() {
|
|
66
|
+
socket.emitChanged(`app-folders-changed`);
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
deleteFile_meta: true,
|
|
70
|
+
async deleteFile({ folder, file, fileType }) {
|
|
71
|
+
await fs.unlink(path.join(appdir(), folder, `${file}.${fileType}`));
|
|
72
|
+
socket.emitChanged('app-files-changed', { app: folder });
|
|
73
|
+
this.emitChangedDbApp(folder);
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
renameFile_meta: true,
|
|
77
|
+
async renameFile({ folder, file, newFile, fileType }) {
|
|
78
|
+
await fs.rename(
|
|
79
|
+
path.join(path.join(appdir(), folder), `${file}.${fileType}`),
|
|
80
|
+
path.join(path.join(appdir(), folder), `${newFile}.${fileType}`)
|
|
81
|
+
);
|
|
82
|
+
socket.emitChanged('app-files-changed', { app: folder });
|
|
83
|
+
this.emitChangedDbApp(folder);
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
renameFolder_meta: true,
|
|
87
|
+
async renameFolder({ folder, newFolder }) {
|
|
88
|
+
const uniqueName = await this.getNewAppFolder({ name: newFolder });
|
|
89
|
+
await fs.rename(path.join(appdir(), folder), path.join(appdir(), uniqueName));
|
|
90
|
+
socket.emitChanged(`app-folders-changed`);
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
deleteFolder_meta: true,
|
|
94
|
+
async deleteFolder({ folder }) {
|
|
95
|
+
if (!folder) throw new Error('Missing folder parameter');
|
|
96
|
+
await fs.rmdir(path.join(appdir(), folder), { recursive: true });
|
|
97
|
+
socket.emitChanged(`app-folders-changed`);
|
|
98
|
+
socket.emitChanged('app-files-changed', { app: folder });
|
|
99
|
+
socket.emitChanged('used-apps-changed');
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
async getNewAppFolder({ name }) {
|
|
103
|
+
if (!(await fs.exists(path.join(appdir(), name)))) return name;
|
|
104
|
+
let index = 2;
|
|
105
|
+
while (await fs.exists(path.join(appdir(), `${name}${index}`))) {
|
|
106
|
+
index += 1;
|
|
107
|
+
}
|
|
108
|
+
return `${name}${index}`;
|
|
109
|
+
},
|
|
110
|
+
|
|
111
|
+
getUsedAppFolders_meta: true,
|
|
112
|
+
async getUsedAppFolders() {
|
|
113
|
+
const list = await connections.list();
|
|
114
|
+
const apps = [];
|
|
115
|
+
|
|
116
|
+
for (const connection of list) {
|
|
117
|
+
for (const db of connection.databases || []) {
|
|
118
|
+
for (const key of _.keys(db || {})) {
|
|
119
|
+
if (key.startsWith('useApp:') && db[key]) {
|
|
120
|
+
apps.push(key.substring('useApp:'.length));
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return _.intersection(_.uniq(apps), await fs.readdir(appdir()));
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
getUsedApps_meta: true,
|
|
130
|
+
async getUsedApps() {
|
|
131
|
+
const apps = await this.getUsedAppFolders();
|
|
132
|
+
const res = [];
|
|
133
|
+
|
|
134
|
+
for (const folder of apps) {
|
|
135
|
+
res.push(await this.loadApp({ folder }));
|
|
136
|
+
}
|
|
137
|
+
return res;
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
// getAppsForDb_meta: true,
|
|
141
|
+
// async getAppsForDb({ conid, database }) {
|
|
142
|
+
// const connection = await connections.get({ conid });
|
|
143
|
+
// if (!connection) return [];
|
|
144
|
+
// const db = (connection.databases || []).find(x => x.name == database);
|
|
145
|
+
// const apps = [];
|
|
146
|
+
// const res = [];
|
|
147
|
+
// if (db) {
|
|
148
|
+
// for (const key of _.keys(db || {})) {
|
|
149
|
+
// if (key.startsWith('useApp:') && db[key]) {
|
|
150
|
+
// apps.push(key.substring('useApp:'.length));
|
|
151
|
+
// }
|
|
152
|
+
// }
|
|
153
|
+
// }
|
|
154
|
+
// for (const folder of apps) {
|
|
155
|
+
// res.push(await this.loadApp({ folder }));
|
|
156
|
+
// }
|
|
157
|
+
// return res;
|
|
158
|
+
// },
|
|
159
|
+
|
|
160
|
+
loadApp_meta: true,
|
|
161
|
+
async loadApp({ folder }) {
|
|
162
|
+
const res = {
|
|
163
|
+
queries: [],
|
|
164
|
+
commands: [],
|
|
165
|
+
name: folder,
|
|
166
|
+
};
|
|
167
|
+
const dir = path.join(appdir(), folder);
|
|
168
|
+
if (await fs.exists(dir)) {
|
|
169
|
+
const files = await fs.readdir(dir);
|
|
170
|
+
|
|
171
|
+
async function processType(ext, field) {
|
|
172
|
+
for (const file of files) {
|
|
173
|
+
if (file.endsWith(ext)) {
|
|
174
|
+
res[field].push({
|
|
175
|
+
name: file.slice(0, -ext.length),
|
|
176
|
+
sql: await fs.readFile(path.join(dir, file), { encoding: 'utf-8' }),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
await processType('.command.sql', 'commands');
|
|
183
|
+
await processType('.query.sql', 'queries');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
res.virtualReferences = JSON.parse(
|
|
188
|
+
await fs.readFile(path.join(dir, 'virtual-references.config.json'), { encoding: 'utf-8' })
|
|
189
|
+
);
|
|
190
|
+
} catch (err) {
|
|
191
|
+
res.virtualReferences = [];
|
|
192
|
+
}
|
|
193
|
+
try {
|
|
194
|
+
res.dictionaryDescriptions = JSON.parse(
|
|
195
|
+
await fs.readFile(path.join(dir, 'dictionary-descriptions.config.json'), { encoding: 'utf-8' })
|
|
196
|
+
);
|
|
197
|
+
} catch (err) {
|
|
198
|
+
res.dictionaryDescriptions = [];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return res;
|
|
202
|
+
},
|
|
203
|
+
|
|
204
|
+
async saveConfigFile(appFolder, filename, filterFunc, newItem) {
|
|
205
|
+
const file = path.join(appdir(), appFolder, filename);
|
|
206
|
+
|
|
207
|
+
let json;
|
|
208
|
+
try {
|
|
209
|
+
json = JSON.parse(await fs.readFile(file, { encoding: 'utf-8' }));
|
|
210
|
+
} catch (err) {
|
|
211
|
+
json = [];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (filterFunc) {
|
|
215
|
+
json = json.filter(filterFunc);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
json = [...json, newItem];
|
|
219
|
+
|
|
220
|
+
await fs.writeFile(file, JSON.stringify(json, undefined, 2));
|
|
221
|
+
|
|
222
|
+
socket.emitChanged('app-files-changed', { app: appFolder });
|
|
223
|
+
socket.emitChanged('used-apps-changed');
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
saveVirtualReference_meta: true,
|
|
227
|
+
async saveVirtualReference({ appFolder, schemaName, pureName, refSchemaName, refTableName, columns }) {
|
|
228
|
+
await this.saveConfigFile(
|
|
229
|
+
appFolder,
|
|
230
|
+
'virtual-references.config.json',
|
|
231
|
+
columns.length == 1
|
|
232
|
+
? x =>
|
|
233
|
+
!(
|
|
234
|
+
x.schemaName == schemaName &&
|
|
235
|
+
x.pureName == pureName &&
|
|
236
|
+
x.columns.length == 1 &&
|
|
237
|
+
x.columns[0].columnName == columns[0].columnName
|
|
238
|
+
)
|
|
239
|
+
: null,
|
|
240
|
+
{
|
|
241
|
+
schemaName,
|
|
242
|
+
pureName,
|
|
243
|
+
refSchemaName,
|
|
244
|
+
refTableName,
|
|
245
|
+
columns,
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
return true;
|
|
249
|
+
},
|
|
250
|
+
|
|
251
|
+
saveDictionaryDescription_meta: true,
|
|
252
|
+
async saveDictionaryDescription({ appFolder, pureName, schemaName, expression, columns, delimiter }) {
|
|
253
|
+
await this.saveConfigFile(
|
|
254
|
+
appFolder,
|
|
255
|
+
'dictionary-descriptions.config.json',
|
|
256
|
+
x => !(x.schemaName == schemaName && x.pureName == pureName),
|
|
257
|
+
{
|
|
258
|
+
schemaName,
|
|
259
|
+
pureName,
|
|
260
|
+
expression,
|
|
261
|
+
columns,
|
|
262
|
+
delimiter,
|
|
263
|
+
}
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
return true;
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
createConfigFile_meta: true,
|
|
270
|
+
async createConfigFile({ appFolder, fileName, content }) {
|
|
271
|
+
const file = path.join(appdir(), appFolder, fileName);
|
|
272
|
+
if (!(await fs.exists(file))) {
|
|
273
|
+
await fs.writeFile(file, JSON.stringify(content, undefined, 2));
|
|
274
|
+
socket.emitChanged('app-files-changed', { app: appFolder });
|
|
275
|
+
socket.emitChanged('used-apps-changed');
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
return false;
|
|
279
|
+
},
|
|
280
|
+
};
|