mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-30 16:13:58 +00:00
Merge branch 'master' into develop
This commit is contained in:
17
CHANGELOG.md
17
CHANGELOG.md
@@ -8,6 +8,23 @@ Builds:
|
|||||||
- linux - application for linux
|
- linux - application for linux
|
||||||
- win - application for Windows
|
- win - application for Windows
|
||||||
|
|
||||||
|
### 5.0.8
|
||||||
|
- ADDED: SQL Server - support using domain logins under Linux and Mac #305
|
||||||
|
- ADDED: Permissions for connections #318
|
||||||
|
- ADDED: Ability to change editor front #308
|
||||||
|
- ADDED: Custom expression in query designer #306
|
||||||
|
- ADDED: OR conditions in query designer #321
|
||||||
|
- ADDED: Ability to configure settings view environment variables #304
|
||||||
|
|
||||||
|
### 5.0.7
|
||||||
|
- FIXED: Fixed some problems with SSH tunnel (upgraded SSH client) #315
|
||||||
|
- FIXED: Fixed MognoDB executing find query #312
|
||||||
|
- ADDED: Interval filters for date/time columns #311
|
||||||
|
- ADDED: Ability to clone rows #309
|
||||||
|
- ADDED: connecting option Trust server certificate for SQL Server #305
|
||||||
|
- ADDED: Autorefresh, reload table every x second #303
|
||||||
|
- FIXED(app): Changing editor theme and font size in Editor Themes #300
|
||||||
|
|
||||||
### 5.0.6
|
### 5.0.6
|
||||||
- ADDED: Search in columns
|
- ADDED: Search in columns
|
||||||
- CHANGED: Upgraded mongodb driver
|
- CHANGED: Upgraded mongodb driver
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "5.0.7-beta.4",
|
"version": "5.0.8",
|
||||||
"name": "dbgate-all",
|
"name": "dbgate-all",
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"packages/*",
|
"packages/*",
|
||||||
|
|||||||
11
packages/api/env/portal/.env
vendored
11
packages/api/env/portal/.env
vendored
@@ -48,4 +48,15 @@ PASSWORD_relational=relational
|
|||||||
ENGINE_relational=mariadb@dbgate-plugin-mysql
|
ENGINE_relational=mariadb@dbgate-plugin-mysql
|
||||||
READONLY_relational=1
|
READONLY_relational=1
|
||||||
|
|
||||||
|
# SETTINGS_dataGrid.showHintColumns=1
|
||||||
|
|
||||||
# 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
|
# 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
|
||||||
|
|
||||||
|
# LOGINS=x,y
|
||||||
|
# LOGIN_PASSWORD_x=x
|
||||||
|
# LOGIN_PASSWORD_y=LOGIN_PASSWORD_y
|
||||||
|
# LOGIN_PERMISSIONS_x=~*
|
||||||
|
# LOGIN_PERMISSIONS_y=~*
|
||||||
|
|
||||||
|
# PERMISSIONS=~*,connections/relational
|
||||||
|
# PERMISSIONS=~*
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ module.exports = {
|
|||||||
async get(_params, req) {
|
async get(_params, req) {
|
||||||
const logins = getLogins();
|
const logins = getLogins();
|
||||||
const login = logins ? logins.find(x => x.login == (req.auth && req.auth.user)) : null;
|
const login = logins ? logins.find(x => x.login == (req.auth && req.auth.user)) : null;
|
||||||
const permissions = login ? login.permissions : null;
|
const permissions = login ? login.permissions : process.env.PERMISSIONS;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
runAsPortal: !!connections.portalConnections,
|
runAsPortal: !!connections.portalConnections,
|
||||||
@@ -73,6 +73,14 @@ module.exports = {
|
|||||||
// res['app.useNativeMenu'] = os.platform() == 'darwin' ? true : false;
|
// res['app.useNativeMenu'] = os.platform() == 'darwin' ? true : false;
|
||||||
res['app.useNativeMenu'] = false;
|
res['app.useNativeMenu'] = false;
|
||||||
}
|
}
|
||||||
|
for (const envVar in process.env) {
|
||||||
|
if (envVar.startsWith('SETTINGS_')) {
|
||||||
|
const key = envVar.substring('SETTINGS_'.length);
|
||||||
|
if (!res[key]) {
|
||||||
|
res[key] = process.env[envVar];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ const JsonLinesDatabase = require('../utility/JsonLinesDatabase');
|
|||||||
const processArgs = require('../utility/processArgs');
|
const processArgs = require('../utility/processArgs');
|
||||||
const { safeJsonParse } = require('dbgate-tools');
|
const { safeJsonParse } = require('dbgate-tools');
|
||||||
const platformInfo = require('../utility/platformInfo');
|
const platformInfo = require('../utility/platformInfo');
|
||||||
|
const { connectionHasPermission, testConnectionPermission } = require('../utility/hasPermission');
|
||||||
|
|
||||||
function getNamedArgs() {
|
function getNamedArgs() {
|
||||||
const res = {};
|
const res = {};
|
||||||
@@ -165,12 +166,12 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
list_meta: true,
|
list_meta: true,
|
||||||
async list() {
|
async list(_params, req) {
|
||||||
if (portalConnections) {
|
if (portalConnections) {
|
||||||
if (platformInfo.allowShellConnection) return portalConnections;
|
if (platformInfo.allowShellConnection) return portalConnections;
|
||||||
return portalConnections.map(maskConnection);
|
return portalConnections.map(maskConnection).filter(x => connectionHasPermission(x, req));
|
||||||
}
|
}
|
||||||
return this.datastore.find();
|
return (await this.datastore.find()).filter(x => connectionHasPermission(x, req));
|
||||||
},
|
},
|
||||||
|
|
||||||
test_meta: true,
|
test_meta: true,
|
||||||
@@ -217,16 +218,18 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
update_meta: true,
|
update_meta: true,
|
||||||
async update({ _id, values }) {
|
async update({ _id, values }, req) {
|
||||||
if (portalConnections) return;
|
if (portalConnections) return;
|
||||||
|
testConnectionPermission(_id, req);
|
||||||
const res = await this.datastore.patch(_id, values);
|
const res = await this.datastore.patch(_id, values);
|
||||||
socket.emitChanged('connection-list-changed');
|
socket.emitChanged('connection-list-changed');
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
|
||||||
updateDatabase_meta: true,
|
updateDatabase_meta: true,
|
||||||
async updateDatabase({ conid, database, values }) {
|
async updateDatabase({ conid, database, values }, req) {
|
||||||
if (portalConnections) return;
|
if (portalConnections) return;
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
const conn = await this.datastore.get(conid);
|
const conn = await this.datastore.get(conid);
|
||||||
let databases = (conn && conn.databases) || [];
|
let databases = (conn && conn.databases) || [];
|
||||||
if (databases.find(x => x.name == database)) {
|
if (databases.find(x => x.name == database)) {
|
||||||
@@ -242,8 +245,9 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
delete_meta: true,
|
delete_meta: true,
|
||||||
async delete(connection) {
|
async delete(connection, req) {
|
||||||
if (portalConnections) return;
|
if (portalConnections) return;
|
||||||
|
testConnectionPermission(connection, req);
|
||||||
const res = await this.datastore.remove(connection._id);
|
const res = await this.datastore.remove(connection._id);
|
||||||
socket.emitChanged('connection-list-changed');
|
socket.emitChanged('connection-list-changed');
|
||||||
return res;
|
return res;
|
||||||
@@ -260,7 +264,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
get_meta: true,
|
get_meta: true,
|
||||||
async get({ conid }) {
|
async get({ conid }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
return this.getCore({ conid, mask: true });
|
return this.getCore({ conid, mask: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ const generateDeploySql = require('../shell/generateDeploySql');
|
|||||||
const { createTwoFilesPatch } = require('diff');
|
const { createTwoFilesPatch } = require('diff');
|
||||||
const diff2htmlPage = require('../utility/diff2htmlPage');
|
const diff2htmlPage = require('../utility/diff2htmlPage');
|
||||||
const processArgs = require('../utility/processArgs');
|
const processArgs = require('../utility/processArgs');
|
||||||
|
const { testConnectionPermission } = require('../utility/hasPermission');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
/** @type {import('dbgate-types').OpenedDatabaseConnection[]} */
|
/** @type {import('dbgate-types').OpenedDatabaseConnection[]} */
|
||||||
@@ -130,7 +131,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
queryData_meta: true,
|
queryData_meta: true,
|
||||||
async queryData({ conid, database, sql }) {
|
async queryData({ conid, database, sql }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
console.log(`Processing query, conid=${conid}, database=${database}, sql=${sql}`);
|
console.log(`Processing query, conid=${conid}, database=${database}, sql=${sql}`);
|
||||||
const opened = await this.ensureOpened(conid, database);
|
const opened = await this.ensureOpened(conid, database);
|
||||||
// if (opened && opened.status && opened.status.name == 'error') {
|
// if (opened && opened.status && opened.status.name == 'error') {
|
||||||
@@ -141,14 +143,16 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
sqlSelect_meta: true,
|
sqlSelect_meta: true,
|
||||||
async sqlSelect({ conid, database, select }) {
|
async sqlSelect({ conid, database, select }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
const opened = await this.ensureOpened(conid, database);
|
const opened = await this.ensureOpened(conid, database);
|
||||||
const res = await this.sendRequest(opened, { msgtype: 'sqlSelect', select });
|
const res = await this.sendRequest(opened, { msgtype: 'sqlSelect', select });
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
|
||||||
runScript_meta: true,
|
runScript_meta: true,
|
||||||
async runScript({ conid, database, sql }) {
|
async runScript({ conid, database, sql }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
console.log(`Processing script, conid=${conid}, database=${database}, sql=${sql}`);
|
console.log(`Processing script, conid=${conid}, database=${database}, sql=${sql}`);
|
||||||
const opened = await this.ensureOpened(conid, database);
|
const opened = await this.ensureOpened(conid, database);
|
||||||
const res = await this.sendRequest(opened, { msgtype: 'runScript', sql });
|
const res = await this.sendRequest(opened, { msgtype: 'runScript', sql });
|
||||||
@@ -156,13 +160,15 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
collectionData_meta: true,
|
collectionData_meta: true,
|
||||||
async collectionData({ conid, database, options }) {
|
async collectionData({ conid, database, options }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
const opened = await this.ensureOpened(conid, database);
|
const opened = await this.ensureOpened(conid, database);
|
||||||
const res = await this.sendRequest(opened, { msgtype: 'collectionData', options });
|
const res = await this.sendRequest(opened, { msgtype: 'collectionData', options });
|
||||||
return res.result || null;
|
return res.result || null;
|
||||||
},
|
},
|
||||||
|
|
||||||
async loadDataCore(msgtype, { conid, database, ...args }) {
|
async loadDataCore(msgtype, { conid, database, ...args }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
const opened = await this.ensureOpened(conid, database);
|
const opened = await this.ensureOpened(conid, database);
|
||||||
const res = await this.sendRequest(opened, { msgtype, ...args });
|
const res = await this.sendRequest(opened, { msgtype, ...args });
|
||||||
if (res.errorMessage) {
|
if (res.errorMessage) {
|
||||||
@@ -176,32 +182,38 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
loadKeys_meta: true,
|
loadKeys_meta: true,
|
||||||
async loadKeys({ conid, database, root, filter }) {
|
async loadKeys({ conid, database, root, filter }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
return this.loadDataCore('loadKeys', { conid, database, root, filter });
|
return this.loadDataCore('loadKeys', { conid, database, root, filter });
|
||||||
},
|
},
|
||||||
|
|
||||||
exportKeys_meta: true,
|
exportKeys_meta: true,
|
||||||
async exportKeys({ conid, database, options }) {
|
async exportKeys({ conid, database, options }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
return this.loadDataCore('exportKeys', { conid, database, options });
|
return this.loadDataCore('exportKeys', { conid, database, options });
|
||||||
},
|
},
|
||||||
|
|
||||||
loadKeyInfo_meta: true,
|
loadKeyInfo_meta: true,
|
||||||
async loadKeyInfo({ conid, database, key }) {
|
async loadKeyInfo({ conid, database, key }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
return this.loadDataCore('loadKeyInfo', { conid, database, key });
|
return this.loadDataCore('loadKeyInfo', { conid, database, key });
|
||||||
},
|
},
|
||||||
|
|
||||||
loadKeyTableRange_meta: true,
|
loadKeyTableRange_meta: true,
|
||||||
async loadKeyTableRange({ conid, database, key, cursor, count }) {
|
async loadKeyTableRange({ conid, database, key, cursor, count }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
return this.loadDataCore('loadKeyTableRange', { conid, database, key, cursor, count });
|
return this.loadDataCore('loadKeyTableRange', { conid, database, key, cursor, count });
|
||||||
},
|
},
|
||||||
|
|
||||||
loadFieldValues_meta: true,
|
loadFieldValues_meta: true,
|
||||||
async loadFieldValues({ conid, database, schemaName, pureName, field, search }) {
|
async loadFieldValues({ conid, database, schemaName, pureName, field, search }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
return this.loadDataCore('loadFieldValues', { conid, database, schemaName, pureName, field, search });
|
return this.loadDataCore('loadFieldValues', { conid, database, schemaName, pureName, field, search });
|
||||||
},
|
},
|
||||||
|
|
||||||
callMethod_meta: true,
|
callMethod_meta: true,
|
||||||
async callMethod({ conid, database, method, args }) {
|
async callMethod({ conid, database, method, args }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
return this.loadDataCore('callMethod', { conid, database, method, args });
|
return this.loadDataCore('callMethod', { conid, database, method, args });
|
||||||
|
|
||||||
// const opened = await this.ensureOpened(conid, database);
|
// const opened = await this.ensureOpened(conid, database);
|
||||||
@@ -213,7 +225,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
updateCollection_meta: true,
|
updateCollection_meta: true,
|
||||||
async updateCollection({ conid, database, changeSet }) {
|
async updateCollection({ conid, database, changeSet }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
const opened = await this.ensureOpened(conid, database);
|
const opened = await this.ensureOpened(conid, database);
|
||||||
const res = await this.sendRequest(opened, { msgtype: 'updateCollection', changeSet });
|
const res = await this.sendRequest(opened, { msgtype: 'updateCollection', changeSet });
|
||||||
if (res.errorMessage) {
|
if (res.errorMessage) {
|
||||||
@@ -225,7 +238,14 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
status_meta: true,
|
status_meta: true,
|
||||||
async status({ conid, database }) {
|
async status({ conid, database }, req) {
|
||||||
|
if (!conid) {
|
||||||
|
return {
|
||||||
|
name: 'error',
|
||||||
|
message: 'No connection',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
const existing = this.opened.find(x => x.conid == conid && x.database == database);
|
const existing = this.opened.find(x => x.conid == conid && x.database == database);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
return {
|
return {
|
||||||
@@ -247,7 +267,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
ping_meta: true,
|
ping_meta: true,
|
||||||
async ping({ conid, database }) {
|
async ping({ conid, database }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
let existing = this.opened.find(x => x.conid == conid && x.database == database);
|
let existing = this.opened.find(x => x.conid == conid && x.database == database);
|
||||||
|
|
||||||
if (existing) {
|
if (existing) {
|
||||||
@@ -263,7 +284,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
refresh_meta: true,
|
refresh_meta: true,
|
||||||
async refresh({ conid, database, keepOpen }) {
|
async refresh({ conid, database, keepOpen }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
if (!keepOpen) this.close(conid, database);
|
if (!keepOpen) this.close(conid, database);
|
||||||
|
|
||||||
await this.ensureOpened(conid, database);
|
await this.ensureOpened(conid, database);
|
||||||
@@ -271,7 +293,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
syncModel_meta: true,
|
syncModel_meta: true,
|
||||||
async syncModel({ conid, database, isFullRefresh }) {
|
async syncModel({ conid, database, isFullRefresh }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
const conn = await this.ensureOpened(conid, database);
|
const conn = await this.ensureOpened(conid, database);
|
||||||
conn.subprocess.send({ msgtype: 'syncModel', isFullRefresh });
|
conn.subprocess.send({ msgtype: 'syncModel', isFullRefresh });
|
||||||
return { status: 'ok' };
|
return { status: 'ok' };
|
||||||
@@ -301,13 +324,15 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
disconnect_meta: true,
|
disconnect_meta: true,
|
||||||
async disconnect({ conid, database }) {
|
async disconnect({ conid, database }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
await this.close(conid, database, true);
|
await this.close(conid, database, true);
|
||||||
return { status: 'ok' };
|
return { status: 'ok' };
|
||||||
},
|
},
|
||||||
|
|
||||||
structure_meta: true,
|
structure_meta: true,
|
||||||
async structure({ conid, database }) {
|
async structure({ conid, database }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
if (conid == '__model') {
|
if (conid == '__model') {
|
||||||
const model = await importDbModel(database);
|
const model = await importDbModel(database);
|
||||||
return model;
|
return model;
|
||||||
@@ -324,14 +349,19 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
serverVersion_meta: true,
|
serverVersion_meta: true,
|
||||||
async serverVersion({ conid, database }) {
|
async serverVersion({ conid, database }, req) {
|
||||||
|
if (!conid) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
if (!conid) return null;
|
if (!conid) return null;
|
||||||
const opened = await this.ensureOpened(conid, database);
|
const opened = await this.ensureOpened(conid, database);
|
||||||
return opened.serverVersion || null;
|
return opened.serverVersion || null;
|
||||||
},
|
},
|
||||||
|
|
||||||
sqlPreview_meta: true,
|
sqlPreview_meta: true,
|
||||||
async sqlPreview({ conid, database, objects, options }) {
|
async sqlPreview({ conid, database, objects, options }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
// wait for structure
|
// wait for structure
|
||||||
await this.structure({ conid, database });
|
await this.structure({ conid, database });
|
||||||
|
|
||||||
@@ -341,7 +371,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
exportModel_meta: true,
|
exportModel_meta: true,
|
||||||
async exportModel({ conid, database }) {
|
async exportModel({ conid, database }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
const archiveFolder = await archive.getNewArchiveFolder({ database });
|
const archiveFolder = await archive.getNewArchiveFolder({ database });
|
||||||
await fs.mkdir(path.join(archivedir(), archiveFolder));
|
await fs.mkdir(path.join(archivedir(), archiveFolder));
|
||||||
const model = await this.structure({ conid, database });
|
const model = await this.structure({ conid, database });
|
||||||
@@ -351,7 +382,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
generateDeploySql_meta: true,
|
generateDeploySql_meta: true,
|
||||||
async generateDeploySql({ conid, database, archiveFolder }) {
|
async generateDeploySql({ conid, database, archiveFolder }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
const opened = await this.ensureOpened(conid, database);
|
const opened = await this.ensureOpened(conid, database);
|
||||||
const res = await this.sendRequest(opened, {
|
const res = await this.sendRequest(opened, {
|
||||||
msgtype: 'generateDeploySql',
|
msgtype: 'generateDeploySql',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const { handleProcessCommunication } = require('../utility/processComm');
|
|||||||
const lock = new AsyncLock();
|
const lock = new AsyncLock();
|
||||||
const config = require('./config');
|
const config = require('./config');
|
||||||
const processArgs = require('../utility/processArgs');
|
const processArgs = require('../utility/processArgs');
|
||||||
|
const { testConnectionPermission } = require('../utility/hasPermission');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
opened: [],
|
opened: [],
|
||||||
@@ -90,19 +91,22 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
disconnect_meta: true,
|
disconnect_meta: true,
|
||||||
async disconnect({ conid }) {
|
async disconnect({ conid }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
await this.close(conid, true);
|
await this.close(conid, true);
|
||||||
return { status: 'ok' };
|
return { status: 'ok' };
|
||||||
},
|
},
|
||||||
|
|
||||||
listDatabases_meta: true,
|
listDatabases_meta: true,
|
||||||
async listDatabases({ conid }) {
|
async listDatabases({ conid }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
const opened = await this.ensureOpened(conid);
|
const opened = await this.ensureOpened(conid);
|
||||||
return opened.databases;
|
return opened.databases;
|
||||||
},
|
},
|
||||||
|
|
||||||
version_meta: true,
|
version_meta: true,
|
||||||
async version({ conid }) {
|
async version({ conid }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
const opened = await this.ensureOpened(conid);
|
const opened = await this.ensureOpened(conid);
|
||||||
return opened.version;
|
return opened.version;
|
||||||
},
|
},
|
||||||
@@ -132,7 +136,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
refresh_meta: true,
|
refresh_meta: true,
|
||||||
async refresh({ conid, keepOpen }) {
|
async refresh({ conid, keepOpen }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
if (!keepOpen) this.close(conid);
|
if (!keepOpen) this.close(conid);
|
||||||
|
|
||||||
await this.ensureOpened(conid);
|
await this.ensureOpened(conid);
|
||||||
@@ -140,7 +145,8 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
createDatabase_meta: true,
|
createDatabase_meta: true,
|
||||||
async createDatabase({ conid, name }) {
|
async createDatabase({ conid, name }, req) {
|
||||||
|
testConnectionPermission(conid, req);
|
||||||
const opened = await this.ensureOpened(conid);
|
const opened = await this.ensureOpened(conid);
|
||||||
if (opened.connection.isReadOnly) return false;
|
if (opened.connection.isReadOnly) return false;
|
||||||
opened.subprocess.send({ msgtype: 'createDatabase', name });
|
opened.subprocess.send({ msgtype: 'createDatabase', name });
|
||||||
|
|||||||
@@ -4,12 +4,21 @@ const _ = require('lodash');
|
|||||||
const userPermissions = {};
|
const userPermissions = {};
|
||||||
|
|
||||||
function hasPermission(tested, req) {
|
function hasPermission(tested, req) {
|
||||||
|
if (!req) {
|
||||||
|
// request object not available, allow all
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const { user } = (req && req.auth) || {};
|
const { user } = (req && req.auth) || {};
|
||||||
const key = user || '';
|
const key = user || '';
|
||||||
const logins = getLogins();
|
const logins = getLogins();
|
||||||
if (!userPermissions[key] && logins) {
|
|
||||||
const login = logins.find(x => x.login == user);
|
if (!userPermissions[key]) {
|
||||||
userPermissions[key] = compilePermissions(login ? login.permissions : null);
|
if (logins) {
|
||||||
|
const login = logins.find(x => x.login == user);
|
||||||
|
userPermissions[key] = compilePermissions(login ? login.permissions : null);
|
||||||
|
} else {
|
||||||
|
userPermissions[key] = compilePermissions(process.env.PERMISSIONS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return testPermission(tested, userPermissions[key]);
|
return testPermission(tested, userPermissions[key]);
|
||||||
}
|
}
|
||||||
@@ -50,7 +59,26 @@ function getLogins() {
|
|||||||
return loginsCache;
|
return loginsCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function connectionHasPermission(connection, req) {
|
||||||
|
if (!connection) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (_.isString(connection)) {
|
||||||
|
return hasPermission(`connections/${connection}`, req);
|
||||||
|
} else {
|
||||||
|
return hasPermission(`connections/${connection._id}`, req);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testConnectionPermission(connection, req) {
|
||||||
|
if (!connectionHasPermission(connection, req)) {
|
||||||
|
throw new Error('Connection permission not granted');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
hasPermission,
|
hasPermission,
|
||||||
getLogins,
|
getLogins,
|
||||||
|
connectionHasPermission,
|
||||||
|
testConnectionPermission,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ module.exports = function useController(app, electron, route, controller) {
|
|||||||
|
|
||||||
let method = 'post';
|
let method = 'post';
|
||||||
let raw = false;
|
let raw = false;
|
||||||
let rawParams = false;
|
|
||||||
|
|
||||||
// if (_.isString(meta)) {
|
// if (_.isString(meta)) {
|
||||||
// method = meta;
|
// method = meta;
|
||||||
@@ -55,7 +54,6 @@ module.exports = function useController(app, electron, route, controller) {
|
|||||||
if (_.isPlainObject(meta)) {
|
if (_.isPlainObject(meta)) {
|
||||||
method = meta.method;
|
method = meta.method;
|
||||||
raw = meta.raw;
|
raw = meta.raw;
|
||||||
rawParams = meta.rawParams;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (raw) {
|
if (raw) {
|
||||||
@@ -67,9 +65,7 @@ module.exports = function useController(app, electron, route, controller) {
|
|||||||
// controller._init_called = true;
|
// controller._init_called = true;
|
||||||
// }
|
// }
|
||||||
try {
|
try {
|
||||||
let params = [{ ...req.body, ...req.query }, req];
|
const data = await controller[key]({ ...req.body, ...req.query }, req);
|
||||||
if (rawParams) params = [req, res];
|
|
||||||
const data = await controller[key](...params);
|
|
||||||
res.json(data);
|
res.json(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
id: 'dataGrid.cloneRows',
|
id: 'dataGrid.cloneRows',
|
||||||
category: 'Data grid',
|
category: 'Data grid',
|
||||||
name: 'Clone rows',
|
name: 'Clone rows',
|
||||||
toolbarName: 'Clone',
|
toolbarName: 'Clone row(s)',
|
||||||
keyText: 'CtrlOrCommand+Shift+C',
|
keyText: 'CtrlOrCommand+Shift+C',
|
||||||
testEnabled: () => getCurrentDataGrid()?.getGrider()?.editable,
|
testEnabled: () => getCurrentDataGrid()?.getGrider()?.editable,
|
||||||
onClick: () => getCurrentDataGrid().cloneRows(),
|
onClick: () => getCurrentDataGrid().cloneRows(),
|
||||||
|
|||||||
@@ -167,8 +167,8 @@
|
|||||||
async function detectSize(tables, domTables) {
|
async function detectSize(tables, domTables) {
|
||||||
await tick();
|
await tick();
|
||||||
const rects = _.values(domTables).map(x => x.getRect());
|
const rects = _.values(domTables).map(x => x.getRect());
|
||||||
const maxX = _.max(rects.map(x => x.right));
|
const maxX = rects.length > 0 ? _.max(rects.map(x => x.right)) : 0;
|
||||||
const maxY = _.max(rects.map(x => x.bottom));
|
const maxY = rects.length > 0 ? _.max(rects.map(x => x.bottom)) : 0;
|
||||||
|
|
||||||
canvasWidth = Math.max(3000, maxX + 50);
|
canvasWidth = Math.max(3000, maxX + 50);
|
||||||
canvasHeight = Math.max(3000, maxY + 50);
|
canvasHeight = Math.max(3000, maxY + 50);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
mergeConditions,
|
mergeConditions,
|
||||||
Source,
|
Source,
|
||||||
ResultField,
|
ResultField,
|
||||||
|
Expression,
|
||||||
} from 'dbgate-sqltree';
|
} from 'dbgate-sqltree';
|
||||||
import { EngineDriver } from 'dbgate-types';
|
import { EngineDriver } from 'dbgate-types';
|
||||||
import { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types';
|
import { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types';
|
||||||
@@ -78,25 +79,27 @@ export class DesignerQueryDumper {
|
|||||||
return select;
|
return select;
|
||||||
}
|
}
|
||||||
|
|
||||||
addConditions(select: Select, tables: DesignerTableInfo[]) {
|
buildConditionFromFilterField(tables: DesignerTableInfo[], filterField: string, getExpression?: Function): Condition {
|
||||||
|
const conditions = [];
|
||||||
|
|
||||||
for (const column of this.designer.columns || []) {
|
for (const column of this.designer.columns || []) {
|
||||||
if (!column.filter) continue;
|
if (!column[filterField]) continue;
|
||||||
const table = (this.designer.tables || []).find(x => x.designerId == column.designerId);
|
|
||||||
if (!table) continue;
|
if (!column.isCustomExpression) {
|
||||||
if (!tables.find(x => x.designerId == table.designerId)) continue;
|
const table = (this.designer.tables || []).find(x => x.designerId == column.designerId);
|
||||||
|
if (!table) continue;
|
||||||
|
if (!tables.find(x => x.designerId == table.designerId)) continue;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const condition = parseFilter(column.filter, findDesignerFilterType(column, this.designer));
|
const condition = parseFilter(column[filterField], findDesignerFilterType(column, this.designer));
|
||||||
if (condition) {
|
if (condition) {
|
||||||
select.where = mergeConditions(
|
conditions.push(
|
||||||
select.where,
|
|
||||||
_.cloneDeepWith(condition, expr => {
|
_.cloneDeepWith(condition, expr => {
|
||||||
if (expr.exprType == 'placeholder')
|
if (expr.exprType == 'placeholder') {
|
||||||
return {
|
if (getExpression) return getExpression(column);
|
||||||
exprType: 'column',
|
return this.getColumnExpression(column);
|
||||||
columnName: column.columnName,
|
}
|
||||||
source: findQuerySource(this.designer, column.designerId),
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -105,33 +108,79 @@ export class DesignerQueryDumper {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conditions.length == 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conditions.length == 1) {
|
||||||
|
return conditions[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
conditionType: 'and',
|
||||||
|
conditions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
addConditionsCore(select: Select, tables: DesignerTableInfo[], filterFields, selectField, getExpression?) {
|
||||||
|
const conditions: Condition[] = _.compact(
|
||||||
|
filterFields.map(field => this.buildConditionFromFilterField(tables, field, getExpression))
|
||||||
|
);
|
||||||
|
|
||||||
|
if (conditions.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (conditions.length == 0) {
|
||||||
|
select[selectField] = mergeConditions(select[selectField], conditions[0]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
select[selectField] = mergeConditions(select[selectField], {
|
||||||
|
conditionType: 'or',
|
||||||
|
conditions,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addConditions(select: Select, tables: DesignerTableInfo[]) {
|
||||||
|
const additionalFilterCount = this.designer.settings?.additionalFilterCount || 0;
|
||||||
|
const filterFields = ['filter', ..._.range(additionalFilterCount).map(index => `additionalFilter${index + 1}`)];
|
||||||
|
this.addConditionsCore(select, tables, filterFields, 'where');
|
||||||
}
|
}
|
||||||
|
|
||||||
addGroupConditions(select: Select, tables: DesignerTableInfo[], selectIsGrouped: boolean) {
|
addGroupConditions(select: Select, tables: DesignerTableInfo[], selectIsGrouped: boolean) {
|
||||||
for (const column of this.designer.columns || []) {
|
const additionalGroupFilterCount = this.designer.settings?.additionalGroupFilterCount || 0;
|
||||||
if (!column.groupFilter) continue;
|
const filterFields = [
|
||||||
const table = (this.designer.tables || []).find(x => x.designerId == column.designerId);
|
'groupFilter',
|
||||||
if (!table) continue;
|
..._.range(additionalGroupFilterCount).map(index => `additionalGroupFilter${index + 1}`),
|
||||||
if (!tables.find(x => x.designerId == table.designerId)) continue;
|
];
|
||||||
|
this.addConditionsCore(select, tables, filterFields, 'having', column =>
|
||||||
const condition = parseFilter(column.groupFilter, findDesignerFilterType(column, this.designer));
|
this.getColumnResultField(column, selectIsGrouped)
|
||||||
if (condition) {
|
);
|
||||||
select.having = mergeConditions(
|
|
||||||
select.having,
|
|
||||||
_.cloneDeepWith(condition, expr => {
|
|
||||||
if (expr.exprType == 'placeholder') {
|
|
||||||
return this.getColumnOutputExpression(column, selectIsGrouped);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getColumnOutputExpression(col, selectIsGrouped): ResultField {
|
getColumnExpression(col): Expression {
|
||||||
const source = findQuerySource(this.designer, col.designerId);
|
const source = findQuerySource(this.designer, col.designerId);
|
||||||
|
const { columnName, isCustomExpression, customExpression } = col;
|
||||||
|
|
||||||
|
const res: Expression = isCustomExpression
|
||||||
|
? {
|
||||||
|
exprType: 'raw',
|
||||||
|
sql: customExpression,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
exprType: 'column',
|
||||||
|
columnName,
|
||||||
|
source,
|
||||||
|
};
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
getColumnResultField(col, selectIsGrouped): ResultField {
|
||||||
const { columnName } = col;
|
const { columnName } = col;
|
||||||
let { alias } = col;
|
let { alias } = col;
|
||||||
|
|
||||||
|
const exprCore = this.getColumnExpression(col);
|
||||||
|
|
||||||
if (selectIsGrouped && !col.isGrouped) {
|
if (selectIsGrouped && !col.isGrouped) {
|
||||||
// use aggregate
|
// use aggregate
|
||||||
const aggregate = col.aggregate == null || col.aggregate == '---' ? 'MAX' : col.aggregate;
|
const aggregate = col.aggregate == null || col.aggregate == '---' ? 'MAX' : col.aggregate;
|
||||||
@@ -142,20 +191,12 @@ export class DesignerQueryDumper {
|
|||||||
func: aggregate == 'COUNT DISTINCT' ? 'COUNT' : aggregate,
|
func: aggregate == 'COUNT DISTINCT' ? 'COUNT' : aggregate,
|
||||||
argsPrefix: aggregate == 'COUNT DISTINCT' ? 'DISTINCT' : null,
|
argsPrefix: aggregate == 'COUNT DISTINCT' ? 'DISTINCT' : null,
|
||||||
alias,
|
alias,
|
||||||
args: [
|
args: [exprCore],
|
||||||
{
|
|
||||||
exprType: 'column',
|
|
||||||
columnName,
|
|
||||||
source,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
exprType: 'column',
|
...exprCore,
|
||||||
columnName,
|
|
||||||
alias,
|
alias,
|
||||||
source,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,24 +220,21 @@ export class DesignerQueryDumper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const topLevelColumns = (this.designer.columns || []).filter(col =>
|
const topLevelColumns = (this.designer.columns || []).filter(
|
||||||
topLevelTables.find(tbl => tbl.designerId == col.designerId)
|
col =>
|
||||||
|
topLevelTables.find(tbl => tbl.designerId == col.designerId) || (col.isCustomExpression && col.customExpression)
|
||||||
);
|
);
|
||||||
const selectIsGrouped = !!topLevelColumns.find(x => x.isGrouped || (x.aggregate && x.aggregate != '---'));
|
const selectIsGrouped = !!topLevelColumns.find(x => x.isGrouped || (x.aggregate && x.aggregate != '---'));
|
||||||
const outputColumns = topLevelColumns.filter(x => x.isOutput);
|
const outputColumns = topLevelColumns.filter(x => x.isOutput);
|
||||||
if (outputColumns.length == 0) {
|
if (outputColumns.length == 0) {
|
||||||
res.selectAll = true;
|
res.selectAll = true;
|
||||||
} else {
|
} else {
|
||||||
res.columns = outputColumns.map(col => this.getColumnOutputExpression(col, selectIsGrouped));
|
res.columns = outputColumns.map(col => this.getColumnResultField(col, selectIsGrouped));
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupedColumns = topLevelColumns.filter(x => x.isGrouped);
|
const groupedColumns = topLevelColumns.filter(x => x.isGrouped);
|
||||||
if (groupedColumns.length > 0) {
|
if (groupedColumns.length > 0) {
|
||||||
res.groupBy = groupedColumns.map(col => ({
|
res.groupBy = groupedColumns.map(col => this.getColumnExpression(col));
|
||||||
exprType: 'column',
|
|
||||||
columnName: col.columnName,
|
|
||||||
source: findQuerySource(this.designer, col.designerId),
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const orderColumns = _.sortBy(
|
const orderColumns = _.sortBy(
|
||||||
@@ -205,10 +243,8 @@ export class DesignerQueryDumper {
|
|||||||
);
|
);
|
||||||
if (orderColumns.length > 0) {
|
if (orderColumns.length > 0) {
|
||||||
res.orderBy = orderColumns.map(col => ({
|
res.orderBy = orderColumns.map(col => ({
|
||||||
exprType: 'column',
|
...this.getColumnExpression(col),
|
||||||
direction: col.sortOrder < 0 ? 'DESC' : 'ASC',
|
direction: col.sortOrder < 0 ? 'DESC' : 'ASC',
|
||||||
columnName: col.columnName,
|
|
||||||
source: findQuerySource(this.designer, col.designerId),
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,14 @@ export type DesignerColumnInfo = {
|
|||||||
sortOrder?: number;
|
sortOrder?: number;
|
||||||
filter?: string;
|
filter?: string;
|
||||||
groupFilter?: string;
|
groupFilter?: string;
|
||||||
|
isCustomExpression?: boolean;
|
||||||
|
customExpression?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DesignerSettings = {
|
export type DesignerSettings = {
|
||||||
isDistinct?: boolean;
|
isDistinct?: boolean;
|
||||||
|
additionalFilterCount?: number;
|
||||||
|
additionalGroupFilterCount?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DesignerInfo = {
|
export type DesignerInfo = {
|
||||||
|
|||||||
@@ -13,8 +13,11 @@
|
|||||||
import SelectField from '../forms/SelectField.svelte';
|
import SelectField from '../forms/SelectField.svelte';
|
||||||
import TextField from '../forms/TextField.svelte';
|
import TextField from '../forms/TextField.svelte';
|
||||||
import InlineButton from '../buttons/InlineButton.svelte';
|
import InlineButton from '../buttons/InlineButton.svelte';
|
||||||
|
import uuidv1 from 'uuid/v1';
|
||||||
|
|
||||||
import TableControl from './TableControl.svelte';
|
import TableControl from './TableControl.svelte';
|
||||||
|
import FormStyledButton from '../buttons/FormStyledButton.svelte';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
export let value;
|
export let value;
|
||||||
export let onChange;
|
export let onChange;
|
||||||
@@ -35,8 +38,56 @@
|
|||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addExpressionColumn = () => {
|
||||||
|
onChange(current => ({
|
||||||
|
...current,
|
||||||
|
columns: [...(current.columns || []), { isCustomExpression: true, isOutput: true, designerId: uuidv1() }],
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const addOrCondition = () => {
|
||||||
|
onChange(current => ({
|
||||||
|
...current,
|
||||||
|
settings: {
|
||||||
|
...current?.settings,
|
||||||
|
additionalFilterCount: (current?.settings?.additionalFilterCount ?? 0) + 1,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeOrCondition = () => {
|
||||||
|
onChange(current => ({
|
||||||
|
...current,
|
||||||
|
settings: {
|
||||||
|
...current?.settings,
|
||||||
|
additionalFilterCount: (current?.settings?.additionalFilterCount ?? 1) - 1,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const addGroupOrCondition = () => {
|
||||||
|
onChange(current => ({
|
||||||
|
...current,
|
||||||
|
settings: {
|
||||||
|
...current?.settings,
|
||||||
|
additionalGroupFilterCount: (current?.settings?.additionalGroupFilterCount ?? 0) + 1,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeGroupOrCondition = () => {
|
||||||
|
onChange(current => ({
|
||||||
|
...current,
|
||||||
|
settings: {
|
||||||
|
...current?.settings,
|
||||||
|
additionalGroupFilterCount: (current?.settings?.additionalGroupFilterCount ?? 1) - 1,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
$: columns = value?.columns;
|
$: columns = value?.columns;
|
||||||
$: tables = value?.tables;
|
$: tables = value?.tables;
|
||||||
|
$: settings = value?.settings;
|
||||||
$: hasGroupedColumn = !!(columns || []).find(x => x.isGrouped);
|
$: hasGroupedColumn = !!(columns || []).find(x => x.isGrouped);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -44,18 +95,49 @@
|
|||||||
<TableControl
|
<TableControl
|
||||||
rows={columns || []}
|
rows={columns || []}
|
||||||
columns={[
|
columns={[
|
||||||
{ fieldName: 'columnName', header: 'Column/Expression' },
|
{ fieldName: 'columnName', slot: 8, header: 'Column/Expression' },
|
||||||
{ fieldName: 'tableDisplayName', header: 'Table', formatter: row => getTableDisplayName(row, tables) },
|
{ fieldName: 'tableDisplayName', header: 'Table', formatter: row => getTableDisplayName(row, tables) },
|
||||||
{ fieldName: 'isOutput', header: 'Output', slot: 0 },
|
{ fieldName: 'isOutput', header: 'Output', slot: 0 },
|
||||||
{ fieldName: 'alias', header: 'Alias', slot: 1 },
|
{ fieldName: 'alias', header: 'Alias', slot: 1 },
|
||||||
{ fieldName: 'isGrouped', header: 'Group by', slot: 2 },
|
{ fieldName: 'isGrouped', header: 'Group by', slot: 2 },
|
||||||
{ fieldName: 'aggregate', header: 'Aggregate', slot: 3 },
|
{ fieldName: 'aggregate', header: 'Aggregate', slot: 3 },
|
||||||
{ fieldName: 'sortOrder', header: 'Sort order', slot: 4 },
|
{ fieldName: 'sortOrder', header: 'Sort order', slot: 4 },
|
||||||
{ fieldName: 'filter', header: 'Filter', slot: 5 },
|
{ fieldName: 'filter', header: 'Filter', slot: 5, props: { filterField: 'filter' } },
|
||||||
hasGroupedColumn && { fieldName: 'groupFilter', header: 'Group filter', slot: 6 },
|
..._.range(settings?.additionalFilterCount || 0).map(index => ({
|
||||||
|
fieldName: `additionalFilter${index + 1}`,
|
||||||
|
header: `OR Filter ${index + 2}`,
|
||||||
|
slot: 5,
|
||||||
|
props: { filterField: `additionalFilter${index + 1}` },
|
||||||
|
})),
|
||||||
|
hasGroupedColumn && {
|
||||||
|
fieldName: 'groupFilter',
|
||||||
|
header: 'Group filter',
|
||||||
|
slot: 5,
|
||||||
|
props: { filterField: 'groupFilter' },
|
||||||
|
},
|
||||||
|
..._.range(hasGroupedColumn ? settings?.additionalGroupFilterCount || 0 : 0).map(index => ({
|
||||||
|
fieldName: `additionalGroupFilter${index + 1}`,
|
||||||
|
header: `OR group filter ${index + 2}`,
|
||||||
|
slot: 5,
|
||||||
|
props: { filterField: `additionalGroupFilter${index + 1}` },
|
||||||
|
})),
|
||||||
{ fieldName: 'actions', header: '', slot: 7 },
|
{ fieldName: 'actions', header: '', slot: 7 },
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
<svelte:fragment slot="8" let:row>
|
||||||
|
{#if row.isCustomExpression}
|
||||||
|
<TextField
|
||||||
|
style="min-width:calc(100% - 9px)"
|
||||||
|
value={row.customExpression}
|
||||||
|
on:input={e => {
|
||||||
|
changeColumn({ ...row, customExpression: e.target.value });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{:else}
|
||||||
|
{row.columnName}
|
||||||
|
{/if}
|
||||||
|
</svelte:fragment>
|
||||||
|
|
||||||
<svelte:fragment slot="0" let:row>
|
<svelte:fragment slot="0" let:row>
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
checked={row.isOutput}
|
checked={row.isOutput}
|
||||||
@@ -67,6 +149,7 @@
|
|||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="1" let:row>
|
<svelte:fragment slot="1" let:row>
|
||||||
<TextField
|
<TextField
|
||||||
|
style="min-width:calc(100% - 9px)"
|
||||||
value={row.alias}
|
value={row.alias}
|
||||||
on:input={e => {
|
on:input={e => {
|
||||||
changeColumn({ ...row, alias: e.target.value });
|
changeColumn({ ...row, alias: e.target.value });
|
||||||
@@ -86,6 +169,7 @@
|
|||||||
{#if !row.isGrouped}
|
{#if !row.isGrouped}
|
||||||
<SelectField
|
<SelectField
|
||||||
isNative
|
isNative
|
||||||
|
style="min-width:calc(100% - 9px)"
|
||||||
value={row.aggregate}
|
value={row.aggregate}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
changeColumn({ ...row, aggregate: e.detail });
|
changeColumn({ ...row, aggregate: e.detail });
|
||||||
@@ -97,6 +181,7 @@
|
|||||||
<svelte:fragment slot="4" let:row>
|
<svelte:fragment slot="4" let:row>
|
||||||
<SelectField
|
<SelectField
|
||||||
isNative
|
isNative
|
||||||
|
style="min-width:calc(100% - 9px)"
|
||||||
value={row.sortOrder}
|
value={row.sortOrder}
|
||||||
on:change={e => {
|
on:change={e => {
|
||||||
changeColumn({ ...row, sortOrder: parseInt(e.detail) });
|
changeColumn({ ...row, sortOrder: parseInt(e.detail) });
|
||||||
@@ -112,21 +197,12 @@
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
<svelte:fragment slot="5" let:row>
|
<svelte:fragment slot="5" let:row let:filterField>
|
||||||
<DataFilterControl
|
<DataFilterControl
|
||||||
filterType={findDesignerFilterType(row, value)}
|
filterType={findDesignerFilterType(row, value)}
|
||||||
filter={row.filter}
|
filter={row[filterField]}
|
||||||
setFilter={filter => {
|
setFilter={filter => {
|
||||||
changeColumn({ ...row, filter });
|
changeColumn({ ...row, [filterField]: filter });
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</svelte:fragment>
|
|
||||||
<svelte:fragment slot="6" let:row>
|
|
||||||
<DataFilterControl
|
|
||||||
filterType={findDesignerFilterType(row, value)}
|
|
||||||
filter={row.groupFilter}
|
|
||||||
setFilter={groupFilter => {
|
|
||||||
changeColumn({ ...row, groupFilter });
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
@@ -134,6 +210,17 @@
|
|||||||
<InlineButton on:click={() => removeColumn(row)}>Remove</InlineButton>
|
<InlineButton on:click={() => removeColumn(row)}>Remove</InlineButton>
|
||||||
</svelte:fragment>
|
</svelte:fragment>
|
||||||
</TableControl>
|
</TableControl>
|
||||||
|
<FormStyledButton value="Add custom expression" on:click={addExpressionColumn} style="width:200px" />
|
||||||
|
<FormStyledButton value="Add OR condition" on:click={addOrCondition} style="width:200px" />
|
||||||
|
{#if settings?.additionalFilterCount > 0}
|
||||||
|
<FormStyledButton value="Remove OR condition" on:click={removeOrCondition} style="width:200px" />
|
||||||
|
{/if}
|
||||||
|
{#if hasGroupedColumn}
|
||||||
|
<FormStyledButton value="Add group OR condition" on:click={addGroupOrCondition} style="width:200px" />
|
||||||
|
{/if}
|
||||||
|
{#if hasGroupedColumn && settings?.additionalGroupFilterCount > 0}
|
||||||
|
<FormStyledButton value="Remove group OR condition" on:click={removeGroupOrCondition} style="width:200px" />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -141,4 +228,4 @@
|
|||||||
overflow: auto;
|
overflow: auto;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
header: string;
|
header: string;
|
||||||
component?: any;
|
component?: any;
|
||||||
getProps?: any;
|
getProps?: any;
|
||||||
|
props?: any;
|
||||||
formatter?: any;
|
formatter?: any;
|
||||||
slot?: number;
|
slot?: number;
|
||||||
isHighlighted?: Function;
|
isHighlighted?: Function;
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
export let clickable = false;
|
export let clickable = false;
|
||||||
export let disableFocusOutline = false;
|
export let disableFocusOutline = false;
|
||||||
export let emptyMessage = null;
|
export let emptyMessage = null;
|
||||||
|
export let noCellPadding = false;
|
||||||
|
|
||||||
export let domTable = undefined;
|
export let domTable = undefined;
|
||||||
|
|
||||||
@@ -77,21 +79,24 @@
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{#each columnList as col}
|
{#each columnList as col}
|
||||||
<td class:isHighlighted={col.isHighlighted && col.isHighlighted(row)}>
|
{@const rowProps = { ...col.props, ...(col.getProps ? col.getProps(row) : null) }}
|
||||||
|
<td class:isHighlighted={col.isHighlighted && col.isHighlighted(row)} class:noCellPadding>
|
||||||
{#if col.component}
|
{#if col.component}
|
||||||
<svelte:component this={col.component} {...col.getProps(row)} />
|
<svelte:component this={col.component} {...rowProps} />
|
||||||
{:else if col.formatter}
|
{:else if col.formatter}
|
||||||
{col.formatter(row)}
|
{col.formatter(row)}
|
||||||
{:else if col.slot != null}
|
{:else if col.slot != null}
|
||||||
{#if col.slot == -1}<slot name="-1" {row} {index} />
|
{#if col.slot == -1}<slot name="-1" {row} {index} />
|
||||||
{:else if col.slot == 0}<slot name="0" {row} {index} />
|
{:else if col.slot == 0}<slot name="0" {row} {index} {...rowProps} />
|
||||||
{:else if col.slot == 1}<slot name="1" {row} {index} />
|
{:else if col.slot == 1}<slot name="1" {row} {index} {...rowProps} />
|
||||||
{:else if col.slot == 2}<slot name="2" {row} {index} />
|
{:else if col.slot == 2}<slot name="2" {row} {index} {...rowProps} />
|
||||||
{:else if col.slot == 3}<slot name="3" {row} {index} />
|
{:else if col.slot == 3}<slot name="3" {row} {index} {...rowProps} />
|
||||||
{:else if col.slot == 4}<slot name="4" {row} {index} />
|
{:else if col.slot == 4}<slot name="4" {row} {index} {...rowProps} />
|
||||||
{:else if col.slot == 5}<slot name="5" {row} {index} />
|
{:else if col.slot == 5}<slot name="5" {row} {index} {...rowProps} />
|
||||||
{:else if col.slot == 6}<slot name="6" {row} {index} />
|
{:else if col.slot == 6}<slot name="6" {row} {index} {...rowProps} />
|
||||||
{:else if col.slot == 7}<slot name="7" {row} {index} />
|
{:else if col.slot == 7}<slot name="7" {row} {index} {...rowProps} />
|
||||||
|
{:else if col.slot == 8}<slot name="8" {row} {index} {...rowProps} />
|
||||||
|
{:else if col.slot == 9}<slot name="9" {row} {index} {...rowProps} />
|
||||||
{/if}
|
{/if}
|
||||||
{:else}
|
{:else}
|
||||||
{row[col.fieldName] || ''}
|
{row[col.fieldName] || ''}
|
||||||
@@ -136,6 +141,9 @@
|
|||||||
}
|
}
|
||||||
tbody td {
|
tbody td {
|
||||||
border: 1px solid var(--theme-border);
|
border: 1px solid var(--theme-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody td:not(.noCellPadding) {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,13 @@
|
|||||||
import 'ace-builds/src-noconflict/theme-tomorrow_night';
|
import 'ace-builds/src-noconflict/theme-tomorrow_night';
|
||||||
import 'ace-builds/src-noconflict/theme-twilight';
|
import 'ace-builds/src-noconflict/theme-twilight';
|
||||||
|
|
||||||
import { currentDropDownMenu, currentEditorFontSize, currentEditorTheme, currentThemeDefinition } from '../stores';
|
import {
|
||||||
|
currentDropDownMenu,
|
||||||
|
currentEditorFontSize,
|
||||||
|
currentEditorFont,
|
||||||
|
currentEditorTheme,
|
||||||
|
currentThemeDefinition,
|
||||||
|
} from '../stores';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { handleCommandKeyDown } from '../commands/CommandListener.svelte';
|
import { handleCommandKeyDown } from '../commands/CommandListener.svelte';
|
||||||
import resizeObserver from '../utility/resizeObserver';
|
import resizeObserver from '../utility/resizeObserver';
|
||||||
@@ -223,12 +229,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$: watchOptions(options);
|
$: watchOptions(options, $currentEditorFont);
|
||||||
function watchOptions(newOption: any) {
|
function watchOptions(newOption: any, fontFamily) {
|
||||||
if (editor) {
|
if (editor) {
|
||||||
editor.setOptions({
|
editor.setOptions({
|
||||||
...stdOptions,
|
...stdOptions,
|
||||||
...newOption,
|
...newOption,
|
||||||
|
fontFamily: fontFamily || 'Menlo, Monaco, Ubuntu Mono, Consolas, source-code-pro, monospace',
|
||||||
|
// fontFamily: 'tahoma,Menlo',
|
||||||
|
// fontSize: '10pt',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,6 +164,10 @@
|
|||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if driver?.showConnectionField('windowsDomain', $values)}
|
||||||
|
<FormTextField label="Domain (specify to use NTLM authentication)" name="windowsDomain" disabled={isConnected} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
{#if driver?.showConnectionField('isReadOnly', $values)}
|
{#if driver?.showConnectionField('isReadOnly', $values)}
|
||||||
<FormCheckboxField label="Is read only" name="isReadOnly" disabled={isConnected} />
|
<FormCheckboxField label="Is read only" name="isReadOnly" disabled={isConnected} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ ORDER BY
|
|||||||
<div class="heading">Editor theme</div>
|
<div class="heading">Editor theme</div>
|
||||||
|
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<div class="col-6">
|
<div class="col-4">
|
||||||
<FormFieldTemplateLarge label="Theme" type="combo">
|
<FormFieldTemplateLarge label="Theme" type="combo">
|
||||||
<SelectField
|
<SelectField
|
||||||
isNative
|
isNative
|
||||||
@@ -139,7 +139,7 @@ ORDER BY
|
|||||||
</FormFieldTemplateLarge>
|
</FormFieldTemplateLarge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-6">
|
<div class="col-4">
|
||||||
<FormFieldTemplateLarge label="Font size " type="combo">
|
<FormFieldTemplateLarge label="Font size " type="combo">
|
||||||
<SelectField
|
<SelectField
|
||||||
isNative
|
isNative
|
||||||
@@ -150,6 +150,10 @@ ORDER BY
|
|||||||
/>
|
/>
|
||||||
</FormFieldTemplateLarge>
|
</FormFieldTemplateLarge>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-4">
|
||||||
|
<FormTextField name="editor.fontFamily" label="Editor font family" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="editor">
|
<div class="editor">
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ export const currentEditorTheme = getElectron()
|
|||||||
export const currentEditorFontSize = getElectron()
|
export const currentEditorFontSize = getElectron()
|
||||||
? writableSettingsValue(null, 'currentEditorFontSize')
|
? writableSettingsValue(null, 'currentEditorFontSize')
|
||||||
: writableWithStorage(null, 'currentEditorFontSize');
|
: writableWithStorage(null, 'currentEditorFontSize');
|
||||||
|
export const currentEditorFont = writableSettingsValue(null, 'editor.fontFamily');
|
||||||
export const activeTabId = derived([openedTabs], ([$openedTabs]) => $openedTabs.find(x => x.selected)?.tabid);
|
export const activeTabId = derived([openedTabs], ([$openedTabs]) => $openedTabs.find(x => x.selected)?.tabid);
|
||||||
export const activeTab = derived([openedTabs], ([$openedTabs]) => $openedTabs.find(x => x.selected));
|
export const activeTab = derived([openedTabs], ([$openedTabs]) => $openedTabs.find(x => x.selected));
|
||||||
export const recentDatabases = writableWithStorage([], 'recentDatabases');
|
export const recentDatabases = writableWithStorage([], 'recentDatabases');
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ function extractTediousColumns(columns, addDriverNativeColumn = false) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function tediousConnect({ server, port, user, password, database, ssl, trustServerCertificate }) {
|
async function tediousConnect({ server, port, user, password, database, ssl, trustServerCertificate, windowsDomnain }) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const connectionOptions = {
|
const connectionOptions = {
|
||||||
encrypt: !!ssl,
|
encrypt: !!ssl,
|
||||||
@@ -43,10 +43,11 @@ async function tediousConnect({ server, port, user, password, database, ssl, tru
|
|||||||
server,
|
server,
|
||||||
|
|
||||||
authentication: {
|
authentication: {
|
||||||
type: 'default',
|
type: windowsDomnain ? 'ntlm' : 'default',
|
||||||
options: {
|
options: {
|
||||||
userName: user,
|
userName: user,
|
||||||
password: password,
|
password: password,
|
||||||
|
...(windowsDomnain ? { domain: windowsDomnain } : {}),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -127,13 +127,16 @@ const driver = {
|
|||||||
['authType', 'server', 'port', 'user', 'password', 'defaultDatabase', 'singleDatabase', 'isReadOnly'].includes(
|
['authType', 'server', 'port', 'user', 'password', 'defaultDatabase', 'singleDatabase', 'isReadOnly'].includes(
|
||||||
field
|
field
|
||||||
) ||
|
) ||
|
||||||
(field == 'trustServerCertificate' && values.authType != 'sql' && values.authType != 'sspi'),
|
(field == 'trustServerCertificate' && values.authType != 'sql' && values.authType != 'sspi') ||
|
||||||
|
(field == 'windowsDomain' && values.authType != 'sql' && values.authType != 'sspi'),
|
||||||
|
// (field == 'useDatabaseUrl' && values.authType != 'sql' && values.authType != 'sspi')
|
||||||
getQuerySplitterOptions: () => mssqlSplitterOptions,
|
getQuerySplitterOptions: () => mssqlSplitterOptions,
|
||||||
|
|
||||||
engine: 'mssql@dbgate-plugin-mssql',
|
engine: 'mssql@dbgate-plugin-mssql',
|
||||||
title: 'Microsoft SQL Server',
|
title: 'Microsoft SQL Server',
|
||||||
defaultPort: 1433,
|
defaultPort: 1433,
|
||||||
defaultAuthTypeName: 'tedious',
|
defaultAuthTypeName: 'tedious',
|
||||||
|
// databaseUrlPlaceholder: 'e.g. server=localhost&authentication.type=default&authentication.type.user=myuser&authentication.type.password=pwd&options.database=mydb',
|
||||||
|
|
||||||
getNewObjectTemplates() {
|
getNewObjectTemplates() {
|
||||||
return [
|
return [
|
||||||
|
|||||||
Reference in New Issue
Block a user