Compare commits
53 Commits
rollup-update
...
oauth
| Author | SHA1 | Date | |
|---|---|---|---|
| 8489c171f3 | |||
| 592865b16e | |||
| 012d3ec2e1 | |||
| d84adcca5d | |||
| b1ae7d53b9 | |||
| 9a5287725b | |||
| 5ccd724166 | |||
| 5e4c286427 | |||
| 70413b954b | |||
| 07b2a3e923 | |||
| 94a91d5fed | |||
| 576fc2062c | |||
| 37a8783751 | |||
| f42d78b2fb | |||
| 792fa75ccd | |||
| cbd3f1bae9 | |||
| cd92231769 | |||
| ecad1ae01b | |||
| dc576e6ced | |||
| 6cca81f8f1 | |||
| a9f1f19696 | |||
| 390ddac75b | |||
| e2e7c6f06b | |||
| 3a3d0683d5 | |||
| d5534dcf07 | |||
| b0a86f9f4a | |||
| b833a30148 | |||
| d9c1bbaa39 | |||
| 4b74dbbd68 | |||
| 9bcc61551c | |||
| ed71ef312d | |||
| 4fa043b7e5 | |||
| 83725dd349 | |||
| 4e25b71b06 | |||
| 607ae7c872 | |||
| 66ade5823f | |||
| ebfa0a1939 | |||
| 909591404f | |||
| 7a5f2a70ad | |||
| d41b254058 | |||
| 435d06ffb9 | |||
| f4a4eb7f9e | |||
| 9910bbead3 | |||
| cb619a0fe0 | |||
| b0d61f974c | |||
| 8c051ff5f7 | |||
| f713a4b183 | |||
| 6c7e263f0e | |||
| ec3bfb4fae | |||
| 712ec8e6ee | |||
| 4da0b25f44 | |||
| 8ed73195c5 | |||
| c69fcd5eff |
@@ -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 }}
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
16.14.2
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
.env
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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}`],
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,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,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,4 +1,4 @@
|
||||
import { TableInfo } from 'dbgate-types';
|
||||
import type { TableInfo } from 'dbgate-types';
|
||||
|
||||
export interface FreeTableModel {
|
||||
structure: TableInfo;
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
import { DisplayColumn } from './GridDisplay';
|
||||
import { TableInfo } from 'dbgate-types';
|
||||
|
||||
export interface GridConfigColumns {
|
||||
hiddenColumns: string[];
|
||||
expandedColumns: string[];
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import _ from 'lodash';
|
||||
import { GridConfig, GridCache, GridConfigColumns, createGridCache, GroupFunc, createGridConfig } from './GridConfig';
|
||||
import {
|
||||
import type {
|
||||
ForeignKeyInfo,
|
||||
TableInfo,
|
||||
ColumnInfo,
|
||||
|
||||
@@ -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,4 +1,3 @@
|
||||
import { RangeDefinition } from 'dbgate-types';
|
||||
import { PerspectiveDataLoadProps } from './PerspectiveDataProvider';
|
||||
import _pick from 'lodash/pick';
|
||||
import _zip from 'lodash/zip';
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
import {
|
||||
import type {
|
||||
CollectionInfo,
|
||||
ColumnInfo,
|
||||
DatabaseInfo,
|
||||
|
||||
@@ -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,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,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,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 {
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
import { DatabaseInfo } from 'dbgate-types';
|
||||
import type { DatabaseInfo } from 'dbgate-types';
|
||||
|
||||
export const chinookDbInfo: DatabaseInfo = {
|
||||
tables: [
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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,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,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,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,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,4 +1,4 @@
|
||||
import { NamedObjectInfo, RangeDefinition, TransformType } from 'dbgate-types';
|
||||
import type { NamedObjectInfo, RangeDefinition, TransformType } from 'dbgate-types';
|
||||
|
||||
// export interface Command {
|
||||
// }
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
import _ from 'lodash';
|
||||
import {
|
||||
import type {
|
||||
ColumnInfo,
|
||||
ConstraintInfo,
|
||||
EngineDriver,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {
|
||||
import type {
|
||||
DatabaseInfo,
|
||||
EngineDriver,
|
||||
FunctionInfo,
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
import {
|
||||
import type {
|
||||
ColumnInfo,
|
||||
ConstraintInfo,
|
||||
DatabaseInfo,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import _cloneDeep from 'lodash/cloneDeep';
|
||||
import _isString from 'lodash/isString';
|
||||
import {
|
||||
import type {
|
||||
ColumnInfo,
|
||||
ColumnReference,
|
||||
DatabaseInfo,
|
||||
|
||||
@@ -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,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,6 +1,6 @@
|
||||
import uuidv1 from 'uuid/v1';
|
||||
import _omit from 'lodash/omit';
|
||||
import {
|
||||
import type {
|
||||
ColumnInfo,
|
||||
ConstraintInfo,
|
||||
ForeignKeyInfo,
|
||||
|
||||
@@ -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,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';
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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,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();
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
import Grider, { GriderRowStatus } from './Grider';
|
||||
import Grider from './Grider';
|
||||
|
||||
export default class RowsArrayGrider extends Grider {
|
||||
constructor(private rows: any[]) {
|
||||
|
||||
@@ -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,4 +1,4 @@
|
||||
import { DesignerTableInfo } from './types';
|
||||
import type { DesignerTableInfo } from './types';
|
||||
|
||||
export default class DomTableRef {
|
||||
domTable: Element;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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]];
|
||||
|
||||
|
||||
@@ -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,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) {
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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';
|
||||
}
|
||||
|
||||
@@ -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,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
Reference in New Issue
Block a user