Compare commits
140 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d68d9206b4 | |||
| 9d99c01018 | |||
| 16ed91b147 | |||
| 4d32e57947 | |||
| 4d25fef37d | |||
| 25835ee19f | |||
| 2cd3aac158 | |||
| ee671297bf | |||
| ce895f92cd | |||
| 4e746a3055 | |||
| 5bc0af1fba | |||
| 66a9e0d14a | |||
| b536b56348 | |||
| ad6a22d2a6 | |||
| 64d9b26d79 | |||
| 284606e3d5 | |||
| e8129fd499 | |||
| d454da325f | |||
| 5c703c786d | |||
| 864c5bb208 | |||
| 504d16f189 | |||
| d87af2a820 | |||
| 4e6e0a79c4 | |||
| f4fbb28124 | |||
| 1c03a8ce9e | |||
| 5bf2cf2784 | |||
| e572cd392c | |||
| a812ff510d | |||
| cafe0e68c3 | |||
| 728f3621eb | |||
| ca0ae2084c | |||
| 0cc7a98391 | |||
| 68a40e5da6 | |||
| bbf2e2f7ed | |||
| 1f75a818c8 | |||
| ebdcd9ad94 | |||
| 2d1ac97191 | |||
| 8f5b395935 | |||
| df60d40134 | |||
| 2723c41832 | |||
| a2102a51a1 | |||
| 958d786dfb | |||
| e2526082b8 | |||
| 0d22c675b6 | |||
| ab481121f9 | |||
| e9ee52ac9d | |||
| 5eecb45961 | |||
| 94e991b059 | |||
| 373a35fe65 | |||
| eba5bd9c2b | |||
| ca3507f5d4 | |||
| 4f5db15c20 | |||
| 22bed04d13 | |||
| d3737b4e08 | |||
| 20c1315380 | |||
| e9442bd633 | |||
| 59bd699fc6 | |||
| cc3fd605de | |||
| 0adf730f0b | |||
| 5ab0907bd8 | |||
| e04da15f72 | |||
| 5fe55af3b7 | |||
| 79793d1b58 | |||
| d00ee890e5 | |||
| 7d984d8faf | |||
| 153f40f13e | |||
| 134d8d1b1a | |||
| 2678daab4d | |||
| 781ee15304 | |||
| 51d4bc9a75 | |||
| df3313e647 | |||
| 4214b4f613 | |||
| 685c0f7dbc | |||
| 4417edf73b | |||
| faf94c1a24 | |||
| c82a877271 | |||
| 8ba85acd3c | |||
| ba65704d55 | |||
| 68f77d4ed7 | |||
| 008f6be6ac | |||
| 7324cef87a | |||
| cb9921918f | |||
| 9839dc795b | |||
| 471fcdc131 | |||
| c2abc83f99 | |||
| a23bda7294 | |||
| a2d643305b | |||
| dd36427a80 | |||
| 9b4683ef53 | |||
| a24271f045 | |||
| f74e57bec2 | |||
| 4fb6b49b86 | |||
| 72c380cef5 | |||
| 39cdaf88f4 | |||
| 52c77031c5 | |||
| fdabe1eeaa | |||
| 4429b1d618 | |||
| 5c24774170 | |||
| 792be82acd | |||
| 6e3cd08d8a | |||
| 696d870c2f | |||
| 26471517a9 | |||
| 58233a2fd5 | |||
| 230948c4b4 | |||
| df593074c2 | |||
| 474ecb1b71 | |||
| e8e5781b59 | |||
| a042ff363e | |||
| 63bdf817c6 | |||
| 82eed3b86e | |||
| 21a24f9ba2 | |||
| 550354fe09 | |||
| e14e7efa1a | |||
| b1cf418058 | |||
| bde4127b33 | |||
| e981cb2734 | |||
| 6f4c0edb46 | |||
| 6591e45a6e | |||
| 397a6b54ff | |||
| 51555da376 | |||
| 8e6b1973c7 | |||
| 5a8627c39f | |||
| 26a46d9037 | |||
| b9a974ca27 | |||
| e4ed163723 | |||
| 4d8c62f3f5 | |||
| 4b2e28483b | |||
| 3cd070e211 | |||
| 1e818e7756 | |||
| 91efb7abda | |||
| 5659311ba2 | |||
| 232031ff5b | |||
| 0e242321ed | |||
| 83f3391b24 | |||
| 715c6f7f29 | |||
| 0fc20f7238 | |||
| c824e32f0a | |||
| 10a916bce6 | |||
| 1080147085 | |||
| f0ebe260e2 |
@@ -23,10 +23,10 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js 16.x
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 18.x
|
||||
- name: yarn adjustPackageJson
|
||||
run: |
|
||||
yarn adjustPackageJson
|
||||
@@ -39,6 +39,11 @@ jobs:
|
||||
- name: setCurrentVersion
|
||||
run: |
|
||||
yarn setCurrentVersion
|
||||
- name: printSecrets
|
||||
run: |
|
||||
yarn printSecrets
|
||||
env:
|
||||
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
|
||||
- name: fillNativeModulesElectron
|
||||
run: |
|
||||
yarn fillNativeModulesElectron
|
||||
@@ -63,6 +68,7 @@ jobs:
|
||||
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
|
||||
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
|
||||
- name: publishSnap
|
||||
@@ -88,6 +94,7 @@ jobs:
|
||||
|
||||
mv app/dist/*.exe artifacts/ || true
|
||||
mv app/dist/*.zip artifacts/ || true
|
||||
mv app/dist/*.tar.gz artifacts/ || true
|
||||
mv app/dist/*.AppImage artifacts/ || true
|
||||
mv app/dist/*.deb artifacts/ || true
|
||||
mv app/dist/*.snap artifacts/ || true
|
||||
|
||||
@@ -27,10 +27,10 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js 16.x
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 18.x
|
||||
- name: yarn adjustPackageJson
|
||||
run: |
|
||||
yarn adjustPackageJson
|
||||
@@ -45,6 +45,11 @@ jobs:
|
||||
- name: setCurrentVersion
|
||||
run: |
|
||||
yarn setCurrentVersion
|
||||
- name: printSecrets
|
||||
run: |
|
||||
yarn printSecrets
|
||||
env:
|
||||
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
|
||||
- name: fillNativeModulesElectron
|
||||
run: |
|
||||
yarn fillNativeModulesElectron
|
||||
@@ -69,6 +74,7 @@ jobs:
|
||||
CSC_KEY_PASSWORD: ${{ secrets.APPLECERT_PASSWORD }}
|
||||
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
|
||||
- name: generatePadFile
|
||||
@@ -98,6 +104,7 @@ jobs:
|
||||
|
||||
mv app/dist/*.exe artifacts/ || true
|
||||
mv app/dist/*.zip artifacts/ || true
|
||||
mv app/dist/*.tar.gz artifacts/ || true
|
||||
mv app/dist/*.AppImage artifacts/ || true
|
||||
mv app/dist/*.deb artifacts/ || true
|
||||
mv app/dist/*.dmg artifacts/ || true
|
||||
|
||||
@@ -53,18 +53,25 @@ jobs:
|
||||
type=match,pattern=\d+.\d+.\d+,suffix=-alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }}
|
||||
type=raw,value=alpine,enable=${{ !contains(github.ref_name, '-docker.') && !contains(github.ref_name, '-beta.') }}
|
||||
|
||||
- name: Use Node.js 16.x
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 18.x
|
||||
- name: yarn install
|
||||
run: |
|
||||
# yarn --version
|
||||
# yarn config set network-timeout 300000
|
||||
yarn install
|
||||
|
||||
- name: setCurrentVersion
|
||||
run: |
|
||||
yarn setCurrentVersion
|
||||
|
||||
- name: printSecrets
|
||||
run: |
|
||||
yarn printSecrets
|
||||
env:
|
||||
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
|
||||
- name: Prepare docker image
|
||||
run: |
|
||||
yarn run prepare:docker
|
||||
|
||||
@@ -30,10 +30,10 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Use Node.js 16.x
|
||||
- name: Use Node.js 18.x
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 16.x
|
||||
node-version: 18.x
|
||||
|
||||
- name: Configure NPM token
|
||||
env:
|
||||
@@ -49,6 +49,12 @@ jobs:
|
||||
run: |
|
||||
yarn setCurrentVersion
|
||||
|
||||
- name: printSecrets
|
||||
run: |
|
||||
yarn printSecrets
|
||||
env:
|
||||
GIST_UPLOAD_SECRET : ${{secrets.GIST_UPLOAD_SECRET}}
|
||||
|
||||
- name: Publish types
|
||||
working-directory: packages/types
|
||||
run: |
|
||||
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
jobs:
|
||||
test-runner:
|
||||
runs-on: ubuntu-latest
|
||||
container: node:16.20
|
||||
container: node:18
|
||||
|
||||
steps:
|
||||
- name: Context
|
||||
|
||||
@@ -8,6 +8,44 @@ Builds:
|
||||
- linux - application for linux
|
||||
- win - application for Windows
|
||||
|
||||
### 5.3.0
|
||||
- CHANGED: New Oracle driver, much better Oracle support. Works now also in docker distribution
|
||||
- FIXED: Connection to oracle with service name #809
|
||||
- ADDED: Connect to redis using a custom username #807
|
||||
- FIXED: Unable to open SQL files #797
|
||||
- FIXED: MongoDB query without columns #811
|
||||
- ADDED: Switch connection for opened file #814
|
||||
|
||||
### 5.2.9
|
||||
- FIXED: PostgresSQL doesn't show tables when connected #793 #805
|
||||
- FIXED: MongoDB write operations fail #798 #802
|
||||
- FIXED: Elecrron app logging losed most of log messages
|
||||
- FIXED: Connection error with SSH tunnel
|
||||
- ADDED: option to disable autoupgrades (with --disable-auto-upgrade)
|
||||
- ADDED: Send error context to github gist
|
||||
|
||||
### 5.2.8
|
||||
- FIXED: file menu save and save as not working
|
||||
- FIXED: query editor on import/export screen overlaps with selector
|
||||
- FIXED: Fixed inconsistencies in max/unmaximize window buttons
|
||||
- FIXED: shortcut for select all
|
||||
- FIXED: download with auth header
|
||||
- CHANGED: Upgraded database drivers for mysql, postgres, sqlite, mssql, mongo, redis
|
||||
- CHANGED: Upgraded electron version (now using v30)
|
||||
- ADDED: Vim keyboard bindings for editor
|
||||
- FIXED: Correctly select the save folder for dump
|
||||
- ADDED: enum + set for mysql (#693)
|
||||
- FIXED: localStorageGabageCollector not working
|
||||
- FIXED: Encoding error when opening Unicode query files
|
||||
- ADDED: Add copy/paste to query tab and database list
|
||||
- ADDED: Add copy name to table list
|
||||
- FIXED: Make TabControl scrollable (#730)
|
||||
- ADDED: Add copy to column list
|
||||
- FIXED: Problems with SQLite + glibc in docker containers
|
||||
- ADDED: Button for discard/reset changes (#759)
|
||||
- FIXED: Don't show error dialog when subprocess fails, as DbGate handles this correctly (#751, #746, #542, #272)
|
||||
|
||||
|
||||
### 5.2.7
|
||||
- FIXED: fix body overflow when context menu height great than viewport #592
|
||||
- FIXED: Pass signals in entrypoint.sh #596
|
||||
|
||||
@@ -22,7 +22,7 @@ DbGate is licensed under MIT license and is completely free.
|
||||
* MySQL
|
||||
* PostgreSQL
|
||||
* SQL Server
|
||||
* Oracle (experimental)
|
||||
* Oracle
|
||||
* MongoDB
|
||||
* Redis
|
||||
* SQLite
|
||||
|
||||
@@ -5,9 +5,6 @@ function adjustFile(file) {
|
||||
if (process.platform != 'win32') {
|
||||
delete json.optionalDependencies.msnodesqlv8;
|
||||
}
|
||||
if (process.arch == 'arm64') {
|
||||
delete json.optionalDependencies.oracledb;
|
||||
}
|
||||
fs.writeFileSync(file, JSON.stringify(json, null, 2), 'utf-8');
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<component type="desktop-application">
|
||||
<id>org.dbgate.DbGate</id>
|
||||
|
||||
<name>DbGate</name>
|
||||
<summary>(no)SQL database client</summary>
|
||||
|
||||
<metadata_license>CC0-1.0</metadata_license>
|
||||
<project_license>MIT</project_license>
|
||||
<developer_name>Jan Prochazka</developer_name>
|
||||
|
||||
<description>
|
||||
<p>DbGate is cross-platform database manager. It's designed to be simple to use and effective, when working with more databases simultaneously. But there are also many advanced features like schema compare, visual query designer, chart visualisation or batch export and import.</p>
|
||||
</description>
|
||||
|
||||
<url type="homepage">https://dbgate.org/</url>
|
||||
<url type="vcs-browser">https://github.com/dbgate/dbgate</url>
|
||||
<url type="contact">https://dbgate.org/about/</url>
|
||||
<url type="donation">https://github.com/sponsors/dbgate</url>
|
||||
<url type="bugtracker">https://github.com/dbgate/dbgate/issues</url>
|
||||
|
||||
<launchable type="desktop-id">org.dbgate.DbGate.desktop</launchable>
|
||||
<screenshots>
|
||||
<screenshot type="default">
|
||||
<image>
|
||||
https://github.com/dbgate/dbgate/raw/c2abc83f994a56945c27fccea3df84b48005961f/img/screenshot1.png</image>
|
||||
</screenshot>
|
||||
<screenshot>
|
||||
<image>
|
||||
https://github.com/dbgate/dbgate/raw/c2abc83f994a56945c27fccea3df84b48005961f/img/screenshot2.png</image>
|
||||
</screenshot>
|
||||
</screenshots>
|
||||
|
||||
<content_rating type="oars-1.1"/>
|
||||
|
||||
<releases>
|
||||
<release version="5.2.7" date="2024-05-13"/>
|
||||
</releases>
|
||||
</component>
|
||||
+6
-6
@@ -48,7 +48,8 @@
|
||||
"armv7l",
|
||||
"arm64"
|
||||
]
|
||||
}
|
||||
},
|
||||
"tar.gz"
|
||||
],
|
||||
"icon": "icons/",
|
||||
"category": "Development",
|
||||
@@ -114,13 +115,12 @@
|
||||
"devDependencies": {
|
||||
"copyfiles": "^2.2.0",
|
||||
"cross-env": "^6.0.3",
|
||||
"electron": "17.4.10",
|
||||
"electron": "30.0.2",
|
||||
"electron-builder": "23.1.0",
|
||||
"electron-builder-notarize": "^1.5.1"
|
||||
"electron-builder-notarize": "^1.5.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"better-sqlite3": "7.6.2",
|
||||
"msnodesqlv8": "^2.6.0",
|
||||
"oracledb": "^5.5.0"
|
||||
"better-sqlite3": "9.6.0",
|
||||
"msnodesqlv8": "^4.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+20
-1
@@ -17,6 +17,7 @@ const path = require('path');
|
||||
const url = require('url');
|
||||
const mainMenuDefinition = require('./mainMenuDefinition');
|
||||
const { settings } = require('cluster');
|
||||
let disableAutoUpgrade = false;
|
||||
|
||||
// require('@electron/remote/main').initialize();
|
||||
|
||||
@@ -27,6 +28,10 @@ let mainModule;
|
||||
// let getLogger;
|
||||
// let loadLogsContent;
|
||||
|
||||
process.on('uncaughtException', function (error) {
|
||||
console.error('uncaughtException', error);
|
||||
});
|
||||
|
||||
const isMac = () => os.platform() == 'darwin';
|
||||
|
||||
// unhandled({
|
||||
@@ -45,11 +50,21 @@ const isMac = () => os.platform() == 'darwin';
|
||||
|
||||
try {
|
||||
initialConfig = JSON.parse(fs.readFileSync(configRootPath, { encoding: 'utf-8' }));
|
||||
disableAutoUpgrade = initialConfig['disableAutoUpgrade'] || false;
|
||||
} catch (err) {
|
||||
console.log('Error loading config-root:', err.message);
|
||||
initialConfig = {};
|
||||
}
|
||||
|
||||
if (process.argv.includes('--disable-auto-upgrade')) {
|
||||
console.log('Disabling auto-upgrade');
|
||||
disableAutoUpgrade = true;
|
||||
}
|
||||
if (process.argv.includes('--enable-auto-upgrade')) {
|
||||
console.log('Enabling auto-upgrade');
|
||||
disableAutoUpgrade = false;
|
||||
}
|
||||
|
||||
// Keep a global reference of the window object, if you don't, the window will
|
||||
// be closed automatically when the JavaScript object is garbage collected.
|
||||
let mainWindow;
|
||||
@@ -317,6 +332,7 @@ function createWindow() {
|
||||
JSON.stringify({
|
||||
winBounds: mainWindow.getBounds(),
|
||||
winIsMaximized: mainWindow.isMaximized(),
|
||||
disableAutoUpgrade,
|
||||
}),
|
||||
'utf-8'
|
||||
);
|
||||
@@ -379,7 +395,10 @@ function createWindow() {
|
||||
}
|
||||
|
||||
function onAppReady() {
|
||||
if (!process.env.DEVMODE) {
|
||||
if (disableAutoUpgrade) {
|
||||
console.log('Auto-upgrade is disabled, run dbgate --enable-auto-upgrade to enable');
|
||||
}
|
||||
if (!process.env.DEVMODE && !disableAutoUpgrade) {
|
||||
autoUpdater.checkForUpdatesAndNotify();
|
||||
}
|
||||
createWindow();
|
||||
|
||||
@@ -90,6 +90,7 @@ module.exports = ({ editMenu }) => [
|
||||
{ divider: true },
|
||||
{ command: 'folder.showLogs', hideDisabled: true },
|
||||
{ command: 'folder.showData', hideDisabled: true },
|
||||
{ command: 'new.gist', hideDisabled: true },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
+459
-345
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -11,8 +11,8 @@ RUN apt-get update && apt-get install -y \
|
||||
make
|
||||
|
||||
RUN curl -fsSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | gpg --dearmor -o /usr/share/keyrings/nodesource-archive-keyring.gpg \
|
||||
&& echo "deb [signed-by=/usr/share/keyrings/nodesource-archive-keyring.gpg] https://deb.nodesource.com/node_16.x jammy main" | tee /etc/apt/sources.list.d/nodesource.list \
|
||||
&& echo "deb-src [signed-by=/usr/share/keyrings/nodesource-archive-keyring.gpg] https://deb.nodesource.com/node_16.x jammy main" | tee -a /etc/apt/sources.list.d/nodesource.list \
|
||||
&& echo "deb [signed-by=/usr/share/keyrings/nodesource-archive-keyring.gpg] https://deb.nodesource.com/node_18.x jammy main" | tee /etc/apt/sources.list.d/nodesource.list \
|
||||
&& echo "deb-src [signed-by=/usr/share/keyrings/nodesource-archive-keyring.gpg] https://deb.nodesource.com/node_18.x jammy main" | tee -a /etc/apt/sources.list.d/nodesource.list \
|
||||
&& apt-get update && apt-get install -y nodejs \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& npm install -g yarn
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM node:14-alpine
|
||||
FROM node:18-alpine
|
||||
|
||||
WORKDIR /home/dbgate-docker
|
||||
|
||||
|
||||
@@ -5,9 +5,6 @@ let fillContent = '';
|
||||
if (process.platform == 'win32') {
|
||||
fillContent += `content.msnodesqlv8 = () => require('msnodesqlv8');`;
|
||||
}
|
||||
if (process.arch != 'arm64') {
|
||||
fillContent += `content.oracledb = () => require('oracledb');`;
|
||||
}
|
||||
fillContent += `content['better-sqlite3'] = () => require('better-sqlite3');`;
|
||||
|
||||
const getContent = empty => `
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const stableStringify = require('json-stable-stringify');
|
||||
const _ = require('lodash');
|
||||
const fp = require('lodash/fp');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const { testWrapper } = require('../tools');
|
||||
const engines = require('../engines');
|
||||
const { getAlterDatabaseScript, extendDatabaseInfo, generateDbPairingId } = require('dbgate-tools');
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const stableStringify = require('json-stable-stringify');
|
||||
const _ = require('lodash');
|
||||
const fp = require('lodash/fp');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const { testWrapper } = require('../tools');
|
||||
const engines = require('../engines');
|
||||
const crypto = require('crypto');
|
||||
const { getAlterTableScript, extendDatabaseInfo, generateDbPairingId } = require('dbgate-tools');
|
||||
|
||||
function pickImportantTableInfo(table) {
|
||||
@@ -76,7 +76,7 @@ describe('Alter table', () => {
|
||||
tbl.columns.push({
|
||||
columnName: 'added',
|
||||
dataType: 'int',
|
||||
pairingId: uuidv1(),
|
||||
pairingId: crypto.randomUUID(),
|
||||
notNull: false,
|
||||
autoIncrement: false,
|
||||
});
|
||||
|
||||
+2
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "5.2.8-beta.7",
|
||||
"version": "5.3.1",
|
||||
"name": "dbgate-all",
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
@@ -39,6 +39,7 @@
|
||||
"build:app:local": "yarn plugins:copydist && cd app && yarn build:local",
|
||||
"start:app:local": "cd app && yarn start:local",
|
||||
"setCurrentVersion": "node setCurrentVersion",
|
||||
"printSecrets": "node printSecrets",
|
||||
"generatePadFile": "node generatePadFile",
|
||||
"adjustPackageJson": "node adjustPackageJson",
|
||||
"fillNativeModules": "node fillNativeModules",
|
||||
|
||||
Vendored
+11
-3
@@ -1,6 +1,6 @@
|
||||
DEVMODE=1
|
||||
|
||||
CONNECTIONS=mysql,postgres,mongo,mongo2,mysqlssh,sqlite,relational
|
||||
CONNECTIONS=mysql,postgres,postgres1,mongo,mongo2,mysqlssh,sqlite,relational
|
||||
|
||||
LABEL_mysql=MySql localhost
|
||||
SERVER_mysql=localhost
|
||||
@@ -12,10 +12,18 @@ ENGINE_mysql=mysql@dbgate-plugin-mysql
|
||||
LABEL_postgres=Postgres localhost
|
||||
SERVER_postgres=localhost
|
||||
USER_postgres=postgres
|
||||
PASSWORD_postgres=test
|
||||
PORT_postgres=5433
|
||||
PASSWORD_postgres=Pwd2020Db
|
||||
PORT_postgres=5432
|
||||
ENGINE_postgres=postgres@dbgate-plugin-postgres
|
||||
|
||||
LABEL_postgres1=Postgres localhost test DB
|
||||
SERVER_postgres1=localhost
|
||||
USER_postgres1=postgres
|
||||
PASSWORD_postgres1=Pwd2020Db
|
||||
PORT_postgres1=5432
|
||||
ENGINE_postgres1=postgres@dbgate-plugin-postgres
|
||||
DATABASE_postgres1=test
|
||||
|
||||
LABEL_mongo=Mongo URL
|
||||
URL_mongo=mongodb://localhost:27017
|
||||
ENGINE_mongo=mongo@dbgate-plugin-mongo
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
"compare-versions": "^3.6.0",
|
||||
"cors": "^2.8.5",
|
||||
"cross-env": "^6.0.3",
|
||||
"dbgate-query-splitter": "^4.9.3",
|
||||
"dbgate-datalib": "^5.0.0-alpha.1",
|
||||
"dbgate-query-splitter": "^4.10.1",
|
||||
"dbgate-sqltree": "^5.0.0-alpha.1",
|
||||
"dbgate-tools": "^5.0.0-alpha.1",
|
||||
"dbgate-datalib": "^5.0.0-alpha.1",
|
||||
"debug": "^4.3.4",
|
||||
"diff": "^5.0.0",
|
||||
"diff2html": "^3.4.13",
|
||||
@@ -52,16 +52,15 @@
|
||||
"ncp": "^2.0.0",
|
||||
"node-cron": "^2.0.3",
|
||||
"on-finished": "^2.4.1",
|
||||
"pinomin": "^1.0.1",
|
||||
"pinomin": "^1.0.4",
|
||||
"portfinder": "^1.0.28",
|
||||
"rimraf": "^3.0.0",
|
||||
"simple-encryptor": "^4.0.0",
|
||||
"ssh2": "^1.11.0",
|
||||
"tar": "^6.0.5",
|
||||
"uuid": "^3.4.0"
|
||||
"tar": "^6.0.5"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "env-cmd node src/index.js --listen-api",
|
||||
"start": "env-cmd -f .env 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",
|
||||
@@ -79,12 +78,11 @@
|
||||
"node-loader": "^1.0.2",
|
||||
"nodemon": "^2.0.2",
|
||||
"typescript": "^4.4.3",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11"
|
||||
"webpack": "^5.91.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"better-sqlite3": "7.6.2",
|
||||
"msnodesqlv8": "^2.6.0",
|
||||
"oracledb": "^5.5.0"
|
||||
"better-sqlite3": "9.6.0",
|
||||
"msnodesqlv8": "^4.2.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const fs = require('fs-extra');
|
||||
const readline = require('readline');
|
||||
const crypto = require('crypto');
|
||||
const path = require('path');
|
||||
const { archivedir, clearArchiveLinksCache, resolveArchiveFolder } = require('../utility/directories');
|
||||
const socket = require('../utility/socket');
|
||||
const loadFilesRecursive = require('../utility/loadFilesRecursive');
|
||||
const getJslFileName = require('../utility/getJslFileName');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const dbgateApi = require('../shell');
|
||||
const jsldata = require('./jsldata');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
@@ -127,7 +127,7 @@ module.exports = {
|
||||
return true;
|
||||
}
|
||||
|
||||
const tmpchangedFilePath = path.join(resolveArchiveFolder(folder), `${file}-${uuidv1()}.jsonl`);
|
||||
const tmpchangedFilePath = path.join(resolveArchiveFolder(folder), `${file}-${crypto.randomUUID()}.jsonl`);
|
||||
const reader = await dbgateApi.modifyJsonLinesReader({
|
||||
fileName: changedFilePath,
|
||||
changeSet,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
const axios = require('axios');
|
||||
const jwt = require('jsonwebtoken');
|
||||
const getExpressPath = require('../utility/getExpressPath');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const { getLogins } = require('../utility/hasPermission');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const AD = require('activedirectory2').promiseWrapper;
|
||||
const crypto = require('crypto');
|
||||
|
||||
const logger = getLogger('auth');
|
||||
|
||||
const tokenSecret = uuidv1();
|
||||
const tokenSecret = crypto.randomUUID();
|
||||
|
||||
function shouldAuthorizeApi() {
|
||||
const logins = getLogins();
|
||||
@@ -90,6 +90,24 @@ module.exports = {
|
||||
) {
|
||||
return { error: `Username ${login} not allowed to log in` };
|
||||
}
|
||||
|
||||
const groups =
|
||||
process.env.OAUTH_GROUP_FIELD && payload && payload[process.env.OAUTH_GROUP_FIELD]
|
||||
? payload[process.env.OAUTH_GROUP_FIELD]
|
||||
: [];
|
||||
|
||||
const allowedGroups =
|
||||
process.env.OAUTH_ALLOWED_GROUPS
|
||||
? process.env.OAUTH_ALLOWED_GROUPS.split(',').map(group => group.toLowerCase().trim())
|
||||
: [];
|
||||
|
||||
if (
|
||||
process.env.OAUTH_ALLOWED_GROUPS &&
|
||||
!groups.some(group => allowedGroups.includes(group.toLowerCase().trim()))
|
||||
) {
|
||||
return { error: `Username ${login} does not belong to an allowed group` };
|
||||
}
|
||||
|
||||
if (access_token) {
|
||||
return {
|
||||
accessToken: jwt.sign({ login }, tokenSecret, { expiresIn: getTokenLifetime() }),
|
||||
@@ -137,7 +155,7 @@ module.exports = {
|
||||
return { error: 'Logins not configured' };
|
||||
}
|
||||
const foundLogin = logins.find(x => x.login == login);
|
||||
if (foundLogin && foundLogin.password == password) {
|
||||
if (foundLogin && foundLogin.password && foundLogin.password == password) {
|
||||
return {
|
||||
accessToken: jwt.sign({ login }, tokenSecret, { expiresIn: getTokenLifetime() }),
|
||||
};
|
||||
|
||||
@@ -61,6 +61,7 @@ function getPortalCollections() {
|
||||
useDatabaseUrl: !!process.env[`URL_${id}`],
|
||||
databaseFile: process.env[`FILE_${id}`],
|
||||
socketPath: process.env[`SOCKET_PATH_${id}`],
|
||||
serviceName: process.env[`SERVICE_NAME_${id}`],
|
||||
authType: process.env[`AUTH_TYPE_${id}`] || (process.env[`SOCKET_PATH_${id}`] ? 'socket' : undefined),
|
||||
defaultDatabase:
|
||||
process.env[`DATABASE_${id}`] ||
|
||||
@@ -88,6 +89,7 @@ function getPortalCollections() {
|
||||
sslCertFilePassword: process.env[`SSL_CERT_FILE_PASSWORD_${id}`],
|
||||
sslKeyFile: process.env[`SSL_KEY_FILE_${id}`],
|
||||
sslRejectUnauthorized: process.env[`SSL_REJECT_UNAUTHORIZED_${id}`],
|
||||
trustServerCertificate: process.env[`SSL_TRUST_CERTIFICATE_${id}`],
|
||||
}));
|
||||
|
||||
logger.info({ connections: connections.map(pickSafeConnectionInfo) }, 'Using connections from ENV variables');
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const connections = require('./connections');
|
||||
const archive = require('./archive');
|
||||
const socket = require('../utility/socket');
|
||||
@@ -30,6 +29,7 @@ const processArgs = require('../utility/processArgs');
|
||||
const { testConnectionPermission } = require('../utility/hasPermission');
|
||||
const { MissingCredentialsError } = require('../utility/exceptions');
|
||||
const pipeForkLogs = require('../utility/pipeForkLogs');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const logger = getLogger('databaseConnections');
|
||||
|
||||
@@ -137,7 +137,7 @@ module.exports = {
|
||||
|
||||
/** @param {import('dbgate-types').OpenedDatabaseConnection} conn */
|
||||
sendRequest(conn, message) {
|
||||
const msgid = uuidv1();
|
||||
const msgid = crypto.randomUUID();
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
this.requests[msgid] = [resolve, reject];
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
const { filesdir, archivedir, resolveArchiveFolder, uploadsdir, appdir } = require('../utility/directories');
|
||||
const getChartExport = require('../utility/getChartExport');
|
||||
const { hasPermission } = require('../utility/hasPermission');
|
||||
@@ -164,7 +164,7 @@ module.exports = {
|
||||
|
||||
generateUploadsFile_meta: true,
|
||||
async generateUploadsFile({ extension }) {
|
||||
const fileName = `${uuidv1()}.${extension || 'html'}`;
|
||||
const fileName = `${crypto.randomUUID()}.${extension || 'html'}`;
|
||||
return {
|
||||
fileName,
|
||||
filePath: path.join(uploadsdir(), fileName),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const crypto = require('crypto');
|
||||
const _ = require('lodash');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const byline = require('byline');
|
||||
const socket = require('../utility/socket');
|
||||
const { fork } = require('child_process');
|
||||
@@ -165,7 +165,7 @@ module.exports = {
|
||||
|
||||
start_meta: true,
|
||||
async start({ script }) {
|
||||
const runid = uuidv1();
|
||||
const runid = crypto.randomUUID()
|
||||
|
||||
if (script.type == 'json') {
|
||||
const js = jsonScriptToJavascript(script);
|
||||
@@ -213,7 +213,7 @@ module.exports = {
|
||||
loadReader_meta: true,
|
||||
async loadReader({ functionName, props }) {
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const runid = uuidv1();
|
||||
const runid = crypto.randomUUID();
|
||||
this.requests[runid] = [resolve, reject];
|
||||
this.startCore(runid, loaderScriptTemplate(functionName, props, runid));
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const connections = require('./connections');
|
||||
const socket = require('../utility/socket');
|
||||
const { fork } = require('child_process');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const _ = require('lodash');
|
||||
const AsyncLock = require('async-lock');
|
||||
const { handleProcessCommunication } = require('../utility/processComm');
|
||||
@@ -152,7 +151,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
ping_meta: true,
|
||||
async ping({ conidArray }) {
|
||||
async ping({ conidArray, strmid }) {
|
||||
await Promise.all(
|
||||
_.uniq(conidArray).map(async conid => {
|
||||
const last = this.lastPinged[conid];
|
||||
@@ -169,6 +168,7 @@ module.exports = {
|
||||
}
|
||||
})
|
||||
);
|
||||
socket.setStreamIdFilter(strmid, { conid: conidArray });
|
||||
return { status: 'ok' };
|
||||
},
|
||||
|
||||
@@ -200,7 +200,7 @@ module.exports = {
|
||||
},
|
||||
|
||||
sendRequest(conn, message) {
|
||||
const msgid = uuidv1();
|
||||
const msgid = crypto.randomUUID();
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
this.requests[msgid] = [resolve, reject];
|
||||
try {
|
||||
@@ -240,4 +240,20 @@ module.exports = {
|
||||
if (opened.connection.isReadOnly) return false;
|
||||
return this.loadDataCore('summaryCommand', { conid, command, row });
|
||||
},
|
||||
|
||||
getOpenedConnectionReport() {
|
||||
return this.opened.map(con => ({
|
||||
status: con.status,
|
||||
versionText: con.version?.versionText,
|
||||
databaseCount: con.databases.length,
|
||||
connection: _.pick(con.connection, [
|
||||
'engine',
|
||||
'useSshTunnel',
|
||||
'authType',
|
||||
'trustServerCertificate',
|
||||
'useSsl',
|
||||
'sshMode',
|
||||
]),
|
||||
}));
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const crypto = require('crypto');
|
||||
const _ = require('lodash');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const connections = require('./connections');
|
||||
const socket = require('../utility/socket');
|
||||
const { fork } = require('child_process');
|
||||
@@ -85,7 +85,7 @@ module.exports = {
|
||||
|
||||
create_meta: true,
|
||||
async create({ conid, database }) {
|
||||
const sesid = uuidv1();
|
||||
const sesid = crypto.randomUUID();
|
||||
const connection = await connections.getCore({ conid });
|
||||
const subprocess = fork(
|
||||
global['API_PACKAGE'] || process.argv[1],
|
||||
@@ -149,7 +149,7 @@ module.exports = {
|
||||
const { sesid } = await this.create({ conid, database });
|
||||
const session = this.opened.find(x => x.sesid == sesid);
|
||||
session.killOnDone = true;
|
||||
const jslid = uuidv1();
|
||||
const jslid = crypto.randomUUID();
|
||||
session.loadingReader_jslid = jslid;
|
||||
const fileName = queryName && appFolder ? path.join(appdir(), appFolder, `${queryName}.query.sql`) : null;
|
||||
|
||||
@@ -169,7 +169,7 @@ module.exports = {
|
||||
|
||||
startProfiler_meta: true,
|
||||
async startProfiler({ sesid }) {
|
||||
const jslid = uuidv1();
|
||||
const jslid = crypto.randomUUID();
|
||||
const session = this.opened.find(x => x.sesid == sesid);
|
||||
if (!session) {
|
||||
throw new Error('Invalid session');
|
||||
|
||||
@@ -1,8 +1,18 @@
|
||||
const crypto = require('crypto');
|
||||
const path = require('path');
|
||||
const { uploadsdir } = require('../utility/directories');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const { uploadsdir, getLogsFilePath } = require('../utility/directories');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const logger = getLogger('uploads');
|
||||
const axios = require('axios');
|
||||
const os = require('os');
|
||||
const fs = require('fs/promises');
|
||||
const { read } = require('./queryHistory');
|
||||
const platformInfo = require('../utility/platformInfo');
|
||||
const _ = require('lodash');
|
||||
const serverConnections = require('./serverConnections');
|
||||
const config = require('./config');
|
||||
const gistSecret = require('../gistSecret');
|
||||
const currentVersion = require('../currentVersion');
|
||||
|
||||
module.exports = {
|
||||
upload_meta: {
|
||||
@@ -15,7 +25,7 @@ module.exports = {
|
||||
res.json(null);
|
||||
return;
|
||||
}
|
||||
const uploadName = uuidv1();
|
||||
const uploadName = crypto.randomUUID();
|
||||
const filePath = path.join(uploadsdir(), uploadName);
|
||||
logger.info(`Uploading file ${data.name}, size=${data.size}`);
|
||||
|
||||
@@ -35,4 +45,89 @@ module.exports = {
|
||||
get(req, res) {
|
||||
res.sendFile(path.join(uploadsdir(), req.query.file));
|
||||
},
|
||||
|
||||
async getGistToken() {
|
||||
const settings = await config.getSettings();
|
||||
|
||||
return settings['other.gistCreateToken'] || gistSecret;
|
||||
},
|
||||
|
||||
uploadErrorToGist_meta: true,
|
||||
async uploadErrorToGist() {
|
||||
const logs = await fs.readFile(getLogsFilePath(), { encoding: 'utf-8' });
|
||||
const connections = await serverConnections.getOpenedConnectionReport();
|
||||
try {
|
||||
const response = await axios.default.post(
|
||||
'https://api.github.com/gists',
|
||||
{
|
||||
description: `DbGate ${currentVersion.version} error report`,
|
||||
public: false,
|
||||
files: {
|
||||
'logs.jsonl': {
|
||||
content: logs,
|
||||
},
|
||||
'os.json': {
|
||||
content: JSON.stringify(
|
||||
{
|
||||
release: os.release(),
|
||||
arch: os.arch(),
|
||||
machine: os.machine(),
|
||||
platform: os.platform(),
|
||||
type: os.type(),
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
},
|
||||
'platform.json': {
|
||||
content: JSON.stringify(
|
||||
_.omit(
|
||||
{
|
||||
...platformInfo,
|
||||
},
|
||||
['defaultKeyfile', 'sshAuthSock']
|
||||
),
|
||||
null,
|
||||
2
|
||||
),
|
||||
},
|
||||
'connections.json': {
|
||||
content: JSON.stringify(connections, null, 2),
|
||||
},
|
||||
'version.json': {
|
||||
content: JSON.stringify(currentVersion, null, 2),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
Authorization: `token ${await this.getGistToken()}`,
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
return response.data;
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error uploading gist');
|
||||
|
||||
return {
|
||||
apiErrorMessage: err.message,
|
||||
};
|
||||
// console.error('Error creating gist:', error.response ? error.response.data : error.message);
|
||||
}
|
||||
},
|
||||
|
||||
deleteGist_meta: true,
|
||||
async deleteGist({ url }) {
|
||||
const response = await axios.default.delete(url, {
|
||||
headers: {
|
||||
Authorization: `token ${await this.getGistToken()}`,
|
||||
'Content-Type': 'application/json',
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
},
|
||||
});
|
||||
return true;
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
module.exports = process.env.GIST_UPLOAD_SECRET;
|
||||
@@ -1,4 +1,4 @@
|
||||
const { setLogger, getLogger, setLoggerName } = require('dbgate-tools');
|
||||
const { setLogConfig, getLogger, setLoggerName } = require('dbgate-tools');
|
||||
const processArgs = require('./utility/processArgs');
|
||||
const fs = require('fs');
|
||||
const moment = require('moment');
|
||||
@@ -30,22 +30,27 @@ function configureLogger() {
|
||||
setLogsFilePath(logsFilePath);
|
||||
setLoggerName('main');
|
||||
|
||||
const logger = createLogger({
|
||||
const consoleLogLevel = process.env.CONSOLE_LOG_LEVEL || process.env.LOG_LEVEL || 'info';
|
||||
const fileLogLevel = process.env.FILE_LOG_LEVEL || process.env.LOG_LEVEL || 'debug';
|
||||
|
||||
const logConfig = {
|
||||
base: { pid: process.pid },
|
||||
targets: [
|
||||
{
|
||||
type: 'console',
|
||||
// @ts-ignore
|
||||
level: process.env.CONSOLE_LOG_LEVEL || process.env.LOG_LEVEL || 'info',
|
||||
level: consoleLogLevel,
|
||||
},
|
||||
{
|
||||
type: 'stream',
|
||||
// @ts-ignore
|
||||
level: process.env.FILE_LOG_LEVEL || process.env.LOG_LEVEL || 'info',
|
||||
level: fileLogLevel,
|
||||
stream: fs.createWriteStream(logsFilePath, { flags: 'a' }),
|
||||
},
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
// logger.info(`Initialized logging, console log level: ${consoleLogLevel}, file log level: ${fileLogLevel}`);
|
||||
|
||||
// const streams = [];
|
||||
// if (!platformInfo.isElectron) {
|
||||
@@ -83,14 +88,15 @@ function configureLogger() {
|
||||
// },
|
||||
// });
|
||||
|
||||
setLogger(logger);
|
||||
// @ts-ignore
|
||||
setLogConfig(logConfig);
|
||||
}
|
||||
|
||||
if (processArgs.listenApi) {
|
||||
configureLogger();
|
||||
}
|
||||
|
||||
const shell = require('./shell');
|
||||
const shell = require('./shell/index');
|
||||
const dbgateTools = require('dbgate-tools');
|
||||
|
||||
global['DBGATE_TOOLS'] = dbgateTools;
|
||||
|
||||
@@ -48,7 +48,7 @@ function start() {
|
||||
if (logins && process.env.BASIC_AUTH) {
|
||||
app.use(
|
||||
basicAuth({
|
||||
users: _.fromPairs(logins.map(x => [x.login, x.password])),
|
||||
users: _.fromPairs(logins.filter(x => x.password).map(x => [x.login, x.password])),
|
||||
challenge: true,
|
||||
realm: 'DbGate Web App',
|
||||
})
|
||||
@@ -77,6 +77,7 @@ function start() {
|
||||
}
|
||||
|
||||
app.get(getExpressPath('/stream'), async function (req, res) {
|
||||
const strmid = req.query.strmid;
|
||||
res.set({
|
||||
'Cache-Control': 'no-cache',
|
||||
'Content-Type': 'text/event-stream',
|
||||
@@ -87,9 +88,9 @@ function start() {
|
||||
|
||||
// Tell the client to retry every 10 seconds if connectivity is lost
|
||||
res.write('retry: 10000\n\n');
|
||||
socket.addSseResponse(res);
|
||||
socket.addSseResponse(res, strmid);
|
||||
onFinished(req, () => {
|
||||
socket.removeSseResponse(res);
|
||||
socket.removeSseResponse(strmid);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const crypto = require('crypto');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const _ = require('lodash');
|
||||
@@ -31,7 +31,7 @@ class TableWriter {
|
||||
}
|
||||
|
||||
initializeFromQuery(structure, resultIndex) {
|
||||
this.jslid = uuidv1();
|
||||
this.jslid = crypto.randomUUID();
|
||||
this.currentFile = path.join(jsldir(), `${this.jslid}.jsonl`);
|
||||
fs.writeFileSync(
|
||||
this.currentFile,
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
const crypto = require('crypto');
|
||||
const path = require('path');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const { uploadsdir } = require('../utility/directories');
|
||||
const { downloadFile } = require('../utility/downloader');
|
||||
|
||||
async function download(url) {
|
||||
if (url && url.match(/(^http:\/\/)|(^https:\/\/)/)) {
|
||||
const tmpFile = path.join(uploadsdir(), uuidv1());
|
||||
const tmpFile = path.join(uploadsdir(), crypto.randomUUID());
|
||||
await downloadFile(url, tmpFile);
|
||||
return tmpFile;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const crypto = require('crypto');
|
||||
const { fork } = require('child_process');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const { handleProcessCommunication } = require('./processComm');
|
||||
const processArgs = require('../utility/processArgs');
|
||||
const pipeForkLogs = require('./pipeForkLogs');
|
||||
@@ -67,7 +67,7 @@ class DatastoreProxy {
|
||||
|
||||
async getRows(offset, limit) {
|
||||
await this.ensureSubprocess();
|
||||
const msgid = uuidv1();
|
||||
const msgid = crypto.randomUUID();
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
this.requests[msgid] = [resolve, reject];
|
||||
try {
|
||||
@@ -81,7 +81,7 @@ class DatastoreProxy {
|
||||
}
|
||||
|
||||
async notifyChangedCore() {
|
||||
const msgid = uuidv1();
|
||||
const msgid = crypto.randomUUID();
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
this.requests[msgid] = [resolve, reject];
|
||||
try {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
const crypto = require('crypto');
|
||||
const AsyncLock = require('async-lock');
|
||||
const fs = require('fs-extra');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
|
||||
const lock = new AsyncLock();
|
||||
|
||||
@@ -57,7 +57,7 @@ class JsonLinesDatabase {
|
||||
? obj
|
||||
: {
|
||||
...obj,
|
||||
_id: uuidv1(),
|
||||
_id: crypto.randomUUID(),
|
||||
};
|
||||
this.data.push(elem);
|
||||
await this._save();
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const crypto = require('crypto');
|
||||
const fs = require('fs');
|
||||
const os = require('os');
|
||||
const rimraf = require('rimraf');
|
||||
@@ -8,7 +9,6 @@ const stableStringify = require('json-stable-stringify');
|
||||
const { evaluateCondition } = require('dbgate-sqltree');
|
||||
const requirePluginFunction = require('./requirePluginFunction');
|
||||
const esort = require('external-sorting');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const { jsldir } = require('./directories');
|
||||
const LineReader = require('./LineReader');
|
||||
|
||||
@@ -28,7 +28,7 @@ class JsonLinesDatastore {
|
||||
}
|
||||
|
||||
static async sortFile(infile, outfile, sort) {
|
||||
const tempDir = path.join(os.tmpdir(), uuidv1());
|
||||
const tempDir = path.join(os.tmpdir(), crypto.randomUUID());
|
||||
fs.mkdirSync(tempDir);
|
||||
|
||||
await esort
|
||||
@@ -210,7 +210,7 @@ class JsonLinesDatastore {
|
||||
async getRows(offset, limit, filter, sort) {
|
||||
const res = [];
|
||||
if (sort && !this.sortedFiles[stableStringify(sort)]) {
|
||||
const jslid = uuidv1();
|
||||
const jslid = crypto.randomUUID();
|
||||
const sortedFile = path.join(jsldir(), `${jslid}.jsonl`);
|
||||
await JsonLinesDatastore.sortFile(this.file, sortedFile, sort);
|
||||
this.sortedFiles[stableStringify(sort)] = sortedFile;
|
||||
|
||||
@@ -52,7 +52,7 @@ async function connectUtility(driver, storedConnection, connectionMode, addition
|
||||
throw new Error(tunnel.message);
|
||||
}
|
||||
|
||||
connection.server = '127.0.0.1';
|
||||
connection.server = 'localhost';
|
||||
connection.port = tunnel.localPort;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const crypto = require('crypto');
|
||||
// const pacote = require('pacote');
|
||||
const axios = require('axios');
|
||||
// const tarballExtract = require('tarball-extract');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const zlib = require('zlib');
|
||||
@@ -38,9 +38,9 @@ async function downloadPackage(packageName, directory) {
|
||||
|
||||
const tarball = infoResp.data.versions[latest].dist.tarball;
|
||||
|
||||
const tmpFile = path.join(uploadsdir(), uuidv1() + '.tgz');
|
||||
const tmpFile = path.join(uploadsdir(), crypto.randomUUID() + '.tgz');
|
||||
await downloadFile(tarball, tmpFile);
|
||||
const tmpDir = path.join(uploadsdir(), uuidv1());
|
||||
const tmpDir = path.join(uploadsdir(), crypto.randomUUID());
|
||||
fs.mkdirSync(tmpDir);
|
||||
await extractTarball(tmpFile, tmpDir);
|
||||
await copyDirectory(path.join(tmpDir, 'package'), directory);
|
||||
|
||||
@@ -9,7 +9,8 @@ function hasPermission(tested, req) {
|
||||
return true;
|
||||
}
|
||||
const { user } = (req && req.auth) || {};
|
||||
const key = user || '';
|
||||
const { login } = (process.env.OAUTH_PERMISSIONS && req && req.user) || {};
|
||||
const key = user || login || '';
|
||||
const logins = getLogins();
|
||||
|
||||
if (!userPermissions[key]) {
|
||||
@@ -53,6 +54,14 @@ function getLogins() {
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (process.env.OAUTH_PERMISSIONS) {
|
||||
const login_permission_keys = Object.keys(process.env).filter((key) => _.startsWith(key, 'LOGIN_PERMISSIONS_'))
|
||||
for (const permissions_key of login_permission_keys) {
|
||||
const login = permissions_key.replace('LOGIN_PERMISSIONS_', '');
|
||||
const permissions = process.env[permissions_key];
|
||||
userPermissions[login] = compilePermissions(permissions);
|
||||
}
|
||||
}
|
||||
|
||||
loginsCache = res.length > 0 ? res : null;
|
||||
loginsLoaded = true;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const _ = require('lodash');
|
||||
const stableStringify = require('json-stable-stringify');
|
||||
|
||||
const sseResponses = [];
|
||||
const sseResponses = {};
|
||||
let electronSender = null;
|
||||
let pingConfigured = false;
|
||||
|
||||
@@ -12,12 +12,15 @@ module.exports = {
|
||||
pingConfigured = true;
|
||||
}
|
||||
},
|
||||
addSseResponse(value) {
|
||||
sseResponses.push(value);
|
||||
addSseResponse(value, strmid) {
|
||||
sseResponses[strmid] = {
|
||||
...sseResponses[strmid],
|
||||
response: value,
|
||||
};
|
||||
this.ensurePing();
|
||||
},
|
||||
removeSseResponse(value) {
|
||||
_.remove(sseResponses, x => x == value);
|
||||
removeSseResponse(strmid) {
|
||||
delete sseResponses[strmid];
|
||||
},
|
||||
setElectronSender(value) {
|
||||
electronSender = value;
|
||||
@@ -27,8 +30,25 @@ module.exports = {
|
||||
if (electronSender) {
|
||||
electronSender.send(message, data == null ? null : data);
|
||||
}
|
||||
for (const res of sseResponses) {
|
||||
res.write(`event: ${message}\ndata: ${stableStringify(data == null ? null : data)}\n\n`);
|
||||
for (const strmid in sseResponses) {
|
||||
let skipThisStream = false;
|
||||
if (sseResponses[strmid].filter) {
|
||||
for (const key in sseResponses[strmid].filter) {
|
||||
if (data && data[key]) {
|
||||
if (!sseResponses[strmid].filter[key].includes(data[key])) {
|
||||
skipThisStream = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (skipThisStream) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sseResponses[strmid].response?.write(
|
||||
`event: ${message}\ndata: ${stableStringify(data == null ? null : data)}\n\n`
|
||||
);
|
||||
}
|
||||
},
|
||||
emitChanged(key, params = undefined) {
|
||||
@@ -36,4 +56,10 @@ module.exports = {
|
||||
this.emit('changed-cache', { key, ...params });
|
||||
// this.emit(key);
|
||||
},
|
||||
setStreamIdFilter(strmid, filter) {
|
||||
sseResponses[strmid] = {
|
||||
...sseResponses[strmid],
|
||||
filter,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const crypto = require('crypto');
|
||||
const { getLogger } = require('dbgate-tools');
|
||||
const uuidv1 = require('uuid/v1');
|
||||
const { getSshTunnel } = require('./sshTunnel');
|
||||
const logger = getLogger('sshTunnelProxy');
|
||||
|
||||
@@ -22,7 +22,7 @@ function handleGetSshTunnelResponse({ msgid, response }, subprocess) {
|
||||
|
||||
async function getSshTunnelProxy(connection) {
|
||||
if (!process.send) return getSshTunnel(connection);
|
||||
const msgid = uuidv1();
|
||||
const msgid = crypto.randomUUID();
|
||||
process.send({ msgtype: 'getsshtunnel-request', msgid, connection });
|
||||
return new Promise((resolve, reject) => {
|
||||
dispatchedMessages[msgid] = { resolve, reject };
|
||||
|
||||
@@ -41,6 +41,6 @@
|
||||
"dbgate-plugin-oracle": "^5.0.0-alpha.1",
|
||||
"dbgate-web": "^5.0.0-alpha.1",
|
||||
"dotenv": "^16.0.0",
|
||||
"pinomin": "^1.0.1"
|
||||
"pinomin": "^1.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,12 @@
|
||||
"typescript": "^4.4.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"dbgate-query-splitter": "^4.9.3",
|
||||
"dbgate-query-splitter": "^4.10.1",
|
||||
"dbgate-sqltree": "^5.0.0-alpha.1",
|
||||
"debug": "^4.3.4",
|
||||
"json-stable-stringify": "^1.0.1",
|
||||
"lodash": "^4.17.21",
|
||||
"pinomin": "^1.0.1",
|
||||
"pinomin": "^1.0.4",
|
||||
"uuid": "^3.4.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import _groupBy from 'lodash/groupBy';
|
||||
import _pick from 'lodash/pick';
|
||||
import _compact from 'lodash/compact';
|
||||
import { getLogger } from './getLogger';
|
||||
import { type Logger } from 'pinomin';
|
||||
|
||||
const logger = getLogger('dbAnalyser');
|
||||
|
||||
@@ -37,9 +38,11 @@ export class DatabaseAnalyser {
|
||||
singleObjectFilter: any;
|
||||
singleObjectId: string = null;
|
||||
dialect: SqlDialect;
|
||||
logger: Logger;
|
||||
|
||||
constructor(public pool, public driver: EngineDriver, version) {
|
||||
this.dialect = (driver?.dialectByVersion && driver?.dialectByVersion(version)) || driver?.dialect;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
async _runAnalysis() {
|
||||
@@ -177,8 +180,15 @@ export class DatabaseAnalyser {
|
||||
// return this.createQueryCore('=OBJECT_ID_CONDITION', typeFields) != ' is not null';
|
||||
// }
|
||||
|
||||
createQuery(template, typeFields) {
|
||||
return this.createQueryCore(template, typeFields);
|
||||
createQuery(template, typeFields, replacements = {}) {
|
||||
return this.createQueryCore(this.processQueryReplacements(template, replacements), typeFields);
|
||||
}
|
||||
|
||||
processQueryReplacements(query, replacements) {
|
||||
for (const repl in replacements) {
|
||||
query = query.replaceAll(repl, replacements[repl]);
|
||||
}
|
||||
return query;
|
||||
}
|
||||
|
||||
createQueryCore(template, typeFields) {
|
||||
@@ -236,9 +246,9 @@ export class DatabaseAnalyser {
|
||||
this.pool.feedback(obj);
|
||||
}
|
||||
if (obj && obj.analysingMessage) {
|
||||
logger.debug(obj.analysingMessage);
|
||||
logger.debug(obj.analysingMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async getModifications() {
|
||||
const snapshot = await this._getFastSnapshot();
|
||||
@@ -299,8 +309,8 @@ export class DatabaseAnalyser {
|
||||
return [..._compact(res), ...this.getDeletedObjects(snapshot)];
|
||||
}
|
||||
|
||||
async analyserQuery(template, typeFields) {
|
||||
const sql = this.createQuery(template, typeFields);
|
||||
async analyserQuery(template, typeFields, replacements = {}) {
|
||||
const sql = this.createQuery(template, typeFields, replacements);
|
||||
|
||||
if (!sql) {
|
||||
return {
|
||||
@@ -308,7 +318,9 @@ export class DatabaseAnalyser {
|
||||
};
|
||||
}
|
||||
try {
|
||||
return await this.driver.query(this.pool, sql);
|
||||
const res = await this.driver.query(this.pool, sql);
|
||||
this.logger.debug({ rows: res.rows.length, template }, `Loaded analyser query`);
|
||||
return res;
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error running analyser query');
|
||||
return {
|
||||
|
||||
@@ -199,14 +199,8 @@ export class SqlDumper implements AlterProcessor {
|
||||
|
||||
selectScopeIdentity(table: TableInfo) {}
|
||||
|
||||
columnDefinition(column: ColumnInfo, { includeDefault = true, includeNullable = true, includeCollate = true } = {}) {
|
||||
if (column.computedExpression) {
|
||||
this.put('^as %s', column.computedExpression);
|
||||
if (column.isPersisted) this.put(' ^persisted');
|
||||
return;
|
||||
}
|
||||
|
||||
const type = column.dataType || this.dialect.fallbackDataType;
|
||||
columnType(dataType: string) {
|
||||
const type = dataType || this.dialect.fallbackDataType;
|
||||
const typeWithValues = type.match(/([^(]+)(\(.+[^)]\))/);
|
||||
|
||||
if (typeWithValues?.length) {
|
||||
@@ -217,6 +211,17 @@ export class SqlDumper implements AlterProcessor {
|
||||
this.putRaw(SqlDumper.convertKeywordCase(type));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
columnDefinition(column: ColumnInfo, { includeDefault = true, includeNullable = true, includeCollate = true } = {}) {
|
||||
if (column.computedExpression) {
|
||||
this.put('^as %s', column.computedExpression);
|
||||
if (column.isPersisted) this.put(' ^persisted');
|
||||
return;
|
||||
}
|
||||
|
||||
this.columnType(column.dataType);
|
||||
|
||||
if (column.autoIncrement) {
|
||||
this.autoIncrement();
|
||||
}
|
||||
|
||||
@@ -56,24 +56,42 @@ export function createBulkInsertStreamBase(driver: EngineDriver, stream, pool, n
|
||||
const rows = writable.buffer;
|
||||
writable.buffer = [];
|
||||
|
||||
const dmp = driver.createDumper();
|
||||
if (driver.dialect.allowMultipleValuesInsert) {
|
||||
const dmp = driver.createDumper();
|
||||
dmp.putRaw(`INSERT INTO ${fullNameQuoted} (`);
|
||||
dmp.putCollection(',', writable.columnNames, col => dmp.putRaw(driver.dialect.quoteIdentifier(col as string)));
|
||||
dmp.putRaw(')\n VALUES\n');
|
||||
|
||||
dmp.putRaw(`INSERT INTO ${fullNameQuoted} (`);
|
||||
dmp.putCollection(',', writable.columnNames, col => dmp.putRaw(driver.dialect.quoteIdentifier(col as string)));
|
||||
dmp.putRaw(')\n VALUES\n');
|
||||
let wasRow = false;
|
||||
for (const row of rows) {
|
||||
if (wasRow) dmp.putRaw(',\n');
|
||||
dmp.putRaw('(');
|
||||
dmp.putCollection(',', writable.columnNames, col => dmp.putValue(row[col as string]));
|
||||
dmp.putRaw(')');
|
||||
wasRow = true;
|
||||
}
|
||||
dmp.putRaw(';');
|
||||
// require('fs').writeFileSync('/home/jena/test.sql', dmp.s);
|
||||
// console.log(dmp.s);
|
||||
await driver.query(pool, dmp.s, { discardResult: true });
|
||||
} else {
|
||||
for (const row of rows) {
|
||||
const dmp = driver.createDumper();
|
||||
dmp.putRaw(`INSERT INTO ${fullNameQuoted} (`);
|
||||
dmp.putCollection(',', writable.columnNames, col => dmp.putRaw(driver.dialect.quoteIdentifier(col as string)));
|
||||
dmp.putRaw(')\n VALUES\n');
|
||||
|
||||
let wasRow = false;
|
||||
for (const row of rows) {
|
||||
if (wasRow) dmp.putRaw(',\n');
|
||||
dmp.putRaw('(');
|
||||
dmp.putCollection(',', writable.columnNames, col => dmp.putValue(row[col as string]));
|
||||
dmp.putRaw(')');
|
||||
wasRow = true;
|
||||
dmp.putRaw('(');
|
||||
dmp.putCollection(',', writable.columnNames, col => dmp.putValue(row[col as string]));
|
||||
dmp.putRaw(')');
|
||||
await driver.query(pool, dmp.s, { discardResult: true });
|
||||
}
|
||||
}
|
||||
if (options.commitAfterInsert) {
|
||||
const dmp = driver.createDumper();
|
||||
dmp.commitTransaction();
|
||||
await driver.query(pool, dmp.s, { discardResult: true });
|
||||
}
|
||||
dmp.putRaw(';');
|
||||
// require('fs').writeFileSync('/home/jena/test.sql', dmp.s);
|
||||
// console.log(dmp.s);
|
||||
await driver.query(pool, dmp.s, { discardResult: true });
|
||||
};
|
||||
|
||||
writable.sendIfFull = async () => {
|
||||
|
||||
@@ -1,27 +1,40 @@
|
||||
import pinomin, { Logger } from 'pinomin';
|
||||
import pinomin, { Logger, type LogConfig } from 'pinomin';
|
||||
|
||||
let _logger: Logger;
|
||||
let _logConfig: LogConfig;
|
||||
let _name: string = null;
|
||||
const defaultLogger: Logger = pinomin({
|
||||
|
||||
const defaultLogConfig: LogConfig = {
|
||||
base: { pid: global?.process?.pid },
|
||||
targets: [{ type: 'console', level: 'info' }],
|
||||
});
|
||||
};
|
||||
|
||||
export function setLogger(value: Logger) {
|
||||
_logger = value;
|
||||
export function setLogConfig(value: LogConfig) {
|
||||
_logConfig = value;
|
||||
}
|
||||
export function setLoggerName(value) {
|
||||
_name = value;
|
||||
}
|
||||
|
||||
export function getLogger(caller?: string): Logger {
|
||||
let res = _logger || defaultLogger;
|
||||
if (caller) {
|
||||
const props = { caller };
|
||||
if (_name) {
|
||||
props['name'] = _name;
|
||||
}
|
||||
res = res.child(props);
|
||||
}
|
||||
return res;
|
||||
return pinomin({
|
||||
getConfig: () => {
|
||||
const config = _logConfig || defaultLogConfig;
|
||||
|
||||
if (caller) {
|
||||
const props = { caller };
|
||||
if (_name) {
|
||||
props['name'] = _name;
|
||||
}
|
||||
return {
|
||||
...config,
|
||||
base: {
|
||||
...config.base,
|
||||
...props,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return config;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,6 +2,10 @@ import type { DatabaseInfo, TableInfo, ApplicationDefinition, ViewInfo, Collecti
|
||||
import _flatten from 'lodash/flatten';
|
||||
|
||||
export function addTableDependencies(db: DatabaseInfo): DatabaseInfo {
|
||||
if (!db.tables) {
|
||||
return db;
|
||||
}
|
||||
|
||||
const allForeignKeys = _flatten(db.tables.map(x => x.foreignKeys || []));
|
||||
return {
|
||||
...db,
|
||||
|
||||
Vendored
+1
@@ -12,6 +12,7 @@ export interface SqlDialect {
|
||||
defaultSchemaName?: string;
|
||||
enableConstraintsPerTable?: boolean;
|
||||
requireStandaloneSelectForScopeIdentity?: boolean;
|
||||
allowMultipleValuesInsert?: boolean;
|
||||
|
||||
dropColumnDependencies?: string[];
|
||||
changeColumnDependencies?: string[];
|
||||
|
||||
Vendored
+1
@@ -24,6 +24,7 @@ export interface WriteTableOptions {
|
||||
dropIfExists?: boolean;
|
||||
truncate?: boolean;
|
||||
createIfNotExists?: boolean;
|
||||
commitAfterInsert?: boolean;
|
||||
}
|
||||
|
||||
export interface EngineAuthType {
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
"@rollup/plugin-typescript": "^8.2.5",
|
||||
"@tsconfig/svelte": "^1.0.0",
|
||||
"ace-builds": "^1.4.8",
|
||||
"chart.js": "^3.6.0",
|
||||
"chart.js": "^4.4.2",
|
||||
"chartjs-adapter-moment": "^1.0.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"dbgate-datalib": "^5.0.0-alpha.1",
|
||||
"dbgate-query-splitter": "^4.9.3",
|
||||
"dbgate-query-splitter": "^4.10.1",
|
||||
"dbgate-sqltree": "^5.0.0-alpha.1",
|
||||
"dbgate-tools": "^5.0.0-alpha.1",
|
||||
"dbgate-types": "^5.0.0-alpha.1",
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
return [
|
||||
{ text: 'Rename column', onClick: handleRenameColumn },
|
||||
{ text: 'Drop column', onClick: handleDropColumn },
|
||||
{ text: 'Copy name', onClick: () => navigator.clipboard.writeText(data.columnName)},
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts" context="module">
|
||||
import {copyTextToClipboard} from "../utility/clipboard";
|
||||
|
||||
export const extractKey = props => props.name;
|
||||
|
||||
export function disconnectDatabaseConnection(conid, database, showConfirmation = true) {
|
||||
@@ -169,6 +171,10 @@
|
||||
);
|
||||
};
|
||||
|
||||
const handleCopyName = async () => {
|
||||
copyTextToClipboard(name);
|
||||
}
|
||||
|
||||
const handleDisconnect = () => {
|
||||
disconnectDatabaseConnection(connection._id, name);
|
||||
};
|
||||
@@ -295,6 +301,7 @@
|
||||
!connection.isReadOnly &&
|
||||
!connection.singleDatabase && { onClick: handleDropDatabase, text: 'Drop database' },
|
||||
{ divider: true },
|
||||
driver?.databaseEngineTypes?.includes('sql') && { onClick: handleCopyName, text: 'Copy database name' },
|
||||
driver?.databaseEngineTypes?.includes('sql') && { onClick: handleShowDiagram, text: 'Show diagram' },
|
||||
driver?.databaseEngineTypes?.includes('sql') && { onClick: handleSqlGenerator, text: 'SQL Generator' },
|
||||
driver?.supportsDatabaseProfiler && { onClick: handleDatabaseProfiler, text: 'Database profiler' },
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<script lang="ts" context="module">
|
||||
import {copyTextToClipboard} from "../utility/clipboard";
|
||||
|
||||
export const extractKey = ({ schemaName, pureName }) => (schemaName ? `${schemaName}.${pureName}` : pureName);
|
||||
export const createMatcher =
|
||||
({ schemaName, pureName, columns }) =>
|
||||
@@ -80,6 +82,11 @@
|
||||
isTruncate: true,
|
||||
requiresWriteAccess: true,
|
||||
},
|
||||
{
|
||||
label: 'Copy table name',
|
||||
isCopyTableName: true,
|
||||
requiresWriteAccess: false
|
||||
},
|
||||
{
|
||||
label: 'Create table backup',
|
||||
isDuplicateTable: true,
|
||||
@@ -511,6 +518,8 @@
|
||||
saveScriptToDatabase(dbid, `db.dropCollection('${data.pureName}')`);
|
||||
},
|
||||
});
|
||||
} else if (menu.isCopyTableName) {
|
||||
copyTextToClipboard(data.pureName);
|
||||
} else if (menu.isRenameCollection) {
|
||||
showModal(InputTextModal, {
|
||||
label: 'New collection name',
|
||||
|
||||
@@ -38,6 +38,7 @@ import { getSettings } from '../utility/metadataLoaders';
|
||||
import { isMac } from '../utility/common';
|
||||
import { doLogout, internalRedirectTo } from '../clientAuth';
|
||||
import { disconnectServerConnection } from '../appobj/ConnectionAppObject.svelte';
|
||||
import UploadErrorModal from '../modals/UploadErrorModal.svelte';
|
||||
|
||||
// function themeCommand(theme: ThemeDefinition) {
|
||||
// return {
|
||||
@@ -575,6 +576,7 @@ export function registerFileCommands({
|
||||
findReplace = false,
|
||||
undoRedo = false,
|
||||
executeAdditionalCondition = null,
|
||||
copyPaste = false,
|
||||
}) {
|
||||
if (save) {
|
||||
registerCommand({
|
||||
@@ -645,6 +647,25 @@ export function registerFileCommands({
|
||||
});
|
||||
}
|
||||
|
||||
if (copyPaste) {
|
||||
registerCommand({
|
||||
id: idPrefix + '.copy',
|
||||
category,
|
||||
name: 'Copy',
|
||||
disableHandleKeyText: 'CtrlOrCommand+C',
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => getCurrentEditor().copy(),
|
||||
});
|
||||
registerCommand({
|
||||
id: idPrefix + '.paste',
|
||||
category,
|
||||
name: 'Paste',
|
||||
disableHandleKeyText: 'CtrlOrCommand+V',
|
||||
testEnabled: () => getCurrentEditor() != null,
|
||||
onClick: () => getCurrentEditor().paste(),
|
||||
});
|
||||
}
|
||||
|
||||
if (findReplace) {
|
||||
registerCommand({
|
||||
id: idPrefix + '.find',
|
||||
@@ -845,6 +866,14 @@ registerCommand({
|
||||
onClick: () => getElectron().send('window-action', 'selectAll'),
|
||||
});
|
||||
|
||||
registerCommand({
|
||||
id: 'new.gist',
|
||||
category: 'New',
|
||||
name: 'Upload error to gist',
|
||||
onClick: () => showModal(UploadErrorModal),
|
||||
});
|
||||
|
||||
|
||||
const electron = getElectron();
|
||||
if (electron) {
|
||||
electron.addEventListener('run-command', (e, commandId) => runCommand(commandId));
|
||||
|
||||
@@ -136,10 +136,11 @@
|
||||
export let macroPreview;
|
||||
export let macroValues;
|
||||
export let selectedCellsPublished;
|
||||
export let setLoadedRows = null;
|
||||
|
||||
// export let onChangeGrider = undefined;
|
||||
|
||||
export let loadedRows = [];
|
||||
let loadedRows = [];
|
||||
|
||||
export const activator = createActivator('CollectionDataGridCore', false);
|
||||
|
||||
@@ -225,6 +226,11 @@
|
||||
...createQuickExportMenu(quickExportHandler, { command: 'collectionDataGrid.export' }),
|
||||
tag: 'export',
|
||||
}));
|
||||
|
||||
function handleSetLoadedRows(rows) {
|
||||
loadedRows = rows;
|
||||
if (setLoadedRows) setLoadedRows(rows);
|
||||
}
|
||||
</script>
|
||||
|
||||
<LoadingDataGridCore
|
||||
@@ -232,7 +238,7 @@
|
||||
loadDataPage={loadCollectionDataPage}
|
||||
{dataPageAvailable}
|
||||
{loadRowCount}
|
||||
bind:loadedRows
|
||||
setLoadedRows={handleSetLoadedRows}
|
||||
bind:selectedCellsPublished
|
||||
frameSelection={!!macroPreview}
|
||||
onOpenQuery={openQuery}
|
||||
|
||||
@@ -88,8 +88,9 @@
|
||||
export let macroCondition;
|
||||
export let onRunMacro;
|
||||
export let hasMultiColumnFilter = false;
|
||||
export let setLoadedRows = null;
|
||||
|
||||
export let loadedRows;
|
||||
let loadedRows;
|
||||
|
||||
export const activator = createActivator('DataGrid', false);
|
||||
|
||||
@@ -208,7 +209,7 @@
|
||||
{#if isFormView}
|
||||
<svelte:component this={formViewComponent} {...$$props} />
|
||||
{:else if isJsonView}
|
||||
<svelte:component this={jsonViewComponent} {...$$props} bind:loadedRows />
|
||||
<svelte:component this={jsonViewComponent} {...$$props} {setLoadedRows} />
|
||||
{:else}
|
||||
<svelte:component
|
||||
this={gridCoreComponent}
|
||||
@@ -217,7 +218,7 @@
|
||||
formViewAvailable={!!formViewComponent}
|
||||
macroValues={extractMacroValuesForMacro($macroValues, $selectedMacro)}
|
||||
macroPreview={$selectedMacro}
|
||||
bind:loadedRows
|
||||
{setLoadedRows}
|
||||
bind:selectedCellsPublished
|
||||
onChangeSelectedColumns={cols => {
|
||||
if (domColumnManager) domColumnManager.setSelectedColumns(cols);
|
||||
|
||||
@@ -38,6 +38,8 @@
|
||||
id: 'dataGrid.revertAllChanges',
|
||||
category: 'Data grid',
|
||||
name: 'Revert all changes',
|
||||
toolbarName: 'Revert all',
|
||||
icon: 'icon undo',
|
||||
testEnabled: () => getCurrentDataGrid()?.getGrider()?.containsChanges,
|
||||
onClick: () => getCurrentDataGrid().revertAllChanges(),
|
||||
});
|
||||
@@ -689,7 +691,6 @@
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
openNewTab(
|
||||
{
|
||||
title: 'Map',
|
||||
@@ -1008,6 +1009,8 @@
|
||||
|
||||
$: selectedCellsInfo = getSelectedCellsInfo(selectedCells, grider, realColumnUniqueNames, getSelectedRowData());
|
||||
|
||||
$: databaseStatus = useDatabaseStatus({ conid, database });
|
||||
|
||||
// $: console.log('visibleRealColumns', visibleRealColumns);
|
||||
// $: console.log('visibleRowCountUpperBound', visibleRowCountUpperBound);
|
||||
// $: console.log('rowHeight', rowHeight);
|
||||
@@ -1163,6 +1166,7 @@
|
||||
if (event.target.closest('.collapseButtonMarker')) return;
|
||||
if (event.target.closest('.showFormButtonMarker')) return;
|
||||
if (event.target.closest('.inplaceeditor-container')) return;
|
||||
if (event.target.closest('input')) return;
|
||||
|
||||
shiftDragStartCell = null;
|
||||
// event.target.closest('table').focus();
|
||||
@@ -1737,7 +1741,11 @@
|
||||
</script>
|
||||
|
||||
{#if !display || (!isDynamicStructure && (!columns || columns.length == 0))}
|
||||
<LoadingInfo wrapper message="Waiting for structure" />
|
||||
{#if $databaseStatus?.name == 'pending' || $databaseStatus?.name == 'checkStructure' || $databaseStatus?.name == 'loadStructure'}
|
||||
<LoadingInfo wrapper message="Waiting for structure" />
|
||||
{:else}
|
||||
<ErrorInfo alignTop message="No structure was loaded, probably table doesn't exist in current database" />
|
||||
{/if}
|
||||
{:else if errorMessage}
|
||||
<div>
|
||||
<ErrorInfo message={errorMessage} alignTop />
|
||||
|
||||
@@ -70,6 +70,10 @@
|
||||
supportsReload,
|
||||
!!changeSetState
|
||||
);
|
||||
|
||||
function handleSetLoadedRows(rows) {
|
||||
loadedRows = rows;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#key jslid}
|
||||
@@ -81,7 +85,7 @@
|
||||
setConfig={config.update}
|
||||
gridCoreComponent={JslDataGridCore}
|
||||
formViewComponent={JslFormView}
|
||||
bind:loadedRows
|
||||
setLoadedRows={handleSetLoadedRows}
|
||||
isDynamicStructure={!!infoUsed?.__isDynamicStructure}
|
||||
useEvalFilters
|
||||
showMacros={!!dispatchChangeSet}
|
||||
|
||||
@@ -72,7 +72,9 @@
|
||||
export let selectedCellsPublished = () => [];
|
||||
export const activator = createActivator('JslDataGridCore', false);
|
||||
|
||||
export let loadedRows = [];
|
||||
export let setLoadedRows;
|
||||
|
||||
let loadedRows = [];
|
||||
let domGrid;
|
||||
|
||||
let changeIndex = 0;
|
||||
@@ -186,12 +188,17 @@
|
||||
...createQuickExportMenu(quickExportHandler, { command: 'jslTableGrid.export' }),
|
||||
tag: 'export',
|
||||
}));
|
||||
|
||||
function handleSetLoadedRows(rows) {
|
||||
loadedRows = rows;
|
||||
setLoadedRows?.(rows);
|
||||
}
|
||||
</script>
|
||||
|
||||
<LoadingDataGridCore
|
||||
bind:this={domGrid}
|
||||
{...$$props}
|
||||
bind:loadedRows
|
||||
setLoadedRows={handleSetLoadedRows}
|
||||
bind:selectedCellsPublished
|
||||
{loadDataPage}
|
||||
{dataPageAvailable}
|
||||
|
||||
@@ -16,10 +16,11 @@
|
||||
export let rowCountLoaded = null;
|
||||
|
||||
export let preprocessLoadedRow = null;
|
||||
export let setLoadedRows = null;
|
||||
|
||||
// export let griderFactory;
|
||||
|
||||
export let loadedRows = [];
|
||||
let loadedRows = [];
|
||||
let isLoading = false;
|
||||
let isLoadedAll = false;
|
||||
let loadedTime = new Date().getTime();
|
||||
@@ -49,7 +50,6 @@
|
||||
// await new Promise(resolve => setTimeout(resolve, 5000));
|
||||
|
||||
loadedTimeRef.set(loadStart);
|
||||
// console.log('LOAD NEXT ROWS', loadedRows);
|
||||
|
||||
const nextRows = await loadDataPage(
|
||||
$$props,
|
||||
@@ -121,6 +121,8 @@
|
||||
display.reload();
|
||||
}
|
||||
}
|
||||
|
||||
$: if (setLoadedRows) setLoadedRows(loadedRows);
|
||||
</script>
|
||||
|
||||
<DataGridCore
|
||||
|
||||
@@ -212,6 +212,10 @@
|
||||
tag: 'export',
|
||||
})
|
||||
);
|
||||
|
||||
function handleSetLoadedRows(rows) {
|
||||
loadedRows = rows;
|
||||
}
|
||||
</script>
|
||||
|
||||
<LoadingDataGridCore
|
||||
@@ -219,7 +223,7 @@
|
||||
{loadDataPage}
|
||||
{dataPageAvailable}
|
||||
{loadRowCount}
|
||||
bind:loadedRows
|
||||
setLoadedRows={handleSetLoadedRows}
|
||||
bind:selectedCellsPublished
|
||||
frameSelection={!!macroPreview}
|
||||
{grider}
|
||||
|
||||
@@ -49,7 +49,9 @@
|
||||
if (index >= 0 && index + d >= 0 && index + d < current.columns?.length) {
|
||||
let columns = [...current.columns];
|
||||
|
||||
[columns[index], columns[index + d]] = [columns[index + d], columns[index]];
|
||||
const tmp = columns[index + d];
|
||||
columns[index + d] = columns[index];
|
||||
columns[index] = tmp;
|
||||
|
||||
return {
|
||||
...current,
|
||||
|
||||
@@ -62,6 +62,7 @@
|
||||
.main {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.main.flex1 {
|
||||
|
||||
@@ -25,13 +25,14 @@
|
||||
|
||||
export let changeSetState;
|
||||
export let dispatchChangeSet;
|
||||
export let setLoadedRows;
|
||||
|
||||
export const activator = createActivator('CollectionJsonView', true);
|
||||
|
||||
let isLoading = false;
|
||||
let loadedTime = null;
|
||||
|
||||
export let loadedRows = [];
|
||||
let loadedRows = [];
|
||||
let skip = 0;
|
||||
let limit = 50;
|
||||
|
||||
@@ -39,6 +40,7 @@
|
||||
isLoading = true;
|
||||
// @ts-ignore
|
||||
loadedRows = await loadCollectionDataPage($$props, parseInt(skip) || 0, parseInt(limit) || 50);
|
||||
if (setLoadedRows) setLoadedRows(loadedRows);
|
||||
isLoading = false;
|
||||
loadedTime = new Date().getTime();
|
||||
}
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
import { getFormContext } from '../forms/FormProviderCore.svelte';
|
||||
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||
import { useDatabaseList } from '../utility/metadataLoaders';
|
||||
import uuidv1 from 'uuid/v1';
|
||||
import { temporaryOpenedConnections } from '../stores';
|
||||
import useEffect from '../utility/useEffect';
|
||||
|
||||
export let conidName;
|
||||
|
||||
@@ -17,6 +20,16 @@
|
||||
})),
|
||||
'label'
|
||||
);
|
||||
|
||||
const tmpid = uuidv1();
|
||||
|
||||
$: effect = useEffect(() => {
|
||||
temporaryOpenedConnections.update(x => [...x, { tmpid, conid: $values && $values[conidName] }]);
|
||||
return () => {
|
||||
temporaryOpenedConnections.update(x => x.filter(y => y.tmpid != tmpid));
|
||||
};
|
||||
});
|
||||
$: $effect;
|
||||
</script>
|
||||
|
||||
<FormSelectField {...$$restProps} options={databaseOptions} />
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
import FormButton from '../forms/FormButton.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { apiCall } from '../utility/api';
|
||||
|
||||
export let editingData;
|
||||
export let savingTab;
|
||||
@@ -29,8 +29,8 @@ import { apiCall } from '../utility/api';
|
||||
urlPath: _.kebabCase(_.deburr(savingTab.title)),
|
||||
}
|
||||
: editingData
|
||||
? _.pick(editingData, savedProperties)
|
||||
: {};
|
||||
? _.pick(editingData, savedProperties)
|
||||
: {};
|
||||
|
||||
$: savedFile = savingTab && savingTab.props && savingTab.props.savedFile;
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<script lang="ts">
|
||||
import _ from 'lodash';
|
||||
|
||||
import getElectron from '../utility/getElectron';
|
||||
import hasPermission from '../utility/hasPermission';
|
||||
import localforage from 'localforage';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import uuidv1 from 'uuid/v1';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { copyTextToClipboard } from '../utility/clipboard';
|
||||
import FormProvider from '../forms/FormProvider.svelte';
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
import FormCheckboxField from '../forms/FormCheckboxField.svelte';
|
||||
import FormValues from '../forms/FormValues.svelte';
|
||||
import FormSelectField from '../forms/FormSelectField.svelte';
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
import FormButton from '../forms/FormButton.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import FormConnectionSelect from '../impexp/FormConnectionSelect.svelte';
|
||||
import FormDatabaseSelect from '../impexp/FormDatabaseSelect.svelte';
|
||||
import { changeTab } from '../utility/common';
|
||||
|
||||
export let editingData;
|
||||
export let callingTab;
|
||||
|
||||
const handleSubmit = async ev => {
|
||||
const { conid, database } = ev.detail;
|
||||
changeTab(callingTab.tabid, tab => ({
|
||||
...tab,
|
||||
props: {
|
||||
...tab.props,
|
||||
conid,
|
||||
database,
|
||||
},
|
||||
}));
|
||||
closeCurrentModal();
|
||||
// console.log('SwitchDatabaseModal.handleSubmit', ev);
|
||||
// changeTab(tabid, tab => ({ ...tab, busy }));
|
||||
};
|
||||
|
||||
$: initialValues = {
|
||||
conid: callingTab?.props?.conid,
|
||||
database: callingTab?.props?.database,
|
||||
};
|
||||
</script>
|
||||
|
||||
<FormProvider {initialValues}>
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Switch database</svelte:fragment>
|
||||
|
||||
<FormConnectionSelect name="conid" label="Server" direction="source" isNative />
|
||||
<FormDatabaseSelect conidName="conid" name="database" label="Database" isNative />
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormValues let:values>
|
||||
<FormSubmit value="OK" on:click={handleSubmit} />
|
||||
<FormButton value="Cancel" on:click={closeCurrentModal} />
|
||||
</FormValues>
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
</FormProvider>
|
||||
@@ -0,0 +1,66 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import FormStyledButton from '../buttons/FormStyledButton.svelte';
|
||||
|
||||
import FormProvider from '../forms/FormProvider.svelte';
|
||||
import FormSubmit from '../forms/FormSubmit.svelte';
|
||||
import FormTextField from '../forms/FormTextField.svelte';
|
||||
import { apiCall } from '../utility/api';
|
||||
import ModalBase from './ModalBase.svelte';
|
||||
import { closeCurrentModal } from './modalTools';
|
||||
import { copyTextToClipboard } from '../utility/clipboard';
|
||||
import Link from '../elements/Link.svelte';
|
||||
import LoadingInfo from '../elements/LoadingInfo.svelte';
|
||||
import { showSnackbarSuccess } from '../utility/snackbar';
|
||||
import ErrorInfo from '../elements/ErrorInfo.svelte';
|
||||
|
||||
export let error = null;
|
||||
let htmlUrl;
|
||||
let url;
|
||||
let errorMessage;
|
||||
|
||||
async function upload() {
|
||||
const resp = await apiCall('uploads/upload-error-to-gist');
|
||||
url = resp.url;
|
||||
htmlUrl = resp.html_url;
|
||||
errorMessage = resp.errorMessage;
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
upload();
|
||||
});
|
||||
|
||||
async function handleDelete() {
|
||||
const resp = await apiCall('uploads/delete-gist', { url });
|
||||
closeCurrentModal();
|
||||
showSnackbarSuccess('Gist was deleted');
|
||||
}
|
||||
|
||||
function handleCopy() {
|
||||
copyTextToClipboard(htmlUrl);
|
||||
}
|
||||
</script>
|
||||
|
||||
<ModalBase {...$$restProps}>
|
||||
<svelte:fragment slot="header">Upload error</svelte:fragment>
|
||||
|
||||
{#if htmlUrl}
|
||||
<div>
|
||||
<p>
|
||||
Upload error to gist was successful. Please copy gist URL abnd paste it to related github issue. You could check
|
||||
uploaded data, if don't want to make them public, use Delete button to remove them from gist.
|
||||
</p>
|
||||
<p><Link href={htmlUrl}>Open uploaded data</Link></p>
|
||||
</div>
|
||||
{:else if errorMessage}
|
||||
<ErrorInfo message={errorMessage} />
|
||||
{:else}
|
||||
<LoadingInfo message="Uploading error to gist..." />
|
||||
{/if}
|
||||
|
||||
<svelte:fragment slot="footer">
|
||||
<FormStyledButton value="Copy URL" disabled={!htmlUrl} on:click={handleCopy} />
|
||||
<FormStyledButton type="button" value="Close" on:click={closeCurrentModal} />
|
||||
<FormStyledButton value="Delete" disabled={!url} on:click={handleDelete} />
|
||||
</svelte:fragment>
|
||||
</ModalBase>
|
||||
@@ -123,6 +123,10 @@
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if driver?.showConnectionField('serviceName', $values)}
|
||||
<FormTextField label="Service name" name="serviceName" disabled={isConnected} />
|
||||
{/if}
|
||||
|
||||
{#if driver?.showConnectionField('socketPath', $values)}
|
||||
<FormTextField
|
||||
label="Socket path"
|
||||
|
||||
@@ -74,6 +74,7 @@ ORDER BY
|
||||
{ label: 'Themes', slot: 3 },
|
||||
{ label: 'Default Actions', slot: 4 },
|
||||
{ label: 'Confirmations', slot: 5 },
|
||||
{ label: 'Other', slot: 6 },
|
||||
]}
|
||||
>
|
||||
<svelte:fragment slot="1">
|
||||
@@ -313,6 +314,15 @@ ORDER BY
|
||||
label="Skip confirmation when saving collection data (NoSQL)"
|
||||
/>
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="6">
|
||||
<div class="heading">Other</div>
|
||||
|
||||
<FormTextField
|
||||
name="other.gistCreateToken"
|
||||
label="API token for creating error gists"
|
||||
defaultValue=""
|
||||
/>
|
||||
</svelte:fragment>
|
||||
</TabControl>
|
||||
</FormValues>
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ export const visibleSelectedWidget = derived(
|
||||
export const emptyConnectionGroupNames = writableWithStorage([], 'emptyConnectionGroupNames');
|
||||
export const collapsedConnectionGroupNames = writableWithStorage([], 'collapsedConnectionGroupNames');
|
||||
export const openedConnections = writable([]);
|
||||
export const temporaryOpenedConnections = writable([]);
|
||||
export const openedSingleDatabaseConnections = writable([]);
|
||||
export const expandedConnections = writable([]);
|
||||
export const currentDatabase = writable(null);
|
||||
@@ -144,6 +145,15 @@ export const activeDbKeysStore = writableWithStorage({}, 'activeDbKeysStore');
|
||||
export const currentThemeDefinition = derived([currentTheme, extensions], ([$currentTheme, $extensions]) =>
|
||||
$extensions.themes.find(x => x.themeClassName == $currentTheme)
|
||||
);
|
||||
export const openedConnectionsWithTemporary = derived(
|
||||
[openedConnections, temporaryOpenedConnections, openedSingleDatabaseConnections],
|
||||
([$openedConnections, $temporaryOpenedConnections, $openedSingleDatabaseConnections]) =>
|
||||
_.uniq([
|
||||
...$openedConnections,
|
||||
...$temporaryOpenedConnections.map(x => x.conid),
|
||||
...$openedSingleDatabaseConnections,
|
||||
])
|
||||
);
|
||||
|
||||
let nativeMenuOnStartup = null;
|
||||
export const visibleTitleBar = derived(useSettings(), $settings => {
|
||||
|
||||
@@ -292,6 +292,7 @@
|
||||
import { useConnectionColorFactory } from '../utility/useConnectionColor';
|
||||
import TabCloseButton from '../elements/TabCloseButton.svelte';
|
||||
import CloseTabModal from '../modals/CloseTabModal.svelte';
|
||||
import SwitchDatabaseModal from '../modals/SwitchDatabaseModal.svelte';
|
||||
|
||||
export let multiTabIndex;
|
||||
export let shownTab;
|
||||
@@ -304,8 +305,8 @@
|
||||
$currentDatabase && $currentDatabase.name && $currentDatabase.connection
|
||||
? `database://${$currentDatabase.name}-${$currentDatabase.connection._id}`
|
||||
: $currentDatabase && $currentDatabase.connection
|
||||
? `server://${$currentDatabase.connection._id}`
|
||||
: '_no';
|
||||
? `server://${$currentDatabase.connection._id}`
|
||||
: '_no';
|
||||
|
||||
$: tabsWithDb = $openedTabs.filter(showTabFilterFunc).map(tab => ({
|
||||
...tab,
|
||||
@@ -372,6 +373,16 @@
|
||||
onClick: () => showModal(FavoriteModal, { savingTab: tab }),
|
||||
},
|
||||
],
|
||||
tabComponent &&
|
||||
tabs[tabComponent] &&
|
||||
tabs[tabComponent].allowSwitchDatabase &&
|
||||
tabs[tabComponent].allowSwitchDatabase(props) && [
|
||||
{ divider: true },
|
||||
{
|
||||
text: 'Switch database',
|
||||
onClick: () => showModal(SwitchDatabaseModal, { callingTab: tab }),
|
||||
},
|
||||
],
|
||||
{ divider: true },
|
||||
appobj &&
|
||||
appobj.createAppObjectMenu &&
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
export const matchingProps = ['conid', 'database', 'schemaName', 'pureName'];
|
||||
export const allowAddToFavorites = props => true;
|
||||
export const allowSwitchDatabase = props => true;
|
||||
|
||||
registerCommand({
|
||||
id: 'collectionTable.save',
|
||||
@@ -167,11 +168,15 @@
|
||||
$: setLocalStorage('collection_collapsedLeftColumn', $collapsedLeftColumnStore);
|
||||
|
||||
const quickExportHandlerRef = createQuickExportHandlerRef();
|
||||
|
||||
function handleSetLoadedRows(rows) {
|
||||
loadedRows = rows;
|
||||
}
|
||||
</script>
|
||||
|
||||
<ToolStripContainer>
|
||||
<DataGrid
|
||||
bind:loadedRows
|
||||
setLoadedRows={handleSetLoadedRows}
|
||||
{...$$props}
|
||||
config={$config}
|
||||
setConfig={config.update}
|
||||
@@ -193,6 +198,7 @@
|
||||
<ToolStripCommandButton command="dataGrid.refresh" hideDisabled />
|
||||
<ToolStripCommandButton command="dataForm.refresh" hideDisabled />
|
||||
<ToolStripCommandButton command="collectionTable.save" />
|
||||
<ToolStripCommandButton command="dataGrid.revertAllChanges" hideDisabled />
|
||||
<ToolStripCommandButton command="dataGrid.insertNewRow" hideDisabled />
|
||||
<ToolStripCommandButton command="dataGrid.deleteSelectedRows" hideDisabled />
|
||||
<ToolStripCommandButton command="dataGrid.switchToJson" hideDisabled />
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
} from '../stores';
|
||||
import _, { Dictionary } from 'lodash';
|
||||
import { apiCall } from '../utility/api';
|
||||
import { showSnackbarSuccess } from '../utility/snackbar';
|
||||
import { showSnackbarError, showSnackbarSuccess } from '../utility/snackbar';
|
||||
import { changeTab } from '../utility/common';
|
||||
import getConnectionLabel from '../utility/getConnectionLabel';
|
||||
import { onMount } from 'svelte';
|
||||
@@ -47,6 +47,9 @@
|
||||
}
|
||||
);
|
||||
|
||||
// $: console.log('ConnectionTab.$values', $values);
|
||||
// $: console.log('ConnectionTab.driver', driver);
|
||||
|
||||
$: engine = $values.engine;
|
||||
$: driver = $extensions.drivers.find(x => x.engine == engine);
|
||||
|
||||
@@ -81,6 +84,7 @@
|
||||
'defaultDatabase',
|
||||
'singleDatabase',
|
||||
'socketPath',
|
||||
'serviceName',
|
||||
];
|
||||
const visibleProps = allProps.filter(x => driver?.showConnectionField(x, $values));
|
||||
const omitProps = _.difference(allProps, visibleProps);
|
||||
@@ -161,7 +165,12 @@
|
||||
|
||||
onMount(async () => {
|
||||
if (conid) {
|
||||
$values = await apiCall('connections/get', { conid });
|
||||
const con = await apiCall('connections/get', { conid });
|
||||
if (con) {
|
||||
$values = con;
|
||||
} else {
|
||||
showSnackbarError(`Connection not found: ${conid}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
<script lang="ts" context="module">
|
||||
import registerCommand from '../commands/registerCommand';
|
||||
import { copyTextToClipboard } from '../utility/clipboard';
|
||||
|
||||
const getCurrentEditor = () => getActiveComponent('QueryTab');
|
||||
|
||||
registerCommand({
|
||||
@@ -37,6 +40,7 @@
|
||||
toggleComment: true,
|
||||
findReplace: true,
|
||||
executeAdditionalCondition: () => getCurrentEditor()?.hasConnection(),
|
||||
copyPaste: true,
|
||||
});
|
||||
registerCommand({
|
||||
id: 'query.executeCurrent',
|
||||
@@ -47,14 +51,14 @@
|
||||
getCurrentEditor() != null && !getCurrentEditor()?.isBusy() && getCurrentEditor()?.hasConnection(),
|
||||
onClick: () => getCurrentEditor().executeCurrent(),
|
||||
});
|
||||
|
||||
export const allowSwitchDatabase = props => true;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { getContext, onDestroy, onMount } from 'svelte';
|
||||
import sqlFormatter from 'sql-formatter';
|
||||
|
||||
import registerCommand from '../commands/registerCommand';
|
||||
|
||||
import VerticalSplitter from '../elements/VerticalSplitter.svelte';
|
||||
import SqlEditor from '../query/SqlEditor.svelte';
|
||||
import useEditorData from '../query/useEditorData';
|
||||
@@ -81,6 +85,7 @@
|
||||
import ToolStripExportButton, { createQuickExportHandlerRef } from '../buttons/ToolStripExportButton.svelte';
|
||||
import ToolStripSaveButton from '../buttons/ToolStripSaveButton.svelte';
|
||||
import ToolStripCommandSplitButton from '../buttons/ToolStripCommandSplitButton.svelte';
|
||||
import { getClipboardText } from '../utility/clipboard';
|
||||
|
||||
export let tabid;
|
||||
export let conid;
|
||||
@@ -241,6 +246,17 @@
|
||||
domEditor.getEditor().execCommand('togglecomment');
|
||||
}
|
||||
|
||||
export function copy() {
|
||||
const selectedText = domEditor.getEditor().getSelectedText();
|
||||
copyTextToClipboard(selectedText);
|
||||
}
|
||||
|
||||
export function paste() {
|
||||
getClipboardText().then(text => {
|
||||
domEditor.getEditor().execCommand('paste', text);
|
||||
});
|
||||
}
|
||||
|
||||
export function find() {
|
||||
domEditor.getEditor().execCommand('find');
|
||||
}
|
||||
@@ -311,6 +327,8 @@
|
||||
{ command: 'query.save' },
|
||||
{ command: 'query.saveAs' },
|
||||
{ divider: true },
|
||||
{ command: 'query.copy' },
|
||||
{ command: 'query.paste' },
|
||||
{ command: 'query.find' },
|
||||
{ command: 'query.replace' },
|
||||
{ divider: true },
|
||||
@@ -320,6 +338,15 @@
|
||||
|
||||
const quickExportHandlerRef = createQuickExportHandlerRef();
|
||||
|
||||
$: {
|
||||
conid;
|
||||
database;
|
||||
if (canKill()) {
|
||||
kill();
|
||||
}
|
||||
errorMessages = [];
|
||||
}
|
||||
|
||||
let isInitialized = false;
|
||||
</script>
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@
|
||||
|
||||
export const matchingProps = ['conid', 'database', 'schemaName', 'pureName'];
|
||||
export const allowAddToFavorites = props => true;
|
||||
export const allowSwitchDatabase = props => true;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -277,6 +278,7 @@
|
||||
<ToolStripCommandButton command="dataForm.goToLast" hideDisabled />
|
||||
|
||||
<ToolStripCommandButton command="tableData.save" />
|
||||
<ToolStripCommandButton command="dataGrid.revertAllChanges" hideDisabled />
|
||||
<ToolStripCommandButton command="dataGrid.insertNewRow" hideDisabled />
|
||||
<ToolStripCommandButton command="dataGrid.deleteSelectedRows" hideDisabled />
|
||||
<ToolStripCommandButton command="dataGrid.switchToForm" hideDisabled />
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts" context="module">
|
||||
export const matchingProps = ['conid', 'database', 'schemaName', 'pureName'];
|
||||
export const allowAddToFavorites = props => true;
|
||||
export const allowSwitchDatabase = props => true;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
@@ -8,9 +9,9 @@
|
||||
import { findEngineDriver } from 'dbgate-tools';
|
||||
import { setContext } from 'svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
|
||||
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
|
||||
import ToolStripExportButton, { createQuickExportHandlerRef } from '../buttons/ToolStripExportButton.svelte';
|
||||
import ToolStripCommandButton from '../buttons/ToolStripCommandButton.svelte';
|
||||
import ToolStripContainer from '../buttons/ToolStripContainer.svelte';
|
||||
import ToolStripExportButton, { createQuickExportHandlerRef } from '../buttons/ToolStripExportButton.svelte';
|
||||
|
||||
import DataGrid from '../datagrid/DataGrid.svelte';
|
||||
import SqlDataGridCore from '../datagrid/SqlDataGridCore.svelte';
|
||||
|
||||
@@ -8,6 +8,9 @@ import { isOauthCallback, redirectToLogin } from '../clientAuth';
|
||||
import { showModal } from '../modals/modalTools';
|
||||
import DatabaseLoginModal, { isDatabaseLoginVisible } from '../modals/DatabaseLoginModal.svelte';
|
||||
import _ from 'lodash';
|
||||
import uuidv1 from 'uuid/v1';
|
||||
|
||||
export const strmid = uuidv1();
|
||||
|
||||
let eventSource;
|
||||
let apiLogging = false;
|
||||
@@ -49,7 +52,7 @@ export function removeVolatileMapping(conid) {
|
||||
|
||||
function wantEventSource() {
|
||||
if (!eventSource) {
|
||||
eventSource = new EventSource(`${resolveApi()}/stream`);
|
||||
eventSource = new EventSource(`${resolveApi()}/stream?strmid=${strmid}`);
|
||||
// eventSource.addEventListener('clean-cache', e => cacheClean(JSON.parse(e.data)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,11 @@ export function copyTextToClipboard(text) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Currently this doesn't work in firefox stable, but works in nightly */
|
||||
export async function getClipboardText() {
|
||||
return await navigator.clipboard.readText();
|
||||
}
|
||||
|
||||
export function extractRowCopiedValue(row, col) {
|
||||
let value = row[col];
|
||||
if (value === undefined) value = _.get(row, col);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import _ from 'lodash';
|
||||
import { openedConnections, currentDatabase } from '../stores';
|
||||
import { apiCall } from './api';
|
||||
import { openedConnections, currentDatabase, openedConnectionsWithTemporary } from '../stores';
|
||||
import { apiCall, strmid } from './api';
|
||||
import { getConnectionList } from './metadataLoaders';
|
||||
|
||||
// const doServerPing = async value => {
|
||||
@@ -10,7 +10,7 @@ import { getConnectionList } from './metadataLoaders';
|
||||
// };
|
||||
|
||||
const doServerPing = value => {
|
||||
apiCall('server-connections/ping', { conidArray: value });
|
||||
apiCall('server-connections/ping', { conidArray: value, strmid });
|
||||
};
|
||||
|
||||
const doDatabasePing = value => {
|
||||
@@ -26,7 +26,7 @@ let openedConnectionsHandle = null;
|
||||
let currentDatabaseHandle = null;
|
||||
|
||||
export function subscribeConnectionPingers() {
|
||||
openedConnections.subscribe(value => {
|
||||
openedConnectionsWithTemporary.subscribe(value => {
|
||||
doServerPing(value);
|
||||
if (openedConnectionsHandle) window.clearInterval(openedConnectionsHandle);
|
||||
openedConnectionsHandle = window.setInterval(() => doServerPing(value), 20 * 1000);
|
||||
|
||||
@@ -97,7 +97,7 @@ function mapItem(item, commands) {
|
||||
if (command) {
|
||||
return {
|
||||
text: item.text || command.menuName || command.toolbarName || command.name,
|
||||
keyText: command.keyText || command.keyTextFromGroup,
|
||||
keyText: command.keyText || command.keyTextFromGroup || command.disableHandleKeyText,
|
||||
onClick: () => {
|
||||
if (command.isGroupCommand) {
|
||||
runGroupCommand(command.group);
|
||||
|
||||
@@ -24,6 +24,9 @@ function getConnectionLabelCore(connection, { allowExplicitDatabase = true } = {
|
||||
if (connection.singleDatabase && connection.defaultDatabase) {
|
||||
return `${connection.defaultDatabase}`;
|
||||
}
|
||||
if (connection.useDatabaseUrl) {
|
||||
return `${connection.databaseUrl}`;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ function getFileEncoding(filePath, fs) {
|
||||
}
|
||||
|
||||
function decodeFile(buf: Uint8Array, enc: string) {
|
||||
// TODO: use import instead of window.require. Requires doesn't work in built electron app
|
||||
const iconv = window.require('iconv-lite');
|
||||
return iconv.decode(buf, enc);
|
||||
}
|
||||
@@ -120,8 +121,9 @@ export function openElectronFileCore(filePath, extensions) {
|
||||
|
||||
if (nameLower.endsWith('.sql')) {
|
||||
const encoding = getFileEncoding(filePath, fs);
|
||||
const buf = fs.readFileSync(filePath);
|
||||
const data = decodeFile(buf, encoding);
|
||||
const data = fs.readFileSync(filePath, { encoding });
|
||||
// const buf = fs.readFileSync(filePath);
|
||||
// const data = decodeFile(buf, encoding);
|
||||
|
||||
newQuery({
|
||||
title: parsed.name,
|
||||
|
||||
@@ -189,6 +189,7 @@
|
||||
padding: 0px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.version {
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
diff --git a/node_modules/svelte/internal/index.js b/node_modules/svelte/internal/index.js
|
||||
index 1cce90d..6220522 100644
|
||||
--- a/node_modules/svelte/internal/index.js
|
||||
+++ b/node_modules/svelte/internal/index.js
|
||||
@@ -374,7 +374,7 @@ function insert_hydration(target, node, anchor) {
|
||||
}
|
||||
}
|
||||
function detach(node) {
|
||||
- node.parentNode.removeChild(node);
|
||||
+ if (node.parentNode) node.parentNode.removeChild(node);
|
||||
}
|
||||
function destroy_each(iterations, detaching) {
|
||||
for (let i = 0; i < iterations.length; i += 1) {
|
||||
diff --git a/node_modules/svelte/internal/index.mjs b/node_modules/svelte/internal/index.mjs
|
||||
index 6650e85..b746187 100644
|
||||
--- a/node_modules/svelte/internal/index.mjs
|
||||
+++ b/node_modules/svelte/internal/index.mjs
|
||||
@@ -370,7 +370,7 @@ function insert_hydration(target, node, anchor) {
|
||||
}
|
||||
}
|
||||
function detach(node) {
|
||||
- node.parentNode.removeChild(node);
|
||||
+ if (node.parentNode) node.parentNode.removeChild(node);
|
||||
}
|
||||
function destroy_each(iterations, detaching) {
|
||||
for (let i = 0; i < iterations.length; i += 1) {
|
||||
@@ -35,7 +35,7 @@
|
||||
"csv": "^5.3.2",
|
||||
"dbgate-plugin-tools": "^1.0.7",
|
||||
"lodash": "^4.17.21",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11"
|
||||
"webpack": "^5.91.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@
|
||||
"lodash": "^4.17.21",
|
||||
"xlsx": "0.16.9",
|
||||
"dbgate-plugin-tools": "^1.0.7",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11"
|
||||
"webpack": "^5.91.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
}
|
||||
@@ -32,12 +32,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"dbgate-plugin-tools": "^1.0.7",
|
||||
"dbgate-query-splitter": "^4.9.3",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"dbgate-query-splitter": "^4.10.1",
|
||||
"webpack": "^5.91.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"dbgate-tools": "^5.0.0-alpha.1",
|
||||
"is-promise": "^4.0.0",
|
||||
"mongodb": "^4.7.0",
|
||||
"mongodb-client-encryption": "^2.1.0"
|
||||
"mongodb": "^6.3.0",
|
||||
"mongodb-client-encryption": "^6.0.0"
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,24 @@ class Analyser extends DatabaseAnalyser {
|
||||
const collections = collectionsAndViews.filter((x) => x.type == 'collection');
|
||||
const views = collectionsAndViews.filter((x) => x.type == 'view');
|
||||
|
||||
const stats = await Promise.all(
|
||||
collections.filter((x) => x.type == 'collection').map((x) => this.pool.__getDatabase().collection(x.name).stats())
|
||||
);
|
||||
let stats;
|
||||
try {
|
||||
stats = await Promise.all(
|
||||
collections
|
||||
.filter((x) => x.type == 'collection')
|
||||
.map((x) =>
|
||||
this.pool
|
||||
.__getDatabase()
|
||||
.collection(x.name)
|
||||
.aggregate([{ $collStats: { count: {} } }])
|
||||
.toArray()
|
||||
.then((resp) => ({ name: x.name, count: resp[0].count }))
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
// $collStats not supported
|
||||
stats = {};
|
||||
}
|
||||
|
||||
const res = this.mergeAnalyseResult({
|
||||
collections: [
|
||||
|
||||
@@ -23,7 +23,9 @@ async function readCursor(cursor, options) {
|
||||
|
||||
function convertObjectId(condition) {
|
||||
return _.cloneDeepWith(condition, (x) => {
|
||||
if (x && x.$oid) return ObjectId(x.$oid);
|
||||
if (x && x.$oid) {
|
||||
return ObjectId.createFromHexString(x.$oid);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -60,7 +62,9 @@ const driver = {
|
||||
mongoUrl = databaseUrl;
|
||||
}
|
||||
} else {
|
||||
mongoUrl = user ? `mongodb://${encodeURIComponent(user)}:${encodeURIComponent(password)}@${server}:${port}` : `mongodb://${server}:${port}`;
|
||||
mongoUrl = user
|
||||
? `mongodb://${encodeURIComponent(user)}:${encodeURIComponent(password)}@${server}:${port}`
|
||||
: `mongodb://${server}:${port}`;
|
||||
}
|
||||
|
||||
const options = {
|
||||
@@ -93,7 +97,7 @@ const driver = {
|
||||
let func;
|
||||
func = eval(`(db,ObjectId) => ${sql}`);
|
||||
const db = await getScriptableDb(pool);
|
||||
const res = func(db, ObjectId);
|
||||
const res = func(db, ObjectId.createFromHexString);
|
||||
if (isPromise(res)) await res;
|
||||
},
|
||||
async stream(pool, sql, options) {
|
||||
@@ -113,7 +117,7 @@ const driver = {
|
||||
|
||||
let exprValue;
|
||||
try {
|
||||
exprValue = func(db, ObjectId);
|
||||
exprValue = func(db, ObjectId.createFromHexString);
|
||||
} catch (err) {
|
||||
options.info({
|
||||
message: 'Error evaluating expression: ' + err.message,
|
||||
@@ -227,7 +231,7 @@ const driver = {
|
||||
|
||||
func = eval(`(db,ObjectId) => ${sql}`);
|
||||
const db = await getScriptableDb(pool);
|
||||
exprValue = func(db, ObjectId);
|
||||
exprValue = func(db, ObjectId.createFromHexString);
|
||||
|
||||
// return directly stream without header row
|
||||
return exprValue.stream();
|
||||
@@ -290,7 +294,7 @@ const driver = {
|
||||
...insert.document,
|
||||
...insert.fields,
|
||||
};
|
||||
const resdoc = await collection.insert(convertObjectId(document));
|
||||
const resdoc = await collection.insertOne(convertObjectId(document));
|
||||
res.inserted.push(resdoc._id);
|
||||
}
|
||||
for (const update of changeSet.updates) {
|
||||
|
||||
@@ -67,7 +67,7 @@ const driver = {
|
||||
getCollectionUpdateScript(changeSet) {
|
||||
let res = '';
|
||||
for (const insert of changeSet.inserts) {
|
||||
res += `db.${insert.pureName}.insert(${jsonStringifyWithObjectId({
|
||||
res += `db.${insert.pureName}.insertOne(${jsonStringifyWithObjectId({
|
||||
...insert.document,
|
||||
...insert.fields,
|
||||
})});\n`;
|
||||
|
||||
@@ -32,11 +32,11 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"dbgate-plugin-tools": "^1.0.7",
|
||||
"dbgate-query-splitter": "^4.9.3",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11",
|
||||
"dbgate-query-splitter": "^4.10.1",
|
||||
"webpack": "^5.91.0",
|
||||
"webpack-cli": "^5.1.4",
|
||||
"dbgate-tools": "^5.0.0-alpha.1",
|
||||
"tedious": "^14.5.0",
|
||||
"tedious": "^18.2.0",
|
||||
"async-lock": "^1.2.6"
|
||||
}
|
||||
}
|
||||
@@ -33,10 +33,10 @@
|
||||
"devDependencies": {
|
||||
"antares-mysql-dumper": "^0.0.1",
|
||||
"dbgate-plugin-tools": "^1.0.7",
|
||||
"dbgate-query-splitter": "^4.9.3",
|
||||
"dbgate-query-splitter": "^4.10.1",
|
||||
"dbgate-tools": "^5.0.0-alpha.1",
|
||||
"mysql2": "^3.6.5",
|
||||
"webpack": "^5.89.0",
|
||||
"mysql2": "^3.9.7",
|
||||
"webpack": "^5.91.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ const dialect = {
|
||||
enableConstraintsPerTable: false,
|
||||
anonymousPrimaryKey: true,
|
||||
explicitDropConstraint: true,
|
||||
allowMultipleValuesInsert: true,
|
||||
quoteIdentifier(s) {
|
||||
return '`' + s + '`';
|
||||
},
|
||||
|
||||
@@ -31,10 +31,13 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"dbgate-plugin-tools": "^1.0.8",
|
||||
"dbgate-query-splitter": "^4.9.0",
|
||||
"dbgate-query-splitter": "^4.10.1",
|
||||
"dbgate-tools": "^5.0.0-alpha.1",
|
||||
"lodash": "^4.17.21",
|
||||
"webpack": "^4.42.0",
|
||||
"webpack-cli": "^3.3.11"
|
||||
"webpack": "^5.91.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"oracledb": "^6.5.1"
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user