Compare commits

...

53 Commits

Author SHA1 Message Date
Jan Prochazka 8489c171f3 AD_ALLOWED_LOGINS support 2022-11-27 18:32:01 +01:00
Jan Prochazka 592865b16e configurable token lifetime 2022-11-27 11:06:33 +01:00
Jan Prochazka 012d3ec2e1 logout button from not logged page 2022-11-27 10:56:50 +01:00
Jan Prochazka d84adcca5d more robust oauth 2022-11-27 10:43:25 +01:00
Jan Prochazka b1ae7d53b9 forms login 2022-11-26 11:21:37 +01:00
Jan Prochazka 9a5287725b login WIP 2022-11-25 16:59:41 +01:00
Jan Prochazka 5ccd724166 support for acticve directory #261 2022-11-25 16:38:17 +01:00
Jan Prochazka 5e4c286427 ignore auth .env 2022-11-25 16:15:41 +01:00
Jan Prochazka 70413b954b login page 2022-11-25 13:36:18 +01:00
Jan Prochazka 07b2a3e923 oauth disabling API 2022-11-17 20:09:27 +01:00
Jan Prochazka 94a91d5fed better oauth handle 2022-11-17 19:55:01 +01:00
Jan Prochazka 576fc2062c fix 2022-11-17 19:26:39 +01:00
Jan Prochazka 37a8783751 oauth working, but cycling sometimes 2022-11-17 12:43:38 +01:00
Jan Prochazka f42d78b2fb oauth returns access token 2022-11-14 21:20:58 +01:00
Jan Prochazka 792fa75ccd v5.1.6-beta.7 2022-11-13 18:39:30 +01:00
Jan Prochazka cbd3f1bae9 fixed number format 2022-11-13 18:39:13 +01:00
Jan Prochazka cd92231769 v5.1.6-beta.6 2022-11-13 17:56:19 +01:00
Jan Prochazka ecad1ae01b imrpoved closing inactive sessions 2022-11-13 17:21:47 +01:00
Jan Prochazka dc576e6ced changed timeouts for connection cleanup 2022-11-13 11:59:57 +01:00
Jan Prochazka 6cca81f8f1 cleanup of not used sessions 2022-11-13 11:52:31 +01:00
Jan Prochazka a9f1f19696 wincert renamed 2022-11-12 20:17:21 +01:00
Jan Prochazka 390ddac75b v5.1.6-beta.5 2022-11-12 19:43:30 +01:00
Jan Prochazka e2e7c6f06b cert update 2022-11-12 19:43:20 +01:00
Jan Prochazka 3a3d0683d5 v5.1.6-beta.4 2022-11-12 18:49:08 +01:00
Jan Prochazka d5534dcf07 added win.publisherName 2022-11-12 18:48:30 +01:00
Jan Prochazka b0a86f9f4a v5.1.6-beta.3 2022-11-12 18:11:54 +01:00
Jan Prochazka b833a30148 wincert changed 2022-11-12 18:11:42 +01:00
Jan Prochazka d9c1bbaa39 v5.1.6-beta.2 2022-11-10 16:43:14 +01:00
Jan Prochazka 4b74dbbd68 fixed #416: Double click does not maximize window do on MacOS 2022-11-10 13:41:55 +01:00
Jan Prochazka 9bcc61551c #406 show/hide reult window 2022-11-10 12:35:25 +01:00
Jan Prochazka ed71ef312d #406 keyboard shortcut to show/hide sidebar 2022-11-10 11:36:31 +01:00
Jan Prochazka 4fa043b7e5 fix 2022-11-10 11:06:40 +01:00
Jan Prochazka 83725dd349 fixed loading perspective 2022-11-10 10:51:51 +01:00
Jan Prochazka 4e25b71b06 removed folder text field 2022-11-10 10:11:47 +01:00
Jan Prochazka 607ae7c872 #274 allow to create folder 2022-11-10 10:10:53 +01:00
Jan Prochazka 66ade5823f ts fix 2022-11-10 09:46:54 +01:00
Jan Prochazka ebfa0a1939 allow drop on group #274 2022-11-10 09:45:41 +01:00
Jan Prochazka 909591404f v5.1.6-beta.1 2022-11-07 07:43:09 +01:00
Jan Prochazka 7a5f2a70ad fixed/removed svelte warnings 2022-11-06 13:32:17 +01:00
Jan Prochazka d41b254058 import type refactor 2022-11-06 13:31:13 +01:00
Jan Prochazka 435d06ffb9 rollup config - ignore some svelte warnings 2022-11-06 13:29:58 +01:00
Jan Prochazka f4a4eb7f9e ts const fix 2022-11-06 13:29:00 +01:00
Jan Prochazka 9910bbead3 Merge pull request #411 from qlaffont/master
FEAT: Folders support
2022-11-05 10:45:10 +01:00
Jan Prochazka cb619a0fe0 drag & drop into/from connection folder 2022-11-05 10:36:41 +01:00
Quentin Laffont b0d61f974c feat(app): able to set a parent 2022-11-04 10:05:17 +01:00
Quentin Laffont 8c051ff5f7 Merge pull request #1 from dbgate/master
update
2022-11-04 08:08:05 +01:00
Jan Prochazka f713a4b183 import type refactor 2022-11-03 16:45:28 +01:00
Jan Prochazka 6c7e263f0e Merge branch 'master' of github.com:dbgate/dbgate 2022-11-03 16:45:05 +01:00
Jan Prochazka ec3bfb4fae Screen postcss fix 2022-11-03 16:41:31 +01:00
Jan Prochazka 712ec8e6ee Merge pull request #409 from notz/fix-mongodb-url-with-ssh
Fix connection to mongodb via database url if a ssh tunnel is used
2022-11-03 13:41:42 +01:00
Gernot Pansy 4da0b25f44 Fix connection to mongodb via database url if a ssh tunnel is used
* Replaces the the port with the tunnel port in the url
2022-11-03 11:01:24 +01:00
Quentin Laffont 8ed73195c5 chore(app): add node version 2022-11-03 09:13:54 +01:00
Quentin Laffont c69fcd5eff feat(connections): able to save parent Id 2022-11-03 08:35:49 +01:00
121 changed files with 1059 additions and 245 deletions
+4 -2
View File
@@ -51,8 +51,10 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
# WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
# WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
+4 -2
View File
@@ -57,8 +57,10 @@ jobs:
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }} # token for electron publish
WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
WIN_CSC_LINK: ${{ secrets.WINCERT_2025 }}
WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_2025_PASSWORD }}
# WIN_CSC_LINK: ${{ secrets.WINCERT_CERTIFICATE }}
# WIN_CSC_KEY_PASSWORD: ${{ secrets.WINCERT_PASSWORD }}
CSC_LINK: ${{ secrets.APPLECERT_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
+1
View File
@@ -0,0 +1 @@
16.14.2
+1
View File
@@ -69,6 +69,7 @@ module.exports = ({ editMenu }) => [
{ command: 'app.toggleDevTools', hideDisabled: true },
{ command: 'app.toggleFullScreen', hideDisabled: true },
{ command: 'app.minimize', hideDisabled: true },
{ command: 'toggle.sidebar' },
{ divider: true },
{ command: 'theme.changeTheme', hideDisabled: true },
{ command: 'settings.show' },
+2 -1
View File
@@ -1,6 +1,6 @@
{
"private": true,
"version": "5.1.5",
"version": "5.1.6-beta.7",
"name": "dbgate-all",
"workspaces": [
"packages/*",
@@ -16,6 +16,7 @@
"start:app:debug:ssh": "cd app && cross-env DEBUG=ssh yarn start",
"start:api:portal": "yarn workspace dbgate-api start:portal",
"start:api:singledb": "yarn workspace dbgate-api start:singledb",
"start:api:auth": "yarn workspace dbgate-api start:auth",
"start:web": "yarn workspace dbgate-web dev",
"start:sqltree": "yarn workspace dbgate-sqltree start",
"start:tools": "yarn workspace dbgate-tools start",
+1
View File
@@ -0,0 +1 @@
.env
+3
View File
@@ -17,6 +17,7 @@
"dbgate"
],
"dependencies": {
"activedirectory2": "^2.1.0",
"async-lock": "^1.2.4",
"axios": "^0.21.1",
"body-parser": "^1.19.0",
@@ -42,6 +43,7 @@
"is-electron": "^2.2.1",
"js-yaml": "^4.1.0",
"json-stable-stringify": "^1.0.1",
"jsonwebtoken": "^8.5.1",
"line-reader": "^0.4.0",
"lodash": "^4.17.21",
"ncp": "^2.0.0",
@@ -57,6 +59,7 @@
"start": "env-cmd node src/index.js --listen-api",
"start:portal": "env-cmd -f env/portal/.env node src/index.js --listen-api",
"start:singledb": "env-cmd -f env/singledb/.env node src/index.js --listen-api",
"start:auth": "env-cmd -f env/auth/.env node src/index.js --listen-api",
"start:filedb": "env-cmd node src/index.js /home/jena/test/chinook/Chinook.db --listen-api",
"start:singleconn": "env-cmd node src/index.js --server localhost --user root --port 3307 --engine mysql@dbgate-plugin-mysql --password test --listen-api",
"ts": "tsc",
+142
View File
@@ -0,0 +1,142 @@
const axios = require('axios');
const jwt = require('jsonwebtoken');
const getExpressPath = require('../utility/getExpressPath');
const uuidv1 = require('uuid/v1');
const { getLogins } = require('../utility/hasPermission');
const AD = require('activedirectory2').promiseWrapper;
const tokenSecret = uuidv1();
function shouldAuthorizeApi() {
const logins = getLogins();
return !!process.env.OAUTH_AUTH || !!process.env.AD_URL || (!!logins && !process.env.BASIC_AUTH);
}
function getTokenLifetime() {
return process.env.TOKEN_LIFETIME || '1d';
}
function unauthorizedResponse(req, res, text) {
// if (req.path == getExpressPath('/config/get-settings')) {
// return res.json({});
// }
// if (req.path == getExpressPath('/connections/list')) {
// return res.json([]);
// }
return res.sendStatus(401).send(text);
}
function authMiddleware(req, res, next) {
const SKIP_AUTH_PATHS = ['/config/get', '/auth/oauth-token', '/auth/login', '/stream'];
if (!shouldAuthorizeApi()) {
return next();
}
let skipAuth = !!SKIP_AUTH_PATHS.find(x => req.path == getExpressPath(x));
const authHeader = req.headers.authorization;
if (!authHeader) {
if (skipAuth) {
return next();
}
return unauthorizedResponse(req, res, 'missing authorization header');
}
const token = authHeader.split(' ')[1];
try {
const decoded = jwt.verify(token, tokenSecret);
req.user = decoded;
return next();
} catch (err) {
if (skipAuth) {
return next();
}
console.log('Sending invalid token error', err.message);
return unauthorizedResponse(req, res, 'invalid token');
}
}
module.exports = {
oauthToken_meta: true,
async oauthToken(params) {
const { redirectUri, code } = params;
const resp = await axios.default.post(
`${process.env.OAUTH_TOKEN}`,
`grant_type=authorization_code&code=${encodeURIComponent(code)}&redirect_uri=${encodeURIComponent(
redirectUri
)}&client_id=${process.env.OAUTH_CLIENT_ID}&client_secret=${process.env.OAUTH_CLIENT_SECRET}`
);
const { access_token, refresh_token } = resp.data;
const payload = jwt.decode(access_token);
console.log('User payload returned from OAUTH:', payload);
const login = process.env.OAUTH_LOGIN_FIELD ? payload[process.env.OAUTH_LOGIN_FIELD] : 'oauth';
if (
process.env.OAUTH_ALLOWED_LOGINS &&
!process.env.OAUTH_ALLOWED_LOGINS.split(',').find(x => x.toLowerCase().trim() == login.toLowerCase().trim())
) {
return { error: `Username ${login} not allowed to log in` };
}
if (access_token) {
return {
accessToken: jwt.sign({ login }, tokenSecret, { expiresIn: getTokenLifetime() }),
};
}
return { error: 'Token not found' };
},
login_meta: true,
async login(params) {
const { login, password } = params;
if (process.env.AD_URL) {
const adConfig = {
url: process.env.AD_URL,
baseDN: process.env.AD_BASEDN,
username: process.env.AD_USERNAME,
password: process.env.AD_PASSOWRD,
};
const ad = new AD(adConfig);
try {
const res = await ad.authenticate(login, password);
if (!res) {
return { error: 'Login failed' };
}
if (
process.env.AD_ALLOWED_LOGINS &&
!process.env.AD_ALLOWED_LOGINS.split(',').find(x => x.toLowerCase().trim() == login.toLowerCase().trim())
) {
return { error: `Username ${login} not allowed to log in` };
}
return {
accessToken: jwt.sign({ login }, tokenSecret, { expiresIn: getTokenLifetime() }),
};
} catch (err) {
console.log('Failed active directory authentization', err.message);
return {
error: err.message,
};
}
}
const logins = getLogins();
if (!logins) {
return { error: 'Logins not configured' };
}
if (logins.find(x => x.login == login)?.password == password) {
return {
accessToken: jwt.sign({ login }, tokenSecret, { expiresIn: getTokenLifetime() }),
};
}
return { error: 'Invalid credentials' };
},
authMiddleware,
shouldAuthorizeApi,
};
+4 -1
View File
@@ -28,7 +28,7 @@ module.exports = {
get_meta: true,
async get(_params, req) {
const logins = getLogins();
const login = logins ? logins.find(x => x.login == (req.auth && req.auth.user)) : null;
const login = req.user ? req.user.login : logins ? logins.find(x => x.login == (req.auth && req.auth.user)) : null;
const permissions = login ? login.permissions : process.env.PERMISSIONS;
return {
@@ -40,6 +40,9 @@ module.exports = {
isDocker: platformInfo.isDocker,
permissions,
login,
oauth: process.env.OAUTH_AUTH,
oauthLogout: process.env.OAUTH_LOGOUT,
isLoginForm: !!process.env.AD_URL || (!!logins && !process.env.BASIC_AUTH),
...currentVersion,
};
},
@@ -62,6 +62,7 @@ function getPortalCollections() {
displayName: process.env[`LABEL_${id}`],
isReadOnly: process.env[`READONLY_${id}`],
databases: process.env[`DBCONFIG_${id}`] ? safeJsonParse(process.env[`DBCONFIG_${id}`]) : null,
parent: process.env[`PARENT_${id}`] || undefined,
// SSH tunnel
useSshTunnel: process.env[`USE_SSH_${id}`],
+17
View File
@@ -103,6 +103,12 @@ module.exports = {
if (handleProcessCommunication(message, subprocess)) return;
this[`handle_${msgtype}`](sesid, message);
});
subprocess.on('exit', () => {
this.opened = this.opened.filter(x => x.sesid != sesid);
this.dispatchMessage(sesid, 'Query session closed');
socket.emit(`session-closed-${sesid}`);
});
subprocess.send({ msgtype: 'connect', ...connection, database });
return _.pick(newOpened, ['conid', 'database', 'sesid']);
},
@@ -165,6 +171,17 @@ module.exports = {
return { state: 'ok' };
},
ping_meta: true,
async ping({ sesid }) {
const session = this.opened.find(x => x.sesid == sesid);
if (!session) {
throw new Error('Invalid session');
}
session.subprocess.send({ msgtype: 'ping' });
return { state: 'ok' };
},
// runCommand_meta: true,
// async runCommand({ conid, database, sql }) {
// console.log(`Running SQL command , conid=${conid}, database=${database}, sql=${sql}`);
+7 -1
View File
@@ -20,6 +20,7 @@ const jsldata = require('./controllers/jsldata');
const config = require('./controllers/config');
const archive = require('./controllers/archive');
const apps = require('./controllers/apps');
const auth = require('./controllers/auth');
const uploads = require('./controllers/uploads');
const plugins = require('./controllers/plugins');
const files = require('./controllers/files');
@@ -41,7 +42,7 @@ function start() {
const server = http.createServer(app);
const logins = getLogins();
if (logins) {
if (logins && process.env.BASIC_AUTH) {
app.use(
basicAuth({
users: _.fromPairs(logins.map(x => [x.login, x.password])),
@@ -53,6 +54,10 @@ function start() {
app.use(cors());
if (auth.shouldAuthorizeApi()) {
app.use(auth.authMiddleware);
}
app.get(getExpressPath('/stream'), async function (req, res) {
res.set({
'Cache-Control': 'no-cache',
@@ -157,6 +162,7 @@ function useAllControllers(app, electron) {
useController(app, electron, '/scheduler', scheduler);
useController(app, electron, '/query-history', queryHistory);
useController(app, electron, '/apps', apps);
useController(app, electron, '/auth', auth);
}
function setElectronSender(electronSender) {
@@ -335,11 +335,11 @@ function start() {
setInterval(() => {
const time = new Date().getTime();
if (time - lastPing > 120 * 1000) {
if (time - lastPing > 40 * 1000) {
console.log('Database connection not alive, exiting');
process.exit(0);
}
}, 60 * 1000);
}, 10 * 1000);
process.on('message', async message => {
if (handleProcessCommunication(message)) return;
@@ -111,11 +111,11 @@ function start() {
setInterval(() => {
const time = new Date().getTime();
if (time - lastPing > 120 * 1000) {
if (time - lastPing > 40 * 1000) {
console.log('Server connection not alive, exiting');
process.exit(0);
}
}, 60 * 1000);
}, 10 * 1000);
process.on('message', async message => {
if (handleProcessCommunication(message)) return;
+17
View File
@@ -15,6 +15,7 @@ let systemConnection;
let storedConnection;
let afterConnectCallbacks = [];
// let currentHandlers = [];
let lastPing = null;
class TableWriter {
constructor() {
@@ -271,10 +272,15 @@ async function handleExecuteReader({ jslid, sql, fileName }) {
});
}
function handlePing() {
lastPing = new Date().getTime();
}
const messageHandlers = {
connect: handleConnect,
executeQuery: handleExecuteQuery,
executeReader: handleExecuteReader,
ping: handlePing,
// cancel: handleCancel,
};
@@ -285,6 +291,17 @@ async function handleMessage({ msgtype, ...other }) {
function start() {
childProcessChecker();
lastPing = new Date().getTime();
setInterval(() => {
const time = new Date().getTime();
if (time - lastPing > 25 * 1000) {
console.log('Session not alive, exiting');
process.exit(0);
}
}, 10 * 1000);
process.on('message', async message => {
if (handleProcessCommunication(message)) return;
try {
+1 -1
View File
@@ -9,7 +9,7 @@ import {
AllowIdentityInsert,
Expression,
} from 'dbgate-sqltree';
import { NamedObjectInfo, DatabaseInfo } from 'dbgate-types';
import type { NamedObjectInfo, DatabaseInfo } from 'dbgate-types';
export interface ChangeSetItem {
pureName: string;
@@ -1,6 +1,6 @@
import _ from 'lodash';
import { GridDisplay, ChangeCacheFunc, ChangeConfigFunc, DisplayColumn } from './GridDisplay';
import { EngineDriver, ViewInfo, ColumnInfo, CollectionInfo } from 'dbgate-types';
import type { EngineDriver, ViewInfo, ColumnInfo, CollectionInfo } from 'dbgate-types';
import { GridConfig, GridCache } from './GridConfig';
function getObjectKeys(obj) {
+1 -1
View File
@@ -1,6 +1,6 @@
import _ from 'lodash';
import { GridConfig, GridCache, GridConfigColumns, createGridCache, GroupFunc } from './GridConfig';
import { TableInfo, EngineDriver, DatabaseInfo, SqlDialect } from 'dbgate-types';
import type { TableInfo, EngineDriver, DatabaseInfo, SqlDialect } from 'dbgate-types';
import { getFilterValueExpression } from 'dbgate-filterparser';
import { ChangeCacheFunc, ChangeConfigFunc, DisplayColumn } from './GridDisplay';
+1 -1
View File
@@ -1,5 +1,5 @@
import _ from 'lodash';
import { EngineDriver, ViewInfo, ColumnInfo } from 'dbgate-types';
import type { EngineDriver, ViewInfo, ColumnInfo } from 'dbgate-types';
import { GridDisplay, ChangeCacheFunc, ChangeConfigFunc } from './GridDisplay';
import { GridConfig, GridCache } from './GridConfig';
import { FreeTableModel } from './FreeTableModel';
+1 -1
View File
@@ -1,4 +1,4 @@
import { TableInfo } from 'dbgate-types';
import type { TableInfo } from 'dbgate-types';
export interface FreeTableModel {
structure: TableInfo;
-3
View File
@@ -1,6 +1,3 @@
import { DisplayColumn } from './GridDisplay';
import { TableInfo } from 'dbgate-types';
export interface GridConfigColumns {
hiddenColumns: string[];
expandedColumns: string[];
+1 -1
View File
@@ -1,6 +1,6 @@
import _ from 'lodash';
import { GridConfig, GridCache, GridConfigColumns, createGridCache, GroupFunc, createGridConfig } from './GridConfig';
import {
import type {
ForeignKeyInfo,
TableInfo,
ColumnInfo,
-1
View File
@@ -1,6 +1,5 @@
import _ from 'lodash';
import { GridDisplay, ChangeCacheFunc, ChangeConfigFunc } from './GridDisplay';
import { QueryResultColumn } from 'dbgate-types';
import { GridConfig, GridCache } from './GridConfig';
import { analyseCollectionDisplayColumns } from './CollectionGridDisplay';
-1
View File
@@ -1,4 +1,3 @@
import { RangeDefinition } from 'dbgate-types';
import { PerspectiveDataLoadProps } from './PerspectiveDataProvider';
import _pick from 'lodash/pick';
import _zip from 'lodash/zip';
+1 -1
View File
@@ -1,4 +1,4 @@
import { DatabaseInfo, ForeignKeyInfo, NamedObjectInfo, TableInfo } from 'dbgate-types';
import type { DatabaseInfo, ForeignKeyInfo, NamedObjectInfo, TableInfo } from 'dbgate-types';
import uuidv1 from 'uuid/v1';
// export interface PerspectiveConfigColumns {
@@ -1,6 +1,6 @@
import debug from 'debug';
import { Condition } from 'dbgate-sqltree';
import { RangeDefinition } from 'dbgate-types';
import type { RangeDefinition } from 'dbgate-types';
import { PerspectiveBindingGroup, PerspectiveCache } from './PerspectiveCache';
import { PerspectiveDataLoader } from './PerspectiveDataLoader';
import { PerspectiveDataPatternDict } from './PerspectiveDataPattern';
+1 -1
View File
@@ -1,4 +1,4 @@
import {
import type {
CollectionInfo,
ColumnInfo,
DatabaseInfo,
+1 -1
View File
@@ -1,7 +1,7 @@
import { FormViewDisplay } from './FormViewDisplay';
import _ from 'lodash';
import { ChangeCacheFunc, DisplayColumn, ChangeConfigFunc } from './GridDisplay';
import { EngineDriver, NamedObjectInfo, DatabaseInfo } from 'dbgate-types';
import type { EngineDriver, NamedObjectInfo, DatabaseInfo } from 'dbgate-types';
import { GridConfig, GridCache } from './GridConfig';
import { mergeConditions, Condition, OrderByExpression } from 'dbgate-sqltree';
import { TableGridDisplay } from './TableGridDisplay';
+1 -1
View File
@@ -1,7 +1,7 @@
import _ from 'lodash';
import { filterName, isTableColumnUnique } from 'dbgate-tools';
import { GridDisplay, ChangeCacheFunc, DisplayColumn, DisplayedColumnInfo, ChangeConfigFunc } from './GridDisplay';
import {
import type {
TableInfo,
EngineDriver,
ViewInfo,
+1 -1
View File
@@ -1,6 +1,6 @@
import _ from 'lodash';
import { GridDisplay, ChangeCacheFunc, ChangeConfigFunc } from './GridDisplay';
import { EngineDriver, ViewInfo, ColumnInfo } from 'dbgate-types';
import type { EngineDriver, ViewInfo, ColumnInfo } from 'dbgate-types';
import { GridConfig, GridCache } from './GridConfig';
export class ViewGridDisplay extends GridDisplay {
+1 -1
View File
@@ -1,6 +1,6 @@
import _ from 'lodash';
import { Command, Insert, Update, Delete, UpdateField, Condition, AllowIdentityInsert } from 'dbgate-sqltree';
import { NamedObjectInfo, DatabaseInfo, ForeignKeyInfo, TableInfo } from 'dbgate-types';
import type { NamedObjectInfo, DatabaseInfo, ForeignKeyInfo, TableInfo } from 'dbgate-types';
import { ChangeSet, ChangeSetItem, extractChangeSetCondition } from './ChangeSet';
export interface ChangeSetDeleteCascade {
+1
View File
@@ -20,3 +20,4 @@ export * from './PerspectiveCache';
export * from './PerspectiveConfig';
export * from './processPerspectiveDefaultColunns';
export * from './PerspectiveDataPattern';
export * from './PerspectiveDataLoader';
@@ -1,5 +1,5 @@
import { findForeignKeyForColumn } from 'dbgate-tools';
import { DatabaseInfo, TableInfo, ViewInfo } from 'dbgate-types';
import type { DatabaseInfo, TableInfo, ViewInfo } from 'dbgate-types';
import { createPerspectiveNodeConfig, MultipleDatabaseInfo, PerspectiveConfig } from './PerspectiveConfig';
import { PerspectiveDataPattern, PerspectiveDataPatternDict } from './PerspectiveDataPattern';
import { PerspectiveTableNode } from './PerspectiveTreeNode';
+1 -1
View File
@@ -1,4 +1,4 @@
import { DatabaseInfo } from 'dbgate-types';
import type { DatabaseInfo } from 'dbgate-types';
export const chinookDbInfo: DatabaseInfo = {
tables: [
+1 -1
View File
@@ -2,7 +2,7 @@ import P from 'parsimmon';
import moment from 'moment';
import { FilterType } from './types';
import { Condition } from 'dbgate-sqltree';
import { TransformType } from 'dbgate-types';
import type { TransformType } from 'dbgate-types';
import { interpretEscapes, token, word, whitespace } from './common';
const compoudCondition = conditionType => conditions => {
-1
View File
@@ -2,7 +2,6 @@ import P from 'parsimmon';
import moment from 'moment';
import { FilterType } from './types';
import { Condition } from 'dbgate-sqltree';
import { TransformType } from 'dbgate-types';
import { interpretEscapes, token, word, whitespace } from './common';
import { mongoParser } from './mongoParser';
import { datetimeParser } from './datetimeParser';
+1 -1
View File
@@ -1,4 +1,4 @@
import { SqlDumper } from 'dbgate-types';
import type { SqlDumper } from 'dbgate-types';
import { Command, Select, Update, Delete, Insert } from './types';
import { dumpSqlExpression } from './dumpSqlExpression';
import { dumpSqlFromDefinition, dumpSqlSourceRef } from './dumpSqlSource';
+1 -1
View File
@@ -1,4 +1,4 @@
import { SqlDumper } from 'dbgate-types';
import type { SqlDumper } from 'dbgate-types';
import { Condition, BinaryCondition } from './types';
import { dumpSqlExpression } from './dumpSqlExpression';
import { dumpSqlSelect } from './dumpSqlCommand';
+1 -1
View File
@@ -1,5 +1,5 @@
import _ from 'lodash';
import { SqlDumper } from 'dbgate-types';
import type { SqlDumper } from 'dbgate-types';
import { Expression, ColumnRefExpression } from './types';
import { dumpSqlSourceRef } from './dumpSqlSource';
+1 -1
View File
@@ -1,5 +1,5 @@
import { Source, FromDefinition, Relation } from './types';
import { SqlDumper } from 'dbgate-types';
import type { SqlDumper } from 'dbgate-types';
import { dumpSqlSelect } from './dumpSqlCommand';
import { dumpSqlCondition } from './dumpSqlCondition';
@@ -1,10 +1,6 @@
import { SqlDumper } from 'dbgate-types';
import _ from 'lodash';
import { Condition, BinaryCondition } from './types';
import { dumpSqlExpression } from './dumpSqlExpression';
import { link } from 'fs';
import { evaluateExpression } from './evaluateExpression';
import { cond } from 'lodash';
function isEmpty(value) {
if (value == null) return true;
+1 -1
View File
@@ -1,4 +1,4 @@
import { NamedObjectInfo, RangeDefinition, TransformType } from 'dbgate-types';
import type { NamedObjectInfo, RangeDefinition, TransformType } from 'dbgate-types';
// export interface Command {
// }
+1 -1
View File
@@ -1,4 +1,4 @@
import { EngineDriver, SqlDumper } from 'dbgate-types';
import type { EngineDriver, SqlDumper } from 'dbgate-types';
import { Command, Condition } from './types';
import { dumpSqlCommand } from './dumpSqlCommand';
+1 -1
View File
@@ -1,5 +1,5 @@
import _ from 'lodash';
import {
import type {
ColumnInfo,
ConstraintInfo,
EngineDriver,
+1 -1
View File
@@ -1,4 +1,4 @@
import {
import type {
DatabaseInfo,
EngineDriver,
FunctionInfo,
+1 -1
View File
@@ -1,5 +1,5 @@
import { DbDiffOptions, testEqualColumns, testEqualTables, testEqualSqlObjects } from './diffTools';
import { DatabaseInfo, EngineDriver, SqlObjectInfo, TableInfo } from 'dbgate-types';
import type { DatabaseInfo, EngineDriver, SqlObjectInfo, TableInfo } from 'dbgate-types';
import _ from 'lodash';
export function computeDiffRowsCore(sourceList, targetList, testEqual) {
@@ -1,4 +1,3 @@
import { EngineDriver } from 'dbgate-types';
import _intersection from 'lodash/intersection';
import { prepareTableForImport } from './tableTransforms';
+1 -1
View File
@@ -1,4 +1,4 @@
import {
import type {
ColumnInfo,
ConstraintInfo,
DatabaseInfo,
+1 -1
View File
@@ -1,6 +1,6 @@
import _cloneDeep from 'lodash/cloneDeep';
import _isString from 'lodash/isString';
import {
import type {
ColumnInfo,
ColumnReference,
DatabaseInfo,
+1 -1
View File
@@ -1,4 +1,4 @@
import { EngineDriver, ExtensionsDirectory } from 'dbgate-types';
import type { EngineDriver, ExtensionsDirectory } from 'dbgate-types';
import _camelCase from 'lodash/camelCase';
import _isString from 'lodash/isString';
import _isPlainObject from 'lodash/isPlainObject';
+1 -1
View File
@@ -1,5 +1,5 @@
import _ from 'lodash';
import { DatabaseInfo, EngineDriver } from 'dbgate-types';
import type { DatabaseInfo, EngineDriver } from 'dbgate-types';
export async function enrichWithPreloadedRows(
dbModel: DatabaseInfo,
+1 -1
View File
@@ -1,6 +1,6 @@
import uuidv1 from 'uuid/v1';
import _omit from 'lodash/omit';
import {
import type {
ColumnInfo,
ConstraintInfo,
ForeignKeyInfo,
+1 -1
View File
@@ -1,4 +1,4 @@
import { DatabaseInfo, TableInfo, ApplicationDefinition, ViewInfo, CollectionInfo } from 'dbgate-types';
import type { DatabaseInfo, TableInfo, ApplicationDefinition, ViewInfo, CollectionInfo } from 'dbgate-types';
import _flatten from 'lodash/flatten';
export function addTableDependencies(db: DatabaseInfo): DatabaseInfo {
+1 -1
View File
@@ -1,4 +1,4 @@
import { TableInfo } from 'dbgate-types';
import type { TableInfo } from 'dbgate-types';
import _cloneDeep from 'lodash/cloneDeep';
import _fromPairs from 'lodash/fromPairs';
import _get from 'lodash/get';
+15 -1
View File
@@ -45,7 +45,7 @@ export default [
resolve({
browser: true,
}),
// If we're building for production (npm run build
// instead of npm run dev), minify
production && terser(),
@@ -88,6 +88,20 @@ export default [
// enable run-time checks when not in production
dev: !production,
},
onwarn: (warning, handler) => {
const ignoreWarnings = [
'a11y-click-events-have-key-events',
'a11y-missing-attribute',
'a11y-invalid-attribute',
'a11y-no-noninteractive-tabindex',
'a11y-label-has-associated-control',
'vite-plugin-svelte-css-no-scopable-elements',
'unused-export-let',
];
if (ignoreWarnings.includes(warning.code)) return;
// console.log('***************************', warning.code);
handler(warning);
},
}),
// we'll extract any component CSS out into
// a separate file - better for performance
+4 -1
View File
@@ -20,6 +20,7 @@
import getElectron from './utility/getElectron';
import AppStartInfo from './widgets/AppStartInfo.svelte';
import SettingsListener from './utility/SettingsListener.svelte';
import { handleAuthOnStartup, handleOauthCallback } from './clientAuth';
let loadedApi = false;
let loadedPlugins = false;
@@ -33,9 +34,11 @@
try {
// console.log('************** LOADING API');
const config = await getConfig();
await handleAuthOnStartup(config);
const connections = await apiCall('connections/list');
const settings = await getSettings();
const config = await getConfig();
const apps = await getUsedApps();
loadedApi = settings && connections && config && apps;
+115
View File
@@ -0,0 +1,115 @@
<script lang="ts">
import { onMount } from 'svelte';
import { internalRedirectTo } from './clientAuth';
import FormButton from './forms/FormButton.svelte';
import FormPasswordField from './forms/FormPasswordField.svelte';
import FormProvider from './forms/FormProvider.svelte';
import FormSubmit from './forms/FormSubmit.svelte';
import FormTextField from './forms/FormTextField.svelte';
import { apiCall, enableApi } from './utility/api';
onMount(() => {
const removed = document.getElementById('starting_dbgate_zero');
if (removed) removed.remove();
});
</script>
<div class="root theme-light theme-type-light">
<div class="text">DbGate</div>
<div class="wrap">
<div class="logo">
<img class="img" src="logo192.png" />
</div>
<div class="box">
<div class="heading">Log In</div>
<FormProvider>
<FormTextField label="Username" name="login" autocomplete="username" />
<FormPasswordField label="Password" name="password" autocomplete="current-password" />
<div class="submit">
<FormSubmit
value="Log In"
on:click={async e => {
enableApi();
const resp = await apiCall('auth/login', e.detail);
if (resp.error) {
internalRedirectTo(`/?page=not-logged&error=${encodeURIComponent(resp.error)}`);
return;
}
const { accessToken } = resp;
if (accessToken) {
localStorage.setItem('accessToken', accessToken);
internalRedirectTo('/');
return;
}
internalRedirectTo(`/?page=not-logged`);
}}
/>
</div>
</FormProvider>
</div>
</div>
</div>
<style>
.logo {
display: flex;
margin-bottom: 1rem;
align-items: center;
justify-content: center;
}
.img {
width: 80px;
}
.text {
position: fixed;
top: 1rem;
left: 1rem;
font-size: 30pt;
font-family: monospace;
color: var(--theme-bg-2);
text-transform: uppercase;
}
.submit {
margin: var(--dim-large-form-margin);
display: flex;
}
.submit :global(input) {
flex: 1;
font-size: larger;
}
.root {
color: var(--theme-font-1);
display: flex;
justify-content: center;
background-color: var(--theme-bg-1);
align-items: baseline;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.box {
width: 600px;
max-width: 80vw;
/* max-width: 600px;
width: 40vw; */
border: 1px solid var(--theme-border);
border-radius: 4px;
background-color: var(--theme-bg-0);
}
.wrap {
margin-top: 20vh;
}
.heading {
text-align: center;
margin: 1em;
font-size: xx-large;
}
</style>
+52
View File
@@ -0,0 +1,52 @@
<script lang="ts">
import { onMount } from 'svelte';
import FormStyledButton from './buttons/FormStyledButton.svelte';
import { doLogout, redirectToLogin } from './clientAuth';
onMount(() => {
const removed = document.getElementById('starting_dbgate_zero');
if (removed) removed.remove();
});
const params = new URLSearchParams(location.search);
const error = params.get('error');
function handleLogin() {
redirectToLogin(undefined, true);
}
</script>
<div class="root theme-light theme-type-light">
<div class="title">Sorry, you are not authorized to run DbGate</div>
{#if error}
<div class="error">{error}</div>
{/if}
<div class="button">
<FormStyledButton value="Log In" on:click={handleLogin} />
<FormStyledButton value="Log Out" on:click={doLogout} />
</div>
</div>
<style>
.root {
color: var(--theme-font-1);
}
.title {
font-size: x-large;
margin-top: 20vh;
text-align: center;
}
.error {
margin-top: 1em;
text-align: center;
}
.button {
display: flex;
justify-content: center;
margin-top: 1em;
}
</style>
+4 -3
View File
@@ -8,6 +8,7 @@
leftPanelWidth,
openedSnackbars,
selectedWidget,
visibleWidgetSideBar,
visibleCommandPalette,
visibleTitleBar,
visibleToolbar,
@@ -29,7 +30,7 @@
$: currentThemeType = $currentThemeDefinition?.themeType == 'dark' ? 'theme-type-dark' : 'theme-type-light';
$: themeStyle = `<style id="themePlugin">${$currentThemeDefinition?.themeCss}</style>`;
$: themeStyle = `<st` + `yle id="themePlugin">${$currentThemeDefinition?.themeCss}</st` + `yle>`;
const isElectron = !!getElectron();
</script>
@@ -63,7 +64,7 @@
<div class="statusbar">
<StatusBar />
</div>
{#if $selectedWidget}
{#if $selectedWidget && $visibleWidgetSideBar}
<div class="leftpanel">
<WidgetContainer />
</div>
@@ -74,7 +75,7 @@
<div class="content">
<TabRegister />
</div>
{#if $selectedWidget}
{#if $selectedWidget && $visibleWidgetSideBar}
<div
class="horizontal-split-handle splitter"
use:splitterDrag={'clientX'}
@@ -16,7 +16,7 @@
tabComponent,
tooltip,
props: {
savedFile:fileName + '.' + fileType,
savedFile: fileName + '.' + fileType,
savedFolder: 'app:' + folderName,
savedFormat: 'text',
appFolder: folderName,
@@ -28,7 +28,10 @@
}
export const extractKey = data => data.fileName;
export const createMatcher = ({ fileName }) => filter => filterName(filter, fileName);
export const createMatcher =
({ fileName }) =>
filter =>
filterName(filter, fileName);
const APP_ICONS = {
'config.json': 'img json',
'command.sql': 'img app-command',
@@ -50,7 +53,6 @@
import InputTextModal from '../modals/InputTextModal.svelte';
import ConfirmModal from '../modals/ConfirmModal.svelte';
import { apiCall } from '../utility/api';
import { currentDatabase, currentDatabase } from '../stores';
export let data;
+26 -14
View File
@@ -10,10 +10,12 @@
export let group;
export let groupFunc;
export let items;
export let groupIconFunc = plusExpandIcon;
export let module;
export let checkedObjectsStore = null;
export let disableContextMenu = false;
export let passProps;
export let onDropOnGroup = undefined;
let isExpanded = true;
@@ -33,11 +35,19 @@
return res;
});
}
function handleDrop(e) {
var data = e.dataTransfer.getData('app_object_drag_data');
if (data && onDropOnGroup) {
e.stopPropagation();
onDropOnGroup(data, group);
}
}
</script>
<div class="group" on:click={() => (isExpanded = !isExpanded)}>
<div class="group" on:click={() => (isExpanded = !isExpanded)} on:drop={handleDrop}>
<span class="expand-icon">
<FontIcon icon={plusExpandIcon(isExpanded)} />
<FontIcon icon={groupIconFunc(isExpanded)} />
</span>
{group}
@@ -53,18 +63,20 @@
</div>
{/if}
{#each items as item}
<AppObjectListItem
isHidden={!item.isMatched}
{...$$restProps}
{module}
data={item.data}
{checkedObjectsStore}
on:objectClick
{disableContextMenu}
{passProps}
/>
{/each}
<div on:drop={handleDrop}>
{#each items as item}
<AppObjectListItem
isHidden={!item.isMatched}
{...$$restProps}
{module}
data={item.data}
{checkedObjectsStore}
on:objectClick
{disableContextMenu}
{passProps}
/>
{/each}
</div>
{/if}
<style>
+21 -3
View File
@@ -1,7 +1,8 @@
<script>
import _ from 'lodash';
import _, { sortBy } from 'lodash';
import { asyncFilter } from '../utility/common';
import AppObjectGroup from './AppObjectGroup.svelte';
import { plusExpandIcon } from '../icons/expandIcons';
import AppObjectListItem from './AppObjectListItem.svelte';
@@ -17,8 +18,12 @@
export let passProps;
export let getIsExpanded = null;
export let setIsExpanded = null;
export let sortGroups = false;
export let groupIconFunc = plusExpandIcon;
export let groupFunc = undefined;
export let onDropOnGroup = undefined;
export let emptyGroupNames = [];
$: filtered = !groupFunc
? list.filter(data => {
@@ -61,16 +66,28 @@
)
: null;
$: groups = groupFunc ? _.groupBy(listGrouped, 'group') : null;
function extendGroups(base, emptyList) {
const res = {
...base,
};
for (const item of emptyList) {
if (res[item]) continue;
res[item] = [];
}
return res;
}
$: groups = groupFunc ? extendGroups(_.groupBy(listGrouped, 'group'), emptyGroupNames) : null;
</script>
{#if groupFunc}
{#each _.keys(groups) as group}
{#each sortGroups ? _.sortBy(_.keys(groups)) : _.keys(groups) as group}
<AppObjectGroup
{group}
{module}
items={groups[group]}
{expandIconFunc}
{groupIconFunc}
{isExpandable}
{subItemsComponent}
{checkedObjectsStore}
@@ -80,6 +97,7 @@
{passProps}
{getIsExpanded}
{setIsExpanded}
{onDropOnGroup}
/>
{/each}
{:else}
@@ -180,6 +180,7 @@
});
currentArchive.set(resp.archiveFolder);
selectedWidget.set('archive');
visibleWidgetSideBar.set(true);
showSnackbarSuccess(`Saved to archive ${resp.archiveFolder}`);
};
@@ -338,6 +339,7 @@
openedSingleDatabaseConnections,
pinnedDatabases,
selectedWidget,
visibleWidgetSideBar,
} from '../stores';
import getElectron from '../utility/getElectron';
import openNewTab from '../utility/openNewTab';
+2 -2
View File
@@ -1,5 +1,5 @@
import { dumpSqlSelect, Select } from 'dbgate-sqltree';
import { EngineDriver } from 'dbgate-types';
import type { Select } from 'dbgate-sqltree';
import type { EngineDriver } from 'dbgate-types';
import _ from 'lodash';
import { apiCall } from '../utility/api';
+107
View File
@@ -0,0 +1,107 @@
import { apiCall, enableApi } from './utility/api';
import { getConfig } from './utility/metadataLoaders';
export function isOauthCallback() {
const params = new URLSearchParams(location.search);
const sentCode = params.get('code');
const sentState = params.get('state');
return (
sentCode && sentState && sentState.startsWith('dbg-oauth:') && sentState == sessionStorage.getItem('oauthState')
);
}
export function handleOauthCallback() {
const params = new URLSearchParams(location.search);
const sentCode = params.get('code');
if (isOauthCallback()) {
sessionStorage.removeItem('oauthState');
apiCall('auth/oauth-token', {
code: sentCode,
redirectUri: location.origin + location.pathname,
}).then(authResp => {
const { accessToken, error, errorMessage } = authResp;
if (accessToken) {
console.log('Settings access token from OAUTH');
localStorage.setItem('accessToken', accessToken);
internalRedirectTo('/');
} else {
console.log('Error when processing OAUTH callback', error || errorMessage);
internalRedirectTo(`/?page=not-logged&error=${error || errorMessage}`);
}
});
return true;
}
return false;
}
export async function handleAuthOnStartup(config) {
if (config.oauth) {
console.log('OAUTH callback URL:', location.origin + location.pathname);
}
if (config.oauth || config.isLoginForm) {
if (localStorage.getItem('accessToken')) {
return;
}
redirectToLogin(config);
}
}
export async function redirectToLogin(config = null, force = false) {
if (!config) {
enableApi();
config = await getConfig();
}
if (config.isLoginForm) {
if (!force) {
const params = new URLSearchParams(location.search);
if (params.get('page') == 'login' || params.get('page') == 'not-logged') {
return;
}
}
internalRedirectTo('/?page=login');
return;
}
if (config.oauth) {
const state = `dbg-oauth:${Math.random().toString().substr(2)}`;
sessionStorage.setItem('oauthState', state);
console.log('Redirecting to OAUTH provider');
location.replace(
`${config.oauth}?client_id=dbgate&response_type=code&redirect_uri=${encodeURIComponent(
location.origin + location.pathname
)}&state=${encodeURIComponent(state)}`
);
return;
}
}
export function internalRedirectTo(path) {
const index = location.pathname.lastIndexOf('/');
const newPath = index >= 0 ? location.pathname.substring(0, index) + path : path;
location.replace(newPath);
}
export async function doLogout() {
enableApi();
const config = await getConfig();
if (config.oauth) {
localStorage.removeItem('accessToken');
if (config.oauthLogout) {
window.location.href = config.oauthLogout;
} else {
internalRedirectTo('/?page=not-logged');
}
} else if (config.isLoginForm) {
localStorage.removeItem('accessToken');
internalRedirectTo('/?page=not-logged');
} else {
window.location.href = 'config/logout';
}
}
@@ -1,6 +1,6 @@
import { tick } from 'svelte';
import { commands } from '../stores';
import { GlobalCommand } from './registerCommand';
import type { GlobalCommand } from './registerCommand';
let isInvalidated = false;
+1 -1
View File
@@ -1,5 +1,5 @@
import { getCommands, visibleCommandPalette } from '../stores';
import { GlobalCommand } from './registerCommand';
import type { GlobalCommand } from './registerCommand';
export default function runCommand(id) {
const commandsValue = getCommands();
+46 -4
View File
@@ -1,4 +1,13 @@
import { currentDatabase, currentTheme, extensions, getExtensions, getVisibleToolbar, visibleToolbar } from '../stores';
import {
currentDatabase,
currentTheme,
emptyConnectionGroupNames,
extensions,
getExtensions,
getVisibleToolbar,
visibleToolbar,
visibleWidgetSideBar,
} from '../stores';
import registerCommand from './registerCommand';
import { get } from 'svelte/store';
import AboutModal from '../modals/AboutModal.svelte';
@@ -27,6 +36,7 @@ import runCommand from './runCommand';
import { openWebLink } from '../utility/exportFileTools';
import { getSettings } from '../utility/metadataLoaders';
import { isMac } from '../utility/common';
import { doLogout, internalRedirectTo } from '../clientAuth';
// function themeCommand(theme: ThemeDefinition) {
// return {
@@ -75,6 +85,15 @@ registerCommand({
onClick: () => showModal(AboutModal),
});
registerCommand({
id: 'toggle.sidebar',
category: 'Sidebar',
name: 'Show',
toolbarName: 'Toggle sidebar',
keyText: 'CtrlOrCommand+B',
onClick: () => visibleWidgetSideBar.update(x => !x),
});
registerCommand({
id: 'new.connection',
toolbar: true,
@@ -93,6 +112,31 @@ registerCommand({
},
});
registerCommand({
id: 'new.connection.folder',
toolbar: true,
icon: 'icon add-folder',
toolbarName: 'Add connection folder',
category: 'New',
toolbarOrder: 1,
name: 'Connection',
testEnabled: () => !getCurrentConfig()?.runAsPortal,
onClick: () => {
showModal(InputTextModal, {
value: '',
label: 'New connection folder name',
header: 'Create connection folder',
onConfirm: async folder => {
emptyConnectionGroupNames.update(names => {
if (!folder) return names;
if (names.includes(folder)) return names;
return [...names, folder];
});
},
});
},
});
registerCommand({
id: 'new.query',
category: 'New',
@@ -505,9 +549,7 @@ registerCommand({
category: 'App',
name: 'Logout',
testEnabled: () => getCurrentConfig()?.login != null,
onClick: () => {
window.location.href = 'config/logout';
},
onClick: doLogout,
});
export function registerFileCommands({
+4 -4
View File
@@ -1,5 +1,6 @@
import type { ChangeSet, MacroDefinition, MacroSelectedCell } from 'dbgate-datalib';
import {
ChangeSet,
changeSetContainsChanges,
changeSetInsertNewRow,
createChangeSet,
@@ -7,8 +8,6 @@ import {
findExistingChangeSetItem,
getChangeSetInsertedRows,
GridDisplay,
MacroDefinition,
MacroSelectedCell,
revertChangeSetRowChanges,
setChangeSetValue,
setChangeSetRowData,
@@ -16,7 +15,8 @@ import {
runMacroOnValue,
changeSetInsertDocuments,
} from 'dbgate-datalib';
import Grider, { GriderRowStatus } from './Grider';
import Grider from './Grider';
import type { GriderRowStatus } from './Grider';
function getRowFromItem(row, matchedChangeSetItem) {
return matchedChangeSetItem.document
+1 -1
View File
@@ -1,4 +1,4 @@
import Grider, { GriderRowStatus } from './Grider';
import Grider from './Grider';
export default class RowsArrayGrider extends Grider {
constructor(private rows: any[]) {
+3 -3
View File
@@ -1,8 +1,8 @@
import _ from 'lodash';
import { SeriesSizes } from './SeriesSizes';
import { CellAddress } from './selection';
import { GridDisplay } from 'dbgate-datalib';
import Grider from './Grider';
import type { CellAddress } from './selection';
import type { GridDisplay } from 'dbgate-datalib';
import type Grider from './Grider';
import { isJsonLikeLongString, safeJsonParse } from 'dbgate-tools';
export function countColumnSizes(grider: Grider, columns, containerWidth, display: GridDisplay) {
@@ -1,7 +1,5 @@
import _ from 'lodash';
import { dumpSqlSelect, Select, JoinType, Condition, Relation, mergeConditions, Source } from 'dbgate-sqltree';
import { EngineDriver } from 'dbgate-types';
import { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types';
import type { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types';
import { findPrimaryTable, findConnectingReference, referenceIsJoin, referenceIsExists } from './designerTools';
export class DesignerComponent {
@@ -1,18 +1,9 @@
import _ from 'lodash';
import {
dumpSqlSelect,
Select,
JoinType,
Condition,
Relation,
mergeConditions,
Source,
ResultField,
Expression,
} from 'dbgate-sqltree';
import { EngineDriver } from 'dbgate-types';
import { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types';
import { DesignerComponent } from './DesignerComponentCreator';
import type { Select, JoinType, Condition, ResultField, Expression } from 'dbgate-sqltree';
import { mergeConditions } from 'dbgate-sqltree';
import type { DesignerInfo, DesignerTableInfo } from './types';
import type { DesignerComponent } from './DesignerComponentCreator';
import {
getReferenceConditions,
referenceIsCrossJoin,
+1 -1
View File
@@ -1,4 +1,4 @@
import { DesignerTableInfo } from './types';
import type { DesignerTableInfo } from './types';
export default class DomTableRef {
domTable: Element;
+2 -8
View File
@@ -1,12 +1,6 @@
import _ from 'lodash';
import {
IBoxBounds,
IPoint,
rectangleDistance,
rectangleIntersectArea,
solveOverlapsInIntervalArray,
Vector2D,
} from './designerMath';
import type { IBoxBounds, IPoint } from './designerMath';
import { rectangleDistance, rectangleIntersectArea, solveOverlapsInIntervalArray, Vector2D } from './designerMath';
import { union, intersection } from 'interval-operations';
const MIN_NODE_DISTANCE = 50;
+4 -3
View File
@@ -1,7 +1,8 @@
import _ from 'lodash';
import { dumpSqlSelect, Select, JoinType, Condition, Relation, mergeConditions, Source } from 'dbgate-sqltree';
import { EngineDriver } from 'dbgate-types';
import { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types';
import type { Select, Condition, Source } from 'dbgate-sqltree';
import { dumpSqlSelect, mergeConditions } from 'dbgate-sqltree';
import type { EngineDriver } from 'dbgate-types';
import type { DesignerInfo, DesignerTableInfo, DesignerReferenceInfo, DesignerJoinType } from './types';
import { DesignerComponentCreator } from './DesignerComponentCreator';
import { DesignerQueryDumper } from './DesignerQueryDumper';
import { getFilterType } from 'dbgate-filterparser';
+2 -2
View File
@@ -1,5 +1,5 @@
import { JoinType } from 'dbgate-sqltree';
import { TableInfo } from 'dbgate-types';
import type { JoinType } from 'dbgate-sqltree';
import type { TableInfo } from 'dbgate-types';
export type DesignerTableInfo = TableInfo & {
designerId: string;
@@ -47,7 +47,7 @@
);
if (index >= 0 && index + d >= 0 && index + d < current.columns?.length) {
const columns = [...current.columns];
let columns = [...current.columns];
[columns[index], columns[index + d]] = [columns[index + d], columns[index]];
+2 -1
View File
@@ -4,6 +4,7 @@
export let value;
export let focused = false;
export let domEditor = undefined;
export let autocomplete = 'new-password';
if (focused) onMount(() => domEditor.focus());
</script>
@@ -17,5 +18,5 @@
on:click
bind:this={domEditor}
on:keydown
autocomplete="new-password"
{autocomplete}
/>
+1 -2
View File
@@ -1,5 +1,5 @@
import type { ChangeSet, ChangeSetRowDefinition } from 'dbgate-datalib';
import {
ChangeSet,
changeSetContainsChanges,
changeSetInsertNewRow,
createChangeSet,
@@ -9,7 +9,6 @@ import {
TableFormViewDisplay,
revertChangeSetRowChanges,
setChangeSetValue,
ChangeSetRowDefinition,
} from 'dbgate-datalib';
import Former from './Former';
@@ -1,5 +1,5 @@
import { FreeTableModel } from 'dbgate-datalib';
import Grider, { GriderRowStatus } from '../datagrid/Grider';
import type { FreeTableModel } from 'dbgate-datalib';
import Grider from '../datagrid/Grider';
export default class FreeTableGrider extends Grider {
public model: FreeTableModel;
@@ -1,5 +1,7 @@
import { FreeTableModel, MacroDefinition, MacroSelectedCell, runMacro } from 'dbgate-datalib';
import Grider, { GriderRowStatus } from '../datagrid/Grider';
import type { FreeTableModel, MacroDefinition, MacroSelectedCell } from 'dbgate-datalib';
import { runMacro } from 'dbgate-datalib';
import Grider from '../datagrid/Grider';
import type { GriderRowStatus } from '../datagrid/Grider';
import _ from 'lodash';
function convertToSet(row, field) {
+1
View File
@@ -29,6 +29,7 @@
'icon arrange': 'mdi mdi-arrange-send-to-back',
'icon app': 'mdi mdi-layers-triple',
'icon open-in-new': 'mdi mdi-open-in-new',
'icon add-folder': 'mdi mdi-folder-plus-outline',
'icon window-restore': 'mdi mdi-window-restore',
'icon window-close': 'mdi mdi-window-close',
+2 -2
View File
@@ -1,7 +1,7 @@
export function plusExpandIcon(isExpanded) {
export function plusExpandIcon(isExpanded): string {
return isExpanded ? 'icon minus-box' : 'icon plus-box';
}
export function chevronExpandIcon(isExpanded) {
export function chevronExpandIcon(isExpanded): string {
return isExpanded ? 'icon chevron-down' : 'icon chevron-right';
}
+32 -5
View File
@@ -3,14 +3,41 @@ import './utility/connectionsPinger';
import './utility/changeCurrentDbByTab';
import './commands/stdCommands';
import localStorageGarbageCollector from './utility/localStorageGarbageCollector';
import { handleOauthCallback } from './clientAuth';
import LoginPage from './LoginPage.svelte';
import NotLoggedPage from './NotLoggedPage.svelte';
const isOauthCallback = handleOauthCallback();
const params = new URLSearchParams(location.search);
const page = params.get('page');
localStorageGarbageCollector();
const app = new App({
target: document.body,
props: {},
});
function createApp() {
if (isOauthCallback) {
return null;
}
// const app = null;
switch (page) {
case 'login':
return new LoginPage({
target: document.body,
props: {},
});
case 'not-logged':
return new NotLoggedPage({
target: document.body,
props: {},
});
}
return new App({
target: document.body,
props: {},
});
}
const app = createApp();
export default app;
@@ -16,7 +16,7 @@
import { getDefaultFileFormat } from '../plugins/fileformats';
import RunnerOutputFiles from '../query/RunnerOutputFiles.svelte';
import SocketMessageView from '../query/SocketMessageView.svelte';
import { currentArchive, currentDatabase, extensions, getCurrentConfig, selectedWidget } from '../stores';
import { currentArchive, currentDatabase, extensions, visibleWidgetSideBar, selectedWidget } from '../stores';
import { apiCall, apiOff, apiOn } from '../utility/api';
import createRef from '../utility/createRef';
import openNewTab from '../utility/openNewTab';
@@ -85,6 +85,7 @@
apiCall('archive/refresh-files', { folder: refreshArchiveFolderRef.get() });
$currentArchive = refreshArchiveFolderRef.get();
$selectedWidget = 'archive';
$visibleWidgetSideBar = true;
}
};
@@ -16,13 +16,8 @@
useViewInfo,
} from '../utility/metadataLoaders';
import { onMount, tick } from 'svelte';
import {
ChangePerspectiveConfigFunc,
createPerspectiveNodeConfig,
PerspectiveConfig,
PerspectiveCustomJoinConfig,
PerspectiveTreeNode,
} from 'dbgate-datalib';
import { createPerspectiveNodeConfig, PerspectiveTreeNode } from 'dbgate-datalib';
import type { ChangePerspectiveConfigFunc, PerspectiveConfig, PerspectiveCustomJoinConfig } from 'dbgate-datalib';
import getConnectionLabel from '../utility/getConnectionLabel';
import uuidv1 from 'uuid/v1';
import TextField from '../forms/TextField.svelte';
@@ -1,14 +1,12 @@
<script lang="ts">
import {
createPerspectiveNodeConfig,
MultipleDatabaseInfo,
PerspectiveConfig,
PerspectiveDataPatternDict,
perspectiveNodesHaveStructure,
PerspectiveTreeNode,
switchPerspectiveReferenceDirection,
} from 'dbgate-datalib';
import { CollectionInfo } from 'dbgate-types';
import type { MultipleDatabaseInfo, PerspectiveConfig, PerspectiveDataPatternDict } from 'dbgate-datalib';
import type { CollectionInfo } from 'dbgate-types';
import _ from 'lodash';
import { tick } from 'svelte';
import runCommand from '../commands/runCommand';
@@ -1,5 +1,6 @@
<script lang="ts">
import { ChangePerspectiveConfigFunc, PerspectiveConfig, PerspectiveTreeNode } from 'dbgate-datalib';
import { PerspectiveTreeNode } from 'dbgate-datalib';
import type { ChangePerspectiveConfigFunc, PerspectiveConfig } from 'dbgate-datalib';
import { keys } from 'localforage';
import _, { map } from 'lodash';
@@ -1,19 +1,10 @@
<script lang="ts">
import DataFilterControl from '../datagrid/DataFilterControl.svelte';
import ColumnLabel from '../elements/ColumnLabel.svelte';
import InlineButton from '../buttons/InlineButton.svelte';
import FontIcon from '../icons/FontIcon.svelte';
import { getFilterType, getFilterValueExpression } from 'dbgate-filterparser';
import {
ChangePerspectiveConfigFunc,
PerspectiveConfig,
PerspectiveFilterColumnInfo,
PerspectiveTreeNode,
} from 'dbgate-datalib';
import { showModal } from '../modals/modalTools';
import DictionaryLookupModal from '../modals/DictionaryLookupModal.svelte';
import ValueLookupModal from '../modals/ValueLookupModal.svelte';
import { PerspectiveTreeNode } from 'dbgate-datalib';
import type { ChangePerspectiveConfigFunc, PerspectiveConfig, PerspectiveFilterColumnInfo } from 'dbgate-datalib';
export let filterInfo: PerspectiveFilterColumnInfo;
@@ -1,5 +1,6 @@
<script lang="ts">
import { ChangePerspectiveConfigFunc, PerspectiveConfig, PerspectiveDisplayColumn } from 'dbgate-datalib';
import { PerspectiveDisplayColumn } from 'dbgate-datalib';
import type { ChangePerspectiveConfigFunc, PerspectiveConfig } from 'dbgate-datalib';
import _, { mapKeys } from 'lodash';
import DropDownButton from '../buttons/DropDownButton.svelte';
@@ -49,13 +50,13 @@
.label {
flex-wrap: nowrap;
}
.order-index {
/* .order-index {
font-size: 10pt;
margin-left: -3px;
margin-right: 2px;
top: -1px;
position: relative;
}
} */
.label {
flex: 1;
min-width: 10px;
@@ -68,13 +69,13 @@
align-self: center;
font-size: 18px;
}
.grouping {
/* .grouping {
color: var(--theme-font-alt);
white-space: nowrap;
}
.data-type {
color: var(--theme-font-3);
}
} */
th {
/* border: 1px solid var(--theme-border); */
@@ -1,5 +1,6 @@
<script lang="ts">
import { ChangePerspectiveConfigFunc, PerspectiveConfig, PerspectiveTreeNode } from 'dbgate-datalib';
import type { ChangePerspectiveConfigFunc, PerspectiveConfig } from 'dbgate-datalib';
import { PerspectiveTreeNode } from 'dbgate-datalib';
import ColumnLabel from '../elements/ColumnLabel.svelte';
import { plusExpandIcon } from '../icons/expandIcons';
@@ -93,7 +94,7 @@
background: var(--theme-bg-hover);
}
.row.isSelected {
/* .row.isSelected {
background: var(--theme-bg-selected);
}
} */
</style>
@@ -13,15 +13,14 @@
<script lang="ts">
import {
ChangePerspectiveConfigFunc,
PerspectiveConfig,
PerspectiveDisplay,
PerspectivePatternColumnNode,
PerspectiveTableColumnNode,
PerspectiveTreeNode,
PERSPECTIVE_PAGE_SIZE,
} from 'dbgate-datalib';
import _, { values } from 'lodash';
import type { ChangePerspectiveConfigFunc, PerspectiveConfig } from 'dbgate-datalib';
import _ from 'lodash';
import { onMount, tick } from 'svelte';
import resizeObserver from '../utility/resizeObserver';
import debug from 'debug';
@@ -597,9 +596,9 @@
z-index: 100;
}
th.filter {
/* th.filter {
padding: 0;
}
} */
thead :global(tr:first-child) :global(th) {
border-top: 1px solid var(--theme-border);
@@ -1,11 +1,6 @@
<script lang="ts">
import {
ChangeConfigFunc,
ChangePerspectiveConfigFunc,
GridConfig,
PerspectiveConfig,
PerspectiveTreeNode,
} from 'dbgate-datalib';
import { PerspectiveTreeNode } from 'dbgate-datalib';
import type { ChangeConfigFunc, ChangePerspectiveConfigFunc, GridConfig, PerspectiveConfig } from 'dbgate-datalib';
import { filterName } from 'dbgate-tools';
import PerspectiveNodeRow from './PerspectiveNodeRow.svelte';
@@ -26,15 +26,14 @@
<script lang="ts">
import {
ChangePerspectiveConfigFunc,
extractPerspectiveDatabases,
PerspectiveConfig,
PerspectiveDataProvider,
PerspectiveTableNode,
PerspectiveTreeNode,
processPerspectiveDefaultColunns,
shouldProcessPerspectiveDefaultColunns,
} from 'dbgate-datalib';
import type { ChangePerspectiveConfigFunc, PerspectiveConfig } from 'dbgate-datalib';
import _ from 'lodash';
@@ -48,7 +47,7 @@
import PerspectiveTable from './PerspectiveTable.svelte';
import { apiCall } from '../utility/api';
import ManagerInnerContainer from '../elements/ManagerInnerContainer.svelte';
import { PerspectiveDataLoader } from 'dbgate-datalib/lib/PerspectiveDataLoader';
import { PerspectiveDataLoader } from 'dbgate-datalib';
import stableStringify from 'json-stable-stringify';
import createActivator, { getActiveComponent } from '../utility/createActivator';
import registerCommand from '../commands/registerCommand';
@@ -1,4 +1,4 @@
import { ChangePerspectiveConfigFunc, PerspectiveConfig, PerspectiveTreeNode } from 'dbgate-datalib';
import type { ChangePerspectiveConfigFunc, PerspectiveConfig, PerspectiveTreeNode } from 'dbgate-datalib';
import _ from 'lodash';
import { showModal } from '../modals/modalTools';
import CustomJoinModal from './CustomJoinModal.svelte';
@@ -1,5 +1,6 @@
<script lang="ts">
import _ from 'lodash';
import ErrorInfo from '../elements/ErrorInfo.svelte';
import SearchBoxWrapper from '../elements/SearchBoxWrapper.svelte';
import SearchInput from '../elements/SearchInput.svelte';
@@ -21,5 +22,9 @@
<SearchInput placeholder="Search extensions on web" {filter} bind:value={filter} />
</SearchBoxWrapper>
<WidgetsInnerContainer>
<PluginsList plugins={$plugins} />
{#if $plugins?.errorMessage}
<ErrorInfo message={$plugins?.errorMessage} />
{:else}
<PluginsList plugins={$plugins} />
{/if}
</WidgetsInnerContainer>
@@ -59,7 +59,7 @@
import { useInstalledPlugins } from '../utility/metadataLoaders';
import { buildFileFormats, buildQuickExports } from './fileformats';
import { buildThemes } from './themes';
import dbgateTools from 'dbgate-tools';
import * as dbgateTools from 'dbgate-tools';
import { apiCall } from '../utility/api';
let pluginsDict = {};
+1 -1
View File
@@ -1,4 +1,4 @@
import { FileFormatDefinition, QuickExportDefinition } from 'dbgate-types';
import type { FileFormatDefinition, QuickExportDefinition } from 'dbgate-types';
const jsonlFormat = {
storageType: 'jsonl',
@@ -18,7 +18,13 @@
import { closeCurrentModal } from '../modals/modalTools';
import { EDITOR_THEMES, FONT_SIZES } from '../query/AceEditor.svelte';
import SqlEditor from '../query/SqlEditor.svelte';
import { currentEditorFontSize, currentEditorTheme, extensions, selectedWidget } from '../stores';
import {
currentEditorFontSize,
currentEditorTheme,
extensions,
selectedWidget,
visibleWidgetSideBar,
} from '../stores';
import { isMac } from '../utility/common';
import getElectron from '../utility/getElectron';
import ThemeSkeleton from './ThemeSkeleton.svelte';
@@ -47,6 +53,7 @@ ORDER BY
function openThemePlugins() {
closeCurrentModal();
$selectedWidget = 'plugins';
$visibleWidgetSideBar = true;
}
</script>
@@ -123,7 +130,6 @@ ORDER BY
{ value: 'lowerCase', label: 'lower case' },
]}
/>
</svelte:fragment>
<svelte:fragment slot="2">
<div class="heading">Application theme</div>

Some files were not shown because too many files have changed in this diff Show More